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

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