diff --git a/player/sub.c b/player/sub.c index 0a55936f54..c2a3e80cb3 100644 --- a/player/sub.c +++ b/player/sub.c @@ -222,8 +222,8 @@ static void update_subtitle(struct MPContext *mpctx, int order) if (subpts_s > curpts_s) { MP_DBG(mpctx, "Sub early: c_pts=%5.3f s_pts=%5.3f\n", curpts_s, subpts_s); - // Libass handled subs can be fed to it in advance - if (!sub_accept_packets_in_advance(dec_sub)) + // 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) diff --git a/sub/dec_sub.c b/sub/dec_sub.c index c357f1d369..9261e2570d 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -415,7 +415,9 @@ bool sub_read_all_packets(struct dec_sub *sub, struct sh_stream *sh) pthread_mutex_lock(&sub->lock); - if (!sub_accept_packets_in_advance(sub) || sub->num_sd < 1) { + // Converters are assumed to always accept packets in advance + struct sd *sd = sub_get_last_sd(sub); + if (!(sd && sd->driver->accept_packets_in_advance)) { pthread_mutex_unlock(&sub->lock); return false; } @@ -486,14 +488,16 @@ bool sub_read_all_packets(struct dec_sub *sub, struct sh_stream *sh) return true; } -bool sub_accept_packets_in_advance(struct dec_sub *sub) +bool sub_accepts_packet_in_advance(struct dec_sub *sub) { + bool res = true; pthread_mutex_lock(&sub->lock); - // Converters are assumed to always accept packets in advance - struct sd *sd = sub_get_last_sd(sub); - bool r = sd && sd->driver->accept_packets_in_advance; + for (int n = 0; n < sub->num_sd; n++) { + if (sub->sd[n]->driver->accepts_packet) + res &= sub->sd[n]->driver->accepts_packet(sub->sd[n]); + } pthread_mutex_unlock(&sub->lock); - return r; + return res; } // You must call sub_lock/sub_unlock if more than 1 thread access sub. diff --git a/sub/dec_sub.h b/sub/dec_sub.h index b9f81a77b7..0c4d59f395 100644 --- a/sub/dec_sub.h +++ b/sub/dec_sub.h @@ -40,7 +40,7 @@ void sub_init_from_sh(struct dec_sub *sub, struct sh_stream *sh); bool sub_is_initialized(struct dec_sub *sub); bool sub_read_all_packets(struct dec_sub *sub, struct sh_stream *sh); -bool sub_accept_packets_in_advance(struct dec_sub *sub); +bool sub_accepts_packet_in_advance(struct dec_sub *sub); 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, struct sub_bitmaps *res); diff --git a/sub/sd.h b/sub/sd.h index a77028a43c..4be48fedb8 100644 --- a/sub/sd.h +++ b/sub/sd.h @@ -52,6 +52,7 @@ struct sd_functions { void (*reset)(struct sd *sd); void (*uninit)(struct sd *sd); + bool (*accepts_packet)(struct sd *sd); // implicit default if NULL: true void (*fix_events)(struct sd *sd); int (*control)(struct sd *sd, enum sd_ctrl cmd, void *arg); diff --git a/sub/sd_lavc.c b/sub/sd_lavc.c index 88c45856b4..1e6180b074 100644 --- a/sub/sd_lavc.c +++ b/sub/sd_lavc.c @@ -53,6 +53,7 @@ struct sd_lavc_priv { int64_t displayed_id; int64_t new_id; struct mp_image_params video_params; + double current_pts; }; static bool supports_format(const char *format) @@ -116,6 +117,7 @@ static int init(struct sd *sd) priv->avctx = ctx; sd->priv = priv; priv->displayed_id = -1; + priv->current_pts = MP_NOPTS_VALUE; return 0; error: @@ -138,8 +140,10 @@ static void clear_sub(struct sub *sub) static void alloc_sub(struct sd_lavc_priv *priv) { clear_sub(&priv->subs[MAX_QUEUE - 1]); + struct sub tmp = priv->subs[MAX_QUEUE - 1]; for (int n = MAX_QUEUE - 1; n > 0; n--) priv->subs[n] = priv->subs[n - 1]; + priv->subs[0] = tmp; // clear only some fields; the memory allocs can be reused priv->subs[0].valid = false; priv->subs[0].count = 0; @@ -239,8 +243,10 @@ static void get_bitmaps(struct sd *sd, struct mp_osd_res d, double pts, struct sd_lavc_priv *priv = sd->priv; struct MPOpts *opts = sd->opts; + priv->current_pts = pts; + struct sub *current = NULL; - for (int n = 0; n < MAX_QUEUE; n++) { + for (int n = MAX_QUEUE - 1; n >= 0; n--) { struct sub *sub = &priv->subs[n]; if (!sub->valid) continue; @@ -297,6 +303,28 @@ static void get_bitmaps(struct sd *sd, struct mp_osd_res d, double pts, osd_rescale_bitmaps(res, insize[0], insize[1], d, video_par); } +static bool accepts_packet(struct sd *sd) +{ + struct sd_lavc_priv *priv = sd->priv; + + double pts = priv->current_pts; + int last_needed = -1; + for (int n = 0; n < MAX_QUEUE; n++) { + struct sub *sub = &priv->subs[n]; + if (!sub->valid) + continue; + if (pts == MP_NOPTS_VALUE || + ((sub->pts == MP_NOPTS_VALUE || sub->pts >= pts) || + (sub->endpts == MP_NOPTS_VALUE || pts < sub->endpts))) + { + last_needed = n; + } + } + // We can accept a packet if it wouldn't overflow the fixed subtitle queue. + // We assume that get_bitmaps() never decreases the PTS. + return last_needed + 1 < MAX_QUEUE; +} + static void reset(struct sd *sd) { struct sd_lavc_priv *priv = sd->priv; @@ -305,6 +333,8 @@ static void reset(struct sd *sd) clear_sub(&priv->subs[n]); // lavc might not do this right for all codecs; may need close+reopen avcodec_flush_buffers(priv->avctx); + + priv->current_pts = MP_NOPTS_VALUE; } static void uninit(struct sd *sd) @@ -340,6 +370,7 @@ const struct sd_functions sd_lavc = { .init = init, .decode = decode, .get_bitmaps = get_bitmaps, + .accepts_packet = accepts_packet, .control = control, .reset = reset, .uninit = uninit,