FFMPEG1.2 音頻解碼的過程

FFMPEG輸出WAV的audio:ffmpeg -i ~/test_22m.audio.flv -acodec pcm_s16le -ar 8000 -ac 1 -f wav -y ~/test_22m.audio.8000.ffmpeg.wav
FFMPEG輸出WAV的audio:ffmpeg -i ~/test_22m.audio.flv -acodec pcm_s16le -ar 32000 -ac 1 -f wav -y ~/test_22m.audio.32000.ffmpeg.wav
FFMPEG輸出WAV的audio:ffmpeg -i ~/test_22m.audio.flv -acodec pcm_s16le -ar 44100 -ac 1 -f wav -y ~/test_22m.audio.44100.ffmpeg.wav
FFMPEG幾個常常定義的變量:
// 用戶的參數Context
OptionsContext *o
// 自定義輸入文件
InputFile* ifile
//  自定義 輸入流
InputStream  *ist
// 輸入AVFormatContext
AVFormatContext *ic
// 自定義輸出文件
InputFile* ofile
// 輸出自定義流
OutputStream *ost
// 輸出AVFormatContext
AVFormatContext *oc
// 輸入輸出AVStream
AVStream *st
// 音頻編碼AVCodecContext
AVCodecContext *audio_enc
// 視頻編碼AVCodecContext
AVCodecContext *video_enc
// 音頻解碼AVCodecContext
AVCodecContext *audio_dec
// 視頻解碼AVCodecContext
AVCodecContext *video_dec
// 輸入的AVCodecContext
AVCodecContext *icodec
// 輸出的AVCodecContext
AVCodecContext *codec
// 解碼器
AVCodec *dec
// 編碼器
AVCodec *enc
調試如下命令,看ffmpeg對於純音頻如何處理:

調試單聲道輸出:ffmpeg -i ~/test_22m.audio.flv -acodec pcm_s16le -ar 8000 -ac 1 -f wav -y ~/output/ffmpeg.wav
設置輸出文件的斷點:
b open_input_file
b add_input_streams
b avformat_open_input
b avformat_find_stream_info
b av_find_best_stream
b avcodec_find_decoder
b avcodec_open2
b avio_open2
b transcode_init
b transcode_step

設置輸出文件的斷點:html

b open_output_fileapp

b new_audio_streamide

b avcodec_find_encoder_by_name函數

b avcodec_alloc_context3佈局

b avformat_alloc_output_context2ui

avcodec_get_context_defaults3
b avcodec_find_encoderthis

設置filter的斷點:
b configure_filtergraph
b configure_input_audio_filter
b avfilter_get_by_name 
b avfilter_init_filter 
b avfilter_graph_parse
b 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
  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
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
            
            
            
            
            
/*
The MIT License (MIT)
Copyright (c) 2013 winlin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// 主函數
main (){
ffmpeg_parse_options () {
open_input_file () {
// 若指定了輸入格式,使用指定的格式。
av_find_input_format ( o -> format )
// 分配AVFormatContext
ic = avformat_alloc_context ();
// 打開輸入的AVFormatContext
avformat_open_input ( & ic , filename , file_iformat , & o -> g -> format_opts );
// 創建AVFormatStream的基本信息結構
avformat_find_stream_info ( ic , opts );
// 打開和初始化解碼器
add_input_streams () {
// 打開解碼器choose_decoder
// codec_id爲AV_CODEC_ID_AAC
codec = ist -> dec = avcodec_find_decoder ( st -> codec -> codec_id )
// 音頻的channel_layout,若codec中沒有設置,則須要
// 猜想音頻channel_layout,參數爲ist->dec即上面的dec
// guess_input_channel_layout(codec)
// dec->channel_layout值爲3(AV_CH_LAYOUT_STEREO),不用猜想。
dec -> channel_layout = av_get_default_channel_layout ()
// 保存其餘設置到自定義的InputStream *ist
// ist保存在全局變量input_streams中
ist -> resample_sample_fmt = dec -> sample_fmt ; // AV_SAMPLE_FMT_FLTP
ist -> resample_sample_rate = dec -> sample_rate ; // 44100
}
// 打印輸入流的信息
av_dump_format ( ic , nb_input_files , filename , 0 );
}
open_output_file () {
// 打開AVFormatContext
// 參數o->format爲"wav",filename爲"/home/winlin/output/ffmpeg.wav"
avformat_alloc_output_context2 ( & oc , NULL , o -> format , filename );
// idx爲選中的InputStream*的索引,如有多個流,則選擇聲道最多的那個
new_audio_stream ( o , oc , idx ) {
// 參數:source_index=0
new_output_stream ( o , oc , AVMEDIA_TYPE_AUDIO , source_index ) {
st = avformat_new_stream ( oc , NULL );
// 設置st的編碼類型
st -> codec -> codec_type = type ; // AVMEDIA_TYPE_AUDIO
// 打開編碼器,調用choose_encoder
// 指定的acodec爲codec_name = 」pcm_s16le「
// 調用find_codec_or_die找到指定的編碼器
ost -> enc = codec = avcodec_find_encoder_by_name ( name )
// 設置st的編碼器id
ost -> st -> codec -> codec_id = ost -> enc -> id ;
// 初始化AVStream的codec
// 注意,這個把AVStream的codec_id設置爲AV_CODEC_ID_NONE
// 可能AVStream中這個沒有關係吧,ffmpeg後面加了一句:
// st->codec->codec_type = type; // XXX hack, avcodec_get_context_defaults2() sets type to unknown for stream copy
// 實際上如今的avcodec_get_context_defaults3已經設置了這個codec_type,因此沒有關係了。
// 若是有須要,把AVStream的codec_id在這裏設置一下
avcodec_get_context_defaults3 ( st -> codec , ost -> enc );
// 設置OutputStream的其餘參數
av_opt_get_int ( o -> g -> sws_opts , "sws_flags" , 0 , & ost -> sws_flags ); // ost->sws_flags set to 4
av_dict_copy ( & ost -> swr_opts , o -> g -> swr_opts , 0 );
av_dict_copy ( & ost -> resample_opts , o -> g -> resample_opts , 0 );
// 常常看到這個,是在這裏設置的
if ( oc -> oformat -> flags & AVFMT_GLOBALHEADER ) st -> codec -> flags |= CODEC_FLAG_GLOBAL_HEADER ;
}
// 設置編碼器的參數
MATCH_PER_STREAM_OPT ( audio_channels , i , audio_enc -> channels , oc , st ); // audio_enc->channels = 1
MATCH_PER_STREAM_OPT ( sample_fmts , str , sample_fmt , oc , st ); // sample_fmt=0;若sample_fmt不爲0,則設置audio_enc->sample_fmt = av_get_sample_fmt(sample_fmt)
MATCH_PER_STREAM_OPT ( audio_sample_rate , i , audio_enc -> sample_rate , oc , st ); // audio_enc->sample_rate = 8000
MATCH_PER_STREAM_OPT ( filters , str , filters , oc , st ); //filters="anull"
ost -> avfilter = av_strdup ( filters );
}
// 打開輸出文件
// 參數:(s=0x1af6be0, filename=0x7fffffffe84d "/home/winlin/output/ffmpeg.wav", flags=2, int_cb=0x1af7070, options=0x1afd5a8)
// 其中:options[0] is NULL,flags is 2(AVIO_FLAG_WRITE)
avio_open2 ( & oc -> pb , filename , AVIO_FLAG_WRITE , & oc -> interrupt_callback , & output_files [ nb_output_files - 1 ] -> opts )
// 將metedata從輸入ic拷貝到輸出oc
av_dict_copy ( & oc -> metadata , input_files [ 0 ] -> ctx -> metadata , AV_DICT_DONT_OVERWRITE );
// 將metadata從輸入st拷貝到輸出st,此時爲NULL,忽略拷貝
av_dict_copy ( & output_streams [ i ] -> st -> metadata , ist -> st -> metadata , AV_DICT_DONT_OVERWRITE );
}
}
transcode () {
transcode_init () {
// 初始化輸出流的編碼參數
// 全部的AVCodecContext都是從AVStream中取的。
codec -> bits_per_raw_sample = icodec -> bits_per_raw_sample ;
codec -> chroma_sample_location = icodec -> chroma_sample_location ;
// 如下爲非copy時的初始化。
// 檢測編碼器是否初始化
// 可見ost->enc是做爲編碼器的,而不是codec->codec。
if ( ! ost -> enc ) ost -> enc = avcodec_find_encoder ( codec -> codec_id );
// 若ost->filter未初始化,多是沒有自定義的filter,則:
fg = init_simple_filtergraph ( ist , ost ); // 結構初始化
configure_filtergraph ( fg ) {
// 初始化數據
fg -> graph = avfilter_graph_alloc ()
graph -> scale_sws_opts = av_strdup ( args ); args : flags = 0x4
graph -> resample_lavr_opts = av_strdup ( args ); // args: aresample_swr_opts=""
avfilter_graph_parse2 ( fg -> graph , "anull" , & inputs , & outputs )
// 配置輸入的filter,對每一個輸入都要配置
for ( cur = inputs , i = 0 ; cur ; cur = cur -> next , i ++ ) {
configure_input_filter ( fg , fg -> inputs [ i ], cur ) {
configure_input_audio_filter () {
AVFilterContext * first_filter = inputs -> filter_ctx ; // "anull"
AVFilter * filter = avfilter_get_by_name ( "abuffer" );
// name is "graph 0 input from stream 0:0"
// args is "time_base=1/44100:sample_rate=44100:sample_fmt=fltp:channel_layout=0x3"
avfilter_graph_create_filter ( & ifilter -> filter , filter , name , args . str , NULL , fg -> graph )
// link: src="abuffer", dst="anull"
avfilter_link ( ifilter -> filter , 0 , first_filter , pad_idx )
}
}
}
// 刪除inputs, 估計是這個inputs只是用來創建圖的,創建完就沒有用了。
avfilter_inout_free ( & inputs );
// 配置輸出的filter, 對每一個outputs都要配置
for ( cur = outputs , i = 0 ; cur ; cur = cur -> next , i ++ ) {
configure_output_filter ( fg , fg -> outputs [ i ], cur ) {
configure_output_audio_filter () {
AVFilterContext * last_filter = out -> filter_ctx ; // "anull"
int pad_idx = out -> pad_idx ;
// 初始化abuffersink的參數
AVABufferSinkParams * params = av_abuffersink_params_alloc ();
params -> all_channel_counts = 1 ;
// 建立filter,這個filter尚未和"anull"鏈接,可能還須要插入其餘的。
ffabuffersink = avfilter_get_by_name ( "ffabuffersink" )
avfilter_graph_create_filter ( & ofilter -> filter , ffabuffersink , name , NULL , params , fg -> graph );
// 若輸出編碼AVCodecContext指定了聲道,可是沒有指定聲道的佈局,
// 就添加aformatfilter。其中,codec爲ic->streams[i]->codec
if ( codec -> channels && ! codec -> channel_layout ) {
// 初始化聲道的layout
codec -> channel_layout = av_get_default_channel_layout ( codec -> channels );
// 將枚舉變爲字符串,這個函數是經過宏定義的。
// 這個值是從ost->enc->sample_fmts裏取的,由於ost->st->codec->sample_fmt爲AV_SAMPLE_FMT_NONE
// 至關於:sample_fmts = av_get_sample_fmt_name(*ost->enc->sample_fmts)
// 結果是:sample_fmts = "s16"
sample_fmts = choose_sample_fmts ( ost );
// 這個值不是從ost->enc取的,由於已經有設置。
// snprintf(name, sizeof(name), "%d", ost->st->codec->sample_rate);
// 結果是:sample_rates = "8000"
sample_rates = choose_sample_rates ( ost );
// 這個值不是從ost->enc取的,由於已經有設置。
// snprintf(name, sizeof(name), "0x%"PRIx64, ost->st->codec->channel_layout);
channel_layouts = choose_channel_layouts ( ost );
// 若上面的值有設置,則須要加一個format的filter。
// args = "sample_fmts=s16:sample_rates=8000:channel_layouts=0x4:"
// name = "audio format for output stream 0:0"
avfilter_graph_create_filter ( & format , avfilter_get_by_name ( "aformat" ), name , args , NULL , fg -> graph );
// 將aformat鏈接到anull
// 這個是給輸出的filter,因此是通過aformat而後給anull(不作改變,直接輸出)
// link的順序和input的正好相反。可是數據流是同樣的:
// 數據流:dst("aformat") ===> src("anull)
avfilter_link ( last_filter , pad_idx , format , 0 );
// 更新最後的filter爲"aformat"
last_filter = format ;
pad_idx = 0 ;
}
// 將abuffersink鏈接到最後的filter
// 數據流:abuffersink ===> aformat ===> anull
avfilter_link ( last_filter , pad_idx , ofilter -> filter , 0 )
}
}
}
// outputs也沒有用了,刪除
avfilter_inout_free ( & outputs );
// 結束圖配置
avfilter_graph_config ( fg -> graph , NULL )
}
// 設置音頻編碼器
// *ost->filter->filter->filter是:"ffabuffersink"
codec -> sample_fmt = ost -> filter -> filter -> inputs [ 0 ] -> format ;
codec -> sample_rate = ost -> filter -> filter -> inputs [ 0 ] -> sample_rate ;
codec -> channel_layout = ost -> filter -> filter -> inputs [ 0 ] -> channel_layout ;
codec -> channels = avfilter_link_get_channels ( ost -> filter -> filter -> inputs [ 0 ]);
codec -> time_base = ( AVRational ){ 1 , codec -> sample_rate };
// 打開編碼器
// 編碼器使用的是ost->enc,而不是ost->st->codec->codec
AVCodec * codec = ost -> enc ;
// 解碼器使用的是流的解碼器
AVCodecContext × dec = ist -> st -> codec ;
// 設置編碼線程
if ( ! av_dict_get ( ost -> opts , "threads" , NULL , 0 ))
av_dict_set ( & ost -> opts , "threads" , "auto" , 0 );
// 打開編碼器
// 打開後,ost->st->codec->codec就等於ost->enc了。
avcodec_open2 ( ost -> st -> codec , codec , & ost -> opts )
// 設置frame大小
if ( ost -> enc -> type == AVMEDIA_TYPE_AUDIO && ! ( ost -> enc -> capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE ))
av_buffersink_set_frame_size ( ost -> filter -> filter , ost -> st -> codec -> frame_size );
// 檢查音頻碼率,不能小於1k
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 " );
// 額外數據的尺寸,wav爲0
extra_size += ost -> st -> codec -> extradata_size ;
// 打開輸入流
init_input_stream ( i , error , sizeof ( error )) {
// 獲取解碼器
AVCodec * codec = ist -> dec ;
// 設置線程
if ( ! av_dict_get ( ist -> opts , "threads" , NULL , 0 ))
av_dict_set ( & ist -> opts , "threads" , "auto" , 0 );
// 打開解碼器
avcodec_open2 ( ist -> st -> codec , codec , & ist -> opts )
// 設置dts和pts爲AV_NOPTS_VALUE(0x8000000000000000)
ist -> next_pts = AV_NOPTS_VALUE ;
ist -> next_dts = AV_NOPTS_VALUE ;
ist -> is_start = 1 ;
}
// 寫入頭
avformat_write_header ( oc , & output_files [ i ] -> opts )
// 輸出日誌
av_dump_format ( output_files [ i ] -> ctx , i , output_files [ i ] -> ctx -> filename , 1 );
}
while ( ! received_sigterm ) {
transcode_step () {
// 若設置了filter,使用filter獲取輸入。
transcode_from_filter () {
// 獲取輸入的filter,這個filter就是"abuffer":
// *ifilter ->filter ->filter "abuffer"
ifilter = graph -> inputs [ i ];
// 最佳的輸入filter
* best_ist = ist ; // "abuffer"
}
// 讀取packet,解碼,輸出給filter
process_input () {
// process_input處理活動的輸入
// 讀取packet,函數爲:get_input_packet
av_read_frame ( f -> ctx , pkt );
// 有一段糾正pkt時間的函數,或者是計算starttime的,略過。
pkt . dts -= 1ULL << ist -> st -> pts_wrap_bits ;
pkt . pts -= 1ULL << ist -> st -> pts_wrap_bits ;
// 輸出包
output_packet ( ist , & pkt ) {
// 將AVPacket拷貝一份,主要是pkt爲NULL表明EOF
// pkt就是參數的包,原始包。pkt可能爲NULL。
// avpkt通常和pkt等價,永遠不爲NULL。
AVPacket avpkt = * pkt ;
// 開始解碼視頻
while ( ist -> decoding_needed && ( avpkt . size > 0 || ( ! pkt && got_output ))) {
decode_audio () {
// 分配一個解碼的frame,只有當沒有分配(NULL)時才分配
// 此處分配爲(AVFrame *)0x1af8500。
if ( ! ist -> decoded_frame ) {
ist -> decoded_frame = avcodec_alloc_frame ()
}
decoded_frame = ist -> decoded_frame ;
avcodec_decode_audio4 ( avctx , decoded_frame , got_output , pkt );
// 沒有解出來,返回
if ( !* got_output ) return ret ;
// 矯正時間戳
// ist->next_pts change from 0 to 46439
ist -> next_pts += (( int64_t ) AV_TIME_BASE * decoded_frame -> nb_samples ) / avctx -> sample_rate ;
// 略太重新採樣的代碼
if ( resample_changed ) {
}
// 使用decoder的時間戳
if ( decoded_frame -> pkt_pts != AV_NOPTS_VALUE ) {
decoded_frame -> pts = decoded_frame -> pkt_pts ;
pkt -> pts = AV_NOPTS_VALUE ;
decoded_frame_tb = ist -> st -> time_base ;
}
// 輸出到filter: "abuffer"
for ( i = 0 ; i < ist -> nb_filters ; i ++ )
av_buffersrc_add_frame ( ist -> filters [ i ] -> filter , decoded_frame , AV_BUFFERSRC_FLAG_PUSH );
decoded_frame -> pts = AV_NOPTS_VALUE ;
}
// 重置pkt的時間
avpkt . dts = avpkt . pts = AV_NOPTS_VALUE ;
// 檢查已經處理的數據,video直接所有處理完,audio不必定
if ( ist -> st -> codec -> codec_type != AVMEDIA_TYPE_AUDIO )
ret = avpkt . size ;
avpkt . data += ret ;
avpkt . size -= ret ;
}
}
// 釋放包
av_free_packet ( & pkt );
}
// 從filter讀取包,並編碼輸出
reap_filters () {
// 建立frame
if ( ! ost -> filtered_frame ) {
ost -> filtered_frame = avcodec_alloc_frame ();
}
// 讀取已經filter的數據
while ( 1 ) {
ret = av_buffersink_get_buffer_ref ( ost -> filter -> filter , & picref , AV_BUFFERSINK_FLAG_NO_REQUEST );
// 矯正時間戳,略過
frame_pts = AV_NOPTS_VALUE ;
if ( picref -> pts != AV_NOPTS_VALUE ) {
}
// 拷貝到frame
avfilter_copy_buf_props ( filtered_frame , picref );
filtered_frame -> pts = frame_pts ;
// 編碼輸出
do_audio_out () {
// 輸出音頻: do_audio_out(of->ctx, ost, filtered_frame);
AVPacket pkt ;
av_init_packet ( & pkt ); pkt . data = NULL ; pkt . size = 0 ;
avcodec_encode_audio2 ( enc , & pkt , frame , & got_packet )
// 設置時間戳和輸出
if ( got_packet ) {
if ( pkt . pts != AV_NOPTS_VALUE )
pkt . pts = av_rescale_q ( pkt . pts , enc -> time_base , ost -> st -> time_base );
if ( pkt . dts != AV_NOPTS_VALUE )
pkt . dts = av_rescale_q ( pkt . dts , enc -> time_base , ost -> st -> time_base );
if ( pkt . duration > 0 )
pkt . duration = av_rescale_q ( pkt . duration , enc -> time_base , ost -> st -> time_base );
write_frame ( s , & pkt , ost );
av_free_packet ( & pkt );
}
}
// 刪除數據
avfilter_unref_buffer ( picref );
}
}
}
}
}
}
 來自CODE的代碼片
ffmpeg-transcode-logic.cpp
詳細的處理流程:
   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
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
             
             
             
             
             
/*
The MIT License (MIT)
Copyright (c) 2013 winlin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
主要流程是:
main // 主函數
ffmpeg_parse_options // 會調用open_input_file和open_output_file,下面有詳細解釋
transcode // 下面有詳細解釋
打開文件 open_input_file
堆棧以下,解析 - i 參數時調用:
#0 open_input_file (o=0x7fffffffe020, filename=0x7fffffffe7fa "/home/winlin/test_22m.audio.flv") at ffmpeg_opt.c:694
#1 0x000000000040f3d8 in open_files (l=0x1adf058, inout=0xf99cc0 "input", open_file=0x407a4a <open_input_file>) at ffmpeg_opt.c:2307
#2 0x000000000040f574 in ffmpeg_parse_options (argc=15, argv=0x7fffffffe538) at ffmpeg_opt.c:2344
#3 0x000000000041fffd in main (argc=15, argv=0x7fffffffe538) at ffmpeg.c:3322
主要的處理邏輯是:
// 若指定了輸入格式,使用指定的格式。
if ( o -> format ) {
file_iformat = av_find_input_format ( o -> format )
}
// 分配AVFormatContext
ic = avformat_alloc_context ();
// 根據用戶參數設置值,譬如:
// -acodec libfdk_aac -i input.flv
// 會設置解碼參數爲:audio_codec_name=libfdk_aac
MATCH_PER_TYPE_OPT ( codec_names , str , audio_codec_name , ic , "a" );
ic -> audio_codec_id = find_codec_or_die ( audio_codec_name , AVMEDIA_TYPE_AUDIO , 0 ) -> id
// 根據用戶參數設置options,譬如:
// -ar 44100 -i input.flv
if ( o -> nb_audio_sample_rate ) {
// set buf to "44100"
av_dict_set ( & o -> g -> format_opts , "sample_rate" , buf , 0 );
}
// 打開輸入的AVFormatContext
avformat_open_input ( & ic , filename , file_iformat , & o -> g -> format_opts );
// 使用函數choose_decoder強制打開
// 用戶指定的流的解碼器
// i = range(0, ic->nb_streams)
ic -> streams [ i ] -> codec -> codec_id = codec -> id ;
// 創建AVFormatStream的基本信息結構
avformat_find_stream_info ( ic , opts );
// 若指定了起始時間(-ss),則seek
// timestamp = o->start_time;
// timestamp += ic->start_time;
if ( o -> start_time != 0 ) {
avformat_seek_file ( ic , - 1 , INT64_MIN , timestamp , timestamp , 0 );
}
// 打開和初始化解碼器
add_input_streams ( o , ic ) // 下面有詳細解釋
// 打印輸入流的信息
av_dump_format ( ic , nb_input_files , filename , 0 );
// 保存信息到自定義文件中:InputFile* file
// InputFile* file和InputStream* ist經過ist_index對應
// 一個file能夠對應多個ist,即一個文件中能夠有多個流。
file -> ctx = ic ;
file -> ist_index = nb_input_streams - ic -> nb_streams ; // 0
file -> ts_offset = o -> input_ts_offset - ( copy_ts ? 0 : timestamp ); // 0
file -> nb_streams = ic -> nb_streams ; // 1
file -> rate_emu = o -> rate_emu ; // 0
初始化解碼器 add_input_streams
調用堆棧是:
#0 add_input_streams (o=0x7fffffffe020, ic=0x1af50c0) at ffmpeg_opt.c:611
#1 0x00000000004082f8 in open_input_file (o=0x7fffffffe020, filename=0x7fffffffe7fa "/home/winlin/test_22m.audio.flv") at ffmpeg_opt.c:808
#2 0x000000000040f3d8 in open_files (l=0x1adf058, inout=0xf99cc0 "input", open_file=0x407a4a <open_input_file>) at ffmpeg_opt.c:2307
#3 0x000000000040f574 in ffmpeg_parse_options (argc=15, argv=0x7fffffffe538) at ffmpeg_opt.c:2344
#4 0x000000000041fffd in main (argc=15, argv=0x7fffffffe538) at ffmpeg.c:3322
主要的處理邏輯是:
// 循環處理每一個stream,此處只有audio
AVStream * st = ic -> streams [ i ];
// 設置AVStream爲丟棄
st -> discard = AVDISCARD_ALL ;
// 打開解碼器choose_decoder
// codec_id爲AV_CODEC_ID_AAC
AVCodec * codec = ist -> dec = avcodec_find_decoder ( st -> codec -> codec_id )
// 音頻的channel_layout,若codec中沒有設置,則須要
// 猜想音頻channel_layout,參數爲ist->dec即上面的dec
// guess_input_channel_layout(codec)
// dec->channel_layout值爲3(AV_CH_LAYOUT_STEREO),不用猜想。
if ( ! dec -> channel_layout ) {
dec -> channel_layout = av_get_default_channel_layout ()
}
// 保存其餘設置到自定義的InputStream *ist
// ist保存在全局變量input_streams中
ist -> dec = codec ; // decoder AV_CODEC_ID_AAC
ist -> st = st ;
ist -> file_index = nb_input_files ; // 0
ist -> discard = 1 ;
ist -> ts_scale = 1.0 ;
ist -> opts = ...; // NULL
ist -> reinit_filters = - 1 ;
ist -> filter_in_rescale_delta_last = AV_NOPTS_VALUE ; // 0x8000000000000000
ist -> resample_sample_fmt = dec -> sample_fmt ; // AV_SAMPLE_FMT_FLTP
ist -> resample_sample_rate = dec -> sample_rate ; // 44100
ist -> resample_channels = dec -> channels ; // 2
ist -> resample_channel_layout = dec -> channel_layout ; // 3(AV_CH_LAYOUT_STEREO)
打開輸出文件 open_output_file
調用堆棧是:
#1 0x000000000040c0a1 in open_output_file (o=0x7fffffffe020, filename=0x7fffffffe84d "/home/winlin/output/ffmpeg.wav") at ffmpeg_opt.c:1546
#2 0x000000000040f3d8 in open_files (l=0x1adf040, inout=0xf99dab "output", open_file=0x40b8f4 <open_output_file>) at ffmpeg_opt.c:2307
#3 0x000000000040f5ba in ffmpeg_parse_options (argc=15, argv=0x7fffffffe538) at ffmpeg_opt.c:2351
#4 0x000000000041fffd in main (argc=15, argv=0x7fffffffe538) at ffmpeg.c:3322
主要的處理邏輯是:
// 打開AVFormatContext
// 參數o->format爲"wav",filename爲"/home/winlin/output/ffmpeg.wav"
avformat_alloc_output_context2 ( & oc , NULL , o -> format , filename );
// 若沒有指定an(o->audio_disable爲0),而且有解碼器(oc->oformat->audio_codec)
// 則獲取輸入流信息,並添加輸出的AVStream
if ( ! o -> audio_disable && oc -> oformat -> audio_codec != AV_CODEC_ID_NONE ) {
// idx爲選中的InputStream*的索引,如有多個流,則選擇聲道最多的那個
new_audio_stream ( o , oc , idx ); // 下面有詳細解釋
}
// 輸出保存到了全局的OutputStream* output_streams中
ost -> avfilter is "anull"
ost -> st is ( AVStream * ) 0x1af7180
ost -> enc is ( AVCodec * ) 0x1485540
// 建立全局的OutputFile* output_files列表。
output_files [ nb_output_files - 1 ] -> ctx = oc ;
output_files [ nb_output_files - 1 ] -> ost_index = nb_output_streams - oc -> nb_streams ;
output_files [ nb_output_files - 1 ] -> recording_time = o -> recording_time ;
// 打開輸出文件
// 參數:(s=0x1af6be0, filename=0x7fffffffe84d "/home/winlin/output/ffmpeg.wav", flags=2, int_cb=0x1af7070, options=0x1afd5a8)
// 其中:options[0] is NULL,flags is 2(AVIO_FLAG_WRITE)
avio_open2 ( & oc -> pb , filename , AVIO_FLAG_WRITE , & oc -> interrupt_callback , & output_files [ nb_output_files - 1 ] -> opts )
// 設置oc的max_delay,這個應該能夠忽略
oc -> max_delay = ( int )( o -> mux_max_delay * AV_TIME_BASE ); //o->mux_max_delay is 0.699999988
// 將metedata從輸入ic拷貝到輸出oc
av_dict_copy ( & oc -> metadata , input_files [ 0 ] -> ctx -> metadata , AV_DICT_DONT_OVERWRITE );
// 將metadata從輸入st拷貝到輸出st,此時爲NULL,忽略拷貝
av_dict_copy ( & output_streams [ i ] -> st -> metadata , ist -> st -> metadata , AV_DICT_DONT_OVERWRITE );
// 輸出文件打開後,ffmpeg_parse_options就完畢了。
// 接下來就是transcode了。
建立輸出音頻流 new_audio_stream
調用堆棧是:
#0 new_audio_stream (o=0x7fffffffe020, oc=0x1af6a00, source_index=0) at ffmpeg_opt.c:1191
#1 0x000000000040c0a1 in open_output_file (o=0x7fffffffe020, filename=0x7fffffffe84d "/home/winlin/output/ffmpeg.wav") at ffmpeg_opt.c:1546
#2 0x000000000040f3d8 in open_files (l=0x1adf040, inout=0xf99dab "output", open_file=0x40b8f4 <open_output_file>) at ffmpeg_opt.c:2307
#3 0x000000000040f5ba in ffmpeg_parse_options (argc=15, argv=0x7fffffffe538) at ffmpeg_opt.c:2351
#4 0x000000000041fffd in main (argc=15, argv=0x7fffffffe538) at ffmpeg.c:3322
主要處理邏輯是:
/***********************************************************/
/* 調用new_output_stream建立AVStream */
/***********************************************************/
// ost = new_output_stream(o, oc, AVMEDIA_TYPE_AUDIO, source_index);
// 參數:source_index=0
// 基本實現以下:
AVStream * st = avformat_new_stream ( oc , NULL );
// 保存st到自定義的OutputStream* ost,全局變量爲output_streams
output_streams [ nb_output_streams - 1 ] = ost ;
ost -> file_index = nb_output_files ;
ost -> index = idx ;
ost -> st = st ;
// 設置st的編碼類型
st -> codec -> codec_type = type ; // AVMEDIA_TYPE_AUDIO
// 打開編碼器,調用choose_encoder
// 指定的acodec爲codec_name = 」pcm_s16le「
// 調用find_codec_or_die找到指定的編碼器
// codec = find_codec_or_die (name=0x1afd600 "pcm_s16le", type=AVMEDIA_TYPE_AUDIO, encoder=1)
// id=AV_CODEC_ID_FIRST_AUDIO,等價於 codec = avcodec_find_encoder(AV_CODEC_ID_PCM_S16LE);
ost -> enc = codec = avcodec_find_encoder_by_name ( name )
// 設置st的編碼器id
ost -> st -> codec -> codec_id = ost -> enc -> id ;
// 解析編碼參數,譬如指定了-ab 48k,則o->g->codec_opts中有這個設置
// 其餘的-ac和-ar不屬於編碼參數,應該屬於filter,在這個地方沒有
// 此處的o->g->codec_opts和ost->opts均爲NULL
ost -> opts = filter_codec_opts ( o -> g -> codec_opts , ost -> enc -> id , oc , st , ost -> enc );
// 加載和解析preset文件,此處沒有指定,忽略
get_preset_file_2 ( preset , ost -> enc -> name , & s ))
// 初始化AVStream的codec
// 注意,這個把AVStream的codec_id設置爲AV_CODEC_ID_NONE
// 可能AVStream中這個沒有關係吧,ffmpeg後面加了一句:
// st->codec->codec_type = type; // XXX hack, avcodec_get_context_defaults2() sets type to unknown for stream copy
// 實際上如今的avcodec_get_context_defaults3已經設置了這個codec_type,因此沒有關係了。
// 若是有須要,把AVStream的codec_id在這裏設置一下
avcodec_get_context_defaults3 ( st -> codec , ost -> enc );
// 設置OutputStream的其餘參數
ost -> max_frames = INT64_MAX ;
ost -> copy_prior_start = - 1 ;
av_opt_get_int ( o -> g -> sws_opts , "sws_flags" , 0 , & ost -> sws_flags ); // ost->sws_flags set to 4
av_dict_copy ( & ost -> swr_opts , o -> g -> swr_opts , 0 );
av_dict_copy ( & ost -> resample_opts , o -> g -> resample_opts , 0 );
ost -> source_index = source_index ; // 0
// 常常看到這個,是在這裏設置的
if ( oc -> oformat -> flags & AVFMT_GLOBALHEADER )
st -> codec -> flags |= CODEC_FLAG_GLOBAL_HEADER ;
// 設置對應的InputStream的屬性,初始化時是丟棄這個流(AVDISCARD_ALL)
ost -> sync_ist = input_streams [ source_index ];
input_streams [ source_index ] -> discard = 0 ;
input_streams [ source_index ] -> st -> discard = AVDISCARD_NONE ;
/***********************************************************/
/* new_output_stream結束 */
/***********************************************************/
// 獲取建立的參數。
// 彷佛AVCodecContext就是用的AVStream中的,沒有再用avcodec_alloc_context3建立一個。
// 此時auodio_enc中的codec爲NULL,編碼器是放在ost->enc中的。
AVStream * st = ost -> st ;
AVCodecContext * audio_enc = st -> codec ; // audio_enc->codec is NULL, ost->enc->id is AV_CODEC_ID_FIRST_AUDIO
// 設置編碼器的參數
MATCH_PER_STREAM_OPT ( audio_channels , i , audio_enc -> channels , oc , st ); // audio_enc->channels = 1
MATCH_PER_STREAM_OPT ( sample_fmts , str , sample_fmt , oc , st ); // sample_fmt=0;若sample_fmt不爲0,則設置audio_enc->sample_fmt = av_get_sample_fmt(sample_fmt)
MATCH_PER_STREAM_OPT ( audio_sample_rate , i , audio_enc -> sample_rate , oc , st ); // audio_enc->sample_rate = 8000
MATCH_PER_STREAM_OPT ( filters , str , filters , oc , st ); //filters="anull"
ost -> avfilter = av_strdup ( filters );
// 設置聲音通道的映射,此處忽略
if ( o -> nb_audio_channel_maps ) {
ost -> audio_channels_map [ ost -> audio_channels_mapped ++ ] = map -> channel_idx ;
}
轉碼函數 transcode
調用堆棧以下:
#0 transcode () at ffmpeg.c:3138
#1 0x000000000042009a in main (argc=13, argv=0x7fffffffe558) at ffmpeg.c:3344
主要邏輯以下:
// 初始化轉碼
ret = transcode_init (); //下面有詳細解釋
// 若一直沒有退出,執行轉碼
while ( ! received_sigterm ) {
ret = transcode_step (); //下面有詳細解釋
}
轉碼初始化 transcode_init
調用堆棧以下:
#0 transcode_init () at ffmpeg.c:2087
#1 0x000000000041fac0 in transcode () at ffmpeg.c:3138
#2 0x000000000042009a in main (argc=13, argv=0x7fffffffe558) at ffmpeg.c:3344
主要邏輯是:
// 初始化rate_emunate(-re)
if ( ifile -> rate_em ) {
input_streams [ j + ifile -> ist_index ] -> start = av_gettime ();
}
// 初始化輸出流的編碼參數
// 全部的AVCodecContext都是從AVStream中取的。
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 ;
// 當編碼爲copy時的初始化和須要編碼的初始化不同。
if ( ost -> stream_copy ) {
}
// 如下爲非copy時的初始化。
// 檢測編碼器是否初始化
// 可見ost->enc是做爲編碼器的,而不是codec->codec。
if ( ! ost -> enc ) ost -> enc = avcodec_find_encoder ( codec -> codec_id );
ist -> decoding_needed ++ ;
ost -> encoding_needed = 1 ;
// 若ost->filter未初始化,多是沒有自定義的filter,則:
fg = init_simple_filtergraph ( ist , ost ); // 結構初始化
configure_filtergraph ( fg ) // 下面有詳細解釋
// 設置音頻編碼器
// *ost->filter->filter->filter是:"ffabuffersink"
codec -> sample_fmt = ost -> filter -> filter -> inputs [ 0 ] -> format ;
codec -> sample_rate = ost -> filter -> filter -> inputs [ 0 ] -> sample_rate ;
codec -> channel_layout = ost -> filter -> filter -> inputs [ 0 ] -> channel_layout ;
codec -> channels = avfilter_link_get_channels ( ost -> filter -> filter -> inputs [ 0 ]);
codec -> time_base = ( AVRational ){ 1 , codec -> sample_rate };
// 打開編碼器
// 編碼器使用的是ost->enc,而不是ost->st->codec->codec
AVCodec * codec = ost -> enc ;
// 解碼器使用的是流的解碼器
AVCodecContext × dec = ist -> st -> codec ;
// 設置編碼線程
if ( ! av_dict_get ( ost -> opts , "threads" , NULL , 0 ))
av_dict_set ( & ost -> opts , "threads" , "auto" , 0 );
// 打開編碼器
// 打開後,ost->st->codec->codec就等於ost->enc了。
avcodec_open2 ( ost -> st -> codec , codec , & ost -> opts )
// 設置frame大小
if ( ost -> enc -> type == AVMEDIA_TYPE_AUDIO && ! ( ost -> enc -> capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE ))
av_buffersink_set_frame_size ( ost -> filter -> filter , ost -> st -> codec -> frame_size );
// 檢查音頻碼率,不能小於1k
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 " );
// 額外數據的尺寸,wav爲0
extra_size += ost -> st -> codec -> extradata_size ;
// 打開輸入流
init_input_stream ( i , error , sizeof ( error )) // 下面有詳細解釋
// 寫入頭
avformat_write_header ( oc , & output_files [ i ] -> opts )
// 輸出日誌
av_dump_format ( output_files [ i ] -> ctx , i , output_files [ i ] -> ctx -> filename , 1 );
配置 filter configure_filtergraph
調用堆棧以下:
#0 configure_filtergraph (fg=0x1ae6460) at ffmpeg_filter.c:727
#1 0x000000000041bc69 in transcode_init () at ffmpeg.c:2283
#2 0x000000000041fac0 in transcode () at ffmpeg.c:3138
#3 0x000000000042009a in main (argc=13, argv=0x7fffffffe558) at ffmpeg.c:3344
主要邏輯以下:
// 初始化數據
simple = ! fg -> graph_desc // true
const char * graph_desc = ... // "anull"
avfilter_graph_free ( & fg -> graph );
fg -> graph = avfilter_graph_alloc ()
// filter的參數:flags=0x4
snprintf ( args , sizeof ( args ), "flags=0x%X" , ( unsigned ) ost -> sws_flags );
graph -> scale_sws_opts = av_strdup ( args );
// filter的參數:args=""
av_opt_set ( fg -> graph , "aresample_swr_opts" , args , 0 );
graph -> resample_lavr_opts = av_strdup ( args );
// 創建filter圖
avfilter_graph_parse2 ( fg -> graph , graph_desc , & inputs , & outputs )
// 配置輸入的filter,對每一個輸入都要配置
for ( cur = inputs , i = 0 ; cur ; cur = cur -> next , i ++ )
configure_input_filter ( fg , fg -> inputs [ i ], cur ) // 下面有詳細解釋
// 刪除inputs, 估計是這個inputs只是用來創建圖的,創建完就沒有用了。
avfilter_inout_free ( & inputs );
// 如果simple,則直接配置輸出的filter
// 對每一個outputs都要配置
for ( cur = outputs , i = 0 ; cur ; cur = cur -> next , i ++ )
configure_output_filter ( fg , fg -> outputs [ i ], cur ); // 下面有詳細解釋
// outputs也沒有用了,刪除
avfilter_inout_free ( & outputs );
// 結束圖配置
avfilter_graph_config ( fg -> graph , NULL )
配置輸入 filter configure_input_filter
調用堆棧以下:
#0 configure_input_filter (fg=0x1ae6460, ifilter=0x1b216e0, in=0x1b20700) at ffmpeg_filter.c:714
#1 0x0000000000412783 in configure_filtergraph (fg=0x1ae6460) at ffmpeg_filter.c:775
#2 0x000000000041bc69 in transcode_init () at ffmpeg.c:2283
#3 0x000000000041fac0 in transcode () at ffmpeg.c:3138
#4 0x000000000042009a in main (argc=13, argv=0x7fffffffe558) at ffmpeg.c:3344
主要邏輯以下:
// 根據不一樣的類型,初始化不一樣的filter
// 此處是調用configure_input_audio_filter
// 設置第一個filter爲AVFilterInOut×inputs
// 這個inputs就是avfilter_graph_parse2建立的inputs
AVFilterContext * first_filter = inputs -> filter_ctx ;
// 建立音頻[in] filter
AVFilter * filter = avfilter_get_by_name ( "abuffer" );
// 初始化abuffer的參數
av_bprintf ( & args , "time_base=%d/%d:sample_rate=%d:sample_fmt=%s" ,
1 , ist -> st -> codec -> sample_rate ,
ist -> st -> codec -> sample_rate ,
av_get_sample_fmt_name ( ist -> st -> codec -> sample_fmt ));
if ( ist -> st -> codec -> channel_layout )
av_bprintf ( & args , ":channel_layout=0x%" PRIx64 ,
ist -> st -> codec -> channel_layout );
else
av_bprintf ( & args , ":channels=%d" , ist -> st -> codec -> channels );
snprintf ( name , sizeof ( name ), "graph %d input from stream %d:%d" , fg -> index ,
ist -> file_index , ist -> st -> index );
// 添加filter到圖中
// name is "graph 0 input from stream 0:0"
// args is "time_base=1/44100:sample_rate=44100:sample_fmt=fltp:channel_layout=0x3"
avfilter_graph_create_filter ( & ifilter -> filter , filter , name , args . str , NULL , fg -> graph )
// 將filter連接到第一個filter
// 建立一個AVFilterLink,將這兩個filter關聯起來,
// src("abuffer")->outputs = dst("anull")->inputs = link
// 就是說, src("abuffer")的輸出端接的是dst("anull"),dst("anull")的輸入端接的是src("abuffer")
// 數據流:src("abuffer") ==> dst("anull")
// 可見,數據就是從src流向dst。
avfilter_link ( ifilter -> filter , 0 , first_filter , pad_idx )
配置輸出 filter configure_output_filter
調用堆棧以下:
#0 configure_output_filter (fg=0x1ae6460, ofilter=0x1b21660, out=0x1b203a0) at ffmpeg_filter.c:491
#1 0x0000000000412841 in configure_filtergraph (fg=0x1ae6460) at ffmpeg_filter.c:783
#2 0x000000000041bc69 in transcode_init () at ffmpeg.c:2283
#3 0x000000000041fac0 in transcode () at ffmpeg.c:3138
#4 0x000000000042009a in main (argc=13, argv=0x7fffffffe558) at ffmpeg.c:3344
主要邏輯說明:
// 調用的是configure_output_audio_filter
// 指定最後一個filter。和input時的first_filter同樣。
// 實際上,假設使用的是"anull",那麼inputs和outputs的filter_ctx是同樣的,
// 也就是說,avfilter_graph_parse2建立的"anull",對應的inputs->filter_ctx和outputs->filter_ctx是一個對象。
// 在avfilter_graph_parse2返回後打印:
// (gdb) p *inputs {name = 0x0, filter_ctx = 0xf048e0, pad_idx = 0, next = 0x0}
// (gdb) p *outputs {name = 0x0, filter_ctx = 0xf048e0, pad_idx = 0, next = 0x0}
AVFilterContext * last_filter = out -> filter_ctx ;
int pad_idx = out -> pad_idx ;
// 初始化abuffersink的參數
AVABufferSinkParams * params = av_abuffersink_params_alloc ();
params -> all_channel_counts = 1 ;
// 建立filter,這個filter尚未和"anull"鏈接,可能還須要插入其餘的。
ffabuffersink = avfilter_get_by_name ( "ffabuffersink" )
avfilter_graph_create_filter ( & ofilter -> filter , ffabuffersink , name , NULL , params , fg -> graph );
// 清理參數
av_freep ( & params );
// 若輸出編碼AVCodecContext指定了聲道,可是沒有指定聲道的佈局,
// 就添加aformatfilter。其中,codec爲ic->streams[i]->codec
if ( codec -> channels && ! codec -> channel_layout ) {
// 初始化聲道的layout
codec -> channel_layout = av_get_default_channel_layout ( codec -> channels );
/**
* 對應的這幾組值是:
* ost->st->codec->sample_fmt AV_SAMPLE_FMT_NONE
* ost->enc->sample_fmts[0] AV_SAMPLE_FMT_S16
* ost->st->codec->sample_rate 8000
* ost->enc->supported_samplerates NULL
* ost->st->codec->channel_layout 4
* ost->enc->channel_layouts NULL
* 優先選擇st->codec中的值,當爲NONE等時才從enc裏面選(估計裏面是編碼器的默認值)。
*/
// 將枚舉變爲字符串,這個函數是經過宏定義的。
// 這個值是從ost->enc->sample_fmts裏取的,由於ost->st->codec->sample_fmt爲AV_SAMPLE_FMT_NONE
// 至關於:sample_fmts = av_get_sample_fmt_name(*ost->enc->sample_fmts)
// 結果是:sample_fmts = "s16"
sample_fmts = choose_sample_fmts ( ost );
// 這個值不是從ost->enc取的,由於已經有設置。
// snprintf(name, sizeof(name), "%d", ost->st->codec->sample_rate);
// 結果是:sample_rates = "8000"
sample_rates = choose_sample_rates ( ost );
// 這個值不是從ost->enc取的,由於已經有設置。
// snprintf(name, sizeof(name), "0x%"PRIx64, ost->st->codec->channel_layout);
channel_layouts = choose_channel_layouts ( ost );
// 若上面的值有設置,則須要加一個format的filter。
// args = "sample_fmts=s16:sample_rates=8000:channel_layouts=0x4:"
// name = "audio format for output stream 0:0"
avfilter_graph_create_filter ( & format , avfilter_get_by_name ( "aformat" ), name , args , NULL , fg -> graph );
// 鏈接aformat和anull
// src("anull")->outputs = dst("aformat")->inputs = link
// 就是說, src("anull")的輸出端接的是dst("aformat"),dst("aformat")的輸入端接的是src("anull")
// 數據流:dst("anull") ===> src("aformat")
// 可見,數據就是從src流向dst。
avfilter_link ( last_filter , pad_idx , format , 0 );
// 更新最後的filter爲"aformat"
last_filter = format ;
pad_idx = 0 ;
}
// 將abuffersink鏈接到最後的filter
// 數據流:anull ===> aformat ==> abuffersink
avfilter_link ( last_filter , pad_idx , ofilter -> filter , 0 )
打開輸入流 init_input_stream
調用堆棧以下:
#0 init_input_stream (ist_index=0, error=0x7fffffffdf50 "`\337\377\377\377\177", error_len=1024) at ffmpeg.c:1958
#1 0x000000000041cb24 in transcode_init () at ffmpeg.c:2452
#2 0x000000000041fac0 in transcode () at ffmpeg.c:3138
#3 0x000000000042009a in main (argc=13, argv=0x7fffffffe558) at ffmpeg.c:3344
主要邏輯以下:
// 獲取解碼器
AVCodec * codec = ist -> dec ;
// 設置線程
if ( ! av_dict_get ( ist -> opts , "threads" , NULL , 0 ))
av_dict_set ( & ist -> opts , "threads" , "auto" , 0 );
// 打開解碼器
avcodec_open2 ( ist -> st -> codec , codec , & ist -> opts )
// 設置dts和pts爲AV_NOPTS_VALUE(0x8000000000000000)
ist -> next_pts = AV_NOPTS_VALUE ;
ist -> next_dts = AV_NOPTS_VALUE ;
ist -> is_start = 1 ;
轉碼執行 trasncode_step
調用堆棧以下:
#0 transcode_step () at ffmpeg.c:3094
#1 0x000000000041fb63 in transcode () at ffmpeg.c:3167
#2 0x000000000042009a in main (argc=13, argv=0x7fffffffe558) at ffmpeg.c:3344
主要邏輯以下:
// 若設置了filter,使用filter轉碼。
/*******************************************/
// transcode_from_filter start
// transcode_from_filter獲取輸入filter
// 處理filter graph
avfilter_graph_request_oldest ( graph -> graph );
// 獲取輸入的filter,這個filter就是"abuffer":
// *ifilter ->filter ->filter "abuffer"
ifilter = graph -> inputs [ i ];
// 獲取filter的請求,nb_requests is 1
nb_requests = av_buffersrc_get_nb_failed_requests ( ifilter -> filter );
// 最佳的輸入filter
* best_ist = ist ;
// transcode_from_filter end
/*******************************************/
/*******************************************/
// process_input start
// process_input處理活動的輸入
// 讀取packet,函數爲:get_input_packet
av_read_frame ( f -> ctx , pkt );
// 有一段糾正pkt時間的函數,或者是計算starttime的,略過。
pkt . dts -= 1ULL << ist -> st -> pts_wrap_bits ;
pkt . pts -= 1ULL << ist -> st -> pts_wrap_bits ;
// 計算pkt的時間戳,
// ifile->ts_offset is 0
// ist->st->time_base is {num = 1, den = 1000}
if ( pkt . dts != AV_NOPTS_VALUE )
pkt . dts += av_rescale_q ( ifile -> ts_offset , AV_TIME_BASE_Q , ist -> st -> time_base );
if ( pkt . pts != AV_NOPTS_VALUE )
pkt . pts += av_rescale_q ( ifile -> ts_offset , AV_TIME_BASE_Q , ist -> st -> time_base );
// 繼續,計算pkt的時間戳
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_dts != AV_NOPTS_VALUE && ! copy_ts ) {
}
// 輸出包
output_packet ( ist , & pkt ) // 下面有詳細解釋
// 釋放包
av_free_packet ( & pkt );
// process_input end
/*******************************************/
// 從filter讀取包,並編碼輸出
reap_filters (); //下面有詳細解釋
輸出包 output_packet
調用堆棧以下:
#0 output_packet (ist=0x1b227c0, pkt=0x7fffffffe000) at ffmpeg.c:1805
#1 0x000000000041f6a5 in process_input (file_index=0) at ffmpeg.c:3019
#2 0x000000000041fa4c in transcode_step () at ffmpeg.c:3115
#3 0x000000000041fb63 in transcode () at ffmpeg.c:3167
#4 0x000000000042009a in main (argc=13, argv=0x7fffffffe558) at ffmpeg.c:3344
主要邏輯以下:
// 第一次初始化ist的ts
// ist->dts is 0
if ( ! ist -> saw_first_ts ) {
ist -> dts = ist -> st -> avg_frame_rate . num ? - ist -> st -> codec -> has_b_frames * AV_TIME_BASE / av_q2d ( ist -> st -> avg_frame_rate ) : 0 ;
ist -> pts = 0 ;
// 不解碼時的邏輯,copy時矯正時間戳用
if ( pkt != NULL && pkt -> pts != AV_NOPTS_VALUE && ! ist -> decoding_needed ) {
ist -> dts += av_rescale_q ( pkt -> pts , ist -> st -> time_base , AV_TIME_BASE_Q );
ist -> pts = ist -> dts ; //unused but better to set it to a value thats not totally wrong
}
ist -> saw_first_ts = 1 ;
}
// 初始化next_ts
if ( ist -> next_dts == AV_NOPTS_VALUE )
ist -> next_dts = ist -> dts ;
if ( ist -> next_pts == AV_NOPTS_VALUE )
ist -> next_pts = ist -> pts ;
// 將AVPacket拷貝一份,主要是pkt爲NULL表明EOF
// pkt就是參數的包,原始包。pkt可能爲NULL。
// avpkt通常和pkt等價,永遠不爲NULL。
AVPacket avpkt = * pkt ;
// 若包dts有效,用它來矯正ist的時間戳
if ( pkt -> dts != AV_NOPTS_VALUE ) {
ist -> next_dts = ist -> dts = av_rescale_q ( pkt -> dts , ist -> st -> time_base , AV_TIME_BASE_Q );
if ( ist -> st -> codec -> codec_type != AVMEDIA_TYPE_VIDEO || ! ist -> decoding_needed )
ist -> next_pts = ist -> pts = ist -> dts ;
}
// 開始解碼視頻
while ( ist -> decoding_needed && ( avpkt . size > 0 || ( ! pkt && got_output ))) {
/******************************************************************
* decode_audio start
******************************************************************/
// 調用的函數是:decode_audio
// 參數:pkt is &avpkt
// 分配一個解碼的frame,只有當沒有分配(NULL)時才分配
// 此處分配爲(AVFrame *)0x1af8500。
if ( ! ist -> decoded_frame ) {
ist -> decoded_frame = avcodec_alloc_frame ()
}
decoded_frame = ist -> decoded_frame ;
avcodec_decode_audio4 ( avctx , decoded_frame , got_output , pkt );
// 若失敗,
// 或者沒有解出來,並且pkt爲NULL(EOF),
// 給filter加一個NULL幀。
if ( !* got_output || ret < 0 ) {
if ( ! pkt -> size ) {
for ( i = 0 ; i < ist -> nb_filters ; i ++ )
av_buffersrc_add_ref ( ist -> filters [ i ] -> filter , NULL , 0 );
}
}
// 矯正時間戳
// ist->next_pts change from 0 to 46439
ist -> next_pts += (( int64_t ) AV_TIME_BASE * decoded_frame -> nb_samples ) / avctx -> sample_rate ;
// ist->next_dts change from 0 to 46439
ist -> next_dts += (( int64_t ) AV_TIME_BASE * decoded_frame -> nb_samples ) / avctx -> sample_rate ;
// 檢測採樣是否改變
resample_changed = ....;
// 略太重新採樣的代碼
if ( resample_changed ) {
// 加filter從新採樣。
}
// 使用decoder的時間戳
if ( decoded_frame -> pts != AV_NOPTS_VALUE ) {
ist -> dts = ist -> next_dts = ist -> pts = ist -> next_pts = av_rescale_q ( decoded_frame -> pts , avctx -> time_base , AV_TIME_BASE_Q );
decoded_frame_tb = avctx -> time_base ;
} else if ( decoded_frame -> pkt_pts != AV_NOPTS_VALUE ) {
decoded_frame -> pts = decoded_frame -> pkt_pts ;
pkt -> pts = AV_NOPTS_VALUE ;
decoded_frame_tb = ist -> st -> time_base ;
} else if ( pkt -> pts != AV_NOPTS_VALUE ) {
decoded_frame -> pts = pkt -> pts ;
pkt -> pts = AV_NOPTS_VALUE ;
decoded_frame_tb = ist -> st -> time_base ;
} else {
decoded_frame -> pts
相關文章
相關標籤/搜索