osd, lua: manage multiple ASS overlays set with set_osd_ass() calls

Until now, there was only 1 global ASS overlay that could be set by all
scripts. This was often perceived as bug when multiple scripts tried to
set their own ASS overlay.

This was kind of hard to solve because the script could set its own ASS
PlayResX/Y, which makes it impossible to share a single ASS_Renderer for
multiple scripts. The OSC unfortunately makes use of this feature (and
unfortunately can't be fixed because it's a POS), so we're stuck with
this complication.

Implement the worst-case solution and fix this by creating separate ASS
track and renderer objects for each script that wants to set an ASS
overlay.

The z-order is decided by the order the scripts set their text first.
This is essentially random, unless you do it at script init, and you
pass scripts in a specific order. Script initialization is currently
serialized (as a feature), so the first loaded script gets lowest
Z-order.

The Lua script API interestingly remains the same. (And also will remain
undocumented, unsupported, and potentially volatile.)
This commit is contained in:
wm4 2016-03-08 21:42:08 +01:00
parent ed254f29a9
commit 75a36662cb
6 changed files with 87 additions and 44 deletions

View File

@ -391,6 +391,7 @@ static int load_lua(struct mpv_handle *client, const char *fname)
r = 0;
error_out:
osd_set_external(ctx->mpctx->osd, client, 0, 0, NULL); // remove overlay
mp_resume_all(client);
if (ctx->state)
lua_close(ctx->state);
@ -963,14 +964,14 @@ static int script_command_native(lua_State *L)
static int script_set_osd_ass(lua_State *L)
{
struct MPContext *mpctx = get_mpctx(L);
struct script_ctx *ctx = get_ctx(L);
int res_x = luaL_checkinteger(L, 1);
int res_y = luaL_checkinteger(L, 2);
const char *text = luaL_checkstring(L, 3);
if (!text[0])
text = " "; // force external OSD initialization
osd_set_external(mpctx->osd, res_x, res_y, (char *)text);
mp_input_wakeup(mpctx->input);
osd_set_external(ctx->mpctx->osd, ctx->client, res_x, res_y, (char *)text);
mp_input_wakeup(ctx->mpctx->input);
return 0;
}

View File

@ -145,7 +145,7 @@ void osd_free(struct osd_state *osd)
talloc_free(osd);
}
static void osd_changed_unlocked(struct osd_state *osd, int obj)
void osd_changed_unlocked(struct osd_state *osd, int obj)
{
osd->objs[obj]->force_redraw = true;
osd->want_redraw = true;
@ -201,23 +201,6 @@ void osd_set_progbar(struct osd_state *osd, struct osd_progbar_state *s)
pthread_mutex_unlock(&osd->lock);
}
void osd_set_external(struct osd_state *osd, int res_x, int res_y, char *text)
{
pthread_mutex_lock(&osd->lock);
struct osd_object *osd_obj = osd->objs[OSDTYPE_EXTERNAL];
if (strcmp(osd_obj->text, text) != 0 ||
osd_obj->external_res_x != res_x ||
osd_obj->external_res_y != res_y)
{
talloc_free(osd_obj->text);
osd_obj->text = talloc_strdup(osd_obj, text);
osd_obj->external_res_x = res_x;
osd_obj->external_res_y = res_y;
osd_changed_unlocked(osd, osd_obj->type);
}
pthread_mutex_unlock(&osd->lock);
}
void osd_set_external2(struct osd_state *osd, struct sub_bitmaps *imgs)
{
pthread_mutex_lock(&osd->lock);

View File

@ -163,8 +163,6 @@ struct osd_progbar_state {
};
void osd_set_progbar(struct osd_state *osd, struct osd_progbar_state *s);
void osd_set_external(struct osd_state *osd, int res_x, int res_y, char *text);
void osd_set_external2(struct osd_state *osd, struct sub_bitmaps *imgs);
enum mp_osd_draw_flags {
@ -203,6 +201,9 @@ void osd_object_get_bitmaps(struct osd_state *osd, struct osd_object *obj,
void osd_init_backend(struct osd_state *osd);
void osd_destroy_backend(struct osd_state *osd);
void osd_set_external(struct osd_state *osd, void *id, int res_x, int res_y,
char *text);
// doesn't need locking
void osd_get_function_sym(char *buffer, size_t buffer_size, int osd_function);
extern const char *const osd_ass_0;

View File

@ -26,3 +26,8 @@ void osd_object_get_bitmaps(struct osd_state *osd, struct osd_object *obj,
{
*out_imgs = (struct sub_bitmaps) {0};
}
void osd_set_external(struct osd_state *osd, char *id, int res_x, int res_y,
char *text)
{
}

View File

@ -79,12 +79,21 @@ static void destroy_ass_renderer(struct ass_state *ass)
ass->log = NULL;
}
static void destroy_external(struct osd_external *ext)
{
talloc_free(ext->text);
destroy_ass_renderer(&ext->ass);
}
void osd_destroy_backend(struct osd_state *osd)
{
for (int n = 0; n < MAX_OSD_PARTS; n++) {
struct osd_object *obj = osd->objs[n];
destroy_ass_renderer(&obj->ass);
talloc_free(obj->parts_cache.parts);
for (int i = 0; i < obj->num_externals; i++)
destroy_external(&obj->externals[i]);
obj->num_externals = 0;
}
}
@ -431,44 +440,76 @@ static void update_osd(struct osd_state *osd, struct osd_object *obj)
update_progbar(osd, obj);
}
static void update_external(struct osd_state *osd, struct osd_object *obj)
static void update_external(struct osd_state *osd, struct osd_object *obj,
struct osd_external *ext)
{
bstr t = bstr0(obj->text);
bstr t = bstr0(ext->text);
if (!t.len)
return;
create_ass_track(osd, obj, &obj->ass, obj->external_res_x,
obj->external_res_y);
create_ass_track(osd, obj, &ext->ass, ext->res_x, ext->res_y);
clear_ass(&obj->ass);
clear_ass(&ext->ass);
int resy = obj->ass.track->PlayResY;
mp_ass_set_style(get_style(&obj->ass, "OSD"), resy, osd->opts->osd_style);
int resy = ext->ass.track->PlayResY;
mp_ass_set_style(get_style(&ext->ass, "OSD"), resy, osd->opts->osd_style);
// Some scripts will reference this style name with \r tags.
const struct osd_style_opts *def = osd_style_conf.defaults;
mp_ass_set_style(get_style(&obj->ass, "Default"), resy, def);
mp_ass_set_style(get_style(&ext->ass, "Default"), resy, def);
while (t.len) {
bstr line;
bstr_split_tok(t, "\n", &line, &t);
if (line.len) {
char *tmp = bstrdup0(NULL, line);
add_osd_ass_event(obj->ass.track, "OSD", tmp);
add_osd_ass_event(ext->ass.track, "OSD", tmp);
talloc_free(tmp);
}
}
}
static void update_object(struct osd_state *osd, struct osd_object *obj)
void osd_set_external(struct osd_state *osd, void *id, int res_x, int res_y,
char *text)
{
switch (obj->type) {
case OSDTYPE_OSD:
update_osd(osd, obj);
break;
case OSDTYPE_EXTERNAL:
update_external(osd, obj);
break;
pthread_mutex_lock(&osd->lock);
struct osd_object *obj = osd->objs[OSDTYPE_EXTERNAL];
struct osd_external *entry = 0;
for (int n = 0; n < obj->num_externals; n++) {
if (obj->externals[n].id == id) {
entry = &obj->externals[n];
break;
}
}
if (!entry && !text)
goto done;
if (!entry) {
struct osd_external new = { .id = id };
MP_TARRAY_APPEND(obj, obj->externals, obj->num_externals, new);
entry = &obj->externals[obj->num_externals - 1];
}
if (!text) {
int index = entry - &obj->externals[0];
destroy_external(entry);
MP_TARRAY_REMOVE_AT(obj->externals, obj->num_externals, index);
goto done;
}
if (!entry->text || strcmp(entry->text, text) != 0 ||
entry->res_x != res_x || entry->res_y != res_y)
{
talloc_free(entry->text);
entry->text = talloc_strdup(NULL, text);
entry->res_x = res_x;
entry->res_y = res_y;
update_external(osd, obj, entry);
obj->parts_cache.change_id = 1;
osd_changed_unlocked(osd, obj->type);
}
done:
pthread_mutex_unlock(&osd->lock);
}
static void append_ass(struct ass_state *ass, struct mp_osd_res *res,
@ -485,10 +526,12 @@ static void append_ass(struct ass_state *ass, struct mp_osd_res *res,
void osd_object_get_bitmaps(struct osd_state *osd, struct osd_object *obj,
struct sub_bitmaps *out_imgs)
{
if (obj->force_redraw)
update_object(osd, obj);
if (obj->force_redraw && obj->type == OSDTYPE_OSD)
update_osd(osd, obj);
append_ass(&obj->ass, &obj->vo_res, &obj->parts_cache);
for (int n = 0; n < obj->num_externals; n++)
append_ass(&obj->externals[n].ass, &obj->vo_res, &obj->parts_cache);
*out_imgs = obj->parts_cache;

View File

@ -20,7 +20,7 @@ struct osd_object {
bool force_redraw;
// OSDTYPE_SUB/OSDTYPE_SUB2/OSDTYPE_OSD/OSDTYPE_EXTERNAL
// OSDTYPE_OSD
char *text;
// OSDTYPE_OSD
@ -30,7 +30,8 @@ struct osd_object {
struct dec_sub *sub;
// OSDTYPE_EXTERNAL
int external_res_x, external_res_y;
struct osd_external *externals;
int num_externals;
// OSDTYPE_EXTERNAL2
struct sub_bitmaps *external2;
@ -48,6 +49,13 @@ struct osd_object {
struct ass_state ass;
};
struct osd_external {
void *id;
char *text;
int res_x, res_y;
struct ass_state ass;
};
struct osd_state {
pthread_mutex_t lock;
@ -64,4 +72,6 @@ struct osd_state {
struct mp_draw_sub_cache *draw_cache;
};
void osd_changed_unlocked(struct osd_state *osd, int obj);
#endif