mirror of
https://github.com/mpv-player/mpv
synced 2025-04-01 00:07:33 +00:00
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
This commit is contained in:
parent
b873f1f8e5
commit
cf2b7a4997
@ -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;
|
||||
}
|
||||
@ -2730,6 +2755,16 @@ static int dequeue_packet(struct demux_stream *ds, struct demux_packet **res)
|
||||
// read ahead to get the next subtitle packet (as the next packet could be
|
||||
// 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;
|
||||
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user