sub: change how subtitles are read

Slightly change how it is decided when a new packet should be read.
Switch to demux_read_packet_async(), and let the player "wait properly"
until required subtitle packets arrive, instead of blocking everything.
Move distinguishing the cases of passive and active reading into the
demuxer, where it belongs.
This commit is contained in:
wm4 2015-12-29 01:35:52 +01:00
parent 9b3daa4974
commit b47bf06f97
7 changed files with 86 additions and 77 deletions

View File

@ -685,15 +685,33 @@ struct demux_packet *demux_read_packet(struct sh_stream *sh)
return pkt; return pkt;
} }
// Sparse packets (Subtitles) interleaved with other non-sparse packets (video,
// audio) should never be read actively, meaning the demuxer thread does not
// try to exceed default readahead in order to find a new packet.
static bool use_lazy_subtitle_reading(struct demux_stream *ds)
{
if (ds->type != STREAM_SUB)
return false;
for (int n = 0; n < ds->in->num_streams; n++) {
struct demux_stream *s = ds->in->streams[n]->ds;
if (s->type != STREAM_SUB && s->selected && !s->eof)
return true;
}
return false;
}
// Poll the demuxer queue, and if there's a packet, return it. Otherwise, just // Poll the demuxer queue, and if there's a packet, return it. Otherwise, just
// make the demuxer thread read packets for this stream, and if there's at // make the demuxer thread read packets for this stream, and if there's at
// least one packet, call the wakeup callback. // least one packet, call the wakeup callback.
// Unlike demux_read_packet(), this always enables readahead (which means you // Unlike demux_read_packet(), this always enables readahead (except for
// must not use it on interleaved subtitle streams). // interleaved subtitles).
// Returns: // Returns:
// < 0: EOF was reached, *out_pkt=NULL // < 0: EOF was reached, *out_pkt=NULL
// == 0: no new packet yet, but maybe later, *out_pkt=NULL // == 0: no new packet yet, but maybe later, *out_pkt=NULL
// > 0: new packet read, *out_pkt is set // > 0: new packet read, *out_pkt is set
// Note: when reading interleaved subtitles, the demuxer won't try to forcibly
// 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) int demux_read_packet_async(struct sh_stream *sh, struct demux_packet **out_pkt)
{ {
struct demux_stream *ds = sh ? sh->ds : NULL; struct demux_stream *ds = sh ? sh->ds : NULL;
@ -703,10 +721,14 @@ int demux_read_packet_async(struct sh_stream *sh, struct demux_packet **out_pkt)
if (ds->in->threading) { if (ds->in->threading) {
pthread_mutex_lock(&ds->in->lock); pthread_mutex_lock(&ds->in->lock);
*out_pkt = dequeue_packet(ds); *out_pkt = dequeue_packet(ds);
r = *out_pkt ? 1 : ((ds->eof || !ds->selected) ? -1 : 0); if (use_lazy_subtitle_reading(ds)) {
ds->active = ds->selected; // enable readahead r = *out_pkt ? 1 : -1;
ds->in->eof = false; // force retry } else {
pthread_cond_signal(&ds->in->wakeup); // possibly read more r = *out_pkt ? 1 : ((ds->eof || !ds->selected) ? -1 : 0);
ds->active = ds->selected; // enable readahead
ds->in->eof = false; // force retry
pthread_cond_signal(&ds->in->wakeup); // possibly read more
}
pthread_mutex_unlock(&ds->in->lock); pthread_mutex_unlock(&ds->in->lock);
} else { } else {
*out_pkt = demux_read_packet(sh); *out_pkt = demux_read_packet(sh);

View File

@ -515,7 +515,7 @@ void reinit_subs(struct MPContext *mpctx, int order);
void uninit_sub(struct MPContext *mpctx, int order); void uninit_sub(struct MPContext *mpctx, int order);
void uninit_sub_all(struct MPContext *mpctx); void uninit_sub_all(struct MPContext *mpctx);
void update_osd_msg(struct MPContext *mpctx); void update_osd_msg(struct MPContext *mpctx);
void update_subtitles(struct MPContext *mpctx); bool update_subtitles(struct MPContext *mpctx, double video_pts);
// video.c // video.c
void reset_video_state(struct MPContext *mpctx); void reset_video_state(struct MPContext *mpctx);

View File

@ -1036,7 +1036,8 @@ void run_playloop(struct MPContext *mpctx)
handle_dummy_ticks(mpctx); handle_dummy_ticks(mpctx);
update_osd_msg(mpctx); update_osd_msg(mpctx);
update_subtitles(mpctx); if (!mpctx->video_out)
update_subtitles(mpctx, mpctx->playback_pts);
handle_segment_switch(mpctx, end_is_new_segment); handle_segment_switch(mpctx, end_is_new_segment);

View File

@ -68,32 +68,14 @@ void uninit_sub_all(struct MPContext *mpctx)
uninit_sub(mpctx, 1); uninit_sub(mpctx, 1);
} }
// When reading subtitles from a demuxer, and we read video or audio from the static bool update_subtitle(struct MPContext *mpctx, double video_pts, int order)
// demuxer, we should not explicitly read subtitle packets. (With external
// subs, we have to.)
static bool is_interleaved(struct MPContext *mpctx, struct track *track)
{
if (track->is_external || !track->demuxer)
return false;
struct demuxer *demuxer = track->demuxer;
for (int t = 0; t < mpctx->num_tracks; t++) {
struct track *other = mpctx->tracks[t];
if (other != track && other->selected && other->demuxer == demuxer &&
(other->type == STREAM_VIDEO || other->type == STREAM_AUDIO))
return true;
}
return track->demuxer == mpctx->demuxer;
}
static void update_subtitle(struct MPContext *mpctx, int order)
{ {
struct MPOpts *opts = mpctx->opts; struct MPOpts *opts = mpctx->opts;
struct track *track = mpctx->current_track[order][STREAM_SUB]; struct track *track = mpctx->current_track[order][STREAM_SUB];
struct dec_sub *dec_sub = mpctx->d_sub[order]; struct dec_sub *dec_sub = mpctx->d_sub[order];
if (!track || !dec_sub) if (!track || !dec_sub || video_pts == MP_NOPTS_VALUE)
return; return true;
if (mpctx->d_video) { if (mpctx->d_video) {
struct mp_image_params params = mpctx->d_video->vfilter->override_params; struct mp_image_params params = mpctx->d_video->vfilter->override_params;
@ -101,46 +83,26 @@ static void update_subtitle(struct MPContext *mpctx, int order)
sub_control(dec_sub, SD_CTRL_SET_VIDEO_PARAMS, &params); sub_control(dec_sub, SD_CTRL_SET_VIDEO_PARAMS, &params);
} }
double refpts_s = mpctx->playback_pts; video_pts -= opts->sub_delay;
double curpts_s = refpts_s - opts->sub_delay;
if (!track->preloaded && track->stream) { if (!track->preloaded) {
struct sh_stream *sh_stream = track->stream; if (!sub_read_packets(dec_sub, video_pts))
bool interleaved = is_interleaved(mpctx, track); return false;
while (1) {
if (interleaved && !demux_has_packet(sh_stream))
break;
double subpts_s = demux_get_next_pts(sh_stream);
if (!demux_has_packet(sh_stream))
break;
if (subpts_s > curpts_s) {
MP_DBG(mpctx, "Sub early: c_pts=%5.3f s_pts=%5.3f\n",
curpts_s, subpts_s);
// Often subs can be handled in advance
if (!sub_accepts_packet_in_advance(dec_sub))
break;
// Try to avoid demuxing whole file at once
if (subpts_s > curpts_s + 1 && !interleaved)
break;
}
struct demux_packet *pkt = demux_read_packet(sh_stream);
MP_DBG(mpctx, "Sub: c_pts=%5.3f s_pts=%5.3f duration=%5.3f len=%d\n",
curpts_s, pkt->pts, pkt->duration, pkt->len);
sub_decode(dec_sub, pkt);
talloc_free(pkt);
}
} }
// Handle displaying subtitles on terminal; never done for secondary subs // Handle displaying subtitles on terminal; never done for secondary subs
if (order == 0 && !mpctx->video_out) if (order == 0 && !mpctx->video_out)
term_osd_set_subs(mpctx, sub_get_text(dec_sub, curpts_s)); term_osd_set_subs(mpctx, sub_get_text(dec_sub, video_pts));
return true;
} }
void update_subtitles(struct MPContext *mpctx) // Return true if the subtitles for the given PTS are ready; false if the player
// should wait for new demuxer data, and then should retry.
bool update_subtitles(struct MPContext *mpctx, double video_pts)
{ {
update_subtitle(mpctx, 0); return update_subtitle(mpctx, video_pts, 0) &
update_subtitle(mpctx, 1); update_subtitle(mpctx, video_pts, 1);
} }
static bool init_subdec(struct MPContext *mpctx, struct track *track) static bool init_subdec(struct MPContext *mpctx, struct track *track)

View File

@ -1206,6 +1206,11 @@ void write_video(struct MPContext *mpctx, double endpts)
mpctx->time_frame -= get_relative_time(mpctx); mpctx->time_frame -= get_relative_time(mpctx);
update_avsync_before_frame(mpctx); update_avsync_before_frame(mpctx);
if (!update_subtitles(mpctx, mpctx->next_frames[0]->pts)) {
MP_WARN(mpctx, "subt wait\n");
return;
}
double time_frame = MPMAX(mpctx->time_frame, -1); double time_frame = MPMAX(mpctx->time_frame, -1);
int64_t pts = mp_time_us() + (int64_t)(time_frame * 1e6); int64_t pts = mp_time_us() + (int64_t)(time_frame * 1e6);
@ -1262,7 +1267,6 @@ void write_video(struct MPContext *mpctx, double endpts)
mpctx->osd_force_update = true; mpctx->osd_force_update = true;
update_osd_msg(mpctx); update_osd_msg(mpctx);
update_subtitles(mpctx);
vo_queue_frame(vo, frame); vo_queue_frame(vo, frame);

View File

@ -49,6 +49,7 @@ struct dec_sub {
struct MPOpts *opts; struct MPOpts *opts;
struct sh_stream *sh; struct sh_stream *sh;
double last_pkt_pts;
struct sd *sd; struct sd *sd;
}; };
@ -90,6 +91,7 @@ struct dec_sub *sub_create(struct mpv_global *global, struct demuxer *demuxer,
sub->log = talloc_steal(sub, log), sub->log = talloc_steal(sub, log),
sub->opts = global->opts; sub->opts = global->opts;
sub->sh = sh; sub->sh = sh;
sub->last_pkt_pts = MP_NOPTS_VALUE;
mpthread_mutex_init_recursive(&sub->lock); mpthread_mutex_init_recursive(&sub->lock);
sub->sd = talloc(NULL, struct sd); sub->sd = talloc(NULL, struct sd);
@ -116,20 +118,12 @@ struct dec_sub *sub_create(struct mpv_global *global, struct demuxer *demuxer,
return NULL; return NULL;
} }
void sub_decode(struct dec_sub *sub, struct demux_packet *packet)
{
pthread_mutex_lock(&sub->lock);
sub->sd->driver->decode(sub->sd, packet);
pthread_mutex_unlock(&sub->lock);
}
// Read all packets from the demuxer and decode/add them. Returns false if // Read all packets from the demuxer and decode/add them. Returns false if
// there are circumstances which makes this not possible. // there are circumstances which makes this not possible.
bool sub_read_all_packets(struct dec_sub *sub) bool sub_read_all_packets(struct dec_sub *sub)
{ {
pthread_mutex_lock(&sub->lock); pthread_mutex_lock(&sub->lock);
// Converters are assumed to always accept packets in advance
if (!sub->sd->driver->accept_packets_in_advance) { if (!sub->sd->driver->accept_packets_in_advance) {
pthread_mutex_unlock(&sub->lock); pthread_mutex_unlock(&sub->lock);
return false; return false;
@ -147,14 +141,40 @@ bool sub_read_all_packets(struct dec_sub *sub)
return true; return true;
} }
bool sub_accepts_packet_in_advance(struct dec_sub *sub) // 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).
bool sub_read_packets(struct dec_sub *sub, double video_pts)
{ {
bool res = true; bool r = true;
pthread_mutex_lock(&sub->lock); pthread_mutex_lock(&sub->lock);
if (sub->sd->driver->accepts_packet) while (1) {
res &= sub->sd->driver->accepts_packet(sub->sd); bool read_more = true;
if (sub->sd->driver->accepts_packet)
read_more = sub->sd->driver->accepts_packet(sub->sd);
if (!read_more)
break;
struct demux_packet *pkt;
int st = demux_read_packet_async(sub->sh, &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.
if (st <= 0) {
r = st < 0 || (sub->last_pkt_pts != MP_NOPTS_VALUE &&
sub->last_pkt_pts >= video_pts);
break;
}
sub->sd->driver->decode(sub->sd, pkt);
sub->last_pkt_pts = pkt->pts;
talloc_free(pkt);
}
pthread_mutex_unlock(&sub->lock); pthread_mutex_unlock(&sub->lock);
return res; return r;
} }
// You must call sub_lock/sub_unlock if more than 1 thread access sub. // You must call sub_lock/sub_unlock if more than 1 thread access sub.
@ -189,6 +209,7 @@ void sub_reset(struct dec_sub *sub)
pthread_mutex_lock(&sub->lock); pthread_mutex_lock(&sub->lock);
if (sub->sd->driver->reset) if (sub->sd->driver->reset)
sub->sd->driver->reset(sub->sd); sub->sd->driver->reset(sub->sd);
sub->last_pkt_pts = MP_NOPTS_VALUE;
pthread_mutex_unlock(&sub->lock); pthread_mutex_unlock(&sub->lock);
} }

View File

@ -29,8 +29,7 @@ void sub_lock(struct dec_sub *sub);
void sub_unlock(struct dec_sub *sub); void sub_unlock(struct dec_sub *sub);
bool sub_read_all_packets(struct dec_sub *sub); bool sub_read_all_packets(struct dec_sub *sub);
bool sub_accepts_packet_in_advance(struct dec_sub *sub); bool sub_read_packets(struct dec_sub *sub, double video_pts);
void sub_decode(struct dec_sub *sub, struct demux_packet *packet);
void sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, double pts, void sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, double pts,
struct sub_bitmaps *res); struct sub_bitmaps *res);
char *sub_get_text(struct dec_sub *sub, double pts); char *sub_get_text(struct dec_sub *sub, double pts);