ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-12-04 14:31:06  阅读:181  来源: 互联网

标签:码流 FFmpeg AAC int uint8 Header length ADTS 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称为可变头部。

标志比特长度描述
A12同步字段,全1
B1MPEG版本: 0 for MPEG-4, 1 for MPEG-2,mp4是0
C2Layer: 全0
D1保护缺失标识ProtectionAbsent , 1 表示无 CRC ,0 表示有 CRC
E2AAC编码级别, MPEG-4 Audio Object Type减1。0: Main Profile, 1:LC, 2: SSR,3:保留。常用低复杂度编码LC。
F4MPEG-4 采样率表序号,注意这里是序号,不是采样率值,参考采样率表。
G1私有位,设为0,解码时忽略
H3声道数,取值范围1-7。
I1源标识, 编码设为0,解码忽略
J1home, 编码设为0,解码忽略
K1版权标志位,编码设为0,解码忽略
L1版权标志开始位,编码设为0,解码忽略
M13帧长度,为AAC原始数据长度+ADTS头长度(ProtectionAbsent == 1 ? 7 : 9)
O11Buffer fullness, 0x7FF 说明是码率可变的码流
P2AAC帧数量减1值,有1个帧时此值为0
Q16如果保护缺失标识ProtectionAbsent为0,标识有2字节CRC校验字段

官方文档原图为
在这里插入图片描述
在这里插入图片描述
采样率表如下所示。
在这里插入图片描述

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个采样点,保存文件的时候不需要时间戳信息,播放时就按采样率播放。

标签:码流,FFmpeg,AAC,int,uint8,Header,length,ADTS,aac
来源: https://blog.csdn.net/shaosunrise/article/details/121714316

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有