使用mp4v2将aac音频h264视频数据封装成mp4开发心得

这阵子在捣鼓一个将游戏视频打包成本地可播放文件的模块。开始使用avi作为容器,弄了半天无奈aviaac的支持实在有限,在播放时音视频时无法完美同步。

关于这点avi文档中有提到:

For AAC, one RAW AAC frame usually spans over 1024 samples. However, depending on
the source container (e.g. ADTS), it is theoretically possible that you are not able to extract
packets of equal duration from your source le. In this case, it is highly recommended not
to mux the AAC stream into AVI, but report a fatal error instead.

因此建议大家不要用avi打包aac,如果实在需要avi格式,可以换成mp3


言归正传,下面重点说说mp4打包时遇到的几个问题,希望对后来开发这方面的朋友能有帮助,少走弯路。

首先需要下载编译开源的mp4v2库。这里一般没什么问题,值得一提的是,mp4v2静态库会导出函数符号。如果你想让程序瘦身,可以这么做在windows的工程属性中去掉MP4V2_EXPORTS预定义,添加MP4V2_USE_STATIC_LIB,这样最终的程序可以小100KB

mp4v2vc2008下编译release版会在link时出现link内部错误(我遇到了,不知道其他人是否也遇到),需要在工程中去掉link时优化,再编译即可。


使用mp4v2打包音视频的具体步骤网上已经有很多例子,不再此啰嗦了,就说说需要注意的几点吧。

1、音频aac不需要包含adts头,即在设置faac选项时:

struConfig.outputFormat = 0; /* Bitstream output format (0 = Raw; 1 = ADTS) */

如果你包含了这个头,我测试下来迅雷播放器可以支持,但是百度影音、暴风影音放出来没声音。(ps,我整个开发过程下来迅雷播放器支持度最好,百度和暴风影音在格式设置错误情况下会出现崩溃和无声音现象,绝非广告)

2MP4AddAudioTrack时,注意第三个参数sampleDuration要设置正确。如果每次添加的音频数据样本数相同,可以在这里先设置好。mp4v2建议把刻度设置为采样率,这样第三个参数就是每次送入数据块的样本数。这个数据可以在编码aac时得到,faacEncOpen返回的input样本数如果是2048,那么双通道实际就是1024

3、设置完这些参数后,本以为万事大吉,但是播放器放出来还是没有声音。那就需要用MP4SetTrackESConfiguration设置音频解码信息。音频解码信息怎么来,可以从faacfaacEncGetDecoderSpecificInfo得到,下面是我的代码:

unsigned int CAACCodec::GetDecoderSpecificInfo(unsigned char * & apInfo)
{
    if ( m_hCodec == NULL )
    {
        return 0;
    }

    unsigned long uLen = 0;
    faacEncGetDecoderSpecificInfo(m_hCodec, &apInfo, &uLen);
    return uLen;
}

将返回的信息,再用MP4SetTrackESConfiguration设置到音频track里去就ok了。

这里有个问题还要注意下,解码信息这块内存,是faacmalloc方式分配出来的,所以你不要忘记free它,否则会造成内存泄露(虽然很小,才2字节)





 MP4V2 录制mp4(h264+aac)视频 

标签: mp4v2 aac h264

2015-08-15 19:34 379人阅读 评论(0) 收藏 举报

category_icon.jpg

 分类:

杂项(5 

arrow_triangle _down.jpg


目录(?)

[+]


MP4录制程序是根据mpeg4ipmpeg4ip-1.5.0.1\server\mp4live\file_mp4_recorder.cpp文件改的。程序支持h264+aacraw 流)的写入方式,用到了动态库mp4v2-2.0.0,不要用mpeg4ip中那个较老的版本,因为在录制大文件时会有效率问题,下面是一些mp4v2接口的简介。

 

MP4FileHandle MP4Create (const char* fileNameuint32_t  flags)
功能:创建MP4文件句柄。
 返回:MP4文件句柄。
 参数:fileName 要录制的MP4文件名;flags 创建文件类型,如果要创建普通文件用默认值0就可以,如要录制大于4GMP4文件此处要设置MP4_CREATE_64BIT_DATA

 

bool MP4SetTimeScale( MP4FileHandle hFile, uint32_t value )
功能:设置时间标度。
返回:成功返回true,失败返回false
参数:hFile MP4文件句柄,value 要设置的值(每秒的时钟ticks数)。

 

MP4TrackId MP4AddH264VideoTrack(MP4FileHandle hFile,
                                     uint32_t timeScale,
                                     MP4Duration sampleDuration,
                                     uint16_t width,
                                      uint16_t height,
                                     uint8_t AVCProfileIndication,
                                     uint8_t profile_compat,
                                     uint8_t AVCLevelIndication,
                                     uint8_t sampleLenFieldSizeMinusOne)
功能:添加h264视频track
返回:返回track id号。
参数:hFile MP4文件句柄,timeScale 视频每秒的ticks数(如90000),sampleDuration 设置为 MP4_INVALID_DURATIONwidth height 视频的宽高,AVCProfileIndication profile (baseline profile, main profile, etc. see)profile_compat compatible profileAVCLevelIndication levelssampleLenFieldSizeMinusOne 设置为3.
注意: AVCProfileIndicationprofile_compat, AVCLevelIndication,这三个参数值是在h264流中得到的。

 

MP4TrackId MP4AddAudioTrack(
        MP4FileHandle hFile,
        uint32_t timeScale,
        MP4Duration sampleDuration,
        uint8_t audioType)
 功能:添加音频(aactrack
 返回:返回track id号。
 参数:hFile MP4句柄,timeScale音频每秒的ticks数(如16000),下面两参数设置为MP4_INVALID_DURATIONMP4_MPEG4_AUDIO_TYPE


bool MP4SetTrackESConfiguration(
       MP4FileHandle  hFile,
       MP4TrackId     trackId,
       const uint8_t* pConfig,
       uint32_t       configSize );

 功能:设置音频解码信息(如果设置错误会导致没有声音)。
 
返回:成功返回true,失败返回false
 
参数:hFile 文件句柄,trackId 音频的track idpConfig 记录解码信息的二进制流,configSize 解码串的长度。
 
注意:mpeg4ip 使用faac进行aac音频编码的,在编码时可以调用相应的函数得到二进制串pConfig和长度configSize,但是如果aac不是用faac编码的,这是需要自己填充pConfig,可以参考faac的实现,下面是一个填充结构例子:


 
前五个字节为 AAC object types  LOW     2
接着4个字节为 码率index        16000      8
接着4个字节为 channels 个数                 1
应打印出的正确2进制形式为  00010 | 1000 | 0001 | 000
                                                            2          8        1


bool MP4WriteSample(
    MP4FileHandle  hFile,
    MP4TrackId     trackId,
    const uint8_t* pBytes,
    uint32_t       numBytes,
    MP4Duration    duration DEFAULT(MP4_INVALID_DURATION),
    MP4Duration    renderingOffset DEFAULT(0),
    bool           isSyncSample DEFAULT(true) );

功能:写一帧视频数据或写一段音频数据。
返回:成功返回true,失败返回false
参数:hFile 文件句柄,trackId 音频或视频的track idpBytes为要写的数据流指针,numBytes为数据字节长度,duration为前一视频帧与当前视频帧之间的ticks数,或这是前一段音频数据和当前音频数据之间的ticksisSyncSample 对视频来说是否为关键帧。
注意:1duration这个参数是用来实现音视频同步用的,如果设置错了会造成音视频不同步,甚至会出现crash现象(一般出现在调用MP4Closecrash)。 2,对于视频流MP4WriteSample函数每次调用是录制前一帧数据,用当前帧的时间戳和前一帧的时间戳计算duration值,然后把当前帧保存下来用做下次调用MP4WriteSample时用,写音频数据一样。

 

 

void MP4AddH264SequenceParameterSet(
    MP4FileHandle  hFile,
    MP4TrackId     trackId,
    const uint8_t* pSequence,
    uint16_t       sequenceLen );

void MP4AddH264PictureParameterSet(
    MP4FileHandle  hFile,
    MP4TrackId     trackId,
    const uint8_t* pPict,
    uint16_t       pictLen );

功能:添加序列参数集,添加图像参数集。
参数:hFile 文件句柄,trackId 视频track idpSequencepPict为要写入的序列图像参数集的数据指针,sequenceLenpictLen为串长度。
注意:当检测到序列参数集或图像参数集更新时要调用MP4AddH264SequenceParameterSetMP4AddH264PictureParameterSet进行更新。

 

void MP4Close(

    MP4FileHandle hFile,

    uint32_t    flags DEFAULT(0) );

功能:关闭以打开的MP4文件。

参数:hFile 文件句柄,flags 是否允许在关闭MP4文件前做一些额外的优化处理。

注意:在录制较小的MP4文件时可以把flags设置为默认值,如果录制较大的文件最好把flags设置为MP4_CLOSE_DO_NOT_COMPUTE_BITRATE否则调用MP4Close函数会用掉很长的时间。

转载自:http://blog.csdn.net/jwzhangjie/article/details/8782650


+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++



音频编解码·实战篇(1PCM转至AACAAC编码)

  • 作者:柳大·Poechant
  • 博客:blog.csdn.net/poechant
  • 邮箱:[email protected]
  • 日期:April 7th, 2012


这里利用FAAC来实现AAC编码。

1 下载安装 FAAC

这里的安装过程是在 Mac Linux 上实现的,Windows可以类似参考。

wget http://downloads.sourceforge.net/faac/faac-1.28.tar.gz

tar zxvf faac-1.28.tar.gz

cd faac-1.28

./configure

make

sudo make install

如果才用默认的 configure 中的 prefix path,那么安装后的 lib .h 文件分别在/usr/local/lib/usr/local/include,后面编译的时候会用到。

如果编译过程中发现错误:

mpeg4ip.h:126: error: new declaration ‘char* strcasestr(const char*, const char*)’

解决方法:

123行开始修改此文件mpeg4ip.h,到129行结束。 修改前:

#ifdef __cplusplus

extern "C" {

#endif

char *strcasestr(const char *haystack, const char *needle);

#ifdef __cplusplus

}

#endif

修改后:

#ifdef __cplusplus

extern "C++" {

#endif

const char *strcasestr(const char *haystack, const char *needle);

#ifdef __cplusplus

}

#endif

2 FAAC API

2.1 Open FAAC engine

Prototype:

faacEncHandle faacEncOpen               // 返回一个FAAChandle

(                   

    unsigned long   nSampleRate,        // 采样率,单位是bps

    unsigned long   nChannels,          // 声道,1为单声道,2为双声道

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

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

);

2.2 Get/Set encoding configuration

Prototype:

获取编码器的配置:

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

(

    faacEncHandle hEncoder  // FAAChandle

);

设定编码器的配置:

int FAACAPI faacEncSetConfiguration

(

    faacDecHandle hDecoder,         // 此前得到的FAAChandle

    faacEncConfigurationPtr config  // FAAC编码器的配置

);

2.3 Encode

Prototype:

int faacEncEncode

(

    faacEncHandle hEncoder,     // FAAChandle

    short *inputBuffer,         // PCM原始数据

    unsigned int samplesInput,  // 调用faacEncOpen时得到的nInputSamples

    unsigned char *outputBuffer,// 至少具有调用faacEncOpen时得到的nMaxOutputBytes字节长度的缓冲区

    unsigned int bufferSize     // outputBuffer缓冲区的实际大小

);

2.4 Close FAAC engine

Prototype

void faacEncClose

(

    faacEncHandle hEncoder  // 此前得到的FAAC handle

);

3 流程

3.1 做什么准备?

采样率,声道数(双声道还是单声道?),还有你的PCM的单个样本是8位的还是16位的?

3.2 开启FAAC编码器,做编码前的准备

  1. 调用faacEncOpen开启FAAC编码器后,得到了单次输入样本数nInputSamples和输出数据最大字节数nMaxOutputBytes
  2. 根据nInputSamplesnMaxOutputBytes,分别为PCM数据和将要得到的AAC数据创建缓冲区;
  3. 调用faacEncGetCurrentConfiguration获取当前配置,修改完配置后,调用faacEncSetConfiguration设置新配置。

3.3 开始编码

调用faacEncEncode,该准备的刚才都准备好了,很简单。

3.4 善后

关闭编码器,另外别忘了释放缓冲区,如果使用了文件流,也别忘记了关闭。

4 测试程序

4.1 完整代码

PCM格式音频文件/home/michael/Development/testspace/in.pcm转至AAC格式文件/home/michael/Development/testspace/out.aac

#include

#include


typedef unsigned long   ULONG;

typedef unsigned int    UINT;

typedef unsigned char   BYTE;

typedef char            _TCHAR;


int main(int argc, _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;

}

4.2 编译运行

将上述代码保存为“pcm2aac.cpp”文件,然后编译:

g++ pcm2aac.cpp -o pcm2aac -L/usr/local/lib -lfaac -I/usr/local/include

运行:

./pcm2aac

然后就生成了out.aac文件了,听听看吧!~

5 Reference

  1. AudioCoding.com - FAAC
  2. Dogfoot – 재밌는 개발

-

转载请注明来自柳大的CSDN博客:blog.csdn.net/poechant

转载自:http://blog.csdn.net/poechant/article/details/7435054


+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++



  

Faac 编码实时pcm流到aac

分类: 多媒体开发2013-04-10 14:09 2843人阅读 评论(3) 收藏 举报

Faac

我的程序是根据faac 1.28 库中的frontend目录下的faac的例子改的。

下面是程序的运行流程:

首先调用faacEncHandle hEncoder=faacEncOpen(samplerate,channels,& samplesInput,

&maxBytesOutput);

1.打开aac编码引擎,创建aac编码句柄。

参数 samplerate 为要编码的音频pcm流的采样率,channels为要编码的音频pcm流的的频道数(原有的例子程序是从wav文件中读出这些信息),sampleInput在编码时要用到,意思是每次要编码的采样数,参数maxBytesOutput为编码时输出地最大字节数。

 

2.然后在设置一些编码参数,如

int version=MPEG4;    //设置版本,录制MP4文件时要用MPEG4

int objecttype=LOW;    //编码类型

int midside=1;          //M/S编码

int usetns=DEFAULT_TNS;   //瞬时噪声定形(temporal noise shapingTNS)滤波器

int shortctl=SHORTCTL_NORMAL;

int inputformat=FAAC_INPUT_16BIT;  //输入数据类型

int outputformat=RAW_STREAM; //录制MP4文件时,要用raw流。检验编码是否正确时可设

                                                           //置为adts传输流,把aac 流写入.aac文件中,如编码正确

                                                          //用千千静听就可以播放。

其他的参数可根据例子程序设置。

设置完参数后就调用faacEncSetConfiguration(hEncoder, aacFormat)设置编码参数。

3.如编码完的aac流要写入MP4文件时,要调用

faacEncGetDecoderSpecificInfo(hEncoder,&(ASC), &(ASCLength));//得到解码信息

                                                                                                                         //mpeg4ip mp4 录制使用)

此函数支持MPEG4版本,得到的ASC ACSLength 数据在录制MP4(mpegip)文件时用。

 

4.然后就是编码了,每次从实时的pcm音频队列中读出samplesInput* channels*(量化位数/8),

字节数的pcm数据。然后再把得到pcm流转变一下存储位数,我是转化为16位的了,这部分

可以根据例子程序写一个函数,这是我写的一个,

size_t read_int16(AACInfo *sndf, int16_t *outbuf, size_t num, unsigned char *inputbuf)

{

       size_t i=0,j=0;

       unsigned char bufi[8];

       while(i

       {

                memcpy(bufi,inputbuf+j,sndf->samplebytes);

               j+=sndf->samplebytes;

              int16_t s=((int16_t*)bufi)[0];

             outbuf[i]=s;

             i++;

       }

       return i;

}

也可以写一个read_float32(AACInfo *sndf, float *outbuf, size_t num ,unsigned char *inputbuf)

size_t read_int24(AACInfo *sndf, int32_t *outbuf, size_t num, unsigned char *inputbuf)

处理完数据转换后就调用

bytesWritten = faacEncEncode(hEncoder,

                                                        (int *)pcmbuf,

                                                        samplesInput,

                                                        outbuff,

                                                        maxbytesoutput);

进行编码,pcmbuf为转换后的pcm流数据,samplesInput为调用faacEncOpen时得到的输入采样数,outbuff为编码后的数据buffmaxbytesoutput为调用faacEncOpen时得到的最大输出字节数。然后每次从outbuff中得到编码后的aac数据流,放到数据队列就行了,如果还要录制MP4文件,在编码完samplesInput(一帧)个采样数时,打上时间戳(mpegip库用于音视频同步)后再放到输出队列中。如果想测试看编码的aac流是否正确,设置输出格式为ADTS_STREAM,把aac数据写入到.aac文件中,看能否用千千静听播放。

5.释放资源,调用faacEncClose(hEncoder);就行了




Mp4v2实现h264+aac打包成Mp4视频文件

分类: 数据库/ DB2/ 文章

使用mp4v2实现录制mp4视频,需要准备如下信息:

1、获取mp4v2源码并编译成库文件,对于mp4v2的编译可以看前面的文章android 编译mp4v2 2.0.0生成动态库 ;

2、获取h264数据中的sps和pps数据,如果不会的话可以查看前面的文章  点击打开链接;

3、获取音频解码信息,在调用MP4SetTrackESConfiguration使用,具体的获取方式一种通过faac获取,方法faacEncGetDecoderSpecificInfo(hEncoder,&(ASC), &(ASCLength));//得到解码信息另一种查看aac音频源码,并并对照aac的adts头格式分析:


查看文本打印

  1. 前五个字节为 AAC object types  LOW     2  
  2. 接着4个字节为 码率index        16000      8  
  3. 接着4个字节为 channels 个数                 1  
  4. 应打印出的正确2进制形式为  00010 | 1000 | 0001 | 000  
  5.                                                             2          8        1  

查看文本打印

  1. 前五个字节为 AAC object types  LOW     2  
  2. 接着4个字节为 码率index        16000      8  
  3. 接着4个字节为 channels 个数                 1  
  4. 应打印出的正确2进制形式为  00010 | 1000 | 0001 | 000  
  5.                                                             2          8        1  


所以为(20,8)

4、规定时间刻度问题,一般网上都是设置90000,这个90000是指1秒的tick数,其实也就是把1秒中分为90000份,在添加音视频的函数中


durationMP4_INVALID_DURATION

查看文本打印

  1. bool MP4WriteSample(  
  2. MP4FileHandle hFile,  
  3.  
  4. MP4TrackId trackId,  
  5.  
  6. u_int8_t* pBytes,  
  7.  
  8. u_int32_t numBytes,  
  9.  
  10. MP4Duration duration = MP4_INVALID_DURATION,  
  11.  
  12. MP4Duration renderingOffset = 0,  
  13.  
  14. bool isSyncSample = true   //如果是视频,此处是指当前视频帧是否是关键帧,如果是则为1,如果不是则为0  
  15.  
  16. );   

查看文本打印

  1. bool MP4WriteSample(  
  2. MP4FileHandle hFile,  
  3.  
  4. MP4TrackId trackId,  
  5.  
  6. u_int8_t* pBytes,  
  7.  
  8. u_int32_t numBytes,  
  9.  
  10. MP4Duration duration = MP4_INVALID_DURATION,  
  11.  
  12. MP4Duration renderingOffset = 0,  
  13.  
  14. bool isSyncSample = true   //如果是视频,此处是指当前视频帧是否是关键帧,如果是则为1,如果不是则为0  
  15.  
  16. );   

其中duration这个参数是指视频帧存在的时间,在90000这个刻度上存在的时间,如果用户确定录像时的音频视频帧是按固定速率的,那么在这里可以设置MP4_INVALID_DURATION,这时mp4v2就会使用下面的函数中的时间刻度


视频:


查看文本打印

  1. MP4AddH264VideoTrack(MP4FileHandle hFile,  
  2.                                     uint32_t timeScale,  
  3.                                     MP4Duration sampleDuration,  
  4.                                     uint16_t width,  
  5.                                     uint16_t height,  
  6.                                     uint8_t AVCProfileIndication,  
  7.                                     uint8_t profile_compat,  
  8.                                     uint8_t AVCLevelIndication,  
  9.                                     uint8_t sampleLenFieldSizeMinusOne)  
  10. sampleDuration为视频的固定的视频帧的显示时间,计算的方法为timeScale(90000)* during(这个值是当前视频帧的采集时间 - 上一帧的视频  
  11. 采集时间)/1000,公式也可以改为timeScale(90000)/fps(码率例如20f)  
  12.  
  13.  
  14. 音频:  
  15.  
  16.  
  17. sampleDuration  
  18. MP4TrackId MP4AddAudioTrack(  
  19. MP4FileHandle hFile,  
  20.  
  21. u_int32_t timeScale,  
  22.  
  23. u_int32_t sampleDuration,  
  24.  
  25. u_int8_t audioType = MP4_MPEG4_AUDIO_TYPE  
  26.  
  27. )   
  28. sampleDuration 是音频的音频帧在时间刻度上存在的时间,这里的timeScale为音频的采样时间,例如44100,32000,计算方法给上面的视频一样的。  
  29. sampleDuration主要作用是用于音视频同步问题。  
  30.  
  31.  
  32.  
  33.  
  34. 5、mp4v2录制视频中h264的格式要求:NAL长度+NAL数据,如果传过来的数据没有0x00000001则是纯数据  
  35.  
  36.  
  37. (1)h264流中的NAL,头四个字节是0x00000001;  
  38.  
  39. (2)mp4中的h264track,头四个字节要求是NAL的长度,并且是大端顺序;  
  40.  
  41. (3)mp4v2很可能针对此种情况并没有做处理,所以写到mp4文件中的每个NAL头四个字节还是0x00000001.  
  42.  
  43. 因此如果传过来的h264数据是纯数据的话则需要如下修改:  
  44.  
  45.  
  46.   int nalsize = frameSize;  
  47.       buf[0] = (nalsize&0xff000000)>>24;  
  48.       buf[1] = (nalsize&0x00ff0000)>>16;  
  49.       buf[2] = (nalsize&0x0000ff00)>>8;  
  50.       buf[3] = nalsize&0x000000ff;如果头部格式有其他的,则按照上面的方式偏移到纯数据位置。  
  51.  
  52. 6、mp4v2录制视频中aac的格式要求:有时远程传过来的aac数据的格式为adts+aac纯数据,则需要将adts部分去掉,即需要偏移7个字节的单位  
  53.  
  54. 下面就开始编写如何调用mp4v2库的方法:  
  55.  
  56.  
  57. #include  
  58. #include  
  59. #include "../mp4v2/mp4v2.h"  
  60. #include "AppCameraShooting.h"  
  61.  
  62. MP4TrackId video;  
  63. MP4TrackId audio;  
  64. MP4FileHandle fileHandle;  
  65. unsigned char sps_pps_640[17] = {0x67, 0x42, 0x40, 0x1F, 0x96 ,0x54, 0x05, 0x01, 0xED, 0x00, 0xF3, 0x9E, 0xA0, 0x68, 0xCE, 0x38, 0x80}; //存储sps和pps  
  66. int video_width = 640;  
  67. int video_height = 480;  
  68.  
  69. //视频录制的调用,实现初始化  
  70.     JNIEXPORT bool JNICALL Java_com_seuic_jni_AppCameraShooting_mp4init  
  71. (JNIEnv *env, jclass clz, jstring title, jint type)  
  72. {  
  73.     const char* local_title = (*env)->GetStringUTFChars(env,title, NULL);  
  74.     //创建mp4文件  
  75.     fileHandle = MP4Create(local_title, 0);  
  76.     if(fileHandle == MP4_INVALID_FILE_HANDLE)  
  77.     {  
  78.         return false;  
  79.     }  
  80.     memcpy(sps_pps, sps_pps_640, 17);  
  81.     video_width = 640;  
  82.     video_height = 480;  
  83.     //设置mp4文件的时间单位  
  84.     MP4SetTimeScale(fileHandle, 90000);  
  85.     //创建视频track //根据ISO/IEC 14496-10 可知sps的第二个,第三个,第四个字节分别是 AVCProfileIndication,profile_compat,AVCLevelIndication     其中90000/20  中的20>是fps  
  86.     video = MP4AddH264VideoTrack(fileHandle, 90000, 90000/20, video_width, video_height, sps_pps[1], sps_pps[2], sps_pps[3], 3);  
  87.     if(video == MP4_INVALID_TRACK_ID)  
  88.     {  
  89.         MP4Close(fileHandle, 0);  
  90.         return false;  
  91.     }  
  92.     audio = MP4AddAudioTrack(fileHandle, 16000, 1024, MP4_MPEG2_AAC_LC_AUDIO_TYPE);  
  93.     if(audio == MP4_INVALID_TRACK_ID)  
  94.     {  
  95.         MP4Close(fileHandle, 0);  
  96.         return false;  
  97.  
  98.     }  
  99.     //设置sps和pps  
  100.     MP4AddH264SequenceParameterSet(fileHandle, video, sps_pps, 13);  
  101.     MP4AddH264PictureParameterSet(fileHandle, video, sps_pps+13, 4);  
  102.     MP4SetVideoProfileLevel(fileHandle, 0x7F);  
  103.     MP4SetAudioProfileLevel(fileHandle, 0x02);  
  104.     MP4SetTrackESConfiguration(fileHandle, audio, &ubuffer[0], 2);  
  105.     (*env)->ReleaseStringUTFChars(env, title, local_title);  
  106.     return true;  
  107. }  
  108.     //添加视频帧的方法  
  109.     JNIEXPORT void JNICALL Java_com_seuic_jni_AppCameraShooting_mp4packVideo  
  110. (JNIEnv *env, jclass clz, jbyteArray data, jint size, jint keyframe)  
  111. {  
  112.     unsigned char *buf = (unsigned char *)(*env)->GetByteArrayElements(env, data, JNI_FALSE);  
  113.     if(video_type == 1){  
  114.         int nalsize = size;  
  115.         buf[0] = (nalsize & 0xff000000) >> 24;  
  116.         buf[1] = (nalsize & 0x00ff0000) >> 16;  
  117.         buf[2] = (nalsize & 0x0000ff00) >> 8;  
  118.         buf[3] =  nalsize & 0x000000ff;  
  119.         MP4WriteSample(fileHandle, video, buf, size, MP4_INVALID_DURATION, 0, keyframe);  
  120.     }  
  121.     (*env)->ReleaseByteArrayElements(env, data, (jbyte *)buf, 0);  
  122. }  
  123.  
  124.     //添加音频帧的方法  
  125.     JNIEXPORT void JNICALL Java_com_seuic_jni_AppCameraShooting_mp4packAudio  
  126. (JNIEnv *env, jclass clz, jbyteArray data, jint size)  
  127. {  
  128.     uint8_t *bufaudio = (uint8_t *)(*env)->GetByteArrayElements(env, data, JNI_FALSE);  
  129.     MP4WriteSample(fileHandle, audio, &bufaudio[7], size-7, MP4_INVALID_DURATION, 0, 1); //减去7为了删除adts头部的7个字节  
  130.     (*env)->ReleaseByteArrayElements(env, data, (jbyte *)bufaudio, 0);  
  131. }  
  132.  
  133.     //视频录制结束调用  
  134.     JNIEXPORT void JNICALL Java_com_seuic_jni_AppCameraShooting_mp4close  
  135. (JNIEnv *env, jclass clz)  
  136. {  
  137.     MP4Close(fileHandle, 0);  
  138. }  

查看文本打印

  1. MP4AddH264VideoTrack(MP4FileHandle hFile,  
  2.                                     uint32_t timeScale,  
  3.                                     MP4Duration sampleDuration,  
  4.                                     uint16_t width,  
  5.                                     uint16_t height,  
  6.                                     uint8_t AVCProfileIndication,  
  7.                                     uint8_t profile_compat,  
  8.                                     uint8_t AVCLevelIndication,  
  9.                                     uint8_t sampleLenFieldSizeMinusOne)  
  10. sampleDuration为视频的固定的视频帧的显示时间,计算的方法为timeScale(90000)* during(这个值是当前视频帧的采集时间 - 上一帧的视频  
  11. 采集时间)/1000,公式也可以改为timeScale(90000)/fps(码率例如20f)  
  12.  
  13.  
  14. 音频:  
  15.  
  16.  
  17. sampleDuration  
  18. MP4TrackId MP4AddAudioTrack(  
  19. MP4FileHandle hFile,  
  20.  
  21. u_int32_t timeScale,  
  22.  
  23. u_int32_t sampleDuration,  
  24.  
  25. u_int8_t audioType = MP4_MPEG4_AUDIO_TYPE  
  26.  
  27. )   
  28. sampleDuration 是音频的音频帧在时间刻度上存在的时间,这里的timeScale为音频的采样时间,例如44100,32000,计算方法给上面的视频一样的。  
  29. sampleDuration主要作用是用于音视频同步问题。  
  30.  
  31.  
  32.  
  33.  
  34. 5、mp4v2录制视频中h264的格式要求:NAL长度+NAL数据,如果传过来的数据没有0x00000001则是纯数据  
  35.  
  36.  
  37. (1)h264流中的NAL,头四个字节是0x00000001;  
  38.  
  39. (2)mp4中的h264track,头四个字节要求是NAL的长度,并且是大端顺序;  
  40.  
  41. (3)mp4v2很可能针对此种情况并没有做处理,所以写到mp4文件中的每个NAL头四个字节还是0x00000001.  
  42.  
  43. 因此如果传过来的h264数据是纯数据的话则需要如下修改:  
  44.  
  45.  
  46.   int nalsize = frameSize;  
  47.       buf[0] = (nalsize&0xff000000)>>24;  
  48.       buf[1] = (nalsize&0x00ff0000)>>16;  
  49.       buf[2] = (nalsize&0x0000ff00)>>8;  
  50.       buf[3] = nalsize&0x000000ff;如果头部格式有其他的,则按照上面的方式偏移到纯数据位置。  
  51.  
  52. 6、mp4v2录制视频中aac的格式要求:有时远程传过来的aac数据的格式为adts+aac纯数据,则需要将adts部分去掉,即需要偏移7个字节的单位  
  53.  
  54. 下面就开始编写如何调用mp4v2库的方法:  
  55.  
  56.  
  57. #include  
  58. #include  
  59. #include "../mp4v2/mp4v2.h"  
  60. #include "AppCameraShooting.h"  
  61.  
  62. MP4TrackId video;  
  63. MP4TrackId audio;  
  64. MP4FileHandle fileHandle;  
  65. unsigned char sps_pps_640[17] = {0x67, 0x42, 0x40, 0x1F, 0x96 ,0x54, 0x05, 0x01, 0xED, 0x00, 0xF3, 0x9E, 0xA0, 0x68, 0xCE, 0x38, 0x80}; //存储sps和pps  
  66. int video_width = 640;  
  67. int video_height = 480;  
  68.  
  69. //视频录制的调用,实现初始化  
  70.     JNIEXPORT bool JNICALL Java_com_seuic_jni_AppCameraShooting_mp4init  
  71. (JNIEnv *env, jclass clz, jstring title, jint type)  
  72. {  
  73.     const char* local_title = (*env)->GetStringUTFChars(env,title, NULL);  
  74.     //创建mp4文件  
  75.     fileHandle = MP4Create(local_title, 0);  
  76.     if(fileHandle == MP4_INVALID_FILE_HANDLE)  
  77.     {  
  78.         return false;  
  79.     }  
  80.     memcpy(sps_pps, sps_pps_640, 17);  
  81.     video_width = 640;  
  82.     video_height = 480;  
  83.     //设置mp4文件的时间单位  
  84.     MP4SetTimeScale(fileHandle, 90000);  
  85.     //创建视频track //根据ISO/IEC 14496-10 可知sps的第二个,第三个,第四个字节分别是 AVCProfileIndication,profile_compat,AVCLevelIndication     其中90000/20  中的20>是fps  
  86.     video = MP4AddH264VideoTrack(fileHandle, 90000, 90000/20, video_width, video_height, sps_pps[1], sps_pps[2], sps_pps[3], 3);  
  87.     if(video == MP4_INVALID_TRACK_ID)  
  88.     {  
  89.         MP4Close(fileHandle, 0);  
  90.         return false;  
  91.     }  
  92.     audio = MP4AddAudioTrack(fileHandle, 16000, 1024, MP4_MPEG2_AAC_LC_AUDIO_TYPE);  
  93.     if(audio == MP4_INVALID_TRACK_ID)  
  94.     {  
  95.         MP4Close(fileHandle, 0);  
  96.         return false;  
  97.  
  98.     }  
  99.     //设置sps和pps  
  100.     MP4AddH264SequenceParameterSet(fileHandle, video, sps_pps, 13);  
  101.     MP4AddH264PictureParameterSet(fileHandle, video, sps_pps+13, 4);  
  102.     MP4SetVideoProfileLevel(fileHandle, 0x7F);  
  103.     MP4SetAudioProfileLevel(fileHandle, 0x02);  
  104.     MP4SetTrackESConfiguration(fileHandle, audio, &ubuffer[0], 2);  
  105.     (*env)->ReleaseStringUTFChars(env, title, local_title);  
  106.     return true;  
  107. }  
  108.     //添加视频帧的方法  
  109.     JNIEXPORT void JNICALL Java_com_seuic_jni_AppCameraShooting_mp4packVideo  
  110. (JNIEnv *env, jclass clz, jbyteArray data, jint size, jint keyframe)  
  111. {  
  112.     unsigned char *buf = (unsigned char *)(*env)->GetByteArrayElements(env, data, JNI_FALSE);  
  113.     if(video_type == 1){  
  114.         int nalsize = size;  
  115.         buf[0] = (nalsize & 0xff000000) >> 24;  
  116.         buf[1] = (nalsize & 0x00ff0000) >> 16;  
  117.         buf[2] = (nalsize & 0x0000ff00) >> 8;  
  118.         buf[3] =  nalsize & 0x000000ff;  
  119.         MP4WriteSample(fileHandle, video, buf, size, MP4_INVALID_DURATION, 0, keyframe);  
  120.     }  
  121.     (*env)->ReleaseByteArrayElements(env, data, (jbyte *)buf, 0);  
  122. }  
  123.  
  124.     //添加音频帧的方法  
  125.     JNIEXPORT void JNICALL Java_com_seuic_jni_AppCameraShooting_mp4packAudio  
  126. (JNIEnv *env, jclass clz, jbyteArray data, jint size)  
  127. {  
  128.     uint8_t *bufaudio = (uint8_t *)(*env)->GetByteArrayElements(env, data, JNI_FALSE);  
  129.     MP4WriteSample(fileHandle, audio, &bufaudio[7], size-7, MP4_INVALID_DURATION, 0, 1); //减去7为了删除adts头部的7个字节  
  130.     (*env)->ReleaseByteArrayElements(env, data, (jbyte *)bufaudio, 0);  
  131. }  
  132.  
  133.     //视频录制结束调用  
  134.     JNIEXPORT void JNICALL Java_com_seuic_jni_AppCameraShooting_mp4close  
  135. (JNIEnv *env, jclass clz)  
  136. {  
  137.     MP4Close(fileHandle, 0);  
  138. }  






你可能感兴趣的:(使用mp4v2将aac音频h264视频数据封装成mp4开发心得)