Android 音视频之音频AAC编码
AAC介绍
介绍
AAC,全称Advanced Audio Coding,是一种专为声音数据设计的文件压缩格式。他的目的是为了取代MP3格式,与MP3不同,它采用了全新的算法进行编码,更加高效,具有更高的“性价比”。利用AAC格式,可使人感觉声音质量没有明显降低的前提下,更加小巧。
为什么重点介绍AAC
1.他的应用范围广。目前市场上泛娱乐化直播系统,90%以上都是采用AAC编码
2.目前的传输协议,一般采用rtmp协议,此协议支持aac,但是不支持OPUS (因为OPUS是最近才推出的,虽然强,但是还不通用)
3.AAC本身编解码器质量非常高。作为一种高压缩比的音频压缩算法,AAC通常压缩比为18:1 (也有资料说为20:1),但是还能保存较好的音质。
AAC音频格式
ADIF (Audio Data Interchange Format)
这种格式只需要在文件开头存一个很小的头,包括采样率,采样大小,声道数量等基本信息,就可以对文件进行解读。这种格式只能从头开始解码,常用在磁盘文件中。
ADTS (Audio Data transport Stream)
这种格式每一帧前面都有一个同步字,占用7-9个字节,好处是可以在音频流的任何位置开始解码,他类似于数据流格式。因为每一帧前面都有同步字,所以ADTS文件要比ADIF增加一些数据量
AAC产生原因
AAC产生目的就是为了取代MP3。
AAC之前,大部分音频都还是使用MP3格式。MP3的使用规范是MPEG-2,他对于音频编解码,主要思想还是有损压缩。有损压缩在《Android 音视频之音频入门讲解》也介绍过,被压缩的数据不能完全还原回来,所以音质上会有一定损耗。而且在码率比较高的情况下,压缩比要非常高的情况下,损耗性会非常大。AAC恰巧弥补了这个问题,AAC对原始数据损耗很低,但是压缩效率很高。
在2000年,MPEG-4标准出现后,AAC还加入了SBR技术和PS技术。
AAC LC :
LC (Low Complexity) 低复杂度
AAC HE V1 : AAC LC + SBR
SBR(Spectral Band Replication)是增频复用。我们知道,音频频带分为高频和低频。所以低频的20hz,如果我们采用44.1khz的采样率,就采样2000次,他就可以完整记录下模拟声波,但是我们没必要进行这么多的采样。高频的20khz,我们采用44.1khz的采样率,结果只采样2次,这样保真性就很差。而采用了SBR技术,SBR把频谱切割开来,低频单独编码保存主要成分, 高频单独放大编码保存音质,这样高频就增加了采样。这样的好处,一是减少了码率,二是提高了音频的质量。
AAC HE V2 : AAC + SBR + PS
PS(Parametric Stereo)是双声道分别保存,一个声道完整保存,另一个只存差异的,参数的部分。因为两个声道相关性非常强,我们可以通过某种函数,完全恢复以前的声音。基于这些原因,所以他只要存一些参数就可以了。
AAC优点
①提升的压缩率:可以以更小的文件大小获得更高的音质;
②支持多声道:可提供最多48个全音域声道;
③更高的解析度:最高支持96KHz的采样频率;
④提升的解码效率:解码播放所占的资源更少;
AAC编解码库
Libfdk_AAC > ffmpeg AAC > libaac > libvo_aacenc
AAC编码使用
使用ffmpeg对音频进行AAC编码
不好意思,前段时间想采用ffmpeg去对AAC进行编码,结果开发过程中,发现ffmpeg avcodec_encode_audio2返回-22,导致编码不成功,一直找不到原因。后期修改好了,我一定在文章和项目中补上。
使用MediaCodec对音频进行AAC硬编码
Android中可以使用MediaCodec来访问底层的媒体编解码器,可以对媒体进行编/解码。
举例,比如之前文章《Android 音视频之音频录制》,我们使用AudioRecord录制了一个pcm文件。我们要将文件数据进行AAC编码,需要先初始化一个MediaCodec对象,设置他的MediaFormat为MediaFormat.MIMETYPE_AUDIO_AAC。如果想使用其他压缩编码,类似。代码如下:
/**
* 初始化AAC编码器
*/
private void initAACMediaEncode() {
try {
//参数对应-> mime type、采样率、声道数
MediaFormat encodeFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, 44100, 2);
encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, 64000);//比特率
encodeFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
encodeFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 100 * 1024);
mediaEncode = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
mediaEncode.configure(encodeFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
} catch (IOException e) {
e.printStackTrace();
}
if (mediaEncode == null) {
Log.e(TAG, "create mediaEncode failed");
return;
}
mediaEncode.start();
encodeInputBuffers = mediaEncode.getInputBuffers();
encodeOutputBuffers = mediaEncode.getOutputBuffers();
encodeBufferInfo = new MediaCodec.BufferInfo();
}
初始化编码器后,将pcm数据传入到下面这个方法中进行AAC编码。
/**
* 编码,得到{@link #encodeType}格式的音频文件,并保存到{@link #dstPath}
* @param data
*/
public void encodeData(byte[] data){
//dequeueInputBuffer(time)需要传入一个时间值,-1表示一直等待,0表示不等待有可能会丢帧,其他表示等待多少毫秒
int inputIndex = mediaEncode.dequeueInputBuffer(-1);//获取输入缓存的index
if (inputIndex >= 0) {
ByteBuffer inputByteBuf = encodeInputBuffers[inputIndex];
inputByteBuf.clear();
inputByteBuf.put(data);//添加数据
inputByteBuf.limit(data.length);//限制ByteBuffer的访问长度
mediaEncode.queueInputBuffer(inputIndex, 0, data.length, 0, 0);//把输入缓存塞回去给MediaCodec
}
int outputIndex = mediaEncode.dequeueOutputBuffer(encodeBufferInfo, 0);//获取输出缓存的index
while (outputIndex >= 0) {
//获取缓存信息的长度
int byteBufSize = encodeBufferInfo.size;
//添加ADTS头部后的长度
int bytePacketSize = byteBufSize + 7;
//拿到输出Buffer
ByteBuffer outPutBuf = encodeOutputBuffers[outputIndex];
outPutBuf.position(encodeBufferInfo.offset);
outPutBuf.limit(encodeBufferInfo.offset+encodeBufferInfo.size);
byte[] targetByte = new byte[bytePacketSize];
//添加ADTS头部
addADTStoPacket(targetByte, bytePacketSize);
/*
get(byte[] dst,int offset,int length):ByteBuffer从position位置开始读,读取length个byte,并写入dst下
标从offset到offset + length的区域
*/
outPutBuf.get(targetByte,7,byteBufSize);
outPutBuf.position(encodeBufferInfo.offset);
try {
bos.write(targetByte);
} catch (IOException e) {
e.printStackTrace();
}
//释放
mediaEncode.releaseOutputBuffer(outputIndex,false);
outputIndex = mediaEncode.dequeueOutputBuffer(encodeBufferInfo, 0);
}
}
如果是录制过程中,可以使用
audioRecord.read(audiodata, 0, bufferSizeInBytes);
方法拉取pcm数据,把数据传入到编码方法中,可以一边录制一边编码。
如果是已经录制好的pcm文件,同样可以把文件转换成流数据,再编码。
/**
* 开始编码
* PCM数据在编码成想要得到的{@link #encodeType}音频格式
* PCM->aac
*/
public void startAsync() {
Log.i(TAG, "start");
new Thread(new Runnable() {
@Override
public void run() {
try {
File file = new File(srcPath);
FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int n;
while ((n = fis.read(b)) != -1) {
bos.write(b, 0, n);
encodeData(bos.toByteArray());
bos.reset();
}
fis.close();
bos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
最后可以在目标路径下得到一个编码格式为aac的文件,可以直接播放。
如果我们想把m4a,mp3的文件,进行aac编码。那就需要进行转码,我们需要先对其进行解码成pcm数据,再进行编码。后面我会在《Android 音视频之音频编码转换》中简单介绍。
未完待更新...
上一篇:《Android 音视频之音频编码》
下一篇:《Android 音视频之音频编码转换》
有问题的地方请大家帮忙指出,谢谢。
持续更新中...