提取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;
}
相关文章
|
监控 网络协议 Shell
【Shell 命令集合 网络通讯 】Linux 监控和记录网络中ARP(Address Resolution Protocol)活动 arpwatch命令 使用指南
【Shell 命令集合 网络通讯 】Linux 监控和记录网络中ARP(Address Resolution Protocol)活动 arpwatch命令 使用指南
473 0
|
缓存 Java 开发工具
Flutter— 第一次运行Flutter工程时的Bug总结
Flutter— 第一次运行Flutter工程时的Bug总结
 Flutter— 第一次运行Flutter工程时的Bug总结
|
Ubuntu 开发工具
Ubuntu更换阿里云软件源
Ubuntu更换阿里云软件源
142577 0
|
容器
给aac音频添加adts头,函数实现
给aac音频添加adts头,函数实现
446 0
给aac音频添加adts头,函数实现
|
XML 前端开发 JavaScript
Web的三个主要部分
Web的三个主要部分
2348 1
|
Web App开发 编解码 vr&ar
使用FFmpeg从音视频处理到流媒体技术的探索和实战应用
使用FFmpeg从音视频处理到流媒体技术的探索和实战应用
650 2
|
存储 开发工具 git
【Git】Git提示Another git process seems to be running in this repository, e.g. an editor opened by ..错误
【Git】Git提示Another git process seems to be running in this repository, e.g. an editor opened by ..错误
876 0
|
监控 网络协议 iOS开发
程序退到后台的时候,所有线程被挂起,系统回收所有的socket资源问题及解决方案
程序退到后台的时候,所有线程被挂起,系统回收所有的socket资源问题及解决方案
534 0
|
存储 编解码 自然语言处理
常见的音频与视频格式
常见的音频格式有 WAV、MP3、FLAC、APE、AAC、OGG 等,常见的视频格式有 MP4、AVI、MOV、WMV、MKV、FLV、3GP、MPEG 等,它们在兼容性、文件大小、画质等方面各有特点。
|
Dart Unix
Flutter 学习 之 时间转换工具类
Flutter 学习之时间转换工具类 在 Flutter 应用程序开发中,处理时间戳是非常常见的需求。我们通常需要将时间戳转换为人类可读的日期时间格式。为了实现这一点,我们可以创建一个时间转换工具类。
463 1