關于從ffmpeg中分離mp4 muxer
找到mp4 muxer源檔案
因為為了統一管理muxer的友善,每個的muxer都封裝進一個資料結構AVOutputFormat。
即用AVOutputFormat這個結構體來管理muxer,到時候把這一系列的結構體放入一個連結清單,要找一個muxer就友善了。
在源代碼中搜AVOutputFormat,即可找到mp4相關的編碼器源檔案。movenc.c/h
以下即為管理mp4 muxer的結構體。
AVOutputFormat ff_mp4_muxer = {
.name = "mp4",
.long_name = NULL_IF_CONFIG_SMALL("MP4 (MPEG-4 Part 14)"),
.mime_type = "application/mp4",
.extensions = "mp4",
.priv_data_size = sizeof(MOVMuxContext),
.audio_codec = AV_CODEC_ID_AAC,
.video_codec = CONFIG_LIBX264_ENCODER ?
AV_CODEC_ID_H264 : AV_CODEC_ID_MPEG4,
.write_header = mov_write_header,
.write_packet = mov_write_packet,
.write_trailer = mov_write_trailer,
.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH,
.codec_tag = (const AVCodecTag* const []){ ff_mp4_obj_type, 0 },
.priv_class = &mp4_muxer_class,
};
為統一接口的調用,這個結構體實作了三個方法
.write_header = mov_write_header,
.write_packet = mov_write_packet,
.write_trailer = mov_write_trailer,
也即每個muxer都要實作的三個函數。友善外部操作的時候,
通過avcodec_find_encoder (codec_id)尋找到muxer即可以調用這些函數了。
是以,mp4的muxer就是三個關鍵函數,
即mov_write_header,
mov_write_packet,
mov_write_trailer,
上層API 接口av_write_frame調用的即是mov_write_packet函數。
上層API 接口av_write_header調用的即是mov_write_ header函數。
上層API 接口av_write_ trailer調用的即是mov_write_ trailer函數。
av_new_stream 此函數實作在哪?
avcodec_alloc_context3
ffmpeg File Index [最全的文檔。源碼都可以在這裡看,結構清晰]
http://cekirdek.pardus.org.tr/~ismail/ffmpeg-docs/files.html
可以迅速掌握整個庫的架構結構。
或Google裡搜ffmpeg AVCODEC可以搜到。看開源庫時,Google盡量搜英文。如“which source file is ffmpeg mp4 muxer”。這樣stackfollow,codeproject等一些網站就出來了。
create_asf()
add_asf_video_stream()
put_asf_video()
add_asf_audio_stream()
put_asf_audio()
end_asf()
close_asf()
get_asf_data()
在ffmpeg中用api寫一個mp4檔案的步驟
1、av_register_all()注冊所有編解碼器
2、配置設定一個AVOutFormat通過fmt = guess_format()
2s、
AVFormatContext *oc =av_alloc_format_context();[1]
oc->oformat =fmt;把猜到的格式資訊放到這裡。做初步的結構體初始化。
av_set_parameters進一步做結構體初始化
dump_format再進一步做結構體初始化。
3、add_audio_stream,add_video_stream向
即是av_new_stream的封裝[1為音頻流,0為視訊流]。
4、寫檔案頭
編碼[包括音,視訊],寫資料幀
寫檔案尾
歸納需要的幾個資料結構
AVFormatContext
AVOutputFormat
AVStream
幾個重要的函數
從上而下地把它們抽取出來。
大緻按照ffmpeg的結構方式去組織
能支援這個編碼到mp4的例子了,就是成功了。
avformatcontext是最大的結構,它包含其他的。
它直接包含avoutputformat,avinputformat,avstream這幾個結構。
avstream中包含avcodeccontext。
avcodeccontext包含avcodec和avframe。
avframe包含avpicture。
avpicture包含avpacket。
大概是這樣的。不知道對不對。如果有錯誤,請指正。
AAC encoder之libfaac1.28接口
一、FAAC API
Open FAAC engine
faacEncHandlefaacEncOpen //傳回一個FAAC的handle
(
unsignedlong nSampleRate, //采樣率,機關是bps
unsignedlong nChannels, //聲道,為單聲道,為雙聲道
unsignedlong &nInputSamples, //傳引用,得到每次調用編碼時所應接收的原始資料長度
unsignedlong &nMaxOutputBytes //傳引用,得到每次調用編碼時生成的AAC資料的最大長度
);
Get/Set encoding configuration
擷取編碼器的配置:
faacEncConfigurationPtrfaacEncGetCurrentConfiguration//得到指向目前編碼器配置的指針
(
faacEncHandlehEncoder // FAAC的handle
);
設定編碼器的配置:
intFAACAPIfaacEncSetConfiguration
(
faacDecHandlehDecoder, //此前得到的FAAC的handle
faacEncConfigurationPtrconfig // FAAC編碼器的配置
);
2.3 Encode
intfaacEncEncode
(
faacEncHandlehEncoder, // FAAC的handle
short *inputBuffer, // PCM原始資料
unsignedintsamplesInput, //調用faacEncOpen時得到的nInputSamples值
unsignedchar *outputBuffer,//至少具有調用faacEncOpen時得到的nMaxOutputBytes位元組長度的緩沖區
unsignedintbufferSize // outputBuffer緩沖區的實際大小
);
Close FAAC engine
voidfaacEncClose
(
faacEncHandlehEncoder //此前得到的FAAC handle
);
二、流程
(一)做什麼準備
采樣率,聲道數(雙聲道還是單聲道?),還有你的PCM的單個樣本是8位的還是16位的?
(二)開啟FAAC編碼器,做編碼前的準備
調用faacEncOpen開啟FAAC編碼器後,得到了單次輸入樣本數nInputSamples和輸出資料最大位元組數nMaxOutputBytes;根據nInputSamples和nMaxOutputBytes,分别為PCM資料和将要得到的AAC資料建立緩沖區;調用faacEncGetCurrentConfiguration擷取目前配置,修改完配置後,調用faacEncSetConfiguration設定新配置。
(三)開始編碼
調用faacEncEncode,該準備的剛才都準備好了,很簡單。
(四)善後
關閉編碼器,另外别忘了釋放緩沖區,如果使用了檔案流,也别忘記了關閉。
三、測試程式
(一)完整代碼
将PCM格式音頻檔案/home/michael/Development/testspace/in.pcm轉至AAC格式檔案/home/michael/Development/testspace/out.aac。
#include<faac.h>
#include<stdio.h>
typedefunsignedlong ULONG;
typedefunsignedintUINT;
typedefunsignedcharBYTE;
typedefchar_TCHAR;
intmain(intargc,_TCHAR*argv[])
{
ULONG nSampleRate = 11025; // 采樣率
UINT nChannels = 1; // 聲道數
UINT nPCMBitSize = 16; // 單樣本位數
ULONG nInputSamples = 0;
ULONG nMaxOutputBytes = 0;
int nRet;
faacEncHandle hEncoder;
faacEncConfigurationPtr pConfiguration;
int nBytesRead;
int nPCMBufferSize;
BYTE* pbPCMBuffer;
BYTE* pbAACBuffer;
FILE* fpIn; // PCM file for input
FILE* fpOut; // AAC file for output
fpIn = fopen("/home/michael/Development/testspace/in.pcm","rb");
fpOut = fopen("/home/michael/Development/testspace/out.aac","wb");
// (1) Open FAAC engine
hEncoder = faacEncOpen(nSampleRate,nChannels, &nInputSamples, &nMaxOutputBytes);
if(hEncoder == NULL)
{
printf("[ERROR] Failed to call faacEncOpen()\n");
return -1;
}
nPCMBufferSize = nInputSamples * nPCMBitSize / 8;
pbPCMBuffer = new BYTE [nPCMBufferSize];
pbAACBuffer = new BYTE [nMaxOutputBytes];
// (2.1) Get current encoding configuration
pConfiguration = faacEncGetCurrentConfiguration(hEncoder);
pConfiguration->inputFormat = FAAC_INPUT_16BIT;
// (2.2) Set encoding configuration
nRet = faacEncSetConfiguration(hEncoder,pConfiguration);
for(int i = 0; 1;i++)
{
//讀入的實際位元組數,最大不會超過nPCMBufferSize,一般隻有讀到檔案尾時才不是這個值
nBytesRead =fread(pbPCMBuffer, 1,nPCMBufferSize,fpIn);
// 輸入樣本數,用實際讀入位元組數計算,一般隻有讀到檔案尾時才不是nPCMBufferSize/(nPCMBitSize/8);
nInputSamples =nBytesRead / (nPCMBitSize / 8);
// (3) Encode
nRet =faacEncEncode(
hEncoder, (int*)pbPCMBuffer,nInputSamples,pbAACBuffer,nMaxOutputBytes);
fwrite(pbAACBuffer, 1,nRet,fpOut);
printf("%d: faacEncEncode returns %d\n",i,nRet);
if(nBytesRead <= 0)
{
break;
}
}
// (4) Close FAAC engine
nRet = faacEncClose(hEncoder);
delete[] pbPCMBuffer;
delete[] pbAACBuffer;
fclose(fpIn);
fclose(fpOut);
//getchar();
return 0;
}
(二)編譯運作
将上述代碼儲存為“pcm2aac.cpp”檔案,然後編譯:
g++ pcm2aac.cpp -o pcm2aac -L/usr/local/lib -lfaac -I/usr/local/include
運作:
./pcm2aac
然後就生成了out.aac檔案了,聽聽看吧!~
注意:
faacEncEncode為什麼總是傳回0?
其實是faac庫在編碼前,需要緩存3、4個樣本(緩沖區?),continue過即可。
為0表示編碼器沒有輸出.即:被緩存了.編碼器會自己先緩存3幀之後才輸出.
[1] av_alloc_format_context 是舊接口, 在最新版[2012-9-28版本]中已經成名為avformat_alloc_context 這個接口名了。你新下載下傳的ffmpeg是1.0即最新版的,已經改成avformat_alloc_context接口了。av_alloc_format_context 當然找不到。