二.使用ffmpeg对原始音频数据重采样并进行AAC编码

重采样:将音频三元组【采样率 采样格式 通道数】之中的任何一个或者多个值改变。

一.为什么要进行重采样?

1.原始音频数据和编码器的数据格式不一致

2.播放器要求的和获取的数据不一致

3.方便运算

二.本次编码流程

1.了解自己本机麦克风参数,我的切换为44100/16/2;包括麦克风录音的size可能不一样,本机windows下录音的size为88200;

1.ffmpeg获取麦克风数据

2.ffmpeg对数据进行重采样(本次三元组无需变换)

3.使用AAC编码器对重采样后的数据进行AAC编码,然后存入.aac文件

4.使用ffplay播放测试

三.整体代码

#include "customcodex.hpp"int add_samples_to_fifo(AVAudioFifo *fifo,uint8_t **input_data,const int frame_size)
{int ret = 0;int size = 0;printf("add_samples_to_fifo size:%d \n", frame_size);size = av_audio_fifo_size(fifo) + frame_size;ret = av_audio_fifo_realloc(fifo, size);if (ret < 0){printf("Error, Failed to reallocate fifo!\n");return ret;}ret = av_audio_fifo_write(fifo, reinterpret_cast<void **>(input_data), frame_size);if (ret < frame_size){printf("Error, Failed to write data to fifo!\n");return AVERROR_EXIT;}return 0;
}
int read_fifo_and_encode(AVAudioFifo *fifo,AVFormatContext *fmt_ctx,AVCodecContext *c_ctx,AVFrame *frame)
{int ret = 0;const int frame_size = FFMIN(av_audio_fifo_size(fifo),c_ctx->frame_size);printf("read fifo - size : %d ,c_ctx->frame_size : %d\n", av_audio_fifo_size(fifo), c_ctx->frame_size);ret = av_audio_fifo_read(fifo, reinterpret_cast<void **>(frame->data), frame_size);if (ret < frame_size){printf("Error, Failed to read data from fifo!\n");return AVERROR_EXIT;}return 0;
}
int open_coder(AVCodecContext **codec_ctx)
{// 编码器const AVCodec *codex = avcodec_find_encoder_by_name("libfdk_aac");// codex->capabilities = AV_CODEC_CAP_VARIABLE_FRAME_SIZE;if (!codex){fprintf(stderr, "Codec not found\n");return -1;}// 编码器上下文*codec_ctx = avcodec_alloc_context3(codex);(*codec_ctx)->sample_fmt = AV_SAMPLE_FMT_S16;       // 采样大小(*codec_ctx)->channel_layout = AV_CH_LAYOUT_STEREO; //(*codec_ctx)->channels = 2;                         // 声道数(*codec_ctx)->sample_rate = 44100;                  // 采样率(*codec_ctx)->bit_rate = 0;                         // AAC 128k;AAC HE 64k; AAC_HE V2:32K// codec_ctx->profile = FF_PROFILE_AAC_HE_V2;       // 用哪个AACif (avcodec_open2(*codec_ctx, codex, NULL) < 0){fprintf(stderr, "failed avcodec_open2 \n");return -1;}return 0;
}
int encode(AVCodecContext *codec_ctx, AVFrame *avframe, AVPacket *outpkt, FILE *outfile)
{printf("open_coder - codec_ctx->frame_size: %d ,avframe-size:%d\n", codec_ctx->frame_size, avframe->nb_samples);int ret = avcodec_send_frame(codec_ctx, avframe);printf("avcodec_send_frame:%d\n", ret);if (ret < 0){fprintf(stderr, "Error sending frame to encoder\n");return -1;}while (ret >= 0){// 获取编码后的音频数据ret = avcodec_receive_packet(codec_ctx, outpkt);printf("avcodec_receive_packet:%d\n", ret);if (ret < 0){printf("encode - ret: %d \n", ret);// 有数据但是不够了生成一帧了   没有数据了if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)return 0;exit(-1);}printf("fwrite - outpkt.size: %d \n", outpkt->size);fwrite(outpkt->data, 1, outpkt->size, outfile);fflush(outfile);}return 0;
}int read_audio()
{int ret = 0;char errors[1024];AVFormatContext *fmt_ctx = NULL;AVDictionary *options = NULL;AVAudioFifo *fifo = nullptr;FILE *outfile = fopen("./out.pcm", "wb+");FILE *outfile_aac = fopen("./out.aac", "wb+");if (outfile == nullptr){printf("filed open out file\n");}AVPacket pkt;av_init_packet(&pkt);int frame_count = 0;const char *devicename = "audio=麦克风 (Realtek(R) Audio)";// 找到采集工具const AVInputFormat *iformat = av_find_input_format("dshow");if (iformat == NULL){printf("AVInputFormat find failed \n");return -1;}// 打开音频设备ret = avformat_open_input(&fmt_ctx, devicename, iformat, &options);if (ret < 0){av_strerror(ret, errors, 1024);av_log(NULL, AV_LOG_ERROR, "error:%s", errors);return -1;}// 重采样缓冲区uint8_t **src_data = NULL;int src_linesize = 0;uint8_t **dst_data = NULL;int dst_linesize = 0;// 初始化重采样上下文SwrContext *swr_ctx = initSwr();if (!swr_ctx){printf("failed init swr\n");return -1;}// 编码器上下文AVCodecContext *codec_ctx;ret = open_coder(&codec_ctx);if (ret != 0){return -1;}// 音频输入数据AVFrame *avframe = initInAvframe();//AVPacket *outpkt = av_packet_alloc();av_init_packet(outpkt);// Create the FIFO buffer for the audio samples to be encoded.fifo = av_audio_fifo_alloc(codec_ctx->sample_fmt, codec_ctx->channels, 1);if (!fifo){printf("Error, Failed to alloc fifo!\n");return -1;}// 88200/2=44100/2=22050// 每次读取数据大小是88200,16位2个字节,双声道// 创建输入缓冲区initBuffer(&src_data, src_linesize, &dst_data, dst_linesize);av_log(NULL, AV_LOG_DEBUG, "src-size:%d , dst-size:%d\n", src_linesize, dst_linesize);int count = 0;while (1){int frame_size = codec_ctx->frame_size;static bool finished = false;while (av_audio_fifo_size(fifo) < frame_size){printf("av_audio_fifo_size(fifo) :%d , frame_size :%d\n", av_audio_fifo_size(fifo), frame_size);ret = av_read_frame(fmt_ctx, &pkt);printf("av_read_frame-ret : %d\n", ret);if (ret == 0){printf("pkt-size:%d\n", pkt.size);memcpy((void *)src_data[0], (void *)pkt.data, pkt.size);ret = swr_convert(swr_ctx,                    // 重采样的上下文dst_data,                   // 输出结果缓冲区22050,                      // 每个通道的采样数(const uint8_t **)src_data, // 输入缓冲区22050);                     // 输入单个通道的采样数printf("swr_convert-ret:%d\n", ret);ret = add_samples_to_fifo(fifo, dst_data, 22050);printf("add_samples_to_fifo-ret:%d\n", ret);}if (count >= 20){finished = true;break;}count++;printf("##################### count:%d\n", count);}while (av_audio_fifo_size(fifo) > frame_size || (finished && av_audio_fifo_size(fifo) > 0)){ret = read_fifo_and_encode(fifo, fmt_ctx, codec_ctx, avframe);encode(codec_ctx, avframe, outpkt, outfile_aac);}if (finished){// 强制将编码器缓冲区中的音频进行编码输出encode(codec_ctx, nullptr, outpkt, outfile_aac);break;}}freePtr(src_data, dst_data, swr_ctx, fmt_ctx, outfile, outfile_aac);return 0;
}
void freePtr(uint8_t **src_data, uint8_t **dst_data, SwrContext *swr_ctx, AVFormatContext *fmt_ctx,FILE *outfile, FILE *outfile_aac)
{// 释放输入输出缓冲区if (src_data){av_freep(&src_data[0]);}av_freep(src_data);if (dst_data){av_freep(&dst_data[0]);}av_freep(dst_data);// 释放重采样的上下文swr_free(&swr_ctx);avformat_close_input(&fmt_ctx);fclose(outfile);fclose(outfile_aac);av_log(NULL, AV_LOG_DEBUG, "end");
}
SwrContext *initSwr()
{SwrContext *swr_ctx = swr_alloc();// 设置重采样参数av_opt_set_int(swr_ctx, "in_channel_count", 2, 0);av_opt_set_int(swr_ctx, "in_sample_rate", 44100, 0); // 输入采样率av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);av_opt_set_int(swr_ctx, "out_channel_count", 2, 0);av_opt_set_int(swr_ctx, "out_sample_rate", 44100, 0); // 输出采样率av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);swr_init(swr_ctx);return swr_ctx;
}
AVFrame *initInAvframe()
{AVFrame *avframe = av_frame_alloc();avframe->nb_samples = 1024; // 单通道一个音频的采样数avframe->format = AV_SAMPLE_FMT_S16;avframe->channel_layout = AV_CH_LAYOUT_STEREO; // AV_CH_LAYOUT_STEREOav_frame_get_buffer(avframe, 0);               // 22050*2*2=88200if (!avframe || !avframe->buf){printf("failed get frame buffer\n");return nullptr;}return avframe;
}
void initBuffer(uint8_t ***src_data, int &src_linesize, uint8_t ***dst_data, int &dst_linesize)
{av_samples_alloc_array_and_samples(src_data,          // 输出缓冲区地址&src_linesize,     // 缓冲区的大小2,                 // 通道个数22050,             // 单通道采样个数AV_SAMPLE_FMT_S16, // 采样格式0);// 创建输出缓冲区av_samples_alloc_array_and_samples(dst_data,          // 输出缓冲区地址&dst_linesize,     // 缓冲区的大小2,                 // 通道个数22050,             // 单通道采样个数AV_SAMPLE_FMT_S16, // 采样格式0);
}

四.遇到的问题

1.采样格式和采样个数不明确

解决办法:查看系统声音设置中,相应设备的输出格式,一般包含位深和采样率

采样个数的话通过打开并读取音频数据,可以通过pkt.size打印出来。

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

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

相关文章

器材借用管理系统详细设计基于Spring Boot-SSM

‌ 目录 ‌摘要 一、系统概述‌ ‌二、系统架构设计‌ 2‌.1技术选型‌ ‌2.2系统架构‌ ‌三、需求分析 3.1用户需求分析 3.2功能模块设计‌ 3.3、性能需求分析 3.4、安全需求分析 ‌四、数据库设计‌ ‌五、安全性设计‌ ‌六、系统测试与维护‌ ‌七、总结‌…

麒麟V10 arm cpu aarch64 下编译 RocketMQ-Client-CPP 2.2.0

国产自主可控服务器需要访问RocketMQ消息队列&#xff0c;最新的CSDK是2020年发布的 rocketmq-client-cpp-2.2.0 这个版本支持TLS模式。 用默认的版本安装遇到一些问题&#xff0c;记录一下。 下载Releases apache/rocketmq-client-cpp GitHubhttps://github.com/apache/roc…

C语言每日一练——day_12(最后一天)

引言 针对初学者&#xff0c;每日练习几个题&#xff0c;快速上手C语言。第十二天。&#xff08;最后一天&#xff0c;完结散花啦&#xff09; 采用在线OJ的形式 什么是在线OJ&#xff1f; 在线判题系统&#xff08;英语&#xff1a;Online Judge&#xff0c;缩写OJ&#xff0…

网络安全应急入门到实战

奇安信&#xff1a;95015网络安全应急响应分析报告&#xff08;2022-2024年&#xff09;官网可以下载 https://github.com/Bypass007/Emergency-Response-Notes 应急响应实战笔记 网络安全应急响应技术实战指南 .pdf 常见场景 第4章 勒索病毒网络安全应急响应 第5章 挖矿木…

jvm中每个类的Class对象是唯一的吗

jvm中每个类的Class对象是唯一的吗 在 Java 中&#xff0c;同一个类的 Class 对象在由同一个类加载器加载时是唯一的。析&#xff1a; 1. 同一类加载器的唯一性 规则&#xff1a;若一个类被同一个类加载器加载&#xff0c;无论创建多少实例&#xff0c;其 Class 对象始终唯一…

Visual Studio里的调试(debugging)功能介绍

参考 1- Introduction to Debugging | Basic Visual Studio Debugging&#xff08;这是一位印度博主视频&#xff0c;我下面做到笔记也主要参考她的视频&#xff0c;但不得不说口音太重了&#xff0c;一股咖喱味&#xff09; 目录 个人对调试浅显的认识和对调试的介绍逐行调…

NLP高频面试题(六)——decoder-only、encoder-only和encoder-decoder的区别与联系

一、基本概念与代表模型 1. Encoder-only 架构 Encoder-only 架构最具代表性的模型是 BERT。BERT 使用 masked language modeling&#xff08;MLM&#xff09;进行预训练&#xff0c;即随机遮蔽部分输入词汇&#xff0c;让模型预测被遮蔽的词汇。由于这种架构能够同时看到输入…

如何判断 MSF 的 Payload 是 Staged 还是 Stageless(含 Meterpreter 与普通 Shell 对比)

在渗透测试领域&#xff0c;Metasploit Framework&#xff08;MSF&#xff09;的 msfvenom 工具是生成 Payload&#xff08;载荷&#xff09;的核心利器。然而&#xff0c;当我们选择 Payload 时&#xff0c;经常会遇到一个问题&#xff1a;这个 Payload 是 Staged&#xff08;…

基于FPGA的3U机箱模拟量高速采样板ADI板卡,应用于轨道交通/电力储能等

板卡简介&#xff1a; 本板为模拟量高速采样板&#xff08;ADI&#xff09;&#xff0c;主要用于电机转速和相电流检测&#xff0c;以实现电机闭环控制。 性能规格&#xff1a; 电源&#xff1a;DC5V&#xff0c;DC3.3V&#xff0c;DC15V&#xff0c;DC24V FPGA&#xff1a;…

Gymnasium Cart Pole 环境与 REINFORCE 算法 —— 强化学习入门 2

Title: Gymnasium Cart Pole 环境与 REINFORCE 算法 —— 强化学习入门 2 文章目录 I. Gymnasium Cart Pole 环境II. REINFORCE 算法1. 原理说明2. REINFORCE 算法实现 I. Gymnasium Cart Pole 环境 Gymnasium Cart Pole 环境是一个倒立摆的动力学仿真环境. 状态空间: 0: Ca…

Python高级:GIL、C扩展与分布式系统深度解析

文章目录 &#x1f4cc; **前言**&#x1f527; **第一章&#xff1a;Python语言的本质与生态**1.1 **Python的实现与版本演进**1.2 **开发环境与工具链** &#x1f527; **第二章&#xff1a;元编程与动态特性**2.1 **描述符协议&#xff08;Descriptor Protocol&#xff09;*…

C++学习笔记(二十一)——文件读写

一、文件读写 作用&#xff1a; 文件读写指的是将数据从程序存储到文件&#xff0c;或从文件读取数据&#xff0c;以实现数据的持久化存储。 C 提供了 fstream 头文件&#xff0c;用于文件操作&#xff0c;主要包括&#xff1a; ofstream&#xff08;输出文件流&#xff09;—…

RBA+minibatch的尝试

目录 还是咬着牙来写 RBA了 JAX JAX->TORCH torch tensor的变形 pytorch怎么把一个【3,3,5】的tensor变成【3,10,5】&#xff0c;多的用0填充 pytorch如何把shape【100】转成【100,1】 把torch shape【100,1】变成【100】 SQUEEZE grad_fn 不能两次反向传播 还…

基于Python+Django的二手房信息管理系统

项目介绍 PythonDjango二手房信息管理系统(Pycharm Django Vue Mysql) 平台采用B/S结构&#xff0c;后端采用主流的Python语言进行开发&#xff0c;前端采用主流的Vue.js进行开发。 整个平台包括前台和后台两个部分。 - 前台功能包括&#xff1a;首页、二手房信息、公告管理、…

爬虫基础之爬取猫眼Top100 可视化

网站: TOP100榜 - 猫眼电影 - 一网打尽好电影 本次案例所需用到的模块 requests (发送HTTP请求) pandas(数据处理和分析 保存数据) parsel(解析HTML数据) pyecharts(数据可视化图表) pymysql(连接和操作MySQL数据库) lxml(数据解析模块) 确定爬取的内容: 电影名称 电影主演…

解决Qt信号在构造函数中失效的问题

情景引入&#xff1a;音乐播放器的“幽灵列表”问题 假设你正在开发一个音乐播放器应用&#xff0c;其中有一个功能是用户首次打开应用时&#xff0c;需要从服务器拉取最新的歌曲列表并显示在“本地音乐”页面中。你可能会写出类似这样的代码&#xff1a; // LocalSong 类的构…

Hadoop 启动,发现 namenode、secondary namenodes,这两个没有启动,报错超时。

今天在启动 hadoop 的时候&#xff0c;发现本应该同时启动的 namenode、secondary namenodes 却都没有启动。我还以为是坏了又重新装了虚拟机&#xff0c;重新下载 Hadoop 重新配置结果还是同样的问题&#xff0c;那没办法只能去解决问题了。 首先先再次尝试启动看他报错是什么…

Ranger 鉴权

Apache Ranger 是一个用来在 Hadoop 平台上进行监控&#xff0c;启用服务&#xff0c;以及全方位数据安全访问管理的安全框架。 使用 ranger 后&#xff0c;会通过在 Ranger 侧配置权限代替在 Doris 中执行 Grant 语句授权。 Ranger 的安装和配置见下文&#xff1a;安装和配置 …

Sqlserver安全篇之_启用和禁用Named Pipes的案列介绍

https://learn.microsoft.com/zh-cn/sql/tools/configuration-manager/named-pipes-properties?viewsql-server-ver16 https://learn.microsoft.com/zh-cn/sql/tools/configuration-manager/client-protocols-named-pipes-properties-protocol-tab?viewsql-server-ver16 默认…

深入解析过滤器模式(Filter Pattern):一种灵活高效的设计模式

过滤器模式&#xff08;Filter Pattern&#xff09;&#xff0c;也被称为标准模式&#xff0c;是一种常见的结构型设计模式。它通过将对象分为不同的标准或条件&#xff0c;使得对对象集合的操作变得更加灵活和高效。特别适用于处理复杂查询和条件过滤的场景。过滤器模式不仅能…