簡單來講,aac是一種音頻編碼格式,須要解碼後才能用於音頻輸出。aac編碼格式,已是一種很常見的音頻編碼格式,以致於不少系統都支持aac的編解碼,好比iOS上的AudioConverterRef接口、Android上的MediaCodec接口等。git
可是,不要覺得用了系統的接口就是用了硬件解碼,由於,這個系統接口有可能最終仍是使用軟件解碼,好比有些手機(好比小米)的MediaCodec對於acc的解碼,就是軟解碼,用的是google提供的OMX.google.aac.decoder,不要覺得用了系統接口解碼的速度就飛快了,要真是硬件支持才行的。macos
那什麼是硬解碼,什麼是軟解碼呢?很簡單,若是硬件芯片專門來作解碼,就是硬解碼,好比使用GPU或DSP之類的模塊來處理解碼就是硬解碼(通常不會使用CPU),這須要硬件上的支持。而軟解碼就是用軟件來解碼了,就至關你寫一個程序來解碼,使用CPU來作事。優缺點方面,硬解速度快功耗低但兼容性差,軟解速度慢功耗高但兼容性好。session
但話說回來,不是非得要硬解碼的,對於音頻來講,當今的手機,除非你要大量的音效運算或合成處理,不然通常的解碼,用軟解碼就足夠了,根本就不須要硬解,硬解是視頻的事情。curl
對於aac的解碼,使用FFmpeg也是一個選擇,但若是隻爲了解碼aac而用FFmpeg,就有點大材小用了,要應對比較複雜的接口調用,另外FFmpeg體積也比較大(即使裁剪後可使FFmpeg編譯出來的庫小不少),那麼,是否有更加簡單一點的解碼庫可使用呢?google
這個開源庫就是faad。編碼
本文講解使用faad,把aac音頻解碼成pcm數據,並以wav來封裝。 pcm數據,簡單來講就是未經壓縮的音頻數據,能夠直接交給音頻輸出模塊(好比揚聲器)進行播放,而wav文件通常存放pcm數據。url
git clone git://git.code.sf.net/p/faac/faad2 faac-faad2.net
文件結構大概是這樣的:
rest
這個開源項目,彷佛一直有維護與更新(請以最新的爲準):
code
執行如下指令,生成configure配置文件,以及makefile編譯腳本,而後,再make出faad的庫文件。
aclocal autoconf autoheader libtoolize --force automake --add-missing ./configure make
因爲只考慮在macos(由於個人是macos)上運行,並且是在mac系統上編譯,因此confiure時並不須要指定特定的參數。
以上命令,使用automake來生成編譯腳本(makefile),若是你發現這類指令執行不了,那有可能尚未正確安裝,能夠查閱相關的知識,也能夠參考如下的內容,這是小程在網上摘錄到的內容:
====install autoconf and automake(摘錄) curl -O http://mirrors.kernel.org/gnu/m4/m4-1.4.13.tar.gz tar -xzvf m4-1.4.13.tar.gz cd m4-1.4.13 ./configure --prefix=/usr/local make sudo make install cd .. curl -O http://mirrors.kernel.org/gnu/autoconf/autoconf-2.65.tar.gz tar -xzvf autoconf-2.65.tar.gz cd autoconf-2.65 ./configure --prefix=/usr/local # ironic, isn't it? make sudo make install cd .. # here you might want to restart your terminal session, to ensure the new autoconf is picked up and used in the rest of the script curl -O http://mirrors.kernel.org/gnu/automake/automake-1.11.tar.gz tar xzvf automake-1.11.tar.gz cd automake-1.11 ./configure --prefix=/usr/local make sudo make install cd .. curl -O http://mirrors.kernel.org/gnu/libtool/libtool-2.2.6b.tar.gz tar xzvf libtool-2.2.6b.tar.gz cd libtool-2.2.6b ./configure --prefix=/usr/local make sudo make install
最終,生成faad庫文件:
經過lipo來查看庫文件支持的指令集:
這裏演示一下,把一個aac文件解碼成pcm數據,並用wav容器來封裝。
demo的文件結構:
demo的代碼:
#include "libfaad/include/faad.h" #include <stdio.h> #include <string.h> #include <stdlib.h> struct WavFileHeader { char id[4]; // should always contain "RIFF" int totallength; // total file length minus 8 char wavefmt[8]; // should be "WAVEfmt " int format; // 16 for PCM format short pcm; // 1 for PCM format short channels; // channels int frequency; // sampling frequency int bytes_per_second; short bytes_by_capture; short bits_per_sample; char data[4]; // should always contain "data" int bytes_in_data; }; void write_wav_header(FILE* file, int totalsamcnt_per_channel, int samplerate, int channels){ struct WavFileHeader filler; strcpy(filler.id, "RIFF"); filler.bits_per_sample = 16; filler.totallength = (totalsamcnt_per_channel * channels * filler.bits_per_sample/8) + sizeof(filler) - 8; //81956 strcpy(filler.wavefmt, "WAVEfmt "); filler.format = 16; filler.pcm = 1; filler.channels = channels; filler.frequency = samplerate; filler.bytes_per_second = filler.channels * filler.frequency * filler.bits_per_sample/8; filler.bytes_by_capture = filler.channels*filler.bits_per_sample/8; filler.bytes_in_data = totalsamcnt_per_channel * filler.channels * filler.bits_per_sample/8; strcpy(filler.data, "data"); fwrite(&filler, 1, sizeof(filler), file); } int main(int argc, char *argv[]) { printf("hello faad\n"); NeAACDecHandle faadhandle = NeAACDecOpen(); if (faadhandle) { printf("aacopen ok\n"); const char* aacfile = "aac20s.aac"; FILE* file = fopen(aacfile, "rb"); if (file) { printf("fopen aac ok\n"); fseek(file, 0, SEEK_END); long filelen = ftell(file); fseek(file, 0, SEEK_SET); unsigned char* filebuf = (unsigned char*)malloc(filelen); int len = fread(filebuf, 1, filelen, file); fclose(file); unsigned long samplerate = 0; unsigned char channel = 0; int ret = NeAACDecInit(faadhandle, filebuf, len, &samplerate, &channel); if (ret >= 0) { printf("aacinit ok: sam=%lu, chn=%d\n", samplerate, channel); NeAACDecFrameInfo frameinfo; unsigned char* curbyte = filebuf; unsigned long leftsize = len; const char* wavename = "out.wav"; FILE* wavfile = fopen(wavename, "wb"); if (wavfile) { int wavheadsize = sizeof(struct WavFileHeader); fseek(wavfile, wavheadsize, SEEK_SET); int totalsmp_per_chl = 0; void* out = NULL; while (out = NeAACDecDecode(faadhandle, &frameinfo, curbyte, leftsize)) { printf("decode one frame ok: sam:%ld, chn=%d, samplecount=%ld, obj_type=%d, header_type=%d, consumed=%ld\n", frameinfo.samplerate, frameinfo.channels, frameinfo.samples, frameinfo.object_type, frameinfo.header_type, frameinfo.bytesconsumed); curbyte += frameinfo.bytesconsumed; leftsize -= frameinfo.bytesconsumed; fwrite(out, 1, frameinfo.samples*2, wavfile); // frameinfo.samples是全部聲道數的樣本總和;16bit位深 totalsmp_per_chl += frameinfo.samples / frameinfo.channels; } printf("aac decode done, totalsmp_per_chl=%d\n", totalsmp_per_chl); fseek(wavfile, 0, SEEK_SET); write_wav_header(wavfile, totalsmp_per_chl, (int)samplerate, (int)channel); fclose(wavfile); } } free(filebuf); } NeAACDecClose(faadhandle); } return 0; }
faad的調用,跟通常的c庫的使用同樣,先建立一個handel,而後init,以後就是反覆的decode,最後是close。
以上代碼保存到aac2pcm.c文件,而後,makefile編譯文件能夠這樣寫(或者直接用gcc來編譯):
out=aac2pcm obj=aac2pcm.c $(out):$(obj) gcc -o $(out) $(obj) -lfaad -L./libfaad clean: rm -rf $(out) *.o
執行這個程序,部分輸出:
最後的輸出:
此時,在執行目錄中,out.wav已經生成。那用faad跟用FFmpeg來解碼aac,出來的wav有多大的差異呢?
與FFmpeg的解碼做一個對比。
可使用ffmpeg命令來解碼一個aac文件:
ffmpeg -i aac20s.aac out_ff.wav
能夠看到,faad與FFmpeg解碼出來的wav文件的大小,有一點差異:
使用Audition,來對比FFmpeg與faad解碼後的pcm數據:
基本看不出差異。
至此,使用faad來解碼的流程就介紹完畢了。另外,對於aac的編碼,可使用faac或fdk-aac、neroaac,或硬編等,這些小程之後再做介紹。
總結一下,本文介紹瞭如何經過faad來解碼aac格式的音頻,並保存成wav文件;對於faad的編譯與調用都做了相應介紹,最後還對比了FFmpeg與faad解碼出來的數據差別。