FAAD庫實現RAW格式AAC封裝成ADTS格式
阿新 • • 發佈:2018-11-11
技術在於交流、溝通,轉載請註明出處並保持作品的完整性。
原文:https://blog.csdn.net/hiwubihe/article/details/81260980
[音訊編解碼系列文章]
- 音訊編解碼基礎
- FFMPEG實現音訊重取樣
- FFMPEG實現PCM編碼(採用封裝格式實現)
- FFMPEG實現PCM編碼(不採用封裝格式實現)
- FAAC庫實現PCM編碼
- FAAD庫實現RAW格式AAC解碼
- FAAD庫實現RAW格式AAC封裝成ADTS格式
- FAAD庫實現ADTS格式解碼
- FFMPEG實現對AAC解碼(採用封裝格式實現)
- FFMPEG實現對AAC解碼(不採用封裝格式實現)
AAC原始檔案格式一般播放器播放不了,資料分析工具也分析不了,需要把AAC原始檔案打包成網路傳輸格式如ADTS等。本篇介紹用FAAD解碼庫,把RAW的AAC檔案重新打包成ADTS格式的AAC,一般播放器能成功播放。
DEMO程式碼如下:
/******************************************************************************* Copyright (c) wubihe Tech. Co., Ltd. All rights reserved. -------------------------------------------------------------------------------- Date Created: 2014-10-25 Author: wubihe QQ:1269122125 Email:
[email protected] Description: 原始AAC音訊流,格式沒有打包ADTS或者ADIF,一般播放器放不了,需要 解碼器解碼 區別出一幀一幀的音訊,然後打包成ADTS格式 -------------------------------------------------------------------------------- Modification History DATE AUTHOR DESCRIPTION -------------------------------------------------------------------------------- ********************************************************************************/ #include <stdio.h> #include <cstddef> #include <string> #include <math.h> #include "faad.h" //待解碼檔案 #define DECODE_FILE_NAME ("huangdun_uadts.aac") //封裝成ADTS格式的AAC檔案 #define DECODE_OUTPUT_FILE ("huangdun_adts.aac") #define MAX_CHANNELS (8) #define MAXWAVESIZE (4294967040LU) #define ADTS_HEAD_LEN (7) #define min(a,b) (((a) < (b)) ? (a) : (b)) typedef struct { //當前快取總資料量 long bytes_into_buffer; //當前快取已經消耗資料量 long bytes_consumed; //整個檔案資料使用量 long file_offset; //快取 unsigned char *buffer; //檔案結束標誌 int at_eof; //檔案操作控制代碼 FILE *infile; } aac_buffer; //aac資料快取 aac_buffer g_AacBuffer; static int fill_buffer(aac_buffer *b) { int bread; //解析消耗資料 if (b->bytes_consumed > 0) { //有剩餘資料 向前面移動 if (b->bytes_into_buffer) { memmove((void*)b->buffer, (void*)(b->buffer + b->bytes_consumed), b->bytes_into_buffer*sizeof(unsigned char)); } if (!b->at_eof) { bread = fread((void*)(b->buffer + b->bytes_into_buffer), 1, b->bytes_consumed, b->infile); if (bread != b->bytes_consumed) b->at_eof = 1; b->bytes_into_buffer += bread; } b->bytes_consumed = 0; if (b->bytes_into_buffer > 3) { if (memcmp(b->buffer, "TAG", 3) == 0) b->bytes_into_buffer = 0; } if (b->bytes_into_buffer > 11) { if (memcmp(b->buffer, "LYRICSBEGIN", 11) == 0) b->bytes_into_buffer = 0; } if (b->bytes_into_buffer > 8) { if (memcmp(b->buffer, "APETAGEX", 8) == 0) b->bytes_into_buffer = 0; } } return 1; } static void advance_buffer(aac_buffer *b, int bytes) { b->file_offset += bytes; b->bytes_consumed = bytes; b->bytes_into_buffer -= bytes; if (b->bytes_into_buffer < 0) b->bytes_into_buffer = 0; } static int adts_sample_rates[] = {96000,88200,64000,48000,44100,32000,24000,22050,16000,12000,11025,8000,7350,0,0,0}; static int FindAdtsSRIndex(int sr) { int i; for (i = 0; i < 16; i++) { if (sr == adts_sample_rates[i]) return i; } return 16 - 1; } unsigned bool MakeAdtsHeader(unsigned char *pHead,int *HeadSize,int iProfile,int iSampleRate,int iChan ,int iFramelen) { unsigned char *data = pHead; int iHeadLen = *HeadSize; if(iHeadLen<7) { return false; } *HeadSize = iHeadLen = 7; int profile = (iProfile - 1) & 0x3; int sr_index = FindAdtsSRIndex(iSampleRate); int framesize = iFramelen + iHeadLen; memset(data, 0, iHeadLen); data[0] += 0xFF; /* 8b: syncword */ data[1] += 0xF0; /* 4b: syncword */ /* 1b: mpeg id = 0 */ /* 2b: layer = 0 */ data[1] += 1; /* 1b: protection absent */ data[2] += ((profile << 6) & 0xC0); /* 2b: profile */ data[2] += ((sr_index << 2) & 0x3C); /* 4b: sampling_frequency_index */ /* 1b: private = 0 */ data[2] += ((iChan >> 2) & 0x1); /* 1b: channel_configuration */ data[3] += ((iChan << 6) & 0xC0); /* 2b: channel_configuration */ /* 1b: original */ /* 1b: home */ /* 1b: copyright_id */ /* 1b: copyright_id_start */ data[3] += ((framesize >> 11) & 0x3); /* 2b: aac_frame_length */ data[4] += ((framesize >> 3) & 0xFF); /* 8b: aac_frame_length */ data[5] += ((framesize << 5) & 0xE0); /* 3b: aac_frame_length */ data[5] += ((0x7FF >> 6) & 0x1F); /* 5b: adts_buffer_fullness */ data[6] += ((0x7FF << 2) & 0x3F); /* 6b: adts_buffer_fullness */ /* 2b: num_raw_data_blocks */ return true; } int main() { memset(&g_AacBuffer, 0, sizeof(aac_buffer)); g_AacBuffer.infile = fopen(DECODE_FILE_NAME, "rb"); if (g_AacBuffer.infile == NULL) { /* unable to open file */ fprintf(stderr, "Error opening file: %s\n", DECODE_FILE_NAME); return 1; } fseek(g_AacBuffer.infile, 0, SEEK_END); double dTotalFileSize = ftell(g_AacBuffer.infile); fseek(g_AacBuffer.infile, 0, SEEK_SET); if (!(g_AacBuffer.buffer = (unsigned char*)malloc(FAAD_MIN_STREAMSIZE*MAX_CHANNELS))) { fprintf(stderr, "Memory allocation error\n"); return 0; } memset(g_AacBuffer.buffer, 0, FAAD_MIN_STREAMSIZE*MAX_CHANNELS); size_t sRealRead = fread(g_AacBuffer.buffer, 1, FAAD_MIN_STREAMSIZE*MAX_CHANNELS, g_AacBuffer.infile); g_AacBuffer.bytes_into_buffer = sRealRead; g_AacBuffer.bytes_consumed = 0; g_AacBuffer.file_offset = 0; if (sRealRead != FAAD_MIN_STREAMSIZE*MAX_CHANNELS) { g_AacBuffer.at_eof = 1; } //判斷檔案格式 int iAacType = 0; if ((g_AacBuffer.buffer[0] == 0xFF) && ((g_AacBuffer.buffer[1] & 0xF6) == 0xF0)) { iAacType = 1; fprintf(stderr, "AAC file Format is: ADTS\n"); } else if (memcmp(g_AacBuffer.buffer, "ADIF", 4) == 0) { iAacType = 2; fprintf(stderr, "AAC file Format is: ADIF\n"); } else { iAacType = 0; fprintf(stderr, "AAC file Format is: RAW AAC\n"); } FILE *pOutputFile = fopen(DECODE_OUTPUT_FILE, "wb"); if(pOutputFile == NULL) { fprintf(stderr, "Error opening file: %s\n", DECODE_OUTPUT_FILE); return 1; } //開啟解碼器 NeAACDecHandle hDecoder = NeAACDecOpen(); //設定解碼器音訊引數 該設定對於原始AAC資料是必須的 否則解碼器不知道音訊封裝資訊 //對於ADTS封裝的AAC 不需要設定 解碼器可以從視訊資料中獲取 NeAACDecConfigurationPtr pDecodeConfig = NeAACDecGetCurrentConfiguration(hDecoder); pDecodeConfig->defSampleRate = 44100; pDecodeConfig->defObjectType = LC; pDecodeConfig->outputFormat = FAAD_FMT_16BIT; pDecodeConfig->downMatrix = 0; pDecodeConfig->useOldADTSFormat = 0; NeAACDecSetConfiguration(hDecoder, pDecodeConfig); long lRealUse =0; unsigned long lRealSampleRate ; unsigned char ucRealChans ; unsigned char ucRealFormat = FAAD_FMT_16BIT; //資料初始化 if ((lRealUse = NeAACDecInit(hDecoder, g_AacBuffer.buffer, g_AacBuffer.bytes_into_buffer, &lRealSampleRate, &ucRealChans)) < 0) { /* If some error initializing occured, skip the file */ fprintf(stderr, "Error initializing decoder library.\n"); if (g_AacBuffer.buffer) { free(g_AacBuffer.buffer); } NeAACDecClose(hDecoder); fclose(g_AacBuffer.infile); return 1; } //拋棄已經使用過的資料 advance_buffer(&g_AacBuffer, lRealUse); //空的快取填充新的資料 fill_buffer(&g_AacBuffer); NeAACDecFrameInfo frameInfo; void *pSampleBuffer = NULL; int iAdtsHeadLen = ADTS_HEAD_LEN; unsigned char szAdtsHead[ADTS_HEAD_LEN]; int iAacProfile = LC; int iOldPercent = 0; do { pSampleBuffer = NeAACDecDecode(hDecoder, &frameInfo, g_AacBuffer.buffer, g_AacBuffer.bytes_into_buffer); //原始AAC格式 if(iAacType == 0) { MakeAdtsHeader(szAdtsHead,&iAdtsHeadLen,iAacProfile,frameInfo.samplerate,frameInfo.channels ,frameInfo.bytesconsumed); fwrite(szAdtsHead, 1, iAdtsHeadLen, pOutputFile); fwrite(g_AacBuffer.buffer, 1, frameInfo.bytesconsumed, pOutputFile); } //原本就是ADTS格式 else if (iAacType == 1) { MakeAdtsHeader(szAdtsHead,&iAdtsHeadLen,iAacProfile,frameInfo.samplerate,frameInfo.channels ,frameInfo.bytesconsumed - 7); fwrite(szAdtsHead, 1, iAdtsHeadLen, pOutputFile); fwrite(g_AacBuffer.buffer, 1, frameInfo.bytesconsumed - 7, pOutputFile); } //拋棄解碼消耗的快取 advance_buffer(&g_AacBuffer, frameInfo.bytesconsumed); if (frameInfo.error > 0) { fprintf(stderr, "Error: %s\n",NeAACDecGetErrorMessage(frameInfo.error)); } int iPercent = min((int)((g_AacBuffer.file_offset*100))/dTotalFileSize, 100); if (iPercent > iOldPercent) { iOldPercent = iPercent; fprintf(stderr, "%d%% decoding %s.\n", iOldPercent, DECODE_FILE_NAME); } /* fill buffer */ fill_buffer(&g_AacBuffer); if (g_AacBuffer.bytes_into_buffer == 0) { pSampleBuffer = NULL; /* to make sure it stops now */ } } while (pSampleBuffer != NULL); NeAACDecClose(hDecoder); fclose(pOutputFile); fclose(g_AacBuffer.infile); if (g_AacBuffer.buffer) { free(g_AacBuffer.buffer); } printf("Decode Raw Aac Success!\n"); getchar(); return 0; }
程式執行完成 生成huangdun_adts.aac用elecard stream analysis分析如下: