encode: fix -ocopyts with certain DVD images

When timestamps jump by more than 30 seconds, assume an unexpected
discontinuity. Fixes encoding aborts (i.e. no more frames written) at
DVD cell switches.
This commit is contained in:
Rudolf Polzer 2012-09-25 11:53:29 +02:00 committed by Rudolf Polzer
parent c22482e08d
commit a89a6f40c7
2 changed files with 95 additions and 79 deletions

View File

@ -56,6 +56,7 @@ struct priv {
int64_t lastpts;
int sample_size;
const void *sample_padding;
double expected_next_pts;
AVRational worst_time_base;
int worst_time_base_is_stream;
@ -288,7 +289,7 @@ static void fill_with_padding(void *buf, int cnt, int sz, const void *padding)
}
// close audio device
static int encode(struct ao *ao, int ptsvalid, double apts, void *data);
static int encode(struct ao *ao, double apts, void *data);
static void uninit(struct ao *ao, bool cut_audio)
{
struct priv *ac = ao->priv;
@ -302,12 +303,12 @@ static void uninit(struct ao *ao, bool cut_audio)
(ac->aframesize * ao->channels * ac->sample_size
- ao->buffer.len) / ac->sample_size,
ac->sample_size, ac->sample_padding);
encode(ao, ao->pts != MP_NOPTS_VALUE, pts, paddingbuf);
encode(ao, pts, paddingbuf);
pts += ac->aframesize / (double) ao->samplerate;
talloc_free(paddingbuf);
ao->buffer.len = 0;
}
while (encode(ao, true, pts, NULL) > 0) ;
while (encode(ao, pts, NULL) > 0) ;
}
ao->priv = NULL;
@ -320,7 +321,7 @@ static int get_space(struct ao *ao)
}
// must get exactly ac->aframesize amount of data
static int encode(struct ao *ao, int ptsvalid, double apts, void *data)
static int encode(struct ao *ao, double apts, void *data)
{
AVFrame *frame;
AVPacket packet;
@ -338,7 +339,7 @@ static int encode(struct ao *ao, int ptsvalid, double apts, void *data)
ac->aframesize * ao->channels, ac->sample_size);
}
if (data && ptsvalid)
if (data)
ectx->audio_pts_offset = realapts - apts;
av_init_packet(&packet);
@ -354,12 +355,9 @@ static int encode(struct ao *ao, int ptsvalid, double apts, void *data)
return -1;
}
if (ao->encode_lavc_ctx->options->rawts) {
// raw audio pts
frame->pts = floor(apts * ac->stream->codec->time_base.den / ac->stream->codec->time_base.num + 0.5);
} else if (ectx->options->copyts) {
if (ectx->options->rawts || ectx->options->copyts) {
// real audio pts
frame->pts = floor((apts + ectx->discontinuity_pts_offset) * ac->stream->codec->time_base.den / ac->stream->codec->time_base.num + 0.5);
frame->pts = floor(apts * ac->stream->codec->time_base.den / ac->stream->codec->time_base.num + 0.5);
} else {
// audio playback time
frame->pts = floor(realapts * ac->stream->codec->time_base.den / ac->stream->codec->time_base.num + 0.5);
@ -385,7 +383,7 @@ static int encode(struct ao *ao, int ptsvalid, double apts, void *data)
ac->savepts = frame->pts;
}
av_free(frame);
av_free(frame);
}
else
{
@ -449,6 +447,8 @@ static int play(struct ao *ao, void *data, int len, int flags)
int64_t ptsoffset;
void *paddingbuf = NULL;
double nextpts;
double pts = ao->pts;
double outpts;
len /= ac->sample_size * ao->channels;
@ -456,6 +456,11 @@ static int play(struct ao *ao, void *data, int len, int flags)
mp_msg(MSGT_ENCODE, MSGL_WARN, "ao-lavc: NOTE: deferred initial audio frame (probably because video is not there yet)\n");
return 0;
}
if (pts == MP_NOPTS_VALUE) {
mp_msg(MSGT_ENCODE, MSGL_WARN, "ao-lavc: frame without pts, please report; synthesizing pts instead\n");
// synthesize pts from previous expected next pts
pts = ac->expected_next_pts;
}
if (ac->worst_time_base.den == 0) {
//if (ac->stream->codec->time_base.num / ac->stream->codec->time_base.den >= ac->stream->time_base.num / ac->stream->time_base.den)
@ -549,26 +554,43 @@ static int play(struct ao *ao, void *data, int len, int flags)
}
}
// fix the discontinuity pts offset
if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) {
nextpts = ao->pts + ptsoffset / (double) ao->samplerate;
ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
if (!ectx->options->rawts && ectx->options->copyts) {
// fix the discontinuity pts offset
nextpts = pts + ptsoffset / (double) ao->samplerate;
if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) {
ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
}
else if (fabs(nextpts + ectx->discontinuity_pts_offset - ectx->next_in_pts) > 30) {
mp_msg(MSGT_ENCODE, MSGL_WARN,
"ao-lavc: detected an unexpected discontinuity (pts jumped by "
"%f seconds)\n",
nextpts + ectx->discontinuity_pts_offset - ectx->next_in_pts);
ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
}
outpts = pts + ectx->discontinuity_pts_offset;
}
else
outpts = pts;
while (len - bufpos >= ac->aframesize) {
encode(ao, ao->pts != MP_NOPTS_VALUE,
ao->pts + (bufpos + ptsoffset) / (double) ao->samplerate +
encode_lavc_getoffset(ectx, ac->stream),
encode(ao,
outpts + (bufpos + ptsoffset) / (double) ao->samplerate + encode_lavc_getoffset(ectx, ac->stream),
(char *) data + ac->sample_size * bufpos * ao->channels);
bufpos += ac->aframesize;
}
talloc_free(paddingbuf);
// set next allowed output pts value
nextpts = ao->pts + ectx->discontinuity_pts_offset + (bufpos + ptsoffset) / (double) ao->samplerate;
if (nextpts > ectx->next_in_pts)
ectx->next_in_pts = nextpts;
// calculate expected pts of next audio frame
ac->expected_next_pts = pts + (bufpos + ptsoffset) / (double) ao->samplerate;
if (!ectx->options->rawts && ectx->options->copyts) {
// set next allowed output pts value
nextpts = ac->expected_next_pts + ectx->discontinuity_pts_offset;
if (nextpts > ectx->next_in_pts)
ectx->next_in_pts = nextpts;
}
return bufpos * ac->sample_size * ao->channels;
}

View File

@ -47,6 +47,7 @@ struct priv {
double lastpts;
int64_t lastipts;
int64_t lastframeipts;
double expected_next_pts;
mp_image_t *lastimg;
int lastdisplaycount;
@ -347,6 +348,11 @@ static void draw_image(struct vo *vo, mp_image_t *mpi, double pts)
mp_msg(MSGT_ENCODE, MSGL_WARN, "vo-lavc: NOTE: skipped initial video frame (probably because audio is not there yet)\n");
return;
}
if (pts == MP_NOPTS_VALUE) {
if (mpi)
mp_msg(MSGT_ENCODE, MSGL_WARN, "vo-lavc: frame without pts, please report; synthesizing pts instead\n");
pts = vc->expected_next_pts;
}
avc = vc->stream->codec;
@ -390,65 +396,53 @@ static void draw_image(struct vo *vo, mp_image_t *mpi, double pts)
double timeunit = (double)vc->worst_time_base.num / vc->worst_time_base.den;
// fix the discontinuity pts offset
if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) {
double outpts;
if (ectx->options->rawts)
outpts = pts;
else if (ectx->options->copyts) {
// fix the discontinuity pts offset
nextpts = pts;
ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
}
// set next allowed output pts value
nextpts = pts + ectx->discontinuity_pts_offset + timeunit;
if (nextpts > ectx->next_in_pts)
ectx->next_in_pts = nextpts;
// vc->lastipts is MP_NOPTS_VALUE, or the start time of vc->lastframe
if (mpi) {
if (pts == MP_NOPTS_VALUE) {
// NOTE: this even applies to ectx->options->copyts!
if (vc->lastipts == MP_NOPTS_VALUE)
frameipts = 0;
else
frameipts = vc->lastipts + 1;
mp_msg(MSGT_ENCODE, MSGL_INFO, "vo-lavc: pts was missing, using %d - "
"consider using -ofps or -vf fixpts\n", (int) frameipts);
if (ectx->last_video_in_pts != MP_NOPTS_VALUE)
ectx->last_video_in_pts += timeunit;
// calculate backwards to set vc->lastpts matchingly
vc->lastpts = frameipts * timeunit - encode_lavc_getoffset(ectx, vc->stream);
} else {
double outpts;
if (ectx->options->rawts)
outpts = pts;
else if (ectx->options->copyts)
outpts = pts + ectx->discontinuity_pts_offset;
else {
double duration = 0;
if (ectx->last_video_in_pts != MP_NOPTS_VALUE)
duration = pts - ectx->last_video_in_pts;
if (duration < 0)
duration = timeunit; // XXX warn about discontinuity?
outpts = vc->lastpts + duration;
if (ectx->audio_pts_offset != MP_NOPTS_VALUE) {
double adj = outpts - pts - ectx->audio_pts_offset;
adj = FFMIN(adj, duration * 0.1);
adj = FFMAX(adj, -duration * 0.1);
outpts -= adj;
}
}
vc->lastpts = outpts;
ectx->last_video_in_pts = pts;
frameipts = floor((outpts + encode_lavc_getoffset(ectx, vc->stream))
/ timeunit + 0.5);
if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) {
ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
}
} else {
if (vc->lastipts == MP_NOPTS_VALUE)
frameipts = 0;
else
frameipts = vc->lastipts + 1;
vc->lastpts = frameipts * timeunit - encode_lavc_getoffset(ectx, vc->stream);
else if (fabs(nextpts + ectx->discontinuity_pts_offset - ectx->next_in_pts) > 30) {
mp_msg(MSGT_ENCODE, MSGL_WARN,
"vo-lavc: detected an unexpected discontinuity (pts jumped by "
"%f seconds)\n",
nextpts + ectx->discontinuity_pts_offset - ectx->next_in_pts);
ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
}
outpts = pts + ectx->discontinuity_pts_offset;
}
else {
// adjust pts by knowledge of audio pts vs audio playback time
double duration = 0;
if (ectx->last_video_in_pts != MP_NOPTS_VALUE)
duration = pts - ectx->last_video_in_pts;
if (duration < 0)
duration = timeunit; // XXX warn about discontinuity?
outpts = vc->lastpts + duration;
if (ectx->audio_pts_offset != MP_NOPTS_VALUE) {
double adj = outpts - pts - ectx->audio_pts_offset;
adj = FFMIN(adj, duration * 0.1);
adj = FFMAX(adj, -duration * 0.1);
outpts -= adj;
}
}
vc->lastpts = outpts;
ectx->last_video_in_pts = pts;
frameipts = floor((outpts + encode_lavc_getoffset(ectx, vc->stream))
/ timeunit + 0.5);
// calculate expected pts of next video frame
vc->expected_next_pts = pts + timeunit;
if (!ectx->options->rawts && ectx->options->copyts) {
// set next allowed output pts value
nextpts = vc->expected_next_pts + ectx->discontinuity_pts_offset;
if (nextpts > ectx->next_in_pts)
ectx->next_in_pts = nextpts;
}
// never-drop mode