[ffmpeg] aac 音频编码

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,  // 0x088200,  // 0x164000,  // 0x248000,  // 0x344100,  // 0x432000,  // 0x524000,  // 0x622050,  // 0x716000,  // 0x812000,  // 0x911025,  // 0xa8000   // 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; // 默认使用48000hzint 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                          高8bitsp_adts_header[1] = 0xf0;         //syncword:0xfff                          低4bitsp_adts_header[1] |= (0 << 3);    //MPEG Version:0 for MPEG-4,1 for MPEG-2  1bitp_adts_header[1] |= (0 << 1);    //Layer:0                                 2bitsp_adts_header[1] |= 1;           //protection absent:1                     1bitp_adts_header[2] = (profile) << 6;            //profile:profile               2bitsp_adts_header[2] |= (sampling_frequency_index & 0x0f) << 2; //sampling frequency index:sampling_frequency_index  4bitsp_adts_header[2] |= (0 << 1);             //private bit:0                   1bitp_adts_header[2] |= (channels & 0x04) >> 2; //channel configuration:channels  高1bitp_adts_header[3] = (channels & 0x03) << 6; //channel configuration:channels 低2bitsp_adts_header[3] |= (0 << 5);               //original:0                1bitp_adts_header[3] |= (0 << 4);               //home:0                    1bitp_adts_header[3] |= (0 << 3);               //copyright id bit:0        1bitp_adts_header[3] |= (0 << 2);               //copyright id start:0      1bitp_adts_header[3] |= ((adtsLen & 0x1800) >> 11);           //frame length:value   高2bitsp_adts_header[4] = (uint8_t)((adtsLen & 0x7f8) >> 3);     //frame length:value    中间8bitsp_adts_header[5] = (uint8_t)((adtsLen & 0x7) << 5);       //frame length:value    低3bitsp_adts_header[5] |= 0x1f;                                 //buffer fullness:0x7ff 高5bitsp_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. 将编码数据传给 avframef = 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.rhkb.cn/news/211529.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

git push 报错 error: src refspec master does not match any 解决

git报错 ➜ *** git:(main) git push -u origin "master" error: src refspec master does not match any error: failed to push some refs to https://gitee.com/***/***.git最新版的仓库初始化后 git 主分支变成了 main 方法 1.把 git 默认分支名改回 master …

maven生命周期回顾

目录 文章目录 **目录**两种最常用打包方法&#xff1a;生命周期&#xff1a; 两种最常用打包方法&#xff1a; 1.先 clean&#xff0c;然后 package2.先 clean&#xff0c;然后install 生命周期&#xff1a; 根据maven生命周期&#xff0c;当你执行mvn install时&#xff0c…

Python函数

1.函数 1.1 函数概述 函数定义和优势 不同形状正方形打印 # 2个 for i in range(0, 2):for j in range(0, 2):print("*", end"")print() # 3个 for i in range(0, 3):for j in range(0, 3):print("*", end"")print() # 4个 for i …

Linux:dockerfile编写搭建nginx练习(8)

dockerfile是创建镜像的一种&#xff0c;通过已有镜像的基础上再在上面部署一些别的。 在这个基础镜像上搭建&#xff0c;我这个是一个空的centos镜像 我这里用http的yum仓库存放了nginx和rpm包 创建dockerfile vim Dockerfile写入#设置基础镜像 FROM centos#维护该镜像的用户…

redis------在java中操作redis

Redis&#xff08;非关系型数据库&#xff09;简介 redis下载 点击即可进入redis中文网进行下载 百度网盘windows版本 提取码 DMH6 redis主要特点 基于内存存储&#xff0c;读写性能高 适合存储热点数据&#xff08;热点商品、资讯、新闻&#xff09; 企业应用广泛 redis不同…

SQL Server 2016(创建数据库)

1、实验环境。 某公司有一台已经安装了SQL Server 2016的服务器&#xff0c;现在需要新建数据库。 2、需求描述。 创建一个名为"db_class"的数据库&#xff0c;数据文件和日志文件初始大小设置为10MB&#xff0c;启用自动增长&#xff0c;数据库文件存放路径为C:\db…

Gti GUI添加标签

通过Git Gui打开项目&#xff0c;通过菜单打开分支历史&#xff0c;我这里是名为"develop"的分支 选中需要打标签的commit&#xff0c;右键-Create tag即可 但貌似无法删除标签&#xff0c;只能通过git bash

linux NAT网卡配置static

由于是内网&#xff0c;资料无法拷贝&#xff0c;借助参考资料&#xff0c;整理发出。 镜像安装 基本操作。 查看VM配置 图1&#xff0c;有几个信息。一个是NAT借用了网卡里的VMnet8适配器。 子网IP是从192.168.142.0 子网掩码255.255.255.255&#xff0c;对应下面配置的N…

CoreDNS实战(五)-接入prometheus监控

1 背景 Prometheus插件作为coredns的Plugins&#xff0c;默认情况下是内置在coredns中&#xff0c;如果是自己编译安装的版本&#xff0c;需要注意在编译安装的时候的plugin.cfg文件中添加了prometheus:metrics&#xff0c;这样才能确保编译成功。 # 首先我们检查一下运行的版…

【从零认识ECS云服务器 | 快速上线个人网站】二、使用ECS云服务器

第二章 使用ECS 2.1 获取ECS 方式一&#xff1a;通过试用中心免费领取ECS实例 满足以下全部条件的阿里云用户&#xff0c;可免费试用云服务器ECS&#xff1a; 阿里云注册会员用户并完成阿里云企业认证或个人认证用户。申请用户是云服务器ECS产品的新用户&#xff0c;可以申…

【链表Linked List】力扣-2 两数相加

目录 题目描述 解题过程 题目描述 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 …

Vue学习计划-Vue2--Vue核心(二)Vue代理方式

Vue data中的两种方式 对象式 data:{}函数式 data(){return {} }示例&#xff1a; <body><div id"app">{{ name }} {{ age}} {{$options}}<input type"text" v-model"value"></div><script>let vm new Vue({el: …

[JavaScript前端开发及实例教程]计算器井字棋游戏的实现

计算器&#xff08;网页内实现效果&#xff09; HTML部分 <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>My Calculator&l…

【头歌系统数据库实验】实验6 SQL的多表查询-2

目录 第1关&#xff1a;查询每个选手的信息及其提交的解答信息&#xff0c;没做题的选手不显示 第2关&#xff1a;查询做了1001题且耗时大于500&#xff08;time&#xff09;的选手信息 第3关&#xff1a;查询所有选手信息及其提交的解答信息&#xff0c;没做题的选手也要显…

css所有属性介绍

文章目录 1️⃣ CSS属性介绍1.1 CSS3 动画属性&#xff08;Animation&#xff09;1.2 CSS 背景属性&#xff08;Background&#xff09;1.3 CSS 边框属性&#xff08;Border 和 Outline&#xff09;1.4 Box 属性1.5 Color 属性1.6 Content for Paged Media 属性1.7 CSS 尺寸属性…

基于 Vue、Datav、Echart 框架的 “ 数据大屏项目 “,通过 Vue 组件实现数据动态刷新渲染,内部图表可实现自由替换

最近在研究大数据分析&#xff0c;基于 Vue、Datav、Echart 框架的 " 数据大屏项目 "&#xff0c;通过 Vue 组件实现数据动态刷新渲染&#xff0c;内部图表可实现自由替换。部分图表使用 DataV 自带组件&#xff0c;可进行更改&#xff0c;详情请点击下方 DataV 文档…

【全栈开发】使用NestJS、Angular和Prisma 打造全栈Typescript开发

在开发Angular应用程序时&#xff0c;我非常喜欢Typescript。使用NestJS&#xff0c;您可以以与Angular非常相似的方式编写后端。 我偶然发现了这个库&#xff0c;发现它非常有趣&#xff0c;所以我想设置一个简单的测试项目。一般来说&#xff0c;我主要使用SQL数据库&#x…

C语言给定数字0-9各若干个。你可以以任意顺序排列这些数字,但必须全部使用。目标是使得最后得到的数尽可能小(注意0不能做首位)

这个题目要求的输出是一串数字&#xff01;&#xff01;&#xff01; 不是下面&#xff1a;输入在一行中给出 10 个非负整数&#xff0c;顺序表示我们拥有数字 0、数字 1、……数字 9 的个数。整数间用一个空格分隔。10 个数字的总个数不超过 50&#xff0c;且至少拥有 1 个非…

0基础学java-day15

一、泛型 1 泛型的理解和好处 1.1 看一个需求 【不小心加入其它类型&#xff0c;会导致出现类型转换异常】 package com.hspedu.generic;import java.util.ArrayList;/*** author 林然* version 1.0*/ public class Generic01 {SuppressWarnings("all")public st…

【前端】-【electron】

文章目录 介绍electron工作流程环境搭建 electron生命周期&#xff08;app的生命周期&#xff09;窗口尺寸窗口标题自定义窗口的实现阻止窗口关闭父子及模态窗口自定义菜单 介绍 electron技术架构&#xff1a;chromium、node.js、native.apis electron工作流程 桌面应用就是…