diff options
-rw-r--r-- | DOCS/encoding.rst | 13 | ||||
-rw-r--r-- | DOCS/interface-changes.rst | 3 | ||||
-rw-r--r-- | DOCS/man/encode.rst | 33 | ||||
-rw-r--r-- | audio/out/ao_lavc.c | 52 | ||||
-rw-r--r-- | common/encode.h | 6 | ||||
-rw-r--r-- | common/encode_lavc.c | 13 | ||||
-rw-r--r-- | video/out/vo_lavc.c | 228 |
7 files changed, 57 insertions, 291 deletions
diff --git a/DOCS/encoding.rst b/DOCS/encoding.rst index 6f9cd1a390..15e38a4581 100644 --- a/DOCS/encoding.rst +++ b/DOCS/encoding.rst @@ -3,8 +3,7 @@ General usage :: - mpv infile -o outfile [-of outfileformat] [-ofopts formatoptions] \ - [-ofps outfps | -oautofps] [-oharddup] [-ocopyts | -orawts] [-oneverdrop] \ + mpv infile -o outfile [-of outfileformat] [-ofopts formatoptions] [-orawts] \ [(any other mpv options)] \ -ovc outvideocodec [-ovcopts outvideocodecoptions] \ -oac outaudiocodec [-oacopts outaudiocodecoptions] @@ -60,13 +59,13 @@ for. Typical MPEG-4 Part 2 ("ASP", "DivX") encoding, AVI container:: mpv infile -o outfile.avi \ - -ofps 25 \ + --vf=fps=25 \ -ovc mpeg4 -ovcopts qscale=4 \ -oac libmp3lame -oacopts ab=128k -Note: AVI does not support variable frame rate, so -ofps must be used. The -frame rate should ideally match the input (25 for PAL, 24000/1001 or 30000/1001 -for NTSC) +Note: AVI does not support variable frame rate, so the fps filter must be used. +The frame rate should ideally match the input (25 for PAL, 24000/1001 or +30000/1001 for NTSC) Typical MPEG-4 Part 10 ("AVC", "H.264") encoding, Matroska (MKV) container:: @@ -129,7 +128,7 @@ What works ========== * Encoding at variable frame rate (default) -* Encoding at constant frame rate using -ofps framerate -oharddup +* Encoding at constant frame rate using --vf=fps=RATE * 2-pass encoding (specify flags=+pass1 in the first pass's -ovcopts, specify flags=+pass2 in the second pass) * Hardcoding subtitles using vobsub, ass or srt subtitle rendering (just diff --git a/DOCS/interface-changes.rst b/DOCS/interface-changes.rst index 9c29f31b0a..fa42b1dc81 100644 --- a/DOCS/interface-changes.rst +++ b/DOCS/interface-changes.rst @@ -94,6 +94,9 @@ Interface changes the future. (This kind of waiting was always a feature to prevent that playback is started while scripts are only half-loaded.) - deprecate --ovoffset, --oaoffset, --ovfirst, --oafirst + - remove the following encoding options: --ocopyts (now the default, old + timestamp handling is gone), --oneverdrop (now default), --oharddup (you + need to use --vf=fps=VALUE), --ofps, --oautofps, --omaxfps - remove --video-stereo-mode. This option was broken out of laziness, and nobody wants to fix it. Automatic 3D down-conversion to 2D is also broken, although you can just insert the stereo3d filter manually. The obscurity diff --git a/DOCS/man/encode.rst b/DOCS/man/encode.rst index 73c1b6b35c..4c885ea168 100644 --- a/DOCS/man/encode.rst +++ b/DOCS/man/encode.rst @@ -24,32 +24,6 @@ You can encode files from one format/codec to another using this facility. ``--ofopts=""`` Completely empties the options list. -``--ofps=<float value>`` - Specifies the output format time base (default: 24000). Low values like 25 - limit video fps by dropping frames. - -``--oautofps`` - Sets the output format time base to the guessed frame rate of the input - video (simulates MEncoder behavior, useful for AVI; may cause frame drops). - Note that not all codecs and not all formats support VFR encoding, and some - which do have bugs when a target bitrate is specified - use ``--ofps`` or - ``--oautofps`` to force CFR encoding in these cases. - -``--omaxfps=<float value>`` - Specifies the minimum distance of adjacent frames (default: 0, which means - unset). Content of lower frame rate is not readjusted to this frame rate; - content of higher frame rate is decimated to this frame rate. - -``--oharddup`` - If set, the frame rate given by ``--ofps`` is attained not by skipping time - codes, but by duplicating frames (constant frame rate mode). - -``--oneverdrop`` - If set, frames are never dropped. Instead, time codes of video are - readjusted to always increase. This may cause AV desync, though; to work - around this, use a high-fps time base using ``--ofps`` and absolutely - avoid ``--oautofps``. - ``--oac=<codec>`` Specifies the output audio codec. See ``--oac=help`` for a full list of supported codecs. @@ -113,13 +87,6 @@ You can encode files from one format/codec to another using this facility. Force the video stream to become the first stream in the output. By default, the order is unspecified. Deprecated. -``--ocopyts`` - Copies input pts to the output video (not supported by some output - container formats, e.g. AVI). Discontinuities are still fixed. - By default, audio pts are set to playback time and video pts are - synchronized to match audio pts, as some output formats do not support - anything else. - ``--orawts`` Copies input pts to the output video (not supported by some output container formats, e.g. AVI). In this mode, discontinuities are not fixed diff --git a/audio/out/ao_lavc.c b/audio/out/ao_lavc.c index e18db667a3..bb86224229 100644 --- a/audio/out/ao_lavc.c +++ b/audio/out/ao_lavc.c @@ -169,7 +169,7 @@ static void uninit(struct ao *ao) double outpts = ac->expected_next_pts; pthread_mutex_lock(&ectx->lock); - if (!ac->enc->options->rawts && ac->enc->options->copyts) + if (!ac->enc->options->rawts) outpts += ectx->discontinuity_pts_offset; pthread_mutex_unlock(&ectx->lock); @@ -214,15 +214,7 @@ static void encode(struct ao *ao, double apts, void **data) frame->linesize[0] = frame->nb_samples * ao->sstride; - if (ac->enc->options->rawts || ac->enc->options->copyts) { - // real audio pts - frame->pts = floor(apts * encoder->time_base.den / - encoder->time_base.num + 0.5); - } else { - // audio playback time - frame->pts = floor(realapts * encoder->time_base.den / - encoder->time_base.num + 0.5); - } + frame->pts = rint(apts * av_q2d(av_inv_q(encoder->time_base))); int64_t frame_pts = av_rescale_q(frame->pts, encoder->time_base, ac->worst_time_base); @@ -254,7 +246,6 @@ static int play(struct ao *ao, void **data, int samples, int flags) struct encode_lavc_context *ectx = ao->encode_lavc_ctx; int bufpos = 0; double nextpts; - double outpts; int orig_samples = samples; // for ectx PTS fields @@ -281,38 +272,9 @@ static int play(struct ao *ao, void **data, int samples, int flags) samples = (bytelen + extralen) / ao->sstride; } - if (pts == MP_NOPTS_VALUE) { - MP_WARN(ao, "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) { - // We don't know the muxer time_base anymore, and can't, because we - // might start encoding before the muxer is opened. (The muxer decides - // the final AVStream.time_base when opening the muxer.) - ac->worst_time_base = enc->encoder->time_base; - - // NOTE: we use the following "axiom" of av_rescale_q: - // if time base A is worse than time base B, then - // av_rescale_q(av_rescale_q(x, A, B), B, A) == x - // this can be proven as long as av_rescale_q rounds to nearest, which - // it currently does - - // av_rescale_q(x, A, B) * B = "round x*A to nearest multiple of B" - // and: - // av_rescale_q(av_rescale_q(x, A, B), B, A) * A - // == "round av_rescale_q(x, A, B)*B to nearest multiple of A" - // == "round 'round x*A to nearest multiple of B' to nearest multiple of A" - // - // assume this fails. Then there is a value of x*A, for which the - // nearest multiple of B is outside the range [(x-0.5)*A, (x+0.5)*A[. - // Absurd, as this range MUST contain at least one multiple of B. - } - - // Fix and apply the discontinuity pts offset. - if (!enc->options->rawts && enc->options->copyts) { - // fix the discontinuity pts offset + double outpts = pts; + if (!enc->options->rawts) { + // Fix and apply the discontinuity pts offset. nextpts = pts; if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) { ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts; @@ -326,8 +288,6 @@ static int play(struct ao *ao, void **data, int samples, int flags) } outpts = pts + ectx->discontinuity_pts_offset; - } else { - outpts = pts; } pthread_mutex_unlock(&ectx->lock); @@ -349,7 +309,7 @@ static int play(struct ao *ao, void **data, int samples, int flags) pthread_mutex_lock(&ectx->lock); // Set next allowed input pts value (input side). - if (!enc->options->rawts && enc->options->copyts) { + if (!enc->options->rawts) { nextpts = ac->expected_next_pts + ectx->discontinuity_pts_offset; if (nextpts > ectx->next_in_pts) ectx->next_in_pts = nextpts; diff --git a/common/encode.h b/common/encode.h index fcf4a8317e..4a09fedaa2 100644 --- a/common/encode.h +++ b/common/encode.h @@ -34,19 +34,13 @@ struct encode_opts { char *file; char *format; char **fopts; - float fps; - float maxfps; char *vcodec; char **vopts; char *acodec; char **aopts; - int harddup; float voffset; float aoffset; - int copyts; int rawts; - int autofps; - int neverdrop; int video_first; int audio_first; int copy_metadata; diff --git a/common/encode_lavc.c b/common/encode_lavc.c index 5d34af609b..2473609921 100644 --- a/common/encode_lavc.c +++ b/common/encode_lavc.c @@ -81,21 +81,15 @@ const struct m_sub_options encode_config = { OPT_STRING("o", file, M_OPT_FIXED | CONF_NOCFG | CONF_PRE_PARSE | M_OPT_FILE), OPT_STRING("of", format, M_OPT_FIXED), OPT_KEYVALUELIST("ofopts", fopts, M_OPT_FIXED | M_OPT_HAVE_HELP), - OPT_FLOATRANGE("ofps", fps, M_OPT_FIXED, 0.0, 1000000.0), - OPT_FLOATRANGE("omaxfps", maxfps, M_OPT_FIXED, 0.0, 1000000.0), OPT_STRING("ovc", vcodec, M_OPT_FIXED), OPT_KEYVALUELIST("ovcopts", vopts, M_OPT_FIXED | M_OPT_HAVE_HELP), OPT_STRING("oac", acodec, M_OPT_FIXED), OPT_KEYVALUELIST("oacopts", aopts, M_OPT_FIXED | M_OPT_HAVE_HELP), - OPT_FLAG("oharddup", harddup, M_OPT_FIXED), OPT_FLOATRANGE("ovoffset", voffset, M_OPT_FIXED, -1000000.0, 1000000.0, .deprecation_message = "--audio-delay (once unbroken)"), OPT_FLOATRANGE("oaoffset", aoffset, M_OPT_FIXED, -1000000.0, 1000000.0, .deprecation_message = "--audio-delay (once unbroken)"), - OPT_FLAG("ocopyts", copyts, M_OPT_FIXED), OPT_FLAG("orawts", rawts, M_OPT_FIXED), - OPT_FLAG("oautofps", autofps, M_OPT_FIXED), - OPT_FLAG("oneverdrop", neverdrop, M_OPT_FIXED), OPT_FLAG("ovfirst", video_first, M_OPT_FIXED, .deprecation_message = "no replacement"), OPT_FLAG("oafirst", audio_first, M_OPT_FIXED, @@ -103,6 +97,13 @@ const struct m_sub_options encode_config = { OPT_FLAG("ocopy-metadata", copy_metadata, M_OPT_FIXED), OPT_KEYVALUELIST("oset-metadata", set_metadata, M_OPT_FIXED), OPT_STRINGLIST("oremove-metadata", remove_metadata, M_OPT_FIXED), + + OPT_REMOVED("ocopyts", "ocopyts is now the default"), + OPT_REMOVED("oneverdrop", "no replacement"), + OPT_REMOVED("oharddup", "use --vf-add=fps=VALUE"), + OPT_REMOVED("ofps", "no replacement (use --vf-add=fps=VALUE for CFR)"), + OPT_REMOVED("oautofps", "no replacement"), + OPT_REMOVED("omaxfps", "no replacement"), {0} }, .size = sizeof(struct encode_opts), diff --git a/video/out/vo_lavc.c b/video/out/vo_lavc.c index 3e0b338b68..848985edc0 100644 --- a/video/out/vo_lavc.c +++ b/video/out/vo_lavc.c @@ -38,21 +38,6 @@ struct priv { struct encoder_context *enc; - int harddup; - - double lastpts; - int64_t lastipts; - int64_t lastframeipts; - int64_t lastencodedipts; - int64_t mindeltapts; - double expected_next_pts; - mp_image_t *lastimg; - int lastdisplaycount; - - double last_video_in_pts; - - AVRational worst_time_base; - bool shutdown; }; @@ -65,25 +50,21 @@ static int preinit(struct vo *vo) if (!vc->enc) return -1; talloc_steal(vc, vc->enc); - vc->harddup = vc->enc->options->harddup; - vc->last_video_in_pts = MP_NOPTS_VALUE; return 0; } static void uninit(struct vo *vo) { struct priv *vc = vo->priv; + struct encoder_context *enc = vc->enc; - if (vc->lastipts >= 0 && !vc->shutdown) - draw_image(vo, NULL); - - mp_image_unrefp(&vc->lastimg); + if (!vc->shutdown) + encoder_encode(enc, NULL); // finish encoding } static int reconfig2(struct vo *vo, struct mp_image *img) { struct priv *vc = vo->priv; - struct encode_lavc_context *ctx = vo->encode_lavc_ctx; AVCodecContext *encoder = vc->enc->encoder; struct mp_image_params *params = &img->params; @@ -117,10 +98,6 @@ static int reconfig2(struct vo *vo, struct mp_image *img) // - Second calls after reconfigure() already succeeded once return early // (due to the avcodec_is_open() check above). - vc->lastipts = AV_NOPTS_VALUE; - vc->lastframeipts = AV_NOPTS_VALUE; - vc->lastencodedipts = AV_NOPTS_VALUE; - if (pix_fmt == AV_PIX_FMT_NONE) { MP_FATAL(vo, "Format %s not supported by lavc.\n", mp_imgfmt_to_name(params->imgfmt)); @@ -136,27 +113,15 @@ static int reconfig2(struct vo *vo, struct mp_image *img) AVRational tb; - if (ctx->options->fps > 0) { - tb = av_d2q(ctx->options->fps, ctx->options->fps * 1001 + 2); - } else if (ctx->options->autofps && img->nominal_fps > 0) { - tb = av_d2q(img->nominal_fps, img->nominal_fps * 1001 + 2); - MP_INFO(vo, "option --ofps not specified " - "but --oautofps is active, using guess of %u/%u\n", - (unsigned)tb.num, (unsigned)tb.den); - } else { - // we want to handle: - // 1/25 - // 1001/24000 - // 1001/30000 - // for this we would need 120000fps... - // however, mpeg-4 only allows 16bit values - // so let's take 1001/30000 out - tb.num = 24000; - tb.den = 1; - MP_INFO(vo, "option --ofps not specified " - "and fps could not be inferred, using guess of %u/%u\n", - (unsigned)tb.num, (unsigned)tb.den); - } + // we want to handle: + // 1/25 + // 1001/24000 + // 1001/30000 + // for this we would need 120000fps... + // however, mpeg-4 only allows 16bit values + // so let's take 1001/30000 out + tb.num = 24000; + tb.den = 1; const AVRational *rates = encoder->codec->supported_framerates; if (rates && rates[0].den) @@ -199,180 +164,57 @@ static void draw_image(struct vo *vo, mp_image_t *mpi) struct encoder_context *enc = vc->enc; struct encode_lavc_context *ectx = enc->encode_lavc_ctx; AVCodecContext *avc = enc->encoder; - int64_t frameipts; - double nextpts; - - double pts = mpi ? mpi->pts : MP_NOPTS_VALUE; - if (mpi) { - assert(vo->params); - - struct mp_osd_res dim = osd_res_from_image_params(vo->params); - - osd_draw_on_image(vo->osd, dim, mpi->pts, OSD_DRAW_SUB_ONLY, mpi); - } + struct mp_osd_res dim = osd_res_from_image_params(vo->params); + osd_draw_on_image(vo->osd, dim, mpi->pts, OSD_DRAW_SUB_ONLY, mpi); if (vc->shutdown) goto done; - if (pts == MP_NOPTS_VALUE) { - if (mpi) - MP_WARN(vo, "frame without pts, please report; synthesizing pts instead\n"); - pts = vc->expected_next_pts; - } - - if (vc->worst_time_base.den == 0) { - // We don't know the muxer time_base anymore, and can't, because we - // might start encoding before the muxer is opened. (The muxer decides - // the final AVStream.time_base when opening the muxer.) - vc->worst_time_base = avc->time_base; - - if (enc->options->maxfps) { - vc->mindeltapts = ceil(vc->worst_time_base.den / - (vc->worst_time_base.num * enc->options->maxfps)); - } else { - vc->mindeltapts = 0; - } - - // NOTE: we use the following "axiom" of av_rescale_q: - // if time base A is worse than time base B, then - // av_rescale_q(av_rescale_q(x, A, B), B, A) == x - // this can be proven as long as av_rescale_q rounds to nearest, which - // it currently does - - // av_rescale_q(x, A, B) * B = "round x*A to nearest multiple of B" - // and: - // av_rescale_q(av_rescale_q(x, A, B), B, A) * A - // == "round av_rescale_q(x, A, B)*B to nearest multiple of A" - // == "round 'round x*A to nearest multiple of B' to nearest multiple of A" - // - // assume this fails. Then there is a value of x*A, for which the - // nearest multiple of B is outside the range [(x-0.5)*A, (x+0.5)*A[. - // Absurd, as this range MUST contain at least one multiple of B. - } - - double timeunit = (double)vc->worst_time_base.num / vc->worst_time_base.den; - // Lock for shared timestamp fields. pthread_mutex_lock(&ectx->lock); - double outpts; - if (enc->options->rawts) { - outpts = pts; - } else if (enc->options->copyts) { + double pts = mpi->pts; + double outpts = pts; + if (!enc->options->rawts) { // fix the discontinuity pts offset - nextpts = pts; 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->discontinuity_pts_offset = ectx->next_in_pts - pts; + } else if (fabs(pts + ectx->discontinuity_pts_offset - ectx->next_in_pts) > 30) { MP_WARN(vo, "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; + pts + ectx->discontinuity_pts_offset - ectx->next_in_pts); + ectx->discontinuity_pts_offset = ectx->next_in_pts - pts; } outpts = pts + ectx->discontinuity_pts_offset; - } else { - // adjust pts by knowledge of audio pts vs audio playback time - double duration = 0; - if (vc->last_video_in_pts != MP_NOPTS_VALUE) - duration = pts - vc->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; - vc->last_video_in_pts = pts; - frameipts = floor((outpts + encoder_get_offset(enc)) / timeunit + 0.5); - // calculate expected pts of next video frame - vc->expected_next_pts = pts + timeunit; + outpts += encoder_get_offset(enc); - if (!enc->options->rawts && enc->options->copyts) { + if (!enc->options->rawts) { + // calculate expected pts of next video frame + double timeunit = av_q2d(avc->time_base); + double expected_next_pts = pts + timeunit; // set next allowed output pts value - nextpts = vc->expected_next_pts + ectx->discontinuity_pts_offset; + double nextpts = expected_next_pts + ectx->discontinuity_pts_offset; if (nextpts > ectx->next_in_pts) ectx->next_in_pts = nextpts; } pthread_mutex_unlock(&ectx->lock); - // never-drop mode - if (enc->options->neverdrop) { - int64_t step = vc->mindeltapts ? vc->mindeltapts : 1; - if (frameipts < vc->lastipts + step) { - MP_INFO(vo, "--oneverdrop increased pts by %d\n", - (int) (vc->lastipts - frameipts + step)); - frameipts = vc->lastipts + step; - vc->lastpts = frameipts * timeunit - encoder_get_offset(enc); - } - } - - if (vc->lastipts != AV_NOPTS_VALUE) { - // we have a valid image in lastimg - while (vc->lastimg && vc->lastipts < frameipts) { - int64_t thisduration = vc->harddup ? 1 : (frameipts - vc->lastipts); - - // we will ONLY encode this frame if it can be encoded at at least - // vc->mindeltapts after the last encoded frame! - int64_t skipframes = (vc->lastencodedipts == AV_NOPTS_VALUE) - ? 0 : vc->lastencodedipts + vc->mindeltapts - vc->lastipts; - if (skipframes < 0) - skipframes = 0; - - if (thisduration > skipframes) { - AVFrame *frame = mp_image_to_av_frame(vc->lastimg); - if (!frame) - abort(); - - // this is a nop, unless the worst time base is the STREAM time base - frame->pts = av_rescale_q(vc->lastipts + skipframes, - vc->worst_time_base, avc->time_base); - frame->pict_type = 0; // keep this at unknown/undefined - frame->quality = avc->global_quality; - encoder_encode(enc, frame); - av_frame_free(&frame); - - vc->lastdisplaycount += 1; - vc->lastencodedipts = vc->lastipts + skipframes; - } - - vc->lastipts += thisduration; - } - } + AVFrame *frame = mp_image_to_av_frame(mpi); + if (!frame) + abort(); - if (!mpi) { - // finish encoding - encoder_encode(enc, NULL); - } else { - if (frameipts >= vc->lastframeipts) { - if (vc->lastframeipts != AV_NOPTS_VALUE && vc->lastdisplaycount != 1) - MP_INFO(vo, "Frame at pts %d got displayed %d times\n", - (int) vc->lastframeipts, vc->lastdisplaycount); - talloc_free(vc->lastimg); - vc->lastimg = mpi; - mpi = NULL; - - vc->lastframeipts = vc->lastipts = frameipts; - if (enc->options->rawts && vc->lastipts < 0) { - MP_ERR(vo, "why does this happen? DEBUG THIS! vc->lastipts = %lld\n", - (long long) vc->lastipts); - vc->lastipts = -1; - } - vc->lastdisplaycount = 0; - } else { - MP_INFO(vo, "Frame at pts %d got dropped " - "entirely because pts went backwards\n", (int) frameipts); - } - } + frame->pts = rint(outpts * av_q2d(av_inv_q(avc->time_base))); + frame->pict_type = 0; // keep this at unknown/undefined + frame->quality = avc->global_quality; + encoder_encode(enc, frame); + av_frame_free(&frame); done: talloc_free(mpi); |