aac简介
AAC,全称Advanced Audio Coding,高级音频编码,是一种专为声音数据设计的文件压缩格式。
> 提升压缩率:以更小的文件大小获得更高的音质;
> 支持多声道:可提供最多48个全音域声道;
> 更高解析度:最高支持96KHz的采样频率:
> 提升解码效率:解码播放所占的资源更少,
ffmpeg命令:
ffmpeg -i video.mp4 -vn -acodec aac out.aac // 提取
ffplay命令:
ffplay out.aac // 播放
-----------------------------------------------------------------ffmpeg - 提取流程----------------------------------------------------------------
1、打开媒体文件 - avformat_open_input
2、获得码流信息 - avformat_find_stream_info
3、获得音频流 - av_find_best_stream
4、初始化packet - av_init_packet
5、读取packet数据 - av_read_frame
6、释放packet资源 - av_packet_unref
7、关闭媒体文件 - avformat_close_input
----------------------------------------------------------------- ADIF 和 ADTS ----------------------------------------------------------------
AAC音频格式有ADIF和ADTS:
ADIF: Audio Data Interchange Format 音频数据交换格式。
这种格式的特征是可以确定的找到这个音频数据的开始,不需进行在音频数据流中间开始的解码,
即它的解码必须在明确定义的开始处进行。故这种格式常用在磁盘文件中。
ADTS: Audio Data Transport Stream 音频数据传输流。
这种格式的特征是它是一个有同步字的比特流,解码可以在这个流中任何位置开始。它的特征类似于mp3数据流格式。
总而言之,ADTS可以在任意帧解码,也就是说它每一帧都有头信息。
ADIF只有一个统一的头,所以必须得到所有的数据后解码。
且这两种的header的格式也是不同的,目前一般编码后的和抽取出的都是ADTS格式的音频流,
ADTS是帧序列,本身具备流特征,在音频流的传输与处理方面更加合适。
每一帧的ADTS的头文件都包含了音频的采样率,声道,长度等信息,这样解码器才能解析读取。
一般情况下ADTS的头信息都是7个字节,分为2部分:
adts_fixed_header();
adts_variable_header();
其一为固定头信息,紧接着是可变头信息。固定头信息中的数据每一都相同,而可变头信息则在帧与帧之间可变
----------------------------------------------------------------- ADTS fixed header ----------------------------------------------------------------
adts_fixed_header()
{
syncword; // 12 bit
ID; // 1 bit
layer; // 2 bit
protection_absent;
profile;
sampling_frequency_index;
private_bit;
channel_configuration;
original_copy;
home;
}
----------------------------------------------------------------- ADTS variable header ----------------------------------------------------------------
Syntax No.of bits Mnemonic
adts_variable_header()
{
copyright_identification_bit; 1 bslbf
copyright_identification_start; 1 bslbf
aac_frame_length; 13 bslbf
adts_buffer_fullness; 11 bslbf
number_of_raw_data_blocks_in_frame; 2 uimsfb
}
获得 ADTS 头
const int sampleFrequencyTable[] = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350};
int getADTSHeader(char *const adtsHeader, const int dataLength, const int profile, const int samplerate, const int channels)
{
int sampleFrequenceIndex = 3; // 默认使用 48000HZ
int adtslen = dataLength + 7;
int frequencise_size = sizeof(sampleFrequencyTable) / sizeof(sampleFrequencyTable[0]);
int i = 0;
for (int i = 0; i < frequencise_size; i++)
{
if (sampleFrequencyTable[i] == samplerate)
{
sampleFrequenceIndex = i;
break;
}
}
if (i >= frequencise_size)
{
av_log(NULL, AV_LOG_ERROR, "不支持次采样率:%d\n", samplerate);
return -1;
}
// syncword :同步头总是OxFFF, all bits must be 1,代表着一个ADTS帧的开始
adtsHeader[0] = 0xff; // syncword:0xff 高8bits
adtsHeader[1] = 0xf0; // syncword:0xfff 低4bits
adtsHeader[1] |= (0 << 3); // MPEG Version:0 for MPEG-4,1 for MPEG-2 1bit
adtsHeader[1] |= (0 << 1); // Layer:0 2bit
adtsHeader[1] |= 1; // protection absent:1 1bit -- 要不要CRC校验 -- 不要
adtsHeader[2] = (profile << 6); // porfile:profile 2bits
adtsHeader[2] |= (sampleFrequenceIndex & 0x0f) << 2; // sampling frequency index:sampling_frequency_index 4bits
adtsHeader[2] |= (0 << 1); // private bit:0 1bit
adtsHeader[2] |= (channels & 0x04) >> 2; // channel configuration:channels 高1bit
adtsHeader[3] = (channels & 0x03) << 6; // channel configuration:channels 低2bits
adtsHeader[3] |= (0 << 5); // original:0 1bit
adtsHeader[3] |= (0 << 4); // home:0 1bit
adtsHeader[3] |= (0 << 3); // copyright id bit: 0 1bit
adtsHeader[3] |= (0 << 2); // copyright id start: 0 1bit
adtsHeader[3] |= ((adtslen & 0x1800) >> 11); // frame length: value 高2bits
adtsHeader[4] = (uint8_t)((adtslen & 0x7f8) >> 3); // frame length:vale 中间8bits
adtsHeader[5] = (uint8_t)((adtslen & 0x7) << 5); // frame length:value 低3bits
adtsHeader[5] |= 0x1f; // buffer fullness: 0x7ff 高5bits
adtsHeader[6] = 0xfc; // buffer fullness: 0x7ff 低6bits
return 0;
}
----------------------------------------------------------------- ADTS 提取 AAC 数据 ----------------------------------------------------------------
int DemuxingAudio(const char *inFileName, const char *aacFileName)
{
/***************************************************************************/
if (inFileName == NULL || aacFileName == NULL)
{
av_log(NULL, AV_LOG_ERROR, "原地址或目的地址为空\n");
return -1;
}
FILE *AAC_FD = NULL;
AAC_FD = fopen(aacFileName, "wb");
if (AAC_FD == NULL)
{
av_log(NULL, AV_LOG_ERROR, "无法打开目标文件: %s!\n", inFileName);
return -1;
}
/***************************************************************************/
int audioIndex = -1;
AVFormatContext *inFmtCtx = NULL;
// 打开输入文件
int ret = avformat_open_input(&inFmtCtx, inFileName, NULL, NULL);
if (ret != 0)
{
av_log(NULL, AV_LOG_ERROR, "open input file:%s error:%s --- line:%d\n", inFileName, av_err2str(ret), __LINE__);
return -1;
}
// 获得解码信息
ret = avformat_find_stream_info(inFmtCtx, NULL);
if (ret < 0)
{
av_log(NULL, AV_LOG_ERROR, "failed to find stream information:%s,error:%s\n", inFileName, av_err2str(ret));
return -1;
}
// dump 媒体信息
av_dump_format(inFmtCtx, 0, inFileName, 0);
AVPacket packet; // AVPacket:数据包的抽象 -- 一个媒体流由大量的数据包组成,是压缩后的数据。
av_init_packet(&packet); // 初始化packet
/** 查找 audio 对应的 stream index
* FFmpeg在调用avformat_open_input()之后,可能码流信息不够完整
* 可以使用avformat_find_stream_info()获取更多的码流信息。
* 比如获取视频帧率、视频宽高,重新计算最大分析时长,打开解码器解码获取codec数据。
*/
audioIndex = av_find_best_stream(inFmtCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
if (audioIndex < 0)
{
av_log(NULL, AV_LOG_ERROR, "Could not find %s stream in input file %s\n", av_get_media_type_string(AVMEDIA_TYPE_AUDIO), inFileName);
return -1;
}
// 打印AAC级别
av_log(NULL, AV_LOG_INFO, "音频模式:%d,FF_PROFILE_AAC_LOW:%d\n", inFmtCtx->streams[audioIndex]->codecpar->profile, FF_PROFILE_AAC_LOW);
if (inFmtCtx->streams[audioIndex]->codecpar->codec_id != AV_CODEC_ID_AAC)
{
printf("媒体文件不包含AAC流, 音频编码格式为: %d\n", inFmtCtx->streams[audioIndex]->codecpar->codec_id);
goto end;
}
// 读取媒体文件,并把aac数据帧写入到本地文件
while (av_read_frame(inFmtCtx, &packet) >= 0) // 读取码流中的音频若干帧或者视频一帧
{
if (packet.stream_index == audioIndex)
{
char adtsHeader[7] = {0};
// 如果数据包属于音频流,则会为音频数据包创建一个 ADTS 头部(ADTS 头部是一种用于封装音频数据的格式,可以通过网络传输或存储在文件中)。
getADTSHeader(adtsHeader, packet.size,
inFmtCtx->streams[audioIndex]->codecpar->profile, // 音频编解码器的配置文件。
inFmtCtx->streams[audioIndex]->codecpar->sample_rate, // 音频的采样率
inFmtCtx->streams[audioIndex]->codecpar->channels); // 音频的通道数。
// 写文件头
ret = fwrite(adtsHeader, 1, 7, AAC_FD);
if (ret != sizeof(adtsHeader))
{
av_log(NULL, AV_LOG_ERROR, "wirte file failed!\n");
goto end;
}
// 写 adts data
ret = fwrite(packet.data, 1, packet.size, AAC_FD);
if (ret != packet.size)
{
av_log(NULL, AV_LOG_WARNING, "写入数据的长度不相等 len:%d packet.size:%d\n", ret, packet.size);
goto end;
}
}
av_packet_unref(&packet);
}
end:
if (inFmtCtx)
{
avformat_close_input(&inFmtCtx);
}
if (AAC_FD)
{
fclose(AAC_FD);
}
}