当前位置:   article > 正文

一文搞懂 Android 音视频编解码器 MediaCodec_android mediacodec

android mediacodec

Android 平台上,我们经常需要处理音视频数据,比如播放视频、录制音频等。为了高效处理这些数据,Android 提供了 MediaCodec 类,它允许我们对音视频进行编解码操作。

什么是 MediaCodec?

MediaCodec 是 Android 提供的一个音视频编解码器,它允许应用程序对音频和视频数据进行编码(压缩)和解码(解压缩)。通过 MediaCodec,我们可以实现音视频的播放、录制、转码等功能。

下面以编码器为例子进行说明,如下图所示,MediaCodec存在两个缓冲区,输入缓冲区(input)和输出缓冲区(output)。编码视频流时即不断从输入缓冲区中读取原始YUV数据,经过Codec编码后,从输出缓冲区获取压缩后的H264数据。

在这里插入图片描述

MediaCodec用法

1. 创建 MediaCodec 实例

首先,我们需要创建一个 MediaCodec 实例。对于视频编码,我们可以使用如下代码:

MediaCodec codec = MediaCodec.createEncoderByType("video/avc");
  • 1

2. 配置 MediaCodec

接下来,我们需要配置 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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Android支持三种码率控制模式

  1. BITRATE_MODE_CQ:0,不控制码率,尽最大可能保证图像质量
  2. BITRATE_MODE_VBR:1,可变码率,根据场景的变化幅度和复杂度动态调整输出数据的比特率
  3. BITRATE_MODE_CBR:2,固定码率,输出数据的比特率尽量保持在设定值

Profile支持三种等级

  1. Baseline: 支持 I/P 帧,只支持无交错和 CAVLC,一般用于低阶或需要额外容错的应用,比如视频通话、手机视频等
  2. Main :提供 I/P/B 帧,支持无交错和交错,同样提供对于 CAVLC 和 CABAC 的支持,用于主流消费类电子产品规格如低解码的 mp4、便携的视频播放器、PSP 和 Ipod 等
  3. High :在 Main 的基础上增加了 8x8 内部预测、自定义量化、无损视频编码和更多的 YUV 格式(如 4:4:4),用于广播及视频碟片 存储(蓝光影片),高清电视的应用

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的最小整数。
在这里插入图片描述

3. 处理输入数据

一旦配置完成,我们就可以开始向编码器输入数据或从解码器接收数据了。对于编码器,我们使用 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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

4. 处理输出数据

最后,需要处理输出的数据。对于编码器,我们使用 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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

5. 销毁编码器

codec.stop();
codec.release();
  • 1
  • 2

踩过的坑

  1. Android机型数据必须以16位对齐:摄像头采集360x640的图像实际上获取到的是384x640,采集180x320的图像获取到的是192x320,这就会导致编码图像绿屏的情况。因此数据在传入编码器之前需要先进行裁剪。
  2. 部分机型编码画面出现大量马赛克:presentationTimeUs需设定为实际时间戳,时间戳用于指示音视频数据的播放或处理时间,错误的时间戳会导致输出码率与设定值不符的情况。可以使用System.nanoTime()获取纳秒级别的系统时间。
  3. 部分鸿蒙机型编码出现绿条:通常情况下Android机型数据都是以16位对齐,但鸿蒙海思芯片不支持,需要单独适配。可以创建指定编码器OMX.google.h264.encoder,或采用NV12编码。

相关系列

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

闽ICP备14008679号