音频编码格式 - AAC

音频编码格式 - AAC_第1张图片

一、两种封装格式

AAC是一种音频编码格式,他的格式有两种:

  • ADIF:音频数据交换格式,他的解码信息直接写在开始的Header里,适合本地文件的格式
  • ADTS:音频数据传输流,他的特点是有同步字的比特流,解码可以在这个流中的任何位置开始,适合网络传输的格式

简单来说,ADTS可以在任意帧进行解码,每一个帧都有头信息,但ADIF却只有一个统一的头,所以必须得到所有的数据后才能解码。一个帧就能单独解码。

两种Header的形式也不同,目前一般编码后和抽取出的基本都是ADTS格式音频流。

二、ADTS结构

AAC裸流是由一连串的ADTS帧组成,而一个ADTS帧分为两部分:

1)ADTS Header;2)AAC Data

当我们获取到网络中的AAC流后,无法在PC或手机上播放,那么很有可能就是因为,这串AAC流缺少了ADTS的包装与拼接,因为AAC帧的长度是可变的,并非固定,所以就需要一个Header去描述。

ADTS Header

一共有7个字节(没有校验位),如果有校验位就是9个字节,其中包括两个部分:

  • 28bit:adts_fixed_header():固定头部分
  • 28bit:adts_variable_header():可变头部分

头文件信息内的字段,通过标准库的表来参考。

音频编码格式 - AAC_第2张图片

从二进制字节序可以看出,一个不带CRC的Header一共7个字节,带CRC一共9个字节,关键字段:

  • 同步头
  • AAC等级
  • 采样率下标
  • 通道数

音频编码格式 - AAC_第3张图片

音频编码格式 - AAC_第4张图片

adts_fixed(28bit)

音频编码格式 - AAC_第5张图片

  • syncword:同步头,固定是0xFFF,代表了一个帧的开始
  • ID:MPEG标识符,0表示MPEG-4,1表示MPEG-2
  • layer:总是00
  • protection_absent:表示是否无码校验(CRC)?0表示有码,1表示无码
  • profile:哪个级别的AAC?

主流就3个:Main、Low、SSR

音频编码格式 - AAC_第6张图片

音频编码格式 - AAC_第7张图片

  • sampling_frequency_index:表示采样率的下标

音频编码格式 - AAC_第8张图片

  • channel_configuration:声道数

adts_variable(28bit)

音频编码格式 - AAC_第9张图片

  • aac_frame_length:一个ADTS帧的长度,其包含了ADTS Header和Payload。

要看fixed_header -> protection_absent == 1 ?7 :9,然后再加上sizeof(AACFrame)

  • number_of_raw_data_blocks_in_frame:表示ADTS里有多少个原始帧(n+1),当它为0时,表示只有一个原始帧
  • adts_buffer_fullness:可能有多个帧

crc(16bit)

当protection_absent == 0时,表示存在CRC,这里的两字节CRC,就携带了CRC信息。

ADTS ES(原始流)

所有bit都是从高位到低位,显示出来

int ff_adts_write_frame_header(ADTSContext *ctx, uint8_t *buf, int size, int pce_size) {
    PutBitContext pb;
    init_put_bits(&pb, buf, ADTS_HEADER_SIZE);
    
    /* adts_fixed_header */
    put_bits(&pb, 12, 0xfff); /* syncword */
    put_bits(&pb, 1, 0); /* ID */
    put_bits(&pb, 2, 0); /* layer */
    put_bits(&pb, 1, 1); /* protection_absent */
    put_bits(&pb, 2, ctx->objecttype); /* profile_objecttype */
    put_bits(&pb, 4, ctx->sample_rate_index);
    put_bits(&pb, 1, 0); /* private_bit */
    put_bits(&pb, 3, ctx->channel_conf); /* channel_configuration */
    put_bits(&pb, 1, 0); /* original_copy */
    put_bits(&pb, 1, 0); /* home */
    
    /* adts_variable_header */
    put_bits(&pb, 1, 0); /* copyright_identification_bit */
    put_bits(&pb, 1, 0); /* copyright_identification_start */
    put_bits(&pb, 13, ADTS_HEADER_SIZE + size + pce_size); /* aac_frame_length */
    put_bits(&pb, 11, 0x7ff); /* adts_buffer_fullness */
    put_bits(&pb, 2, 0); /* number_of_raw_data_blocks_in_frame */
    
    flush_put_bits(&pb);
    
    return 0;
}

音频编码格式 - AAC_第10张图片

音频编码格式 - AAC_第11张图片

三、源码实战

通过代码,将h264、flv文件,抽取aac裸流到文件。

#include 

#ifdef __cplusplus
extern "C" {
#endif

#include 
#include "libavcodec/avcodec.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是保留的
};
/**
 * adts_header
 * ADTS Header 解析
 */
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;

    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:0xfff 高8bits
    p_adts_header[1] = 0xf0; // syncword:0xfff 低4bits
    p_adts_header[1] |= (0 << 3); // MPEG Version:0:MPEG-4,1:MPEG-2 1bit
    p_adts_header[1] |= (0 << 1); // Layer:0 2bits
    p_adts_header[1] |= 1; // protection absent:1 1bit

    p_adts_header[2] = (profile) << 6; // profile:profile 2bits
    p_adts_header[2] |=
            (sampling_frequency_index & 0x0f) << 2; //sampling frequency index:sampling_frequency_index  4bits
    p_adts_header[2] |= (0 << 1); // private bit:0 1bit
    p_adts_header[2] |= (channels & 0x04) >> 2; // channel configuration:channels 高1bit

    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
    // number_of_raw_data_blocks_in_frame:
    // 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧。
    return 0;
}

int main(int argc, char *argv[]) {
    int ret = -1;
    char errors[1024];
    char *in_filename = NULL;
    char *aac_filename = NULL;
    FILE *aac_fd = NULL;
    int audio_index = -1;
    int len = 0;
    AVFormatContext *ifmt_ctx = NULL;
    AVPacket pkt;
    // 设置打印级别
    av_log_set_level(AV_LOG_DEBUG);

    if (argc < 3) {
        av_log(NULL, AV_LOG_DEBUG, "the count of parameters should be more than three!\n");
        return -1;
    }

    in_filename = argv[1]; // 输入文件
    aac_filename = argv[2]; // 输出文件

    if (in_filename == NULL || aac_filename == NULL) {
        av_log(NULL, AV_LOG_DEBUG, "src or dts file is null, plz check them!\n");
        return -1;
    }
    // 检验文件
    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(&ifmt_ctx, 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(ifmt_ctx, 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(ifmt_ctx, 0, in_filename, 0);

    // 初始化packet
    av_init_packet(&pkt);
    // 查找audio对应的steam index
    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_filename);
        return AVERROR(EINVAL);
    }
    // 打印AAC级别
    printf("audio profile:%d, FF_PROFILE_AAC_LOW:%d\n",
           ifmt_ctx->streams[audio_index]->codecpar->profile,
           FF_PROFILE_AAC_LOW);

    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;
    }
    // 读取媒体文件,并把aac数据帧写入到本地文件
    while (av_read_frame(ifmt_ctx, &pkt) >= 0) {
        // 只读取音频通道
        if (pkt.stream_index == audio_index) {
            char adts_header_buf[7] = {0};
            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);
            // 写adts header , ts流不适用,ts流分离出来的packet带了adts header
            fwrite(adts_header_buf, 1, 7, aac_fd);
            // 写adts data
            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);
    }

    failed:
    // 关闭输入文件
    if (ifmt_ctx) {
        avformat_close_input(&ifmt_ctx);
    }
    if (aac_fd) {
        fclose(aac_fd);
    }

    return 0;
}

#ifdef  __cplusplus
}
#endif

你可能感兴趣的:(音视频流媒体基础,音视频,aac,ffmpeg)