一、两种封装格式
AAC是一种音频编码格式,他的格式有两种:
- ADIF:音频数据交换格式,他的解码信息直接写在开始的Header里,适合本地文件的格式
- ADTS:音频数据传输流,他的特点是有同步字的比特流,解码可以在这个流中的任何位置开始,适合网络传输的格式
简单来说,ADTS可以在任意帧进行解码,每一个帧都有头信息,但ADIF却只有一个统一的头,所以必须得到所有的数据后才能解码。一个帧就能单独解码。
两种Header的形式也不同,目前一般编码后和抽取出的基本都是ADTS格式音频流。
二、ADTS结构
AAC裸流是由一连串的ADTS帧组成,而一个ADTS帧分为两部分:
1)ADTS Header;2)AAC Data
当我们获取到网络中的AAC流后,无法在PC或手机上播放,那么很有可能就是因为,这串AAC流缺少了ADTS的包装与拼接,因为AAC帧的长度是可变的,并非固定,所以就需要一个Header去描述。
ADTS Header
一共有7个字节(没有校验位),如果有校验位就是9个字节,其中包括两个部分:
- 28bit:adts_fixed_header():固定头部分
- 28bit:adts_variable_header():可变头部分
头文件信息内的字段,通过标准库的表来参考。
从二进制字节序可以看出,一个不带CRC的Header一共7个字节,带CRC一共9个字节,关键字段
- 同步头
- AAC等级
- 采样率下标
- 通道数
adts_fixed(28bit)
- syncword:同步头,固定是0xFFF,代表了一个帧的开始
- ID:MPEG标识符,0表示MPEG-4,1表示MPEG-2
- layer:总是00
- protection_absent:表示是否无码校验(CRC)?0表示有码,1表示无码
- profile:哪个级别的AAC?
主流就3个:Main、Low、SSR
- sampling_frequency_index:表示采样率的下标
- channel_configuration:声道数
adts_variable(28bit)
- aac_frame_length:一个ADTS帧的长度,其包含了ADTS Header和Payload。
要看fixed_header -> protection_absent == 1 ?7 :9,然后再加上sizeof(AACFrame)
- number_of_raw_data_blocks_in_frame:表示ADTS里有多少个原始帧(n+1),当它为0时,表示只有一个原始帧
- adts_buffer_fullness:可能有多个帧
crc(16bit)
当protection_absent == 0时,表示存在CRC,这里的两字节CRC,就携带了CRC信息。
ADTS ES(原始流)
所有bit都是从高位到低位,显示出来
int ff_adts_write_frame_header(ADTSContext *ctx, uint8_t *buf, int size, int pce_size) {
PutBitContext pb;
init_put_bits(&pb, buf, ADTS_HEADER_SIZE);
/* adts_fixed_header */
put_bits(&pb, 12, 0xfff); /* syncword */
put_bits(&pb, 1, 0); /* ID */
put_bits(&pb, 2, 0); /* layer */
put_bits(&pb, 1, 1); /* protection_absent */
put_bits(&pb, 2, ctx->objecttype); /* profile_objecttype */
put_bits(&pb, 4, ctx->sample_rate_index);
put_bits(&pb, 1, 0); /* private_bit */
put_bits(&pb, 3, ctx->channel_conf); /* channel_configuration */
put_bits(&pb, 1, 0); /* original_copy */
put_bits(&pb, 1, 0); /* home */
/* adts_variable_header */
put_bits(&pb, 1, 0); /* copyright_identification_bit */
put_bits(&pb, 1, 0); /* copyright_identification_start */
put_bits(&pb, 13, ADTS_HEADER_SIZE + size + pce_size); /* aac_frame_length */
put_bits(&pb, 11, 0x7ff); /* adts_buffer_fullness */
put_bits(&pb, 2, 0); /* number_of_raw_data_blocks_in_frame */
flush_put_bits(&pb);
return 0;
}
三、源码实战
通过代码,将h264、flv文件,抽取aac裸流到文件。
#include <iostream>
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include "libavcodec/avcodec.h"
#include "libavutil/log.h"
#include "libavformat/avio.h"
#include "libavformat/avformat.h"
#define ADTS_HEADER_LEN 7;
const int sampling_frequencies[] = {
96000, // 0x0
88200, // 0x1
64000, // 0x2
48000, // 0x3
44100, // 0x4
32000, // 0x5
24000, // 0x6
22050, // 0x7
16000, // 0x8
12000, // 0x9
11025, // 0xa
8000 // 0xb
// 0xc d e f是保留的
};
/**
* adts_header
* ADTS Header 解析
*/
int adts_header(char *const p_adts_header, const int data_length,
const int profile, const int samplerate,
const int channels) {
int sampling_frequency_index = 3; // 默认使用48000hz
int adtsLen = data_length + 7;
int frequencies_size = sizeof(sampling_frequencies) / sizeof(sampling_frequencies[0]);
int i = 0;
for (i = 0; i < frequencies_size; i++) {
if (sampling_frequencies[i] == samplerate) {
sampling_frequency_index = i;
break;
}
}
if (i >= frequencies_size) {
printf("unsupport samplerate:%d\n", samplerate);
return -1;
}
p_adts_header[0] = 0xff; // syncword:0xfff 高8bits
p_adts_header[1] = 0xf0; // syncword:0xfff 低4bits
p_adts_header[1] |= (0 << 3); // MPEG Version:0:MPEG-4,1:MPEG-2 1bit
p_adts_header[1] |= (0 << 1); // Layer:0 2bits
p_adts_header[1] |= 1; // protection absent:1 1bit
p_adts_header[2] = (profile) << 6; // profile:profile 2bits
p_adts_header[2] |=
(sampling_frequency_index & 0x0f) << 2; //sampling frequency index:sampling_frequency_index 4bits
p_adts_header[2] |= (0 << 1); // private bit:0 1bit
p_adts_header[2] |= (channels & 0x04) >> 2; // channel configuration:channels 高1bit
p_adts_header[3] = (channels & 0x03) << 6; // channel configuration:channels 低2bits
p_adts_header[3] |= (0 << 5); // original:0 1bit
p_adts_header[3] |= (0 << 4); // home:0 1bit
p_adts_header[3] |= (0 << 3); // copyright id bit:0 1bit
p_adts_header[3] |= (0 << 2); // copyright id start:0 1bit
p_adts_header[3] |= ((adtsLen & 0x1800) >> 11); // frame length:value 高2bits
p_adts_header[4] = (uint8_t) ((adtsLen & 0x7f8) >> 3); // frame length:value 中间8bits
p_adts_header[5] = (uint8_t) ((adtsLen & 0x7) << 5); // frame length:value 低3bits
p_adts_header[5] |= 0x1f; // buffer fullness:0x7ff 高5bits
p_adts_header[6] = 0xfc; // 11111100 // buffer fullness:0x7ff 低6bits
// number_of_raw_data_blocks_in_frame:
// 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧。
return 0;
}
int main(int argc, char *argv[]) {
int ret = -1;
char errors[1024];
char *in_filename = NULL;
char *aac_filename = NULL;
FILE *aac_fd = NULL;
int audio_index = -1;
int len = 0;
AVFormatContext *ifmt_ctx = NULL;
AVPacket pkt;
// 设置打印级别
av_log_set_level(AV_LOG_DEBUG);
if (argc < 3) {
av_log(NULL, AV_LOG_DEBUG, "the count of parameters should be more than three!\n");
return -1;
}
in_filename = argv[1]; // 输入文件
aac_filename = argv[2]; // 输出文件
if (in_filename == NULL || aac_filename == NULL) {
av_log(NULL, AV_LOG_DEBUG, "src or dts file is null, plz check them!\n");
return -1;
}
// 检验文件
aac_fd = fopen(aac_filename, "wb");
if (!aac_fd) {
av_log(NULL, AV_LOG_DEBUG, "Could not open destination file %s\n", aac_filename);
return -1;
}
// 打开输入文件
if ((ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL)) < 0) {
av_strerror(ret, errors, 1024);
av_log(NULL, AV_LOG_DEBUG, "Could not open source file: %s, %d(%s)\n",
in_filename,
ret,
errors);
return -1;
}
// 获取解码器信息
if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0) {
av_strerror(ret, errors, 1024);
av_log(NULL, AV_LOG_DEBUG, "failed to find stream information: %s, %d(%s)\n",
in_filename,
ret,
errors);
return -1;
}
// dump媒体信息
av_dump_format(ifmt_ctx, 0, in_filename, 0);
// 初始化packet
av_init_packet(&pkt);
// 查找audio对应的steam index
audio_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
if (audio_index < 0) {
av_log(NULL, AV_LOG_DEBUG, "Could not find %s stream in input file %s\n",
av_get_media_type_string(AVMEDIA_TYPE_AUDIO),
in_filename);
return AVERROR(EINVAL);
}
// 打印AAC级别
printf("audio profile:%d, FF_PROFILE_AAC_LOW:%d\n",
ifmt_ctx->streams[audio_index]->codecpar->profile,
FF_PROFILE_AAC_LOW);
if (ifmt_ctx->streams[audio_index]->codecpar->codec_id != AV_CODEC_ID_AAC) {
printf("the media file no contain AAC stream, it's codec_id is %d\n",
ifmt_ctx->streams[audio_index]->codecpar->codec_id);
goto failed;
}
// 读取媒体文件,并把aac数据帧写入到本地文件
while (av_read_frame(ifmt_ctx, &pkt) >= 0) {
// 只读取音频通道
if (pkt.stream_index == audio_index) {
char adts_header_buf[7] = {0};
adts_header(adts_header_buf, pkt.size,
ifmt_ctx->streams[audio_index]->codecpar->profile,
ifmt_ctx->streams[audio_index]->codecpar->sample_rate,
ifmt_ctx->streams[audio_index]->codecpar->channels);
// 写adts header , ts流不适用,ts流分离出来的packet带了adts header
fwrite(adts_header_buf, 1, 7, aac_fd);
// 写adts data
len = fwrite(pkt.data, 1, pkt.size, aac_fd);
if (len != pkt.size) {
av_log(NULL, AV_LOG_DEBUG, "warning, length of writed data isn't equal pkt.size(%d, %d)\n",
len, pkt.size);
}
}
av_packet_unref(&pkt);
}
failed:
// 关闭输入文件
if (ifmt_ctx) {
avformat_close_input(&ifmt_ctx);
}
if (aac_fd) {
fclose(aac_fd);
}
return 0;
}
#ifdef __cplusplus
}
#endif