2、音频编码格式--AAC

news2024/11/24 16:00:17

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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1879608.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

密码学及其应用 —— 非对称加密/公匙密码技术

1 RSA加密算法 RSA加密算法是一种基于公钥密码学的加密技术&#xff0c;由罗纳德里维斯特&#xff08;Ron Rivest&#xff09;、阿迪萨莫尔&#xff08;Adi Shamir&#xff09;和伦纳德阿德曼&#xff08;Leonard Adleman&#xff09;在1977年共同发明。RSA算法是第一个既能用于…

树莓派开发之文件传输

文章目录 一、简介使用U盘传输文件使用SD卡传输文件使用Xftp 7传输文件 二、 总结 一、简介 在树莓派开发中经常会用到文件传输&#xff0c;下面介绍几种树莓派文件传输的几种方法。 使用U盘传输文件 &#xff08;1&#xff09;复制所需传输文件到U盘 &#xff08;2&#…

双指针-旋转链表

目录 一、问题描述 二、解题思路 三、代码实现 四、刷题链接 一、问题描述 二、解题思路 1.先确定链表长度为len 2.注意当K>len时&#xff0c;如果每个节点都往右移动len个位置&#xff0c;等价于不移动&#xff0c;所以需要求KK%len。 3.所有元素右移K个位置&#xf…

Golang-channel理解

channel golang-channel语雀笔记整理 channelgolang channel的设计动机&#xff1f;chanel的数据结构/设计思考 golang channel的设计动机&#xff1f; channel是一种不同协程之间实现异步通信的数据结构。golang中有一种很经典的说法是要基于通信实现共享内存&#xff0c;而不…

多表执行嵌套查询,减少笛卡尔积,防止内存溢出

问题&#xff1a;当涉及四个表的查询时&#xff0c;会产生大量的笛卡尔积导致内存溢出。 解决办法 &#xff1a;可以使用嵌套查询将多表的联合查询拆分为单个表的查询&#xff0c;使用resultmap中的association&#xff08;适合一对一&#xff09; 或 collection&#xff08;一…

docker -run hello-world超时

主要原因就是尝试拉取库的时候没有从阿里云镜像里拉&#xff0c;所以设置一下就好了 这里使用的是ubuntu系统&#xff08;命令行下逐行敲就行了&#xff09; sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-EOF {"registry-mirrors": [&quo…

51个图表,完美展示数据分布关系!

本节介绍seaborn展示数据分布关系的图表&#xff08;Distribution plots&#xff09;的实现&#xff0c;该类图表用于展示数据集的分布规律&#xff0c;帮助快速获取数据多方面信息&#xff0c;例如&#xff0c;观测值的范围、中心趋势、是否存在某个方向上严重偏斜、是否存在双…

每日一题(6.22-6.28)

(&#xff61;&#xff65;∀&#xff65;)&#xff89;&#xff9e;嗨&#xff0c;中途考电路分析去了&#xff0c;空了几天的题没有练&#xff0c;为什么三相电路他都没讲过的都要考啊&#xff1f;我服了&#xff0c;什么在Y型三相电路&#xff0c;线电压和相电压的比值都考&…

Hadoop3:Yarn容量调度器配置多队列案例

一、情景描述 需求1&#xff1a; default队列占总内存的40%&#xff0c;最大资源容量占总资源60%&#xff0c;hive队列占总内存的60%&#xff0c;最大资源容量占总资源80%。 二、多队列优点 &#xff08;1&#xff09;因为担心员工不小心&#xff0c;写递归死循环代码&#…

扛鼎中国AI搜索,天工凭什么?

人类的创作不会没有瓶颈&#xff0c;但AI的热度可不会消停。 大模型之战依旧精彩&#xff0c;OpenAI选择在Google前一天举行发布会&#xff0c;两家AI企业之间的拉扯赚足了热度。 反观国内&#xff0c;百模大战激发了大家对于科技变革的热切期盼&#xff0c;而如今行业已逐渐…

20240628每日前端---------解决vue项目滥用watch

主题 滥用watch。 名字解释 watch 例子 先看一个代码例子&#xff1a; <template>{{ dataList }} </template><script setup lang"ts"> import { ref, watch } from "vue";const dataList ref([]); const props defineProps([&q…

MySQL高级-SQL优化-insert优化-批量插入-手动提交事务-主键顺序插入

文章目录 1、批量插入1.1、大批量插入数据1.2、启动Linux中的mysql服务1.3、客户端连接到mysql数据库&#xff0c;加上参数 --local-infile1.4、查询当前会话中 local_infile 系统变量的值。1.5、开启从本地文件加载数据到服务器的功能1.6、创建表 tb_user 结构1.7、上传文件到…

笔记本电脑安装CentOS

正文共&#xff1a;1234 字 24 图&#xff0c;预估阅读时间&#xff1a;2 分钟 前面我们对VPP进行了多次介绍&#xff08;羡慕&#xff01;大佬的VPP能达到180G性能&#xff0c;而我的却只有13.5G&#xff09;&#xff0c;可以发现他的很多优点&#xff0c;但是我们也可以发现它…

从源码分析Springboot自动配置原理

一、什么是Springboot的自动配置 Spring Boot自动配置&#xff08;Auto-configuration&#xff09;是Spring Boot框架的核心特性之一&#xff0c;它使得开发者可以更容易地创建基于Spring的应用程序&#xff0c;而无需进行大量的手动配置。自动配置基于开发者添加的jar依赖项来…

Nuxt3 的生命周期和钩子函数(六)

title: Nuxt3 的生命周期和钩子函数&#xff08;六&#xff09; date: 2024/6/30 updated: 2024/6/30 author: cmdragon excerpt: 摘要&#xff1a;本文深入解析了Nuxt3框架中的多个核心生命周期钩子和组件注册功能&#xff0c;包括imports:sources、imports:extend、import…

【Qt】之【Bug】大量出现“未定义的标识符”问题

背景 构建时出现大量错误 原因 中文注释问题 解决 方法1. 报错代码附近的中文注释全部删掉。。。 方法2. 报错的文件添加 // Chinese word comment solution #pragma execution_character_set("utf-8")

2024年【安全生产监管人员】考试资料及安全生产监管人员考试总结

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 安全生产监管人员考试资料是安全生产模拟考试一点通生成的&#xff0c;安全生产监管人员证模拟考试题库是根据安全生产监管人员最新版教材汇编出安全生产监管人员仿真模拟考试。2024年【安全生产监管人员】考试资料及…

算法力扣刷题 二十六【459.重复的子字符串】

前言 字符串篇&#xff0c;继续。 记录 二十六【459.重复的子字符串】 一、题目阅读 给定一个非空的字符串 s &#xff0c;检查是否可以通过由它的一个子串重复多次构成。 示例 1: 输入: s "abab" 输出: true 解释: 可由子串 "ab" 重复两次构成。示例…

录取查询怎么公布?

这篇文章对于教育工作者来说非常有用&#xff0c;提供了关于如何公布学生录取情况的多种方法和注意事项。以下是对您文章的一些补充和建议&#xff1a; 1.易查分的使用&#xff1a;易查分系统是一个很好的工具&#xff0c;特别是对于那些不擅长技术开发的老师。它简化了查询过程…

40 - 餐馆营业额变化增长(高频 SQL 50 题基础版)

40 - 餐馆营业额变化增长 -- 方法一 SELECT t.visited_on,sum(c.amount) amount,ROUND(sum(c.amount) / 7, 2) average_amount FROM customer c,-- 查出足够7天的数据日期(SELECT DISTINCT visited_onFROM customerWHERE visited_on >(SELECT ADDDATE(MIN(visited_on), 6) F…