赞
踩
在 Android 平台上,我们经常需要处理音视频数据,比如播放视频、录制音频等。为了高效处理这些数据,Android 提供了 MediaCodec
类,它允许我们对音视频进行编解码操作。
MediaCodec
是 Android 提供的一个音视频编解码器,它允许应用程序对音频和视频数据进行编码(压缩)和解码(解压缩)。通过 MediaCodec
,我们可以实现音视频的播放、录制、转码等功能。
下面以编码器为例子进行说明,如下图所示,MediaCodec存在两个缓冲区,输入缓冲区(input)和输出缓冲区(output)。编码视频流时即不断从输入缓冲区中读取原始YUV数据,经过Codec编码后,从输出缓冲区获取压缩后的H264数据。
首先,我们需要创建一个 MediaCodec
实例。对于视频编码,我们可以使用如下代码:
MediaCodec codec = MediaCodec.createEncoderByType("video/avc");
接下来,我们需要配置 MediaCodec
。对于编码器,我们需要设置输入数据的格式和输出数据的格式:
MediaFormat format = MediaFormat.createVideoFormat("video/avc", width, height); // 设置宽高
format.setInteger(MediaFormat.KEY_BIT_RATE, 500000); // 设置码率
format.setInteger(MediaFormat.KEY_FRAME_RATE, 15); // 设置帧率
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 60); // 设置关键帧间隔
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible); // 设置颜色格式
format.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR); // 设置码率控制模式
format.setInteger(MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline); // 设置profile
format.setInteger(MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.AVCLevel31); // 设置level
codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
Android支持三种码率控制模式:
Profile支持三种等级:
Level等级确认:
以360*640为例
水平宏块数 = ceil(视频宽度/16) = ceil(640/16) = ceil(40.0) = 40
垂直宏块数 = ceil(视频高度/16) = ceil(360/16) = ceil(22.5) = 23
每帧宏块数 = 水平宏块数 * 垂直宏块数 = 40 * 23 = 920
查表可知支持每帧宏块数920的最低级别是2.2。级别2.2所允许的每秒最大宏块数是20250。20250 / 920 = 22,即最高支持每秒22帧。
注:ceil(x)是向上舍入函数,返回的是大于等于x的最小整数。
一旦配置完成,我们就可以开始向编码器输入数据或从解码器接收数据了。对于编码器,我们使用 codec.queueInputBuffer()
方法向编码器输入原始数据:
int inputBufferIndex = codec.dequeueInputBuffer(timeout);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferIndex);
inputBuffer.put(data);
codec.queueInputBuffer(inputBufferIndex, 0, data.length, presentationTimeUs, 0);
}
最后,需要处理输出的数据。对于编码器,我们使用 codec.dequeueOutputBuffer()
方法获取编码后的数据:
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, timeout);
if (outputBufferIndex >= 0) {
ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferIndex);
// 处理编码后的数据
codec.releaseOutputBuffer(outputBufferIndex, false);
}
codec.stop();
codec.release();
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。