summaryrefslogtreecommitdiffstats
path: root/sub/osd.c
diff options
context:
space:
mode:
Diffstat (limited to 'sub/osd.c')
-rw-r--r--sub/osd.c327
1 files changed, 212 insertions, 115 deletions
diff --git a/sub/osd.c b/sub/osd.c
index f7935e3772..d33aabdd53 100644
--- a/sub/osd.c
+++ b/sub/osd.c
@@ -32,6 +32,7 @@
#include "options/options.h"
#include "common/global.h"
#include "common/msg.h"
+#include "common/stats.h"
#include "player/client.h"
#include "player/command.h"
#include "osd.h"
@@ -44,28 +45,30 @@
#define OPT_BASE_STRUCT struct osd_style_opts
static const m_option_t style_opts[] = {
- OPT_STRING("font", font, 0),
- OPT_FLOATRANGE("font-size", font_size, 0, 1, 9000),
- OPT_COLOR("color", color, 0),
- OPT_COLOR("border-color", border_color, 0),
- OPT_COLOR("shadow-color", shadow_color, 0),
- OPT_COLOR("back-color", back_color, 0),
- OPT_FLOAT("border-size", border_size, 0),
- OPT_FLOAT("shadow-offset", shadow_offset, 0),
- OPT_FLOATRANGE("spacing", spacing, 0, -10, 10),
- OPT_INTRANGE("margin-x", margin_x, 0, 0, 300),
- OPT_INTRANGE("margin-y", margin_y, 0, 0, 600),
- OPT_CHOICE("align-x", align_x, 0,
- ({"left", -1}, {"center", 0}, {"right", +1})),
- OPT_CHOICE("align-y", align_y, 0,
- ({"top", -1}, {"center", 0}, {"bottom", +1})),
- OPT_FLOATRANGE("blur", blur, 0, 0, 20),
- OPT_FLAG("bold", bold, 0),
- OPT_FLAG("italic", italic, 0),
- OPT_CHOICE("justify", justify, 0,
- ({"auto", 0}, {"left", 1}, {"center", 2}, {"right", 3})),
- OPT_CHOICE("font-provider", font_provider, 0,
- ({"auto", 0}, {"none", 1}, {"fontconfig", 2})),
+ {"font", OPT_STRING(font)},
+ {"font-size", OPT_FLOAT(font_size), M_RANGE(1, 9000)},
+ {"color", OPT_COLOR(color)},
+ {"border-color", OPT_COLOR(border_color)},
+ {"shadow-color", OPT_COLOR(shadow_color)},
+ {"back-color", OPT_COLOR(back_color)},
+ {"border-size", OPT_FLOAT(border_size)},
+ {"shadow-offset", OPT_FLOAT(shadow_offset)},
+ {"spacing", OPT_FLOAT(spacing), M_RANGE(-10, 10)},
+ {"margin-x", OPT_INT(margin_x), M_RANGE(0, 300)},
+ {"margin-y", OPT_INT(margin_y), M_RANGE(0, 600)},
+ {"align-x", OPT_CHOICE(align_x,
+ {"left", -1}, {"center", 0}, {"right", +1})},
+ {"align-y", OPT_CHOICE(align_y,
+ {"top", -1}, {"center", 0}, {"bottom", +1})},
+ {"blur", OPT_FLOAT(blur), M_RANGE(0, 20)},
+ {"bold", OPT_BOOL(bold)},
+ {"italic", OPT_BOOL(italic)},
+ {"justify", OPT_CHOICE(justify,
+ {"auto", 0}, {"left", 1}, {"center", 2}, {"right", 3})},
+ {"font-provider", OPT_CHOICE(font_provider,
+ {"auto", 0}, {"none", 1}, {"fontconfig", 2}), .flags = UPDATE_SUB_HARD},
+ {"fonts-dir", OPT_STRING(fonts_dir),
+ .flags = M_OPT_FILE | UPDATE_SUB_HARD},
{0}
};
@@ -116,7 +119,7 @@ bool osd_res_equals(struct mp_osd_res a, struct mp_osd_res b)
struct osd_state *osd_create(struct mpv_global *global)
{
- assert(MAX_OSD_PARTS >= OSDTYPE_COUNT);
+ static_assert(MAX_OSD_PARTS >= OSDTYPE_COUNT, "");
struct osd_state *osd = talloc_zero(NULL, struct osd_state);
*osd = (struct osd_state) {
@@ -124,8 +127,9 @@ struct osd_state *osd_create(struct mpv_global *global)
.global = global,
.log = mp_log_new(osd, global->log, "osd"),
.force_video_pts = MP_NOPTS_VALUE,
+ .stats = stats_ctx_create(osd, global, "osd"),
};
- pthread_mutex_init(&osd->lock, NULL);
+ mp_mutex_init(&osd->lock);
osd->opts = osd->opts_cache->opts;
for (int n = 0; n < MAX_OSD_PARTS; n++) {
@@ -134,6 +138,7 @@ struct osd_state *osd_create(struct mpv_global *global)
.type = n,
.text = talloc_strdup(obj, ""),
.progbar_state = {.type = -1},
+ .vo_change_id = 1,
};
osd->objs[n] = obj;
}
@@ -150,13 +155,14 @@ void osd_free(struct osd_state *osd)
if (!osd)
return;
osd_destroy_backend(osd);
- pthread_mutex_destroy(&osd->lock);
+ talloc_free(osd->objs[OSDTYPE_EXTERNAL2]->external2);
+ mp_mutex_destroy(&osd->lock);
talloc_free(osd);
}
void osd_set_text(struct osd_state *osd, const char *text)
{
- pthread_mutex_lock(&osd->lock);
+ mp_mutex_lock(&osd->lock);
struct osd_object *osd_obj = osd->objs[OSDTYPE_OSD];
if (!text)
text = "";
@@ -166,54 +172,57 @@ void osd_set_text(struct osd_state *osd, const char *text)
osd_obj->osd_changed = true;
osd->want_redraw_notification = true;
}
- pthread_mutex_unlock(&osd->lock);
+ mp_mutex_unlock(&osd->lock);
}
void osd_set_sub(struct osd_state *osd, int index, struct dec_sub *dec_sub)
{
- pthread_mutex_lock(&osd->lock);
+ mp_mutex_lock(&osd->lock);
if (index >= 0 && index < 2) {
struct osd_object *obj = osd->objs[OSDTYPE_SUB + index];
obj->sub = dec_sub;
obj->vo_change_id += 1;
}
osd->want_redraw_notification = true;
- pthread_mutex_unlock(&osd->lock);
+ mp_mutex_unlock(&osd->lock);
}
bool osd_get_render_subs_in_filter(struct osd_state *osd)
{
- pthread_mutex_lock(&osd->lock);
+ mp_mutex_lock(&osd->lock);
bool r = osd->render_subs_in_filter;
- pthread_mutex_unlock(&osd->lock);
+ mp_mutex_unlock(&osd->lock);
return r;
}
void osd_set_render_subs_in_filter(struct osd_state *osd, bool s)
{
- pthread_mutex_lock(&osd->lock);
- osd->render_subs_in_filter = s;
- pthread_mutex_unlock(&osd->lock);
+ mp_mutex_lock(&osd->lock);
+ if (osd->render_subs_in_filter != s) {
+ osd->render_subs_in_filter = s;
+
+ int change_id = 0;
+ for (int n = 0; n < MAX_OSD_PARTS; n++)
+ change_id = MPMAX(change_id, osd->objs[n]->vo_change_id);
+ for (int n = 0; n < MAX_OSD_PARTS; n++)
+ osd->objs[n]->vo_change_id = change_id + 1;
+ }
+ mp_mutex_unlock(&osd->lock);
}
void osd_set_force_video_pts(struct osd_state *osd, double video_pts)
{
- pthread_mutex_lock(&osd->lock);
- osd->force_video_pts = video_pts;
- pthread_mutex_unlock(&osd->lock);
+ atomic_store(&osd->force_video_pts, video_pts);
}
double osd_get_force_video_pts(struct osd_state *osd)
{
- pthread_mutex_lock(&osd->lock);
- double pts = osd->force_video_pts;
- pthread_mutex_unlock(&osd->lock);
- return pts;
+ return atomic_load(&osd->force_video_pts);
}
void osd_set_progbar(struct osd_state *osd, struct osd_progbar_state *s)
{
- pthread_mutex_lock(&osd->lock);
+ mp_mutex_lock(&osd->lock);
struct osd_object *osd_obj = osd->objs[OSDTYPE_OSD];
osd_obj->progbar_state.type = s->type;
osd_obj->progbar_state.value = s->value;
@@ -225,16 +234,18 @@ void osd_set_progbar(struct osd_state *osd, struct osd_progbar_state *s)
}
osd_obj->osd_changed = true;
osd->want_redraw_notification = true;
- pthread_mutex_unlock(&osd->lock);
+ mp_mutex_unlock(&osd->lock);
}
void osd_set_external2(struct osd_state *osd, struct sub_bitmaps *imgs)
{
- pthread_mutex_lock(&osd->lock);
- osd->objs[OSDTYPE_EXTERNAL2]->external2 = imgs;
- osd->objs[OSDTYPE_EXTERNAL2]->vo_change_id += 1;
+ mp_mutex_lock(&osd->lock);
+ struct osd_object *obj = osd->objs[OSDTYPE_EXTERNAL2];
+ talloc_free(obj->external2);
+ obj->external2 = sub_bitmaps_copy(NULL, imgs);
+ obj->vo_change_id += 1;
osd->want_redraw_notification = true;
- pthread_mutex_unlock(&osd->lock);
+ mp_mutex_unlock(&osd->lock);
}
static void check_obj_resize(struct osd_state *osd, struct mp_osd_res res,
@@ -242,6 +253,7 @@ static void check_obj_resize(struct osd_state *osd, struct mp_osd_res res,
{
if (!osd_res_equals(res, obj->vo_res)) {
obj->vo_res = res;
+ obj->osd_changed = true;
mp_client_broadcast_event_external(osd->global->client_api,
MP_EVENT_WIN_RESIZE, NULL);
}
@@ -256,57 +268,74 @@ static void check_obj_resize(struct osd_state *osd, struct mp_osd_res res,
// Unnecessary for anything else.
void osd_resize(struct osd_state *osd, struct mp_osd_res res)
{
- pthread_mutex_lock(&osd->lock);
+ mp_mutex_lock(&osd->lock);
int types[] = {OSDTYPE_OSD, OSDTYPE_EXTERNAL, OSDTYPE_EXTERNAL2, -1};
for (int n = 0; types[n] >= 0; n++)
check_obj_resize(osd, res, osd->objs[types[n]]);
- pthread_mutex_unlock(&osd->lock);
+ mp_mutex_unlock(&osd->lock);
}
-static void render_object(struct osd_state *osd, struct osd_object *obj,
- struct mp_osd_res res, double video_pts,
- const bool sub_formats[SUBBITMAP_COUNT],
- struct sub_bitmaps *out_imgs)
+static struct sub_bitmaps *render_object(struct osd_state *osd,
+ struct osd_object *obj,
+ struct mp_osd_res osdres, double video_pts,
+ const bool sub_formats[SUBBITMAP_COUNT])
{
int format = SUBBITMAP_LIBASS;
if (!sub_formats[format] || osd->opts->force_rgba_osd)
- format = SUBBITMAP_RGBA;
+ format = SUBBITMAP_BGRA;
- *out_imgs = (struct sub_bitmaps) {0};
+ struct sub_bitmaps *res = NULL;
- check_obj_resize(osd, res, obj);
+ check_obj_resize(osd, osdres, obj);
- if (obj->type == OSDTYPE_SUB || obj->type == OSDTYPE_SUB2) {
- if (obj->sub)
- sub_get_bitmaps(obj->sub, obj->vo_res, format, video_pts, out_imgs);
+ if (obj->type == OSDTYPE_SUB) {
+ if (obj->sub && sub_is_primary_visible(obj->sub))
+ res = sub_get_bitmaps(obj->sub, obj->vo_res, format, video_pts);
+ } else if (obj->type == OSDTYPE_SUB2) {
+ if (obj->sub && sub_is_secondary_visible(obj->sub))
+ res = sub_get_bitmaps(obj->sub, obj->vo_res, format, video_pts);
} else if (obj->type == OSDTYPE_EXTERNAL2) {
if (obj->external2 && obj->external2->format) {
- *out_imgs = *obj->external2;
+ res = sub_bitmaps_copy(NULL, obj->external2); // need to be owner
obj->external2->change_id = 0;
}
} else {
- osd_object_get_bitmaps(osd, obj, format, out_imgs);
+ res = osd_object_get_bitmaps(osd, obj, format);
}
- obj->vo_change_id += out_imgs->change_id;
+ if (obj->vo_had_output != !!res) {
+ obj->vo_had_output = !!res;
+ obj->vo_change_id += 1;
+ }
- if (out_imgs->num_parts == 0)
- return;
+ if (res) {
+ obj->vo_change_id += res->change_id;
- out_imgs->render_index = obj->type;
- out_imgs->change_id = obj->vo_change_id;
+ res->render_index = obj->type;
+ res->change_id = obj->vo_change_id;
+ }
+
+ return res;
}
+// Render OSD to a list of bitmap and return it. The returned object is
+// refcounted. Typically you should hold it only for a short time, and then
+// release it.
// draw_flags is a bit field of OSD_DRAW_* constants
-void osd_draw(struct osd_state *osd, struct mp_osd_res res,
- double video_pts, int draw_flags,
- const bool formats[SUBBITMAP_COUNT],
- void (*cb)(void *ctx, struct sub_bitmaps *imgs), void *cb_ctx)
+struct sub_bitmap_list *osd_render(struct osd_state *osd, struct mp_osd_res res,
+ double video_pts, int draw_flags,
+ const bool formats[SUBBITMAP_COUNT])
{
- pthread_mutex_lock(&osd->lock);
+ mp_mutex_lock(&osd->lock);
+
+ struct sub_bitmap_list *list = talloc_zero(NULL, struct sub_bitmap_list);
+ list->change_id = 1;
+ list->w = res.w;
+ list->h = res.h;
- if (osd->force_video_pts != MP_NOPTS_VALUE)
- video_pts = osd->force_video_pts;
+ double force_video_pts = atomic_load(&osd->force_video_pts);
+ if (force_video_pts != MP_NOPTS_VALUE)
+ video_pts = force_video_pts;
if (draw_flags & OSD_DRAW_SUB_FILTER)
draw_flags |= OSD_DRAW_SUB_ONLY;
@@ -323,22 +352,28 @@ void osd_draw(struct osd_state *osd, struct mp_osd_res res,
if ((draw_flags & OSD_DRAW_OSD_ONLY) && obj->is_sub)
continue;
- if (obj->sub)
- sub_lock(obj->sub);
+ char *stat_type_render = obj->is_sub ? "sub-render" : "osd-render";
+ stats_time_start(osd->stats, stat_type_render);
+
+ struct sub_bitmaps *imgs =
+ render_object(osd, obj, res, video_pts, formats);
- struct sub_bitmaps imgs;
- render_object(osd, obj, res, video_pts, formats, &imgs);
- if (imgs.num_parts > 0) {
- if (formats[imgs.format]) {
- cb(cb_ctx, &imgs);
+ stats_time_end(osd->stats, stat_type_render);
+
+ if (imgs && imgs->num_parts > 0) {
+ if (formats[imgs->format]) {
+ talloc_steal(list, imgs);
+ MP_TARRAY_APPEND(list, list->items, list->num_items, imgs);
+ imgs = NULL;
} else {
MP_ERR(osd, "Can't render OSD part %d (format %d).\n",
- obj->type, imgs.format);
+ obj->type, imgs->format);
}
}
- if (obj->sub)
- sub_unlock(obj->sub);
+ list->change_id += obj->vo_change_id;
+
+ talloc_free(imgs);
}
// If this is called with OSD_DRAW_SUB_ONLY or OSD_DRAW_OSD_ONLY set, assume
@@ -348,36 +383,35 @@ void osd_draw(struct osd_state *osd, struct mp_osd_res res,
if (!(draw_flags & OSD_DRAW_SUB_FILTER))
osd->want_redraw_notification = false;
- pthread_mutex_unlock(&osd->lock);
+ mp_mutex_unlock(&osd->lock);
+ return list;
}
-struct draw_on_image_closure {
- struct osd_state *osd;
- struct mp_image *dest;
- struct mp_image_pool *pool;
- bool changed;
-};
-
-static void draw_on_image(void *ctx, struct sub_bitmaps *imgs)
+// Warning: this function should be considered legacy. Use osd_render() instead.
+void osd_draw(struct osd_state *osd, struct mp_osd_res res,
+ double video_pts, int draw_flags,
+ const bool formats[SUBBITMAP_COUNT],
+ void (*cb)(void *ctx, struct sub_bitmaps *imgs), void *cb_ctx)
{
- struct draw_on_image_closure *closure = ctx;
- struct osd_state *osd = closure->osd;
- if (!mp_image_pool_make_writeable(closure->pool, closure->dest))
- return; // on OOM, skip
- mp_draw_sub_bitmaps(&osd->draw_cache, closure->dest, imgs);
- talloc_steal(osd, osd->draw_cache);
- closure->changed = true;
+ struct sub_bitmap_list *list =
+ osd_render(osd, res, video_pts, draw_flags, formats);
+
+ stats_time_start(osd->stats, "draw");
+
+ for (int n = 0; n < list->num_items; n++)
+ cb(cb_ctx, list->items[n]);
+
+ stats_time_end(osd->stats, "draw");
+
+ talloc_free(list);
}
// Calls mp_image_make_writeable() on the dest image if something is drawn.
-// Returns whether anything was drawn.
-bool osd_draw_on_image(struct osd_state *osd, struct mp_osd_res res,
+// draw_flags as in osd_render().
+void osd_draw_on_image(struct osd_state *osd, struct mp_osd_res res,
double video_pts, int draw_flags, struct mp_image *dest)
{
- struct draw_on_image_closure closure = {osd, dest};
- osd_draw(osd, res, video_pts, draw_flags, mp_draw_sub_formats,
- &draw_on_image, &closure);
- return closure.changed;
+ osd_draw_on_image_p(osd, res, video_pts, draw_flags, NULL, dest);
}
// Like osd_draw_on_image(), but if dest needs to be copied to make it
@@ -387,9 +421,34 @@ void osd_draw_on_image_p(struct osd_state *osd, struct mp_osd_res res,
double video_pts, int draw_flags,
struct mp_image_pool *pool, struct mp_image *dest)
{
- struct draw_on_image_closure closure = {osd, dest, pool};
- osd_draw(osd, res, video_pts, draw_flags, mp_draw_sub_formats,
- &draw_on_image, &closure);
+ struct sub_bitmap_list *list =
+ osd_render(osd, res, video_pts, draw_flags, mp_draw_sub_formats);
+
+ if (!list->num_items) {
+ talloc_free(list);
+ return;
+ }
+
+ if (!mp_image_pool_make_writeable(pool, dest))
+ return; // on OOM, skip
+
+ // Need to lock for the dumb osd->draw_cache thing.
+ mp_mutex_lock(&osd->lock);
+
+ if (!osd->draw_cache)
+ osd->draw_cache = mp_draw_sub_alloc(osd, osd->global);
+
+ stats_time_start(osd->stats, "draw-bmp");
+
+ if (!mp_draw_sub_bitmaps(osd->draw_cache, dest, list))
+ MP_WARN(osd, "Failed rendering OSD.\n");
+ talloc_steal(osd, osd->draw_cache);
+
+ stats_time_end(osd->stats, "draw-bmp");
+
+ mp_mutex_unlock(&osd->lock);
+
+ talloc_free(list);
}
// Setup the OSD resolution to render into an image with the given parameters.
@@ -407,29 +466,29 @@ struct mp_osd_res osd_res_from_image_params(const struct mp_image_params *p)
// Typically called to react to OSD style changes.
void osd_changed(struct osd_state *osd)
{
- pthread_mutex_lock(&osd->lock);
+ mp_mutex_lock(&osd->lock);
osd->objs[OSDTYPE_OSD]->osd_changed = true;
osd->want_redraw_notification = true;
// Done here for a lack of a better place.
m_config_cache_update(osd->opts_cache);
- pthread_mutex_unlock(&osd->lock);
+ mp_mutex_unlock(&osd->lock);
}
bool osd_query_and_reset_want_redraw(struct osd_state *osd)
{
- pthread_mutex_lock(&osd->lock);
+ mp_mutex_lock(&osd->lock);
bool r = osd->want_redraw_notification;
osd->want_redraw_notification = false;
- pthread_mutex_unlock(&osd->lock);
+ mp_mutex_unlock(&osd->lock);
return r;
}
struct mp_osd_res osd_get_vo_res(struct osd_state *osd)
{
- pthread_mutex_lock(&osd->lock);
+ mp_mutex_lock(&osd->lock);
// Any OSDTYPE is fine; but it mustn't be a subtitle one (can have lower res.)
struct mp_osd_res res = osd->objs[OSDTYPE_OSD]->vo_res;
- pthread_mutex_unlock(&osd->lock);
+ mp_mutex_unlock(&osd->lock);
return res;
}
@@ -446,8 +505,10 @@ void osd_rescale_bitmaps(struct sub_bitmaps *imgs, int frame_w, int frame_h,
int vidh = res.h - res.mt - res.mb;
double xscale = (double)vidw / frame_w;
double yscale = (double)vidh / frame_h;
- if (compensate_par < 0)
+ if (compensate_par < 0) {
+ assert(res.display_par);
compensate_par = xscale / yscale / res.display_par;
+ }
if (compensate_par > 0)
xscale /= compensate_par;
int cx = vidw / 2 - (int)(frame_w * xscale) / 2;
@@ -460,3 +521,39 @@ void osd_rescale_bitmaps(struct sub_bitmaps *imgs, int frame_w, int frame_h,
bi->dh = (int)(bi->h * yscale + 0.5);
}
}
+
+// Copy *in and return a new allocation of it. Free with talloc_free(). This
+// will contain a refcounted copy of the image data.
+//
+// in->packed must be set and must be a refcounted image, unless there is no
+// data (num_parts==0).
+//
+// p_cache: if not NULL, then this points to a struct sub_bitmap_copy_cache*
+// variable. The function may set this to an allocation and may later
+// read it. You have to free it with talloc_free() when done.
+// in: valid struct, or NULL (in this case it also returns NULL)
+// returns: new copy, or NULL if there was no data in the input
+struct sub_bitmaps *sub_bitmaps_copy(struct sub_bitmap_copy_cache **p_cache,
+ struct sub_bitmaps *in)
+{
+ if (!in || !in->num_parts)
+ return NULL;
+
+ struct sub_bitmaps *res = talloc(NULL, struct sub_bitmaps);
+ *res = *in;
+
+ // Note: the p_cache thing is a lie and unused.
+
+ // The bitmaps being refcounted is essential for performance, and for
+ // not invalidating in->parts[*].bitmap pointers.
+ assert(in->packed && in->packed->bufs[0]);
+
+ res->packed = mp_image_new_ref(res->packed);
+ talloc_steal(res, res->packed);
+
+ res->parts = NULL;
+ MP_RESIZE_ARRAY(res, res->parts, res->num_parts);
+ memcpy(res->parts, in->parts, sizeof(res->parts[0]) * res->num_parts);
+
+ return res;
+}