AAC ADTS格式协议:
从flv文件中提取AAC音频数据保存为文件。
如果需要详细了解AAC ADTS格式,可以查询文档。
原文件:
提取aac文件:
main.c
#include <stdio.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是保留的
};
int adts_header(char* const p_adts_header, const int data_length,
const int profile, const int samplerate, const int channels)
{
int sampling_frequencies_index = 3; //默认使用48000
int adtsLen = data_length + 7;
//根据输入文件的samplerate 获取 相应的在ADTS中设置的索引
int frequencies_size = sizeof(sampling_frequencies) / sizeof(sampling_frequencies[0]);
int i = 0;
for(i = 0; i < frequencies_size; i++)
{
if(samplerate == sampling_frequencies[i])
{
sampling_frequencies_index = i;
break;
}
}
if(sampling_frequencies_index >= frequencies_size)
{
printf("unsupport samplerate:%d\n", samplerate);
return -1;
}
//同步头 总是0xFFF(12个bit),代表着一个ADTS帧的开始
p_adts_header[0] = 0xff;
p_adts_header[1] = 0xf0;
//MPEG标识符,0标识MPEG-4,1标识MPEG-2(1个bit)
p_adts_header[1] |= (0 << 3);
//layer,总是0(2个bit)
p_adts_header[1] |= (0 << 1);
//protection_absent ,表示是否误码校验,1表示 没有, 0 表示有。(1个bit)
//(注意:ADTS Header的长度在protection_absent = 0 时占9个字节, protection_absent = 1时占7个字节)
p_adts_header[1] |= 1;
//profile 使用aac的级别(质量)(2个bit)
//MPEG-4 profile:
//MAIN = 0
//LC = 1
//SSR = 2
//LTP = 3
p_adts_header[2] = (profile)<<6;
//采样率的索引(4个bit)
p_adts_header[2] |= (sampling_frequencies_index & 0x0f) << 2;
//private bit: 0 (1个bit)
p_adts_header[2] |= (0 << 1);
//声道(3个bit)
p_adts_header[2] |= (channels & 0x04) >> 2;
p_adts_header[3] = (channels & 0x03) << 6;
//original_copy = 0 (1个bit)
p_adts_header[3] |= (0 << 5);
//home = 0 (1个bit)
p_adts_header[3] |= (0 << 4);
//copyright_identification_bit = 0 (1个bit)
p_adts_header[3] |= (0 << 3);
//copyright_identification_start = 0 (1个bit)
p_adts_header[3] |= (0 << 2);
//frame_length:1个ADTS帧的长度包括ADTS头和AAC原始流(13bit)
p_adts_header[3] |= ((adtsLen & 0x1800) >> 11);
p_adts_header[4] = (uint8_t)((adtsLen & 0x7f8) >> 3);
p_adts_header[5] = (uint8_t)((adtsLen & 0x7) << 5);
//adts_buffer_fullness:0x7FF 说明是码率可变的码流
p_adts_header[5] |= 0x1f;
p_adts_header[6] = 0xfc;
//最后还有两个bit:number_of_raw_data_blocks_in_frame
//表示这个ADTS帧有几个AAC数据块
//计算方法:
//number_of_raw_data_blocks_in_frame + 1个AAC原始帧。
//所以说number_of_raw_data_blocks_in_frame == 0 表示说ADTS帧中有⼀个
//AAC数据块。
p_adts_header[6] &= 0xfc;//其实上面p_adts_header[6] = 0xfc的操作这2个bit已经为0了
return 0;
}
int main()
{
int ret = -1;
char errors[1024];
char* in_filename = "in_file.flv";
char* aac_filename = "test_out.aac";
FILE* aac_fd = NULL;
int audio_index = -1;
int len = 0;
AVFormatContext* ifmat_ctc = NULL;
AVPacket pkt;
//设置打印级别
av_log_set_level(AV_LOG_DEBUG);
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(&ifmat_ctc, 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(ifmat_ctc, 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(ifmat_ctc, 0, in_filename, 0);
//初始化packet
av_init_packet(&pkt);
//查找audio对应的stream index
audio_index = av_find_best_stream(ifmat_ctc, 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",
ifmat_ctc->streams[audio_index]->codecpar->profile,
FF_PROFILE_AAC_LOW);
if(ifmat_ctc->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",
ifmat_ctc->streams[audio_index]->codecpar->codec_id);
goto END;
}
//读取媒体文件,并把aac数据帧写入本地文件
while (av_read_frame(ifmat_ctc, &pkt) >=0 )
{
if(pkt.stream_index == audio_index)
{
char adts_header_buf[7] = {
0};
//获取ADTS帧头信息
adts_header(adts_header_buf, pkt.size,
ifmat_ctc->streams[audio_index]->codecpar->profile,
ifmat_ctc->streams[audio_index]->codecpar->sample_rate,
ifmat_ctc->streams[audio_index]->codecpar->channels);
//写入adts header,ts流不适用,ts流分离出来的packet带了adts header
fwrite(adts_header_buf, 1, 7, aac_fd);
len = fwrite(pkt.data, 1, pkt.size, aac_fd);//写入adts data
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);
}
END:
if(ifmat_ctc)
avformat_close_input(&ifmat_ctc);
if(aac_fd)
fclose(aac_fd);
return 0;
}