summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--options/options.c2
-rw-r--r--options/options.h1
-rw-r--r--player/core.h3
-rw-r--r--player/sub.c8
-rw-r--r--player/video.c7
-rw-r--r--sub/dec_sub.c362
-rw-r--r--sub/dec_sub.h10
-rw-r--r--sub/osd.c9
-rw-r--r--sub/osd.h2
-rw-r--r--sub/sd.h1
-rw-r--r--sub/sd_ass.c1
-rw-r--r--sub/sd_lavc.c3
12 files changed, 344 insertions, 65 deletions
diff --git a/options/options.c b/options/options.c
index 49c44cb189..5adad13691 100644
--- a/options/options.c
+++ b/options/options.c
@@ -39,6 +39,7 @@
#include "stream/stream.h"
#include "video/csputils.h"
#include "video/hwdec.h"
+#include "sub/dec_sub.h"
#include "sub/osd.h"
#include "audio/mixer.h"
#include "audio/filter/af.h"
@@ -379,6 +380,7 @@ const m_option_t mp_opts[] = {
OPT_SUBSTRUCT("osd", osd_style, osd_style_conf, 0),
OPT_SUBSTRUCT("sub-text", sub_text_style, sub_style_conf, 0),
OPT_FLAG("sub-clear-on-seek", sub_clear_on_seek, 0),
+ OPT_INTRANGE("sub-render-ahead", sub_render_ahead, 0, 0, MAX_SUB_RENDER_AHEAD),
//---------------------- libao/libvo options ------------------------
OPT_SETTINGSLIST("vo", vo.video_driver_list, 0, &vo_obj_list),
diff --git a/options/options.h b/options/options.h
index 6ce4be472a..54e59e2b30 100644
--- a/options/options.h
+++ b/options/options.h
@@ -270,6 +270,7 @@ typedef struct MPOpts {
int ass_hinting;
int ass_shaper;
int sub_clear_on_seek;
+ int sub_render_ahead;
int hwdec_api;
char *hwdec_codecs;
diff --git a/player/core.h b/player/core.h
index 879470724e..01ad7646b2 100644
--- a/player/core.h
+++ b/player/core.h
@@ -25,6 +25,7 @@
#include "common/common.h"
#include "options/options.h"
+#include "sub/dec_sub.h"
#include "sub/osd.h"
#include "demux/timeline.h"
#include "audio/audio.h"
@@ -304,7 +305,7 @@ typedef struct MPContext {
struct vo *video_out;
// next_frame[0] is the next frame, next_frame[1] the one after that.
// The +1 is for adding 1 additional frame in backstep mode.
- struct mp_image *next_frames[VO_MAX_REQ_FRAMES + 1];
+ struct mp_image *next_frames[VO_MAX_REQ_FRAMES + MAX_SUB_RENDER_AHEAD + 1];
int num_next_frames;
struct mp_image *saved_frame; // for hrseek_lastframe and hrseek_backstep
diff --git a/player/sub.c b/player/sub.c
index ab9f714352..4d2c4782f7 100644
--- a/player/sub.c
+++ b/player/sub.c
@@ -95,7 +95,7 @@ static bool update_subtitle(struct MPContext *mpctx, double video_pts,
if (mpctx->vo_chain) {
struct mp_image_params params = mpctx->vo_chain->vf->input_params;
if (params.imgfmt)
- sub_control(dec_sub, SD_CTRL_SET_VIDEO_PARAMS, &params);
+ sub_set_video_fmt(dec_sub, &params);
}
video_pts -= opts->sub_delay;
@@ -108,10 +108,8 @@ static bool update_subtitle(struct MPContext *mpctx, double video_pts,
track->preloaded = sub_read_all_packets(track->d_sub);
}
- if (!track->preloaded) {
- if (!sub_read_packets(dec_sub, video_pts))
- return false;
- }
+ if (!sub_read_packets(dec_sub, video_pts))
+ return false;
// Handle displaying subtitles on terminal; never done for secondary subs
if (mpctx->current_track[0][STREAM_SUB] == track && !mpctx->video_out)
diff --git a/player/video.c b/player/video.c
index 5c5e7fb39c..800f4250a6 100644
--- a/player/video.c
+++ b/player/video.c
@@ -665,6 +665,7 @@ static int get_req_frames(struct MPContext *mpctx, bool eof)
return mpctx->opts->video_sync == VS_DEFAULT ? 1 : 2;
int req = vo_get_num_req_frames(mpctx->video_out);
+ req += mpctx->opts->sub_render_ahead;
return MPCLAMP(req, 2, MP_ARRAY_SIZE(mpctx->next_frames) - 1);
}
@@ -682,6 +683,12 @@ static void add_new_frame(struct MPContext *mpctx, struct mp_image *frame)
mpctx->next_frames[mpctx->num_next_frames++] = frame;
if (mpctx->num_next_frames == 1)
handle_new_frame(mpctx);
+
+ for (int n = 0; n < NUM_PTRACKS; n++) {
+ struct track *sub = mpctx->current_track[n][STREAM_SUB];
+ if (sub && sub->d_sub)
+ sub_read_packets(sub->d_sub, frame->pts);
+ }
}
// Enough video filtered already to push one frame to the VO?
diff --git a/sub/dec_sub.c b/sub/dec_sub.c
index e8b63986ab..77ef2af94e 100644
--- a/sub/dec_sub.c
+++ b/sub/dec_sub.c
@@ -30,6 +30,7 @@
#include "common/global.h"
#include "common/msg.h"
#include "osdep/threads.h"
+#include "video/mp_image.h"
extern const struct sd_functions sd_ass;
extern const struct sd_functions sd_lavc;
@@ -42,36 +43,195 @@ static const struct sd_functions *const sd_list[] = {
NULL
};
-struct dec_sub {
- pthread_mutex_t lock;
+// It's hard to put an upper bound on the ahead rendering caused by use of
+// vo_opengl interpolation + blend-subtitles.
+#define MAX_BUFFER (MAX_SUB_RENDER_AHEAD + 10)
+struct dec_sub {
+ // --- the following fields are invariant after init
struct mp_log *log;
struct MPOpts *opts;
struct sh_stream *sh;
- double last_pkt_pts;
+ bool threaded;
+ pthread_t thread;
+
+ // --- the following fields are protected by state_lock
+ pthread_mutex_t state_lock;
+ pthread_cond_t state_wakeup;
+ double last_pkt_pts;
+ struct mp_image_params last_video_fmt;
+ struct mp_osd_res last_osd_res;
+ struct cache_entry *entries[MAX_BUFFER];
+ int num_entries;
+ struct demux_packet **packets;
+ int num_packets;
+ bool preloaded;
+ struct cache_entry *cur;
+
+ // This lock serialized accesses to sub_get_bitmaps(). The problem is that
+ // a call to sub_get_bitmaps() can invalidate the result to the previous
+ // call, so sub_get_bitmaps() "reserves" access to the renderer, and must
+ // wait until the previous reservation has expired.
+ bool reserved;
+
+ // --- the following fields are protected by sd_lock
+ // (lock order: lock state_lock before sd_lock)
+ pthread_mutex_t sd_lock;
struct sd *sd;
};
-void sub_lock(struct dec_sub *sub)
+struct cache_entry {
+ // all fields, including refcount, are protected by state_lock, or immutable
+ double pts;
+ int refcount;
+ bool rendered;
+ struct sub_bitmaps data;
+ struct cache_entry *references;
+};
+
+static void copy_sub_bitmaps(struct sub_bitmaps *dst, struct sub_bitmaps *src)
+{
+ assert(src->format == SUBBITMAP_EMPTY || src->format == SUBBITMAP_LIBASS);
+ *dst = *src;
+ dst->parts =
+ talloc_memdup(NULL, src->parts, sizeof(src->parts[0]) * src->num_parts);
+ for (int n = 0; n < dst->num_parts; n++) {
+ struct sub_bitmap *p = &dst->parts[n];
+ p->bitmap = talloc_memdup(dst->parts, p->bitmap, p->stride * p->h);
+ }
+}
+
+static void cache_entry_unref(struct cache_entry *e)
+{
+ if (e) {
+ e->refcount -= 1;
+ if (e->refcount == 0) {
+ if (e->references) {
+ cache_entry_unref(e->references);
+ } else {
+ talloc_free(e->data.parts);
+ }
+ talloc_free(e);
+ }
+ }
+}
+
+static void *sub_thread(void *arg)
{
- pthread_mutex_lock(&sub->lock);
+ struct dec_sub *sub = arg;
+ mpthread_set_name("subrender");
+
+ pthread_mutex_lock(&sub->state_lock);
+ while (sub->threaded) {
+ struct demux_packet **packets = sub->packets;
+ int num_packets = sub->num_packets;
+ sub->packets = NULL;
+ sub->num_packets = 0;
+ if (num_packets) {
+ pthread_mutex_unlock(&sub->state_lock);
+ pthread_mutex_lock(&sub->sd_lock);
+
+ for (int n = 0; n < num_packets; n++) {
+ sub->sd->driver->decode(sub->sd, packets[n]);
+ talloc_free(packets[n]);
+ }
+
+ pthread_mutex_unlock(&sub->sd_lock);
+ pthread_mutex_lock(&sub->state_lock);
+ talloc_free(packets);
+ continue;
+ }
+ talloc_free(packets);
+
+ struct cache_entry *e = NULL;
+ struct cache_entry *prev = NULL;
+ for (int n = 0; n < sub->num_entries; n++) {
+ if (!sub->entries[n]->rendered) {
+ e = sub->entries[n];
+ if (n > 0)
+ prev = sub->entries[n - 1];
+ break;
+ }
+ }
+
+ struct mp_osd_res res = sub->last_osd_res;
+ if (e && res.w && res.h) {
+ assert(e->refcount);
+ e->refcount += 1;
+ if (prev && !prev->rendered)
+ prev = NULL;
+ if (prev)
+ prev->refcount += 1;
+ pthread_mutex_unlock(&sub->state_lock);
+ pthread_mutex_lock(&sub->sd_lock);
+
+ struct sub_bitmaps data = {0};
+ sub->sd->driver->get_bitmaps(sub->sd, res, e->pts, &data);
+ bool keep_ref = prev && data.change_id == 0;
+ if (keep_ref) {
+ e->data = prev->data;
+ e->data.change_id = 0;
+ e->references = prev;
+ } else {
+ copy_sub_bitmaps(&e->data, &data);
+ }
+
+ pthread_mutex_unlock(&sub->sd_lock);
+ pthread_mutex_lock(&sub->state_lock);
+ e->rendered = true;
+ if (!keep_ref)
+ cache_entry_unref(prev);
+ cache_entry_unref(e);
+ // Potentially wakeup VO thread waiting on this
+ pthread_cond_broadcast(&sub->state_wakeup);
+ } else {
+ pthread_cond_wait(&sub->state_wakeup, &sub->state_lock);
+ }
+ }
+ pthread_mutex_unlock(&sub->state_lock);
+
+ return NULL;
+}
+
+static void flush_cache(struct dec_sub *sub)
+{
+ for (int n = 0; n < sub->num_entries; n++)
+ cache_entry_unref(sub->entries[n]);
+ sub->num_entries = 0;
}
-void sub_unlock(struct dec_sub *sub)
+static void flush_packets(struct dec_sub *sub)
{
- pthread_mutex_unlock(&sub->lock);
+ for (int n = 0; n < sub->num_packets; n++)
+ talloc_free(sub->packets[n]);
+ sub->num_packets = 0;
}
void sub_destroy(struct dec_sub *sub)
{
if (!sub)
return;
- sub_reset(sub);
- sub->sd->driver->uninit(sub->sd);
+ if (sub->threaded) {
+ pthread_mutex_lock(&sub->state_lock);
+ sub->threaded = false;
+ pthread_cond_broadcast(&sub->state_wakeup);
+ pthread_mutex_unlock(&sub->state_lock);
+ pthread_join(sub->threaded, NULL);
+ }
+ flush_cache(sub);
+ flush_packets(sub);
+ assert(sub->cur == NULL);
+ if (sub->sd) {
+ sub_reset(sub);
+ sub->sd->driver->uninit(sub->sd);
+ }
talloc_free(sub->sd);
- pthread_mutex_destroy(&sub->lock);
+ talloc_free(sub->packets);
+ pthread_mutex_destroy(&sub->state_lock);
+ pthread_cond_destroy(&sub->state_wakeup);
+ pthread_mutex_destroy(&sub->sd_lock);
talloc_free(sub);
}
@@ -92,7 +252,9 @@ struct dec_sub *sub_create(struct mpv_global *global, struct demuxer *demuxer,
sub->opts = global->opts;
sub->sh = sh;
sub->last_pkt_pts = MP_NOPTS_VALUE;
- mpthread_mutex_init_recursive(&sub->lock);
+ pthread_mutex_init(&sub->state_lock, NULL);
+ pthread_cond_init(&sub->state_wakeup, NULL);
+ pthread_mutex_init(&sub->sd_lock, NULL);
sub->sd = talloc(NULL, struct sd);
*sub->sd = (struct sd){
@@ -104,12 +266,20 @@ struct dec_sub *sub_create(struct mpv_global *global, struct demuxer *demuxer,
.codec = sh->codec,
};
- if (sh->codec && sub->sd->driver->init(sub->sd) >= 0)
+ if (sh->codec && sub->sd->driver->init(sub->sd) >= 0) {
+ if (sub->opts->sub_render_ahead && !sub->sd->driver->accepts_packet)
+ {
+ sub->threaded = true;
+ if (pthread_create(&sub->thread, NULL, sub_thread, sub))
+ sub->threaded = false;
+ }
return sub;
+ }
ta_set_parent(log, NULL);
talloc_free(sub->sd);
- talloc_free(sub);
+ sub->sd = NULL;
+ sub_destroy(sub);
}
mp_err(log, "Could not find subtitle decoder for format '%s'.\n",
@@ -118,14 +288,24 @@ struct dec_sub *sub_create(struct mpv_global *global, struct demuxer *demuxer,
return NULL;
}
+static void feed_packet(struct dec_sub *sub, struct demux_packet *pkt)
+{
+ if (sub->threaded) {
+ MP_TARRAY_APPEND(NULL, sub->packets, sub->num_packets, pkt);
+ } else {
+ sub->sd->driver->decode(sub->sd, pkt);
+ talloc_free(pkt);
+ }
+}
+
// Read all packets from the demuxer and decode/add them. Returns false if
// there are circumstances which makes this not possible.
bool sub_read_all_packets(struct dec_sub *sub)
{
- pthread_mutex_lock(&sub->lock);
+ pthread_mutex_lock(&sub->state_lock);
- if (!sub->sd->driver->accept_packets_in_advance) {
- pthread_mutex_unlock(&sub->lock);
+ if (sub->sd->driver->accepts_packet) {
+ pthread_mutex_unlock(&sub->state_lock);
return false;
}
@@ -133,27 +313,31 @@ bool sub_read_all_packets(struct dec_sub *sub)
struct demux_packet *pkt = demux_read_packet(sub->sh);
if (!pkt)
break;
- sub->sd->driver->decode(sub->sd, pkt);
- talloc_free(pkt);
+ feed_packet(sub, pkt);
}
- pthread_mutex_unlock(&sub->lock);
+ sub->preloaded = true;
+
+ pthread_cond_broadcast(&sub->state_wakeup);
+ pthread_mutex_unlock(&sub->state_lock);
return true;
}
// Read packets from the demuxer stream passed to sub_create(). Return true if
// enough packets were read, false if the player should wait until the demuxer
// signals new packets available (and then should retry).
+// This can also be used to render subtitles with the given timestamp ahead.
+// (Then it's assumed that the player will want to render subtitles at this
+// point.)
bool sub_read_packets(struct dec_sub *sub, double video_pts)
{
bool r = true;
- pthread_mutex_lock(&sub->lock);
+ pthread_mutex_lock(&sub->state_lock);
while (1) {
- bool read_more = true;
- if (sub->sd->driver->accepts_packet)
- read_more = sub->sd->driver->accepts_packet(sub->sd);
-
- if (!read_more)
+ if (sub->preloaded)
+ break;
+ if (sub->sd->driver->accepts_packet &&
+ !sub->sd->driver->accepts_packet(sub->sd))
break;
struct demux_packet *pkt;
@@ -169,25 +353,100 @@ bool sub_read_packets(struct dec_sub *sub, double video_pts)
break;
}
- sub->sd->driver->decode(sub->sd, pkt);
sub->last_pkt_pts = pkt->pts;
- talloc_free(pkt);
+ feed_packet(sub, pkt);
+ }
+ if (sub->threaded && r) {
+ // xxx: can overflow if either
+ // - static readahead count is greater than MAX_BUFFER
+ // - VO is somehow not rendering subs
+ //assert(sub->num_entries < MAX_BUFFER);
+ if ((!sub->num_entries ||
+ video_pts > sub->entries[sub->num_entries - 1]->pts) &&
+ sub->num_entries < MAX_BUFFER)
+ {
+ // MP_WARN(sub,"add %f -> %d \n", video_pts, sub->num_entries);
+ struct cache_entry *e = talloc_ptrtype(NULL, e);
+ *e = (struct cache_entry){
+ .pts = video_pts,
+ .refcount = 1,
+ };
+ sub->entries[sub->num_entries++] = e;
+ }
+ // process packets, render ahead subtitles
+ pthread_cond_broadcast(&sub->state_wakeup);
}
- pthread_mutex_unlock(&sub->lock);
+ pthread_mutex_unlock(&sub->state_lock);
return r;
}
-// You must call sub_lock/sub_unlock if more than 1 thread access sub.
-// The issue is that *res will contain decoder allocated data, which might
-// be deallocated on the next decoder access.
+// Warning: you must call sub_release_bitmaps() when done. sub_get_bitmaps()
+// will block until the previous one has been released.
void sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, double pts,
struct sub_bitmaps *res)
{
- struct MPOpts *opts = sub->opts;
-
*res = (struct sub_bitmaps) {0};
- if (opts->sub_visibility && sub->sd->driver->get_bitmaps)
- sub->sd->driver->get_bitmaps(sub->sd, dim, pts, res);
+
+ if (sub->threaded) {
+ pthread_mutex_lock(&sub->state_lock);
+ if (!osd_res_equals(sub->last_osd_res, dim)) {
+ sub->last_osd_res = dim;
+ flush_cache(sub);
+ pthread_cond_broadcast(&sub->state_wakeup);
+ }
+ assert(!sub->cur);
+ for (int n = 0; n < sub->num_entries; n++) {
+ struct cache_entry *e = sub->entries[n];
+ if (e->pts < pts) {
+ // Assume old entries are not needed anymore.
+// MP_WARN(sub, "prune %f at %f -> %d \n", e->pts, pts, sub->num_entries);
+ cache_entry_unref(e);
+ MP_TARRAY_REMOVE_AT(sub->entries, sub->num_entries, n);
+ n--;
+ } else if (e->pts == pts) {
+ e->refcount += 1;
+ sub->cur = e;
+ break;
+ }
+ }
+ bool done = false;
+ if (sub->cur) {
+ done = true;
+ while (!sub->cur->rendered)
+ pthread_cond_wait(&sub->state_wakeup, &sub->state_lock);
+ *res = sub->cur->data;
+ //xxx: if there's a cache miss, then it might happen that older
+ // subs are being rendered again => must add change_id
+ //MP_WARN(sub, "%f in cache\n", pts);
+ }
+ pthread_mutex_unlock(&sub->state_lock);
+ if (done)
+ return;
+ }
+
+ //MP_WARN(sub, "%f not in cache\n", pts);
+ pthread_mutex_lock(&sub->sd_lock);
+ assert(!sub->reserved);
+ sub->reserved = true;
+ sub->sd->driver->get_bitmaps(sub->sd, dim, pts, res);
+}
+
+void sub_release_bitmaps(struct dec_sub *sub)
+{
+ if (sub->threaded) {
+ pthread_mutex_lock(&sub->state_lock);
+ bool had_sub = !!sub->cur;
+ cache_entry_unref(sub->cur);
+ sub->cur = NULL;
+ pthread_mutex_unlock(&sub->state_lock);
+ if (had_sub)
+ return;
+ }
+
+ if (sub->reserved) {
+ sub->reserved = false;
+ pthread_mutex_unlock(&sub->sd_lock);
+ }
}
// See sub_get_bitmaps() for locking requirements.
@@ -195,38 +454,53 @@ void sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, double pts,
// at a time (unless exclusive access is guaranteed).
char *sub_get_text(struct dec_sub *sub, double pts)
{
- pthread_mutex_lock(&sub->lock);
+ pthread_mutex_lock(&sub->sd_lock);
struct MPOpts *opts = sub->opts;
char *text = NULL;
if (opts->sub_visibility && sub->sd->driver->get_text)
text = sub->sd->driver->get_text(sub->sd, pts);
- pthread_mutex_unlock(&sub->lock);
+ pthread_mutex_unlock(&sub->sd_lock);
return text;
}
void sub_reset(struct dec_sub *sub)
{
- pthread_mutex_lock(&sub->lock);
- if (sub->sd->driver->reset)
+ pthread_mutex_lock(&sub->state_lock);
+ pthread_mutex_lock(&sub->sd_lock);
+ if (sub->sd && sub->sd->driver->reset)
sub->sd->driver->reset(sub->sd);
+ pthread_mutex_unlock(&sub->sd_lock);
sub->last_pkt_pts = MP_NOPTS_VALUE;
- pthread_mutex_unlock(&sub->lock);
+ flush_cache(sub);
+ flush_packets(sub);
+ pthread_mutex_unlock(&sub->state_lock);
}
void sub_select(struct dec_sub *sub, bool selected)
{
- pthread_mutex_lock(&sub->lock);
+ pthread_mutex_lock(&sub->sd_lock);
if (sub->sd->driver->select)
sub->sd->driver->select(sub->sd, selected);
- pthread_mutex_unlock(&sub->lock);
+ pthread_mutex_unlock(&sub->sd_lock);
}
int sub_control(struct dec_sub *sub, enum sd_ctrl cmd, void *arg)
{
int r = CONTROL_UNKNOWN;
- pthread_mutex_lock(&sub->lock);
+ pthread_mutex_lock(&sub->sd_lock);
if (sub->sd->driver->control)
r = sub->sd->driver->control(sub->sd, cmd, arg);
- pthread_mutex_unlock(&sub->lock);
+ pthread_mutex_unlock(&sub->sd_lock);
return r;
}
+
+void sub_set_video_fmt(struct dec_sub *sub, struct mp_image_params *fmt)
+{
+ pthread_mutex_lock(&sub->state_lock);
+ if (!mp_image_params_equal(&sub->last_video_fmt, fmt)) {
+ sub->last_video_fmt = *fmt;
+ sub_control(sub, SD_CTRL_SET_VIDEO_PARAMS, &sub->last_video_fmt);
+ }
+ pthread_mutex_unlock(&sub->state_lock);
+}
+
diff --git a/sub/dec_sub.h b/sub/dec_sub.h
index b3f30520e3..072f5eff00 100644
--- a/sub/dec_sub.h
+++ b/sub/dec_sub.h
@@ -9,7 +9,7 @@
struct demuxer;
struct sh_stream;
struct mpv_global;
-struct demux_packet;
+struct mp_image_params;
struct dec_sub;
struct sd;
@@ -17,25 +17,27 @@ struct sd;
enum sd_ctrl {
SD_CTRL_SUB_STEP,
SD_CTRL_SET_VIDEO_PARAMS,
- SD_CTRL_GET_RESOLUTION,
SD_CTRL_SET_TOP,
SD_CTRL_SET_VIDEO_DEF_FPS,
};
+#define MAX_SUB_RENDER_AHEAD 500
+
struct dec_sub *sub_create(struct mpv_global *global, struct demuxer *demuxer,
struct sh_stream *sh);
void sub_destroy(struct dec_sub *sub);
-void sub_lock(struct dec_sub *sub);
-void sub_unlock(struct dec_sub *sub);
bool sub_read_all_packets(struct dec_sub *sub);
bool sub_read_packets(struct dec_sub *sub, double video_pts);
+void sub_add_pts(struct dec_sub *sub, double pts);
void sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, double pts,
struct sub_bitmaps *res);
+void sub_release_bitmaps(struct dec_sub *sub);
char *sub_get_text(struct dec_sub *sub, double pts);
void sub_reset(struct dec_sub *sub);
void sub_select(struct dec_sub *sub, bool selected);
int sub_control(struct dec_sub *sub, enum sd_ctrl cmd, void *arg);
+void sub_set_video_fmt(struct dec_sub *sub, struct mp_image_params *fmt);
#endif
diff --git a/sub/osd.c b/sub/osd.c
index fd821923e5..797ba585d3 100644
--- a/sub/osd.c
+++ b/sub/osd.c
@@ -100,7 +100,7 @@ const struct m_sub_options sub_style_conf = {
},
};
-static bool osd_res_equals(struct mp_osd_res a, struct mp_osd_res b)
+bool osd_res_equals(struct mp_osd_res a, struct mp_osd_res b)
{
return a.w == b.w && a.h == b.h && a.ml == b.ml && a.mt == b.mt
&& a.mr == b.mr && a.mb == b.mb
@@ -248,7 +248,7 @@ static void render_object(struct osd_state *osd, struct osd_object *obj,
}
if (obj->type == OSDTYPE_SUB || obj->type == OSDTYPE_SUB2) {
- if (obj->sub) {
+ if (obj->sub && opts->sub_visibility) {
double sub_pts = video_pts;
if (sub_pts != MP_NOPTS_VALUE)
sub_pts -= opts->sub_delay;
@@ -326,9 +326,6 @@ 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);
-
struct sub_bitmaps imgs;
render_object(osd, obj, res, video_pts, formats, &imgs);
if (imgs.num_parts > 0) {
@@ -341,7 +338,7 @@ void osd_draw(struct osd_state *osd, struct mp_osd_res res,
}
if (obj->sub)
- sub_unlock(obj->sub);
+ sub_release_bitmaps(obj->sub);
}
pthread_mutex_unlock(&osd->lock);
diff --git a/sub/osd.h b/sub/osd.h
index b41a2eec7a..772483a260 100644
--- a/sub/osd.h
+++ b/sub/osd.h
@@ -201,6 +201,8 @@ struct mp_osd_res osd_get_vo_res(struct osd_state *osd, int obj);
void osd_rescale_bitmaps(struct sub_bitmaps *imgs, int frame_w, int frame_h,
struct mp_osd_res res, double compensate_par);
+bool osd_res_equals(struct mp_osd_res a, struct mp_osd_res b);
+
// defined in osd_libass.c and osd_dummy.c
// internal use only
diff --git a/sub/sd.h b/sub/sd.h
index b142654ed1..1a2d65e8b8 100644
--- a/sub/sd.h
+++ b/sub/sd.h
@@ -23,7 +23,6 @@ struct sd {
struct sd_functions {
const char *name;
- bool accept_packets_in_advance;
int (*init)(struct sd *sd);
void (*decode)(struct sd *sd, struct demux_packet *packet);
void (*reset)(struct sd *sd);
diff --git a/sub/sd_ass.c b/sub/sd_ass.c
index 5c56d3e0df..b139f39d44 100644
--- a/sub/sd_ass.c
+++ b/sub/sd_ass.c
@@ -647,7 +647,6 @@ static int control(struct sd *sd, enum sd_ctrl cmd, void *arg)
const struct sd_functions sd_ass = {
.name = "ass",
- .accept_packets_in_advance = true,
.init = init,
.decode = decode,
.get_bitmaps = get_bitmaps,
diff --git a/sub/sd_lavc.c b/sub/sd_lavc.c
index 2da69e937e..ed163449c7 100644
--- a/sub/sd_lavc.c
+++ b/sub/sd_lavc.c
@@ -454,9 +454,6 @@ static int control(struct sd *sd, enum sd_ctrl cmd, void *arg)
case SD_CTRL_SET_VIDEO_PARAMS:
priv->video_params = *(struct mp_image_params *)arg;
return CONTROL_OK;
- case SD_CTRL_GET_RESOLUTION:
- get_resolution(sd, arg);
- return CONTROL_OK;
default:
return CONTROL_UNKNOWN;
}