mirror of
https://github.com/mpv-player/mpv
synced 2025-01-18 13:14:36 +00:00
encode: remove old timestamp handling
This effectively makes --ocopyts the default. The --ocopyts option itself is also removed, because it's redundant.
This commit is contained in:
parent
60dade1040
commit
f18c4175ad
@ -3,8 +3,7 @@ General usage
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
mpv infile -o outfile [-of outfileformat] [-ofopts formatoptions] \
|
mpv infile -o outfile [-of outfileformat] [-ofopts formatoptions] [-orawts] \
|
||||||
[-ofps outfps | -oautofps] [-oharddup] [-ocopyts | -orawts] [-oneverdrop] \
|
|
||||||
[(any other mpv options)] \
|
[(any other mpv options)] \
|
||||||
-ovc outvideocodec [-ovcopts outvideocodecoptions] \
|
-ovc outvideocodec [-ovcopts outvideocodecoptions] \
|
||||||
-oac outaudiocodec [-oacopts outaudiocodecoptions]
|
-oac outaudiocodec [-oacopts outaudiocodecoptions]
|
||||||
@ -60,13 +59,13 @@ for.
|
|||||||
Typical MPEG-4 Part 2 ("ASP", "DivX") encoding, AVI container::
|
Typical MPEG-4 Part 2 ("ASP", "DivX") encoding, AVI container::
|
||||||
|
|
||||||
mpv infile -o outfile.avi \
|
mpv infile -o outfile.avi \
|
||||||
-ofps 25 \
|
--vf=fps=25 \
|
||||||
-ovc mpeg4 -ovcopts qscale=4 \
|
-ovc mpeg4 -ovcopts qscale=4 \
|
||||||
-oac libmp3lame -oacopts ab=128k
|
-oac libmp3lame -oacopts ab=128k
|
||||||
|
|
||||||
Note: AVI does not support variable frame rate, so -ofps must be used. The
|
Note: AVI does not support variable frame rate, so the fps filter must be used.
|
||||||
frame rate should ideally match the input (25 for PAL, 24000/1001 or 30000/1001
|
The frame rate should ideally match the input (25 for PAL, 24000/1001 or
|
||||||
for NTSC)
|
30000/1001 for NTSC)
|
||||||
|
|
||||||
Typical MPEG-4 Part 10 ("AVC", "H.264") encoding, Matroska (MKV) container::
|
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 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
|
* 2-pass encoding (specify flags=+pass1 in the first pass's -ovcopts, specify
|
||||||
flags=+pass2 in the second pass)
|
flags=+pass2 in the second pass)
|
||||||
* Hardcoding subtitles using vobsub, ass or srt subtitle rendering (just
|
* Hardcoding subtitles using vobsub, ass or srt subtitle rendering (just
|
||||||
|
@ -94,6 +94,9 @@ Interface changes
|
|||||||
the future. (This kind of waiting was always a feature to prevent that
|
the future. (This kind of waiting was always a feature to prevent that
|
||||||
playback is started while scripts are only half-loaded.)
|
playback is started while scripts are only half-loaded.)
|
||||||
- deprecate --ovoffset, --oaoffset, --ovfirst, --oafirst
|
- 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
|
- 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,
|
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
|
although you can just insert the stereo3d filter manually. The obscurity
|
||||||
|
@ -24,32 +24,6 @@ You can encode files from one format/codec to another using this facility.
|
|||||||
``--ofopts=""``
|
``--ofopts=""``
|
||||||
Completely empties the options list.
|
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>``
|
``--oac=<codec>``
|
||||||
Specifies the output audio codec. See ``--oac=help`` for a full list of
|
Specifies the output audio codec. See ``--oac=help`` for a full list of
|
||||||
supported codecs.
|
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.
|
Force the video stream to become the first stream in the output.
|
||||||
By default, the order is unspecified. Deprecated.
|
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``
|
``--orawts``
|
||||||
Copies input pts to the output video (not supported by some output
|
Copies input pts to the output video (not supported by some output
|
||||||
container formats, e.g. AVI). In this mode, discontinuities are not fixed
|
container formats, e.g. AVI). In this mode, discontinuities are not fixed
|
||||||
|
@ -169,7 +169,7 @@ static void uninit(struct ao *ao)
|
|||||||
double outpts = ac->expected_next_pts;
|
double outpts = ac->expected_next_pts;
|
||||||
|
|
||||||
pthread_mutex_lock(&ectx->lock);
|
pthread_mutex_lock(&ectx->lock);
|
||||||
if (!ac->enc->options->rawts && ac->enc->options->copyts)
|
if (!ac->enc->options->rawts)
|
||||||
outpts += ectx->discontinuity_pts_offset;
|
outpts += ectx->discontinuity_pts_offset;
|
||||||
pthread_mutex_unlock(&ectx->lock);
|
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;
|
frame->linesize[0] = frame->nb_samples * ao->sstride;
|
||||||
|
|
||||||
if (ac->enc->options->rawts || ac->enc->options->copyts) {
|
frame->pts = rint(apts * av_q2d(av_inv_q(encoder->time_base)));
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t frame_pts = av_rescale_q(frame->pts, encoder->time_base,
|
int64_t frame_pts = av_rescale_q(frame->pts, encoder->time_base,
|
||||||
ac->worst_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;
|
struct encode_lavc_context *ectx = ao->encode_lavc_ctx;
|
||||||
int bufpos = 0;
|
int bufpos = 0;
|
||||||
double nextpts;
|
double nextpts;
|
||||||
double outpts;
|
|
||||||
int orig_samples = samples;
|
int orig_samples = samples;
|
||||||
|
|
||||||
// for ectx PTS fields
|
// 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;
|
samples = (bytelen + extralen) / ao->sstride;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pts == MP_NOPTS_VALUE) {
|
double outpts = pts;
|
||||||
MP_WARN(ao, "frame without pts, please report; synthesizing pts instead\n");
|
if (!enc->options->rawts) {
|
||||||
// synthesize pts from previous expected next pts
|
// Fix and apply the discontinuity pts offset.
|
||||||
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
|
|
||||||
nextpts = pts;
|
nextpts = pts;
|
||||||
if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) {
|
if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) {
|
||||||
ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
|
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;
|
outpts = pts + ectx->discontinuity_pts_offset;
|
||||||
} else {
|
|
||||||
outpts = pts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(&ectx->lock);
|
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);
|
pthread_mutex_lock(&ectx->lock);
|
||||||
|
|
||||||
// Set next allowed input pts value (input side).
|
// 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;
|
nextpts = ac->expected_next_pts + ectx->discontinuity_pts_offset;
|
||||||
if (nextpts > ectx->next_in_pts)
|
if (nextpts > ectx->next_in_pts)
|
||||||
ectx->next_in_pts = nextpts;
|
ectx->next_in_pts = nextpts;
|
||||||
|
@ -34,19 +34,13 @@ struct encode_opts {
|
|||||||
char *file;
|
char *file;
|
||||||
char *format;
|
char *format;
|
||||||
char **fopts;
|
char **fopts;
|
||||||
float fps;
|
|
||||||
float maxfps;
|
|
||||||
char *vcodec;
|
char *vcodec;
|
||||||
char **vopts;
|
char **vopts;
|
||||||
char *acodec;
|
char *acodec;
|
||||||
char **aopts;
|
char **aopts;
|
||||||
int harddup;
|
|
||||||
float voffset;
|
float voffset;
|
||||||
float aoffset;
|
float aoffset;
|
||||||
int copyts;
|
|
||||||
int rawts;
|
int rawts;
|
||||||
int autofps;
|
|
||||||
int neverdrop;
|
|
||||||
int video_first;
|
int video_first;
|
||||||
int audio_first;
|
int audio_first;
|
||||||
int copy_metadata;
|
int copy_metadata;
|
||||||
|
@ -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("o", file, M_OPT_FIXED | CONF_NOCFG | CONF_PRE_PARSE | M_OPT_FILE),
|
||||||
OPT_STRING("of", format, M_OPT_FIXED),
|
OPT_STRING("of", format, M_OPT_FIXED),
|
||||||
OPT_KEYVALUELIST("ofopts", fopts, M_OPT_FIXED | M_OPT_HAVE_HELP),
|
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_STRING("ovc", vcodec, M_OPT_FIXED),
|
||||||
OPT_KEYVALUELIST("ovcopts", vopts, M_OPT_FIXED | M_OPT_HAVE_HELP),
|
OPT_KEYVALUELIST("ovcopts", vopts, M_OPT_FIXED | M_OPT_HAVE_HELP),
|
||||||
OPT_STRING("oac", acodec, M_OPT_FIXED),
|
OPT_STRING("oac", acodec, M_OPT_FIXED),
|
||||||
OPT_KEYVALUELIST("oacopts", aopts, M_OPT_FIXED | M_OPT_HAVE_HELP),
|
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,
|
OPT_FLOATRANGE("ovoffset", voffset, M_OPT_FIXED, -1000000.0, 1000000.0,
|
||||||
.deprecation_message = "--audio-delay (once unbroken)"),
|
.deprecation_message = "--audio-delay (once unbroken)"),
|
||||||
OPT_FLOATRANGE("oaoffset", aoffset, M_OPT_FIXED, -1000000.0, 1000000.0,
|
OPT_FLOATRANGE("oaoffset", aoffset, M_OPT_FIXED, -1000000.0, 1000000.0,
|
||||||
.deprecation_message = "--audio-delay (once unbroken)"),
|
.deprecation_message = "--audio-delay (once unbroken)"),
|
||||||
OPT_FLAG("ocopyts", copyts, M_OPT_FIXED),
|
|
||||||
OPT_FLAG("orawts", rawts, 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,
|
OPT_FLAG("ovfirst", video_first, M_OPT_FIXED,
|
||||||
.deprecation_message = "no replacement"),
|
.deprecation_message = "no replacement"),
|
||||||
OPT_FLAG("oafirst", audio_first, M_OPT_FIXED,
|
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_FLAG("ocopy-metadata", copy_metadata, M_OPT_FIXED),
|
||||||
OPT_KEYVALUELIST("oset-metadata", set_metadata, M_OPT_FIXED),
|
OPT_KEYVALUELIST("oset-metadata", set_metadata, M_OPT_FIXED),
|
||||||
OPT_STRINGLIST("oremove-metadata", remove_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}
|
{0}
|
||||||
},
|
},
|
||||||
.size = sizeof(struct encode_opts),
|
.size = sizeof(struct encode_opts),
|
||||||
|
@ -38,21 +38,6 @@
|
|||||||
struct priv {
|
struct priv {
|
||||||
struct encoder_context *enc;
|
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;
|
bool shutdown;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -65,25 +50,21 @@ static int preinit(struct vo *vo)
|
|||||||
if (!vc->enc)
|
if (!vc->enc)
|
||||||
return -1;
|
return -1;
|
||||||
talloc_steal(vc, vc->enc);
|
talloc_steal(vc, vc->enc);
|
||||||
vc->harddup = vc->enc->options->harddup;
|
|
||||||
vc->last_video_in_pts = MP_NOPTS_VALUE;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uninit(struct vo *vo)
|
static void uninit(struct vo *vo)
|
||||||
{
|
{
|
||||||
struct priv *vc = vo->priv;
|
struct priv *vc = vo->priv;
|
||||||
|
struct encoder_context *enc = vc->enc;
|
||||||
|
|
||||||
if (vc->lastipts >= 0 && !vc->shutdown)
|
if (!vc->shutdown)
|
||||||
draw_image(vo, NULL);
|
encoder_encode(enc, NULL); // finish encoding
|
||||||
|
|
||||||
mp_image_unrefp(&vc->lastimg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int reconfig2(struct vo *vo, struct mp_image *img)
|
static int reconfig2(struct vo *vo, struct mp_image *img)
|
||||||
{
|
{
|
||||||
struct priv *vc = vo->priv;
|
struct priv *vc = vo->priv;
|
||||||
struct encode_lavc_context *ctx = vo->encode_lavc_ctx;
|
|
||||||
AVCodecContext *encoder = vc->enc->encoder;
|
AVCodecContext *encoder = vc->enc->encoder;
|
||||||
|
|
||||||
struct mp_image_params *params = &img->params;
|
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
|
// - Second calls after reconfigure() already succeeded once return early
|
||||||
// (due to the avcodec_is_open() check above).
|
// (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) {
|
if (pix_fmt == AV_PIX_FMT_NONE) {
|
||||||
MP_FATAL(vo, "Format %s not supported by lavc.\n",
|
MP_FATAL(vo, "Format %s not supported by lavc.\n",
|
||||||
mp_imgfmt_to_name(params->imgfmt));
|
mp_imgfmt_to_name(params->imgfmt));
|
||||||
@ -136,27 +113,15 @@ static int reconfig2(struct vo *vo, struct mp_image *img)
|
|||||||
|
|
||||||
AVRational tb;
|
AVRational tb;
|
||||||
|
|
||||||
if (ctx->options->fps > 0) {
|
// we want to handle:
|
||||||
tb = av_d2q(ctx->options->fps, ctx->options->fps * 1001 + 2);
|
// 1/25
|
||||||
} else if (ctx->options->autofps && img->nominal_fps > 0) {
|
// 1001/24000
|
||||||
tb = av_d2q(img->nominal_fps, img->nominal_fps * 1001 + 2);
|
// 1001/30000
|
||||||
MP_INFO(vo, "option --ofps not specified "
|
// for this we would need 120000fps...
|
||||||
"but --oautofps is active, using guess of %u/%u\n",
|
// however, mpeg-4 only allows 16bit values
|
||||||
(unsigned)tb.num, (unsigned)tb.den);
|
// so let's take 1001/30000 out
|
||||||
} else {
|
tb.num = 24000;
|
||||||
// we want to handle:
|
tb.den = 1;
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
const AVRational *rates = encoder->codec->supported_framerates;
|
const AVRational *rates = encoder->codec->supported_framerates;
|
||||||
if (rates && rates[0].den)
|
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 encoder_context *enc = vc->enc;
|
||||||
struct encode_lavc_context *ectx = enc->encode_lavc_ctx;
|
struct encode_lavc_context *ectx = enc->encode_lavc_ctx;
|
||||||
AVCodecContext *avc = enc->encoder;
|
AVCodecContext *avc = enc->encoder;
|
||||||
int64_t frameipts;
|
|
||||||
double nextpts;
|
|
||||||
|
|
||||||
double pts = mpi ? mpi->pts : MP_NOPTS_VALUE;
|
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 (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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vc->shutdown)
|
if (vc->shutdown)
|
||||||
goto done;
|
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.
|
// Lock for shared timestamp fields.
|
||||||
pthread_mutex_lock(&ectx->lock);
|
pthread_mutex_lock(&ectx->lock);
|
||||||
|
|
||||||
double outpts;
|
double pts = mpi->pts;
|
||||||
if (enc->options->rawts) {
|
double outpts = pts;
|
||||||
outpts = pts;
|
if (!enc->options->rawts) {
|
||||||
} else if (enc->options->copyts) {
|
|
||||||
// fix the discontinuity pts offset
|
// fix the discontinuity pts offset
|
||||||
nextpts = pts;
|
|
||||||
if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) {
|
if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) {
|
||||||
ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
|
ectx->discontinuity_pts_offset = ectx->next_in_pts - pts;
|
||||||
} else if (fabs(nextpts + ectx->discontinuity_pts_offset -
|
} else if (fabs(pts + ectx->discontinuity_pts_offset -
|
||||||
ectx->next_in_pts) > 30)
|
ectx->next_in_pts) > 30)
|
||||||
{
|
{
|
||||||
MP_WARN(vo, "detected an unexpected discontinuity (pts jumped by "
|
MP_WARN(vo, "detected an unexpected discontinuity (pts jumped by "
|
||||||
"%f seconds)\n",
|
"%f seconds)\n",
|
||||||
nextpts + ectx->discontinuity_pts_offset - ectx->next_in_pts);
|
pts + ectx->discontinuity_pts_offset - ectx->next_in_pts);
|
||||||
ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
|
ectx->discontinuity_pts_offset = ectx->next_in_pts - pts;
|
||||||
}
|
}
|
||||||
|
|
||||||
outpts = pts + ectx->discontinuity_pts_offset;
|
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
|
outpts += encoder_get_offset(enc);
|
||||||
vc->expected_next_pts = pts + timeunit;
|
|
||||||
|
|
||||||
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
|
// 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)
|
if (nextpts > ectx->next_in_pts)
|
||||||
ectx->next_in_pts = nextpts;
|
ectx->next_in_pts = nextpts;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(&ectx->lock);
|
pthread_mutex_unlock(&ectx->lock);
|
||||||
|
|
||||||
// never-drop mode
|
AVFrame *frame = mp_image_to_av_frame(mpi);
|
||||||
if (enc->options->neverdrop) {
|
if (!frame)
|
||||||
int64_t step = vc->mindeltapts ? vc->mindeltapts : 1;
|
abort();
|
||||||
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) {
|
frame->pts = rint(outpts * av_q2d(av_inv_q(avc->time_base)));
|
||||||
// we have a valid image in lastimg
|
frame->pict_type = 0; // keep this at unknown/undefined
|
||||||
while (vc->lastimg && vc->lastipts < frameipts) {
|
frame->quality = avc->global_quality;
|
||||||
int64_t thisduration = vc->harddup ? 1 : (frameipts - vc->lastipts);
|
encoder_encode(enc, frame);
|
||||||
|
av_frame_free(&frame);
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
done:
|
done:
|
||||||
talloc_free(mpi);
|
talloc_free(mpi);
|
||||||
|
Loading…
Reference in New Issue
Block a user