技术在于交流、沟通,转载请注明出处并保持作品的完整性。
原文: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:1269122125@qq.com
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分析如下: