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

使用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 <string.h>  
  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] = {0x670x420x400x1F0x96 ,0x540x050x010xED0x000xF30x9E0xA00x680xCE0x380x80}; //存储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, 9000090000/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, 160001024, 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+134);  
  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, 01); //减去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 <string.h>  
  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] = {0x670x420x400x1F0x96 ,0x540x050x010xED0x000xF30x9E0xA00x680xCE0x380x80}; //存储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, 9000090000/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, 160001024, 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+134);  
  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, 01); //减去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. }  

你可能感兴趣的:(音视频,h264,Android)