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這些變量以及函數的意思)