0. AAC介紹
AAC音頻文件的每一幀都由一個ADTS頭和AAC ES(AAC音頻數據)組成。
以下是wiki的介紹:https://wiki.multimedia.cx/index.php?title=ADTS
0.1 其包括固定頭信息:adts_fixed_header()
ADTS頭的固定頭信息在每個幀中都是一樣的。
syncword:幀同步標識一個幀的開始,固定爲0xFFF
ID:MPEG 標示符。0表示MPEG-4,1表示MPEG-2
layer:固定爲’00’
protection_absent:標識是否進行誤碼校驗。0表示有CRC校驗,1表示沒有CRC校驗
profile:標識使用哪個級別的AAC。1: AAC Main 2:AAC LC (Low Complexity) 3:AAC SSR (Scalable Sample Rate) 4:AAC LTP (Long Term Prediction)
sampling_frequency_index:標識使用的採樣率的下標
private_bit:私有位,編碼時設置爲0,解碼時忽略
channel_configuration:標識聲道數
original_copy:編碼時設置爲0,解碼時忽略
home:編碼時設置爲0,解碼時忽略
0.2 可變頭信息:adts_variable_header()
copyrighted_id_bit:編碼時設置爲0,解碼時忽略
copyrighted_id_start:編碼時設置爲0,解碼時忽略
aac_frame_length:ADTS幀長度包括ADTS長度和AAC聲音數據長度的和(單位是字節)。即 aac_frame_length = (protection_absent == 0 ? 9 : 7) + audio_data_length
adts_buffer_fullness:固定爲0x7FF。表示是碼率可變的碼流
number_of_raw_data_blocks_in_frame:表示當前幀有number_of_raw_data_blocks_in_frame + 1 個原始幀(一個AAC原始幀包含一段時間內1024個採樣及相關數據)。
0.3 音頻碼流在視頻播放器的位置
1. 代碼
extern "C"
{
#ifdef __cplusplus
#define __STDC_CONSTANT_MACROS
#endif
}
extern "C" {
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
}
int getADTSframe(unsigned char* buffer, int buf_size, unsigned char* data, int* data_size) {
// buffer是每次傳進來的1024*1024字節的數據
// buf_size是成功讀取1個字節的總的個數,這裏讀取成功即1024*1024,
//也可能末尾讀着讀着沒了,那就沒這麼多
// data即傳進來的aacframe,空間大小是1024*5個字節
// data_size是aac_frame_length:ADTS幀長度包括ADTS長度和AAC聲音數據長度的和
//
int size = 0;
// 避免空指針,這個代碼是有意義的,在實際的android開發中,
// 有時候遇到系統穩定性問題,比如死機,解dump會發現空指針,
// 我們找到函數的位置,然後加這個避免空指針的代碼即可解決.
if (!buffer || !data || !data_size) {
return -1;
}
while (1) {
if (buf_size < 7) {
//ADTS頭一般是7個字節,如果對數據進行CRC校驗,還需要2個Byte的校驗碼,
//所以ADTS頭實際長度是7個字節或者9個字節,
//所以若傳進來的buf_size<7,那麼也就沒必要繼續解析文件頭了
return -1;
}
//Sync words
if ((buffer[0] == 0xff) && ((buffer[1] & 0xf0) == 0xf0)) {
size |= ((buffer[3] & 0x03) << 11); //high 2 bit
size |= buffer[4] << 3; //middle 8 bit
size |= ((buffer[5] & 0xe0) >> 5); //low 3bit
//size存儲的是aac_frame_length,即ADTS長度和AAC聲音數據長度的和
break;
}
--buf_size;
++buffer;
}
if (buf_size < size) {
//數據分割,若小於一個aac_frame_length幀長度,直接return
return 1;
}
memcpy(data, buffer, size);
//由buffer所指內存區域複製size個字節到data所指內存區域
//把重要信息存到aacframe指針裏面
//需要知道profile以及sampling_frequency_index
*data_size = size;
return 0;
}
int simplest_aac_parser(const char* url)
{
int data_size = 0;
int size = 0;
int cnt = 0;
int offset = 0;
//FILE *myout=fopen("output_log.txt","wb+");
FILE* myout = stdout;
unsigned char* aacframe = (unsigned char*)malloc(1024 * 10);
unsigned char* aacbuffer = (unsigned char*)malloc(1024 * 2048);
FILE* ifile = fopen(url, "rb");
if (!ifile) {
printf("Open file error");
return -1;
}
printf("-----+- ADTS Frame Table -+------+\n");
printf(" NUM | Profile | Frequency| Size |\n");
printf("-----+---------+----------+------+\n");
while (!feof(ifile)) {
data_size = fread(aacbuffer + offset, 1, 1024 * 2048 - offset, ifile);
unsigned char* input_data = aacbuffer;
while (1)
{
int ret = getADTSframe(input_data, data_size, aacframe, &size );
if (ret == -1) {
break;
}
else if (ret == 1) {
memcpy(aacbuffer, input_data, data_size);
offset = data_size;
break;
}
char profile_str[10] = { 0 };
char frequence_str[10] = { 0 };
unsigned char profile = aacframe[2] & 0xC0;
profile = profile >> 6;
//獲取標識使用哪個級別的AAC。1: AAC Main
//2:AAC LC (Low Complexity)
//3:AAC SSR (Scalable Sample Rate)
//4:AAC LTP (Long Term Prediction)
switch (profile) {
case 0: sprintf(profile_str, "Main"); break;
case 1: sprintf(profile_str, "LC"); break;
case 2: sprintf(profile_str, "SSR"); break;
default:sprintf(profile_str, "unknown"); break;
}
unsigned char sampling_frequency_index = aacframe[2] & 0x3C;
sampling_frequency_index = sampling_frequency_index >> 2;
//獲取標識使用的採樣率的下標
switch (sampling_frequency_index) {
case 0: sprintf(frequence_str, "96000Hz"); break;
case 1: sprintf(frequence_str, "88200Hz"); break;
case 2: sprintf(frequence_str, "64000Hz"); break;
case 3: sprintf(frequence_str, "48000Hz"); break;
case 4: sprintf(frequence_str, "44100Hz"); break;
case 5: sprintf(frequence_str, "32000Hz"); break;
case 6: sprintf(frequence_str, "24000Hz"); break;
case 7: sprintf(frequence_str, "22050Hz"); break;
case 8: sprintf(frequence_str, "16000Hz"); break;
case 9: sprintf(frequence_str, "12000Hz"); break;
case 10: sprintf(frequence_str, "11025Hz"); break;
case 11: sprintf(frequence_str, "8000Hz"); break;
default:sprintf(frequence_str, "unknown"); break;
}
fprintf(myout, "%5d| %8s| %8s| %5d|\n", cnt, profile_str, frequence_str, size);
data_size -= size;
input_data += size;
cnt++;
}
}
fclose(ifile);
free(aacbuffer);
free(aacframe);
return 0;
}
int main()
{
simplest_aac_parser("nocturne.aac");
return 0;
}
nocturne.aac 音源獲取鏈接: https://github.com/leixiaohua1020/simplest_mediadata_test
2. 結果
本程序的輸入爲一個AAC原始碼流(裸流)的文件路徑,輸出爲該碼流中ADTS frame的統計數據
…
參考鏈接:
- https://www.jianshu.com/p/b5ca697535bd
- https://blog.csdn.net/leixiaohua1020/article/details/50535042?locationNum=3&fps=1