command: add commands for displaying overlays

Requested by github issue #255.

Does not work where mmap is not available (i.e. Windows).
This commit is contained in:
wm4 2013-09-30 22:27:37 +02:00
parent fd49edccf8
commit ae9a3e33aa
8 changed files with 227 additions and 4 deletions

View File

@ -325,6 +325,77 @@ Input Commands that are Possibly Subject to Change
``disable_section "<section>"``
Disable the named input section. Undoes ``enable_section``.
``overlay_add <id> <x> <y> "<file>" <offset> "<fmt>" <w> <h> <stride>``
Add an OSD overlay sourced from raw data. This might be useful for scripts
and applications controlling mpv, and which want to display things on top
of the video window.
Overlays are usually displayed in screen resolution, but with some VOs,
the resolution is reduced to that of the video's. You can read the
``osd-width`` and ``osd-height`` properties. At least with ``--vo-xv`` and
anamorphic video (such as DVD), ``osd-par`` should be read as well, and the
overlay should be aspect-compensated. (Future directions: maybe mpv should
take care of some of these things automatically, but it's hard to tell
where to draw the line.)
``id`` is an integer between 0 and 63 identifying the overlay element. The
ID can be used to add multiple overlay parts, update a part by using this
command with an already existing ID, or to remove a part with
``overlay_remove``. Using a previously unused ID will add a new overlay,
while reusing an ID will update it. (Future directions: there should be
something to ensure different programs wanting to create overlays don't
conflict with each others, should that ever be needed.)
``x`` and ``y`` specify the position where the OSD should be displayed.
``file`` specifies the file the raw image data is read from. It can be
either a numeric UNIX file descriptor prefixed with ``@`` (e.g. ``@4``),
or a filename. The file will be mapped into memory with ``mmap()``. Some VOs
will pass the mapped pointer directly to display APIs (e.g. opengl or
vdpau), so no actual copying is involved. Truncating the source file while
the overlay is active will crash the player. You shouldn't change the data
while the overlay is active, because the data is essentially accessed at
random points. Instead, call ``overlay_add`` again (preferably with a
different memory region to prevent tearing).
``offset`` is the offset of the first pixel in the source file. It is
passed directly to ``mmap`` and is subject to certain restrictions
(see ``man mmap`` for details). In particular, this value has to be a
multiple of the system's page size.
``fmt`` is a string identifying the image format. Currently, only ``bgra``
is defined. This format has 4 bytes per pixels, with 8 bits per component.
The least significant 8 bits are blue, and the most significant 8 bits
are alpha (in little endian, the components are B-G-R-A, with B as first
byte). This uses premultiplied alpha: every color component is already
multiplied with the alpha component. This means the numeric value of each
component is equal to or smaller than the alpha component. (Violating this
rule will lead to different results with different VOs: numeric overflows
resulting from blending broken alpha values is considered something that
shouldn't happen, and consequently implementations don't ensure that you
get predictable behavior in this case.)
``w``, ``h``, and ``stride`` specify the size of the overlay. ``w`` is the
visible width of the overlay, while ``stride`` gives the width in bytes in
memory. In the simple case, and with the ``bgra`` format, ``stride==4*w``.
In general, the total amount of memory accessed is ``stride * h``.
(Technically, the minimum size would be ``stride * (h - 1) + w * 4``, but
for simplicity, the player will access all ``stride * h`` bytes.)
.. admonition:: Warning
When updating the overlay, you should prepare a second shared memory
region (e.g. make use of the offset parameter) and add this as overlay,
instead of reusing the same memory every time. Otherwise, you might
get the equivalent of tearing, when your application and mpv write/read
the buffer at the same time. Also, keep in mind that mpv might access
an overlay's memory at random times whenever it feels the need to do
so, for example when redrawing the screen.
``overlay_remove <id>``
Remove an overlay added with ``overlay_add`` and the same ID. Does nothing
if no overlay with this ID exists.
Undocumented commands: ``tv_start_scan``, ``tv_step_channel``, ``tv_step_norm``,
``tv_step_chanlist``, ``tv_set_channel``, ``tv_last_channel``, ``tv_set_freq``,
``tv_step_freq``, ``tv_set_norm``, ``dvb_set_channel``, ``radio_step_channel``,

View File

@ -65,11 +65,21 @@
#include "stream/stream_dvd.h"
#endif
#include "screenshot.h"
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#include "mpvcore/mp_core.h"
#include "mp_lua.h"
struct command_ctx {
int events;
#define OVERLAY_MAX_ID 64
void *overlay_map[OVERLAY_MAX_ID];
};
static int edit_filters(struct MPContext *mpctx, enum stream_type mediatype,
const char *cmd, const char *arg);
static int set_filters(struct MPContext *mpctx, enum stream_type mediatype,
@ -2227,6 +2237,113 @@ static int edit_filters_osd(struct MPContext *mpctx, enum stream_type mediatype,
return r;
}
#ifdef HAVE_SYS_MMAN_H
static int ext2_sub_find(struct MPContext *mpctx, int id)
{
struct command_ctx *cmd = mpctx->command_ctx;
struct sub_bitmaps *sub = &mpctx->osd->external2;
void *p = NULL;
if (id >= 0 && id < OVERLAY_MAX_ID)
p = cmd->overlay_map[id];
if (sub && p) {
for (int n = 0; n < sub->num_parts; n++) {
if (sub->parts[n].bitmap == p)
return n;
}
}
return -1;
}
static int ext2_sub_alloc(struct MPContext *mpctx)
{
struct osd_state *osd = mpctx->osd;
struct sub_bitmaps *sub = &osd->external2;
struct sub_bitmap b = {0};
MP_TARRAY_APPEND(osd, sub->parts, sub->num_parts, b);
return sub->num_parts - 1;
}
static int overlay_add(struct MPContext *mpctx, int id, int x, int y,
char *file, int offset, char *fmt, int w, int h,
int stride)
{
struct command_ctx *cmd = mpctx->command_ctx;
struct osd_state *osd = mpctx->osd;
if (strcmp(fmt, "bgra") != 0) {
MP_ERR(mpctx, "overlay_add: unsupported OSD format '%s'\n", fmt);
return -1;
}
if (id < 0 || id >= OVERLAY_MAX_ID) {
MP_ERR(mpctx, "overlay_add: invalid id %d\n", id);
return -1;
}
int fd = -1;
bool close_fd = true;
if (file[0] == '@') {
char *end;
fd = strtol(&file[1], &end, 10);
if (!file[1] || end[0])
fd = -1;
close_fd = false;
} else {
fd = open(file, O_RDONLY | O_BINARY);
}
void *p = mmap(NULL, h * stride, PROT_READ, MAP_SHARED, fd, offset);
if (fd >= 0 && close_fd)
close(fd);
if (!p) {
MP_ERR(mpctx, "overlay_add: could not open or map '%s'\n", file);
return -1;
}
int index = ext2_sub_find(mpctx, id);
if (index < 0)
index = ext2_sub_alloc(mpctx);
if (index < 0) {
munmap(p, h * stride);
return -1;
}
cmd->overlay_map[id] = p;
osd->external2.parts[index] = (struct sub_bitmap) {
.bitmap = p,
.stride = stride,
.x = x, .y = y,
.w = w, .h = h,
.dw = w, .dh = h,
};
osd->external2.bitmap_id = osd->external2.bitmap_pos_id = 1;
osd->external2.format = SUBBITMAP_RGBA;
osd->want_redraw = true;
return 0;
}
static void overlay_remove(struct MPContext *mpctx, int id)
{
struct command_ctx *cmd = mpctx->command_ctx;
struct osd_state *osd = mpctx->osd;
int index = ext2_sub_find(mpctx, id);
if (index >= 0) {
struct sub_bitmaps *sub = &osd->external2;
struct sub_bitmap *part = &sub->parts[index];
munmap(part->bitmap, part->h * part->stride);
MP_TARRAY_REMOVE_AT(sub->parts, sub->num_parts, index);
cmd->overlay_map[id] = NULL;
sub->bitmap_id = sub->bitmap_pos_id = 1;
}
}
static void overlay_uninit(struct MPContext *mpctx)
{
for (int id = 0; id < OVERLAY_MAX_ID; id++)
overlay_remove(mpctx, id);
}
#else
static void overlay_uninit(struct MPContext *mpctx){}
#endif
void run_command(MPContext *mpctx, mp_cmd_t *cmd)
{
struct MPOpts *opts = mpctx->opts;
@ -2775,6 +2892,19 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
}
break;
#ifdef HAVE_SYS_MMAN_H
case MP_CMD_OVERLAY_ADD:
overlay_add(mpctx,
cmd->args[0].v.i, cmd->args[1].v.i, cmd->args[2].v.i,
cmd->args[3].v.s, cmd->args[4].v.i, cmd->args[5].v.s,
cmd->args[6].v.i, cmd->args[7].v.i, cmd->args[8].v.i);
break;
case MP_CMD_OVERLAY_REMOVE:
overlay_remove(mpctx, cmd->args[0].v.i);
break;
#endif
case MP_CMD_COMMAND_LIST: {
for (struct mp_cmd *sub = cmd->args[0].v.p; sub; sub = sub->queue_next)
run_command(mpctx, sub);
@ -2802,13 +2932,16 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
}
}
struct command_ctx {
int events;
};
void command_uninit(struct MPContext *mpctx)
{
overlay_uninit(mpctx);
talloc_free(mpctx->command_ctx);
mpctx->command_ctx = NULL;
}
void command_init(struct MPContext *mpctx)
{
mpctx->command_ctx = talloc_zero(mpctx, struct command_ctx);
mpctx->command_ctx = talloc_zero(NULL, struct command_ctx);
}
// Notify that a property might have changed.

View File

@ -23,6 +23,7 @@ struct MPContext;
struct mp_cmd;
void command_init(struct MPContext *mpctx);
void command_uninit(struct MPContext *mpctx);
void run_command(struct MPContext *mpctx, struct mp_cmd *cmd);
char *mp_property_expand_string(struct MPContext *mpctx, const char *str);

View File

@ -232,6 +232,11 @@ static const mp_cmd_t mp_cmds[] = {
{ MP_CMD_SCRIPT_DISPATCH, "script_dispatch", { ARG_STRING, ARG_INT } },
{ MP_CMD_OVERLAY_ADD, "overlay_add",
{ ARG_INT, ARG_INT, ARG_INT, ARG_STRING, ARG_INT, ARG_STRING, ARG_INT,
ARG_INT, ARG_INT }},
{ MP_CMD_OVERLAY_REMOVE, "overlay_remove", { ARG_INT } },
{0}
};

View File

@ -89,6 +89,9 @@ enum mp_command_type {
/// Internal for Lua scripts
MP_CMD_SCRIPT_DISPATCH,
MP_CMD_OVERLAY_ADD,
MP_CMD_OVERLAY_REMOVE,
// Internal
MP_CMD_COMMAND_LIST, // list of sub-commands in args[0].v.p
};

View File

@ -563,6 +563,8 @@ static MP_NORETURN void exit_player(struct MPContext *mpctx,
cocoa_set_input_context(NULL);
#endif
command_uninit(mpctx);
mp_input_uninit(mpctx->input);
osd_free(mpctx->osd);

View File

@ -163,6 +163,11 @@ static void render_object(struct osd_state *osd, struct osd_object *obj,
sub_pts -= osd->video_offset - opts->sub_delay;
sub_get_bitmaps(osd->dec_sub, obj->vo_res, sub_pts, out_imgs);
}
} else if (obj->type == OSDTYPE_EXTERNAL2) {
if (osd->external2.format) {
*out_imgs = osd->external2;
osd->external2.bitmap_id = osd->external2.bitmap_pos_id = 0;
}
} else {
osd_object_get_bitmaps(osd, obj, out_imgs);
}

View File

@ -91,6 +91,7 @@ enum mp_osdtype {
OSDTYPE_OSD,
OSDTYPE_EXTERNAL,
OSDTYPE_EXTERNAL2,
MAX_OSD_PARTS
};
@ -144,6 +145,8 @@ struct osd_state {
// OSDTYPE_EXTERNAL
char *external;
int external_res_x, external_res_y;
// OSDTYPE_EXTERNAL2
struct sub_bitmaps external2;
// OSDTYPE_SUB
struct dec_sub *dec_sub;