提取mp4中的音频Pkt,以adts的方式写为aac文件

简介: 使用FFmpeg库从MP4文件中提取音频流,并将其转换为带有ADTS头的AAC文件,提供了两种方法:位运算和位域操作。

提取mp4文件中的音频pkt,加上adts头写入到文件中(位运算的方式)

/*****************************************************************//**
 * \file   writeAAC01.cpp
 * \brief  提取出mp4文件中的音频包,加上adts_header, 写入到aac格式音频文件
 * 
 * \author 13648
 * \date   April 2024
 *********************************************************************/
#define _CRT_SECURE_NO_WARNINGS
#include "myLog.h"
#include <iostream>

extern "C"
{
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
}

#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avcodec.lib")

// 根据采样率查找对应的采样率下标
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是保留的
};

int get_adts_header_buffer(char* const adts_header_buff, const int pkt_size, const int profile,
    const int sample_rate, const int channels)
{
    int sampling_frequency_index = 3;    // 默认使用48khz
    int adtsLen = pkt_size + 7;             // 数据长度加7字节头长度

    int frequencies_size = sizeof(sampling_frequencies) / sizeof(sampling_frequencies[0]);
    for (int i = 0; i < frequencies_size; i++)
    {
        sampling_frequency_index = i;
        if (sampling_frequencies[i] == sample_rate)
        {
            break;
        }
    }
    if (sampling_frequency_index >= frequencies_size)
    {
        LOG_WARNING("unsupport samplerate:%d\n", sample_rate);
        return -1;
    }

    // syncword:0xfff    adts frame start(同步字)
    adts_header_buff[0] = 0xff;
    adts_header_buff[1] = 0xf0;                                                    // 12 bits

    // 0001
    // 0: MPEG-4 00(给00即可)  1: 表示无校验码        
    adts_header_buff[1] |= (0 << 3);    //MPEG Version:0 for MPEG-4,1 for MPEG-2  1 bit
    adts_header_buff[1] |= (0 << 1);    //Layer:0                                 2 bits
    adts_header_buff[1] |= 1;           //protection absent:1(表示没有校验码)     1 bit

    // 01 0100 0 0
    // AAC级别:profile   2 bits  
    adts_header_buff[2] = (profile) << 6;
    // sampling frequency index:采样率索引  4bits   44100: index =  4
    adts_header_buff[2] |= (sampling_frequency_index & 0x0f) << 2;
    //private bit:0                   1bit
    adts_header_buff[2] |= (0 << 1);
    //channel configuration:channels  占3bit 此处剩高位1bit
    adts_header_buff[2] |= (channels & 0x04) >> 2;    // 0000 0010 & 0000 0100 (0 >> 2)

    // 10 0 0     0 0 00
    adts_header_buff[3] = (channels & 0x03) << 6;  //channel configuration:channels 低2bits
    adts_header_buff[3] |= (0 << 5);               //original:0                1bit
    adts_header_buff[3] |= (0 << 4);               //home:0                    1bit

    // 变长adts_header
    adts_header_buff[3] |= (0 << 3);               //copyright id bit:0        1bit
    adts_header_buff[3] |= (0 << 2);               //copyright id start:0      1bit
    adts_header_buff[3] |= ((adtsLen & 0x1800) >> 11);           //frame length:一共13bits   高2bits

    // 0000 0000
    adts_header_buff[4] = (uint8_t)((adtsLen & 0x7f8) >> 3);     //frame length:value    中间8bits

    // 000 11111
    adts_header_buff[5] = (uint8_t)((adtsLen & 0x7) << 5);       //frame length:value    低3bits
    adts_header_buff[5] |= 0x1f;                                 //码率可变:0x7ff 高5bits

    // 1111 1100
    adts_header_buff[6] = 0xfc;              // buffer fullness:0x7ff 低6bits
    // 后还有 2bit 低位字节
    // 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧(数据块)
    return 0;
}

int main_writeAAC01()
{
    const std::string in_filename = "./MediaFile/HTA_13s.mp4";
    AVFormatContext* ifmt_ctx = nullptr;
    size_t nRet = avformat_open_input(&ifmt_ctx, in_filename.c_str(), NULL, NULL);
    if (nRet != 0)
    {
        LOG_WARNING("avformat_open_input error\n");
        return -1;
    }

    nRet = avformat_find_stream_info(ifmt_ctx, NULL);
    if (nRet < 0)
    {
        LOG_WARNING("avformat_find_stream_info error\n");
        avformat_close_input(&ifmt_ctx);
        return -2;
    }

    int audio_idx = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    if (audio_idx < 0)
    {
        LOG_WARNING("av_find_best_stream audio error\n");
        avformat_close_input(&ifmt_ctx);
        return -3;
    }

    // 不是AAC编码
    if (ifmt_ctx->streams[audio_idx]->codecpar->codec_id != AV_CODEC_ID_AAC)
    {
        LOG_WARNING("this file audio not is aac\n");
        avformat_close_input(&ifmt_ctx);
        return -4;
    }

    AVStream* audio_stream = ifmt_ctx->streams[audio_idx];
    // 打印AAC级别
    LOG_INFO("audio profile: %d, FF_PROFILE_AAC_LOW:%d\n", audio_stream->codecpar->profile, FF_PROFILE_AAC_LOW);

    // 读取音频包写入到本地文件
    const std::string out_filename = "./MediaFile/out.aac";
    FILE* out_fp = fopen(out_filename.c_str(), "wb");
    if (NULL == out_fp)
    {
        avformat_close_input(&ifmt_ctx);
        LOG_WARNING("open out_filename faild\n");
        return -5;
    }

    AVPacket* pkt = av_packet_alloc();
    while (av_read_frame(ifmt_ctx, pkt) >= 0)
    {
        if (pkt->stream_index == audio_idx)
        {
            char adts_header_buff[7] = { 0 };    // adts头信息
            nRet = get_adts_header_buffer(adts_header_buff, pkt->size, audio_stream->codecpar->profile, 
                audio_stream->codecpar->sample_rate, audio_stream->codecpar->ch_layout.nb_channels);
            if (nRet >= 0)
            {
                nRet = fwrite(adts_header_buff, 1, sizeof(adts_header_buff), out_fp);
            }
            nRet = fwrite(pkt->data, 1, pkt->size, out_fp);
            if (nRet != pkt->size)
            {
                LOG_WARNING("write size != pkt->size\n");
            }
        }
        av_packet_unref(pkt);
    }

    av_packet_free(&pkt);
    fclose(out_fp);
    if (ifmt_ctx)
    {
        avformat_close_input(&ifmt_ctx);
    }
    return 0;
}

提取mp4文件中的音频pkt,加上adts头写入到文件中(位域的方式)

/*****************************************************************//**
 * \file   writeAAC02.cpp
 * \brief  提取出mp4文件中的音频包,加上adts_header, 写入到aac格式音频文件(使用位域的方式)
 *            
 * \author 13648
 * \date   April 2024
 *********************************************************************/
#define _CRT_SECURE_NO_WARNINGS
#include "myLog.h"
#include <iostream>

extern "C"
{
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
}

#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avcodec.lib")

// 根据采样率查找对应的采样率下标
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
struct st_adts_header
{
    // adts_fixed_header
    unsigned int syncword : 12;
    unsigned int id : 1;
    unsigned int layer : 2;
    unsigned int protection_absent : 1;
    unsigned int profile : 2;
    unsigned int sampling_frequency_index : 4;
    unsigned int private_bit : 1;
    unsigned int channel_configuration : 3;
    unsigned int original_copy : 1;
    unsigned int home : 1;

    // adts_var_header
    unsigned int copyright_identification_bit : 1;
    unsigned int copyright_identification_start : 1;
    unsigned int aac_frame_length : 13;
    unsigned int adts_buffer_fullness : 11;
    unsigned int number_of_raw_data_blocks_in_frame : 2;
};

unsigned long long copyAdtsHeaderToCharArray(st_adts_header* header)
{
    // 将fixed_header的各个字段按照ADTS的同步词格式排列
    // 同步词占用前12位,其余字段按照ADTS头部格式排列
    // (0000多的4bit) 1111 1111 1111 0 00 0 00 0000 0 000 0 0
    unsigned int fixed_part = (header->syncword << 16) |    // 1111 1111 1111
        (header->id << 15) |
        (header->layer << 13) |
        (header->protection_absent << 12) |
        (header->profile << 10) |
        (header->sampling_frequency_index << 6) |
        (header->private_bit << 5) |
        (header->channel_configuration << 2) |
        (header->original_copy << 1) |
        (header->home << 0);

    std::cout << std::hex << fixed_part << std::endl;

    // 将var_header的各个字段按照ADTS的格式排列
    // (0000多的bit) 0 0 0000000000000 00000000000 00
    unsigned int var_part = (header->copyright_identification_bit << 27) |
        (header->copyright_identification_start << 26) |
        (header->aac_frame_length << 13) |
        (header->adts_buffer_fullness << 2) |
        (header->number_of_raw_data_blocks_in_frame << 0);

    std::cout << std::hex << var_part << std::endl;

    unsigned long long ret = (fixed_part & 0XFFFFFFFFFFFFFFFF);    // 低32bit
    // 将fixed_part和var_part合并成一个64位整数
    unsigned long long adts_header_combined = (ret << 28) | var_part;
    std::cout << std::hex << adts_header_combined << std::endl;
    return adts_header_combined;
}

static int get_adts_header_buffer(char* const adts_header_buff, const int pkt_size, const int profile,
    const int sample_rate, const int channels)
{
    int sampling_frequency_index = 3;    // 默认使用48khz
    int adtsLen = pkt_size + 7;             // 数据长度加7字节头长度

    int frequencies_size = sizeof(sampling_frequencies) / sizeof(sampling_frequencies[0]);
    for (int i = 0; i < frequencies_size; i++)
    {
        sampling_frequency_index = i;
        if (sampling_frequencies[i] == sample_rate)
        {
            break;
        }
    }
    if (sampling_frequency_index >= frequencies_size)
    {
        LOG_WARNING("unsupport samplerate:%d\n", sample_rate);
        return -1;
    }

    // 使用位域的方式填入adts_header数据
    st_adts_header adts_header;
    // 赋值ADTS的固定头部分
    adts_header.syncword = 0xFFF;
    adts_header.id = 0;
    adts_header.layer = 0;
    adts_header.protection_absent = 1;

    adts_header.profile = profile;
    adts_header.sampling_frequency_index = sampling_frequency_index;
    adts_header.private_bit = 0x00;
    adts_header.channel_configuration = channels;
    adts_header.original_copy = 0x00;
    adts_header.home = 0x00;
    // 赋值ADTS的可变头部分
    adts_header.copyright_identification_bit = 0x00;
    adts_header.copyright_identification_start = 0x00;
    adts_header.aac_frame_length = adtsLen;
    adts_header.adts_buffer_fullness = 0x7FF;
    adts_header.number_of_raw_data_blocks_in_frame = 0x00;

    // 获取一个64bit的值,然后逐个拷贝到adts_header_buff中
    unsigned long long value = copyAdtsHeaderToCharArray(&adts_header);
    // 00000000 00000000 00000000 00000000 00000000 00000000 00000000 (56 bit)
    for (int i = 0; i < 7; i++)
    {
        adts_header_buff[i] = value >> ((7 - i - 1) * 8);
    }

    return 0;
}

int main()
{
    const std::string in_filename = "./MediaFile/HTA_13s.mp4";
    AVFormatContext* ifmt_ctx = nullptr;
    size_t nRet = avformat_open_input(&ifmt_ctx, in_filename.c_str(), NULL, NULL);
    if (nRet != 0)
    {
        LOG_WARNING("avformat_open_input error\n");
        return -1;
    }

    nRet = avformat_find_stream_info(ifmt_ctx, NULL);
    if (nRet < 0)
    {
        LOG_WARNING("avformat_find_stream_info error\n");
        avformat_close_input(&ifmt_ctx);
        return -2;
    }

    int audio_idx = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    if (audio_idx < 0)
    {
        LOG_WARNING("av_find_best_stream audio error\n");
        avformat_close_input(&ifmt_ctx);
        return -3;
    }

    // 不是AAC编码
    if (ifmt_ctx->streams[audio_idx]->codecpar->codec_id != AV_CODEC_ID_AAC)
    {
        LOG_WARNING("this file audio not is aac\n");
        avformat_close_input(&ifmt_ctx);
        return -4;
    }

    AVStream* audio_stream = ifmt_ctx->streams[audio_idx];
    // 打印AAC级别
    LOG_INFO("audio profile: %d, FF_PROFILE_AAC_LOW:%d\n", audio_stream->codecpar->profile, FF_PROFILE_AAC_LOW);

    // 读取音频包写入到本地文件
    const std::string out_filename = "./MediaFile/out_02.aac";
    FILE* out_fp = fopen(out_filename.c_str(), "wb");
    if (NULL == out_fp)
    {
        avformat_close_input(&ifmt_ctx);
        LOG_WARNING("open out_filename faild\n");
        return -5;
    }

    AVPacket* pkt = av_packet_alloc();
    while (av_read_frame(ifmt_ctx, pkt) >= 0)
    {
        if (pkt->stream_index == audio_idx)
        {
            char adts_header_buff[7] = { 0 };    // adts头信息
            nRet = get_adts_header_buffer(adts_header_buff, pkt->size, audio_stream->codecpar->profile,
                audio_stream->codecpar->sample_rate, audio_stream->codecpar->ch_layout.nb_channels);
            if (nRet >= 0)
            {
                nRet = fwrite(adts_header_buff, 1, sizeof(adts_header_buff), out_fp);
            }
            nRet = fwrite(pkt->data, 1, pkt->size, out_fp);
            if (nRet != pkt->size)
            {
                LOG_WARNING("write size != pkt->size\n");
            }
        }
        av_packet_unref(pkt);
    }

    av_packet_free(&pkt);
    fclose(out_fp);
    if (ifmt_ctx)
    {
        avformat_close_input(&ifmt_ctx);
    }
    return 0;
}
相关文章
|
27天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
4天前
|
人工智能 Rust Java
10月更文挑战赛火热启动,坚持热爱坚持创作!
开发者社区10月更文挑战,寻找热爱技术内容创作的你,欢迎来创作!
438 17
|
7天前
|
JSON 自然语言处理 数据管理
阿里云百炼产品月刊【2024年9月】
阿里云百炼产品月刊【2024年9月】,涵盖本月产品和功能发布、活动,应用实践等内容,帮助您快速了解阿里云百炼产品的最新动态。
阿里云百炼产品月刊【2024年9月】
|
20天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。
|
7天前
|
Linux 虚拟化 开发者
一键将CentOs的yum源更换为国内阿里yum源
一键将CentOs的yum源更换为国内阿里yum源
379 2
|
22天前
|
人工智能 IDE 程序员
期盼已久!通义灵码 AI 程序员开启邀测,全流程开发仅用几分钟
在云栖大会上,阿里云云原生应用平台负责人丁宇宣布,「通义灵码」完成全面升级,并正式发布 AI 程序员。
|
24天前
|
机器学习/深度学习 算法 大数据
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
2024“华为杯”数学建模竞赛,对ABCDEF每个题进行详细的分析,涵盖风电场功率优化、WLAN网络吞吐量、磁性元件损耗建模、地理环境问题、高速公路应急车道启用和X射线脉冲星建模等多领域问题,解析了问题类型、专业和技能的需要。
2600 22
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
|
6天前
|
存储 人工智能 搜索推荐
数据治理,是时候打破刻板印象了
瓴羊智能数据建设与治理产品Datapin全面升级,可演进扩展的数据架构体系为企业数据治理预留发展空间,推出敏捷版用以解决企业数据量不大但需构建数据的场景问题,基于大模型打造的DataAgent更是为企业用好数据资产提供了便利。
286 2
|
4天前
|
编译器 C#
C#多态概述:通过继承实现的不同对象调用相同的方法,表现出不同的行为
C#多态概述:通过继承实现的不同对象调用相同的方法,表现出不同的行为
106 65
|
24天前
|
机器学习/深度学习 算法 数据可视化
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
2024年中国研究生数学建模竞赛C题聚焦磁性元件磁芯损耗建模。题目背景介绍了电能变换技术的发展与应用,强调磁性元件在功率变换器中的重要性。磁芯损耗受多种因素影响,现有模型难以精确预测。题目要求通过数据分析建立高精度磁芯损耗模型。具体任务包括励磁波形分类、修正斯坦麦茨方程、分析影响因素、构建预测模型及优化设计条件。涉及数据预处理、特征提取、机器学习及优化算法等技术。适合电气、材料、计算机等多个专业学生参与。
1582 17
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码