赞
踩
1️⃣、目标:把音视频流(使用各种通道传输过来的字节流)合成视频文件
2️⃣、工具:Android原生框架自带的MediaMuxer类
3️⃣、方案:
private MediaMuxer mMuxer;
①MediaMuxer初始化
mMuxer = new MediaMuxer(“需要保存的路径”,MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
MUXER_OUTPUT_MPEG_4//合成的视频文件格式
②MediaMuxer添加音视频配置
private int mVideoTrackIndex, mAudioTrackIndex;
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);
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);
以上初始化流程完成后
mMuxer.start();//开启合成器
③MediaMuxer写数据
private MediaCodec.BufferInfo bufferInfo = null;
private MediaCodec.BufferInfo audioBufferInfo = null;
视频流
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);
bufferInfo在合成时非常重要,这要是出现异常,则合成的视频也会存在异常
private long prevOutputPTSUs = 0;
/**
*获取下一个编码表示时间
*/
protected long getPTSUs() {
long result = System.nanoTime() / 1000L;
if (result < prevOutputPTSUs)
result = (prevOutputPTSUs - result) + result;
return result;
}
音频流
audioBufferInfo.offset = 0;
audioBufferInfo.size = /*nReadSize*/音频数据流大小;
audioBufferInfo.flags = 0;
audioBufferInfo.presentationTimeUs = getPTSUs();
mMuxer.writeSampleData(mVideoTrackIndex , /*outputBuffer*/音频流, bufferInfo);
④stop和release
if (mMuxer != null) {
try {
mMuxer.stop();
mMuxer.release();
} catch (IllegalStateException ex) {
ex.printStackTrace();
}
}
4️⃣结束语:
1.没有音频数据的情况下,不要addAudioTrack。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。