sub: allow feeding bitmap subs in advance

Until now, feeding packets to the decoder in advance was done for text
subtitles only. This was possible because libass buffers all subtitle
data anyway (in ASS_Track). sd_lavc, responsible for bitmap subs, does
not do this. But it can buffer a small number of subtitle frames ahead.
Enable this.

Repurpose the sub_accept_packets_in_advance(). Instead of "can take all
packets" it means "can take 1 packet" now. (The old meaning is still
needed locally in dec_sub.c; keep it there.) It asks the decoder whether
there is place for at least 1 subtitle packet. sd_lavc implements it and
returns true if its internal fixed-size subtitle queue still has a free
slot. (The implementation of this in dec_sub.c isn't entirely clean.
For one, decode_chain() ignores this mechanism, so it's implied that
bitmap subtitles do not use the subtitle filter chain in any advanced
way.)

Also fix 2 bugs in the sd_lavc queue handling. Subtitles must be checked
in reverse, because the first entry will often have endpts==NOPTS, which
would always match. alloc_sub() must cycle the queue buffer, because it
reuses memory allocations (like sub.imgs) by design.
This commit is contained in:
wm4 2015-12-05 23:54:00 +01:00
parent 565efeeada
commit a2e7642d3c
5 changed files with 46 additions and 10 deletions

View File

@ -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)

View File

@ -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.

View File

@ -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);

View File

@ -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);

View File

@ -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,