本文講解把aac音頻解碼成pcm資料,并以wav來封裝。
小白:解碼是吧?用你之前介紹的FFmpeg就行啦,它這麼萬能。
花滿樓:FFmpeg可以解碼aac,但如果隻為了解碼aac而用FFmpeg,就有點大材小用了,而且要應對比較複雜的接口調用,另外體積也比較大,即便裁剪後可以讓FFmpeg編譯出來的庫小很多。
小白:是以你不是講FFmpeg?
花滿樓:這裡講faad的使用。
解碼aac,可以使用FFmpeg或者faad(或者使用平台的硬體解碼),這裡介紹faad。
另外,對于aac的編碼,可以使用faac或fdk-aac、neroaac,或硬編等。
(1)下載下傳faad
git clone git://git.code.sf.net/p/faac/faad2 faac-faad2
檔案結構大概是這樣的:
這個開源項目,似乎一直有維護與更新:
(2)編譯faad
執行以下指令,生成configure與makefile,并make出faad的庫檔案。
aclocal
autoconf
autoheader
libtoolize --force
automake --add-missing
./configure
make
生成的庫檔案:
通過lipo來檢視庫檔案支援的指令集:
由于隻考慮在macos上運作,而且是在mac系統上編譯,是以confiure時并不需要指定特定的參數。
小白:那一堆指令是什麼?
花滿樓:這個,你問一下西門吹雪,他是負責編譯環境的。
小白:…
(3)調用faad
這裡示範把一個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;
makefile檔案可以這樣寫(或者直接用gcc來編譯):
out=aac2pcm
obj=aac2pcm.c
$(out):$(obj)
gcc -o $(out) $(obj) -lfaad -L./libfaad
clean:
rm -rf $(out) *.o
對于aac檔案,adts-aac或adif-aac封裝格式,都适用。
執行這個程式,部分輸出:
最後的輸出:
(4)pcm資料觀察
與ffmpeg的解碼作一個對比。
可以使用ffmpeg指令來解碼一個aac檔案:
ffmpeg -i aac20s.aac out_ff.wav
可以看到faad與ffmpeg轉碼出來的wav的檔案大小,有一點差别:
ffmpeg與faad解碼後的pcm資料,對比圖是這樣的:
基本看不出差别。