网站建设与准备乐清城市网官网
news/
2025/9/28 8:24:51/
文章来源:
网站建设与准备,乐清城市网官网,网站的风格分析,h网站模板这阵子在捣鼓一个将游戏视频打包成本地可播放文件的模块。开始使用avi作为容器#xff0c;弄了半天无奈avi对aac的支持实在有限#xff0c;在播放时音视频时无法完美同步。 关于这点avi文档中有提到#xff1a; For AAC, one RAW AAC frame usually spans over 1024 samples… 这阵子在捣鼓一个将游戏视频打包成本地可播放文件的模块。开始使用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 录制mp4h264aac视频 标签 mp4v2 aac h264 2015-08-15 19:34 379人阅读 评论(0) 收藏 举报 分类 杂项5 目录(?) [] MP4录制程序是根据mpeg4ip中mpeg4ip-1.5.0.1\server\mp4live\file_mp4_recorder.cpp文件改的。程序支持h264aacraw 流的写入方式用到了动态库mp4v2-2.0.0不要用mpeg4ip中那个较老的版本因为在录制大文件时会有效率问题下面是一些mp4v2接口的简介。 MP4FileHandle MP4Create (const char* fileNameuint32_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数如90000sampleDuration 设置为 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_DURATION和MP4_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数或这是前一段音频数据和当前音频数据之间的ticks。isSyncSample 对视频来说是否为关键帧。 注意1duration这个参数是用来实现音视频同步用的如果设置错了会造成音视频不同步甚至会出现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 idpSequence和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.ustcgmail.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编码器做编码前的准备
调用faacEncOpen开启FAAC编码器后得到了单次输入样本数nInputSamples和输出数据最大字节数nMaxOutputBytes根据nInputSamples和nMaxOutputBytes分别为PCM数据和将要得到的AAC数据创建缓冲区调用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 AudioCoding.com - FAAC 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 hEncoderfaacEncOpen(samplerate,channels, samplesInput, maxBytesOutput); 1.打开aac编码引擎创建aac编码句柄。 参数 samplerate 为要编码的音频pcm流的采样率channels为要编码的音频pcm流的的频道数原有的例子程序是从wav文件中读出这些信息sampleInput在编码时要用到意思是每次要编码的采样数参数maxBytesOutput为编码时输出地最大字节数。 2.然后在设置一些编码参数如 int versionMPEG4; //设置版本,录制MP4文件时要用MPEG4 int objecttypeLOW; //编码类型 int midside1; //M/S编码 int usetnsDEFAULT_TNS; //瞬时噪声定形(temporal noise shapingTNS)滤波器 int shortctlSHORTCTL_NORMAL; int inputformatFAAC_INPUT_16BIT; //输入数据类型 int outputformatRAW_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 i0,j0; unsigned char bufi[8]; while(inum) { memcpy(bufi,inputbufj,sndf-samplebytes); jsndf-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实现h264aac打包成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头格式分析 查看文本打印 前五个字节为 AAC object types LOW 2 接着4个字节为 码率index 16000 8 接着4个字节为 channels 个数 1 应打印出的正确2进制形式为 00010 | 1000 | 0001 | 000 2 8 1 查看文本打印 前五个字节为 AAC object types LOW 2 接着4个字节为 码率index 16000 8 接着4个字节为 channels 个数 1 应打印出的正确2进制形式为 00010 | 1000 | 0001 | 000 2 8 1 所以为208 4、规定时间刻度问题一般网上都是设置90000这个90000是指1秒的tick数其实也就是把1秒中分为90000份在添加音视频的函数中 durationMP4_INVALID_DURATION 查看文本打印 bool MP4WriteSample( MP4FileHandle hFile, MP4TrackId trackId, u_int8_t* pBytes, u_int32_t numBytes, MP4Duration duration MP4_INVALID_DURATION, MP4Duration renderingOffset 0, bool isSyncSample true //如果是视频此处是指当前视频帧是否是关键帧如果是则为1如果不是则为0 ); 查看文本打印 bool MP4WriteSample( MP4FileHandle hFile, MP4TrackId trackId, u_int8_t* pBytes, u_int32_t numBytes, MP4Duration duration MP4_INVALID_DURATION, MP4Duration renderingOffset 0, bool isSyncSample true //如果是视频此处是指当前视频帧是否是关键帧如果是则为1如果不是则为0 ); 其中duration这个参数是指视频帧存在的时间在90000这个刻度上存在的时间如果用户确定录像时的音频视频帧是按固定速率的那么在这里可以设置MP4_INVALID_DURATION这时mp4v2就会使用下面的函数中的时间刻度 视频 查看文本打印 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) sampleDuration为视频的固定的视频帧的显示时间计算的方法为timeScale90000* during(这个值是当前视频帧的采集时间 - 上一帧的视频 采集时间)/1000,公式也可以改为timeScale90000/fps(码率例如20f) 音频 sampleDuration MP4TrackId MP4AddAudioTrack( MP4FileHandle hFile, u_int32_t timeScale, u_int32_t sampleDuration, u_int8_t audioType MP4_MPEG4_AUDIO_TYPE ) sampleDuration 是音频的音频帧在时间刻度上存在的时间这里的timeScale为音频的采样时间例如44100,32000,计算方法给上面的视频一样的。 sampleDuration主要作用是用于音视频同步问题。 5、mp4v2录制视频中h264的格式要求NAL长度NAL数据如果传过来的数据没有0x00000001则是纯数据 (1)h264流中的NAL头四个字节是0x00000001; (2)mp4中的h264track头四个字节要求是NAL的长度并且是大端顺序 (3)mp4v2很可能针对此种情况并没有做处理所以写到mp4文件中的每个NAL头四个字节还是0x00000001. 因此如果传过来的h264数据是纯数据的话则需要如下修改 int nalsize frameSize; buf[0] (nalsize0xff000000)24; buf[1] (nalsize0x00ff0000)16; buf[2] (nalsize0x0000ff00)8; buf[3] nalsize0x000000ff;如果头部格式有其他的则按照上面的方式偏移到纯数据位置。 6、mp4v2录制视频中aac的格式要求有时远程传过来的aac数据的格式为adtsaac纯数据则需要将adts部分去掉即需要偏移7个字节的单位 下面就开始编写如何调用mp4v2库的方法 #include stdio.h #include string.h #include ../mp4v2/mp4v2.h #include AppCameraShooting.h MP4TrackId video; MP4TrackId audio; MP4FileHandle fileHandle; 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 int video_width 640; int video_height 480; //视频录制的调用,实现初始化 JNIEXPORT bool JNICALL Java_com_seuic_jni_AppCameraShooting_mp4init (JNIEnv *env, jclass clz, jstring title, jint type) { const char* local_title (*env)-GetStringUTFChars(env,title, NULL); //创建mp4文件 fileHandle MP4Create(local_title, 0); if(fileHandle MP4_INVALID_FILE_HANDLE) { return false; } memcpy(sps_pps, sps_pps_640, 17); video_width 640; video_height 480; //设置mp4文件的时间单位 MP4SetTimeScale(fileHandle, 90000); //创建视频track //根据ISO/IEC 14496-10 可知sps的第二个第三个第四个字节分别是 AVCProfileIndication,profile_compat,AVCLevelIndication 其中90000/20 中的20是fps video MP4AddH264VideoTrack(fileHandle, 90000, 90000/20, video_width, video_height, sps_pps[1], sps_pps[2], sps_pps[3], 3); if(video MP4_INVALID_TRACK_ID) { MP4Close(fileHandle, 0); return false; } audio MP4AddAudioTrack(fileHandle, 16000, 1024, MP4_MPEG2_AAC_LC_AUDIO_TYPE); if(audio MP4_INVALID_TRACK_ID) { MP4Close(fileHandle, 0); return false; } //设置sps和pps MP4AddH264SequenceParameterSet(fileHandle, video, sps_pps, 13); MP4AddH264PictureParameterSet(fileHandle, video, sps_pps13, 4); MP4SetVideoProfileLevel(fileHandle, 0x7F); MP4SetAudioProfileLevel(fileHandle, 0x02); MP4SetTrackESConfiguration(fileHandle, audio, ubuffer[0], 2); (*env)-ReleaseStringUTFChars(env, title, local_title); return true; } //添加视频帧的方法 JNIEXPORT void JNICALL Java_com_seuic_jni_AppCameraShooting_mp4packVideo (JNIEnv *env, jclass clz, jbyteArray data, jint size, jint keyframe) { unsigned char *buf (unsigned char *)(*env)-GetByteArrayElements(env, data, JNI_FALSE); if(video_type 1){ int nalsize size; buf[0] (nalsize 0xff000000) 24; buf[1] (nalsize 0x00ff0000) 16; buf[2] (nalsize 0x0000ff00) 8; buf[3] nalsize 0x000000ff; MP4WriteSample(fileHandle, video, buf, size, MP4_INVALID_DURATION, 0, keyframe); } (*env)-ReleaseByteArrayElements(env, data, (jbyte *)buf, 0); } //添加音频帧的方法 JNIEXPORT void JNICALL Java_com_seuic_jni_AppCameraShooting_mp4packAudio (JNIEnv *env, jclass clz, jbyteArray data, jint size) { uint8_t *bufaudio (uint8_t *)(*env)-GetByteArrayElements(env, data, JNI_FALSE); MP4WriteSample(fileHandle, audio, bufaudio[7], size-7, MP4_INVALID_DURATION, 0, 1); //减去7为了删除adts头部的7个字节 (*env)-ReleaseByteArrayElements(env, data, (jbyte *)bufaudio, 0); } //视频录制结束调用 JNIEXPORT void JNICALL Java_com_seuic_jni_AppCameraShooting_mp4close (JNIEnv *env, jclass clz) { MP4Close(fileHandle, 0); } 查看文本打印 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) sampleDuration为视频的固定的视频帧的显示时间计算的方法为timeScale90000* during(这个值是当前视频帧的采集时间 - 上一帧的视频 采集时间)/1000,公式也可以改为timeScale90000/fps(码率例如20f) 音频 sampleDuration MP4TrackId MP4AddAudioTrack( MP4FileHandle hFile, u_int32_t timeScale, u_int32_t sampleDuration, u_int8_t audioType MP4_MPEG4_AUDIO_TYPE ) sampleDuration 是音频的音频帧在时间刻度上存在的时间这里的timeScale为音频的采样时间例如44100,32000,计算方法给上面的视频一样的。 sampleDuration主要作用是用于音视频同步问题。 5、mp4v2录制视频中h264的格式要求NAL长度NAL数据如果传过来的数据没有0x00000001则是纯数据 (1)h264流中的NAL头四个字节是0x00000001; (2)mp4中的h264track头四个字节要求是NAL的长度并且是大端顺序 (3)mp4v2很可能针对此种情况并没有做处理所以写到mp4文件中的每个NAL头四个字节还是0x00000001. 因此如果传过来的h264数据是纯数据的话则需要如下修改 int nalsize frameSize; buf[0] (nalsize0xff000000)24; buf[1] (nalsize0x00ff0000)16; buf[2] (nalsize0x0000ff00)8; buf[3] nalsize0x000000ff;如果头部格式有其他的则按照上面的方式偏移到纯数据位置。 6、mp4v2录制视频中aac的格式要求有时远程传过来的aac数据的格式为adtsaac纯数据则需要将adts部分去掉即需要偏移7个字节的单位 下面就开始编写如何调用mp4v2库的方法 #include stdio.h #include string.h #include ../mp4v2/mp4v2.h #include AppCameraShooting.h MP4TrackId video; MP4TrackId audio; MP4FileHandle fileHandle; 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 int video_width 640; int video_height 480; //视频录制的调用,实现初始化 JNIEXPORT bool JNICALL Java_com_seuic_jni_AppCameraShooting_mp4init (JNIEnv *env, jclass clz, jstring title, jint type) { const char* local_title (*env)-GetStringUTFChars(env,title, NULL); //创建mp4文件 fileHandle MP4Create(local_title, 0); if(fileHandle MP4_INVALID_FILE_HANDLE) { return false; } memcpy(sps_pps, sps_pps_640, 17); video_width 640; video_height 480; //设置mp4文件的时间单位 MP4SetTimeScale(fileHandle, 90000); //创建视频track //根据ISO/IEC 14496-10 可知sps的第二个第三个第四个字节分别是 AVCProfileIndication,profile_compat,AVCLevelIndication 其中90000/20 中的20是fps video MP4AddH264VideoTrack(fileHandle, 90000, 90000/20, video_width, video_height, sps_pps[1], sps_pps[2], sps_pps[3], 3); if(video MP4_INVALID_TRACK_ID) { MP4Close(fileHandle, 0); return false; } audio MP4AddAudioTrack(fileHandle, 16000, 1024, MP4_MPEG2_AAC_LC_AUDIO_TYPE); if(audio MP4_INVALID_TRACK_ID) { MP4Close(fileHandle, 0); return false; } //设置sps和pps MP4AddH264SequenceParameterSet(fileHandle, video, sps_pps, 13); MP4AddH264PictureParameterSet(fileHandle, video, sps_pps13, 4); MP4SetVideoProfileLevel(fileHandle, 0x7F); MP4SetAudioProfileLevel(fileHandle, 0x02); MP4SetTrackESConfiguration(fileHandle, audio, ubuffer[0], 2); (*env)-ReleaseStringUTFChars(env, title, local_title); return true; } //添加视频帧的方法 JNIEXPORT void JNICALL Java_com_seuic_jni_AppCameraShooting_mp4packVideo (JNIEnv *env, jclass clz, jbyteArray data, jint size, jint keyframe) { unsigned char *buf (unsigned char *)(*env)-GetByteArrayElements(env, data, JNI_FALSE); if(video_type 1){ int nalsize size; buf[0] (nalsize 0xff000000) 24; buf[1] (nalsize 0x00ff0000) 16; buf[2] (nalsize 0x0000ff00) 8; buf[3] nalsize 0x000000ff; MP4WriteSample(fileHandle, video, buf, size, MP4_INVALID_DURATION, 0, keyframe); } (*env)-ReleaseByteArrayElements(env, data, (jbyte *)buf, 0); } //添加音频帧的方法 JNIEXPORT void JNICALL Java_com_seuic_jni_AppCameraShooting_mp4packAudio (JNIEnv *env, jclass clz, jbyteArray data, jint size) { uint8_t *bufaudio (uint8_t *)(*env)-GetByteArrayElements(env, data, JNI_FALSE); MP4WriteSample(fileHandle, audio, bufaudio[7], size-7, MP4_INVALID_DURATION, 0, 1); //减去7为了删除adts头部的7个字节 (*env)-ReleaseByteArrayElements(env, data, (jbyte *)bufaudio, 0); } //视频录制结束调用 JNIEXPORT void JNICALL Java_com_seuic_jni_AppCameraShooting_mp4close (JNIEnv *env, jclass clz) { MP4Close(fileHandle, 0); }
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/920388.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!