MediaCodec编码音频

使用MediaCodec编码AAC对Android系统是有要求的,必须是4.1系统以上,即要求Android的版本代号在Jelly_Bean以上。MediaCodec是Android系统提供的硬件编码器,它可以利用设备的硬件来完成编码,从而大大提高编码的效率,还可以降低电量的使用,但是其在兼容性方面不如软件编码好,因为Android设备的碎片化太严重,可以自己衡量在应用中是否使用Android平台的硬件编码特性。AAC编码格式其中有一种是ADTS封装格式,另外一种就是裸的AAC的封装格式。裸的AAC即AAC的原始数据块,一个AAC原始数据块的长度是可变的,对原始帧加上ADTS头进行封装,就形成了ADTS帧。ADTS的全称是Audio Data Transport Stream,是AAC音频的传输流格式,通常我们将得到的AAC原始帧加上ADTS头进行封装后写入文件,该文件使用常用的播放器即可播放,这是个验证AAC数据是否正确的方法。进行封装之前,需要了解相关的参数,如采样率、声道数、原始数据块的长度等。下面将AAC原始数据帧加工为ADTS格式的帧,根据相关参数填写组成7字节的ADTS头。下面就来分配一下这7个字节:

int adtsLength = 7;
char *packet = malloc(sizeof(char) * adtsLength);
其中,前两个字节是ADTS的同步字:
packet[0] = (char)0xFF;
packet[1] = (char)0xF9;

紧接着第三个字节是编码的Profile、采样率下标(注意是下标,而不是采样率)、声道配置(注意是声道配置,而不是声道数)、数据长度的组合(注意packetLen是原始数据长度加上ADTS头的长度):

int profile = 2; // AAC LC
int freqIdx = 4; // 44.1kHz
int chanCfg = 2;
packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));
packet[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11));
packet[4] = (byte) ((packetLen & 0x7FF) >> 3);
packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);

其中具体的编码Profile、采样率的下标以及声道数都可以从下方这个链接中查到相关的所有表示:

https:// wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Channel_Configurations

最后一个字节也是固定的:

packet[6] = (byte) 0xFC

至此,ADTS头就拼接上了,对于编码出一个可以播放的AAC文件来讲,这是非常重要的,而对于MediaCodec编码出来的AAC来说,都需要拼接上该ADTS头,最终文件就可以正确地播放出来了。

下面就来看看MediaCodec如何使用。首先来看一下初始化方法,先构造一个MediaCodec
实例,通过该类的静态函数来实现。构造一个AAC的Codec,其代码如下:

MediaCodec mediaCodec = MediaCodec.createEncoderByType("audio/mp4a-latm");

其实该方法有点类似于前面所讲的FFmpeg中根据Codec的name来找出编码器。构造出该实例之后,就需要配置该编码器了,配置编码器最重要的是需要传递一个MediaFormat类型的对象,该对象中配置的是比特率、采样率、声道数以及编码AAC的Profile,此外,还需要配置输入
Buffer的最大值,代码如下:

MediaFormat encodeFormat = MediaFormat.createAudioFormat(MINE_TYPE, sampleRate,
channels);
encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
encodeFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, Media
CodecInfo.CodecProfileLevel.AACObjectLC);
encodeFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 10 * 1024);

与FFmpeg中的配置编码器类似,上述代码也配置了编码器要求的输入,现在就将该对象配置到编码器内部:

mediaCodec.configure(encodeFormat, null, null,MediaCodec.CONFIGURE_FLAG_ENCODE);

最后一个参数代表需要配置一个编码器,而非解码器(如果是解码器则传递为0)。调用start方法,代表开启该编码器。

至此,编码器已经完全配置好了,打开编码器可以从MediaCodec实例中取出两个buffer,一个是inputBuffer,用于存放输入的PCM数据(类似于FFmpeg编码的AVFrame);另外一个是
outputBuffer,用于存放编码之后的原始AAC的数据(类似于FFmpeg编码的AVPacket),代码如下:

ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();

到此,初始化方法已实现完毕,下面来看一下编码方法

代码实现具体如下。首先,从mediaCodec中读取出一个可以用来输入的buffer的Index,然后填充数据,并且把填充好的buffer发送给Codec:

int bufferIndex = codec.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = inputBuffers[bufferIndex];
inputBuffer.clear();
inputBuffer.put(data);
long time = System.nanoTime();
codec.queueInputBuffer(bufferIndex, 0, len, time, 0);
}

然后,从Codec中读取出一个编码好的buffer的Index,通过Index读取出对应的output-Buffer,然后将数据读取出来,添加上ADTS头部,写文件,之后再把该outputBuffer放回到待编码填充队列中去:

BufferInfo info = new BufferInfo();
int index = codec.dequeueOutputBuffer(info, 0);
while (index >= 0) {
ByteBuffer outputBuffer = outputBuffers[index];
if (outputAACDelegate != null) {
int outPacketSize = info.size + 7;
outputBuffer.position(info.offset);
outputBuffer.limit(info.offset + info.size);
byte[] outData = new byte[outPacketSize];
// 添加ADTS头信息
addADTStoPacket(outData, outPacketSize);
// 将编码得到的AAC数据取出到目标数组中,其中7代表偏移量
outputBuffer.get(outData, 7, info.size);
outputBuffer.position(info.offset);
outputAACDelegate.outputAACPacket(outData);
}
codec.releaseOutputBuffer(index, false);
index = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
}

需要注意的是,上述代码中的第一行是构造出一个BufferInfo类型的对象,当开发者从Codec的输出缓冲区中读取一个buffer的时候,Codec会将该buffer的描述信息放入该对象中,后续会按照该对象中的信息来处理实际的内容。最后来看一下销毁方法,使用完了MediaCodec编码器之后,就需要停止运行并释放编码器,代码如下:
if (null != mediaCodec) {

mediaCodec.stop();
mediaCodec.release();
}

外界调用端需要做的事情是先初始化该类,然后读取PCM文件并调用该类的编码方法,实现该类的Delegate接口,在重写的方法中将输出带有ADTS头的AAC码流直接写文件,最终编码结束之后,调用该类的停止编码方法。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MediaCodec是Android平台上的一个类,用于对音频和视频进行解码和编码操作。对于音频播放,MediaCodec可以通过解码音频数据并将其渲染到音频轨道上来实现。 要使用MediaCodec播放音频,首先需要创建一个MediaCodec对象,并指定要解码的音频格式。然后,需要将音频数据发送给MediaCodec进行解码。可以通过读取音频文件或从网络获取音频数据。一旦将音频数据发送给MediaCodec,它将开始解码操作。 解码完成后,可以将解码后的音频数据渲染到音频轨道上进行播放。可以使用AudioTrack类来创建一个音频轨道,并将解码后的音频数据写入音频轨道。然后,可以通过启动音频轨道来播放解码后的音频数据。 在播放音频之前,可能需要配置一些音频参数,如音频格式、采样率、声道数等。可以使用MediaFormat类来指定这些参数。此外,还可以使用MediaCodec.Callback类来监视解码操作的状态,例如解码完成、解码错误等。 总的来说,使用MediaCodec播放音频需要以下步骤: 1. 创建MediaCodec对象并指定音频格式。 2. 读取音频数据或从网络获取音频数据。 3. 将音频数据发送给MediaCodec进行解码。 4. 创建音频轨道并将解码后的音频数据写入音频轨道。 5. 启动音频轨道来播放解码后的音频数据。 6. 可选:配置音频参数和监视解码操作状态。 通过以上步骤,可以使用MediaCodec播放音频。使用MediaCodec可以实现更低延迟和更高效的音频播放,适用于需要对音频进行实时处理和优化的应用场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值