// 主程式: play.cpp
#include <memory>
#include <stdio.h>
#include <sys/mman.h>
#include <fdk-aac/aacdecoder_lib.h>
#include "alsa/asoundlib.h"
struct PCM16 {
int16_t *data;
int length;
int channel;
int fs;
};
const PCM16 zeroPCM16 = {.data = (int16_t *)0, .length = 0, .channel = 0, .fs = 0};
const uint32_t fsHz[16] = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 0, 0, 0};
const uint32_t multiChannel[16] = {0, 1, 2, 3, 4, 5 + 1, 7 + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
struct ChunkWAV { // sizeof(ChunkWAV) = 8
char id[4];
int32_t length;
char *textID() {
static char str[5];
memcpy(str, id, 4);
return str;
}
bool is_id(const char *name) {
if (*name == 0) return false;
for (int i = 0; i < 4; i ++) {
if (name[i] == 0) break; // end of String
if (name[i] != id[i]) return false;
}
return true;
}
};
struct StructWAV { // sizeof(StructWAV) = 36
ChunkWAV title;
char wave[4];
ChunkWAV fmt;
int16_t pcmType , channels;
int32_t sampleRate, byteRate;
int16_t frameBytes, bitsData;
bool is_wav() {
if (! title.is_id("RIFF")) return false;
static const char *waveID = "WAVE";
for (int i = 0; i < 4; i ++) { if (wave[i] != waveID[i]) return false; }
return fmt.is_id("fmt");
}
};
struct WavMMAP {
void *mmapFile = MAP_FAILED; // to management mmap file
int32_t length = 0;// mmap file length
int32_t position = 0; // trace file position
int32_t audioStart = 0; // start position of audio data
bool syncFound = false;
int fs = 8000;
int channel = 2;
int frameBytes = 4;
virtual ~WavMMAP() { if (mmapFile != MAP_FAILED) munmap(mmapFile, length); }// auto unmap
virtual PCM16 decode() { return wavDecode(); }
PCM16 wavDecode() {
if (position < length) {
auto src = (unsigned char *)mmapFile + position;// byte pointer
const uint32_t maxSteps = length - position;// availabe bytes in file
int tempSteps = 1024 * frameBytes;// number of byte to return
if (tempSteps > maxSteps) tempSteps = maxSteps;
position += tempSteps;
return PCM16 {
.data = (int16_t *)src,
.length = tempSteps / frameBytes,
.channel = channel,
.fs = fs
};
}
return zeroPCM16;
}
WavMMAP(){ }// basic constructor
WavMMAP(const char *filename) {
if (mmapFile != MAP_FAILED) munmap(mmapFile, length);
int fd = open(filename, O_RDONLY);
if (fd < 0) {
printf("%s => not found.\n", filename);
} else {
int fileLength = lseek(fd, 0l, SEEK_END);
mmapFile = mmap(0, fileLength, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
length = fileLength;
auto header = (StructWAV *)mmapFile;
if (header->is_wav()) { // wav file
fs = header->sampleRate;
channel = header->channels;
frameBytes = header->frameBytes;
audioStart = sizeof(StructWAV);
int offset = sizeof(ChunkWAV);
while (audioStart < length) {
auto *chunkList = (ChunkWAV *)((unsigned char *)mmapFile + audioStart);
if (chunkList->is_id("data")) {
audioStart += offset;
break;
}
audioStart += offset + chunkList->length;
}
} else {
auto uid = (unsigned char *)mmapFile;
if (uid[0] == 0xff && (uid[1] >> 4) == 0xf) {
syncFound = true;
if (uid[1] == 0xf1 || uid[1] == 0xf9) {// aac file
fs = fsHz[(uid[2] & 0x3e) >> 2];
channel = multiChannel[((uid[2] & 1) << 2) | (uid[3] >> 6)];
frameBytes = channel * 2; // 16bits
}
}
}
}
position = audioStart;
}
};
struct FdkAAC: WavMMAP {
int16_t pcmdist[1152 * 5];
HANDLE_AACDECODER decoder = aacDecoder_Open(TT_MP4_ADTS, 1);
PCM16 decode( ) override {
if (! syncFound) {// fall back to use super.wavDecoder()
return wavDecode();
}
if (position < length) {
unsigned char *src[] = { (unsigned char *)mmapFile + position };
const uint32_t maxSteps = length - position;
uint32_t tempSteps = maxSteps;
aacDecoder_Fill(decoder, src, &maxSteps, &tempSteps);
int result = (int)aacDecoder_DecodeFrame(decoder, pcmdist, sizeof(pcmdist), 0);
position += maxSteps - tempSteps;
if (result == AAC_DEC_OK) {
CStreamInfo *info = aacDecoder_GetStreamInfo(decoder);
return PCM16 {
.data = pcmdist,
.length = info->frameSize,
.channel = info->numChannels,
.fs = info->sampleRate
};
}
}
return zeroPCM16;
}
~FdkAAC() { aacDecoder_Close(decoder); }
FdkAAC(const char *filename): WavMMAP(filename) { }
};
typedef std::unique_ptr<WavMMAP> MyDecoder;
int main(int argc, char const *argv[]) {
const char* filename[2] = {"test.aac", "test.wav"};
int len = sizeof(filename) / sizeof(filename[0]);
for (int i = 0; i < len; i++) {
auto play = MyDecoder(new FdkAAC(filename[i]));
auto frame = play->decode();
if (frame.length == 0) break;
snd_pcm_t *handle;
if (snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0) == 0) { // hw:0,0 , default
if (snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, frame.channel, frame.fs, 1, frame.fs / 4) == 0) {
do {
int err = snd_pcm_writei(handle, frame.data, frame.length);
if (err < 0) {// alsa try to recover
if (snd_pcm_recover(handle, err, 0) < 0) {
printf("alsa can't recover\n");
break;
}
}
frame = play->decode();
} while (frame.length > 0);
}
snd_pcm_close(handle);
}
}
return 0;
}
編譯並執行:
g++ play.cpp -lfdk-aac -lasound && ./a.out
沒有留言:
張貼留言