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

github项目地址

未完待更新...

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容

  • 介绍 AAC(Advanced Audio Coding),中文称为“高级音频编码”,出现于1997年,基于 MP...
    sxyxsp123阅读 6,663评论 0 7
  • 前言 本篇开始讲解在Android平台上进行的音频编辑开发,首先需要对音频相关概念有基础的认识。所以本篇要讲解以下...
    Ihesong阅读 7,544评论 2 18
  • 音频技术开发,我们得对声音有所了解,掌握音频的基础知识,这才能更好地去做技术开发。首先介绍音频基础知识,然后介绍音...
    安仔夏天勤奋阅读 7,343评论 3 18
  • 手机里总是有些不愿删掉的歌,也许不再那么喜欢,可是始终不忍丢弃。 你有没有改不掉的习惯,比如说,因为某件事熬夜,反...
    长亭微雨阅读 254评论 0 0
  • 你是否在某段时期,内心感觉极度压抑,办事处处碰壁不顺。人生经历这么些许,虽然知道以后总会有风光精彩,普希金的假如生...
    萧能志阅读 207评论 0 0