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:
wm4 2016-07-01 19:39:04 +02:00
parent 614efea3e6
commit 549a9ea6fa
2 changed files with 109 additions and 54 deletions

View File

@ -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

View File

@ -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 {