Android MediaMuxer合成MP4,音視頻同步

H264/AAC實時流 錄製成MP4格式的本地視頻

GITHUB:
https://github.com/chezi008/mp4muxer

建議使用場景

一般視頻流有如下兩種途徑獲取:

  1. Android攝像頭採集
  2. 服務端傳輸過來的視頻流

如果數據由本機攝像頭直接採集,建議使用MediaMuxer類去實現mp4的合成。如果是服務端傳輸過來的視頻流可以使用mp4v2的方法實現mp4的合成。

一、MediaMuxer合成Mp4

####官方文檔介紹:http://www.loverobots.cn/android-api/reference/android/media/MediaMuxer.html

 MediaMuxer muxer = new MediaMuxer("temp.mp4", OutputFormat.MUXER_OUTPUT_MPEG_4);
 // More often, the MediaFormat will be retrieved from MediaCodec.getOutputFormat()
 // or MediaExtractor.getTrackFormat().
 MediaFormat audioFormat = new MediaFormat(...);
 MediaFormat videoFormat = new MediaFormat(...);
 int audioTrackIndex = muxer.addTrack(audioFormat);
 int videoTrackIndex = muxer.addTrack(videoFormat);
 ByteBuffer inputBuffer = ByteBuffer.allocate(bufferSize);
 boolean finished = false;
 BufferInfo bufferInfo = new BufferInfo();

 muxer.start();
 while(!finished) {
   // getInputBuffer() will fill the inputBuffer with one frame of encoded
   // sample from either MediaCodec or MediaExtractor, set isAudioSample to
   // true when the sample is audio data, set up all the fields of bufferInfo,
   // and return true if there are no more samples.
   finished = getInputBuffer(inputBuffer, isAudioSample, bufferInfo);
   if (!finished) {
     int currentTrackIndex = isAudioSample ? audioTrackIndex : videoTrackIndex;
     muxer.writeSampleData(currentTrackIndex, inputBuffer, bufferInfo);
   }
 };
 muxer.stop();
 muxer.release();

MediaMuxer的使用

  1. 初始化:
mMuxer = new MediaMuxer(outPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
  1. 根據自己情況添加track,不要方法添加,此操作必須在mMuxer start方法之前調用:
 mVideoTrackIndex = mMuxer.addTrack(mediaFormat);
 mAudioTrackIndex = mMuxer.addTrack(mediaFormat);
  1. 寫入數據
outputBuffer.position(bufferInfo.offset);
outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
mMuxer.writeSampleData(track, outputBuffer, bufferInfo);
  1. 結束
 mMuxer.stop();
 mMuxer.release();

在結束的時候你可能會遇到幾種異常:

  • E/MPEG4Writer: Missing codec specific data:
    這是因爲在寫入數據的時候沒有寫入編碼參數,h264的編碼參數包含SPS和PPS。所以當你視頻流遇到這些參數幀的時候,請設置好對應的參數。
 //設置sps和pps 如果設置不正確會導致合成的mp4視頻作爲文件預覽的時候,預覽圖片是黑色的
 //視頻進度條拖拽畫面會出現綠色,以及塊狀現象
 mediaformat.setByteBuffer("csd-0", mCSD0);
 mediaformat.setByteBuffer("csd-1", mCSD1);

AAC參數:PCM在用編碼器編碼成AAC格式音頻的時候,編碼器會在自動設置參數。


當outIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED
我們可以獲取到,MediaFormat format = mEnc.getOutputFormat(),
format就包含了CODEC_CONFIG。此時的format可直接作爲addTrack()的參數使用。
  • There are no sync frames for video track
    這是因爲沒有設置關鍵幀的flag。
switch (nalType & 0x1f) {
            case H264Decoder.NAL_TYPE_I:
                bufferInfo.flags = MediaCodec.BUFFER_FLAG_KEY_FRAME;
				break;
}
  • 音視頻同步
    方法裏面傳入的時間參數PTS的單位時:microseconds,微妙。
 long pts = System.nanoTime() / 1000;

直接使用當前時間戳會有問題,錄製成的MP4總時間會很大。我使用一個相對時間,當前時間相對於開始的時間差。

bufferInfo.presentationTimeUs = System.nanoTime()/1000-startPts;

使用中一些需要注意的地方

1. MediaFormat 可以在初始化編碼器的時候獲取
mediaformat = MediaFormat.createVideoFormat("video/avc", VIDEO_WIDTH, VIDEO_HEIGHT);
2. 寫入數據時候的inputBuffer 和 bufferInfo 需要自己構造

二、Q&S

  1. 錄製的視頻或快或慢。
    在 MP4Encoder::CreateMP4File本地方法中有一個m_nFrameRate變量,控制幀率的,也就是每分鐘多少幀,這裏可以自己去控制,和錄製視頻的幀率一致就行了,這裏默認的是25幀。
  2. 錄製的本地mp4視頻預覽畫面是黑色或者是綠色的
    造成的原因是錄製視頻流的時候第一幀不是關鍵幀(I幀),所以在使用writeH264Data方法的時候,記得第一幀傳入關鍵幀。
  3. download下載的項目無法運行
    。。。這個,你就自己去配置編譯環境了,代碼都在這了。
  4. 錄製成的MP4第一幀模糊
    這是因爲寫數據的時候沒有進行關鍵幀的判斷,第一幀寫入關鍵幀就不會有這個問題了。

三、引用

Android在MediaMuxer和MediaCodec用例:https://www.cnblogs.com/hrhguanli/p/5043610.html
Grafika: https://github.com/google/grafika
HWEncoderExperiments:https://github.com/OnlyInAmerica/HWEncoderExperiments/tree/audioonly/HWEncoderExperiments/src/main/java/net/openwatch/hwencoderexperiments

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章