mirror of
https://github.com/mpv-player/mpv
synced 2025-03-25 04:38:01 +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_section "<section>"``
|
||||||
Disable the named input section. Undoes ``enable_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``,
|
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_chanlist``, ``tv_set_channel``, ``tv_last_channel``, ``tv_set_freq``,
|
||||||
``tv_step_freq``, ``tv_set_norm``, ``dvb_set_channel``, ``radio_step_channel``,
|
``tv_step_freq``, ``tv_set_norm``, ``dvb_set_channel``, ``radio_step_channel``,
|
||||||
|
@ -65,11 +65,21 @@
|
|||||||
#include "stream/stream_dvd.h"
|
#include "stream/stream_dvd.h"
|
||||||
#endif
|
#endif
|
||||||
#include "screenshot.h"
|
#include "screenshot.h"
|
||||||
|
#ifdef HAVE_SYS_MMAN_H
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "mpvcore/mp_core.h"
|
#include "mpvcore/mp_core.h"
|
||||||
|
|
||||||
#include "mp_lua.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,
|
static int edit_filters(struct MPContext *mpctx, enum stream_type mediatype,
|
||||||
const char *cmd, const char *arg);
|
const char *cmd, const char *arg);
|
||||||
static int set_filters(struct MPContext *mpctx, enum stream_type mediatype,
|
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;
|
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)
|
void run_command(MPContext *mpctx, mp_cmd_t *cmd)
|
||||||
{
|
{
|
||||||
struct MPOpts *opts = mpctx->opts;
|
struct MPOpts *opts = mpctx->opts;
|
||||||
@ -2775,6 +2892,19 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
|
|||||||
}
|
}
|
||||||
break;
|
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: {
|
case MP_CMD_COMMAND_LIST: {
|
||||||
for (struct mp_cmd *sub = cmd->args[0].v.p; sub; sub = sub->queue_next)
|
for (struct mp_cmd *sub = cmd->args[0].v.p; sub; sub = sub->queue_next)
|
||||||
run_command(mpctx, sub);
|
run_command(mpctx, sub);
|
||||||
@ -2802,13 +2932,16 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct command_ctx {
|
void command_uninit(struct MPContext *mpctx)
|
||||||
int events;
|
{
|
||||||
};
|
overlay_uninit(mpctx);
|
||||||
|
talloc_free(mpctx->command_ctx);
|
||||||
|
mpctx->command_ctx = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void command_init(struct MPContext *mpctx)
|
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.
|
// Notify that a property might have changed.
|
||||||
|
@ -23,6 +23,7 @@ struct MPContext;
|
|||||||
struct mp_cmd;
|
struct mp_cmd;
|
||||||
|
|
||||||
void command_init(struct MPContext *mpctx);
|
void command_init(struct MPContext *mpctx);
|
||||||
|
void command_uninit(struct MPContext *mpctx);
|
||||||
|
|
||||||
void run_command(struct MPContext *mpctx, struct mp_cmd *cmd);
|
void run_command(struct MPContext *mpctx, struct mp_cmd *cmd);
|
||||||
char *mp_property_expand_string(struct MPContext *mpctx, const char *str);
|
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_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}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -89,6 +89,9 @@ enum mp_command_type {
|
|||||||
/// Internal for Lua scripts
|
/// Internal for Lua scripts
|
||||||
MP_CMD_SCRIPT_DISPATCH,
|
MP_CMD_SCRIPT_DISPATCH,
|
||||||
|
|
||||||
|
MP_CMD_OVERLAY_ADD,
|
||||||
|
MP_CMD_OVERLAY_REMOVE,
|
||||||
|
|
||||||
// Internal
|
// Internal
|
||||||
MP_CMD_COMMAND_LIST, // list of sub-commands in args[0].v.p
|
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);
|
cocoa_set_input_context(NULL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
command_uninit(mpctx);
|
||||||
|
|
||||||
mp_input_uninit(mpctx->input);
|
mp_input_uninit(mpctx->input);
|
||||||
|
|
||||||
osd_free(mpctx->osd);
|
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_pts -= osd->video_offset - opts->sub_delay;
|
||||||
sub_get_bitmaps(osd->dec_sub, obj->vo_res, sub_pts, out_imgs);
|
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 {
|
} else {
|
||||||
osd_object_get_bitmaps(osd, obj, out_imgs);
|
osd_object_get_bitmaps(osd, obj, out_imgs);
|
||||||
}
|
}
|
||||||
|
@ -91,6 +91,7 @@ enum mp_osdtype {
|
|||||||
OSDTYPE_OSD,
|
OSDTYPE_OSD,
|
||||||
|
|
||||||
OSDTYPE_EXTERNAL,
|
OSDTYPE_EXTERNAL,
|
||||||
|
OSDTYPE_EXTERNAL2,
|
||||||
|
|
||||||
MAX_OSD_PARTS
|
MAX_OSD_PARTS
|
||||||
};
|
};
|
||||||
@ -144,6 +145,8 @@ struct osd_state {
|
|||||||
// OSDTYPE_EXTERNAL
|
// OSDTYPE_EXTERNAL
|
||||||
char *external;
|
char *external;
|
||||||
int external_res_x, external_res_y;
|
int external_res_x, external_res_y;
|
||||||
|
// OSDTYPE_EXTERNAL2
|
||||||
|
struct sub_bitmaps external2;
|
||||||
// OSDTYPE_SUB
|
// OSDTYPE_SUB
|
||||||
struct dec_sub *dec_sub;
|
struct dec_sub *dec_sub;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user