History

client API, lua: add new API for setting OSD overlays

Lua scripting has an undocumented mp.set_osd_ass() function, which is used by osc.lua and console.lua. Apparently, 3rd party scripts also use this. It's probably time to make this a public API. The Lua implementation just bypassed the libmpv API. To make it usable by any type of client, turn it into a command, "osd-overlay". There's already a "overlay-add". Ignore it (although the manpage admits guiltiness). I don't really want to deal with that old command. Its main problem is that it uses global IDs, while I'd like to avoid that scripts mess with each others overlays (whether that is accidentally or intentionally). Maybe "overlay-add" can eventually be merged into "osd-overlay", but I'm too lazy to do that now. Scripting now uses the commands. There is a helper to manage OSD overlays. The helper is very "thin"; I only want to force script authors to use the ID allocation, which may help with putting multiple scripts into a single .lua file without causing conflicts (basically, avoiding singletons within a script's environment). The old set_osd_ass() is emulated with the new API. The JS scripting wrapper also provides a set_osd_ass() function, which calls internal mpv API. Comment that part (to keep it compiling), but I'm leaving it to @avih to finish the change.

wm4 committed 6 years ago

Commit 07287261 parent 96932fe11 files · +292 −68Browse files

Unified · Split

11 files
  1. DOCS/man/input.rst +80 −0
  2. DOCS/man/lua.rst +35 −0
  3. player/client.c +1 −1
  4. player/command.c +32 −0
  5. player/javascript.c +1 −1
  6. player/lua.c +0 −27
  7. player/lua/defaults.lua +59 −0
  8. sub/osd.h +11 −2
  9. sub/osd_dummy.c +5 −2
  10. sub/osd_libass.c +66 −31
  11. sub/osd_state.h +2 −4
DOCS/man/input.rst+80−0
@@ -937,6 +937,86 @@ Input Commands that are Possibly Subject to Change · 936 unmodified lines
937937 Remove an overlay added with ``overlay-add`` and the same ID. Does nothing
938938 if no overlay with this ID exists.
939939
940+``osd-overlay``
941+ Add/update/remove an OSD overlay.
942+
943+ (Although this sounds similar to ``overlay-add``, ``osd-overlay`` is for
944+ text overlays, while ``overlay-add`` is for bitmaps. Maybe ``overlay-add``
945+ will be merged into ``osd-overlay`` to remove this oddity.)
946+
947+ You can use this to add text overlays in ASS format. ASS has advanced
948+ positioning and rendering tags, which can be used to render almost any kind
949+ of vector graphics.
950+
951+ This command accepts the following parameters:
952+
953+ ``id``
954+ Arbitrary integer that identifies the overlay. Multiple overlays can be
955+ added by calling this command with different ``id`` parameters. Calling
956+ this command with the same ``id`` replaces the previously set overlay.
957+
958+ There is a separate namespace for each libmpv client (i.e. IPC
959+ connection, script), so IDs can be made up and assigned by the API user
960+ without conflicting with other API users.
961+
962+ If the libmpv client is destroyed, all overlays associated with it are
963+ also deleted. In particular, connecting via ``--input-ipc-server``,
964+ adding an overlay, and disconnecting will remove the overlay immediately
965+ again.
966+
967+ ``format``
968+ String that gives the type of the overlay. Accepts the following values:
969+
970+ ``ass-events``
971+ The ``data`` parameter is a string. The string is split on the
972+ newline character. Every line is turned into the ``Text`` part of
973+ a ``Dialogue`` ASS event. Timing is unused (but behavior of timing
974+ dependent ASS tags may change in future mpv versions).
975+
976+ Note that it's better to put multiple lines into ``data``, instead
977+ of adding multiple OSD overlays.
978+
979+ This provides 2 ASS ``Styles``. ``OSD`` contains the text style as
980+ defined by the current ``--osd-...`` options. ``Default`` is
981+ similar, and contains style that ``OSD`` would have if all options
982+ were set to the default.
983+
984+ In addition, the ``res_x`` and ``res_y`` options specify the value
985+ of the ASS ``PlayResX`` and ``PlayResY`` header fields. If ``res_y``
986+ is set to 0, ``PlayResY`` is initialized to an arbitrary default
987+ value (but note that the default for this command is 720, not 0).
988+ If ``res_x`` is set to 0, ``PlayResX`` is set based on ``res_y``
989+ such that a virtual ASS pixel has a square pixel aspect ratio.
990+
991+ ``none``
992+ Special value that causes the overlay to be removed. Most parameters
993+ other than ``id`` and ``format`` are mostly ignored.
994+
995+ ``data``
996+ String defining the overlay contents according to the ``format``
997+ parameter.
998+
999+ ``res_x``, ``res_y``
1000+ Used if ``format`` is set to ``ass-events`` (see description there).
1001+ Optional, defaults to 0/720.
1002+
1003+ ``z``
1004+ The Z order of the overlay. Optional, defaults to 0.
1005+
1006+ Note that Z order between different overlays of different formats is
1007+ static, and cannot be changed (currently, this means that bitmap
1008+ overlays added by ``overlay-add`` are always on top of the ASS overlays
1009+ added by ``osd-overlay``). In addition, the builtin OSD components are
1010+ always below any of the custom OSD. (This includes subtitles of any kind
1011+ as well as text rendered by ``show-text``.)
1012+
1013+ It's possible that future mpv versions will randomly change how Z order
1014+ between different OSD formats and builtin OSD is handled.
1015+
1016+ Note: always use named arguments (``mpv_command_node()``). Scripts should
1017+ use the ``mp.create_osd_overlay()`` helper instead of invoking this command
1018+ directly.
1019+
9401020 ``script-message [<arg1> [<arg2> [...]]]``
9411021 Send a message to all clients, and pass it the following list of arguments.
9421022 What this message means, how many arguments it takes, and what the arguments
DOCS/man/lua.rst+35−0
@@ -536,6 +536,41 @@ are useful only in special situations. · 535 unmodified lines
536536 Undo a previous registration with ``mp.register_script_message``. Does
537537 nothing if the ``name`` wasn't registered.
538538
539+``mp.create_osd_overlay(format)``
540+ Create an OSD overlay. This is a very thin wrapper around the ``osd-overlay``
541+ command. The function returns a table, which mostly contains fields that
542+ will be passed to ``osd-overlay``. The ``format`` parameter is used to
543+ initialize the ``format`` field. The ``data`` field contains the text to
544+ be used as overlay. For details, see the ``osd-overlay`` command.
545+
546+ In addition, it provides the following methods:
547+
548+ ``update()``
549+ Commit the OSD overlay to the screen, or in other words, run the
550+ ``osd-overlay`` command with the current fields of the overlay table.
551+
552+ ``remove()``
553+ Remove the overlay from the screen. A ``update()`` call will add it
554+ again.
555+
556+ Example:
557+
558+ ::
559+
560+ ov = mp.create_osd_overlay("ass-events")
561+ ov.data = "{\\an5}{\\b1}hello world!"
562+ ov:update()
563+
564+ The advantage of using this wrapper (as opposed to running ``osd-overlay``
565+ directly) is that the ``id`` field is allocated automatically.
566+
567+``mp.get_osd_size()``
568+ Returns a tuple of ``osd_width, osd_height, osd_par``. The first two give
569+ the size of the OSD in pixels (for video ouputs like ``--vo=xv``, this may
570+ be "scaled" pixels). The third is the display pixel aspect ratio.
571+
572+ May return invalid/nonsense values if OSD is not initialized yet.
573+
539574 mp.msg functions
540575 ----------------
541576
player/client.c+1−1
@@ -457,7 +457,7 @@ static void mp_destroy_client(mpv_handle *ctx, bool terminate) · 456 unmodified lines
457457 // causes a crash, block until all asynchronous requests were served.
458458 mpv_wait_async_requests(ctx);
459459
460− osd_set_external(mpctx->osd, ctx, 0, 0, NULL);
460+ osd_set_external_remove_owner(mpctx->osd, ctx);
461461 mp_input_remove_sections_by_owner(mpctx->input, ctx->name);
462462
463463 pthread_mutex_lock(&clients->lock);
player/command.c+32−0
@@ -4085,6 +4085,25 @@ static void overlay_uninit(struct MPContext *mpctx) · 4084 unmodified lines
40854085 mp_image_unrefp(&cmd->overlay_osd[n].packed);
40864086 }
40874087
4088+static void cmd_osd_overlay(void *p)
4089+{
4090+ struct mp_cmd_ctx *cmd = p;
4091+ struct MPContext *mpctx = cmd->mpctx;
4092+
4093+ struct osd_external_ass ov = {
4094+ .owner = cmd->cmd->sender,
4095+ .id = cmd->args[0].v.i64,
4096+ .format = cmd->args[1].v.i,
4097+ .data = cmd->args[2].v.s,
4098+ .res_x = cmd->args[3].v.i,
4099+ .res_y = cmd->args[4].v.i,
4100+ .z = cmd->args[5].v.i,
4101+ };
4102+
4103+ osd_set_external(mpctx->osd, &ov);
4104+ mp_wakeup_core(mpctx);
4105+}
4106+
40884107 static struct track *find_track_with_url(struct MPContext *mpctx, int type,
40894108 const char *url)
40904109 {
@@ -5877,6 +5896,19 @@ const struct mp_cmd_def mp_cmds[] = { · 1786 unmodified lines
58775896 OPT_INT("stride", v.i, 0), }},
58785897 { "overlay-remove", cmd_overlay_remove, { OPT_INT("id", v.i, 0) } },
58795898
5899+ { "osd-overlay", cmd_osd_overlay,
5900+ {
5901+ OPT_INT64("id", v.i64, 0),
5902+ OPT_CHOICE("format", v.i, 0, ({"none", 0},
5903+ {"ass-events", 1})),
5904+ OPT_STRING("data", v.s, 0),
5905+ OPT_INT("res_x", v.i, 0, OPTDEF_INT(0)),
5906+ OPT_INT("res_y", v.i, 0, OPTDEF_INT(720)),
5907+ OPT_INT("z", v.i, 0, OPTDEF_INT(0)),
5908+ },
5909+ .is_noisy = true,
5910+ },
5911+
58805912 { "write-watch-later-config", cmd_write_watch_later_config },
58815913
58825914 { "hook-add", cmd_hook_add, { OPT_STRING("arg0", v.s, 0),
player/javascript.c+1−1
@@ -738,7 +738,7 @@ static void script_set_osd_ass(js_State *J) · 737 unmodified lines
738738 int res_x = jsL_checkint(J, 1);
739739 int res_y = jsL_checkint(J, 2);
740740 const char *text = js_tostring(J, 3);
741− osd_set_external(ctx->mpctx->osd, ctx->client, res_x, res_y, (char *)text);
741+ //osd_set_external(ctx->mpctx->osd, ctx->client, res_x, res_y, (char *)text);
742742 mp_wakeup_core(ctx->mpctx);
743743 push_success(J);
744744 }
player/lua.c+0−27
@@ -998,31 +998,6 @@ static int script_raw_abort_async_command(lua_State *L) · 997 unmodified lines
998998 return 0;
999999 }
10001000
1001−static int script_set_osd_ass(lua_State *L)
1002−{
1003− struct script_ctx *ctx = get_ctx(L);
1004− int res_x = luaL_checkinteger(L, 1);
1005− int res_y = luaL_checkinteger(L, 2);
1006− const char *text = luaL_checkstring(L, 3);
1007− if (!text[0])
1008− text = " "; // force external OSD initialization
1009− osd_set_external(ctx->mpctx->osd, ctx->client, res_x, res_y, (char *)text);
1010− mp_wakeup_core(ctx->mpctx);
1011− return 0;
1012−}
1013−
1014−static int script_get_osd_size(lua_State *L)
1015−{
1016− struct MPContext *mpctx = get_mpctx(L);
1017− struct mp_osd_res vo_res = osd_get_vo_res(mpctx->osd);
1018− double aspect = 1.0 * vo_res.w / MPMAX(vo_res.h, 1) /
1019− (vo_res.display_par ? vo_res.display_par : 1);
1020− lua_pushnumber(L, vo_res.w);
1021− lua_pushnumber(L, vo_res.h);
1022− lua_pushnumber(L, aspect);
1023− return 3;
1024−}
1025−
10261001 static int script_get_osd_margins(lua_State *L)
10271002 {
10281003 struct MPContext *mpctx = get_mpctx(L);
@@ -1275,8 +1250,6 @@ static const struct fn_entry main_fns[] = { · 246 unmodified lines
12751250 FN_ENTRY(set_property_native),
12761251 FN_ENTRY(raw_observe_property),
12771252 FN_ENTRY(raw_unobserve_property),
1278− FN_ENTRY(set_osd_ass),
1279− FN_ENTRY(get_osd_size),
12801253 FN_ENTRY(get_osd_margins),
12811254 FN_ENTRY(get_mouse_pos),
12821255 FN_ENTRY(get_time),
player/lua/defaults.lua+59−0
@@ -589,6 +589,65 @@ function mp.abort_async_command(t) · 588 unmodified lines
589589 end
590590 end
591591
592+local overlay_mt = {}
593+overlay_mt.__index = overlay_mt
594+local overlay_new_id = 0
595+
596+function mp.create_osd_overlay(format)
597+ overlay_new_id = overlay_new_id + 1
598+ local overlay = {
599+ format = format,
600+ id = overlay_new_id,
601+ data = "",
602+ res_x = 0,
603+ res_y = 720,
604+ }
605+ setmetatable(overlay, overlay_mt)
606+ return overlay
607+end
608+
609+function overlay_mt.update(ov)
610+ local cmd = {}
611+ for k, v in pairs(ov) do
612+ cmd[k] = v
613+ end
614+ cmd.name = "osd-overlay"
615+ mp.command_native(cmd)
616+end
617+
618+function overlay_mt.remove(ov)
619+ mp.command_native {
620+ name = "osd-overlay",
621+ id = ov.id,
622+ format = "none",
623+ data = "",
624+ }
625+end
626+
627+-- legacy API
628+function mp.set_osd_ass(res_x, res_y, data)
629+ if not mp._legacy_overlay then
630+ mp._legacy_overlay = mp.create_osd_overlay("ass-events")
631+ end
632+ mp._legacy_overlay.res_x = res_x
633+ mp._legacy_overlay.res_y = res_y
634+ mp._legacy_overlay.data = data
635+ mp._legacy_overlay:update()
636+end
637+
638+function mp.get_osd_size()
639+ local w = mp.get_property_number("osd-width", 0)
640+ local h = mp.get_property_number("osd-height", 0)
641+ local par = mp.get_property_number("osd-par", 0)
642+ if par == 0 then
643+ par = 1
644+ end
645+
646+ local aspect = 1.0 * w / math.max(h) / par
647+ return w, h, aspect
648+end
649+
650+
592651 local mp_utils = package.loaded["mp.utils"]
593652
594653 function mp_utils.format_table(t, set)
sub/osd.h+11−2
@@ -202,9 +202,18 @@ struct mp_osd_res osd_get_vo_res(struct osd_state *osd); · 201 unmodified lines
202202 void osd_rescale_bitmaps(struct sub_bitmaps *imgs, int frame_w, int frame_h,
203203 struct mp_osd_res res, double compensate_par);
204204
205+struct osd_external_ass {
206+ void *owner; // unique pointer (NULL is also allowed)
207+ int64_t id;
208+ int format;
209+ char *data;
210+ int res_x, res_y;
211+ int z;
212+};
213+
205214 // defined in osd_libass.c and osd_dummy.c
206−void osd_set_external(struct osd_state *osd, void *id, int res_x, int res_y,
207− char *text);
215+void osd_set_external(struct osd_state *osd, struct osd_external_ass *ov);
216+void osd_set_external_remove_owner(struct osd_state *osd, void *owner);
208217 void osd_get_text_size(struct osd_state *osd, int *out_screen_h, int *out_font_h);
209218 void osd_get_function_sym(char *buffer, size_t buffer_size, int osd_function);
210219
sub/osd_dummy.c+5−2
@@ -24,8 +24,11 @@ void osd_object_get_bitmaps(struct osd_state *osd, struct osd_object *obj, · 23 unmodified lines
2424 *out_imgs = (struct sub_bitmaps) {0};
2525 }
2626
27−void osd_set_external(struct osd_state *osd, void *id, int res_x, int res_y,
28− char *text)
27+void osd_set_external(struct osd_state *osd, struct osd_external_ass *ov)
28+{
29+}
30+
31+void osd_set_external_remove_owner(struct osd_state *osd, void *owner)
2932 {
3033 }
3134
sub/osd_libass.c+66−31
@@ -80,8 +80,8 @@ static void destroy_ass_renderer(struct ass_state *ass) · 79 unmodified lines
8080
8181 static void destroy_external(struct osd_external *ext)
8282 {
83− talloc_free(ext->text);
8483 destroy_ass_renderer(&ext->ass);
84+ talloc_free(ext);
8585 }
8686
8787 void osd_destroy_backend(struct osd_state *osd)
@@ -90,7 +90,7 @@ void osd_destroy_backend(struct osd_state *osd) · 2 unmodified lines
9090 struct osd_object *obj = osd->objs[n];
9191 destroy_ass_renderer(&obj->ass);
9292 for (int i = 0; i < obj->num_externals; i++)
93− destroy_external(&obj->externals[i]);
93+ destroy_external(obj->externals[i]);
9494 obj->num_externals = 0;
9595 }
9696 }
@@ -470,11 +470,9 @@ static void update_osd(struct osd_state *osd, struct osd_object *obj) · 373 unmodified lines
470470 static void update_external(struct osd_state *osd, struct osd_object *obj,
471471 struct osd_external *ext)
472472 {
473− bstr t = bstr0(ext->text);
474− if (!t.len)
475− return;
476− ext->ass.res_x = ext->res_x;
477− ext->ass.res_y = ext->res_y;
473+ bstr t = bstr0(ext->ov.data);
474+ ext->ass.res_x = ext->ov.res_x;
475+ ext->ass.res_y = ext->ov.res_y;
478476 create_ass_track(osd, obj, &ext->ass);
479477
480478 clear_ass(&ext->ass);
@@ -497,29 +495,42 @@ static void update_external(struct osd_state *osd, struct osd_object *obj, · 16 unmodified lines
497495 }
498496 }
499497
500−void osd_set_external(struct osd_state *osd, void *id, int res_x, int res_y,
501− char *text)
498+static int cmp_zorder(const void *pa, const void *pb)
499+{
500+ const struct osd_external *a = *(struct osd_external **)pa;
501+ const struct osd_external *b = *(struct osd_external **)pb;
502+ return a->ov.z == b->ov.z ? 0 : (a->ov.z > b->ov.z ? 1 : -1);
503+}
504+
505+void osd_set_external(struct osd_state *osd, struct osd_external_ass *ov)
502506 {
503507 pthread_mutex_lock(&osd->lock);
504508 struct osd_object *obj = osd->objs[OSDTYPE_EXTERNAL];
505− struct osd_external *entry = 0;
509+ bool zorder_changed = false;
510+ int index = -1;
511+
506512 for (int n = 0; n < obj->num_externals; n++) {
507− if (obj->externals[n].id == id) {
508− entry = &obj->externals[n];
513+ struct osd_external *e = obj->externals[n];
514+ if (e->ov.id == ov->id && e->ov.owner == ov->owner) {
515+ index = n;
509516 break;
510517 }
511518 }
512− if (!entry && !text)
513− goto done;
514519
515− if (!entry) {
516− struct osd_external new = { .id = id };
520+ if (index < 0) {
521+ if (!ov->format)
522+ goto done;
523+ struct osd_external *new = talloc_zero(NULL, struct osd_external);
524+ new->ov.owner = ov->owner;
525+ new->ov.id = ov->id;
517526 MP_TARRAY_APPEND(obj, obj->externals, obj->num_externals, new);
518− entry = &obj->externals[obj->num_externals - 1];
527+ index = obj->num_externals - 1;
528+ zorder_changed = true;
519529 }
520530
521− if (!text) {
522− int index = entry - &obj->externals[0];
531+ struct osd_external *entry = obj->externals[index];
532+
533+ if (!ov->format) {
523534 destroy_external(entry);
524535 MP_TARRAY_REMOVE_AT(obj->externals, obj->num_externals, index);
525536 obj->changed = true;
@@ -527,22 +538,46 @@ void osd_set_external(struct osd_state *osd, void *id, int res_x, int res_y, · 1 unmodified line
527538 goto done;
528539 }
529540
530− if (!entry->text || strcmp(entry->text, text) != 0 ||
531− entry->res_x != res_x || entry->res_y != res_y)
532− {
533− talloc_free(entry->text);
534− entry->text = talloc_strdup(NULL, text);
535− entry->res_x = res_x;
536− entry->res_y = res_y;
537− update_external(osd, obj, entry);
538− obj->changed = true;
539− osd->want_redraw_notification = true;
541+ entry->ov.format = ov->format;
542+ if (!entry->ov.data)
543+ entry->ov.data = talloc_strdup(entry, "");
544+ entry->ov.data[0] = '\0'; // reuse memory allocation
545+ entry->ov.data = talloc_strdup_append(entry->ov.data, ov->data);
546+ entry->ov.res_x = ov->res_x;
547+ entry->ov.res_y = ov->res_y;
548+ zorder_changed |= entry->ov.z != ov->z;
549+ entry->ov.z = ov->z;
550+
551+ update_external(osd, obj, entry);
552+
553+ obj->changed = true;
554+ osd->want_redraw_notification = true;
555+
556+ if (zorder_changed) {
557+ qsort(obj->externals, obj->num_externals, sizeof(obj->externals[0]),
558+ cmp_zorder);
540559 }
541560
542561 done:
543562 pthread_mutex_unlock(&osd->lock);
544563 }
545564
565+void osd_set_external_remove_owner(struct osd_state *osd, void *owner)
566+{
567+ pthread_mutex_lock(&osd->lock);
568+ struct osd_object *obj = osd->objs[OSDTYPE_EXTERNAL];
569+ for (int n = obj->num_externals - 1; n >= 0; n--) {
570+ struct osd_external *e = obj->externals[n];
571+ if (e->ov.owner == owner) {
572+ destroy_external(e);
573+ MP_TARRAY_REMOVE_AT(obj->externals, obj->num_externals, n);
574+ obj->changed = true;
575+ osd->want_redraw_notification = true;
576+ }
577+ }
578+ pthread_mutex_unlock(&osd->lock);
579+}
580+
546581 static void append_ass(struct ass_state *ass, struct mp_osd_res *res,
547582 ASS_Image **img_list, bool *changed)
548583 {
@@ -574,8 +609,8 @@ void osd_object_get_bitmaps(struct osd_state *osd, struct osd_object *obj, · 25 unmodified lines
574609
575610 append_ass(&obj->ass, &obj->vo_res, &obj->ass_imgs[0], &obj->changed);
576611 for (int n = 0; n < obj->num_externals; n++) {
577− append_ass(&obj->externals[n].ass, &obj->vo_res, &obj->ass_imgs[n + 1],
578− &obj->changed);
612+ append_ass(&obj->externals[n]->ass, &obj->vo_res,
613+ &obj->ass_imgs[n + 1], &obj->changed);
579614 }
580615
581616 mp_ass_packer_pack(obj->ass_packer, obj->ass_imgs, obj->num_externals + 1,
sub/osd_state.h+2−4
@@ -38,7 +38,7 @@ struct osd_object { · 37 unmodified lines
3838 struct dec_sub *sub;
3939
4040 // OSDTYPE_EXTERNAL
41− struct osd_external *externals;
41+ struct osd_external **externals;
4242 int num_externals;
4343
4444 // OSDTYPE_EXTERNAL2
@@ -56,9 +56,7 @@ struct osd_object { · 11 unmodified lines
5656 };
5757
5858 struct osd_external {
59− void *id;
60− char *text;
61− int res_x, res_y;
59+ struct osd_external_ass ov;
6260 struct ass_state ass;
6361 };
6462

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t