Android 音視頻之音頻AAC編碼

AAC介紹

介紹
AAC,全稱Advanced Audio Coding,是一種專為聲音數(shù)據(jù)設(shè)計(jì)的文件壓縮格式。他的目的是為了取代MP3格式,與MP3不同,它采用了全新的算法進(jìn)行編碼,更加高效,具有更高的“性價(jià)比”。利用AAC格式,可使人感覺(jué)聲音質(zhì)量沒(méi)有明顯降低的前提下,更加小巧。

為什么重點(diǎn)介紹AAC
1.他的應(yīng)用范圍廣。目前市場(chǎng)上泛娛樂(lè)化直播系統(tǒng),90%以上都是采用AAC編碼
2.目前的傳輸協(xié)議,一般采用rtmp協(xié)議,此協(xié)議支持aac,但是不支持OPUS??(因?yàn)镺PUS是最近才推出的,雖然強(qiáng),但是還不通用)
3.AAC本身編解碼器質(zhì)量非常高。作為一種高壓縮比的音頻壓縮算法,AAC通常壓縮比為18:1??(也有資料說(shuō)為20:1),但是還能保存較好的音質(zhì)。

AAC音頻格式
ADIF (Audio Data Interchange Format)
這種格式只需要在文件開頭存一個(gè)很小的頭,包括采樣率,采樣大小,聲道數(shù)量等基本信息,就可以對(duì)文件進(jìn)行解讀。這種格式只能從頭開始解碼,常用在磁盤文件中。
ADTS (Audio Data transport Stream)
這種格式每一幀前面都有一個(gè)同步字,占用7-9個(gè)字節(jié),好處是可以在音頻流的任何位置開始解碼,他類似于數(shù)據(jù)流格式。因?yàn)槊恳粠懊娑加型阶?,所以ADTS文件要比ADIF增加一些數(shù)據(jù)量

AAC產(chǎn)生原因
AAC產(chǎn)生目的就是為了取代MP3。
AAC之前,大部分音頻都還是使用MP3格式。MP3的使用規(guī)范是MPEG-2,他對(duì)于音頻編解碼,主要思想還是有損壓縮。有損壓縮在《Android 音視頻之音頻入門講解》也介紹過(guò),被壓縮的數(shù)據(jù)不能完全還原回來(lái),所以音質(zhì)上會(huì)有一定損耗。而且在碼率比較高的情況下,壓縮比要非常高的情況下,損耗性會(huì)非常大。AAC恰巧彌補(bǔ)了這個(gè)問(wèn)題,AAC對(duì)原始數(shù)據(jù)損耗很低,但是壓縮效率很高。
在2000年,MPEG-4標(biāo)準(zhǔn)出現(xiàn)后,AAC還加入了SBR技術(shù)和PS技術(shù)。
AAC LC :
LC (Low Complexity) 低復(fù)雜度

AAC HE V1 : AAC LC + SBR
SBR(Spectral Band Replication)是增頻復(fù)用。我們知道,音頻頻帶分為高頻和低頻。所以低頻的20hz,如果我們采用44.1khz的采樣率,就采樣2000次,他就可以完整記錄下模擬聲波,但是我們沒(méi)必要進(jìn)行這么多的采樣。高頻的20khz,我們采用44.1khz的采樣率,結(jié)果只采樣2次,這樣保真性就很差。而采用了SBR技術(shù),SBR把頻譜切割開來(lái),低頻單獨(dú)編碼保存主要成分, 高頻單獨(dú)放大編碼保存音質(zhì),這樣高頻就增加了采樣。這樣的好處,一是減少了碼率,二是提高了音頻的質(zhì)量。

AAC HE V2 : AAC + SBR + PS
PS(Parametric Stereo)是雙聲道分別保存,一個(gè)聲道完整保存,另一個(gè)只存差異的,參數(shù)的部分。因?yàn)閮蓚€(gè)聲道相關(guān)性非常強(qiáng),我們可以通過(guò)某種函數(shù),完全恢復(fù)以前的聲音?;谶@些原因,所以他只要存一些參數(shù)就可以了。


AAC優(yōu)點(diǎn)
①提升的壓縮率:可以以更小的文件大小獲得更高的音質(zhì);
②支持多聲道:可提供最多48個(gè)全音域聲道;
③更高的解析度:最高支持96KHz的采樣頻率;
④提升的解碼效率:解碼播放所占的資源更少;

AAC編解碼庫(kù)
Libfdk_AAC > ffmpeg AAC > libaac > libvo_aacenc

AAC編碼使用

使用ffmpeg對(duì)音頻進(jìn)行AAC編碼

不好意思,前段時(shí)間想采用ffmpeg去對(duì)AAC進(jìn)行編碼,結(jié)果開發(fā)過(guò)程中,發(fā)現(xiàn)ffmpeg avcodec_encode_audio2返回-22,導(dǎo)致編碼不成功,一直找不到原因。后期修改好了,我一定在文章和項(xiàng)目中補(bǔ)上。

使用MediaCodec對(duì)音頻進(jìn)行AAC硬編碼

????????Android中可以使用MediaCodec來(lái)訪問(wèn)底層的媒體編解碼器,可以對(duì)媒體進(jìn)行編/解碼。
????????舉例,比如之前文章《Android 音視頻之音頻錄制》,我們使用AudioRecord錄制了一個(gè)pcm文件。我們要將文件數(shù)據(jù)進(jìn)行AAC編碼,需要先初始化一個(gè)MediaCodec對(duì)象,設(shè)置他的MediaFormat為MediaFormat.MIMETYPE_AUDIO_AAC。如果想使用其他壓縮編碼,類似。代碼如下:

    /**
     * 初始化AAC編碼器
     */
    private void initAACMediaEncode() {
        try {
            //參數(shù)對(duì)應(yīng)-> mime type、采樣率、聲道數(shù)
            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數(shù)據(jù)傳入到下面這個(gè)方法中進(jìn)行AAC編碼。

    /**
     * 編碼,得到{@link #encodeType}格式的音頻文件,并保存到{@link #dstPath}
     * @param data
     */
    public void encodeData(byte[] data){
        //dequeueInputBuffer(time)需要傳入一個(gè)時(shí)間值,-1表示一直等待,0表示不等待有可能會(huì)丟幀,其他表示等待多少毫秒
        int inputIndex = mediaEncode.dequeueInputBuffer(-1);//獲取輸入緩存的index
        if (inputIndex >= 0) {
            ByteBuffer inputByteBuf = encodeInputBuffers[inputIndex];
            inputByteBuf.clear();
            inputByteBuf.put(data);//添加數(shù)據(jù)
            inputByteBuf.limit(data.length);//限制ByteBuffer的訪問(wèn)長(zhǎng)度
            mediaEncode.queueInputBuffer(inputIndex, 0, data.length, 0, 0);//把輸入緩存塞回去給MediaCodec
        }

        int outputIndex = mediaEncode.dequeueOutputBuffer(encodeBufferInfo, 0);//獲取輸出緩存的index
        while (outputIndex >= 0) {
            //獲取緩存信息的長(zhǎng)度
            int byteBufSize = encodeBufferInfo.size;
            //添加ADTS頭部后的長(zhǎng)度
            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個(gè)byte,并寫入dst下
            標(biāo)從offset到offset + length的區(qū)域
             */
            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);
        }
    }

如果是錄制過(guò)程中,可以使用

audioRecord.read(audiodata, 0, bufferSizeInBytes);

方法拉取pcm數(shù)據(jù),把數(shù)據(jù)傳入到編碼方法中,可以一邊錄制一邊編碼。

如果是已經(jīng)錄制好的pcm文件,同樣可以把文件轉(zhuǎn)換成流數(shù)據(jù),再編碼。

    /**
     * 開始編碼
     * PCM數(shù)據(jù)在編碼成想要得到的{@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();
    }

最后可以在目標(biāo)路徑下得到一個(gè)編碼格式為aac的文件,可以直接播放。

如果我們想把m4a,mp3的文件,進(jìn)行aac編碼。那就需要進(jìn)行轉(zhuǎn)碼,我們需要先對(duì)其進(jìn)行解碼成pcm數(shù)據(jù),再進(jìn)行編碼。后面我會(huì)在《Android 音視頻之音頻編碼轉(zhuǎn)換》中簡(jiǎn)單介紹。

github項(xiàng)目地址

未完待更新...

上一篇:《Android 音視頻之音頻編碼》
下一篇:《Android 音視頻之音頻編碼轉(zhuǎn)換》

有問(wèn)題的地方請(qǐng)大家?guī)兔χ赋觯x謝。
持續(xù)更新中...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 介紹 AAC(Advanced Audio Coding),中文稱為“高級(jí)音頻編碼”,出現(xiàn)于1997年,基于 MP...
    sxyxsp123閱讀 8,002評(píng)論 0 7
  • 前言 本篇開始講解在Android平臺(tái)上進(jìn)行的音頻編輯開發(fā),首先需要對(duì)音頻相關(guān)概念有基礎(chǔ)的認(rèn)識(shí)。所以本篇要講解以下...
    Ihesong閱讀 8,040評(píng)論 2 18
  • 音頻技術(shù)開發(fā),我們得對(duì)聲音有所了解,掌握音頻的基礎(chǔ)知識(shí),這才能更好地去做技術(shù)開發(fā)。首先介紹音頻基礎(chǔ)知識(shí),然后介紹音...
    安仔夏天勤奮閱讀 7,718評(píng)論 3 18
  • 手機(jī)里總是有些不愿刪掉的歌,也許不再那么喜歡,可是始終不忍丟棄。 你有沒(méi)有改不掉的習(xí)慣,比如說(shuō),因?yàn)槟臣掳疽梗?..
    長(zhǎng)亭微雨閱讀 336評(píng)論 0 0
  • 你是否在某段時(shí)期,內(nèi)心感覺(jué)極度壓抑,辦事處處碰壁不順。人生經(jīng)歷這么些許,雖然知道以后總會(huì)有風(fēng)光精彩,普希金的假如生...
    蕭能志閱讀 267評(píng)論 0 0

友情鏈接更多精彩內(nèi)容