diff --git a/libao2/ao_lavc.c b/libao2/ao_lavc.c index b22cd325dc..f23f14d59e 100644 --- a/libao2/ao_lavc.c +++ b/libao2/ao_lavc.c @@ -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; } diff --git a/libvo/vo_lavc.c b/libvo/vo_lavc.c index 4a1af15eb0..5b467f1f6a 100644 --- a/libvo/vo_lavc.c +++ b/libvo/vo_lavc.c @@ -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