最新版ffmpeg源碼分析一:框架windows
(ffmpeg v0.9)數組
框架
最新版的ffmpeg中發現了一個新的東西:avconv,並且ffmpeg.c與avconv.c一個模樣,一研究才發現是libav下把ffmpeg更名爲avconv了.網絡
到底libav與ffmpeg如今是什麼個關係?我也搞得希裏糊塗的,先無論它了.app
ffmpeg的主要功能是音視頻的轉換和處理.其功能之強大已經到了匪夷所思的地步(有點替它吹了).它的主要特色是能作到把多個輸入文件中的任意幾個流從新組合到輸出文件中,固然輸出文件也能夠有多個.框架
因此咱們就會發現,在ffmpeg.c中,有相似於以下的一些變量:
static InputStream *input_streams = NULL;
static int nb_input_streams = 0;
static InputFile *input_files = NULL;
static int nb_input_files = 0;
static OutputStream *output_streams = NULL;
static int nb_output_streams = 0;
static OutputFile *output_files = NULL;
static int nb_output_files = 0;</span>
<span style="font-size:18px;">static InputStream *input_streams = NULL;
static int nb_input_streams = 0;
static InputFile *input_files = NULL;
static int nb_input_files = 0;ide
static OutputStream *output_streams = NULL;
static int nb_output_streams = 0;
static OutputFile *output_files = NULL;
static int nb_output_files = 0;</span>
其中:
input_streams 是輸入流的數組,nb_input_streams是輸入流的個數.
InputFile 是輸入文件(也多是設備)的數組,input_files是輸入文件的個數.
下面的輸出相關的變量們就不用解釋了. www.2cto.com函數
能夠看出,文件和流是分別保存的.因而,能夠想象,結構InputStream中應有其所屬的文件在input_files中的序號,結構OutputStream中也應有其所屬文件在output_files中的序號.輸入流數組應是這樣填充的:每當在輸入文件中找到一個流時,就把它添加到input_streams中,因此一個輸入文件對應的流們在input_streams中是緊靠着的,因而InputFile結構中應有其第一個流在input_streams中的開始序號和被放在input_streams中的流的個數,由於並非一個輸入文件中全部的流都會被轉到輸出文件中.咱們看一下InputFile:
<span style="font-size:18px;">typedef struct InputFile {
AVFormatContext *ctx;
int eof_reached; /* true if eof reached */
int ist_index; /* index of first stream in input_streams */
int buffer_size; /* current total buffer size */
int64_t ts_offset;
int nb_streams; /* number of stream that ffmpeg is aware of; may be different
from ctx.nb_streams if new streams appear during av_read_frame() */
int rate_emu;
} InputFile;</span>
<span style="font-size:18px;">typedef struct InputFile {
AVFormatContext *ctx;
int eof_reached; /* true if eof reached */
int ist_index; /* index of first stream in input_streams */
int buffer_size; /* current total buffer size */
int64_t ts_offset;
int nb_streams; /* number of stream that ffmpeg is aware of; may be different
from ctx.nb_streams if new streams appear during av_read_frame() */
int rate_emu;
} InputFile;</span>
注意其中的ist_index和nb_streams。源碼分析
在輸出流中,除了要保存其所在的輸出文件在output_files中的序號,還應保存其對應的輸入流在input_streams中的序號,也應保存其在所屬輸出文件中的流序號.而輸出文件中呢,只需保存它的第一個流在output_streams中的序號,可是爲啥不保存輸出文件中流的個數呢?我也不知道,但我知道確定不用保存也不影響實現功能(嘿嘿,至關於沒說).
各位看官看到這裏應該明白ffmpeg是怎樣作到能夠把多個文件中的任意個流從新組和到輸出文件中了吧?ui
流和文件都準備好了,下面就是轉換,那麼轉換過程是怎樣的呢?仍是我來猜一猜吧:
首先打開輸入文件們,而後跟據輸入流們準備並打開解碼器們,而後跟據輸出流們準備並打開編碼器們,而後建立輸出文件們,而後爲全部輸出文件們寫好頭部,而後就在循環中把輸入流轉換到輸出流並寫入輸出文件中,轉換完後跳出循環,而後寫入文件尾,最後關閉全部的輸出文件.編碼
概述就先到這裏吧,後面會對幾個重要函數作詳細分析
仍是先看一下主函數吧:(省略了不少無關大雅的代碼)
[cpp] view plaincopy
- 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()函數,轉換就發生在它裏面.不廢話,看註釋吧,應很詳細了
[cpp] view plaincopy
- 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{
- <span> </span>.................................
- }
-
- /* 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;
- }
ffmpeg源碼分析三
transcode_init()函數是在轉換前作準備工做的.其大致要完成的任務在第一篇中已作了猜想.此處看一下它的真面目,不廢話,看註釋吧:
[cpp] view plaincopy
- //爲轉換過程作準備
- static int transcode_init(OutputFile *output_files,
- int nb_output_files,
- InputFile *input_files,
- int nb_input_files)
- {
- int ret = 0, i, j, k;
- AVFormatContext *oc;
- AVCodecContext *codec, *icodec;
- OutputStream *ost;
- InputStream *ist;
- char error[1024];
- int want_sdp = 1;
-
- /* init framerate emulation */
- //初始化幀率仿真(轉換時是不按幀率來的,但若是要求幀率仿真,就能夠作到)
- for (i = 0; i < nb_input_files; i++)
- {
- InputFile *ifile = &input_files[i];
- //若是一個輸入文件被要求幀率仿真(指的是即便是轉換也像播放那樣按照幀率來進行),
- //則爲這個文件中全部流記錄下開始時間
- if (ifile->rate_emu)
- for (j = 0; j < ifile->nb_streams; j++)
- input_streams[j + ifile->ist_index].start = av_gettime();
- }
-
- /* output stream init */
- for (i = 0; i < nb_output_files; i++)
- {
- //什麼也沒作,只是作了個判斷而已
- oc = output_files[i].ctx;
- if (!oc->nb_streams && !(oc->oformat->flags & AVFMT_NOSTREAMS))
- {
- av_dump_format(oc, i, oc->filename, 1);
- av_log(NULL, AV_LOG_ERROR,
- "Output file #%d does not contain any stream\n", i);
- return AVERROR(EINVAL);
- }
- }
-
- //輪循全部的輸出流,跟據對應的輸入流,設置其編解碼器的參數
- for (i = 0; i < nb_output_streams; i++)
- {
- //輪循全部的輸出流
- ost = &output_streams[i];
- //輸出流對應的FormatContext
- oc = output_files[ost->file_index].ctx;
- //取得輸出流對應的輸入流
- ist = &input_streams[ost->source_index];
-
- //attachment_filename是否是這樣的東西:一個文件,它單獨容納一個輸出流?此處不懂
- if (ost->attachment_filename)
- continue;
-
- codec = ost->st->codec;//輸出流的編解碼器結構
- icodec = ist->st->codec;//輸入流的編解碼器結構
-
- //先把能複製的複製一下
- ost->st->disposition = ist->st->disposition;
- codec->bits_per_raw_sample = icodec->bits_per_raw_sample;
- codec->chroma_sample_location = icodec->chroma_sample_location;
-
- //若是隻是複製一個流(不用解碼後再編碼),則把輸入流的編碼參數直接複製給輸出流
- //此時是不須要解碼也不須要編碼的,因此不需打開解碼器和編碼器
- if (ost->stream_copy)
- {
- //計算輸出流的編解碼器的extradata的大小,而後分配容納extradata的緩衝
- //而後把輸入流的編解碼器的extradata複製到輸出流的編解碼器中
- uint64_t extra_size = (uint64_t) icodec->extradata_size
- + FF_INPUT_BUFFER_PADDING_SIZE;
-
- if (extra_size > INT_MAX) {
- return AVERROR(EINVAL);
- }
-
- /* if stream_copy is selected, no need to decode or encode */
- codec->codec_id = icodec->codec_id;
- codec->codec_type = icodec->codec_type;
-
- if (!codec->codec_tag){
- if (!oc->oformat->codec_tag
- ||av_codec_get_id(oc->oformat->codec_tag,icodec->codec_tag) == codec->codec_id
- ||av_codec_get_tag(oc->oformat->codec_tag,icodec->codec_id) <= 0)
- codec->codec_tag = icodec->codec_tag;
- }
-
- codec->bit_rate = icodec->bit_rate;
- codec->rc_max_rate = icodec->rc_max_rate;
- codec->rc_buffer_size = icodec->rc_buffer_size;
- codec->extradata = av_mallocz(extra_size);
- if (!codec->extradata){
- return AVERROR(ENOMEM);
- }
- memcpy(codec->extradata, icodec->extradata, icodec->extradata_size);
- codec->extradata_size = icodec->extradata_size;
-
- //從新鼓搗一下time base(這傢伙就是幀率)
- codec->time_base = ist->st->time_base;
- //若是輸出文件是avi,作一點特殊處理
- if (!strcmp(oc->oformat->name, "avi")) {
- if (copy_tb < 0
- && av_q2d(icodec->time_base) * icodec->ticks_per_frame >
- 2 * av_q2d(ist->st->time_base)
- && av_q2d(ist->st->time_base) < 1.0 / 500
- || copy_tb == 0)
- {
- codec->time_base = icodec->time_base;
- codec->time_base.num *= icodec->ticks_per_frame;
- codec->time_base.den *= 2;
- }
- }
- else if (!(oc->oformat->flags & AVFMT_VARIABLE_FPS))
- {
- if (copy_tb < 0
- && av_q2d(icodec->time_base) * icodec->ticks_per_frame
- > av_q2d(ist->st->time_base)
- && av_q2d(ist->st->time_base) < 1.0 / 500
- || copy_tb == 0)
- {
- codec->time_base = icodec->time_base;
- codec->time_base.num *= icodec->ticks_per_frame;
- }
- }
- //再修正一下幀率
- av_reduce(&codec->time_base.num, &codec->time_base.den,
- codec->time_base.num, codec->time_base.den, INT_MAX);
- //單獨複製各不一樣媒體本身的編碼參數
- switch (codec->codec_type)
- {
- case AVMEDIA_TYPE_AUDIO:
- //音頻的
- if (audio_volume != 256){
- av_log( NULL,AV_LOG_FATAL,
- "-acodec copy and -vol are incompatible (frames are not decoded)\n");
- exit_program(1);
- }
- codec->channel_layout = icodec->channel_layout;
- codec->sample_rate = icodec->sample_rate;
- codec->channels = icodec->channels;
- codec->frame_size = icodec->frame_size;
- codec->audio_service_type = icodec->audio_service_type;
- codec->block_align = icodec->block_align;
- break;
- case AVMEDIA_TYPE_VIDEO:
- //視頻的
- codec->pix_fmt = icodec->pix_fmt;
- codec->width = icodec->width;
- codec->height = icodec->height;
- codec->has_b_frames = icodec->has_b_frames;
- if (!codec->sample_aspect_ratio.num){
- codec->sample_aspect_ratio = ost->st->sample_aspect_ratio =
- ist->st->sample_aspect_ratio.num ?ist->st->sample_aspect_ratio :
- ist->st->codec->sample_aspect_ratio.num ?ist->st->codec->sample_aspect_ratio :(AVRational){0, 1};
- }
- ost->st->avg_frame_rate = ist->st->avg_frame_rate;
- break;
- case AVMEDIA_TYPE_SUBTITLE:
- //字幕的
- codec->width = icodec->width;
- codec->height = icodec->height;
- break;
- case AVMEDIA_TYPE_DATA:
- case AVMEDIA_TYPE_ATTACHMENT:
- //??的
- break;
- default:
- abort();
- }
- }
- else
- {
- //若是不是複製,就麻煩多了
- //獲取編碼器
- if (!ost->enc)
- ost->enc = avcodec_find_encoder(ost->st->codec->codec_id);
- //由於須要轉換,因此既需解碼又需編碼
- ist->decoding_needed = 1;
- ost->encoding_needed = 1;
- switch(codec->codec_type)
- {
- case AVMEDIA_TYPE_AUDIO:
- //鼓搗音頻編碼器的參數,基本上是把一些不合適的參數替換掉
- ost->fifo = av_fifo_alloc(1024);//音頻數據所在的緩衝
- if (!ost->fifo) {
- return AVERROR(ENOMEM);
- }
- //採樣率
- if (!codec->sample_rate)
- codec->sample_rate = icodec->sample_rate;
- choose_sample_rate(ost->st, ost->enc);
- codec->time_base = (AVRational){1, codec->sample_rate};
- //樣點格式
- if (codec->sample_fmt == AV_SAMPLE_FMT_NONE)
- codec->sample_fmt = icodec->sample_fmt;
- choose_sample_fmt(ost->st, ost->enc);
- //聲道
- if (ost->audio_channels_mapped) {
- /* the requested output channel is set to the number of
- * -map_channel only if no -ac are specified */
- if (!codec->channels) {
- codec->channels = ost->audio_channels_mapped;
- codec->channel_layout = av_get_default_channel_layout(codec->channels);
- if (!codec->channel_layout) {
- av_log(NULL, AV_LOG_FATAL, "Unable to find an appropriate channel layout for requested number of channel\n);
- exit_program(1);
- }
- }
- /* fill unused channel mapping with -1 (which means a muted
- * channel in case the number of output channels is bigger
- * than the number of mapped channel) */
- for (j = ost->audio_channels_mapped; j < FF_ARRAY_ELEMS(ost->audio_channels_map); j++)
- <span> </span>ost->audio_channels_map[j] = -1;
- }else if (!codec->channels){
- codec->channels = icodec->channels;
- codec->channel_layout = icodec->channel_layout;
- }
- if (av_get_channel_layout_nb_channels(codec->channel_layout) != codec->channels)
- codec->channel_layout = 0;
- //是否須要重採樣
- ost->audio_resample = codec->sample_rate != icodec->sample_rate || audio_sync_method > 1;
- ost->audio_resample |= codec->sample_fmt != icodec->sample_fmt ||
- codec->channel_layout != icodec->channel_layout;
- icodec->request_channels = codec->channels;
- ost->resample_sample_fmt = icodec->sample_fmt;
- ost->resample_sample_rate = icodec->sample_rate;
- ost->resample_channels = icodec->channels;
- break;
- case AVMEDIA_TYPE_VIDEO:
- //鼓搗視頻編碼器的參數,基本上是把一些不合適的參數替換掉
- if (codec->pix_fmt == PIX_FMT_NONE)
- codec->pix_fmt = icodec->pix_fmt;
- choose_pixel_fmt(ost->st, ost->enc);
- if (ost->st->codec->pix_fmt == PIX_FMT_NONE){
- av_log(NULL, AV_LOG_FATAL, "Video pixel format is unknown, stream cannot be encoded\n");
- exit_program(1);
- }
- //寬高
- if (!codec->width || !codec->height){
- codec->width = icodec->width;
- codec->height = icodec->height;
- }
- //視頻是否須要重採樣
- ost->video_resample = codec->width != icodec->width ||
- codec->height != icodec->height ||
- codec->pix_fmt != icodec->pix_fmt;
- if (ost->video_resample){
- codec->bits_per_raw_sample= frame_bits_per_raw_sample;
- }
- ost->resample_height = icodec->height;
- ost->resample_width = icodec->width;
- ost->resample_pix_fmt = icodec->pix_fmt;
- //計算幀率
- if (!ost->frame_rate.num)
- ost->frame_rate = ist->st->r_frame_rate.num ?
- ist->st->r_frame_rate : (AVRational){25,1};
- if (ost->enc && ost->enc->supported_framerates && !ost->force_fps) {
- int idx = av_find_nearest_q_idx(ost->frame_rate,ost->enc->supported_framerates);
- ost->frame_rate = ost->enc->supported_framerates[idx];
- }
- codec->time_base = (AVRational) {ost->frame_rate.den, ost->frame_rate.num};
- if( av_q2d(codec->time_base) < 0.001 &&
- video_sync_method &&
- (video_sync_method==1 ||
- (video_sync_method<0 && !
- (oc->oformat->flags & AVFMT_VARIABLE_FPS))))
- {
- av_log(oc, AV_LOG_WARNING, "Frame rate very high for a muxer not effciciently supporting it.\n"
- "Please consider specifiying a lower framerate, a different muxer or -vsync 2\n");
- }
- <span> </span>for (j = 0; j < ost->forced_kf_count; j++)
- ost->forced_kf_pts[j] = av_rescale_q(ost->forced_kf_pts[j],
- AV_TIME_BASE_Q, codec->time_base);
- break;
- case AVMEDIA_TYPE_SUBTITLE:
- break;
- default:
- abort();
- break;
- }
- /* two pass mode */
- if (codec->codec_id != CODEC_ID_H264 &&
- (codec->flags & (CODEC_FLAG_PASS1 | CODEC_FLAG_PASS2)))
- {
- char logfilename[1024];
- FILE *f;
- snprintf(logfilename, sizeof(logfilename), "%s-%d.log",
- pass_logfilename_prefix ? pass_logfilename_prefix : DEFAULT_PASS_LOGFILENAME_PREFIX,
- i);
- if (codec->flags & CODEC_FLAG_PASS2){
- char *logbuffer;
- size_t logbuffer_size;
- if (cmdutils_read_file(logfilename, &logbuffer, &logbuffer_size) < 0){
- av_log(NULL, AV_LOG_FATAL,
- "Error reading log file '%s' for pass-2 encoding\n",
- logfilename);
- exit_program(1);
- }
- codec->stats_in = logbuffer;
- }
- if (codec->flags & CODEC_FLAG_PASS1){
- f = fopen(logfilename, "wb");
- if (!f) {
- av_log(NULL, AV_LOG_FATAL, "Cannot write log file '%s' for pass-1 encoding: %s\n",
- logfilename, strerror(errno));
- exit_program(1);
- }
- ost->logfile = f;
- }
- }
- }
- if (codec->codec_type == AVMEDIA_TYPE_VIDEO){
- /* maximum video buffer size is 6-bytes per pixel, plus DPX header size (1664)*/
- //計算編碼輸出緩衝的大小,計算一個最大值
- int size = codec->width * codec->height;
- bit_buffer_size = FFMAX(bit_buffer_size, 7 * size + 10000);
- }
- }
- //分配編碼後數據所在的緩衝
- if (!bit_buffer)
- bit_buffer = av_malloc(bit_buffer_size);
- if (!bit_buffer){
- av_log(NULL, AV_LOG_ERROR,
- "Cannot allocate %d bytes output buffer\n",
- bit_buffer_size);
- return AVERROR(ENOMEM);
- }
- //輪循全部輸出流,打開每一個輸出流的編碼器
- for (i = 0; i < nb_output_streams; i++)
- {
- ost = &output_streams[i];
- if (ost->encoding_needed){
- //固然,只有在須要編碼時纔打開編碼器
- AVCodec *codec = ost->enc;
- AVCodecContext *dec = input_streams[ost->source_index].st->codec;
- if (!codec) {
- snprintf(error, sizeof(error),
- "Encoder (codec %s) not found for output stream #%d:%d",
- avcodec_get_name(ost->st->codec->codec_id),
- ost->file_index, ost->index);
- ret = AVERROR(EINVAL);
- goto dump_format;
- }
- if (dec->subtitle_header){
- ost->st->codec->subtitle_header = av_malloc(dec->subtitle_header_size);
- if (!ost->st->codec->subtitle_header){
- ret = AVERROR(ENOMEM);
- goto dump_format;
- }
- memcpy(ost->st->codec->subtitle_header,
- dec->subtitle_header,dec->subtitle_header_size);
- ost->st->codec->subtitle_header_size = dec->subtitle_header_size;
- }
- //打開啦
- if (avcodec_open2(ost->st->codec, codec, &ost->opts) < 0) {
- snprintf(error, sizeof(error),
- "Error while opening encoder for output stream #%d:%d - maybe incorrect parameters such as bit_rate, rate, width or height",
- ost->file_index, ost->index);
- ret = AVERROR(EINVAL);
- goto dump_format;
- }
- assert_codec_experimental(ost->st->codec, 1);
- assert_avoptions(ost->opts);
- if (ost->st->codec->bit_rate && ost->st->codec->bit_rate < 1000)
- av_log(NULL, AV_LOG_WARNING,
- "The bitrate parameter is set too low."
- " It takes bits/s as argument, not kbits/s\n");
- extra_size += ost->st->codec->extradata_size;
- if (ost->st->codec->me_threshold)
- input_streams[ost->source_index].st->codec->debug |= FF_DEBUG_MV;
- }
- }
- //初始化全部的輸入流(主要作的就是在須要時打開解碼器)
- for (i = 0; i < nb_input_streams; i++)
- if ((ret = init_input_stream(i, output_streams, nb_output_streams,
- error, sizeof(error))) < 0)
- goto dump_format;
- /* discard unused programs */
- for (i = 0; i < nb_input_files; i++){
- InputFile *ifile = &input_files[i];
- for (j = 0; j < ifile->ctx->nb_programs; j++){
- AVProgram *p = ifile->ctx->programs[j];
- int discard = AVDISCARD_ALL;
- for (k = 0; k < p->nb_stream_indexes; k++){
- if (!input_streams[ifile->ist_index + p->stream_index[k]].discard){
- discard = AVDISCARD_DEFAULT;
- break;
- }
- }
- p->discard = discard;
- }
- }
- //打開全部輸出文件,寫入媒體文件頭
- for (i = 0; i < nb_output_files; i++){
- oc = output_files[i].ctx;
- oc->interrupt_callback = int_cb;
- if (avformat_write_header(oc, &output_files[i].opts) < 0){
- snprintf(error, sizeof(error),
- "Could not write header for output file #%d (incorrect codec parameters ?)",
- i);
- ret = AVERROR(EINVAL);
- goto dump_format;
- }
424.// assert_avoptions(output_files[i].opts);
- if (strcmp(oc->oformat->name, "rtp")){
- want_sdp = 0;
- }
- }
- return 0;
431.}