最新版ffmpeg原始碼分析二:transcode()函式
還是先看一下主函式吧:(省略了很多無關大雅的程式碼)
-
int main(int argc, char **argv)
-
{
-
OptionsContext o = { 0 };
-
int64_t ti;
-
//與命令列分析有關的結構的初始化,下面不再羅嗦
-
reset_options(&o, 0);
-
//設定日誌級別
-
av_log_set_flags(AV_LOG_SKIP_REPEATED);
-
parse_loglevel(argc, argv, options);
-
if (argc > 1 && !strcmp(argv[1], "-d")) {
-
run_as_daemon = 1;
-
av_log_set_callback(log_callback_null);
-
argc--;
-
argv++;
-
}
-
//註冊元件們
-
avcodec_register_all();
-
#if CONFIG_AVDEVICE
-
avdevice_register_all();
-
#endif
-
#if CONFIG_AVFILTER
-
avfilter_register_all();
-
#endif
-
av_register_all();
-
//初始化網路,windows下需要
-
avformat_network_init();
-
show_banner();
-
term_init();
-
//分析命令列輸入的引數們
-
parse_options(&o, argc, argv, options, opt_output_file);
-
//檔案的轉換就在此函式中發生
-
if (transcode(output_files, nb_output_files, input_files, nb_input_files)< 0)
-
exit_program(1);
-
exit_program(0);
-
return 0;
-
}
下面是transcode()函式,轉換就發生在它裡面.不廢話,看註釋吧,應很詳細了
-
static int transcode(
-
OutputFile *output_files,//輸出檔案陣列
-
int nb_output_files,//輸出檔案的數量
-
InputFile *input_files,//輸入檔案陣列
-
int nb_input_files)//輸入檔案的數量
-
{
-
int ret, i;
-
AVFormatContext *is, *os;
-
OutputStream *ost;
-
InputStream *ist;
-
uint8_t *no_packet;
-
int no_packet_count = 0;
-
int64_t timer_start;
-
int key;
-
if (!(no_packet = av_mallocz(nb_input_files)))
-
exit_program(1);
-
//設定編碼引數,開啟所有輸出流的編碼器,開啟所有輸入流的解碼器,寫入所有輸出檔案的檔案頭,於是準備好了
-
ret = transcode_init(output_files, nb_output_files, input_files,nb_input_files);
-
if (ret < 0)
-
goto fail;
-
if (!using_stdin){
-
av_log(NULL, AV_LOG_INFO, "Press [q] to stop, [?] for help\n");
-
}
-
timer_start = av_gettime();
-
//迴圈,直到收到系統訊號才退出
-
for (; received_sigterm == 0;)
-
{
-
int file_index, ist_index;
-
AVPacket pkt;
-
int64_t ipts_min;
-
double opts_min;
-
int64_t cur_time = av_gettime();
-
ipts_min = INT64_MAX;
-
opts_min = 1e100;
-
/* if 'q' pressed, exits */
-
if (!using_stdin)
-
{
-
//先檢視使用者按下了什麼鍵,跟據鍵做出相應的反應
-
static int64_t last_time;
-
if (received_nb_signals)
-
break;
-
/* read_key() returns 0 on EOF */
-
if (cur_time - last_time >= 100000 && !run_as_daemon){
-
key = read_key();
-
last_time = cur_time;
-
}else{
-
.................................
-
}
-
/* select the stream that we must read now by looking at the
-
smallest output pts */
-
//下面這個迴圈的目的是找一個最小的輸出pts(也就是離當前最近的)的輸出流
-
file_index = -1;
-
for (i = 0; i < nb_output_streams; i++){
-
OutputFile *of;
-
int64_t ipts;
-
double opts;
-
ost = &output_streams[i];//迴圈每一個輸出流
-
of = &output_files[ost->file_index];//輸出流對應的輸出檔案
-
os = output_files[ost->file_index].ctx;//輸出流對應的FormatContext
-
ist = &input_streams[ost->source_index];//輸出流對應的輸入流
-
if (ost->is_past_recording_time || //是否過了錄製時間?(可能使用者指定了一個錄製時間段)
-
no_packet[ist->file_index]|| //對應的輸入流這個時間內沒有資料?
-
(os->pb && avio_tell(os->pb) >= of->limit_filesize))//是否超出了錄製範圍(也是使用者指定的)
-
continue;//是的,符合上面某一條,那麼再看下一個輸出流吧
-
//判斷當前輸入流所在的檔案是否可以使用(我也不很明白)
-
opts = ost->st->pts.val * av_q2d(ost->st->time_base);
-
ipts = ist->pts;
-
if (!input_files[ist->file_index].eof_reached) {
-
if (ipts < ipts_min){
-
//每找到一個pts更小的輸入流就記錄下來,這樣迴圈完所有的輸出流時就找到了
-
//pts最小的輸入流,及輸入檔案的序號
-
ipts_min = ipts;
-
if (input_sync)
-
file_index = ist->file_index;
-
}
-
if (opts < opts_min){
-
opts_min = opts;
-
if (!input_sync)
-
file_index = ist->file_index;
-
}
-
}
-
//難道下面這句話的意思是:如果當前的輸出流已接收的幀數,超出使用者指定的輸出最大幀數時,
-
//則當前輸出流所屬的輸出檔案對應的所有輸出流,都算超過了錄影時間?
-
if (ost->frame_number >= ost->max_frames){
-
int j;
-
for (j = 0; j < of->ctx->nb_streams; j++)
-
output_streams[of->ost_index + j].is_past_recording_time = 1;
-
continue;
-
}
-
}
-
/* if none, if is finished */
-
if (file_index < 0) {
-
//如果沒有找到合適的輸入檔案
-
if (no_packet_count){
-
//如果是因為有的輸入檔案暫時得不到資料,則還不算是結束
-
no_packet_count = 0;
-
memset(no_packet, 0, nb_input_files);
-
usleep(10000);
-
continue;
-
}
-
//全部轉換完成了,跳出大迴圈
-
break;
-
}
-
//從找到的輸入檔案中讀出一幀(可能是音訊也可能是視訊),並放到fifo佇列中
-
is = input_files[file_index].ctx;
-
ret = av_read_frame(is, &pkt);
-
if (ret == AVERROR(EAGAIN)) {
-
//此時發生了暫時沒資料的情況
-
no_packet[file_index] = 1;
-
no_packet_count++;
-
continue;
-
}
-
//下文判斷是否有輸入檔案到最後了
-
if (ret < 0){
-
input_files[file_index].eof_reached = 1;
-
if (opt_shortest)
-
break;
-
else
-
continue;
-
}
-
no_packet_count = 0;
-
memset(no_packet, 0, nb_input_files);
-
if (do_pkt_dump){
-
av_pkt_dump_log2(NULL, AV_LOG_DEBUG, &pkt, do_hex_dump,
-
is->streams[pkt.stream_index]);
-
}
-
/* the following test is needed in case new streams appear
-
dynamically in stream : we ignore them */
-
//如果在輸入檔案中遇到一個忽然冒出的流,那麼我們不鳥它
-
if (pkt.stream_index >= input_files[file_index].nb_streams)
-
goto discard_packet;
-
//取得當前獲得的幀對應的輸入流
-
ist_index = input_files[file_index].ist_index + pkt.stream_index;
-
ist = &input_streams[ist_index];
-
if (ist->discard)
-
goto discard_packet;
-
//重新鼓搗一下幀的時間戳
-
if (pkt.dts != AV_NOPTS_VALUE)
-
pkt.dts += av_rescale_q(input_files[ist->file_index].ts_offset,
-
AV_TIME_BASE_Q, ist->st->time_base);
-
if (pkt.pts != AV_NOPTS_VALUE)
-
pkt.pts += av_rescale_q(input_files[ist->file_index].ts_offset,
-
AV_TIME_BASE_Q, ist->st->time_base);
-
if (pkt.pts != AV_NOPTS_VALUE)
-
pkt.pts *= ist->ts_scale;
-
if (pkt.dts != AV_NOPTS_VALUE)
-
pkt.dts *= ist->ts_scale;
-
if (pkt.dts != AV_NOPTS_VALUE && ist->next_pts != AV_NOPTS_VALUE
-
&& (is->iformat->flags & AVFMT_TS_DISCONT))
-
{
-
int64_t pkt_dts = av_rescale_q(pkt.dts, ist->st->time_base,
-
AV_TIME_BASE_Q);
-
int64_t delta = pkt_dts - ist->next_pts;
-
if ((delta < -1LL * dts_delta_threshold * AV_TIME_BASE
-
|| (delta > 1LL * dts_delta_threshold * AV_TIME_BASE
-
&& ist->st->codec->codec_type
-
!= AVMEDIA_TYPE_SUBTITLE)
-
|| pkt_dts + 1 < ist->pts) && !copy_ts)
-
{
-
input_files[ist->file_index].ts_offset -= delta;
-
av_log( NULL, AV_LOG_DEBUG,
-
"timestamp discontinuity %"PRId64", new offset= %"PRId64"\n",
-
delta, input_files[ist->file_index].ts_offset);
-
pkt.dts -= av_rescale_q(delta, AV_TIME_BASE_Q, ist->st->time_base);
-
if (pkt.pts != AV_NOPTS_VALUE)
-
pkt.pts -= av_rescale_q(delta, AV_TIME_BASE_Q, ist->st->time_base);
-
}
-
}
-
//把這一幀轉換並寫入到輸出檔案中
-
if (output_packet(ist, output_streams, nb_output_streams, &pkt) < 0){
-
av_log(NULL, AV_LOG_ERROR,
-
"Error while decoding stream #%d:%d\n",
-
ist->file_index, ist->st->index);
-
if (exit_on_error)
-
exit_program(1);
-
av_free_packet(&pkt);
-
continue;
-
}
-
discard_packet:
-
av_free_packet(&pkt);
-
/* dump report by using the output first video and audio streams */
-
print_report(output_files, output_streams, nb_output_streams, 0,
-
timer_start, cur_time);
-
}
-
//檔案處理完了,把緩衝中剩餘的資料寫到輸出檔案中
-
for (i = 0; i < nb_input_streams; i++){
-
ist = &input_streams[i];
-
if (ist->decoding_needed){
-
output_packet(ist, output_streams, nb_output_streams, NULL);
-
}
-
}
-
flush_encoders(output_streams, nb_output_streams);
-
term_exit();
-
//為輸出檔案寫檔案尾(有的不需要).
-
for (i = 0; i < nb_output_files; i++){
-
os = output_files[i].ctx;
-
av_write_trailer(os);
-
}
-
/* dump report by using the first video and audio streams */
-
print_report(output_files, output_streams, nb_output_streams, 1,
-
timer_start, av_gettime());
-
//關閉所有的編碼器
-
for (i = 0; i < nb_output_streams; i++){
-
ost = &output_streams[i];
-
if (ost->encoding_needed){
-
av_freep(&ost->st->codec->stats_in);
-
avcodec_close(ost->st->codec);
-
}
-
#if CONFIG_AVFILTER
-
avfilter_graph_free(&ost->graph);
-
#endif
-
}
-
//關閉所有的解碼器
-
for (i = 0; i < nb_input_streams; i++){
-
ist = &input_streams[i];
-
if (ist->decoding_needed){
-
avcodec_close(ist->st->codec);
-
}
-
}
-
/* finished ! */
-
ret = 0;
-
fail: av_freep(&bit_buffer);
-
av_freep(&no_packet);
-
if (output_streams) {
-
for (i = 0; i < nb_output_streams; i++) {
-
ost = &output_streams[i];
-
if (ost) {
-
if (ost->stream_copy)
-
av_freep(&ost->st->codec->extradata);
-
if (ost->logfile){
-
fclose(ost->logfile);
-
ost->logfile = NULL;
-
}
-
av_fifo_free(ost->fifo); /* works even if fifo is not
-
initialized but set to zero */
-
av_freep(&ost->st->codec->subtitle_header);
-
av_free(ost->resample_frame.data[0]);
-
av_free(ost->forced_kf_pts);
-
if (ost->video_resample)
-
sws_freeContext(ost->img_resample_ctx);
-
swr_free(&ost->swr);
-
av_dict_free(&ost->opts);
-
}
-
}
-
}
-
return ret;