mirror of
https://github.com/mpv-player/mpv
synced 2025-03-21 18:57:35 +00:00
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:
parent
fd49edccf8
commit
ae9a3e33aa
@ -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``,
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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}
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user