From ae9a3e33aafaa0d708d54e2c42a563e45dadb0f7 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 30 Sep 2013 22:27:37 +0200 Subject: command: add commands for displaying overlays Requested by github issue #255. Does not work where mmap is not available (i.e. Windows). --- DOCS/man/en/input.rst | 71 +++++++++++++++++++++++++ mpvcore/command.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++-- mpvcore/command.h | 1 + mpvcore/input/input.c | 5 ++ mpvcore/input/input.h | 3 ++ mpvcore/mplayer.c | 2 + sub/sub.c | 5 ++ sub/sub.h | 3 ++ 8 files changed, 227 insertions(+), 4 deletions(-) diff --git a/DOCS/man/en/input.rst b/DOCS/man/en/input.rst index a0014c51bd..c2ba45a61c 100644 --- a/DOCS/man/en/input.rst +++ b/DOCS/man/en/input.rst @@ -325,6 +325,77 @@ Input Commands that are Possibly Subject to Change ``disable_section "
"`` Disable the named input section. Undoes ``enable_section``. +``overlay_add "" "" `` + 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 `` + 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``, diff --git a/mpvcore/command.c b/mpvcore/command.c index e1c7e6e79e..038e58bc8d 100644 --- a/mpvcore/command.c +++ b/mpvcore/command.c @@ -65,11 +65,21 @@ #include "stream/stream_dvd.h" #endif #include "screenshot.h" +#ifdef HAVE_SYS_MMAN_H +#include +#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. diff --git a/mpvcore/command.h b/mpvcore/command.h index dfdc066ac7..d3469fc131 100644 --- a/mpvcore/command.h +++ b/mpvcore/command.h @@ -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); diff --git a/mpvcore/input/input.c b/mpvcore/input/input.c index c78447c4bf..4ce7fe0156 100644 --- a/mpvcore/input/input.c +++ b/mpvcore/input/input.c @@ -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} }; diff --git a/mpvcore/input/input.h b/mpvcore/input/input.h index 33c269c1a7..dc308ff9f6 100644 --- a/mpvcore/input/input.h +++ b/mpvcore/input/input.h @@ -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 }; diff --git a/mpvcore/mplayer.c b/mpvcore/mplayer.c index b82e6a9e5d..4d3f24d057 100644 --- a/mpvcore/mplayer.c +++ b/mpvcore/mplayer.c @@ -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); diff --git a/sub/sub.c b/sub/sub.c index cc4a76c065..9be61bd1d3 100644 --- a/sub/sub.c +++ b/sub/sub.c @@ -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); } diff --git a/sub/sub.h b/sub/sub.h index 1118fec5c1..64e10193ea 100644 --- a/sub/sub.h +++ b/sub/sub.h @@ -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; -- cgit v1.2.3