1:简介
如下图所示,解封装就是将Flv、MP4等文件解封装为视频H264或H265压缩数据,音频解封装为AAC压缩数据。
2:ADTS头结构
ADTS的全称是Audio Data Transport Stream,是AAC音频的传输流TS格式。ADTS可以再任意帧解码,每一帧都有头信息。
AAC音频文件的每一帧由ADTS Header和AAC Audio Data组成,ADTS头信息为7个字节或者9个字节(一般情况为7个字节,取决于是否包含CRC校验,也就是固定头中protection_absent的值),分为adts_fixed_header固定头,adts_variable_header可变头;固定头信息中的数据每一帧都相同,可变头信息在帧与帧之间可变。
3:流程分析
4:代码分析
这里代码逻辑不是很严谨,异常部分也未作处理,着力先提取出AAC数据。
#include <iostream>
#include <stdio.h>
extern "C"
{
#include <libavformat/avformat.h>
}
using namespace std;
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
};
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;//adts头为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
p_adts_header[1] = 0xf0;//syncword
p_adts_header[1] |= 0;//ID:MPEG标识符,0标识MPEG-4,1标识MPEG-2
p_adts_header[1] |= 0;
p_adts_header[1] |= 1;
p_adts_header[2] |= profile << 6;
p_adts_header[2] |= (sampling_frequency_index & 0x0f) << 2;
p_adts_header[2] |= 0 << 1;
p_adts_header[2] |= (channels & 0x04)>>2;
p_adts_header[3] = (channels & 0x03)<<6; //channel configuration:channels 低2bits
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
}
int main()
{
const char *default_filename = "believe.mp4";
FILE *aac_fd = NULL;
aac_fd = fopen("mp4ToAac.aac", "wb");
//1:解复用器上下文分配
AVFormatContext *ctx = NULL;
int audio_index = -1;
//2:打根据url打开文件
int ret = 0;
char errors[1024];
if((ret = avformat_open_input(&ctx, default_filename, NULL, NULL)) < 0)
{
av_strerror(ret, errors, 1024);
av_log(NULL, AV_LOG_DEBUG, "Could not open source file: %s, %d(%s)\n",
default_filename,
ret,
errors);
return -1;
}
//3:读取媒体数据包获取码流信息
ret = avformat_find_stream_info(ctx,NULL);
if(ret <0)
{
if(ctx)
avformat_free_context(ctx);
getchar();
return 0;
}
//av_dump_format(ctx,0,default_filename,0);
//4:查找音频流
audio_index = av_find_best_stream(ctx,AVMEDIA_TYPE_AUDIO,-1,-1,NULL,0);
if(audio_index < 0)
{
printf("av_find_best_Stream failed");
return 0;
}
AVPacket pkt;
av_init_packet(&pkt);
if(ctx->streams[audio_index]->codecpar->codec_id != AV_CODEC_ID_AAC)
{
printf("the medio file not find aav stream");
return 0;
}
printf("av_read_frame");
//5:读一帧数据到pkt中
while(av_read_frame(ctx,&pkt) >= 0)
{
if (pkt.stream_index == audio_index)
{
//6:添加adts头
char adts_header_buf[7] = {0};
adts_header(adts_header_buf,pkt.size,
ctx->streams[audio_index]->codecpar->profile,
ctx->streams[audio_index]->codecpar->sample_rate,
ctx->streams[audio_index]->codecpar->channels);
//7:aac文件输出
fwrite(adts_header_buf,1,7,aac_fd);
int 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);
}
printf("succes");
if(ctx)
{
avformat_close_input(&ctx);
}
if(aac_fd)
{
fclose(aac_fd);
}
return 0;
}