AAC ADTS 媒体流格式分析 及 FFmpeg解析mp4的aac码流方法

和其他编码格式一样,AAC只是数据编码格式,码流组织格式有ADIF(Audio Data Interchange Format) 和 ADTS (Audio Data Transport Stream)。
ADIFADTS的显著区别就是前者的编码信息存在一个固定的地方,后者的编码信息是每一个包中都有。所以ADIF主要用于磁盘存储文件,ADTS主要用于渐进式传输的网络流,本文主要分析ADTS流。

ADTS 流格式

ADTS流格式为ADTS头部加AAC裸数据。
[ADTS Header](AAC ES data) | [ADTS Header](AAC ES data) | ...

ADTS Header为固定的7字节长度,格式可以用下面字母序列来表示,一个字母表示一种字段,数量表示比特长度。
AAAAAAAA AAAABCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP (QQQQQQQQ QQQQQQQQ)
其中A-J称为ADTS固定头部,K-Q称为可变头部。

标志 比特长度 描述
A 12 同步字段,全1
B 1 MPEG版本: 0 for MPEG-4, 1 for MPEG-2,mp4是0
C 2 Layer: 全0
D 1 保护缺失标识ProtectionAbsent , 1 表示无 CRC ,0 表示有 CRC
E 2 AAC编码级别, MPEG-4 Audio Object Type减1。0: Main Profile, 1:LC, 2: SSR,3:保留。常用低复杂度编码LC。
F 4 MPEG-4 采样率表序号,注意这里是序号,不是采样率值,参考采样率表。
G 1 私有位,设为0,解码时忽略
H 3 声道数,取值范围1-7。
I 1 源标识, 编码设为0,解码忽略
J 1 home, 编码设为0,解码忽略
K 1 版权标志位,编码设为0,解码忽略
L 1 版权标志开始位,编码设为0,解码忽略
M 13 帧长度,为AAC原始数据长度+ADTS头长度(ProtectionAbsent == 1 ? 7 : 9)
O 11 Buffer fullness, 0x7FF 说明是码率可变的码流
P 2 AAC帧数量减1值,有1个帧时此值为0
Q 16 如果保护缺失标识ProtectionAbsent为0,标识有2字节CRC校验字段

官方文档原图为
AAC ADTS 媒体流格式分析 及 FFmpeg解析mp4的aac码流方法
AAC ADTS 媒体流格式分析 及 FFmpeg解析mp4的aac码流方法
采样率表如下所示。
AAC ADTS 媒体流格式分析 及 FFmpeg解析mp4的aac码流方法

FFmpeg 解析保存mp4中aac码流方法

FFmpeg使用av_read_frame(AVFormatContext *s, AVPacket *pkt); 函数从mp4文件中读取到的AVPacket的data是AAC裸数据,直接保存成文件的话,是没法播放的,因为没有采样率等信息。可以根据上文ADTS头部结构描述,每个帧都填充1个Header信息。

下面是我自己使用的ADTS头部定义,可以参考。注意其中单个字节内的按位定义是倒序的,可能不便于理解。
ADTSHeader.h

#ifndef _ADTSHEADER_H
#define _ADTSHEADER_H

#include <string.h>

// 7 bytes
struct ADTS_Header {
    // fixed header
    // 1 byte
    uint8_t sync_word_l : 8;  // 0xFF
    // 2 byte
    // sync_word_h + id + layer + protection_absent
    uint8_t protection_absent : 1;  // 1 no CRC, 0 has CRC
    uint8_t layer : 2;              // 00
    uint8_t id : 1;                 // 0: MPEG-4, 1: MPEG-2
    uint8_t sync_word_h : 4;        // 0xF

    // 3rd byte
    // profile + sampling_frequency_index + private_bit + channel_configuration_l
    uint8_t channel_configuration_l : 1;
    uint8_t private_bit : 1;
    uint8_t sampling_frequency_index : 4;
    uint8_t profile : 2;  // 0:main, 1: LC, 2: SSR, 3: reserved

    // 4th byte
    uint8_t aac_frame_length_l : 2;
    uint8_t copyright_identification_start : 1;
    uint8_t copyright_identification_bit : 1;
    uint8_t home : 1;
    uint8_t original_copy : 1;
    uint8_t channel_configuration_h : 2;

    // 5th byte
    uint8_t aac_frame_length_m : 8;
    // 6th byte
    uint8_t adts_buffer_fullness_l : 5;
    uint8_t aac_frame_length_h : 3;
    // 7th byte
    uint8_t number_of_raw_data_blocks_in_frame : 2;
    uint8_t adts_buffer_fullness_h : 6;  // adts_buffer_fullness 0x7ff VBR

    ADTS_Header()
    {
        memset(this, 0, sizeof(ADTS_Header));
        setSyncWord();
        protection_absent = 1;
        profile = 1;
    }

    ADTS_Header(int samplingFreq, int channel, int length)
    {
        memset(this, 0, sizeof(ADTS_Header));
        setSyncWord();
        setSamplingFrequency(samplingFreq);
        setChannel(channel);
        setLength(length);
        protection_absent = 1;
        profile = 1;
    }

    ADTS_Header &setSyncWord()
    {
        sync_word_l = 0xff;
        sync_word_h = 0xf;
        return *this;
    }

    ADTS_Header &setSamplingFrequency(int sf)
    {
        int sampling_frequency_table[13] = {96000, 88200, 64000, 48000, 44100, 32000, 24000,
                                            22050, 16000, 12000, 11025, 8000,  7350};
        sampling_frequency_index = 0xf;
        for (int i = 0; i < 13; ++i) {
            if (sampling_frequency_table[i] == sf) {
                sampling_frequency_index = i;
                break;
            }
        }
        return *this;
    }

    ADTS_Header &setChannel(int ch)
    {
        if (ch > 0 && ch < 7)
            channel_configuration_h = ch;
        else if (ch == 8)
            channel_configuration_h = 7 >> 24;
        return *this;
    }

    // length = header length + aac es stream length
    ADTS_Header &setLength(int length)
    {
        aac_frame_length_l = (length >> 11) & 0x03;
        aac_frame_length_m = (length >> 3) & 0xff;
        aac_frame_length_h = (length & 0x07);
        return *this;
    }

    int getLength()
    {
        int l = (aac_frame_length_l << 11) | (aac_frame_length_m << 3) | (aac_frame_length_h);
        return l;
    }

    int getChannel()
    {
        if (channel_configuration_h == 8)
            return 8;
        return channel_configuration_h;
    }
};

#endif  // _ADTSHEADER_H

保存文件的时候,在每个帧之前都写一个ADTS Header就可以播放了。

ADTS_Header header(48000, 2, avPacket->size + 7);  //假设是48K采样率2声道音频
fwrite(&header, 1, sizeof(ADTS_Header), _file);
fwrite(avPacket->data, 1, avPacket->size, _file);

提醒一下,AAC编码1个帧为1024个采样点,保存文件的时候不需要时间戳信息,播放时就按采样率播放。

上一篇:网站提示有风险?上海网站制作给你解决方案!


下一篇:软件测试之性能测试浅谈(2)