summaryrefslogtreecommitdiffstats
path: root/sub
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2014-06-18 00:18:37 +0200
committerwm4 <wm4@nowhere>2014-06-18 01:57:57 +0200
commitc48dd85821445d701caa02d2b619294e4982d62b (patch)
treedb2d2b48e7cc23d903e3b82139b358cc8d223ba2 /sub
parenta28e2a7432f68e4a5db96cce8cbaa859327c4527 (diff)
downloadmpv-c48dd85821445d701caa02d2b619294e4982d62b.tar.bz2
mpv-c48dd85821445d701caa02d2b619294e4982d62b.tar.xz
sd_lavc: improve bitmap subtitle timing
Until now, bitmap subtitles were decoded at "some" point, and then simply replaced the old subtitle. Although the subtitle is selected by time (PTS), it could happen that a subtitle was replaced too early. One consequence is that this might lead to flicker even if the subtitles are timed to follow each other without a gap (although most subtitles are explicitly timed to introduce such a gap). With this commit the past 4 subtitles are kept (instead of 1), so that the correct one can be picked by time. This should fix the aforementioned cases, but more importantly will allow demuxing/decoding and video display to be somewhat asynchronous. Still missing: somehow making sure the correct range of decoded subtitles is available, instead of just passing along whatever comes from the demuxer, and hoping that 4 queued subtitles are enough. But it should certainly be good enough for now. This removes a check that resets the subtitles if the PTS is 5 minutes before the end of the current subtitle; this is probably not needed.
Diffstat (limited to 'sub')
-rw-r--r--sub/sd_lavc.c174
1 files changed, 103 insertions, 71 deletions
diff --git a/sub/sd_lavc.c b/sub/sd_lavc.c
index 1da2c1a4bf..0593edccda 100644
--- a/sub/sd_lavc.c
+++ b/sub/sd_lavc.c
@@ -32,17 +32,26 @@
#include "sd.h"
#include "dec_sub.h"
-struct sd_lavc_priv {
- AVCodecContext *avctx;
- AVSubtitle sub;
- bool have_sub;
+#define MAX_QUEUE 4
+
+struct sub {
+ bool valid;
+ AVSubtitle avsub;
int count;
struct sub_bitmap *inbitmaps;
- struct sub_bitmap *outbitmaps;
struct osd_bmp_indexed *imgs;
- bool bitmaps_changed;
double pts;
double endpts;
+ int64_t id;
+};
+
+struct sd_lavc_priv {
+ AVCodecContext *avctx;
+ struct sub subs[MAX_QUEUE]; // lowest pts first
+ struct sub_bitmap *outbitmaps;
+ int64_t displayed_id;
+ int64_t new_id;
+ bool unknown_pts; // at least one sub with MP_NOPTS_VALUE start
struct mp_image_params video_params;
};
@@ -143,6 +152,7 @@ static int init(struct sd *sd)
goto error;
priv->avctx = ctx;
sd->priv = priv;
+ priv->displayed_id = -1;
return 0;
error:
@@ -152,20 +162,25 @@ static int init(struct sd *sd)
return -1;
}
-static void clear(struct sd_lavc_priv *priv)
+static void clear_sub(struct sub *sub)
{
- priv->count = 0;
- talloc_free(priv->inbitmaps);
- talloc_free(priv->outbitmaps);
- priv->inbitmaps = priv->outbitmaps = NULL;
- talloc_free(priv->imgs);
- priv->imgs = NULL;
- priv->bitmaps_changed = true;
- priv->pts = MP_NOPTS_VALUE;
- priv->endpts = MP_NOPTS_VALUE;
- if (priv->have_sub)
- avsubtitle_free(&priv->sub);
- priv->have_sub = false;
+ sub->count = 0;
+ sub->pts = MP_NOPTS_VALUE;
+ sub->endpts = MP_NOPTS_VALUE;
+ if (sub->valid)
+ avsubtitle_free(&sub->avsub);
+ sub->valid = false;
+}
+
+static void alloc_sub(struct sd_lavc_priv *priv)
+{
+ clear_sub(&priv->subs[MAX_QUEUE - 1]);
+ for (int n = MAX_QUEUE - 1; n > 0; n--)
+ priv->subs[n] = priv->subs[n - 1];
+ // clear only some fields; the memory allocs can be reused
+ priv->subs[0].valid = false;
+ priv->subs[0].count = 0;
+ priv->subs[0].id = priv->new_id++;
}
static void decode(struct sd *sd, struct demux_packet *packet)
@@ -184,19 +199,22 @@ static void decode(struct sd *sd, struct demux_packet *packet)
if (duration == 0)
duration = -1;
- clear(priv);
+ if (pts == MP_NOPTS_VALUE) {
+ MP_WARN(sd, "Subtitle with unknown start time.\n");
+ priv->unknown_pts = true;
+ }
+
av_init_packet(&pkt);
pkt.data = packet->buffer;
pkt.size = packet->len;
- pkt.pts = pts * 1000;
+ pkt.pts = AV_NOPTS_VALUE;
if (duration >= 0)
pkt.convergence_duration = duration * 1000;
int got_sub;
int res = avcodec_decode_subtitle2(ctx, &sub, &got_sub, &pkt);
if (res < 0 || !got_sub)
return;
- priv->sub = sub;
- priv->have_sub = true;
+
if (pts != MP_NOPTS_VALUE) {
if (sub.end_display_time > sub.start_display_time)
duration = (sub.end_display_time - sub.start_display_time) / 1000.0;
@@ -205,42 +223,44 @@ static void decode(struct sd *sd, struct demux_packet *packet)
double endpts = MP_NOPTS_VALUE;
if (pts != MP_NOPTS_VALUE && duration >= 0)
endpts = pts + duration;
- if (sub.num_rects > 0) {
- switch (sub.rects[0]->type) {
- case SUBTITLE_BITMAP:
- priv->count = 0;
- priv->pts = pts;
- priv->endpts = endpts;
- priv->inbitmaps = talloc_array(priv, struct sub_bitmap,
- sub.num_rects);
- priv->imgs = talloc_array(priv, struct osd_bmp_indexed,
- sub.num_rects);
- for (int i = 0; i < sub.num_rects; i++) {
- struct AVSubtitleRect *r = sub.rects[i];
- struct sub_bitmap *b = &priv->inbitmaps[priv->count];
- struct osd_bmp_indexed *img = &priv->imgs[priv->count];
- if (!(r->flags & AV_SUBTITLE_FLAG_FORCED) &&
- opts->forced_subs_only)
- continue;
- if (r->w == 0 || r->h == 0)
- continue;
- img->bitmap = r->pict.data[0];
- assert(r->nb_colors > 0);
- assert(r->nb_colors * 4 <= sizeof(img->palette));
- memcpy(img->palette, r->pict.data[1], r->nb_colors * 4);
- b->bitmap = img;
- b->stride = r->pict.linesize[0];
- b->w = r->w;
- b->h = r->h;
- b->x = r->x;
- b->y = r->y;
- priv->count++;
- }
- break;
- default:
+
+ // set end time of previous sub
+ if (priv->subs[0].endpts == MP_NOPTS_VALUE || priv->subs[0].endpts > pts)
+ priv->subs[0].endpts = pts;
+
+ alloc_sub(priv);
+ struct sub *current = &priv->subs[0];
+
+ current->valid = true;
+ current->pts = pts;
+ current->endpts = endpts;
+ current->avsub = sub;
+
+ for (int i = 0; i < sub.num_rects; i++) {
+ struct AVSubtitleRect *r = sub.rects[i];
+ MP_TARRAY_GROW(priv, current->inbitmaps, current->count);
+ MP_TARRAY_GROW(priv, current->imgs, current->count);
+ struct sub_bitmap *b = &current->inbitmaps[current->count];
+ struct osd_bmp_indexed *img = &current->imgs[current->count];
+ if (r->type != SUBTITLE_BITMAP) {
MP_ERR(sd, "unsupported subtitle type from libavcodec\n");
- break;
+ continue;
}
+ if (!(r->flags & AV_SUBTITLE_FLAG_FORCED) && opts->forced_subs_only)
+ continue;
+ if (r->w <= 0 || r->h <= 0)
+ continue;
+ img->bitmap = r->pict.data[0];
+ assert(r->nb_colors > 0);
+ assert(r->nb_colors * 4 <= sizeof(img->palette));
+ memcpy(img->palette, r->pict.data[1], r->nb_colors * 4);
+ b->bitmap = img;
+ b->stride = r->pict.linesize[0];
+ b->w = r->w;
+ b->h = r->h;
+ b->x = r->x;
+ b->y = r->y;
+ current->count++;
}
}
@@ -250,21 +270,29 @@ 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;
- if (priv->pts != MP_NOPTS_VALUE && pts < priv->pts)
+ struct sub *current = NULL;
+ for (int n = 0; n < MAX_QUEUE; n++) {
+ struct sub *sub = &priv->subs[n];
+ if (pts == MP_NOPTS_VALUE ||
+ ((sub->pts == MP_NOPTS_VALUE || pts >= sub->pts) &&
+ (sub->endpts == MP_NOPTS_VALUE || pts < sub->endpts)))
+ {
+ current = sub;
+ break;
+ }
+ }
+ if (!current)
return;
- if (priv->endpts != MP_NOPTS_VALUE && (pts >= priv->endpts ||
- pts < priv->endpts - 300))
- clear(priv);
- size_t size = talloc_get_size(priv->inbitmaps);
- if (!priv->outbitmaps)
- priv->outbitmaps = talloc_size(priv, size);
- memcpy(priv->outbitmaps, priv->inbitmaps, size);
+
+ MP_TARRAY_GROW(priv, priv->outbitmaps, current->count);
+ for (int n = 0; n < current->count; n++)
+ priv->outbitmaps[n] = current->inbitmaps[n];
res->parts = priv->outbitmaps;
- res->num_parts = priv->count;
- if (priv->bitmaps_changed)
+ res->num_parts = current->count;
+ if (priv->displayed_id != current->id)
res->bitmap_id = ++res->bitmap_pos_id;
- priv->bitmaps_changed = false;
+ priv->displayed_id = current->id;
res->format = SUBBITMAP_INDEXED;
double video_par = -1;
@@ -286,8 +314,11 @@ static void reset(struct sd *sd)
{
struct sd_lavc_priv *priv = sd->priv;
- if (priv->pts == MP_NOPTS_VALUE)
- clear(priv);
+ if (priv->unknown_pts) {
+ for (int n = 0; n < MAX_QUEUE; n++)
+ clear_sub(&priv->subs[n]);
+ priv->unknown_pts = false;
+ }
// lavc might not do this right for all codecs; may need close+reopen
avcodec_flush_buffers(priv->avctx);
}
@@ -296,7 +327,8 @@ static void uninit(struct sd *sd)
{
struct sd_lavc_priv *priv = sd->priv;
- clear(priv);
+ for (int n = 0; n < MAX_QUEUE; n++)
+ clear_sub(&priv->subs[n]);
avcodec_close(priv->avctx);
av_free(priv->avctx->extradata);
av_free(priv->avctx);