[[AAC ADTS格式分析]]
目标:从 MP4 格式的视频格式中抽取 aac 音频单独保存。
采样率
根据采样率和对应编号整理为一个数组:
int samplingFrequency[] = {
96000,
88200,
64000,
48000,
44100,
32000,
24000,
22050,
16000,
12000,
11025,
};
组织 ADTS 头
void encode_adts_header(char* buf, const int data_length, int profile, int samplerate, int channels) {
int sampling_frequency_index = 3;
// 默认48000频率
int adtsLen = data_length + 7;
// 包长+头长=总长
int frequencies_size = sizeof(samplingFrequency) / sizeof(samplingFrequency[0]);
int i = 0;
// 匹配采样评率
for (i = 0; i < frequencies_size; i++) {
if (samplingFrequency[i] == samplerate) {
sampling_frequency_index = i;
break;
}
}
if (i >= frequencies_size) {
printf("unsupport samplerate: %d\n", samplerate);
return -1;
}
buf[0] = 0xFF;
// syncword 高8位
buf[1] = 0xF0;
// syncword 第8位
buf[1] |= (0 <<
3);
// ID 0表示MPEG-4
buf[1] |= (0 <<
2);
// layer 00
buf[1] |= (0 <<
1);
buf[1] |= 1;
// protection_absent 1 表示没有校验
buf[2] = (profile <<
6);
// profile 支持哪个级别的AAC
buf[2] |= (sampling_frequency_index &
0x0F) <<
2;
// 采样频率
buf[2] |= (0 <<
1);
// private 0
buf[2] |= (channels &
0x04) >>
2;
// channels 声道 高1位
buf[3] = (channels &
0x03) <<
6;
// channels 声道 低2位
buf[3] |= (0 <<
5);
buf[3] |= (0 <<
4);
buf[3] |= (0 <<
3);
buf[3] |= (0 <<
2);
buf[3] |= (adtsLen &
0x1800) >>
11;
// 包长高2位
buf[4] = (uint8_t)((adtsLen &
0x7F8) >>
3);
// 包长3-10位
buf[5] = (uint8_t)((adtsLen &
0x7) <<
5);
// 包长最后三位
// 0x7FF 可变码流
buf[5] |= 0x1F;
buf[6] = 0xFC;
return 0;
}
读取 packet
// 运行带两个参数 分别时候输入文件和输出文件
if (argc <
3) {
return -1;
}
const char* in_filepath = argv[1];
const char* out_filepath = argv[2];
int errbuf[1024] = {
0
};
FILE* acc_fp = NULL;
int ret = -1;
AVFormatContext* ifmt_ctx = NULL;
// 解码器上下文
AVPacket pkt;
av_log_set_level(AV_LOG_DEBUG);
// 设置日志级别
acc_fp = fopen(out_filepath, "wb");
// 打开文件准备写入
if (!acc_fp) {
av_log(NULL, AV_LOG_ERROR, "Open out_file error\n");
return ret;
}
ret = avformat_open_input(&ifmt_ctx, in_filepath, NULL, NULL);
// 输入源
if (ret <
0) {
av_strerror(ret, errbuf, 1024);
av_log(NULL, AV_LOG_ERROR, "Could not open source file: %s, %d(%s)", in_filepath, ret, errbuf);
return ret;
}
ret = avformat_find_stream_info(ifmt_ctx, NULL);
if (ret <
0) {
av_strerror(ret, errbuf, 1024);
av_log(NULL, AV_LOG_ERROR, "Find stream error: %d(%s)\n", ret, errbuf);
return ret;
}
av_dump_format(ifmt_ctx, NULL, in_filepath, NULL);
// dump文件到上下文
// pkt = av_packet_alloc();
av_init_packet(&pkt);
// 找到音频流
int 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_filepath);
return AVERROR(EINVAL);
}
// 判断音频流是不是aac
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;
}
写入文件
while (av_read_frame(ifmt_ctx, &pkt) >= 0) {
/* code */
if (pkt.stream_index == audio_index) {
char adts_header_buf[7] = {
0
};
// adts_header
encode_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);
fwrite(adts_header_buf, 1, 7, acc_fp);
int len = fwrite(pkt.data, 1, pkt.size, acc_fp);
if (len != pkt.size) {
av_log(NULL, AV_LOG_DEBUG, "warning, length of writed data isn't equal\n");
}
}
av_packet_unref(&pkt);
}
完整代码
#include <stdio.h>
#include <libavformat/avformat.h>
int samplingFrequency[] = {
96000,
88200,
64000,
48000,
44100,
32000,
24000,
22050,
16000,
12000,
11025,
};
void encode_adts_header(char* buf, const int data_length, int profile, int samplerate, int channels) {
int sampling_frequency_index = 3;
int adtsLen = data_length + 7;
int frequencies_size = sizeof(samplingFrequency) / sizeof(samplingFrequency[0]);
int i = 0;
for (i = 0; i < frequencies_size; i++) {
if (samplingFrequency[i] == samplerate) {
sampling_frequency_index = i;
break;
}
}
if (i >= frequencies_size) {
printf("unsupport samplerate: %d\n", samplerate);
return -1;
}
buf[0] = 0xFF;
buf[1] = 0xF0;
buf[1] |= (0 <<
3);
buf[1] |= (0 <<
2);
buf[1] |= (0 <<
1);
buf[1] |= 1;
buf[2] = (profile <<
6);
buf[2] |= (sampling_frequency_index &
0x0F) <<
2;
buf[2] |= (0 <<
1);
buf[2] |= (channels &
0x04) >>
2;
buf[3] = (channels &
0x03) <<
6;
buf[3] |= (0 <<
5);
buf[3] |= (0 <<
4);
buf[3] |= (0 <<
3);
buf[3] |= (0 <<
2);
buf[3] |= (adtsLen &
0x1800) >>
11;
buf[4] = (uint8_t)((adtsLen &
0x7F8) >>
3);
buf[5] = (uint8_t)((adtsLen &
0x7) <<
5);
buf[5] |= 0x1F;
buf[6] = 0xFC;
return 0;
}
int main(int argc, char* argv[]) {
if (argc <
3) {
return -1;
}
const char* in_filepath = argv[1];
const char* out_filepath = argv[2];
int errbuf[1024] = {
0
};
FILE* acc_fp = NULL;
int ret = -1;
AVFormatContext* ifmt_ctx = NULL;
AVPacket pkt;
av_log_set_level(AV_LOG_DEBUG);
acc_fp = fopen(out_filepath, "wb");
if (!acc_fp) {
av_log(NULL, AV_LOG_ERROR, "Open out_file error\n");
return ret;
}
ret = avformat_open_input(&ifmt_ctx, in_filepath, NULL, NULL);
if (ret <
0) {
av_strerror(ret, errbuf, 1024);
av_log(NULL, AV_LOG_ERROR, "Could not open source file: %s, %d(%s)", in_filepath, ret, errbuf);
return ret;
}
ret = avformat_find_stream_info(ifmt_ctx, NULL);
if (ret <
0) {
av_strerror(ret, errbuf, 1024);
av_log(NULL, AV_LOG_ERROR, "Find stream error: %d(%s)\n", ret, errbuf);
return ret;
}
av_dump_format(ifmt_ctx, NULL, in_filepath, NULL);
// pkt = av_packet_alloc();
av_init_packet(&pkt);
int 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_filepath);
return AVERROR(EINVAL);
}
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;
}
while (av_read_frame(ifmt_ctx, &pkt) >= 0) {
/* code */
if (pkt.stream_index == audio_index) {
char adts_header_buf[7] = {
0
};
// adts_header
encode_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);
fwrite(adts_header_buf, 1, 7, acc_fp);
int len = fwrite(pkt.data, 1, pkt.size, acc_fp);
if (len != pkt.size) {
av_log(NULL, AV_LOG_DEBUG, "warning, length of writed data isn't equal\n");
}
}
av_packet_unref(&pkt);
}
failed:
if (ifmt_ctx) {
avformat_close_input(&ifmt_ctx);
}
if (acc_fp) {
fclose(acc_fp);
}
}
参考资料:https://github.com/0voice