mirror of https://github.com/mpv-player/mpv
command: pack sub image data in overlay-add command
Working towards refcounted sub images, and also for removing bitmap packers from VOs. I'm not sure why we even have this overlay-add command. It was sort of "needed" before opengl-cb was introduced, and before Lua scripts could put ASS drawings on OSD without conflicting with the OSC. But now trying to use it doesn't make too much sense anymore. Still keep it because we're trying to be nice, but throw performance out of the window. Now image data is copied 2 more times before displaying it. This also makes using the command a bit simpler.
This commit is contained in:
parent
614efea3e6
commit
549a9ea6fa
|
@ -561,29 +561,20 @@ Input Commands that are Possibly Subject to Change
|
|||
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.)
|
||||
overlay should be aspect-compensated.
|
||||
|
||||
``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.)
|
||||
while reusing an ID will update it.
|
||||
|
||||
``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).
|
||||
or a filename. The file will be mapped into memory with ``mmap()``,
|
||||
copied, and unmapped before the command returns (changed in mpv 0.18.1).
|
||||
|
||||
It is also possible to pass a raw memory address for use as bitmap memory
|
||||
by passing a memory address as integer prefixed with an ``&`` character.
|
||||
|
@ -616,15 +607,14 @@ Input Commands that are Possibly Subject to Change
|
|||
(Technically, the minimum size would be ``stride * (h - 1) + w * 4``, but
|
||||
for simplicity, the player will access all ``stride * h`` bytes.)
|
||||
|
||||
.. admonition:: Warning
|
||||
.. note::
|
||||
|
||||
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.
|
||||
Before mpv 0.18.1, you had to do manual "double buffering" when updating
|
||||
an overlay by replacing it with a different memory buffer. Since mpv
|
||||
0.18.1, the memory is simply copied and doesn't reference any of the
|
||||
memory indicated by the command's arguments after the commend returns.
|
||||
If you want to use this command before mpv 0.18.1, reads the old docs
|
||||
to see how to handle this correctly.
|
||||
|
||||
``overlay-remove <id>``
|
||||
Remove an overlay added with ``overlay-add`` and the same ID. Does nothing
|
||||
|
|
131
player/command.c
131
player/command.c
|
@ -60,6 +60,7 @@
|
|||
#include "audio/filter/af.h"
|
||||
#include "video/decode/dec_video.h"
|
||||
#include "audio/decode/dec_audio.h"
|
||||
#include "video/out/bitmap_packer.h"
|
||||
#include "options/path.h"
|
||||
#include "screenshot.h"
|
||||
|
||||
|
@ -88,7 +89,8 @@ struct command_ctx {
|
|||
// One of these is in use by the OSD; the other one exists so that the
|
||||
// bitmap list can be manipulated without additional synchronization.
|
||||
struct sub_bitmaps overlay_osd[2];
|
||||
struct sub_bitmaps *overlay_osd_current;
|
||||
int overlay_osd_current;
|
||||
struct bitmap_packer *overlay_packer;
|
||||
|
||||
struct hook_handler **hooks;
|
||||
int num_hooks;
|
||||
|
@ -98,9 +100,8 @@ struct command_ctx {
|
|||
};
|
||||
|
||||
struct overlay {
|
||||
void *map_start;
|
||||
size_t map_size;
|
||||
struct sub_bitmap osd;
|
||||
struct mp_image *source;
|
||||
int x, y;
|
||||
};
|
||||
|
||||
struct hook_handler {
|
||||
|
@ -4314,20 +4315,85 @@ static int edit_filters_osd(struct MPContext *mpctx, enum stream_type mediatype,
|
|||
static void recreate_overlays(struct MPContext *mpctx)
|
||||
{
|
||||
struct command_ctx *cmd = mpctx->command_ctx;
|
||||
struct sub_bitmaps *new = &cmd->overlay_osd[0];
|
||||
if (new == cmd->overlay_osd_current)
|
||||
new += 1; // pick the unused one
|
||||
int overlay_next = !cmd->overlay_osd_current;
|
||||
struct sub_bitmaps *new = &cmd->overlay_osd[overlay_next];
|
||||
new->format = SUBBITMAP_RGBA;
|
||||
new->change_id = 1;
|
||||
// overlay array can have unused entries, but parts list must be "packed"
|
||||
|
||||
bool valid = false;
|
||||
|
||||
new->num_parts = 0;
|
||||
for (int n = 0; n < cmd->num_overlays; n++) {
|
||||
struct overlay *o = &cmd->overlays[n];
|
||||
if (o->osd.bitmap)
|
||||
MP_TARRAY_APPEND(cmd, new->parts, new->num_parts, o->osd);
|
||||
if (o->source) {
|
||||
struct mp_image *s = o->source;
|
||||
struct sub_bitmap b = {
|
||||
.bitmap = s->planes[0],
|
||||
.stride = s->stride[0],
|
||||
.w = s->w, .dw = s->w,
|
||||
.h = s->h, .dh = s->h,
|
||||
.x = o->x,
|
||||
.y = o->y,
|
||||
};
|
||||
MP_TARRAY_APPEND(cmd, new->parts, new->num_parts, b);
|
||||
}
|
||||
cmd->overlay_osd_current = new;
|
||||
osd_set_external2(mpctx->osd, cmd->overlay_osd_current);
|
||||
}
|
||||
|
||||
if (!cmd->overlay_packer)
|
||||
cmd->overlay_packer = talloc_zero(cmd, struct bitmap_packer);
|
||||
|
||||
cmd->overlay_packer->padding = 1; // assume bilinear scaling
|
||||
packer_set_size(cmd->overlay_packer, new->num_parts);
|
||||
|
||||
for (int n = 0; n < new->num_parts; n++)
|
||||
cmd->overlay_packer->in[n] = (struct pos){new->parts[n].w, new->parts[n].h};
|
||||
|
||||
if (packer_pack(cmd->overlay_packer) < 0 || new->num_parts == 0)
|
||||
goto done;
|
||||
|
||||
struct pos bb[2];
|
||||
packer_get_bb(cmd->overlay_packer, bb);
|
||||
|
||||
new->packed_w = bb[1].x;
|
||||
new->packed_h = bb[1].y;
|
||||
|
||||
if (!new->packed || new->packed->w < new->packed_w ||
|
||||
new->packed->h < new->packed_h)
|
||||
{
|
||||
talloc_free(new->packed);
|
||||
new->packed = mp_image_alloc(IMGFMT_BGRA, cmd->overlay_packer->w,
|
||||
cmd->overlay_packer->h);
|
||||
if (!new->packed)
|
||||
goto done;
|
||||
}
|
||||
|
||||
// clear padding
|
||||
mp_image_clear(new->packed, 0, 0, new->packed->w, new->packed->h);
|
||||
|
||||
for (int n = 0; n < new->num_parts; n++) {
|
||||
struct sub_bitmap *b = &new->parts[n];
|
||||
struct pos pos = cmd->overlay_packer->result[n];
|
||||
|
||||
int stride = new->packed->stride[0];
|
||||
void *pdata = (uint8_t *)new->packed->planes[0] + pos.y * stride + pos.x * 4;
|
||||
memcpy_pic(pdata, b->bitmap, b->w * 4, b->h, stride, b->stride);
|
||||
|
||||
b->bitmap = pdata;
|
||||
b->stride = stride;
|
||||
|
||||
b->src_x = pos.x;
|
||||
b->src_y = pos.y;
|
||||
}
|
||||
|
||||
valid = true;
|
||||
done:
|
||||
if (!valid) {
|
||||
new->format = SUBBITMAP_EMPTY;
|
||||
new->num_parts = 0;
|
||||
}
|
||||
|
||||
osd_set_external2(mpctx->osd, new);
|
||||
cmd->overlay_osd_current = overlay_next;
|
||||
}
|
||||
|
||||
// Set overlay with the given ID to the contents as described by "new".
|
||||
|
@ -4342,17 +4408,11 @@ static void replace_overlay(struct MPContext *mpctx, int id, struct overlay *new
|
|||
}
|
||||
|
||||
struct overlay *ptr = &cmd->overlays[id];
|
||||
struct overlay old = *ptr;
|
||||
|
||||
if (!ptr->osd.bitmap && !new->osd.bitmap)
|
||||
return; // don't need to recreate or unmap
|
||||
|
||||
talloc_free(ptr->source);
|
||||
*ptr = *new;
|
||||
recreate_overlays(mpctx);
|
||||
|
||||
// Do this afterwards, so we never unmap while the OSD is using it.
|
||||
if (old.map_start && old.map_size)
|
||||
munmap(old.map_start, old.map_size);
|
||||
recreate_overlays(mpctx);
|
||||
}
|
||||
|
||||
static int overlay_add(struct MPContext *mpctx, int id, int x, int y,
|
||||
|
@ -4368,18 +4428,17 @@ static int overlay_add(struct MPContext *mpctx, int id, int x, int y,
|
|||
MP_ERR(mpctx, "overlay_add: invalid id %d\n", id);
|
||||
goto error;
|
||||
}
|
||||
if (w < 0 || h < 0 || stride < w * 4 || (stride % 4)) {
|
||||
if (w <= 0 || h <= 0 || stride < w * 4 || (stride % 4)) {
|
||||
MP_ERR(mpctx, "overlay_add: inconsistent parameters\n");
|
||||
goto error;
|
||||
}
|
||||
struct overlay overlay = {
|
||||
.osd = {
|
||||
.stride = stride,
|
||||
.x = x, .y = y,
|
||||
.w = w, .h = h,
|
||||
.dw = w, .dh = h,
|
||||
},
|
||||
.source = mp_image_alloc(IMGFMT_BGRA, w, h),
|
||||
.x = x,
|
||||
.y = y,
|
||||
};
|
||||
if (!overlay.source)
|
||||
goto error;
|
||||
int fd = -1;
|
||||
bool close_fd = true;
|
||||
void *p = NULL;
|
||||
|
@ -4398,21 +4457,25 @@ static int overlay_add(struct MPContext *mpctx, int id, int x, int y,
|
|||
} else {
|
||||
fd = open(file, O_RDONLY | O_BINARY | O_CLOEXEC);
|
||||
}
|
||||
int map_size = 0;
|
||||
if (fd >= 0) {
|
||||
overlay.map_size = offset + h * stride;
|
||||
void *m = mmap(NULL, overlay.map_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
map_size = offset + h * stride;
|
||||
void *m = mmap(NULL, map_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (close_fd)
|
||||
close(fd);
|
||||
if (m && m != MAP_FAILED) {
|
||||
overlay.map_start = m;
|
||||
if (m && m != MAP_FAILED)
|
||||
p = m;
|
||||
}
|
||||
}
|
||||
if (!p) {
|
||||
MP_ERR(mpctx, "overlay_add: could not open or map '%s'\n", file);
|
||||
talloc_free(overlay.source);
|
||||
goto error;
|
||||
}
|
||||
overlay.osd.bitmap = (char *)p + offset;
|
||||
memcpy_pic(overlay.source->planes[0], (char *)p + offset, w * 4, h,
|
||||
overlay.source->stride[0], stride);
|
||||
if (map_size)
|
||||
munmap(p, map_size);
|
||||
|
||||
replace_overlay(mpctx, id, &overlay);
|
||||
r = 0;
|
||||
error:
|
||||
|
@ -4434,6 +4497,8 @@ static void overlay_uninit(struct MPContext *mpctx)
|
|||
for (int id = 0; id < cmd->num_overlays; id++)
|
||||
overlay_remove(mpctx, id);
|
||||
osd_set_external2(mpctx->osd, NULL);
|
||||
for (int n = 0; n < 2; n++)
|
||||
mp_image_unrefp(&cmd->overlay_osd[n].packed);
|
||||
}
|
||||
|
||||
struct cycle_counter {
|
||||
|
|
Loading…
Reference in New Issue