FFMPEG源码分析通过ffmpeg截图命令分析ffmpeg.c源码流程
Posted 猿来如此yXy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FFMPEG源码分析通过ffmpeg截图命令分析ffmpeg.c源码流程相关的知识,希望对你有一定的参考价值。
环境搭建
- Ubuntu 20.04
- 开启外设摄像头
- 截图命令:
ffmpeg -f video4linux2 -s 640x480 -i /dev/video0 -ss 0:0:2 -frames 1 /data/ffmpeg-4.2.7/exe_cmd/tmp/out2.jpg
参数解析
int main(int argc, char **argv)
.................................;
#if CONFIG_AVDEVICE
avdevice_register_all();
#endif
avformat_network_init();
show_banner(argc, argv, options);
//1. 重点看下参数解析
ffmpeg_parse_options(argc, argv);
//2. 这里面进行具体的数据处理
if (transcode() < 0)
exit_program(1);
.................................;
int ffmpeg_parse_options(int argc, char **argv)
OptionParseContext octx;
uint8_t error[128];
int ret;
memset(&octx, 0, sizeof(octx));
/* split the commandline into an internal representation */
split_commandline(&octx, argc, argv, options, groups,
FF_ARRAY_ELEMS(groups));
/* apply global options */
parse_optgroup(NULL, &octx.global_opts);
/* configure terminal and setup signal handlers */
term_init();
/* open input files */
open_files(&octx.groups[GROUP_INFILE], "input", open_input_file);
init_complex_filters();
/* open output files */
open_files(&octx.groups[GROUP_OUTFILE], "output", open_output_file);
.....................................;
check_filter_outputs();
.....................................;
return ret;
split_commandline
//全局变量 设置给split_commandline
const OptionDef options[] =
/* main options */
CMDUTILS_COMMON_OPTIONS
"f", HAS_ARG | OPT_STRING | OPT_OFFSET |
OPT_INPUT | OPT_OUTPUT, .off = OFFSET(format) ,
"force format", "fmt" ,
"y", OPT_BOOL, &file_overwrite ,
"overwrite output files" ,
...............................;
enum OptGroup
GROUP_OUTFILE,
GROUP_INFILE,
;
//全局变量 设置给split_commandline
static const OptionGroupDef groups[] =
[GROUP_OUTFILE] = "output url", NULL, OPT_OUTPUT ,
[GROUP_INFILE] = "input url", "i", OPT_INPUT ,
;
int split_commandline(OptionParseContext *octx, int argc, char *argv[],
const OptionDef *options,
const OptionGroupDef *groups, int nb_groups)
int optindex = 1;
int dashdash = -2;
//针对windows上输入参数字符集的转换如:utf-8等,不必深入。
prepare_app_arguments(&argc, &argv);
init_parse_context(octx, groups, nb_groups);
av_log(NULL, AV_LOG_DEBUG, "Splitting the commandline.\\n");
while (optindex < argc)
const char *opt = argv[optindex++], *arg;
const OptionDef *po;
int ret;
if (opt[0] == '-' && opt[1] == '-' && !opt[2])
dashdash = optindex;
continue;
// -i之后的作为output的一个group,即全局变量groups中的GROUP_OUTFILE
if (opt[0] != '-' || !opt[1] || dashdash+1 == optindex)
finish_group(octx, 0, opt);
av_log(NULL, AV_LOG_DEBUG, " matched as %s.\\n", groups[0].name);
continue;
opt++;
#define GET_ARG(arg) \\
do \\
arg = argv[optindex++]; \\
if (!arg) \\
av_log(NULL, AV_LOG_ERROR, "Missing argument for option '%s'.\\n", opt);\\
return AVERROR(EINVAL); \\
\\
while (0)
//-i 以及-i 之前的参数全部存入全局变量groups中的GROUP_INFILE
/* named group separators, e.g. -i */
if ((ret = match_group_separator(groups, nb_groups, opt)) >= 0)
GET_ARG(arg);
finish_group(octx, ret, arg);
av_log(NULL, AV_LOG_DEBUG, " matched as %s with argument '%s'.\\n",
groups[ret].name, arg);
continue;
/* normal options */
po = find_option(options, opt);
if (po->name)
if (po->flags & OPT_EXIT)
/* optional argument, e.g. -h */
arg = argv[optindex++];
else if (po->flags & HAS_ARG)
GET_ARG(arg);
else
arg = "1";
add_opt(octx, po, opt, arg);
av_log(NULL, AV_LOG_DEBUG, " matched as option '%s' (%s) with "
"argument '%s'.\\n", po->name, po->help, arg);
continue;
/* AVOptions */
if (argv[optindex])
ret = opt_default(NULL, opt, argv[optindex]);
if (ret >= 0)
av_log(NULL, AV_LOG_DEBUG, " matched as AVOption '%s' with "
"argument '%s'.\\n", opt, argv[optindex]);
optindex++;
continue;
else if (ret != AVERROR_OPTION_NOT_FOUND)
av_log(NULL, AV_LOG_ERROR, "Error parsing option '%s' "
"with argument '%s'.\\n", opt, argv[optindex]);
return ret;
/* boolean -nofoo options */
if (opt[0] == 'n' && opt[1] == 'o' &&
(po = find_option(options, opt + 2)) &&
po->name && po->flags & OPT_BOOL)
add_opt(octx, po, opt, "0");
av_log(NULL, AV_LOG_DEBUG, " matched as option '%s' (%s) with "
"argument 0.\\n", po->name, po->help);
continue;
av_log(NULL, AV_LOG_ERROR, "Unrecognized option '%s'.\\n", opt);
return AVERROR_OPTION_NOT_FOUND;
if (octx->cur_group.nb_opts || codec_opts || format_opts || resample_opts)
av_log(NULL, AV_LOG_WARNING, "Trailing options were found on the "
"commandline.\\n");
av_log(NULL, AV_LOG_DEBUG, "Finished splitting the commandline.\\n");
return 0;
上述代码主要是将ffmpeg命令行参数解析到OptionParseContext中。OptionParseContext的成员变量OptionGroupList *groups由两个,一个为input OptionGroupList另一个为output OptionGroupList。一个OptionGroupList中由多个OptionGroup组成,而一个OptionGroup由多个Option组成。具体关系如下图:
OptionGroupList输入/输出参数以及option的遍历代码如下:
OptionParseContext octx;
for(int i = 0; i < octx.nb_groups; i++)
OptionGroupList *ll = &octx.groups[i];
av_log(NULL,AV_LOG_ERROR, "OptionGroupList[%d] def:%s\\n",i, ll->group_def->name);
for(int j = 0; j < ll->nb_groups; j++)
OptionGroup *g = &ll->groups[j];
av_log(NULL,AV_LOG_ERROR,"g[%d][%d],arg:%s, def: %s \\n",i,j, g->arg, g->group_def->name);
for (int x = 0; x < g->nb_opts; x++)
Option *opt = &g->opts[x];
av_log(NULL,AV_LOG_ERROR,"g[%d][%d], opts[%d],key:%s, value:%s, def: %s \\n",i,j,x, opt->key, opt->val, opt->opt->name);
如命令行: ffmpeg -f video4linux2 -s 640x480 -i /dev/video0 -ss 0:0:2 -frames 1 /data/ffmpeg-4.2.7/exe_cmd/tmp/out2.jpg 解析后遍历结果如下:
OptionGroupList[0] def:output url
g[0][0],arg:/data/ffmpeg-4.2.7/exe_cmd/tmp/out2.jpg, def: output url
g[0][0], opts[0],key:ss, value:0:0:2, def: ss
g[0][0], opts[1],key:frames, value:1, def: frames
OptionGroupList[1] def:input url
g[1][0],arg:/dev/video0, def: input url
g[1][0], opts[0],key:f, value:video4linux2, def: f
g[1][0], opts[1],key:s, value:640x480, def: s
parse_optgroup
这个函数的作中是存储全局的option,如: -v debug 对所有模块都适用
int parse_optgroup(void *optctx, OptionGroup *g)
int i, ret;
for (i = 0; i < g->nb_opts; i++)
Option *o = &g->opts[i];
ret = write_option(optctx, o->opt, o->key, o->val);
if (ret < 0)
return ret;
return 0;
open_files
参数解析完成之后,就是通过input url和output url找到对应的AVInputFormat和AVOutputFormat。
int ffmpeg_parse_options(int argc, char **argv)
................................................;
//将input arg group传入进去
open_files(&octx.groups[GROUP_INFILE], "input", open_input_file)
................................................;
static int open_files(OptionGroupList *l, const char *inout,
int (*open_file)(OptionsContext*, const char*))
int i, ret;
for (i = 0; i < l->nb_groups; i++)
OptionGroup *g = &l->groups[i];
OptionsContext o;
init_options(&o);
o.g = g;
ret = parse_optgroup(&o, g);
av_log(NULL, AV_LOG_DEBUG, "Opening an %s file: %s.\\n", inout, g->arg);
//将input arg option group赋值到OptionsContext后调用open_input_file
ret = open_file(&o, g->arg);
uninit_options(&o);
return 0;
open_input_file
static int open_input_file(OptionsContext *o, const char *filename)
InputFile *f;
AVFormatContext *ic;
AVInputFormat *file_iformat = NULL;
int err, i, ret;
int64_t timestamp;
AVDictionary *unused_opts = NULL;
AVDictionaryEntry *e = NULL;
char * video_codec_name = NULL;
char * audio_codec_name = NULL;
char *subtitle_codec_name = NULL;
char * data_codec_name = NULL;
int scan_all_pmts_set = 0;
.....................................;
if (o->format)
//在本例中通过video4linux2 找到ff_v4l2_demuxer
if (!(file_iformat = av_find_input_format(o->format)))
av_log(NULL, AV_LOG_FATAL, "Unknown input format: '%s'\\n", o->format);
exit_program(1);
............................;
//分配AVFormatContext
ic = avformat_alloc_context();
..............FFMPEG源码分析从ffplay源码摸清ffmpeg框架
FFmpeg源码简单分析:结构体成员管理系统-AVOption
FFmpeg-4.2.4的filter: overlay源码分析
FFmpeg-4.2.4的filter: overlay源码分析