03 - FFmpeg 提取 AAC 数据

aac简介
AAC,全称Advanced Audio Coding,高级音频编码,是一种专为声音数据设计的文件压缩格式。
> 提升压缩率:以更小的文件大小获得更高的音质;
> 支持多声道:可提供最多48个全音域声道;
> 更高解析度:最高支持96KHz的采样频率:
> 提升解码效率:解码播放所占的资源更少,
ffmpeg命令:
  ffmpeg -i video.mp4 -vn -acodec aac out.aac // 提取
ffplay命令:
  ffplay out.aac // 播放

-----------------------------------------------------------------ffmpeg - 提取流程----------------------------------------------------------------

1、打开媒体文件 - avformat_open_input
2、获得码流信息 - avformat_find_stream_info
3、获得音频流 - av_find_best_stream
4、初始化packet - av_init_packet
5、读取packet数据 - av_read_frame
6、释放packet资源 - av_packet_unref
7、关闭媒体文件 - avformat_close_input

----------------------------------------------------------------- ADIF 和 ADTS ----------------------------------------------------------------

AAC音频格式有ADIF和ADTS:
ADIF: Audio Data Interchange Format 音频数据交换格式。
这种格式的特征是可以确定的找到这个音频数据的开始,不需进行在音频数据流中间开始的解码,
即它的解码必须在明确定义的开始处进行。故这种格式常用在磁盘文件中。

ADTS: Audio Data Transport Stream 音频数据传输流。
这种格式的特征是它是一个有同步字的比特流,解码可以在这个流中任何位置开始。它的特征类似于mp3数据流格式。

总而言之,ADTS可以在任意帧解码,也就是说它每一帧都有头信息。
ADIF只有一个统一的头,所以必须得到所有的数据后解码。
且这两种的header的格式也是不同的,目前一般编码后的和抽取出的都是ADTS格式的音频流,
ADTS是帧序列,本身具备流特征,在音频流的传输与处理方面更加合适。

每一帧的ADTS的头文件都包含了音频的采样率,声道,长度等信息,这样解码器才能解析读取。
一般情况下ADTS的头信息都是7个字节,分为2部分:
adts_fixed_header();
adts_variable_header();
其一为固定头信息,紧接着是可变头信息。固定头信息中的数据每一都相同,而可变头信息则在帧与帧之间可变

-----------------------------------------------------------------  ADTS fixed header ----------------------------------------------------------------

adts_fixed_header()
{
    syncword; // 12 bit
    ID;       // 1 bit
    layer;    // 2 bit
    protection_absent;
    profile;
    sampling_frequency_index;
    private_bit;
    channel_configuration;
    original_copy;
    home;
}

-----------------------------------------------------------------  ADTS variable header ----------------------------------------------------------------

Syntax                                          No.of bits      Mnemonic
adts_variable_header()
{
    copyright_identification_bit;                   1               bslbf
    copyright_identification_start;                 1               bslbf
    aac_frame_length;                               13              bslbf
    adts_buffer_fullness;                           11              bslbf
    number_of_raw_data_blocks_in_frame;             2               uimsfb
}

获得 ADTS 头


const int sampleFrequencyTable[] = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350};
int getADTSHeader(char *const adtsHeader, const int dataLength, const int profile, const int samplerate, const int channels)
{
    int sampleFrequenceIndex = 3; // 默认使用 48000HZ
    int adtslen = dataLength + 7;

    int frequencise_size = sizeof(sampleFrequencyTable) / sizeof(sampleFrequencyTable[0]);
    int i = 0;
    for (int i = 0; i < frequencise_size; i++)
    {
        if (sampleFrequencyTable[i] == samplerate)
        {
            sampleFrequenceIndex = i;
            break;
        }
    }
    if (i >= frequencise_size)
    {
        av_log(NULL, AV_LOG_ERROR, "不支持次采样率:%d\n", samplerate);
        return -1;
    }
    // syncword :同步头总是OxFFF, all bits must be 1,代表着一个ADTS帧的开始
    adtsHeader[0] = 0xff; // syncword:0xff 高8bits
    adtsHeader[1] = 0xf0; // syncword:0xfff 低4bits

    adtsHeader[1] |= (0 << 3); // MPEG Version:0 for MPEG-4,1 for MPEG-2 1bit
    adtsHeader[1] |= (0 << 1); // Layer:0 2bit
    adtsHeader[1] |= 1;        // protection absent:1 1bit -- 要不要CRC校验 -- 不要

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

    adtsHeader[3] = (channels & 0x03) << 6;      // channel configuration:channels 低2bits
    adtsHeader[3] |= (0 << 5);                   // original:0 1bit
    adtsHeader[3] |= (0 << 4);                   // home:0 1bit
    adtsHeader[3] |= (0 << 3);                   // copyright id bit: 0 1bit
    adtsHeader[3] |= (0 << 2);                   // copyright id start: 0 1bit
    adtsHeader[3] |= ((adtslen & 0x1800) >> 11); // frame length: value 高2bits

    adtsHeader[4] = (uint8_t)((adtslen & 0x7f8) >> 3); // frame length:vale 中间8bits

    adtsHeader[5] = (uint8_t)((adtslen & 0x7) << 5); // frame length:value 低3bits

    adtsHeader[5] |= 0x1f; // buffer fullness: 0x7ff 高5bits
    adtsHeader[6] = 0xfc;  // buffer fullness: 0x7ff 低6bits
    return 0;
}

-----------------------------------------------------------------  ADTS 提取 AAC 数据 ----------------------------------------------------------------


int DemuxingAudio(const char *inFileName, const char *aacFileName)
{
    /***************************************************************************/
    if (inFileName == NULL || aacFileName == NULL)
    {
        av_log(NULL, AV_LOG_ERROR, "原地址或目的地址为空\n");
        return -1;
    }
    FILE *AAC_FD = NULL;
    AAC_FD = fopen(aacFileName, "wb");
    if (AAC_FD == NULL)
    {
        av_log(NULL, AV_LOG_ERROR, "无法打开目标文件: %s!\n", inFileName);
        return -1;
    }
    /***************************************************************************/

    int audioIndex = -1;
    AVFormatContext *inFmtCtx = NULL;

    // 打开输入文件
    int ret = avformat_open_input(&inFmtCtx, inFileName, NULL, NULL);
    if (ret != 0)
    {
        av_log(NULL, AV_LOG_ERROR, "open input file:%s error:%s --- line:%d\n", inFileName, av_err2str(ret), __LINE__);
        return -1;
    }

    // 获得解码信息
    ret = avformat_find_stream_info(inFmtCtx, NULL);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "failed to find stream information:%s,error:%s\n", inFileName, av_err2str(ret));
        return -1;
    }
    // dump 媒体信息
    av_dump_format(inFmtCtx, 0, inFileName, 0);

    AVPacket packet; // AVPacket:数据包的抽象 -- 一个媒体流由大量的数据包组成,是压缩后的数据。
    av_init_packet(&packet); // 初始化packet

    /** 查找 audio 对应的 stream index
     * FFmpeg在调用avformat_open_input()之后,可能码流信息不够完整
     * 可以使用avformat_find_stream_info()获取更多的码流信息。
     * 比如获取视频帧率、视频宽高,重新计算最大分析时长,打开解码器解码获取codec数据。
     */
    audioIndex = av_find_best_stream(inFmtCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    if (audioIndex < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "Could not find %s stream in input file %s\n", av_get_media_type_string(AVMEDIA_TYPE_AUDIO), inFileName);
        return -1;
    }

    // 打印AAC级别
    av_log(NULL, AV_LOG_INFO, "音频模式:%d,FF_PROFILE_AAC_LOW:%d\n", inFmtCtx->streams[audioIndex]->codecpar->profile, FF_PROFILE_AAC_LOW);

    if (inFmtCtx->streams[audioIndex]->codecpar->codec_id != AV_CODEC_ID_AAC)
    {
        printf("媒体文件不包含AAC流, 音频编码格式为: %d\n", inFmtCtx->streams[audioIndex]->codecpar->codec_id);
        goto end;
    }

    // 读取媒体文件,并把aac数据帧写入到本地文件
    while (av_read_frame(inFmtCtx, &packet) >= 0) // 读取码流中的音频若干帧或者视频一帧
    {
        if (packet.stream_index == audioIndex)
        {
            char adtsHeader[7] = {0};
            // 如果数据包属于音频流,则会为音频数据包创建一个 ADTS 头部(ADTS 头部是一种用于封装音频数据的格式,可以通过网络传输或存储在文件中)。
            getADTSHeader(adtsHeader, packet.size,
                          inFmtCtx->streams[audioIndex]->codecpar->profile,     // 音频编解码器的配置文件。
                          inFmtCtx->streams[audioIndex]->codecpar->sample_rate, // 音频的采样率
                          inFmtCtx->streams[audioIndex]->codecpar->channels);   // 音频的通道数。
            // 写文件头
            ret = fwrite(adtsHeader, 1, 7, AAC_FD);
            if (ret != sizeof(adtsHeader))
            {
                av_log(NULL, AV_LOG_ERROR, "wirte file failed!\n");
                goto end;
            }
            // 写 adts data
            ret = fwrite(packet.data, 1, packet.size, AAC_FD);
            if (ret != packet.size)
            {
                av_log(NULL, AV_LOG_WARNING, "写入数据的长度不相等 len:%d packet.size:%d\n", ret, packet.size);
                goto end;
            }
           
        }
        av_packet_unref(&packet);
    }
end:
    if (inFmtCtx)
    {
        avformat_close_input(&inFmtCtx);
    }
    if (AAC_FD)
    {
        fclose(AAC_FD);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值