android 音视频硬编解码

编码分为软编和硬编,毫无疑问,能用硬编就用硬编,而Android/ target=_blank class=infotextkey>安卓硬编,绕不开MediaCodec 。
MediaCodec关于MediaCodec,官方文档有着详细的解答,这里就不赘述了 。

android 音视频硬编解码

文章插图
 
视频硬编码我这里需要将相机实时预览的YUV数据,编码为H.264格式的数据,在开始编码之前,首先要
MediaFormat mediaFormat = MediaFormat.createVideoFormat(MIMETYPE_VIDEO_AVC, width, height);mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, width * height * 5);mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30);mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);try {mMediaCodec = MediaCodec.createEncoderByType(MIMETYPE_VIDEO_AVC);mMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);mMediaCodec.start();} catch (Exception e) {e.printStackTrace();}接下来就可以传入数据进行编码了
private void encodeBuffer(@NonNull byte[] buffer, long pts) {int inputBufferIndex = mMediaCodec.dequeueInputBuffer(TIMEOUT_S);if (inputBufferIndex >= 0) {ByteBuffer inputBuffer = mMediaCodec.getInputBuffer(inputBufferIndex);inputBuffer.clear();inputBuffer.put(buffer);mMediaCodec.queueInputBuffer(inputBufferIndex, 0, buffer.length, pts, 0);}MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_S);while (outputBufferIndex >= 0) {ByteBuffer outputBuffer = mMediaCodec.getOutputBuffer(outputBufferIndex);if (bufferInfo.flags == MediaCodec.BUFFER_FLAG_CODEC_CONFIG) {bufferInfo.size = 0;}if (bufferInfo.size > 0) {outputBuffer.position(bufferInfo.offset);outputBuffer.limit(bufferInfo.offset + bufferInfo.size);bufferInfo.presentationTimeUs = pts;// todo 编码后的数据,可做回调处理...}mMediaCodec.releaseoutputBuffer(outputBufferIndex, false);bufferInfo = new MediaCodec.BufferInfo();outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_S);}}音频硬编码同时,将麦克风录制的PCM数据,编码为AAC格式的数据,同理,在开始编码之前
MediaFormat mediaFormat = MediaFormat.createAudioFormat(MIMETYPE_AUDIO_AAC, sampleRateInHz, channelConfig == AudioFormat.CHANNEL_IN_MONO ? 1 : 2);mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 64000);mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, AudioRecord.getMinBufferSize(DEFAULT_SAMPLE_RATE_IN_HZ, DEFAULT_CHANNEL_CONFIG, DEFAULT_ENCODING) * 3);mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, channelConfig == AudioFormat.CHANNEL_IN_MONO ? 1 : 2);try {mMediaCodec = MediaCodec.createEncoderByType(MIMETYPE_AUDIO_AAC);mMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);mMediaCodec.start();} catch (Exception e) {e.printStackTrace();}同理,接下来就可以传入数据进行编码了
private void encodeBuffer(@NonNull byte[] buffer, long pts) {int inputBufferIndex = mMediaCodec.dequeueInputBuffer(TIMEOUT_S);if (inputBufferIndex >= 0) {ByteBuffer inputBuffer = mMediaCodec.getInputBuffer(inputBufferIndex);inputBuffer.clear();inputBuffer.limit(buffer.length);inputBuffer.put(buffer);mMediaCodec.queueInputBuffer(inputBufferIndex, 0, buffer.length, pts, 0);}MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_S);while (outputBufferIndex >= 0) {ByteBuffer outputBuffer = mMediaCodec.getOutputBuffer(outputBufferIndex);if (bufferInfo.flags == MediaCodec.BUFFER_FLAG_CODEC_CONFIG) {bufferInfo.size = 0;}if (bufferInfo.size > 0) {outputBuffer.position(bufferInfo.offset);outputBuffer.limit(bufferInfo.offset + bufferInfo.size);bufferInfo.presentationTimeUs = pts;// todo 编码后的数据,可做回调处理...}mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);bufferInfo = new MediaCodec.BufferInfo();outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_S);}}可以发现,音视频编码流程是一样的,通过上面的操作,看起来数据的编码流程已经完成,接下来,就是解码了,同样的,解码也要用到的MediaCodec
视频硬解码解码之前
try {mMediaCodec = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);MediaFormat mediaFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height);mMediaCodec.configure(mediaFormat, surface, null, 0);mMediaCodec.start();} catch (Exception e) {throw new RuntimeException(e);}接下来便是解码已经编码好的H.264帧数据并在Surface中进行渲染
public void decodeAndRenderV(byte[] in, int offset, int length, long pts) {int inputBufferIndex = mMediaCodec.dequeueInputBuffer(-1);if (inputBufferIndex >= 0) {ByteBuffer inputBuffer = mMediaCodec.getInputBuffer(inputBufferIndex);inputBuffer.clear();inputBuffer.put(in, offset, length);mMediaCodec.queueInputBuffer(inputBufferIndex, 0, length, pts, 0);}MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_US);while (outputBufferIndex >= 0) {mMediaCodec.releaseOutputBuffer(outputBufferIndex, true);outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_US);}}


推荐阅读