音视频

Android 音视频之音频AAC编码

2018-09-22  本文已影响0人  请叫我果爸

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 音视频之音频编码转换》中简单介绍。

github项目地址

未完待更新...

上一篇:《Android 音视频之音频编码》
下一篇:《Android 音视频之音频编码转换》

有问题的地方请大家帮忙指出,谢谢。
持续更新中...

上一篇下一篇

猜你喜欢

热点阅读