天天看點

關于從ffmpeg中分離mp4 muxer

關于從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 當然找不到。