FFmpeg.c分析

本文将会对FFmpeg.c中的主要函数分别解析。主要参考http://blog.csdn.net/leixiaohua1020/article/details/39760711,并对其中更新的API进行修改。


main()

main()是FFmpeg的主函数。
调用了如下函数
av_register_all():注册所有编码器和解码器。
show_banner():打印输出FFmpeg版本信息(编译时间,编译选项,类库信息等)。
parse_options():解析输入的命令。
transcode():转码。
exit_progam():退出和清理。
 
下图红框中的内容即为show_banner()的输出结果。


 

parse_options()



parse_options()解析全部输入选项。即将输入命令“ffmpeg -i xxx.mpg -vcodec libx264 yyy.mkv”中的“-i”,“-vcodec”这样的命令解析出来。其函数调用结构如下图所示。

注:定义位于cmdutils.c中。


调用了如下函数:
parse_option():解析一个输入选项。具体的解析步骤不再赘述。parse_options()会循环调用parse_option()直到所有选项解析完毕。FFmpeg的每一个选项信息存储在一个OptionDef结构体中。定义如下:
[cpp]  view plain  copy
  1. typedef struct OptionDef {  
  2.     const char *name;  
  3.     int flags;  
  4. #define HAS_ARG    0x0001  
  5. #define OPT_BOOL   0x0002  
  6. #define OPT_EXPERT 0x0004  
  7. #define OPT_STRING 0x0008  
  8. #define OPT_VIDEO  0x0010  
  9. #define OPT_AUDIO  0x0020  
  10. #define OPT_INT    0x0080  
  11. #define OPT_FLOAT  0x0100  
  12. #define OPT_SUBTITLE 0x0200  
  13. #define OPT_INT64  0x0400  
  14. #define OPT_EXIT   0x0800  
  15. #define OPT_DATA   0x1000  
  16. #define OPT_PERFILE  0x2000     /* the option is per-file (currently ffmpeg-only).  
  17.          implied by OPT_OFFSET or OPT_SPEC */  
  18. #define OPT_OFFSET 0x4000       /* option is specified as an offset in a passed optctx */  
  19. #define OPT_SPEC   0x8000       /* option is to be stored in an array of SpecifierOpt.  
  20.          Implies OPT_OFFSET. Next element after the offset is  
  21.          an int containing element count in the array. */  
  22. #define OPT_TIME  0x10000  
  23. #define OPT_DOUBLE 0x20000  
  24.      union {  
  25.         void *dst_ptr;  
  26.         int (*func_arg)(void *, const char *, const char *);  
  27.         size_t off;  
  28.     } u;  
  29.     const char *help;  
  30.     const char *argname;  
  31. } OptionDef;  

其中的重要字段:
name:用于存储选项的名称。例如“i”,“f”,“codec”等等。
flags:存储选项值的类型。例如:HAS_ARG(包含选项值),OPT_STRING(选项值为字符串类型),OPT_TIME(选项值为时间类型。
u:存储该选项的处理函数。
help:选项的说明信息。
FFmpeg使用一个名称为options,类型为OptionDef的数组存储所有的选项。有一部分通用选项存储在cmdutils_common_opts.h中。cmdutils_common_opts.h内容如下:
[cpp]  view plain  copy
  1. "L"          , OPT_EXIT, {(void*)show_license},      "show license" },  
  2. "h"          , OPT_EXIT, {(void*) show_help},         "show help""topic" },  
  3. "?"          , OPT_EXIT, {(void*)show_help},         "show help""topic" },  
  4. "help"       , OPT_EXIT, {(void*)show_help},         "show help""topic" },  
  5. "-help"      , OPT_EXIT, {(void*)show_help},         "show help""topic" },  
  6. "version"    , OPT_EXIT, {(void*)show_version},      "show version" },  
  7. "formats"    , OPT_EXIT, {(void*)show_formats  },    "show available formats" },  
  8. "codecs"     , OPT_EXIT, {(void*)show_codecs   },    "show available codecs" },  
  9. "decoders"   , OPT_EXIT, {(void*)show_decoders },    "show available decoders" },  
  10. "encoders"   , OPT_EXIT, {(void*)show_encoders },    "show available encoders" },  
  11. "bsfs"       , OPT_EXIT, {(void*)show_bsfs     },    "show available bit stream filters" },  
  12. "protocols"  , OPT_EXIT, {(void*)show_protocols},    "show available protocols" },  
  13. "filters"    , OPT_EXIT, {(void*)show_filters  },    "show available filters" },  
  14. "pix_fmts"   , OPT_EXIT, {(void*)show_pix_fmts },    "show available pixel formats" },  
  15. "layouts"    , OPT_EXIT, {(void*)show_layouts  },    "show standard channel layouts" },  
  16. "sample_fmts", OPT_EXIT, {(void*)show_sample_fmts }, "show available audio sample formats" },  
  17. "loglevel"   , HAS_ARG,  {(void*)opt_loglevel},      "set libav* logging level""loglevel" },  
  18. "v",           HAS_ARG,  {(void*)opt_loglevel},      "set libav* logging level""loglevel" },  
  19. "debug"      , HAS_ARG,  {(void*)opt_codec_debug},   "set debug flags""flags" },  
  20. "fdebug"     , HAS_ARG,  {(void*)opt_codec_debug},   "set debug flags""flags" },  
  21. "report"     , 0,        {(void*)opt_report}, "generate a report" },  
  22. "max_alloc"  , HAS_ARG,  {(void*) opt_max_alloc},     "set maximum size of a single allocated block""bytes" },  
  23. "cpuflags"   , HAS_ARG | OPT_EXPERT, {(void*) opt_cpuflags}, "force specific cpu flags""flags" },  

options数组的定义位于ffmpeg_opt.c中:
[cpp]  view plain  copy
  1. const OptionDef options[] = {  
  2.     /* main options */  
  3. #include "cmdutils_common_opts.h"//包含了cmdutils_common_opts.h中的选项  
  4.     { "f", HAS_ARG | OPT_STRING | OPT_OFFSET,           { (void*)OFFSET(format) },  
  5.         "force format""fmt" },  
  6.     { "i", HAS_ARG | OPT_PERFILE,          { (void*) opt_input_file },  
  7.         "input file name""filename" },  
  8.     { "y", OPT_BOOL,          { &file_overwrite },  
  9.         "overwrite output files" },  
  10.     { "n", OPT_BOOL,          { &no_file_overwrite },  
  11.         "do not overwrite output files" },  
  12.     { "c", HAS_ARG | OPT_STRING | OPT_SPEC,{ (void*) OFFSET(codec_names) },  
  13.         "codec name""codec" },  
  14.     { "codec",          HAS_ARG | OPT_STRING | OPT_SPEC,{(void*) OFFSET(codec_names) },  
  15.         "codec name""codec" },  
  16.     { "pre",            HAS_ARG | OPT_STRING | OPT_SPEC,{ (void*) OFFSET(presets) },  
  17.         "preset name""preset" },  
  18.     { "map",            HAS_ARG | OPT_EXPERT | OPT_PERFILE,          { (void*) opt_map },  
  19.         "set input stream mapping",  
  20.         "[-]input_file_id[:stream_specifier][,sync_file_id[:stream_specifier]]" },  
  21.     { "map_channel",    HAS_ARG | OPT_EXPERT | OPT_PERFILE,          {(void*)opt_map_channel },  
  22.         "map an audio channel from one stream to another""file.stream.channel[:syncfile.syncstream]" },  
  23.     { "map_metadata",   HAS_ARG | OPT_STRING | OPT_SPEC,{ (void*)OFFSET(metadata_map) },  
  24.         "set metadata information of outfile from infile",  
  25.         "outfile[,metadata]:infile[,metadata]" },  
  26.     { "map_chapters",   HAS_ARG | OPT_INT | OPT_EXPERT | OPT_OFFSET, { (void*) OFFSET(chapters_input_file) },  
  27.         "set chapters mapping""input_file_index" },  
  28.     { "t", HAS_ARG | OPT_TIME | OPT_OFFSET,{(void*) OFFSET(recording_time) },  
  29.         "record or transcode \"duration\" seconds of audio/video",  
  30.         "duration" },  
  31.     { "fs",HAS_ARG | OPT_INT64 | OPT_OFFSET,            { (void*) OFFSET(limit_filesize) },  
  32.         "set the limit file size in bytes""limit_size" },  
  33.     { "ss",HAS_ARG | OPT_TIME | OPT_OFFSET,{ (void*) OFFSET(start_time) },  
  34.         "set the start time offset""time_off" },  
  35.    …//选项太多,不一一列出  
  36. };  

在这里,例举一个选项的OptionDef结构体:输入
[cpp]  view plain  copy
  1. "i",HAS_ARG | OPT_PERFILE, { (void*) opt_input_file }, "input file name""filename" }  
在这个结构体中,可以看出选项的名称为“i”,选项包含选项值(HAS_ARG),选项的处理函数是opt_input_file(),选项的说明是“input file name”。下面可以详细看一下选项的处理函数opt_input_file()。该函数的定义位于ffmpeg_opt.c文件中。可以看出,调用了avformat_alloc_context()初始化了AVFormatContext结构体,调用了avformat_open_input()函数打开了“-i”选项指定的文件。此外,调用了avformat_find_stream_info()等完成了一些初始化操作。此外,调用了av_dump_format()打印输出输入文件信息。
[cpp]  view plain  copy
  1. static int opt_input_file(void *optctx, const char *opt, const char *filename)  
  2. {  
  3.     //略…  
  4.     /* open the input file with generic avformat function */  
  5.     err = avformat_open_input(&ic, filename, file_iformat, &format_opts);  
  6.     if (err < 0) {  
  7.         print_error(filename, err);  
  8.         exit(1);  
  9.     }  
  10.      
  11.     //略…  
  12.     /* Set AVCodecContext options for avformat_find_stream_info */  
  13.     opts = setup_find_stream_info_opts(ic, codec_opts);  
  14.     orig_nb_streams = ic->nb_streams;  
  15.    
  16.     /* If not enough info to get the stream parameters, we decode the 
  17.        first frames to get it. (used in mpeg case for example) */  
  18.     ret = avformat_find_stream_info(ic, opts);  
  19.     if (ret < 0) {  
  20.         av_log(NULL, AV_LOG_FATAL, "%s: could not find codec parameters\n", filename);  
  21.         avformat_close_input(&ic);  
  22.         exit(1);  
  23.     }  
  24.      
  25.     //略…  
  26.     /* dump the file content */  
  27.     av_dump_format(ic, nb_input_files, filename, 0);  
  28.      
  29.     //略…  
  30.     return 0;  
  31. }  
 
再例举一个输出文件处理函数opt_output_file()。这里需要注意,输出文件的处理并不包含在OptionDef类型的数组options中。因为FFmpeg中指定输出文件时并不包含选项名称,这是一个比较特殊的地方。一般的选项格式是“-名称 值”,例如指定输入文件的时候,选项格式是“-i xxx.flv”。而指定输出文件的时候,直接指定“值”即可,这是新手可能容易搞混的地方。
例如,最简单的转码命令如下(输出文件前面不包含选项):
[plain]  view plain  copy
  1. ffmpeg -i xxx.mpg xxx.mkv  
而不是
[plain]  view plain  copy
  1. ffmpeg -i xxx.mpeg -o xxx.mkv  
 
下面简单看一下opt_output_file()函数的定义。该函数的定义同样位于ffmpeg_opt.c文件中。这个函数的定义特别长,完成了输出视频的初始化工作。在这里就不列出代码了。该函数首先调用avformat_alloc_output_context2()初始化AVFormatContext结构体。而后根据媒体类型的不同,分别调用new_video_stream(),new_audio_stream(),new_subtitle_stream()等创建不同的AVStream。实际上上述的几个创建AVStream的函数调用了new_output_stream()。而new_output_stream()又调用了FFmpeg类库的API函数avformat_new_stream()。
 
[cpp]  view plain  copy
  1. void opt_output_file(void *optctx, const char *filename)  
  2. {  
  3.     //略…  
  4.     err = avformat_alloc_output_context2(&oc, NULL, o->format, filename);  
  5.    
  6.     if (!oc) {  
  7.         print_error(filename, err);  
  8.         exit(1);  
  9.     }  
  10.     //略…  
  11.    new_video_stream();  
  12.    …  
  13.    new_audio_stream();  
  14.    …  
  15.    new_subtitle_stream ();  
  16.     //略…  
  17.    
  18. }  
  19.    


transcode()


transcode()的功能是转码。其函数调用结构如下图所示。

调用了如下函数
transcode_init():转码的初始化工作。
check_keyboard_interaction():检测键盘操作。例如转码的过程中按下“Q”键之后,会退出转码。
transcode_step():进行转码。
print_report():打印转码信息,输出到屏幕上。
flush_encoder():输出编码器中剩余的帧。
其中check_keyboard_interaction(),transcode_step(),print_report()三个函数位于一个循环之中会不断地执行。

下图红框所示即为print_report()打印输出到屏幕上的信息。


下面简单介绍两个重要的函数transcode_init()和transcode_step()。

transcode_init()

transcode_init()调用了以下几个重要的函数:
av_dump_format():在屏幕上打印输出格式信息。注意是输出格式的信息,输入格式的信息的打印是在parse_options()函数执行过程中调用opt_input_file()的时候打印到屏幕上的。
init_input_stream():其中调用了avcodec_open2()打开编码器。
avformat_write_header():写输出文件的文件头。
 

transcode_step()

transcode_step()调用了如下函数:
process_input():完成解码工作。
transcode_from_filter():未分析。
reap_filters():完成编码工作。
 
process_input()
process_input()主要完成了解码的工作。其函数调用结构如下图所示。

process_input()调用了如下函数:
get_input_packet():获取一帧压缩编码数据,即一个AVPacket。其中调用了av_read_frame()。
output_packet():解码压缩编码的数据并将之送至AVFilterContext。
 
output_packet()调用了如下函数:
decode_video():解码一帧视频(一个AVPacket)。
decode_audio():解码音频(并不一定是一帧,是一个AVPacket)。
do_streamcopy():如果不需要重新编码的话,则调用此函数,一般用于封装格式之间的转换。速度比转码快很多。
 
decode_video()调用了如下函数:
新版本中调用decode()函数,decode中主要使用了新的API avcodec_send_packet和avcodec_receive_frame。代替原来的avcodec_decode_video2():解码一帧视频。
rate_emu_sleep():要求按照帧率处理数据的时候调用,可以避免FFmpeg处理速度过快。常用于网络实时流的处理(RTP/RTMP流的推送)。
configure_filtergraph():设置AVFilterGraph。
av_buffersrc_add_frame():将解码后的数据(一个AVFrame)送至AVFilterContext。
 
decode_audio()调用的函数和decode_video()基本一样。 都是调用了decode函数
 

configure_filtergraph()

未分析。


 
 
reap_filters()
reap_filters()主要完成了编码的工作。其函数调用结构如下图所示。

reap_filters()调用了如下函数
av_buffersink_get_buffer_ref():从AVFilterContext中取出一帧解码后的数据(结构为AVFilterBufferRef,可以转换为AVFrame)。
avfilter_copy_buf_props():AVFilterBufferRef转换为AVFrame。
do_audio_out():编码音频。
do_video_out():编码视频。
avfilter_unref_buffer():释放资源。
 
do_video_out()调用了如下函数
使用了新的API avcodec_send_packet和avcodec_receive_frame//avcodec_encode_video2():编码一帧视频。
write_frame():写入编码后的视频压缩数据。
 
write_frame()调用了如下函数:
av_bitstream_filter_filter():使用AVBitStreamFilter的时候,会调用此函数进行处理。
av_interleaved_write_frame():写入压缩编码数据。
 
do_audio_out()调用的函数与do_video_out()基本上一样。

 


exit_program()

exit_program()主要完成了清理工作。调用关系如下图所示。

 
调用了如下函数:
avfilter_graph_free():释放AVFilterGraph。
avformat_free_context():释放输出文件的AVFormatContext。
av_bitstream_filter_close():关闭AVBitStreamFilter。
avformat_close_input():关闭输入文件。
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
[h264 @ 000001ef94ed8280] Format h264 detected only with low score of 1, misdetection possible! [h264 @ 000001ef94ed9f40] illegal POC type 5 [h264 @ 000001ef94ed9f40] non-existing PPS 10 referenced [AVBSFContext @ 000001ef94ee0280] Invalid NAL unit 0, skipping. Last message repeated 6 times [h264 @ 000001ef94ed9f40] Invalid NAL unit 0, skipping. Last message repeated 6 times [h264 @ 000001ef94ed9f40] slice type 32 too large at 25 [h264 @ 000001ef94ed9f40] decode_slice_header error [h264 @ 000001ef94ed9f40] no frame! [h264 @ 000001ef94ed9f40] non-existing PPS 0 referenced [AVBSFContext @ 000001ef94ee0280] Invalid NAL unit 16, skipping. Last message repeated 3 times [h264 @ 000001ef94ed9f40] Invalid NAL unit 16, skipping. Last message repeated 3 times [h264 @ 000001ef94ed9f40] data partitioning is not implemented. Update your FFmpeg version to the newest one from Git. If the problem still occurs, it means that your file has a feature which has not been implemented. [h264 @ 000001ef94ed9f40] If you want to help, upload a sample of this file to ftp://upload.ffmpeg.org/incoming/ and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org) [h264 @ 000001ef94ed9f40] data partitioning is not implemented. Update your FFmpeg version to the newest one from Git. If the problem still occurs, it means that your file has a feature which has not been implemented. [h264 @ 000001ef94ed9f40] If you want to help, upload a sample of this file to ftp://upload.ffmpeg.org/incoming/ and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org) [h264 @ 000001ef94ed9f40] data partitioning is not implemented. Update your FFmpeg version to the newest one from Git. If the problem still occurs, it means that your file has a feature which has not been implemented. [h264 @ 000001ef94ed9f40] If you want to help, upload a sample of this file to ftp://upload.ffmpeg.org/incoming/ and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org) [h264 @ 000001ef94ed9f40] no frame! [h264 @ 000001ef94ed8280] Stream #0: not enough frames to estimate rate; consider increasing probesize [h264 @ 000001ef94ed8280] decoding for stream 0 failed [h264 @ 000001ef94ed8280] Could not find codec parameters for stream 0 (Video: h264, none): unspecified size Consider increasing the value for the 'analyzeduration' and 'probesize' options Input #0, h264, from 'C:\Users\26742\Desktop\1.264': Duration: N/A, bitrate: N/A Stream #0:0: Video: h264, none, 25 tbr, 1200k tbn, 50 tbc At least one output file must be specified
07-15

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值