[ffmpeg] aac 音频编码

news2025/1/19 3:08:20

aac 介绍

aac 简单说就是音频的一种压缩编码器,相同音质下压缩比 mp3好,目前比较常用。

aac 编码支持的格式

aac 支持的 sample_fmts: 8
在这里插入图片描述

aac 支持的 samplerates: 96000 88200 64000 48000 44100 32000 24000 22050 16000 12000 11025 8000 7350

通过 AVCodec 中的 supported_xx 字段来获取
在这里插入图片描述
具体代码

static int check_sample_fmt(const AVCodec* codec, enum AVSampleFormat sample_fmt)
{
    const enum AVSampleFormat* p = codec->sample_fmts;
    cout << "sample_fmts: ";
    while (*p != AV_SAMPLE_FMT_NONE)
    {
        cout << *p << " ";
        p++;
    }
	cout << endl;
    p = codec->sample_fmts;
    while (*p != AV_SAMPLE_FMT_NONE) {
        if (*p == sample_fmt)
            return 1;
        p++;
    }
    return 0;
}

也可以用命令行获取支持格式,以及可设置的额外参数
在这里插入图片描述

具体实现

编码步骤

// 1. 通过名字或者 id 找到编码器(相当于找到了那个能力结构体指针);获取的结构体会有些编码器的简单介绍,以及编码器支持的能力
// 2. 通过编码器创建上下文,相当于创建上下文实例,并将 codec 指针保存在上下文中,并根据编码器能力初始化一些参数
 // 3. 根据用户需要,以及编码器支持的能力,将编码参数设置到编码器上下文中
 // 4. 根据编码器上下文初始化编码器
 // 5. 创建 avframe 并把编码器上下文中的参数赋值给他
 // 6. avframe 根据参数,算出每次编码需要的内部大小,并分配
 // 7. 将编码数据传给 avframe
 // 8. 将 avframe 传给 avcodec_send_frame
 // 9. 通过 avcodec_receive_packet 获取 avpacket 数据

具体代码

#include <iostream>
using namespace std;
extern"C"
{
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/frame.h>
#include <libavutil/samplefmt.h>
}

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 adts_header(char* const p_adts_header, const int data_length,
	const int profile, const int samplerate,
	const int channels)
{

	int sampling_frequency_index = 3; // 默认使用48000hz
	int adtsLen = data_length + 7;

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

	p_adts_header[0] = 0xff;         //syncword:0xfff                          高8bits
	p_adts_header[1] = 0xf0;         //syncword:0xfff                          低4bits
	p_adts_header[1] |= (0 << 3);    //MPEG Version:0 for MPEG-4,1 for MPEG-2  1bit
	p_adts_header[1] |= (0 << 1);    //Layer:0                                 2bits
	p_adts_header[1] |= 1;           //protection absent:1                     1bit

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

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

	p_adts_header[4] = (uint8_t)((adtsLen & 0x7f8) >> 3);     //frame length:value    中间8bits
	p_adts_header[5] = (uint8_t)((adtsLen & 0x7) << 5);       //frame length:value    低3bits
	p_adts_header[5] |= 0x1f;                                 //buffer fullness:0x7ff 高5bits
	p_adts_header[6] = 0xfc;      //       //buffer fullness:0x7ff 低6bits
	// number_of_raw_data_blocks_in_frame:
	//    表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧。

	return 0;
}

/* select layout with the highest channel count */
static int select_channel_layout(const AVCodec* codec, AVChannelLayout* dst)
{
    const AVChannelLayout* p, * best_ch_layout;
    int best_nb_channels = 0;

    if (!codec->ch_layouts)
    {
        AVChannelLayout layout = AV_CHANNEL_LAYOUT_STEREO;
        return av_channel_layout_copy(dst, &layout);

    }

    p = codec->ch_layouts;
    while (p->nb_channels) {
        int nb_channels = p->nb_channels;

        if (nb_channels > best_nb_channels) {
            best_ch_layout = p;
            best_nb_channels = nb_channels;
        }
        p++;
    }
    return av_channel_layout_copy(dst, best_ch_layout);
}

static void encode(AVCodecContext* ctx, AVFrame* frame, AVPacket* pkt,
    FILE* output)
{
    int ret;

    // 把 frame 传给编码器,调用编码器 cb.encode 函数进行处理
    ret = avcodec_send_frame(ctx, frame);
    if (ret < 0) {
        fprintf(stderr, "Error sending the frame to the encoder\n");
        exit(1);
    }

    /* read all the available output packets (in general there may be any
     * number of them */
    while (ret >= 0) {
        ret = avcodec_receive_packet(ctx, pkt);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            return;
        else if (ret < 0) {
            fprintf(stderr, "Error encoding audio frame\n");
            exit(1);
        }
        char adts_header_buf[7] = { 0 };
        adts_header(adts_header_buf, pkt->size, ctx->profile, ctx->sample_rate, ctx->ch_layout.nb_channels);
        fwrite(adts_header_buf, 1, 7, output);

        fwrite(pkt->data, 1, pkt->size, output);
        av_packet_unref(pkt);
    }
}

int main(int argc, char** argv)
{
	const char* filename;
	AVFrame* frame;
	AVPacket* pkt;
	int i, j, k, ret;
	FILE* f;
	float* samples;
	float t, tincr;

	if (argc <= 1) {
		fprintf(stderr, "Usage: %s <output file>\n", argv[0]);
		return 0;
	}
	filename = argv[1];

    // 1. 通过名字或者 id 找到编码器(相当于找到了那个能力结构体指针);获取的结构体会有些编码器的简单介绍,以及编码器支持的能力
    // AVCodec 和 FFCodec 可以相互转换
    const AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
	if (!codec) {
		fprintf(stderr, "Codec not found\n");
		exit(1);
	}
    // 2. 通过编码器创建上下文,相当于创建上下文实例,并将 codec 指针保存在上下文中,并根据编码器能力初始化一些参数
    AVCodecContext* c = avcodec_alloc_context3(codec);
	if (!c) {
		fprintf(stderr, "Could not allocate audio codec context\n");
		exit(1);
	}
    // 3. 根据用户需要,以及编码器支持的能力,将编码参数设置到编码器上下文中
	c->bit_rate = 64000;
	c->sample_fmt = AV_SAMPLE_FMT_FLTP;
	c->sample_rate = 48000;
	ret = select_channel_layout(codec, &c->ch_layout);
	if (ret < 0)
		exit(1);

    // 4. 进一步初始化编码器和编码器上下文参数,将 options 传到编码器内部,最后调用编码器的 init 函数,初始化编码器
	if (avcodec_open2(c, codec, NULL) < 0) {
		fprintf(stderr, "Could not open codec\n");
		exit(1);
	}

    // 5. 创建 avframe 并把编码器上下文中的参数赋值给他
	frame = av_frame_alloc();
	if (!frame) {
		fprintf(stderr, "Could not allocate audio frame\n");
		exit(1);
	}

	frame->nb_samples = c->frame_size;
	frame->format = c->sample_fmt;
	ret = av_channel_layout_copy(&frame->ch_layout, &c->ch_layout);
	if (ret < 0)
		exit(1);
    // 6. avframe 根据参数,算出 linesize 和 当前格式需要的 data 大小并创建
	ret = av_frame_get_buffer(frame, 0);
	if (ret < 0) {
		fprintf(stderr, "Could not allocate audio data buffers\n");
		exit(1);
	}
	
    // 7. 将编码数据传给 avframe
        f = fopen(filename, "wb");
    if (!f) {
        fprintf(stderr, "Could not open %s\n", filename);
        exit(1);
    }

    pkt = av_packet_alloc();
    if (!pkt) {
        fprintf(stderr, "could not allocate the packet\n");
        exit(1);
    }

    t = 0;
    tincr = 2 * M_PI * 440.0 / c->sample_rate;
    for (i = 0; i < 200; i++) {
        ret = av_frame_make_writable(frame);
        if (ret < 0)
            exit(1);
        for (k = 0; k < c->ch_layout.nb_channels; k++)
        {
			samples = (float*)frame->data[k];
			for (j = 0; j < c->frame_size; j++) {
				samples[j] = sin(t) * 10000;
				t += tincr;
			}
        }
		// 8. 将 avframe 传给 avcodec_send_frame
		// 9. 通过 avcodec_receive_packet 获取 avpacket 数据
        encode(c, frame, pkt, f);
    }

    encode(c, NULL, pkt, f);
    fclose(f);
    av_frame_free(&frame);
    av_packet_free(&pkt);
    avcodec_free_context(&c);

    return 0;
}

现象

用 vlc 波形显示查看输出的曲线图
在这里插入图片描述

备注

ffmpeg demo 在 c++ 环境不能直接编译通过

  1. 添加头文件需要加上 extern “C”
extern"C"
{
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/frame.h>
#include <libavutil/samplefmt.h>
}
  1. 另一个报错不清楚,ffmpeg是怎么编译通过的,c++这边会报错
av_channel_layout_copy(dst, &(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO);

需要改成
AVChannelLayout layout = AV_CHANNEL_LAYOUT_STEREO;
av_channel_layout_copy(dst, &layout);

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

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

相关文章

U盘不仅能在电脑上使用,在手机上也可使用,包括安卓和苹果手机,但苹果的较特殊

许多最好的安卓手机都使用USB-C端口在电脑上充电和来回传输文件,但如果你需要给老板发电子邮件的文件放在闪存驱动器或全尺寸SD卡上呢? 幸运的是,使用廉价的适配器电缆,你可以将USB加密狗或读卡器直接连接到手机上。你甚至可以直接使用USB-C闪存驱动器,以实现更轻松的过程…

带头双向循环链表:一种高效的数据结构

&#x1f493; 博客主页&#xff1a;江池俊的博客⏩ 收录专栏&#xff1a;数据结构探索&#x1f449;专栏推荐&#xff1a;✅cpolar ✅C语言进阶之路&#x1f4bb;代码仓库&#xff1a;江池俊的代码仓库&#x1f525;编译环境&#xff1a;Visual Studio 2022&#x1f389;欢迎大…

Unity DOTS《群体战斗弹幕游戏》核心技术分析之3D角色动画

最近DOTS发布了正式的版本, 我们来分享现在流行基于群体战斗的弹幕类游戏&#xff0c;实现的核心原理。今天给大家介绍大规模战斗群体3D角色的动画如何来实现。 DOTS 对角色动画支持的局限性 截止到Unity DOTS发布的版本1.0.16,目前还是无法很好的支持3D角色动画。在DOTS 的b…

【Python】tensorflow学习的个人纪录(2)

actor.learn(s, a, td_error)def learn(self, s, a, td):s s[np.newaxis, :]feed_dict {self.s: s, self.a: a, self.td_error: td}_, exp_v self.sess.run([self.train_op, self.exp_v], feed_dict)return exp_v输入变量的数值&#xff1a; 步进&#xff1a; []---->[…

算法设计与实现--动态规划篇

什么是动态规划算法 动态规划算法是一种求解复杂问题的方法&#xff0c;通过将原问题分解为相对简单的子问题来求解。其基本思想是将待求解的问题分解为若干个子问题&#xff08;阶段&#xff09;&#xff0c;按顺序求解子阶段&#xff0c;前一子问题的解&#xff0c;为后一子…

割裂式“多渠道”不是真正的全渠道!浅析全渠道零售和DTC在理念上的不谋而合|徐礼昭

图文&#xff1a;徐礼昭 全渠道零售概念解析 全渠道零售概念由来已久&#xff0c;单纯从业务经营角度&#xff0c;一个品牌在线上线下多个渠道铺货卖货&#xff0c;只能说是多渠道零售&#xff0c;而不是全渠道零售。商派市场负责人徐礼昭认为&#xff0c;品牌企业应该从消费者…

前后端分离部署https

引用&#xff1a;https://blog.csdn.net/weixin_35676679/article/details/127841598 前后端部署&#xff0c;&#xff0c;一般用的是nginx和java&#xff0c;&#xff0c;&#xff0c; 下载SSL证书&#xff1a; java配置https 将证书配置到springboot中 server:port: 544…

为什么要构建指标中台?数据指标的问题

1、综合内部实践和外部交流&#xff0c;指标使用问题主要集中在以下六大方面&#xff1a; 指标口径不一致&#xff1a;常规数据质量问题统计中&#xff0c;约有 31% 涉及指标口径问题&#xff1b; 指标入口不统一&#xff1a;缺少一个企业级的统一消费入口&#xff0c;不知道从…

Ubuntu22.04无需命令行将软件更新源切换到国内

1、右上角打开设置 2、在设置中拉到最下面点击About&#xff0c;然后点击Software Updates 3、点击下拉框 4、选择other 5、找到China&#xff0c;选择一个网址&#xff0c;然后点击Choose Server 6、输入密码并回车 7、点击Close 8、点击Reload 9、等待完成即可 10、等结束之后…

【数电笔记】16-卡诺图绘制(逻辑函数的卡诺图化简)

目录 说明&#xff1a; 最小项卡诺图的组成 1. 相邻最小项 2. 卡诺图的组成 2.1 二变量卡诺图 2.2 三表变量卡诺图 2.3 四变量卡诺图 3. 卡诺图中的相邻项&#xff08;几何相邻&#xff09; 说明&#xff1a; 笔记配套视频来源&#xff1a;B站&#xff1b;本系列笔记并…

【STM32】STM32学习笔记-软件安装(03)

00. 目录 文章目录 00. 目录01. MDK安装02. Keil5注册03. 支持包安装04. ST-LINK驱动安装05. USB转串口驱动06. 附录 01. MDK安装 MDK 源自德国的 KEIL 公司&#xff0c;是 RealView MDK 的简称。在全球 MDK 被超过 10 万的嵌入式开发工程师使用。目前最新版本为&#xff1a; …

Python生产者消费者模型

额滴名片儿 &#x1f388; 博主&#xff1a;一只程序猿子 &#x1f388; 博客主页&#xff1a;一只程序猿子 博客主页 &#x1f388; 个人介绍&#xff1a;爱好(bushi)编程&#xff01; &#x1f388; 创作不易&#xff1a;如喜欢麻烦您点个&#x1f44d;或者点个⭐&#xff01…

Python面向对象⑤:多态【侯小啾python领航班系列(二十三)】

Python面向对象⑤:多态【侯小啾python领航班系列(二十三)】 大家好,我是博主侯小啾, 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹…

Pytest测试攻略:探寻pytest.main()隐藏的利器

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 在Pytest测试框架中&#xff0c;pytest.main()是一个重要的功能&#xff0c;用于启动测试执行。它允许以不同方式运行测试&#xff0c;传递参数和配置选项。本文将深入探讨pytest.main()的核心功能&#xff0c;提…

大数据技术学习笔记(七)—— Zookeeper

目录 1 Zookeeper 概述1.1 Zookeeper 定义1.2 Zookeeper 工作机制1.3 Zookeeper 特点1.4 数据结构1.5 应用场景 2 Zookeeper 安装3 客户端命令行操作4 Zookeeper 的 Java 客户端操作4.1 IDEA 环境搭建4.2 初始化 ZooKeeper 客户端4.3 创建子节点4.4 获取子节点4.5 判断Znode是否…

根据豆瓣对《流浪地球》的短评数据进行文本分析和挖掘

1背景 2019年2月5日电影《流浪地球》正式在中国内地上映。该电影在举行首映的时候&#xff0c;口德好得出奇&#xff0c;所有去看片的业界大咖都发出了画样赞叹&#xff0c;文化学者能锦说:“中国科幻电影元年开启了。"导演徐峰则说&#xff0c;“里程碑式的电影&#xf…

Debian12配置ssh服务器

Debian12配置ssh服务器 安装ssh-server sudo apt install openssh-server启动ssh sudo systemctl start ssh启用ssh sudo systemctl enable ssh查看ssh状态 sudo systemctl status ssh可以看到有enabled和running字样 说明ssh启用成功 连接到服务器 # username是你的用…

洛谷 P5715 三位数排序 C++代码

目录 前言 思路点拨 AC代码1 AC代码2 AC代码3 结尾 前言 今天我们来做洛谷上的一道题目。 网址&#xff1a;【深基3.例8】三位数排序 - 洛谷 思路点拨 ​ 这题思路很简单&#xff0c;就是普通的排序题目。 但是我们要学习小题大做这个道理&#xff0c;于是我将介绍三…

第十五届蓝桥杯模拟赛(第二期)

大家好&#xff0c;我是晴天学长&#xff0c;本次分享&#xff0c;制作不易&#xff0c;本次题解只用于学习用途&#xff0c;如果有考试需要的小伙伴请考完试再来看题解进行学习&#xff0c;需要的小伙伴可以点赞关注评论一波哦&#xff01;后续会继续更新第三期的。&#x1f4…

Nacos 客户端版本从1.x 升级到 2.x 的排坑记

问题描述 应用引入 Nacos Config 配置管理功能&#xff0c;应用启动时读取 Nacos 配置中心的配置作为启动参数&#xff0c;其中包括数据源信息 url 。 当 Nacos 正在进行 GC 操作、无法响应客户端请求时&#xff0c;应用端刚启动时发送的登录认证请求 http://IP:PORT/nacos/v…