关于从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即可以调用这些函数了。

 

因此,mp4muxer就是三个关键函数,

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”。这样stackfollowcodeproject等一些网站就出来了。

 

 

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文件的步骤

1av_register_all()注册所有编解码器

2、分配一个AVOutFormat通过fmt = guess_format()

2s/* allocate the output media context */

AVFormatContext *oc =av_alloc_format_context();[1]

oc->oformat =fmt;把猜到的格式信息放到这里。做初步的结构体初始化。

av_set_parameters进一步做结构体初始化

dump_format再进一步做结构体初始化。

3add_audio_stream,add_video_stream

    即是av_new_stream的封装[1为音频流,0为视频流]。

4、写文件头

   编码[包括音,视频],写数据帧

   写文件尾

 

 

归纳需要的几个数据结构

AVFormatContext

AVOutputFormat

AVStream

 

几个重要的函数

从上而下地把它们抽取出来。

大致按照ffmpeg的结构方式去组织

能支持这个编码到mp4的例子了,就是成功了。

avformatcontext是最大的结构,它包含其他的。
它直接包含avoutputformat,avinputformat,avstream这几个结构。
avstream中包含avcodeccontext
avcodeccontext包含avcodecavframe
avframe包含avpicture
avpicture包含avpacket
大概是这样的。不知道对不对。如果有错误,请指正。



 

 

 

AAC encoderlibfaac1.28接口

 

一、FAAC API

Open FAAC engine

faacEncHandlefaacEncOpen         //返回一个FAAChandle

(                  

 unsignedlong  nSampleRate,      //采样率,单位是bps

 unsignedlong  nChannels,        //声道,为单声道,为双声道

 unsignedlong   &nInputSamples,   //传引用,得到每次调用编码时所应接收的原始数据长度

 unsignedlong   &nMaxOutputBytes //传引用,得到每次调用编码时生成的AAC数据的最大长度

);

 

Get/Set encoding configuration

获取编码器的配置:

faacEncConfigurationPtrfaacEncGetCurrentConfiguration//得到指向当前编码器配置的指针

(

 faacEncHandlehEncoder // FAAChandle

 );

 

设定编码器的配置:

intFAACAPIfaacEncSetConfiguration

(

 faacDecHandlehDecoder,        //此前得到的FAAChandle

 faacEncConfigurationPtrconfig // FAAC编码器的配置

 );

 

2.3 Encode

intfaacEncEncode

(

 faacEncHandlehEncoder,    // FAAChandle

 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;根据nInputSamplesnMaxOutputBytes,分别为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;

        }

    }

 

    /*

   while(1)

   {

       // (3) Flushing

       nRet = faacEncEncode(hEncoder, (int*) pbPCMBuffer, 0, pbAACBuffer, nMaxOutputBytes);

        if(nRet <= 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库在编码前,需要缓存34个样本(缓冲区?),continue过即可。
0表示编码器没有输出.:被缓存了.编码器会自己先缓存3帧之后才输出.

 

 


[1]  av_alloc_format_context  是旧接口, 在最新版[2012-9-28版本]中已经成名为avformat_alloc_context 这个接口名了。你新下载的ffmpeg1.0即最新版的,已经改成avformat_alloc_context接口了。av_alloc_format_context 当然找不到。

你可能感兴趣的:(关于从ffmpeg中分离mp4 muxer)