2014 10 27
Ffmpeg 1.0 contact分析
ffmpeg -y -i test.3gp -i test1.3gp -filter_complex 'concat' -c:v libx264 -an -strict -2 merge.3gp
注:test.3gp和test1.3gp的分辨率大小一样,为了简单起见只处理视频部分。
由于avf_concat.c文件的ConcatContext结构的nb_segments缺省值为2,所以在这个文件的init函数会创建2个input_pads,ctx->nb_inputs=2。在后续的graphparser.c文件的avfilter_graph_parse2函数里调用link_filter_inouts时,link_filter_inouts函数会linker两个input到open_inputs.
avfilter_graph_parse2函数后面会调用
for (cur = inputs, i = 0; cur; cur = cur->next, i++)
if ((ret = configure_input_filter(fg, fg->inputs[i], cur)) < 0)
return ret;
从而调用了2次configure_input_video_filter,创建了2个buffer filter,如下:
Buffer(1)---
|--->concat--->format--->sink
Buffer(2)---
av_buffersrc_add_ref--->buffersrc.c::request_frame()--->ff_end_frame--->avf_concat.c:: end_frame--->process_frame--->push_frame(),push_frame函数的主要用处是根据当前输入的input_pad的index(就是代码中的in_no)来进行pts计算。
transcode_from_filter--->avfilter_graph_request_oldest--->ff_request_frame---> avf_concat.c:: request_frame,在request_frame函数里有如下处理:
while (1) {
if (in_no >= ctx->nb_inputs)
return AVERROR_EOF;
if (!cat->in[in_no].eof) {
ret = ff_request_frame(ctx->inputs[in_no]);
if (ret != AVERROR_EOF)
return ret;
close_input(ctx, in_no);
}
可见,即使第一个文件的ff_request_frame返回为AVERROR_EOF,改函数只是调用close_input来关闭第一个,继续获取第二个,这时候会返回EAGAIN。从而在transcode_from_filter函数不会调用close_output_stream来关闭结束整个编码。transcode_from_filter函数又有如下代码来将*best_ist指向第二个输入文件:
for (i = 0; i < graph->nb_inputs; i++) {
ifilter = graph->inputs[i];
ist = ifilter->ist;
if (input_files[ist->file_index]->eagain ||
input_files[ist->file_index]->eof_reached)
{
av_log(NULL, AV_LOG_INFO, "transcode_from_filter contine\n");
continue;
}
nb_requests = av_buffersrc_get_nb_failed_requests(ifilter->filter);
if (nb_requests > nb_requests_max) {
nb_requests_max = nb_requests;
*best_ist = ist;
}
}
由于input_files[0]->eof_reached已经结束,所以*best_ist会指向第二个输入文件。
综上,avf_concat.c的主要作用是:
1. 当第一个文件结束时,通过控制使avfilter_graph_request_oldest返回EAGAIN,从而继续编码第2个文件
2. end_frame/push_frame计算正确的pts(累加第一个文件的pts)
3. 第一个文件结束时,确保将解码缓冲区的buffer正确输出(未完全看明白cur_idx/nb_in_active/queue/flush_segment/send_silence这些变量以及函数的意思)