summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2020-02-27 02:23:58 +0100
committerwm4 <wm4@nowhere>2020-02-27 02:23:58 +0100
commitcf2b7a4997299ff9e0ff91d4273cd294686b001f (patch)
tree21c3f4a5c83407d5e1df92ba35d5847c6eba6097
parentb873f1f8e5a0d34defe4c7b975ec584377c731ac (diff)
downloadmpv-cf2b7a4997299ff9e0ff91d4273cd294686b001f.tar.bz2
mpv-cf2b7a4997299ff9e0ff91d4273cd294686b001f.tar.xz
sub, demux: improve behavior with negative subtitle delay/muxed subs
A negative subtitle delay means that subtitles from the future should be shown earlier. With muxed subtitles, subtitle packets are demuxed along with audio and video packets. But since they are demuxed "lazily", nothing guarantees that subtitle packets from the future are available in time. Typically, the user-observed effect is that subtitles do not appear at all (or too late) with large negative --sub-delay values, but that using --cache might fix this. Make this behave better. Automatically extend read-ahead to as much as needed by the subtitles. It seems it's the easiest to pass the subtitle render timestamp to the demuxer in order to guarantee that everything is read. This timestamp based approach might be fragile, so disable it if no negative sub-delay is used. As far as the player frontend part is concerned, this makes use of the code path for external subtitles, which are not lazily demuxed, and may already trigger waiting. Fixes: #7484
-rw-r--r--demux/demux.c43
-rw-r--r--demux/demux.h2
-rw-r--r--sub/dec_sub.c11
3 files changed, 48 insertions, 8 deletions
diff --git a/demux/demux.c b/demux/demux.c
index 6b5123d49a..0b80c33de8 100644
--- a/demux/demux.c
+++ b/demux/demux.c
@@ -400,6 +400,7 @@ struct demux_stream {
bool skip_to_keyframe;
bool attached_picture_added;
bool need_wakeup; // call wakeup_cb on next reader_head state change
+ double force_read_until;// eager=false streams (subs): force read-ahead
// For demux_internal.dumper. Currently, this is used only temporarily
// during blocking dumping.
@@ -824,6 +825,7 @@ static void ds_clear_reader_state(struct demux_stream *ds,
ds->attached_picture_added = false;
ds->last_ret_pos = -1;
ds->last_ret_dts = MP_NOPTS_VALUE;
+ ds->force_read_until = MP_NOPTS_VALUE;
if (clear_back_state) {
ds->back_restart_pos = -1;
@@ -2140,6 +2142,17 @@ static void mark_stream_eof(struct demux_stream *ds)
}
}
+static bool lazy_stream_needs_wait(struct demux_stream *ds)
+{
+ struct demux_internal *in = ds->in;
+ // Attempt to read until force_read_until was reached, or reading has
+ // stopped for some reason (true EOF, queue overflow).
+ return !ds->eager && !ds->reader_head && !in->back_demuxing &&
+ !in->last_eof && ds->force_read_until != MP_NOPTS_VALUE &&
+ (in->demux_ts == MP_NOPTS_VALUE ||
+ in->demux_ts <= ds->force_read_until);
+}
+
// Returns true if there was "progress" (lock was released temporarily).
static bool read_packet(struct demux_internal *in)
{
@@ -2160,6 +2173,12 @@ static bool read_packet(struct demux_internal *in)
read_more |= !ds->reader_head;
if (in->back_demuxing)
read_more |= ds->back_restarting || ds->back_resuming;
+ } else {
+ if (lazy_stream_needs_wait(ds)) {
+ read_more = true;
+ } else {
+ mark_stream_eof(ds); // let playback continue
+ }
}
refresh_more |= ds->refreshing;
if (ds->eager && ds->queue->last_ts != MP_NOPTS_VALUE &&
@@ -2588,7 +2607,8 @@ static struct demux_packet *read_packet_from_cache(struct demux_internal *in,
// < 0: EOF was reached, *res is not set
// == 0: no new packet yet, wait, *res is not set
// > 0: new packet is moved to *res
-static int dequeue_packet(struct demux_stream *ds, struct demux_packet **res)
+static int dequeue_packet(struct demux_stream *ds, double min_pts,
+ struct demux_packet **res)
{
struct demux_internal *in = ds->in;
@@ -2616,6 +2636,8 @@ static int dequeue_packet(struct demux_stream *ds, struct demux_packet **res)
pthread_cond_signal(&in->wakeup); // possibly read more
}
+ ds->force_read_until = min_pts;
+
if (ds->back_resuming || ds->back_restarting) {
assert(in->back_demuxing);
return 0;
@@ -2652,7 +2674,10 @@ static int dequeue_packet(struct demux_stream *ds, struct demux_packet **res)
// the reader would have to wait for new packets, which does not
// make sense due to the sparseness and passiveness of non-eager
// streams.
- return -1;
+ // Unless the min_pts feature is used: then EOF is only signaled
+ // if read-ahead went above min_pts.
+ if (!lazy_stream_needs_wait(ds))
+ ds->eof = eof = true;
}
return eof ? -1 : 0;
}
@@ -2731,6 +2756,16 @@ static int dequeue_packet(struct demux_stream *ds, struct demux_packet **res)
// minutes away). In this situation, this function will just return -1.
int demux_read_packet_async(struct sh_stream *sh, struct demux_packet **out_pkt)
{
+ return demux_read_packet_async_until(sh, MP_NOPTS_VALUE, out_pkt);
+}
+
+// Like demux_read_packet_async(). They are the same for min_pts==MP_NOPTS_VALUE.
+// If min_pts is set, and the stream is lazily read (eager=false, interleaved
+// subtitles), then return 0 until demuxing has reached min_pts, or the queue
+// overflowed, or EOF was reached, or packet was read for this stream.
+int demux_read_packet_async_until(struct sh_stream *sh, double min_pts,
+ struct demux_packet **out_pkt)
+{
struct demux_stream *ds = sh ? sh->ds : NULL;
*out_pkt = NULL;
if (!ds)
@@ -2740,7 +2775,7 @@ int demux_read_packet_async(struct sh_stream *sh, struct demux_packet **out_pkt)
pthread_mutex_lock(&in->lock);
int r = -1;
while (1) {
- r = dequeue_packet(ds, out_pkt);
+ r = dequeue_packet(ds, min_pts, out_pkt);
if (in->threading || in->blocked || r != 0)
break;
// Needs to actually read packets until we got a packet or EOF.
@@ -2763,7 +2798,7 @@ struct demux_packet *demux_read_any_packet(struct demuxer *demuxer)
bool all_eof = true;
for (int n = 0; n < in->num_streams; n++) {
in->reading = true; // force read_packet() to read
- int r = dequeue_packet(in->streams[n]->ds, &out_pkt);
+ int r = dequeue_packet(in->streams[n]->ds, MP_NOPTS_VALUE, &out_pkt);
if (r > 0)
goto done;
if (r == 0)
diff --git a/demux/demux.h b/demux/demux.h
index c94f690da0..263b584ad3 100644
--- a/demux/demux.h
+++ b/demux/demux.h
@@ -251,6 +251,8 @@ bool demux_free_async_finish(struct demux_free_async_state *state);
void demuxer_feed_caption(struct sh_stream *stream, demux_packet_t *dp);
int demux_read_packet_async(struct sh_stream *sh, struct demux_packet **out_pkt);
+int demux_read_packet_async_until(struct sh_stream *sh, double min_pts,
+ struct demux_packet **out_pkt);
bool demux_stream_is_selected(struct sh_stream *stream);
void demux_set_stream_wakeup_cb(struct sh_stream *sh,
void (*cb)(void *ctx), void *ctx);
diff --git a/sub/dec_sub.c b/sub/dec_sub.c
index f98a478be4..ad46eaea73 100644
--- a/sub/dec_sub.c
+++ b/sub/dec_sub.c
@@ -299,13 +299,16 @@ bool sub_read_packets(struct dec_sub *sub, double video_pts)
if (sub->new_segment)
break;
+ // (Use this mechanism only if sub_delay matters to avoid corner cases.)
+ double min_pts = sub->opts->sub_delay < 0 ? video_pts : MP_NOPTS_VALUE;
+
struct demux_packet *pkt;
- int st = demux_read_packet_async(sub->sh, &pkt);
+ int st = demux_read_packet_async_until(sub->sh, min_pts, &pkt);
// Note: "wait" (st==0) happens with non-interleaved streams only, and
// then we should stop the playloop until a new enough packet has been
- // seen (or the subtitle decoder's queue is full). This does not happen
- // for interleaved subtitle streams, which never return "wait" when
- // reading.
+ // seen (or the subtitle decoder's queue is full). This usually does not
+ // happen for interleaved subtitle streams, which never return "wait"
+ // when reading, unless min_pts is set.
if (st <= 0) {
r = st < 0 || (sub->last_pkt_pts != MP_NOPTS_VALUE &&
sub->last_pkt_pts > video_pts);