当前位置:   article > 正文

Android音视频合成工具-MediaMuxer

mediamuxer

1️⃣、目标:把音视频流(使用各种通道传输过来的字节流)合成视频文件
2️⃣、工具:Android原生框架自带的MediaMuxer类
3️⃣、方案:

 private MediaMuxer mMuxer;
  • 1

MediaMuxer初始化

mMuxer = new MediaMuxer(“需要保存的路径”,MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
  • 1

MUXER_OUTPUT_MPEG_4//合成的视频文件格式

MediaMuxer添加音视频配置

private int mVideoTrackIndex, mAudioTrackIndex;
  • 1

Video Track

MediaFormat videoMediaformat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height);

byte[] header_SPS_1080 = {0, 0, 0, 1, 103, 77, 0, 42, -99, -88, 30, 0, -119, -7, 102, -32, 32, 32, 32, 64};
byte[] header_PPS_1080 = {0, 0, 0, 1, 104, -18, 60, -128};

byte[] header_SPS_720 = {0, 0, 0, 1, 103, 77, 0, 31, -99, -88, 20, 1, 110, -101, -128, -128, -128, -127};
byte[] header_PPS_720 = {0, 0, 0, 1, 104, -18, 60, -128};

byte[] header_SPS_360 = {0, 0, 0, 1, 103, 77, 0, 30, -99, -88, 40, 11, -2, 89, -72, 8, 8, 8, 16};
byte[] header_PPS_360 = {0, 0, 0, 1, 104, -18, 60, -128};

if (width == 640 && height == 360) {
    videoMediaformat.setByteBuffer("csd-0", ByteBuffer.wrap(header_SPS_360));
    videoMediaformat.setByteBuffer("csd-1", ByteBuffer.wrap(header_PPS_360));//H264格式的视频需要添加
    videoMediaformat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities
            .COLOR_FormatYUV420Planar);//视频数据格式
    videoMediaformat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 640 * 360);//缓冲区最大值
    videoMediaformat.setInteger(MediaFormat.KEY_CAPTURE_RATE, 20);//帧率
} else if (width == 1280 && height == 720) {
    videoMediaformat.setByteBuffer("csd-0", ByteBuffer.wrap(header_SPS_720));
    videoMediaformat.setByteBuffer("csd-1", ByteBuffer.wrap(header_PPS_720));
    videoMediaformat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities
            .COLOR_FormatYUV420Planar);
    videoMediaformat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 1280 * 720);
    videoMediaformat.setInteger(MediaFormat.KEY_CAPTURE_RATE, 20);
} else {
    videoMediaformat.setByteBuffer("csd-0", ByteBuffer.wrap(header_SPS_1080));
    videoMediaformat.setByteBuffer("csd-1", ByteBuffer.wrap(header_PPS_1080));
    videoMediaformat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities
            .COLOR_FormatYUV420Planar);
    videoMediaformat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 1920 * 1080);
    videoMediaformat.setInteger(MediaFormat.KEY_CAPTURE_RATE, 20);
}

mVideoTrackIndex = mMuxer .addTrack(videoMediaformat);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

Audio Track

 MediaFormat audioFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, 8000, 1);//音频格式,采样率,通道数
 audioFormat.setInteger(MediaFormat.KEY_IS_ADTS, 1);//AAC音频需要添加
 byte[] bytes = new byte[]{(byte) 0x11, (byte) 0x90};
 ByteBuffer audiobuffer = ByteBuffer.wrap(bytes);
 audioFormat.setByteBuffer("csd-0", audiobuffer);
 audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, 96000);//比特率
 audioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
 audioFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 100 * 1024);//作用于inputBuffer的大小
 mAudioTrackIndex = mMuxer .addTrack(audioFormat);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

以上初始化流程完成后

mMuxer.start();//开启合成器
  • 1

MediaMuxer写数据

private MediaCodec.BufferInfo       bufferInfo     = null;
private MediaCodec.BufferInfo       audioBufferInfo = null;
  • 1
  • 2

视频流

bufferInfo.offset = 0;
bufferInfo.size = /*naluIndex*/视频流数据的大小;
 if (/*(naluBuffer[4] & 0x1f) == 5*/判断I帧) {
     bufferInfo.flags = MediaCodec.BUFFER_FLAG_KEY_FRAME;//I帧
 } else if (/*(naluBuffer[4] & 0x1f) == 7 || (naluBuffer[4] & 0x1f) == 8 ||
         (frameBuffer[4] & 0x1f) == 6*/判断预测帧) {
     bufferInfo.flags = MediaCodec.BUFFER_FLAG_CODEC_CONFIG;//B,P帧
 } else {
     bufferInfo.flags = 0;//SEI
 }
bufferInfo.presentationTimeUs = getPTSUs();
mMuxer.writeSampleData(mVideoTrackIndex , /*outputBuffer*/视频流, bufferInfo);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

bufferInfo在合成时非常重要,这要是出现异常,则合成的视频也会存在异常

private long prevOutputPTSUs = 0;
/**
  *获取下一个编码表示时间
  */
 protected long getPTSUs() {
     long result = System.nanoTime() / 1000L;

     if (result < prevOutputPTSUs)
         result = (prevOutputPTSUs - result) + result;
     return result;
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

音频流

audioBufferInfo.offset = 0;
audioBufferInfo.size = /*nReadSize*/音频数据流大小;
audioBufferInfo.flags = 0;
audioBufferInfo.presentationTimeUs = getPTSUs();
mMuxer.writeSampleData(mVideoTrackIndex , /*outputBuffer*/音频流, bufferInfo);
  • 1
  • 2
  • 3
  • 4
  • 5

stop和release

if (mMuxer != null) {
        try {
            mMuxer.stop();
            mMuxer.release();
        } catch (IllegalStateException ex) {
            ex.printStackTrace();
        }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

4️⃣结束语:
1.没有音频数据的情况下,不要addAudioTrack。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/683466
推荐阅读
相关标签
  

闽ICP备14008679号