天天看点

aac文件转码为wav文件

本文讲解把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 

文件结构大概是这样的: 

aac文件转码为wav文件

这个开源项目,似乎一直有维护与更新: 

aac文件转码为wav文件

(2)编译faad

执行以下指令,生成configure与makefile,并make出faad的库文件。

aclocal
autoconf
autoheader
libtoolize --force 
automake --add-missing
./configure
make      

生成的库文件: 

aac文件转码为wav文件

通过lipo来查看库文件支持的指令集: 

aac文件转码为wav文件

由于只考虑在macos上运行,而且是在mac系统上编译,所以confiure时并不需要指定特定的参数。

小白:那一堆命令是什么?

花满楼:这个,你问一下西门吹雪,他是负责编译环境的。

小白:…

(3)调用faad

这里演示把一个aac文件解码成pcm数据,并用wav容器来封装。

demo的文件结构: 

aac文件转码为wav文件

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封装格式,都适用。

执行这个程序,部分输出: 

aac文件转码为wav文件

最后的输出: 

aac文件转码为wav文件

(4)pcm数据观察

与ffmpeg的解码作一个对比。

可以使用ffmpeg命令来解码一个aac文件: 

ffmpeg -i aac20s.aac out_ff.wav

可以看到faad与ffmpeg转码出来的wav的文件大小,有一点差别: 

aac文件转码为wav文件

ffmpeg与faad解码后的pcm数据,对比图是这样的: 

aac文件转码为wav文件

基本看不出差别。