summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2016-01-23 20:40:13 +0100
committerwm4 <wm4@nowhere>2016-01-28 17:17:46 +0100
commit0c300e773192862c4bc09bd139de6c5f92cde58f (patch)
treefcf3fadbe2ccda89bd2bb3a4c3cdea83470f417a
parent65b309edeead3c1681d4cdf06adb2e824c3d4a5a (diff)
downloadmpv-sub_render.tar.bz2
mpv-sub_render.tar.xz
sub: add capability to render subtitles ahead on a threadsub_render
If --sub-render-ahead is set to something higher than 0, it will create a thread and render ahead subtitles for this many video frames. For various reasons, this works only with ASS subtitles. Other types use the old code. This is less efficient than the normal mode, because the subtitle data is copied. But it may fix pipelining/latency issues, especially with display-sync mode. Some bugs left. Expect crashes. Resizing doesn't work properly, because resizing doesn't invalidate the render-ahead-cache yet. New: - raise maximum number of readahead frames - avoid copying the previous sub if nothing changed (this affects copying the libass result images; the opengl textures weren't uploaded before this either)
-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;
}