FFmpeg深入之ffmpeg_parse_options

FFmpeg深入之ffmpeg_parse_options

简述

1
ffmpeg [global_options] {[input_file_options] -i input_url} ... {[output_file_options] output_url} ...

根据FFmpeg官方文档定义,FFmpeg命令由 全局参数+{输入参数+输入源}+{输出参数+输出流} 组成。其中全局参数可放在任何地方,输入参数必须放在输入源之前。

例:

1
ffmpeg -i video.mp4 -i 1.png -i 1.mp3 -filter_complex "[0:v]scale=750:1334,pad=750:1334:0:0:black[source];[source][1:v]overlay=x=0:y=0[result]" -map "[result]" -map 2:a -s 540x960 -t 7 -shortest -movflags faststart -y output.mp4

那么当FFmpeg接收到上面这样一串字符命令时,是如何解析和处理的?

1
2
3
4
5
6
int main(int argc, char **argv)
{
...
/* parse options and open all input/output files */
ret = ffmpeg_parse_options(argc, argv);
}

在FFmpeg的main方法中可以看到,所有的解析逻辑都在ffmpeg_parse_options函数中,下面我们来一起分析下该函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
int ffmpeg_parse_options(int argc, char **argv)
{
OptionParseContext octx;
uint8_t error[128];
int ret;

memset(&octx, 0, sizeof(octx));

/* 解析输入的字符命令 */
ret = split_commandline(&octx, argc, argv, options, groups,
FF_ARRAY_ELEMS(groups));
if (ret < 0) {
av_log(NULL, AV_LOG_FATAL, "Error splitting the argument list: ");
goto fail;
}

/* 应用全局参数 */
ret = parse_optgroup(NULL, &octx.global_opts);
if (ret < 0) {
av_log(NULL, AV_LOG_FATAL, "Error parsing global options: ");
goto fail;
}

/* configure terminal and setup signal handlers */
term_init();

/* 打开输入文件流 */
ret = open_files(&octx.groups[GROUP_INFILE], "input", open_input_file);
if (ret < 0) {
av_log(NULL, AV_LOG_FATAL, "Error opening input files: ");
goto fail;
}

/* 初始化滤镜图 */
ret = init_complex_filters();
if (ret < 0) {
av_log(NULL, AV_LOG_FATAL, "Error initializing complex filters.\n");
goto fail;
}

/* 打开输出文件流 */
ret = open_files(&octx.groups[GROUP_OUTFILE], "output", open_output_file);
if (ret < 0) {
av_log(NULL, AV_LOG_FATAL, "Error opening output files: ");
goto fail;
}

check_filter_outputs();

fail:
uninit_parse_context(&octx);
if (ret < 0) {
av_strerror(ret, error, sizeof(error));
av_log(NULL, AV_LOG_FATAL, "%s\n", error);
}
return ret;
}

解析参数

将参数解析到OptionParseContext中

1
2
3
4
5
6
// octx: OptionParseContext
// argc, argv: main方法中接收的字符命令
// options: OptionDef数组,定义了所有可用的参数
// groups: OptionGroupDef数组,定义了输入和输出两种参数组
// FF_ARRAY_ELEMS(groups): groups的size
ret = split_commandline(&octx, argc, argv, options, groups,FF_ARRAY_ELEMS(groups));

init_parse_context

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static void init_parse_context(OptionParseContext *octx,
const OptionGroupDef *groups, int nb_groups)
{
static const OptionGroupDef global_group = { "global" };
int i;

memset(octx, 0, sizeof(*octx));

octx->nb_groups = nb_groups;
octx->groups = av_mallocz_array(octx->nb_groups, sizeof(*octx->groups));
if (!octx->groups)
exit_program(1);
// 将输入参数组和输出参数组设置到 octx->groups中
for (i = 0; i < octx->nb_groups; i++)
octx->groups[i].group_def = &groups[i];
// 将全局参数组放入到 octx->global_opts.group_def中
octx->global_opts.group_def = &global_group;
octx->global_opts.arg = "";

init_opts();
}

遍历所有参数

遍历所有参数,将参数加入到OptionParseContext中对应的参数组中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
    while (optindex < argc) {
const char *opt = argv[optindex++], *arg;
const OptionDef *po;
int ret;

av_log(NULL, AV_LOG_DEBUG, "Reading option '%s' ...", opt);
// 过滤 --开头的参数
if (opt[0] == '-' && opt[1] == '-' && !opt[2]) {
dashdash = optindex;
continue;
}
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)

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;
}

// 处理一般参数,根据参数名称查找OptionDef对象
po = find_option(options, opt);
if (po->name) {
if (po->flags & OPT_EXIT) {
arg = argv[optindex++];
} else if (po->flags & HAS_ARG) {
GET_ARG(arg);
} else {
arg = "1";
}
// 将参数加入到octx对应的参数组中
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;
}
...
return AVERROR_OPTION_NOT_FOUND;
}

解析全局参数

解析所有的全局参数

1
2
3
4
5
6
7
8
9
10
11
12
13
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;
}

write_option

该方法中用来解析所有全局参数,根据每个Options对象定义的解析方法来解析。比如说 filter_complex 参数

1
2
{ "filter_complex", HAS_ARG | OPT_EXPERT,                        { .func_arg = opt_filter_complex },
"create a complex filtergraph", "graph_description" },

该Options定义了自己的func_arg,那么解析该参数就会调用 opt_filter_complex函数来解析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
static int write_option(void *optctx, const OptionDef *po, const char *opt,
const char *arg)
{
// 根据Option定义的flag,来解析不同类型的Option
if (po->flags & OPT_STRING) {
char *str;
str = av_strdup(arg);
av_freep(dst);
if (!str)
return AVERROR(ENOMEM);
*(char **)dst = str;
} else if (po->flags & OPT_BOOL || po->flags & OPT_INT) {
*(int *)dst = parse_number_or_die(opt, arg, OPT_INT64, INT_MIN, INT_MAX);
} else if (po->flags & OPT_INT64) {
*(int64_t *)dst = parse_number_or_die(opt, arg, OPT_INT64, INT64_MIN, INT64_MAX);
} else if (po->flags & OPT_TIME) {
*(int64_t *)dst = parse_time_or_die(opt, arg, 1);
} else if (po->flags & OPT_FLOAT) {
*(float *)dst = parse_number_or_die(opt, arg, OPT_FLOAT, -INFINITY, INFINITY);
} else if (po->flags & OPT_DOUBLE) {
*(double *)dst = parse_number_or_die(opt, arg, OPT_DOUBLE, -INFINITY, INFINITY);
} else if (po->u.func_arg) {
// 调用Options的func_arg函数来解析
int ret = po->u.func_arg(optctx, opt, arg);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR,
"Failed to set value '%s' for option '%s': %s\n",
arg, opt, av_err2str(ret));
return ret;
}
}
if (po->flags & OPT_EXIT)
exit_program(0);

return 0;
}

打开输入文件流

从OptionParseContext取出输入参数组,

1
ret = open_files(&octx.groups[GROUP_INFILE], "input", open_input_file);

open_files

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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);
...
// 调用参数中传递的函数打开文件,g->arg就是-i后面的文件路径
ret = open_file(&o, g->arg);
uninit_options(&o);
...

return 0;
}

open_input_file

首先会处理该输入文件对应的输入参数,然后通过avformat_open_input开启流,再通过avformat_find_stream_info获取流详细信息,最后将流加入到全局输入流集合中。

关于avformat_open_input可以查看这篇文章 FFmpeg深入之avformat_open_input

关于avformat_find_stream_info可以查看这篇文章 FFmpeg深入之avformat_find_stream_info

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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) {
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);
}
}

if (!strcmp(filename, "-"))
filename = "pipe:";

stdin_interaction &= strncmp(filename, "pipe:", 5) &&
strcmp(filename, "/dev/stdin");

/* 创建AVFormatContext */
ic = avformat_alloc_context();
...
// 设置输入音频采样率
if (o->nb_audio_sample_rate) {
av_dict_set_int(&o->g->format_opts, "sample_rate", o->audio_sample_rate[o->nb_audio_sample_rate - 1].u.i, 0);
}
// 设置音频声道
if (o->nb_audio_channels) {
/* because we set audio_channels based on both the "ac" and
* "channel_layout" options, we need to check that the specified
* demuxer actually has the "channels" option before setting it */
if (file_iformat && file_iformat->priv_class &&
av_opt_find(&file_iformat->priv_class, "channels", NULL, 0,
AV_OPT_SEARCH_FAKE_OBJ)) {
av_dict_set_int(&o->g->format_opts, "channels", o->audio_channels[o->nb_audio_channels - 1].u.i, 0);
}
}
// 设置视频帧率
if (o->nb_frame_rates) {
/* set the format-level framerate option;
* this is important for video grabbers, e.g. x11 */
if (file_iformat && file_iformat->priv_class &&
av_opt_find(&file_iformat->priv_class, "framerate", NULL, 0,
AV_OPT_SEARCH_FAKE_OBJ)) {
av_dict_set(&o->g->format_opts, "framerate",
o->frame_rates[o->nb_frame_rates - 1].u.str, 0);
}
}
// 设置视频帧数
if (o->nb_frame_sizes) {
av_dict_set(&o->g->format_opts, "video_size", o->frame_sizes[o->nb_frame_sizes - 1].u.str, 0);
}
// 设置视频像素的颜色空间格式
if (o->nb_frame_pix_fmts)
av_dict_set(&o->g->format_opts, "pixel_format", o->frame_pix_fmts[o->nb_frame_pix_fmts - 1].u.str, 0);
// 设置 c:v c:a c:s c:d 解码格式参数
MATCH_PER_TYPE_OPT(codec_names, str, video_codec_name, ic, "v");
MATCH_PER_TYPE_OPT(codec_names, str, audio_codec_name, ic, "a");
MATCH_PER_TYPE_OPT(codec_names, str, subtitle_codec_name, ic, "s");
MATCH_PER_TYPE_OPT(codec_names, str, data_codec_name, ic, "d");
if (video_codec_name)
ic->video_codec = find_codec_or_die(video_codec_name , AVMEDIA_TYPE_VIDEO , 0);
if (audio_codec_name)
ic->audio_codec = find_codec_or_die(audio_codec_name , AVMEDIA_TYPE_AUDIO , 0);
if (subtitle_codec_name)
ic->subtitle_codec = find_codec_or_die(subtitle_codec_name, AVMEDIA_TYPE_SUBTITLE, 0);
if (data_codec_name)
ic->data_codec = find_codec_or_die(data_codec_name , AVMEDIA_TYPE_DATA , 0);
// 如果该解码格式没有开启不存在,则设置为AV_CODEC_ID_NONE
ic->video_codec_id = video_codec_name ? ic->video_codec->id : AV_CODEC_ID_NONE;
ic->audio_codec_id = audio_codec_name ? ic->audio_codec->id : AV_CODEC_ID_NONE;
ic->subtitle_codec_id = subtitle_codec_name ? ic->subtitle_codec->id : AV_CODEC_ID_NONE;
ic->data_codec_id = data_codec_name ? ic->data_codec->id : AV_CODEC_ID_NONE;

ic->flags |= AVFMT_FLAG_NONBLOCK;
if (o->bitexact)
ic->flags |= AVFMT_FLAG_BITEXACT;
ic->interrupt_callback = int_cb;

if (!av_dict_get(o->g->format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE)) {
av_dict_set(&o->g->format_opts, "scan_all_pmts", "1", AV_DICT_DONT_OVERWRITE);
scan_all_pmts_set = 1;
}
// 打开文件流,读取多媒体文件头信息
err = avformat_open_input(&ic, filename, file_iformat, &o->g->format_opts);
...
if (scan_all_pmts_set)
av_dict_set(&o->g->format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE);
remove_avoptions(&o->g->format_opts, o->g->codec_opts);
assert_avoptions(o->g->format_opts);

/* 根据avformat_open_input开启的流信息中确定的编码格式,重新选择解码器 */
for (i = 0; i < ic->nb_streams; i++)
choose_decoder(o, ic, ic->streams[i]);

if (find_stream_info) {
AVDictionary **opts = setup_find_stream_info_opts(ic, o->g->codec_opts);
int orig_nb_streams = ic->nb_streams;

/* 解码文件头数据获取流信息,如果不足,则解码首帧来获取信息 */
ret = avformat_find_stream_info(ic, opts);

for (i = 0; i < orig_nb_streams; i++)
av_dict_free(&opts[i]);
av_freep(&opts);
...
}

...

/* 将流信息添加到全局的输入流集合中 */
add_input_streams(o, ic);

// 释放资源,重置状态
...
return 0;
}

初始化滤镜图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
int init_complex_filtergraph(FilterGraph *fg)
{
AVFilterInOut *inputs, *outputs, *cur;
AVFilterGraph *graph;
int ret = 0;
// AVFilterGraph
graph = avfilter_graph_alloc();
if (!graph)
return AVERROR(ENOMEM);
graph->nb_threads = 1;
// 解析滤镜参数
ret = avfilter_graph_parse2(graph, fg->graph_desc, &inputs, &outputs);
if (ret < 0)
goto fail;

for (cur = inputs; cur; cur = cur->next)
init_input_filter(fg, cur);
// 将解析的结果放到AVFilterGraph中,将每一个滤镜解析成 输入+滤镜+输出
for (cur = outputs; cur;) {
GROW_ARRAY(fg->outputs, fg->nb_outputs);
fg->outputs[fg->nb_outputs - 1] = av_mallocz(sizeof(*fg->outputs[0]));
if (!fg->outputs[fg->nb_outputs - 1])
exit_program(1);

fg->outputs[fg->nb_outputs - 1]->graph = fg;
fg->outputs[fg->nb_outputs - 1]->out_tmp = cur;
fg->outputs[fg->nb_outputs - 1]->type = avfilter_pad_get_type(cur->filter_ctx->output_pads,
cur->pad_idx);
fg->outputs[fg->nb_outputs - 1]->name = describe_filter_link(fg, cur, 0);
cur = cur->next;
fg->outputs[fg->nb_outputs - 1]->out_tmp->next = NULL;
}

fail:
avfilter_inout_free(&inputs);
avfilter_graph_free(&graph);
return ret;
}

avfilter_graph_parse2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
int avfilter_graph_parse2(AVFilterGraph *graph, const char *filters,
AVFilterInOut **inputs,
AVFilterInOut **outputs)
{
int index = 0, ret = 0;
char chr = 0;

AVFilterInOut *curr_inputs = NULL, *open_inputs = NULL, *open_outputs = NULL;

filters += strspn(filters, WHITESPACES);

if ((ret = parse_sws_flags(&filters, graph)) < 0)
goto fail;
// 循环处理所有滤镜
do {
AVFilterContext *filter;
filters += strspn(filters, WHITESPACES);
// 解析输入 0:v
if ((ret = parse_inputs(&filters, &curr_inputs, &open_outputs, graph)) < 0)
goto end;
// 解析滤镜 scale=w:h
if ((ret = parse_filter(&filter, &filters, graph, index, graph)) < 0)
goto end;


if ((ret = link_filter_inouts(filter, &curr_inputs, &open_inputs, graph)) < 0)
goto end;
// 解析输出 [source]
if ((ret = parse_outputs(&filters, &curr_inputs, &open_inputs, &open_outputs,
graph)) < 0)
goto end;

filters += strspn(filters, WHITESPACES);
chr = *filters++;

if (chr == ';' && curr_inputs)
append_inout(&open_outputs, &curr_inputs);
index++;
} while (chr == ',' || chr == ';');

...

append_inout(&open_outputs, &curr_inputs);


*inputs = open_inputs;
*outputs = open_outputs;
return 0;
...
}

打开输出文件流

open_files

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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);
...
// 调用参数中传递的函数打开文件,g->arg就是参数最后面的输出文件路径
ret = open_file(&o, g->arg);
uninit_options(&o);
...

return 0;
}

open_output_file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
static int open_output_file(OptionsContext *o, const char *filename)
{
AVFormatContext *oc;
int i, j, err;
OutputFile *of;
OutputStream *ost;
InputStream *ist;
AVDictionary *unused_opts = NULL;
AVDictionaryEntry *e = NULL;
int format_flags = 0;

...
// 创建AVFormatContext
err = avformat_alloc_output_context2(&oc, NULL, o->format, filename);
...

/* create streams for all unlabeled output pads */
for (i = 0; i < nb_filtergraphs; i++) {
FilterGraph *fg = filtergraphs[i];
for (j = 0; j < fg->nb_outputs; j++) {
OutputFilter *ofilter = fg->outputs[j];

if (!ofilter->out_tmp || ofilter->out_tmp->name)
continue;

switch (ofilter->type) {
case AVMEDIA_TYPE_VIDEO: o->video_disable = 1; break;
case AVMEDIA_TYPE_AUDIO: o->audio_disable = 1; break;
case AVMEDIA_TYPE_SUBTITLE: o->subtitle_disable = 1; break;
}
init_output_filter(ofilter, o, oc);
}
}
// 是否有-map操作
if (!o->nb_stream_maps) {
char *subtitle_codec_name = NULL;
/* pick the "best" stream of each type */

/* video: highest resolution */
if (!o->video_disable && av_guess_codec(oc->oformat, NULL, filename, NULL, AVMEDIA_TYPE_VIDEO) != AV_CODEC_ID_NONE) {
int area = 0, idx = -1;
// 查询解码器
int qcr = avformat_query_codec(oc->oformat, oc->oformat->video_codec, 0);
// 遍历所有的输入流
for (i = 0; i < nb_input_streams; i++) {
int new_area;
ist = input_streams[i];
new_area = ist->st->codecpar->width * ist->st->codecpar->height + 100000000*!!ist->st->codec_info_nb_frames
+ 5000000*!!(ist->st->disposition & AV_DISPOSITION_DEFAULT);
if (ist->user_set_discard == AVDISCARD_ALL)
continue;
if((qcr!=MKTAG('A', 'P', 'I', 'C')) && (ist->st->disposition & AV_DISPOSITION_ATTACHED_PIC))
new_area = 1;
if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
new_area > area) {
if((qcr==MKTAG('A', 'P', 'I', 'C')) && !(ist->st->disposition & AV_DISPOSITION_ATTACHED_PIC))
continue;
area = new_area;
idx = i;
}
}
// 创建一个新的视频输出流
if (idx >= 0)
new_video_stream(o, oc, idx);
}

/* audio: most channels */
if (!o->audio_disable && av_guess_codec(oc->oformat, NULL, filename, NULL, AVMEDIA_TYPE_AUDIO) != AV_CODEC_ID_NONE) {
int best_score = 0, idx = -1;
for (i = 0; i < nb_input_streams; i++) {
int score;
ist = input_streams[i];
score = ist->st->codecpar->channels + 100000000*!!ist->st->codec_info_nb_frames
+ 5000000*!!(ist->st->disposition & AV_DISPOSITION_DEFAULT);
if (ist->user_set_discard == AVDISCARD_ALL)
continue;
if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
score > best_score) {
best_score = score;
idx = i;
}
}
// 创建一个新的音频输出流
if (idx >= 0)
new_audio_stream(o, oc, idx);
}

...
// 创建新的字幕和数据输出流
} else {
// 遍历所有的map
for (i = 0; i < o->nb_stream_maps; i++) {
StreamMap *map = &o->stream_maps[i];

if (map->disabled)
continue;
// map是一个输出的链接,类似于-map [source]
if (map->linklabel) {
FilterGraph *fg;
OutputFilter *ofilter = NULL;
int j, k;
// 应用滤镜
for (j = 0; j < nb_filtergraphs; j++) {
fg = filtergraphs[j];
for (k = 0; k < fg->nb_outputs; k++) {
AVFilterInOut *out = fg->outputs[k]->out_tmp;
if (out && !strcmp(out->name, map->linklabel)) {
ofilter = fg->outputs[k];
goto loop_end;
}
}
}
...
// 创建音频和视频输出流,只有音频和视频流支持滤镜
init_output_filter(ofilter, o, oc);
} else {
// 类似于-map 2:a
int src_idx = input_files[map->file_index]->ist_index + map->stream_index;

ist = input_streams[input_files[map->file_index]->ist_index + map->stream_index];
...
// 根据当前map流的类型,创建不同类型的输出流
ost = NULL;
switch (ist->st->codecpar->codec_type) {
case AVMEDIA_TYPE_VIDEO: ost = new_video_stream (o, oc, src_idx); break;
case AVMEDIA_TYPE_AUDIO: ost = new_audio_stream (o, oc, src_idx); break;
case AVMEDIA_TYPE_SUBTITLE: ost = new_subtitle_stream (o, oc, src_idx); break;
case AVMEDIA_TYPE_DATA: ost = new_data_stream (o, oc, src_idx); break;
case AVMEDIA_TYPE_ATTACHMENT: ost = new_attachment_stream(o, oc, src_idx); break;
case AVMEDIA_TYPE_UNKNOWN:
...
default:
...
}
if (ost)
ost->sync_ist = input_streams[ input_files[map->sync_file_index]->ist_index
+ map->sync_stream_index];
}
}
}

...

/* set the decoding_needed flags and create simple filtergraphs */


/* check filename in case of an image number is expected */


for (i = 0; i < o->nb_metadata_map; i++) {
char *p;
int in_file_index = strtol(o->metadata_map[i].u.str, &p, 0);

...
// 复制metadata
copy_metadata(o->metadata_map[i].specifier, *p ? p + 1 : p, oc,
in_file_index >= 0 ?
input_files[in_file_index]->ctx : NULL, o);
}

/* copy chapters */
if (o->chapters_input_file >= nb_input_files) {
if (o->chapters_input_file == INT_MAX) {
/* copy chapters from the first input file that has them*/
o->chapters_input_file = -1;
for (i = 0; i < nb_input_files; i++)
if (input_files[i]->ctx->nb_chapters) {
o->chapters_input_file = i;
break;
}
} else {
...
}
}
if (o->chapters_input_file >= 0)
copy_chapters(input_files[o->chapters_input_file], of,
!o->metadata_chapters_manual);

/* 复制全局metadata */
if (!o->metadata_global_manual && nb_input_files){
av_dict_copy(&oc->metadata, input_files[0]->ctx->metadata,
AV_DICT_DONT_OVERWRITE);
if(o->recording_time != INT64_MAX)
av_dict_set(&oc->metadata, "duration", NULL, 0);
av_dict_set(&oc->metadata, "creation_time", NULL, 0);
}
if (!o->metadata_streams_manual)
for (i = of->ost_index; i < nb_output_streams; i++) {
InputStream *ist;
if (output_streams[i]->source_index < 0) /* this is true e.g. for attached files */
continue;
ist = input_streams[output_streams[i]->source_index];
av_dict_copy(&output_streams[i]->st->metadata, ist->st->metadata, AV_DICT_DONT_OVERWRITE);
if (!output_streams[i]->stream_copy) {
av_dict_set(&output_streams[i]->st->metadata, "encoder", NULL, 0);
}
}

/* process manually set programs */
for (i = 0; i < o->nb_program; i++) {
const char *p = o->program[i].u.str;
int progid = i+1;
AVProgram *program;

while(*p) {
const char *p2 = av_get_token(&p, ":");
const char *to_dealloc = p2;
char *key;
if (!p2)
break;

if(*p) p++;

key = av_get_token(&p2, "=");
if (!key || !*p2) {
av_freep(&to_dealloc);
av_freep(&key);
break;
}
p2++;

if (!strcmp(key, "program_num"))
progid = strtol(p2, NULL, 0);
av_freep(&to_dealloc);
av_freep(&key);
}

program = av_new_program(oc, progid);

}
...

return 0;
}
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×