Mp4v2實現h264+aac打包成Mp4視頻檔案

來源:互聯網
上載者:User

使用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
所以為(20,8)

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

其中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為視頻的固定的視訊框架的顯示時間,計算的方法為timeScale(90000)* during(這個值是當前視訊框架的採集時間 - 上一幀的視頻採集時間)/1000,公式也可以改為timeScale(90000)/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] = (nalsize&0xff000000)>>24;      buf[1] = (nalsize&0x00ff0000)>>16;      buf[2] = (nalsize&0x0000ff00)>>8;      buf[3] = nalsize&0x000000ff;

如果頭部格式有其他的,則按照上面的方式位移到純資料位元置。

6、mp4v2錄製視頻中aac的格式要求:有時遠程傳過來的aac資料的格式為adts+aac純資料,則需要將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和ppsint 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>是fpsvideo = 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和ppsMP4AddH264SequenceParameterSet(fileHandle, video, sps_pps, 13);MP4AddH264PictureParameterSet(fileHandle, video, sps_pps+13, 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);}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.