mirror of
				https://github.com/nyanmisaka/ffmpeg-rockchip.git
				synced 2025-10-31 12:36:41 +08:00 
			
		
		
		
	fftools/ffmpeg: add options for writing encoding stats
Similar to -vstats, but more flexible: - works for audio as well as video - frame and/or packet information - user-specifiable format
This commit is contained in:
		| @@ -32,6 +32,7 @@ version <next>: | |||||||
| - WADY DPCM decoder and demuxer | - WADY DPCM decoder and demuxer | ||||||
| - CBD2 DPCM decoder | - CBD2 DPCM decoder | ||||||
| - ssim360 video filter | - ssim360 video filter | ||||||
|  | - ffmpeg CLI new options: -enc_stats_pre[_fmt], -enc_stats_post[_fmt] | ||||||
|  |  | ||||||
|  |  | ||||||
| version 5.1: | version 5.1: | ||||||
|   | |||||||
| @@ -2047,6 +2047,94 @@ encoder/muxer, it does not change the stream to conform to this value. Setting | |||||||
| values that do not match the stream properties may result in encoding failures | values that do not match the stream properties may result in encoding failures | ||||||
| or invalid output files. | 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}) | ||||||
|  | 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}. | ||||||
|  |  | ||||||
|  | When stats for multiple streams are written into a single file, the lines | ||||||
|  | corresponding to different streams will be interleaved. The precise order of | ||||||
|  | this interleaving is not specified and not guaranteed to remain stable between | ||||||
|  | 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}) | ||||||
|  | Specify the format for the lines written with @option{-enc_stats_pre} / | ||||||
|  | @option{-enc_stats_post}. | ||||||
|  |  | ||||||
|  | @var{format_spec} is a string that may contain directives of the form | ||||||
|  | @var{@{fmt@}}. @var{format_spec} is backslash-escaped --- use \@{, \@}, and \\ | ||||||
|  | to write a literal @{, @}, or \, respectively, into the output. | ||||||
|  |  | ||||||
|  | The directives given with @var{fmt} may be one of the following: | ||||||
|  | @table @option | ||||||
|  | @item fidx | ||||||
|  | Index of the output file. | ||||||
|  |  | ||||||
|  | @item sidx | ||||||
|  | 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. | ||||||
|  |  | ||||||
|  | @item tb | ||||||
|  | Encoder timebase, as a rational number @var{num/den}. Note that this may be | ||||||
|  | different from the timebase used by the muxer. | ||||||
|  |  | ||||||
|  | @item pts | ||||||
|  | Presentation timestamp of the frame or packet, as an integer. Should be | ||||||
|  | multiplied by the timebase to compute presentation time. | ||||||
|  |  | ||||||
|  | @item t | ||||||
|  | Presentation time of the frame or packet, as a decimal number. Equal to | ||||||
|  | @var{pts} multiplied by @var{tb}. | ||||||
|  |  | ||||||
|  | @item dts | ||||||
|  | Decoding timestamp of the packet, as an integer. Should be multiplied by the | ||||||
|  | timebase to compute presentation time. Post-encoding only. | ||||||
|  |  | ||||||
|  | @item dt | ||||||
|  | Decoding time of the frame or packet, as a decimal number. Equal to | ||||||
|  | @var{dts} multiplied by @var{tb}. | ||||||
|  |  | ||||||
|  | @item sn | ||||||
|  | Number of audio samples sent to the encoder so far. Audio and pre-encoding only. | ||||||
|  |  | ||||||
|  | @item samp | ||||||
|  | Number of audio samples in the frame. Audio and pre-encoding only. | ||||||
|  |  | ||||||
|  | @item size | ||||||
|  | Size of the encoded packet in bytes. Post-encoding only. | ||||||
|  |  | ||||||
|  | @item br | ||||||
|  | Current bitrate in bits per second. Post-encoding only. | ||||||
|  |  | ||||||
|  | @item abr | ||||||
|  | Average bitrate for the whole stream so far, in bits per second, -1 if it cannot | ||||||
|  | be determined at this point. Post-encoding only. | ||||||
|  | @end table | ||||||
|  |  | ||||||
|  | The default format strings are: | ||||||
|  | @table @option | ||||||
|  | @item pre-encoding | ||||||
|  | @{fidx@} @{sidx@} @{n@} @{t@} | ||||||
|  | @item post-encoding | ||||||
|  | @{fidx@} @{sidx@} @{n@} @{t@} | ||||||
|  | @end table | ||||||
|  | In the future, new items may be added to the end of the default formatting | ||||||
|  | strings. Users who depend on the format staying exactly the same, should | ||||||
|  | prescribe it manually. | ||||||
|  |  | ||||||
|  | Note that stats for different streams written into the same file may have | ||||||
|  | different formats. | ||||||
|  |  | ||||||
| @end table | @end table | ||||||
|  |  | ||||||
| @section Preset files | @section Preset files | ||||||
|   | |||||||
| @@ -554,6 +554,8 @@ static void ffmpeg_cleanup(int ret) | |||||||
|                    av_err2str(AVERROR(errno))); |                    av_err2str(AVERROR(errno))); | ||||||
|     } |     } | ||||||
|     av_freep(&vstats_filename); |     av_freep(&vstats_filename); | ||||||
|  |     of_enc_stats_close(); | ||||||
|  |  | ||||||
|     av_freep(&filter_nbthreads); |     av_freep(&filter_nbthreads); | ||||||
|  |  | ||||||
|     av_freep(&input_files); |     av_freep(&input_files); | ||||||
| @@ -798,6 +800,56 @@ 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)); |     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) | ||||||
|  | { | ||||||
|  |     AVIOContext *io = ost->enc_stats_pre.io; | ||||||
|  |     AVRational   tb = ost->enc_ctx->time_base; | ||||||
|  |     int64_t     pts = frame ? frame->pts : pkt->pts; | ||||||
|  |  | ||||||
|  |     for (size_t i = 0; i < es->nb_components; i++) { | ||||||
|  |         const EncStatsComponent *c = &es->components[i]; | ||||||
|  |  | ||||||
|  |         switch (c->type) { | ||||||
|  |         case ENC_STATS_LITERAL:         avio_write (io, c->str,     c->str_len);                    continue; | ||||||
|  |         case ENC_STATS_FILE_IDX:        avio_printf(io, "%d",       ost->file_index);               continue; | ||||||
|  |         case ENC_STATS_STREAM_IDX:      avio_printf(io, "%d",       ost->index);                    continue; | ||||||
|  |         case ENC_STATS_TIMEBASE:        avio_printf(io, "%d/%d",    tb.num, tb.den);                continue; | ||||||
|  |         case ENC_STATS_PTS:             avio_printf(io, "%"PRId64,  pts);                           continue; | ||||||
|  |         case ENC_STATS_PTS_TIME:        avio_printf(io, "%g",       pts * av_q2d(tb));              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); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             switch (c->type) { | ||||||
|  |             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); | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |             case ENC_STATS_AVG_BITRATE: { | ||||||
|  |                 double duration = pkt->dts * av_q2d(tb); | ||||||
|  |                 avio_printf(io, "%g",  duration > 0 ? 8.0 * ost->data_size_enc / duration : -1.); | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |             default: av_assert0(0); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     avio_w8(io, '\n'); | ||||||
|  |     avio_flush(io); | ||||||
|  | } | ||||||
|  |  | ||||||
| static int encode_frame(OutputFile *of, OutputStream *ost, AVFrame *frame) | static int encode_frame(OutputFile *of, OutputStream *ost, AVFrame *frame) | ||||||
| { | { | ||||||
|     AVCodecContext   *enc = ost->enc_ctx; |     AVCodecContext   *enc = ost->enc_ctx; | ||||||
| @@ -807,6 +859,9 @@ static int encode_frame(OutputFile *of, OutputStream *ost, AVFrame *frame) | |||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|     if (frame) { |     if (frame) { | ||||||
|  |         if (ost->enc_stats_pre.io) | ||||||
|  |             enc_stats_write(ost, &ost->enc_stats_pre, frame, NULL); | ||||||
|  |  | ||||||
|         ost->frames_encoded++; |         ost->frames_encoded++; | ||||||
|         ost->samples_encoded += frame->nb_samples; |         ost->samples_encoded += frame->nb_samples; | ||||||
|  |  | ||||||
| @@ -848,6 +903,11 @@ static int encode_frame(OutputFile *of, OutputStream *ost, AVFrame *frame) | |||||||
|             return ret; |             return ret; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         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); | ||||||
|  |  | ||||||
|         if (debug_ts) { |         if (debug_ts) { | ||||||
|             av_log(NULL, AV_LOG_INFO, "encoder -> type:%s " |             av_log(NULL, AV_LOG_INFO, "encoder -> type:%s " | ||||||
|                    "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s " |                    "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s " | ||||||
| @@ -872,9 +932,6 @@ static int encode_frame(OutputFile *of, OutputStream *ost, AVFrame *frame) | |||||||
|  |  | ||||||
|         ost->data_size_enc += pkt->size; |         ost->data_size_enc += pkt->size; | ||||||
|  |  | ||||||
|         if (enc->codec_type == AVMEDIA_TYPE_VIDEO) |  | ||||||
|             update_video_stats(ost, pkt, !!vstats_filename); |  | ||||||
|  |  | ||||||
|         ost->packets_encoded++; |         ost->packets_encoded++; | ||||||
|  |  | ||||||
|         of_output_packet(of, pkt, ost, 0); |         of_output_packet(of, pkt, ost, 0); | ||||||
|   | |||||||
| @@ -252,6 +252,14 @@ typedef struct OptionsContext { | |||||||
|     int        nb_autoscale; |     int        nb_autoscale; | ||||||
|     SpecifierOpt *bits_per_raw_sample; |     SpecifierOpt *bits_per_raw_sample; | ||||||
|     int        nb_bits_per_raw_sample; |     int        nb_bits_per_raw_sample; | ||||||
|  |     SpecifierOpt *enc_stats_pre; | ||||||
|  |     int        nb_enc_stats_pre; | ||||||
|  |     SpecifierOpt *enc_stats_post; | ||||||
|  |     int        nb_enc_stats_post; | ||||||
|  |     SpecifierOpt *enc_stats_pre_fmt; | ||||||
|  |     int        nb_enc_stats_pre_fmt; | ||||||
|  |     SpecifierOpt *enc_stats_post_fmt; | ||||||
|  |     int        nb_enc_stats_post_fmt; | ||||||
| } OptionsContext; | } OptionsContext; | ||||||
|  |  | ||||||
| typedef struct InputFilter { | typedef struct InputFilter { | ||||||
| @@ -480,6 +488,37 @@ enum forced_keyframes_const { | |||||||
| #define ABORT_ON_FLAG_EMPTY_OUTPUT        (1 <<  0) | #define ABORT_ON_FLAG_EMPTY_OUTPUT        (1 <<  0) | ||||||
| #define ABORT_ON_FLAG_EMPTY_OUTPUT_STREAM (1 <<  1) | #define ABORT_ON_FLAG_EMPTY_OUTPUT_STREAM (1 <<  1) | ||||||
|  |  | ||||||
|  | enum EncStatsType { | ||||||
|  |     ENC_STATS_LITERAL = 0, | ||||||
|  |     ENC_STATS_FILE_IDX, | ||||||
|  |     ENC_STATS_STREAM_IDX, | ||||||
|  |     ENC_STATS_FRAME_NUM, | ||||||
|  |     ENC_STATS_TIMEBASE, | ||||||
|  |     ENC_STATS_PTS, | ||||||
|  |     ENC_STATS_PTS_TIME, | ||||||
|  |     ENC_STATS_DTS, | ||||||
|  |     ENC_STATS_DTS_TIME, | ||||||
|  |     ENC_STATS_SAMPLE_NUM, | ||||||
|  |     ENC_STATS_NB_SAMPLES, | ||||||
|  |     ENC_STATS_PKT_SIZE, | ||||||
|  |     ENC_STATS_BITRATE, | ||||||
|  |     ENC_STATS_AVG_BITRATE, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | typedef struct EncStatsComponent { | ||||||
|  |     enum EncStatsType type; | ||||||
|  |  | ||||||
|  |     uint8_t *str; | ||||||
|  |     size_t   str_len; | ||||||
|  | } EncStatsComponent; | ||||||
|  |  | ||||||
|  | typedef struct EncStats { | ||||||
|  |     EncStatsComponent  *components; | ||||||
|  |     int              nb_components; | ||||||
|  |  | ||||||
|  |     AVIOContext        *io; | ||||||
|  | } EncStats; | ||||||
|  |  | ||||||
| extern const char *const forced_keyframes_const_names[]; | extern const char *const forced_keyframes_const_names[]; | ||||||
|  |  | ||||||
| typedef enum { | typedef enum { | ||||||
| @@ -625,6 +664,9 @@ typedef struct OutputStream { | |||||||
|  |  | ||||||
|     int sq_idx_encode; |     int sq_idx_encode; | ||||||
|     int sq_idx_mux; |     int sq_idx_mux; | ||||||
|  |  | ||||||
|  |     EncStats enc_stats_pre; | ||||||
|  |     EncStats enc_stats_post; | ||||||
| } OutputStream; | } OutputStream; | ||||||
|  |  | ||||||
| typedef struct OutputFile { | typedef struct OutputFile { | ||||||
| @@ -749,6 +791,8 @@ int of_write_trailer(OutputFile *of); | |||||||
| int of_open(const OptionsContext *o, const char *filename); | int of_open(const OptionsContext *o, const char *filename); | ||||||
| void of_close(OutputFile **pof); | void of_close(OutputFile **pof); | ||||||
|  |  | ||||||
|  | void of_enc_stats_close(void); | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Send a single packet to the output, applying any bitstream filters |  * Send a single packet to the output, applying any bitstream filters | ||||||
|  * associated with the output stream.  This may result in any number |  * associated with the output stream.  This may result in any number | ||||||
|   | |||||||
| @@ -686,6 +686,14 @@ static void ost_free(OutputStream **post) | |||||||
|         av_freep(&ost->enc_ctx->stats_in); |         av_freep(&ost->enc_ctx->stats_in); | ||||||
|     avcodec_free_context(&ost->enc_ctx); |     avcodec_free_context(&ost->enc_ctx); | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < ost->enc_stats_pre.nb_components; i++) | ||||||
|  |         av_freep(&ost->enc_stats_pre.components[i].str); | ||||||
|  |     av_freep(&ost->enc_stats_pre.components); | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < ost->enc_stats_post.nb_components; i++) | ||||||
|  |         av_freep(&ost->enc_stats_post.components[i].str); | ||||||
|  |     av_freep(&ost->enc_stats_post.components); | ||||||
|  |  | ||||||
|     av_freep(post); |     av_freep(post); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -55,6 +55,10 @@ static const char *const opt_name_copy_initial_nonkeyframes[] = {"copyinkf", NUL | |||||||
| static const char *const opt_name_copy_prior_start[]          = {"copypriorss", NULL}; | static const char *const opt_name_copy_prior_start[]          = {"copypriorss", NULL}; | ||||||
| static const char *const opt_name_disposition[]               = {"disposition", NULL}; | static const char *const opt_name_disposition[]               = {"disposition", NULL}; | ||||||
| static const char *const opt_name_enc_time_bases[]            = {"enc_time_base", NULL}; | 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_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_filters[]                   = {"filter", "af", "vf", 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_filter_scripts[]            = {"filter_script", NULL}; | ||||||
| static const char *const opt_name_fps_mode[]                  = {"fps_mode", NULL}; | static const char *const opt_name_fps_mode[]                  = {"fps_mode", NULL}; | ||||||
| @@ -170,6 +174,201 @@ static int get_preset_file_2(const char *preset_name, const char *codec_name, AV | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | typedef struct EncStatsFile { | ||||||
|  |     char        *path; | ||||||
|  |     AVIOContext *io; | ||||||
|  | } EncStatsFile; | ||||||
|  |  | ||||||
|  | static EncStatsFile   *enc_stats_files; | ||||||
|  | static          int nb_enc_stats_files; | ||||||
|  |  | ||||||
|  | static int enc_stats_get_file(AVIOContext **io, const char *path) | ||||||
|  | { | ||||||
|  |     EncStatsFile *esf; | ||||||
|  |     int ret; | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < nb_enc_stats_files; i++) | ||||||
|  |         if (!strcmp(path, enc_stats_files[i].path)) { | ||||||
|  |             *io = enc_stats_files[i].io; | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     GROW_ARRAY(enc_stats_files, nb_enc_stats_files); | ||||||
|  |  | ||||||
|  |     esf = &enc_stats_files[nb_enc_stats_files - 1]; | ||||||
|  |  | ||||||
|  |     ret = avio_open2(&esf->io, path, AVIO_FLAG_WRITE, &int_cb, NULL); | ||||||
|  |     if (ret < 0) { | ||||||
|  |         av_log(NULL, AV_LOG_ERROR, "Error opening stats file '%s': %s\n", | ||||||
|  |                path, av_err2str(ret)); | ||||||
|  |         return ret; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     esf->path = av_strdup(path); | ||||||
|  |     if (!esf->path) | ||||||
|  |         return AVERROR(ENOMEM); | ||||||
|  |  | ||||||
|  |     *io = esf->io; | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void of_enc_stats_close(void) | ||||||
|  | { | ||||||
|  |     for (int i = 0; i < nb_enc_stats_files; i++) { | ||||||
|  |         av_freep(&enc_stats_files[i].path); | ||||||
|  |         avio_closep(&enc_stats_files[i].io); | ||||||
|  |     } | ||||||
|  |     av_freep(&enc_stats_files); | ||||||
|  |     nb_enc_stats_files = 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int unescape(char **pdst, size_t *dst_len, | ||||||
|  |                     const char **pstr, char delim) | ||||||
|  | { | ||||||
|  |     const char *str = *pstr; | ||||||
|  |     char *dst; | ||||||
|  |     size_t len, idx; | ||||||
|  |  | ||||||
|  |     *pdst = NULL; | ||||||
|  |  | ||||||
|  |     len = strlen(str); | ||||||
|  |     if (!len) | ||||||
|  |         return 0; | ||||||
|  |  | ||||||
|  |     dst = av_malloc(len + 1); | ||||||
|  |     if (!dst) | ||||||
|  |         return AVERROR(ENOMEM); | ||||||
|  |  | ||||||
|  |     for (idx = 0; *str; idx++, str++) { | ||||||
|  |         if (str[0] == '\\' && str[1]) | ||||||
|  |             str++; | ||||||
|  |         else if (*str == delim) | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         dst[idx] = *str; | ||||||
|  |     } | ||||||
|  |     if (!idx) { | ||||||
|  |         av_freep(&dst); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     dst[idx] = 0; | ||||||
|  |  | ||||||
|  |     *pdst    = dst; | ||||||
|  |     *dst_len = idx; | ||||||
|  |     *pstr    = str; | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int enc_stats_init(OutputStream *ost, int pre, | ||||||
|  |                           const char *path, const char *fmt_spec) | ||||||
|  | { | ||||||
|  |     static const struct { | ||||||
|  |         enum EncStatsType  type; | ||||||
|  |         const char        *str; | ||||||
|  |         int                pre_only:1; | ||||||
|  |         int                post_only:1; | ||||||
|  |     } fmt_specs[] = { | ||||||
|  |         { ENC_STATS_FILE_IDX,       "fidx"                      }, | ||||||
|  |         { ENC_STATS_STREAM_IDX,     "sidx"                      }, | ||||||
|  |         { ENC_STATS_FRAME_NUM,      "n"                         }, | ||||||
|  |         { ENC_STATS_TIMEBASE,       "tb"                        }, | ||||||
|  |         { ENC_STATS_PTS,            "pts"                       }, | ||||||
|  |         { ENC_STATS_PTS_TIME,       "t"                         }, | ||||||
|  |         { ENC_STATS_DTS,            "dts",      0, 1            }, | ||||||
|  |         { ENC_STATS_DTS_TIME,       "dt",       0, 1            }, | ||||||
|  |         { ENC_STATS_SAMPLE_NUM,     "sn",       1               }, | ||||||
|  |         { ENC_STATS_NB_SAMPLES,     "samp",     1               }, | ||||||
|  |         { ENC_STATS_PKT_SIZE,       "size",     0, 1            }, | ||||||
|  |         { 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; | ||||||
|  |  | ||||||
|  |     while (*next) { | ||||||
|  |         EncStatsComponent *c; | ||||||
|  |         char *val; | ||||||
|  |         size_t val_len; | ||||||
|  |  | ||||||
|  |         // get the sequence up until next opening brace | ||||||
|  |         ret = unescape(&val, &val_len, &next, '{'); | ||||||
|  |         if (ret < 0) | ||||||
|  |             return ret; | ||||||
|  |  | ||||||
|  |         if (val) { | ||||||
|  |             GROW_ARRAY(es->components, es->nb_components); | ||||||
|  |  | ||||||
|  |             c          = &es->components[es->nb_components - 1]; | ||||||
|  |             c->type    = ENC_STATS_LITERAL; | ||||||
|  |             c->str     = val; | ||||||
|  |             c->str_len = val_len; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!*next) | ||||||
|  |             break; | ||||||
|  |         next++; | ||||||
|  |  | ||||||
|  |         // get the part inside braces | ||||||
|  |         ret = unescape(&val, &val_len, &next, '}'); | ||||||
|  |         if (ret < 0) | ||||||
|  |             return ret; | ||||||
|  |  | ||||||
|  |         if (!val) { | ||||||
|  |             av_log(NULL, AV_LOG_ERROR, | ||||||
|  |                    "Empty formatting directive in: %s\n", fmt_spec); | ||||||
|  |             return AVERROR(EINVAL); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!*next) { | ||||||
|  |             av_log(NULL, AV_LOG_ERROR, | ||||||
|  |                    "Missing closing brace in: %s\n", fmt_spec); | ||||||
|  |             ret = AVERROR(EINVAL); | ||||||
|  |             goto fail; | ||||||
|  |         } | ||||||
|  |         next++; | ||||||
|  |  | ||||||
|  |         GROW_ARRAY(es->components, es->nb_components); | ||||||
|  |         c = &es->components[es->nb_components - 1]; | ||||||
|  |  | ||||||
|  |         for (size_t i = 0; i < FF_ARRAY_ELEMS(fmt_specs); i++) { | ||||||
|  |             if (!strcmp(val, fmt_specs[i].str)) { | ||||||
|  |                 if ((pre && fmt_specs[i].post_only) || (!pre && fmt_specs[i].pre_only)) { | ||||||
|  |                     av_log(NULL, AV_LOG_ERROR, | ||||||
|  |                            "Format directive '%s' may only be used %s-encoding\n", | ||||||
|  |                            val, pre ? "post" : "pre"); | ||||||
|  |                     ret = AVERROR(EINVAL); | ||||||
|  |                     goto fail; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 c->type = fmt_specs[i].type; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!c->type) { | ||||||
|  |             av_log(NULL, AV_LOG_ERROR, "Invalid format directive: %s\n", val); | ||||||
|  |             ret = AVERROR(EINVAL); | ||||||
|  |             goto fail; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  | fail: | ||||||
|  |         av_freep(&val); | ||||||
|  |         if (ret < 0) | ||||||
|  |             return ret; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ret = enc_stats_get_file(&es->io, path); | ||||||
|  |     if (ret < 0) | ||||||
|  |         return ret; | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| static OutputStream *new_output_stream(Muxer *mux, const OptionsContext *o, | static OutputStream *new_output_stream(Muxer *mux, const OptionsContext *o, | ||||||
|                                        enum AVMediaType type, InputStream *ist) |                                        enum AVMediaType type, InputStream *ist) | ||||||
| { | { | ||||||
| @@ -230,6 +429,7 @@ static OutputStream *new_output_stream(Muxer *mux, const OptionsContext *o, | |||||||
|         AVCodecContext *enc = ost->enc_ctx; |         AVCodecContext *enc = ost->enc_ctx; | ||||||
|         AVIOContext *s = NULL; |         AVIOContext *s = NULL; | ||||||
|         char *buf = NULL, *arg = NULL, *preset = NULL; |         char *buf = NULL, *arg = NULL, *preset = NULL; | ||||||
|  |         const char *enc_stats_pre = NULL, *enc_stats_post = NULL; | ||||||
|  |  | ||||||
|         ost->encoder_opts = filter_codec_opts(o->g->codec_opts, enc->codec_id, |         ost->encoder_opts = filter_codec_opts(o->g->codec_opts, enc->codec_id, | ||||||
|                                               oc, st, enc->codec); |                                               oc, st, enc->codec); | ||||||
| @@ -261,6 +461,30 @@ static OutputStream *new_output_stream(Muxer *mux, const OptionsContext *o, | |||||||
|                    preset, ost->file_index, ost->index); |                    preset, ost->file_index, ost->index); | ||||||
|             exit_program(1); |             exit_program(1); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         MATCH_PER_STREAM_OPT(enc_stats_pre, str, enc_stats_pre, oc, st); | ||||||
|  |         if (enc_stats_pre && | ||||||
|  |             (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) { | ||||||
|  |             const char *format = "{fidx} {sidx} {n} {t}"; | ||||||
|  |  | ||||||
|  |             MATCH_PER_STREAM_OPT(enc_stats_pre_fmt, str, format, oc, st); | ||||||
|  |  | ||||||
|  |             ret = enc_stats_init(ost, 1, enc_stats_pre, format); | ||||||
|  |             if (ret < 0) | ||||||
|  |                 exit_program(1); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         MATCH_PER_STREAM_OPT(enc_stats_post, str, enc_stats_post, oc, st); | ||||||
|  |         if (enc_stats_post && | ||||||
|  |             (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) { | ||||||
|  |             const char *format = "{fidx} {sidx} {n} {t}"; | ||||||
|  |  | ||||||
|  |             MATCH_PER_STREAM_OPT(enc_stats_post_fmt, str, format, oc, st); | ||||||
|  |  | ||||||
|  |             ret = enc_stats_init(ost, 0, enc_stats_post, format); | ||||||
|  |             if (ret < 0) | ||||||
|  |                 exit_program(1); | ||||||
|  |         } | ||||||
|     } else { |     } else { | ||||||
|         ost->encoder_opts = filter_codec_opts(o->g->codec_opts, AV_CODEC_ID_NONE, oc, st, NULL); |         ost->encoder_opts = filter_codec_opts(o->g->codec_opts, AV_CODEC_ID_NONE, oc, st, NULL); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1543,6 +1543,15 @@ const OptionDef options[] = { | |||||||
|         { .off = OFFSET(bits_per_raw_sample) }, |         { .off = OFFSET(bits_per_raw_sample) }, | ||||||
|         "set the number of bits per raw sample", "number" }, |         "set the number of bits per raw sample", "number" }, | ||||||
|  |  | ||||||
|  |     { "enc_stats_pre",      HAS_ARG | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT | OPT_STRING, { .off = OFFSET(enc_stats_pre)      }, | ||||||
|  |         "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" }, | ||||||
|  |     { "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" }, | ||||||
|  |  | ||||||
|     /* video options */ |     /* video options */ | ||||||
|     { "vframes",      OPT_VIDEO | HAS_ARG  | OPT_PERFILE | OPT_OUTPUT,           { .func_arg = opt_video_frames }, |     { "vframes",      OPT_VIDEO | HAS_ARG  | OPT_PERFILE | OPT_OUTPUT,           { .func_arg = opt_video_frames }, | ||||||
|         "set the number of video frames to output", "number" }, |         "set the number of video frames to output", "number" }, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Anton Khirnov
					Anton Khirnov