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

关于这点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,这样最终的程序可以小100多KB。

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

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

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

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

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

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

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

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了。

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

MP4V2 录制mp4(h264+aac)视频

标签: mp4v2 aac h264

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

分类:

杂项(5)

目录(?)

[+]

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

MP4FileHandle MP4Create (const char* fileName,uint32_t  flags)
功能:创建MP4文件句柄。
 返回:MP4文件句柄。
 参数:fileName 要录制的MP4文件名;flags 创建文件类型,如果要创建普通文件用默认值0就可以,如要录制大于4G的MP4文件此处要设置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_DURATION,width height 视频的宽高,AVCProfileIndication profile (baseline profile, main profile, etc. see),profile_compat compatible profile,AVCLevelIndication levels,sampleLenFieldSizeMinusOne 设置为3.
注意: AVCProfileIndication,profile_compat, AVCLevelIndication,这三个参数值是在h264流中得到的。

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

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

功能:设置音频解码信息(如果设置错误会导致没有声音)。
 返回:成功返回true,失败返回false。
 参数:hFile 文件句柄,trackId 音频的track id,pConfig 记录解码信息的二进制流,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 id,pBytes为要写的数据流指针,numBytes为数据字节长度,duration为前一视频帧与当前视频帧之间的ticks数,或这是前一段音频数据和当前音频数据之间的ticks。isSyncSample 对视频来说是否为关键帧。
注意:1,duration这个参数是用来实现音视频同步用的,如果设置错了会造成音视频不同步,甚至会出现crash现象(一般出现在调用MP4Close是crash)。 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 id,pSequence和pPict为要写入的序列图像参数集的数据指针,sequenceLen和pictLen为串长度。
注意:当检测到序列参数集或图像参数集更新时要调用MP4AddH264SequenceParameterSet或MP4AddH264PictureParameterSet进行更新。

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
  • 邮箱:zhongchao.ustc@gmail.com
  • 日期: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               // 返回一个FAAC的handle

(

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  // FAAC的handle

);

设定编码器的配置:

int FAACAPI faacEncSetConfiguration

(

faacDecHandle hDecoder,         // 此前得到的FAAC的handle

faacEncConfigurationPtr config  // FAAC编码器的配置

);

2.3 Encode

Prototype:

int faacEncEncode

(

faacEncHandle hEncoder,     // FAAC的handle

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. 根据nInputSamples和nMaxOutputBytes,分别为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 <faac.h>

#include <stdio.h>

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 shaping,TNS)滤波器

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<num)

{

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为编码后的数据buff,maxbytesoutput为调用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. MP4TrackId trackId,
  4. u_int8_t* pBytes,
  5. u_int32_t numBytes,
  6. MP4Duration duration = MP4_INVALID_DURATION,
  7. MP4Duration renderingOffset = 0,
  8. bool isSyncSample = true   //如果是视频,此处是指当前视频帧是否是关键帧,如果是则为1,如果不是则为0
  9. );

查看文本打印

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

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

查看文本打印

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

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

  1. mp4v2再学习 -- H264视频编码成MP4文件

    一.H264视频编码成MP4文件 参看:H264视频编码成MP4文件 参看:mp4v2在VS2010下的编译与在项目中的使用 最近做项目需要将H264文件封装为mp4文件,从网上找到了MP4V2库,下 ...

  2. H264视频编码成MP4文件

    http://blog.csdn.net/firehood_/article/details/8813587 版权声明:本文为博主原创文章,未经博主允许不得转载. 最近需要将H264视频编码成MP4格 ...

  3. 工具---《.264视频 转成 MP4视频》

    <.264视频 转成 MP4视频> 安装了"爱奇艺万能播放器"可以打开.264视频,但是opencv却不能直接读取.264视频,还是需要想办法".264视频 ...

  4. 电脑视频转换成mp4格式,视频格式转换器转换

    怎么把电脑视频转换成mp4格式?使用视频转换器,可以转换来自各种设备的音视频格式,包括相机.手机.视频播放器.电视.平板电脑等.因此,音视频爱好者都可以使用它在各种设备上播放或在社交平台上分享. 主要 ...

  5. b站电脑客户端_如何将B站的flv格式的视频转换成mp4格式

    经常看到B站有精彩的视频片段,于是想把这些视频下载保存到电脑,但是发现没有下载按钮,是不是很悲催.有些时候想从优酷.土豆网这些视频网站下载视频,结果却提示要先下载视频客户端才能继续下载视频,运气差的话 ...

  6. 怎样将wmv格式的视频转换成mp4格式

    WMV视频格式的全称为:Windows Media Video,是微软开发的一系列视频编解码和其相关的视频编码格式的统称,是微软Windows媒体框架的一部分.也是一种有着高压缩率.体积小等优势的视频 ...

  7. mov格式的视频转换成mp4,教你3种快速方法来处理

    怎么把mov格式的视频转换成mp4?有这样疑问的朋友,我相信不在少数.为什么这么说呢?因为现在是短视频时代,人们接触视频的机会非常多,当然也会遇到mov格式.MOV格式:可以用QuickTime,包括 ...

  8. 怎么把mkv视频转换成mp4?

    在视频格式中有一种是可以在网上直接下载的视频格式叫做mkv,按照专业的介绍,mkv是一种多媒体封装视频格式,可以同时容纳视频,音频,图片以及字幕,比起常用的MP4,mkv更高级一些.但是mkv文件体积 ...

  9. 视频转mp4格式转换器如何把其他格式的视频转换成mp4格式 1

    冬天到了,很多人都放弃出去游玩的念头.而选择宅在家里开启冬眠模式.这时很多人无聊时就会选择追剧,追剧是最容易打发时间的娱乐方式之一.最近朋友就遇到一个棘手的问题,他用腾讯视频下载了一个电视,但是导入到 ...

最新文章

  1. python学习之循环语句的九九乘法表
  2. xshell如何登陆堡垒机_Xshell连接有跳板机(堡垒机)的服务器
  3. 开箱即用的微服务框架 Go-zero(进阶篇)
  4. 1042 mysql57_一次处理DB2宕机的实战经历(SQL1042C )
  5. Azure Data Science Virtual Machine Linux的初步体验
  6. DSP之有限字长效应
  7. 用Atlas来实现一个基于AJAX的无刷新Chatroom
  8. golang gin框架全局捕获500和404
  9. Cubic(Custom Ubuntu ISO Creator)创建自定义镜像
  10. vue3.0之-watch全面解析
  11. 学生党无线蓝牙耳机推荐哪个,2022口碑最好的蓝牙耳机推荐
  12. 质性分析软件nvivo的学习(二)
  13. Java final关键字的使用和优点
  14. APP网络测试要点及弱网模拟
  15. oracle 01405 提取的值为null,OCI : ORA-01405: 提取的列值为 NULL
  16. HDU 5963 朋友(找规律)
  17. 一文详解蓝牙模块分类
  18. ie11浏览器不支持java插件_JRE / JDK / Java 9的64位插件与IE 11 64位不兼容
  19. RPC(Remote Procedure Call)
  20. 仓库怎么管?五大最实用管理方法都在这了

热门文章

  1. C++中 list与vector的区别
  2. tensorflow mnist read_data_sets fails
  3. Topcoder SRM 648 (div.2)
  4. Linux SPI框架
  5. Java 注解学习笔记
  6. IE8给你选择的理由
  7. cs模式下,显示网络图片一例
  8. linux 挂载有数据硬盘分区,linux下磁盘分区、挂载知多少
  9. 计算机硬件系统的构成教学设计,2.1 计算机硬件系统教学设计思路
  10. predict函数 R_学习|R语言做机器学习的常用函数总结