Android音频开发(三)——音频编解码

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android音频开发(三)——音频编解码相关的知识,希望对你有一定的参考价值。

参考技术A 上一节中我们讲了怎么采集音频并播放,由于AudioRecord采集的是PCM数据,没有经过处理,所有播放的时候会有杂音,啸叫等现象出现。因此处理掉这些不需要的数据就是本节的内容,编码与解码。

android官方提供给我们的用于编解码的类是 MediaCodec ,它是android 4.1(API 16)才引入的,所以只能工作于andorid4.1以上的手机,如果想兼容4.1以下版本的手机,只能使用第三方库,如大名鼎鼎的 ffmpeg ,B站的 ijkplayer 等。

(1)提供了一套访问 Android 底层多媒体模块的接口,主要是音视频的编解码接口

(2)在Android上,预设的多媒体框架是基于第三方PacketVideo公司的OpenCORE来实现,OpenCORE的优点是兼顾了跨平台的移植性,而且已经过多方验证,所以相对来说较为稳定;缺点是国语庞大复杂,需要耗费相当多的时间去维护。因此从Android 2.0开始,Google引进了较为简洁的StageFright。Android 底层多媒体模块采用的是 StageFright 框架,它是基于OpenMax标准实现的,任何 Android 底层编解码模块的实现,都必须遵循 OpenMax 标准。值得一提的是,OpenMAX是Khronos制定的API,Khronos也是OpenGL的制定者。Google 官方默认提供了一系列的软件编解码器:包括:OMX.google.h264.encoder,OMX.google.h264.encoder, OMX.google.aac.encoder, OMX.google.aac.decoder 等等,而硬件编解码功能,则需要由芯片厂商依照 OpenMax 框架标准来完成,所以,一般采用不同芯片型号的手机,硬件编解码的实现和性能是不同的

(3)Android 应用层统一由 MediaCodec API 来提供各种音视频编解码功能,由参数配置来决定采用何种编解码算法、是否采用硬件编解码加速等等

根据android官方文档的描述,MediaCodec的核心就是使用缓冲区队列来操作数据,使用流程如下:

//name既是媒体文件的类型,如audio/3gpp,详情参考MediaFormat的MIMETYPE常量
MediaCodec codec = MediaCodec.createByCodecName(name);
codec.configure(format, …);
MediaFormat outputFormat = codec.getOutputFormat(); // option B
codec.start();
for (;;)
////获取可用的inputBuffer -1代表一直等待,0表示不等待 建议-1,避免丢帧
int inputBufferId = codec.dequeueInputBuffer(-1);
if (inputBufferId >= 0)
ByteBuffer inputBuffer = codec.getInputBuffer(…);
// fill inputBuffer with valid data

codec.queueInputBuffer(inputBufferId, …);

//执行上面的操作后就把待编解码的数据存入了输入缓冲区,然后下一步就是操作然后把编解码的数据存入输出缓冲区
int outputBufferId = codec.dequeueOutputBuffer(…);
if (outputBufferId >= 0)
ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A
// bufferFormat is identical to outputFormat
// outputBuffer is ready to be processed or rendered.

codec.releaseOutputBuffer(outputBufferId, …);
else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED)
// Subsequent data will conform to new format.
// Can ignore if using getOutputFormat(outputBufferId)
outputFormat = codec.getOutputFormat(); // option B


codec.stop();
codec.release();

MediaCodec codec = MediaCodec.createByCodecName(name);
MediaFormat mOutputFormat; // member variable
codec.setCallback(new MediaCodec.Callback()
@Override
void onInputBufferAvailable(MediaCodec mc, int inputBufferId)
ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferId);
// fill inputBuffer with valid data

codec.queueInputBuffer(inputBufferId, …);


@Override
void onOutputBufferAvailable(MediaCodec mc, int outputBufferId, …)
ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A
// bufferFormat is equivalent to mOutputFormat
// outputBuffer is ready to be processed or rendered.

codec.releaseOutputBuffer(outputBufferId, …);


@Override
void onOutputFormatChanged(MediaCodec mc, MediaFormat format)
// Subsequent data will conform to new format.
// Can ignore if using getOutputFormat(outputBufferId)
mOutputFormat = format; // option B


@Override
void onError(…)


);
codec.configure(format, …);
mOutputFormat = codec.getOutputFormat(); // option B
codec.start();
// wait for processing to complete
codec.stop();
codec.release();

MediaCodec codec = MediaCodec.createByCodecName(name);
codec.configure(format, …);
codec.start();
//API的区别在这里
ByteBuffer[] inputBuffers = codec.getInputBuffers();
ByteBuffer[] outputBuffers = codec.getOutputBuffers();
for (;;)
int inputBufferId = codec.dequeueInputBuffer(…);
if (inputBufferId >= 0)
// fill inputBuffers[inputBufferId] with valid data

codec.queueInputBuffer(inputBufferId, …);

int outputBufferId = codec.dequeueOutputBuffer(…);
if (outputBufferId >= 0)
// outputBuffers[outputBufferId] is ready to be processed or rendered.

codec.releaseOutputBuffer(outputBufferId, …);
else if (outputBufferId == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED)
outputBuffers = codec.getOutputBuffers();
else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED)
// Subsequent data will conform to new format.
MediaFormat format = codec.getOutputFormat();


codec.stop();
codec.release();

silk 编解码器下载

参考 silk 编解码_Silk编解码在android实现
Silk编解码是Skype向第三方开发人员和硬件制造商提供免版税认证(RF)的Silk宽带音频编码器,Skype已将其开源。SILK Codec是一个语音和音频编解码算法, 对于音频带宽、网络带宽和算法复杂度都具有很好的弹性。支持4种采样率:8KHz、12KHz、16KHz、24KHz;三种复杂度:低、中、高。编码码率在 6~40kbps(不同采样率具有不同的码率范围)以及还支持VAD、DTX、FEC等模块,感觉还是比较全面。最重要的一点是提供了定点C代码,非常有利于向ARM、DSP移植和优化。

sipdroid 官网地址
http://sipdroid.org/

sipdroid 的 git 网页地址
https://github.com/i-p-tel/sipdroid

sipdroid 的 git 下载链接
https://github.com/i-p-tel/sipdroid.git

我的 gitee
https://gitee.com/hugang2021/sipdroid

可以看到 jni 部分有 silk 的实现

以上是关于Android音频开发(三)——音频编解码的主要内容,如果未能解决你的问题,请参考以下文章

Android音频开发:音频数据的编解码

实时AAC音频/本地AAC音视频硬解码详细介绍附带Demo

android音视频音频硬编解码pcm&aac&wav

android音视频音频硬编解码pcm&aac&wav

Android G711A 音频编解码,去除“吱吱”电流声,附上so下载地址

android MediaCodec 音频编解码的实现——转码