[FFmpeg-cvslog] fftools/ffmpeg: add an option for writing pre-muxing stats
Anton Khirnov
git at videolan.org
Thu Feb 9 16:37:10 EET 2023
ffmpeg | branch: master | Anton Khirnov <anton at khirnov.net> | Mon Feb 6 13:14:53 2023 +0100| [42a0dd6e7e3bc343d65a5499bd1373ad15160ab7] | committer: Anton Khirnov
fftools/ffmpeg: add an option for writing pre-muxing stats
Analogous to -enc_stats*, but happens right before muxing. Useful
because bitstream filters and the sync queue can modify packets after
encoding and before muxing. Also has access to the muxing timebase.
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=42a0dd6e7e3bc343d65a5499bd1373ad15160ab7
---
Changelog | 3 ++-
doc/ffmpeg.texi | 13 +++++++++----
fftools/ffmpeg.c | 14 ++++++++------
fftools/ffmpeg.h | 8 ++++++++
fftools/ffmpeg_mux.c | 10 +++++++++-
fftools/ffmpeg_mux.h | 2 ++
fftools/ffmpeg_mux_init.c | 23 ++++++++++++++++++-----
fftools/ffmpeg_opt.c | 4 ++++
8 files changed, 60 insertions(+), 17 deletions(-)
diff --git a/Changelog b/Changelog
index df9cd69da2..bd9fe9922d 100644
--- a/Changelog
+++ b/Changelog
@@ -32,7 +32,8 @@ version <next>:
- WADY DPCM decoder and demuxer
- CBD2 DPCM decoder
- ssim360 video filter
-- ffmpeg CLI new options: -enc_stats_pre[_fmt], -enc_stats_post[_fmt]
+- ffmpeg CLI new options: -enc_stats_pre[_fmt], -enc_stats_post[_fmt],
+ -stats_mux_pre[_fmt]
- hstack_vaapi, vstack_vaapi and xstack_vaapi filters
- XMD ADPCM decoder and demuxer
- media100 to mjpegb bsf
diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi
index 592c4b4393..076956d128 100644
--- a/doc/ffmpeg.texi
+++ b/doc/ffmpeg.texi
@@ -2063,14 +2063,17 @@ or invalid output files.
@item -enc_stats_pre[:@var{stream_specifier}] @var{path} (@emph{output,per-stream})
@item -enc_stats_post[:@var{stream_specifier}] @var{path} (@emph{output,per-stream})
+ at item -stats_mux_pre[:@var{stream_specifier}] @var{path} (@emph{output,per-stream})
Write per-frame encoding information about the matching streams into the file
given by @var{path}.
@option{-enc_stats_pre} writes information about raw video or audio frames right
before they are sent for encoding, while @option{-enc_stats_post} writes
-information about encoded packets as they are received from the encoder. Every
-frame or packet produces one line in the specified file. The format of this line
-is controlled by @option{-enc_stats_pre_fmt} / @option{-enc_stats_post_fmt}.
+information about encoded packets as they are received from the encoder.
+ at option{-stats_mux_pre} writes information about packets just as they are about to
+be sent to the muxer. Every frame or packet produces one line in the specified
+file. The format of this line is controlled by @option{-enc_stats_pre_fmt} /
+ at option{-enc_stats_post_fmt} / @option{-stats_mux_pre_fmt}.
When stats for multiple streams are written into a single file, the lines
corresponding to different streams will be interleaved. The precise order of
@@ -2079,8 +2082,9 @@ different invocations of the program, even with the same options.
@item -enc_stats_pre_fmt[:@var{stream_specifier}] @var{format_spec} (@emph{output,per-stream})
@item -enc_stats_post_fmt[:@var{stream_specifier}] @var{format_spec} (@emph{output,per-stream})
+ at item -stats_mux_pre_fmt[:@var{stream_specifier}] @var{format_spec} (@emph{output,per-stream})
Specify the format for the lines written with @option{-enc_stats_pre} /
- at option{-enc_stats_post}.
+ at option{-enc_stats_post} / @option{-stats_mux_pre}.
@var{format_spec} is a string that may contain directives of the form
@var{@{fmt@}}. @var{format_spec} is backslash-escaped --- use \@{, \@}, and \\
@@ -2097,6 +2101,7 @@ Index of the output stream in the file.
@item n
Frame number. Pre-encoding: number of frames sent to the encoder so far.
Post-encoding: number of packets received from the encoder so far.
+Muxing: number of packets submitted to the muxer for this stream so far.
@item ni
Input frame number. Index of the input frame (i.e. output by a decoder) that
diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
index 32e0c3febd..aac393c714 100644
--- a/fftools/ffmpeg.c
+++ b/fftools/ffmpeg.c
@@ -808,8 +808,9 @@ static void update_video_stats(OutputStream *ost, const AVPacket *pkt, int write
fprintf(vstats_file, "type= %c\n", av_get_picture_type_char(ost->pict_type));
}
-static void enc_stats_write(OutputStream *ost, EncStats *es,
- const AVFrame *frame, const AVPacket *pkt)
+void enc_stats_write(OutputStream *ost, EncStats *es,
+ const AVFrame *frame, const AVPacket *pkt,
+ uint64_t frame_num)
{
AVIOContext *io = es->io;
AVRational tb = frame ? frame->time_base : pkt->time_base;
@@ -840,12 +841,12 @@ static void enc_stats_write(OutputStream *ost, EncStats *es,
case ENC_STATS_PTS_TIME: avio_printf(io, "%g", pts * av_q2d(tb)); continue;
case ENC_STATS_PTS_TIME_IN: avio_printf(io, "%g", ptsi == INT64_MAX ?
INFINITY : ptsi * av_q2d(tbi)); continue;
+ case ENC_STATS_FRAME_NUM: avio_printf(io, "%"PRIu64, frame_num); continue;
case ENC_STATS_FRAME_NUM_IN: avio_printf(io, "%"PRIu64, fd ? fd->idx : -1); continue;
}
if (frame) {
switch (c->type) {
- case ENC_STATS_FRAME_NUM: avio_printf(io, "%"PRIu64, ost->frames_encoded); continue;
case ENC_STATS_SAMPLE_NUM: avio_printf(io, "%"PRIu64, ost->samples_encoded); continue;
case ENC_STATS_NB_SAMPLES: avio_printf(io, "%d", frame->nb_samples); continue;
default: av_assert0(0);
@@ -855,7 +856,6 @@ static void enc_stats_write(OutputStream *ost, EncStats *es,
case ENC_STATS_DTS: avio_printf(io, "%"PRId64, pkt->dts); continue;
case ENC_STATS_DTS_TIME: avio_printf(io, "%g", pkt->dts * av_q2d(tb)); continue;
case ENC_STATS_PKT_SIZE: avio_printf(io, "%d", pkt->size); continue;
- case ENC_STATS_FRAME_NUM: avio_printf(io, "%"PRIu64, ost->packets_encoded); continue;
case ENC_STATS_BITRATE: {
double duration = FFMAX(pkt->duration, 1) * av_q2d(tb);
avio_printf(io, "%g", 8.0 * pkt->size / duration);
@@ -884,7 +884,8 @@ static int encode_frame(OutputFile *of, OutputStream *ost, AVFrame *frame)
if (frame) {
if (ost->enc_stats_pre.io)
- enc_stats_write(ost, &ost->enc_stats_pre, frame, NULL);
+ enc_stats_write(ost, &ost->enc_stats_pre, frame, NULL,
+ ost->frames_encoded);
ost->frames_encoded++;
ost->samples_encoded += frame->nb_samples;
@@ -932,7 +933,8 @@ static int encode_frame(OutputFile *of, OutputStream *ost, AVFrame *frame)
if (enc->codec_type == AVMEDIA_TYPE_VIDEO)
update_video_stats(ost, pkt, !!vstats_filename);
if (ost->enc_stats_post.io)
- enc_stats_write(ost, &ost->enc_stats_post, NULL, pkt);
+ enc_stats_write(ost, &ost->enc_stats_post, NULL, pkt,
+ ost->packets_encoded);
if (debug_ts) {
av_log(ost, AV_LOG_INFO, "encoder -> type:%s "
diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
index 933312dba7..f1412f6446 100644
--- a/fftools/ffmpeg.h
+++ b/fftools/ffmpeg.h
@@ -258,10 +258,14 @@ typedef struct OptionsContext {
int nb_enc_stats_pre;
SpecifierOpt *enc_stats_post;
int nb_enc_stats_post;
+ SpecifierOpt *mux_stats;
+ int nb_mux_stats;
SpecifierOpt *enc_stats_pre_fmt;
int nb_enc_stats_pre_fmt;
SpecifierOpt *enc_stats_post_fmt;
int nb_enc_stats_post_fmt;
+ SpecifierOpt *mux_stats_fmt;
+ int nb_mux_stats_fmt;
} OptionsContext;
typedef struct InputFilter {
@@ -789,6 +793,10 @@ int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame);
int ffmpeg_parse_options(int argc, char **argv);
+void enc_stats_write(OutputStream *ost, EncStats *es,
+ const AVFrame *frame, const AVPacket *pkt,
+ uint64_t frame_num);
+
HWDevice *hw_device_get_by_name(const char *name);
int hw_device_init_from_string(const char *arg, HWDevice **dev);
void hw_device_free_all(void);
diff --git a/fftools/ffmpeg_mux.c b/fftools/ffmpeg_mux.c
index 30764e22d1..dffc1410c8 100644
--- a/fftools/ffmpeg_mux.c
+++ b/fftools/ffmpeg_mux.c
@@ -64,6 +64,7 @@ static int write_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt)
AVFormatContext *s = mux->fc;
AVStream *st = ost->st;
int64_t fs;
+ uint64_t frame_num;
int ret;
fs = filesize(s->pb);
@@ -128,7 +129,7 @@ static int write_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt)
ms->last_mux_dts = pkt->dts;
ost->data_size_mux += pkt->size;
- atomic_fetch_add(&ost->packets_written, 1);
+ frame_num = atomic_fetch_add(&ost->packets_written, 1);
pkt->stream_index = ost->index;
@@ -143,6 +144,9 @@ static int write_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt)
);
}
+ if (ms->stats.io)
+ enc_stats_write(ost, &ms->stats, NULL, pkt, frame_num);
+
ret = av_interleaved_write_frame(s, pkt);
if (ret < 0) {
print_error("av_interleaved_write_frame()", ret);
@@ -688,6 +692,10 @@ static void ost_free(OutputStream **post)
av_freep(&ost->enc_stats_post.components[i].str);
av_freep(&ost->enc_stats_post.components);
+ for (int i = 0; i < ms->stats.nb_components; i++)
+ av_freep(&ms->stats.components[i].str);
+ av_freep(&ms->stats.components);
+
av_freep(post);
}
diff --git a/fftools/ffmpeg_mux.h b/fftools/ffmpeg_mux.h
index 1487d86ae1..c76dc2e524 100644
--- a/fftools/ffmpeg_mux.h
+++ b/fftools/ffmpeg_mux.h
@@ -45,6 +45,8 @@ typedef struct MuxStream {
AVBSFContext *bsf_ctx;
+ EncStats stats;
+
int64_t max_frames;
/*
diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c
index 834cdbcc9f..f4ef83f6af 100644
--- a/fftools/ffmpeg_mux_init.c
+++ b/fftools/ffmpeg_mux_init.c
@@ -57,8 +57,10 @@ static const char *const opt_name_disposition[] = {"disposition",
static const char *const opt_name_enc_time_bases[] = {"enc_time_base", NULL};
static const char *const opt_name_enc_stats_pre[] = {"enc_stats_pre", NULL};
static const char *const opt_name_enc_stats_post[] = {"enc_stats_post", NULL};
+static const char *const opt_name_mux_stats[] = {"mux_stats", NULL};
static const char *const opt_name_enc_stats_pre_fmt[] = {"enc_stats_pre_fmt", NULL};
static const char *const opt_name_enc_stats_post_fmt[] = {"enc_stats_post_fmt", NULL};
+static const char *const opt_name_mux_stats_fmt[] = {"mux_stats_fmt", NULL};
static const char *const opt_name_filters[] = {"filter", "af", "vf", NULL};
static const char *const opt_name_filter_scripts[] = {"filter_script", NULL};
static const char *const opt_name_fix_sub_duration_heartbeat[] = {"fix_sub_duration_heartbeat", NULL};
@@ -262,7 +264,7 @@ static int unescape(char **pdst, size_t *dst_len,
return 0;
}
-static int enc_stats_init(OutputStream *ost, int pre,
+static int enc_stats_init(OutputStream *ost, EncStats *es, int pre,
const char *path, const char *fmt_spec)
{
static const struct {
@@ -290,7 +292,6 @@ static int enc_stats_init(OutputStream *ost, int pre,
{ ENC_STATS_BITRATE, "br", 0, 1 },
{ ENC_STATS_AVG_BITRATE, "abr", 0, 1 },
};
- EncStats *es = pre ? &ost->enc_stats_pre : &ost->enc_stats_post;
const char *next = fmt_spec;
int ret;
@@ -479,7 +480,7 @@ static OutputStream *new_output_stream(Muxer *mux, const OptionsContext *o,
AVCodecContext *enc = ost->enc_ctx;
AVIOContext *s = NULL;
char *buf = NULL, *arg = NULL, *preset = NULL;
- const char *enc_stats_pre = NULL, *enc_stats_post = NULL;
+ const char *enc_stats_pre = NULL, *enc_stats_post = NULL, *mux_stats = NULL;
ost->encoder_opts = filter_codec_opts(o->g->codec_opts, enc->codec_id,
oc, st, enc->codec);
@@ -518,7 +519,7 @@ static OutputStream *new_output_stream(Muxer *mux, const OptionsContext *o,
MATCH_PER_STREAM_OPT(enc_stats_pre_fmt, str, format, oc, st);
- ret = enc_stats_init(ost, 1, enc_stats_pre, format);
+ ret = enc_stats_init(ost, &ost->enc_stats_pre, 1, enc_stats_pre, format);
if (ret < 0)
exit_program(1);
}
@@ -530,7 +531,19 @@ static OutputStream *new_output_stream(Muxer *mux, const OptionsContext *o,
MATCH_PER_STREAM_OPT(enc_stats_post_fmt, str, format, oc, st);
- ret = enc_stats_init(ost, 0, enc_stats_post, format);
+ ret = enc_stats_init(ost, &ost->enc_stats_post, 0, enc_stats_post, format);
+ if (ret < 0)
+ exit_program(1);
+ }
+
+ MATCH_PER_STREAM_OPT(mux_stats, str, mux_stats, oc, st);
+ if (mux_stats &&
+ (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) {
+ const char *format = "{fidx} {sidx} {n} {t}";
+
+ MATCH_PER_STREAM_OPT(mux_stats_fmt, str, format, oc, st);
+
+ ret = enc_stats_init(ost, &ms->stats, 0, mux_stats, format);
if (ret < 0)
exit_program(1);
}
diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c
index 799dcf071e..ed7ee6ab7d 100644
--- a/fftools/ffmpeg_opt.c
+++ b/fftools/ffmpeg_opt.c
@@ -1548,10 +1548,14 @@ const OptionDef options[] = {
"write encoding stats before encoding" },
{ "enc_stats_post", HAS_ARG | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT | OPT_STRING, { .off = OFFSET(enc_stats_post) },
"write encoding stats after encoding" },
+ { "stats_mux_pre", HAS_ARG | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT | OPT_STRING, { .off = OFFSET(mux_stats) },
+ "write packets stats before muxing" },
{ "enc_stats_pre_fmt", HAS_ARG | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT | OPT_STRING, { .off = OFFSET(enc_stats_pre_fmt) },
"format of the stats written with -enc_stats_pre" },
{ "enc_stats_post_fmt", HAS_ARG | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT | OPT_STRING, { .off = OFFSET(enc_stats_post_fmt) },
"format of the stats written with -enc_stats_post" },
+ { "stats_mux_pre_fmt", HAS_ARG | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT | OPT_STRING, { .off = OFFSET(mux_stats_fmt) },
+ "format of the stats written with -stats_mux_pre" },
/* video options */
{ "vframes", OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_video_frames },
More information about the ffmpeg-cvslog
mailing list