2、音频编码格式--AAC

1、AAC:AAC是一种音频编码格式,由于其可以任意帧解码的优点,常用于直播中。
AAC的封装格式:ADTS、ADIF。那为什么需要对AAC进行封装呢?这是由于音频流在传输的过程中,是以一个一个数据包进行发送的,我们需要明确各个数据包之间的界限,这样可以避免数据包的丢失导致的解码错误。
2、ADTS封装格式有这样一个优点:可以在任意帧进行解码而不需要等待数据发送完成后再进行,这是由于ADTS是对每个数据包都进行了头部封装。ADIF是对所有数据包加一个头部,这样只能在接收到所有数据后在进行解码,实际上我们使用ADTS这种封装格式更多。
在这里插入图片描述3、ADTS的头部一般是7字节,包括固定头部与可变头部。由于头部信息过多,我们只关心常见的:syncword:0xFFF用于帧边界划分,channel:声道数,frame_length:头部长度+pkt长度。对于MP4/FLV格式的视频文件读出来的AAC没有ADTS头部,需要手动封装。
固定头部:

字节字段说明
08syncword同步字,固定为 0xFFF
11IDMPEG 版本,0 表示 MPEG-4,1 表示 MPEG-2
12layer固定为 00
11protection_absent是否有 CRC 校验,0 表示有 CRC,1 表示无 CRC
1-22profileAAC 编码等级
24sampling_frequency_index采样率索引
21private_bit私有位
23channel_configuration声道配置
31original/copy原始或复制标志
31homeHome 标志

可变头部:

字节字段说明
31copyright_identification_bit版权标志
31copyright_identification_start版权开始
3-513frame_lengthADTS 帧长度,包括头部和AACd数据
5-711adts_buffer_fullnessADTS 缓冲区满度
6-72number_of_raw_data_blocks_in_frame当前帧中的原始数据块数目

4、FLV文件中提取AAC并添加ADTS头部
在这里插入图片描述完整代码如下:

#include <stdio.h>
#include <libavutil/log.h>
#include <libavformat/avio.h>
#include <libavformat/avformat.h>

#define ADTS_HEADER_LEN  7;

// 采样率选择结构体
const int sampling_frequencies[] = {
    96000,
    88200,
    64000,
    48000,
    44100,
    32000,
    24000,
    22050,
    16000,
    12000,
    11025,
    8000
};

int adts_header(char* p_adts_header,int data_length,int profile,int samplerate,int channels)// 构建ADTS头部7字节
{
    int adts_size = data_length + 7;            // ADTS数据长度
    int i = 0;
    int sampling_frequency_index = 3;           // 默认采用48KHZ采样率
    for(i = 0;i<sizeof(sampling_frequencies)/sizeof(sampling_frequencies[0]);i++)
    {
        if(sampling_frequencies[i] == samplerate)
        {
            sampling_frequency_index = i;
            break;
        }
    }
    if(i >= sizeof(sampling_frequencies)/sizeof(sampling_frequencies[0]))
    {
        printf("not support samplerate:%d\n",samplerate);
        return -1;
    }

    p_adts_header[1] = 0xf0;                                     // sync:0xFFF
    p_adts_header[1] |= (0 << 3);
    p_adts_header[1] |= (0 << 1);
    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;
    p_adts_header[3] |= (0 << 5);
    p_adts_header[3] |= (0 << 4);
    p_adts_header[3] |= (0 << 3);
    p_adts_header[3] |= (0 << 2);
    p_adts_header[3] |= ((adts_size & 0x1800) >> 11);           // ADTS总长度

    p_adts_header[4] = (uint8_t)((adts_size & 0x7f8) >> 3);
    p_adts_header[5] = (uint8_t)((adts_size & 0x7) << 5);
    p_adts_header[5] |= 0x1f;
    p_adts_header[6] = 0xfc;

    return 0;
}
int aac_adts()
{
    char* in_filename = "/home/yx/media_file/believe.flv";              // 输入文件路径
    char* out_filename = "/home/yx/media_file/believe.aac";             // 输出文件路径
    AVFormatContext* in_file_ctx = NULL;                                // 解复用器上下文

    FILE* out_aac_fd = NULL;                                            // 输出文件句柄
    int audio_index = -1;                                               // 音频标签
    AVPacket* pkt = av_packet_alloc();                                  // 保存音频包
    out_aac_fd = fopen(out_filename,"w");                               // 打开输出文件

    avformat_open_input(&in_file_ctx,in_filename,NULL,NULL);            // 将输入媒体流与输入文件上下文进行关联
    avformat_find_stream_info(in_file_ctx, NULL);                       // 查找媒体流信息

    audio_index = av_find_best_stream(in_file_ctx,AVMEDIA_TYPE_AUDIO,-1,-1,NULL,0); // 查找音频流对应的标签
    printf("audio_index:%d\n",audio_index);
    while(1)
    {
        if(av_read_frame(in_file_ctx,pkt) < 0)							// 读取数据包
        {
            av_free_packet(pkt);
            break;
        }
        if(pkt->stream_index == audio_index)								// 如果是音频流
        {
            char adts_header_buf[7] = {0};
            // 构建ADTS头部7字节
            adts_header(adts_header_buf,pkt->size,in_file_ctx->streams[audio_index]->codecpar->profile,in_file_ctx->streams[audio_index]->codecpar->sample_rate,in_file_ctx->streams[audio_index]->codecpar->channels);
            fwrite(adts_header_buf,1,7,out_aac_fd);                     // 将ADTS头部写到文件中
            fwrite(pkt->data,1,pkt->size,out_aac_fd);
        }
        av_packet_unref(pkt);
    }

}
int main()
{
    aac_adts();
    printf("Hello World!\n");
    return 0;
}

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

￴ㅤ￴￴ㅤ9527超级帅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值