音视频过滤器实战--音频混音

1、添加并初始化音频输入、输出流

2、打开输入、输出音频文件

3、添加音频帧数据,然后循环获取输出的音频帧,将音频数据写文件保存

4、代码实例

audiomixer.h

#ifndef AUDIOMIXER_H
#define AUDIOMIXER_H#include <map>
#include <mutex>
#include <cstdio>
#include <cstdint>
#include <string>
#include <memory>extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/opt.h>
}class AudioMixer
{
public:AudioMixer();virtual ~AudioMixer();int addAudioInput(uint32_t index, uint32_t samplerate, uint32_t channels, uint32_t bitsPerSample, AVSampleFormat format);int addAudioOutput(const uint32_t samplerate, const uint32_t channels,const uint32_t bitsPerSample, const AVSampleFormat format);int init(const  char *duration = "longest");int exit();int addFrame(uint32_t index, uint8_t *inBuf, uint32_t size);int getFrame(uint8_t *outBuf, uint32_t maxOutBufSize);private:struct AudioInfo{AudioInfo(){filterCtx = nullptr;}uint32_t samplerate;uint32_t channels;uint32_t bitsPerSample;AVSampleFormat format;std::string name;AVFilterContext *filterCtx;};bool initialized_ = false;std::mutex mutex_;std::map<uint32_t, AudioInfo> audio_input_info_;std::shared_ptr<AudioInfo> audio_output_info_;std::shared_ptr<AudioInfo> audio_mix_info_;std::shared_ptr<AudioInfo> audio_sink_info_;AVFilterGraph *filter_graph_ = nullptr;
};
#endif // AUDIOMIXER_H

audiomixer.cpp

#include "audiomixer.h"AudioMixer::AudioMixer(): initialized_(false), filter_graph_(nullptr), audio_output_info_(nullptr)
{audio_mix_info_.reset(new AudioInfo);audio_mix_info_->name = "amix";     // 混音用的audio_sink_info_.reset(new AudioInfo);audio_sink_info_->name = "sink";    // 输出
}AudioMixer::~AudioMixer()
{if(initialized_) {exit();}
}int AudioMixer::addAudioInput(uint32_t index, uint32_t samplerate, uint32_t channels,uint32_t bitsPerSample, AVSampleFormat format)
{std::lock_guard<std::mutex> locker(mutex_);if (initialized_){return -1;}if (audio_input_info_.find(index) != audio_input_info_.end()){return -1;      // 已经存在则返回-1}// 初始化一个input 可以有多个输入auto& filterInfo = audio_input_info_[index];// 初始化音频相关的参数filterInfo.samplerate = samplerate;filterInfo.channels = channels;filterInfo.bitsPerSample = bitsPerSample;filterInfo.format = format;filterInfo.name = std::string("input") + std::to_string(index);return 0;
}int AudioMixer::addAudioOutput(const uint32_t samplerate, const uint32_t channels,const uint32_t bitsPerSample, const AVSampleFormat format)
{std::lock_guard<std::mutex> locker(mutex_);if (initialized_){return -1;}// 初始化输出相关的参数       只有一个输出audio_output_info_.reset(new AudioInfo);audio_output_info_->samplerate = samplerate;audio_output_info_->channels = channels;audio_output_info_->bitsPerSample = bitsPerSample;audio_output_info_->format = format;audio_output_info_->name = "output";return 0;
}
/*
inputsThe number of inputs. If unspecified, it defaults to 2.//输入的数量,如果没有指明,默认为2.
durationHow to determine the end-of-stream.//决定了流的结束longestThe duration of the longest input. (default)//最长输入的持续时间shortestThe duration of the shortest input.//最短输入的持续时间firstThe duration of the first input.//第一个输入的持续时间 比如直播,mic为主的时候 first,
dropout_transitionThe transition time, in seconds, for volume renormalization when an input stream ends.The default value is 2 seconds.
*/
/*** @brief 初始化* @param duration longest最长输入时间,shortest最短,first第一个输入持续的时间* @return*/
int AudioMixer::init(const  char *duration)
{std::lock_guard<std::mutex> locker(mutex_);if (initialized_){return -1;}if (audio_input_info_.size() == 0){return -1;}filter_graph_ = avfilter_graph_alloc(); // 创建avfilter_graphif (filter_graph_ == nullptr){return -1;}char args[512] = {0};const AVFilter *amix = avfilter_get_by_name("amix");    // 混音audio_mix_info_->filterCtx = avfilter_graph_alloc_filter(filter_graph_, amix, "amix");/*inputs=输入流数量, duration=决定流的结束,* dropout_transition= 输入流结束时,容量重整时间,* (longest最长输入时间,shortest最短,first第一个输入持续的时间))*/snprintf(args, sizeof(args), "inputs=%d:duration=%s:dropout_transition=0",audio_input_info_.size(), duration);if (avfilter_init_str(audio_mix_info_->filterCtx, args) != 0){printf("[AudioMixer] avfilter_init_str(amix) failed.\n");return -1;}const AVFilter *abuffersink = avfilter_get_by_name("abuffersink");audio_sink_info_->filterCtx = avfilter_graph_alloc_filter(filter_graph_, abuffersink, "sink");if (avfilter_init_str(audio_sink_info_->filterCtx, nullptr) != 0){printf("[AudioMixer] avfilter_init_str(abuffersink) failed.\n");return -1;}for (auto& iter : audio_input_info_){const AVFilter *abuffer = avfilter_get_by_name("abuffer");snprintf(args, sizeof(args),"sample_rate=%d:sample_fmt=%s:channel_layout=0x%I64x",iter.second.samplerate,av_get_sample_fmt_name(iter.second.format),av_get_default_channel_layout(iter.second.channels));printf("[AudioMixer] input(%d) args: %s\n", iter.first, args);iter.second.filterCtx = avfilter_graph_alloc_filter(filter_graph_, abuffer,audio_output_info_->name.c_str());if (avfilter_init_str(iter.second.filterCtx, args) != 0){printf("[AudioMixer] avfilter_init_str(abuffer) failed.\n");return -1;}// iter.first 是input indexif (avfilter_link(iter.second.filterCtx, 0, audio_mix_info_->filterCtx, iter.first) != 0){printf("[AudioMixer] avfilter_link(abuffer(%d), amix) failed.", iter.first);return -1;}}if (audio_output_info_ != nullptr){const AVFilter *aformat = avfilter_get_by_name("aformat");snprintf(args, sizeof(args),"sample_rates=%d:sample_fmts=%s:channel_layouts=0x%I64x",audio_output_info_->samplerate,av_get_sample_fmt_name(audio_output_info_->format),av_get_default_channel_layout(audio_output_info_->channels));printf("[AudioMixer] output args: %s\n", args);audio_output_info_->filterCtx = avfilter_graph_alloc_filter(filter_graph_, aformat,"aformat");if (avfilter_init_str(audio_output_info_->filterCtx, args) != 0){printf("[AudioMixer] avfilter_init_str(aformat) failed. %s\n", args);return -1;}if (avfilter_link(audio_mix_info_->filterCtx, 0, audio_output_info_->filterCtx, 0) != 0){printf("[AudioMixer] avfilter_link(amix, aformat) failed.\n");return -1;}if (avfilter_link(audio_output_info_->filterCtx, 0, audio_sink_info_->filterCtx, 0) != 0){printf("[AudioMixer] avfilter_link(aformat, abuffersink) failed.\n");return -1;}}if (avfilter_graph_config(filter_graph_, NULL) < 0){printf("[AudioMixer] avfilter_graph_config() failed.\n");return -1;}initialized_ = true;return 0;
}int AudioMixer::exit()
{std::lock_guard<std::mutex> locker(mutex_);if (initialized_){for (auto iter : audio_input_info_){if (iter.second.filterCtx != nullptr){avfilter_free(iter.second.filterCtx);}}audio_input_info_.clear();if (audio_output_info_ && audio_output_info_->filterCtx){avfilter_free(audio_output_info_->filterCtx);audio_output_info_->filterCtx = nullptr;}if (audio_mix_info_->filterCtx){avfilter_free(audio_mix_info_->filterCtx);audio_mix_info_->filterCtx = nullptr;}if (audio_sink_info_->filterCtx){avfilter_free(audio_sink_info_->filterCtx);audio_sink_info_->filterCtx = nullptr;}avfilter_graph_free(&filter_graph_);filter_graph_ = nullptr;initialized_ = false;}return 0;
}int AudioMixer::addFrame(uint32_t index, uint8_t *inBuf, uint32_t size)
{std::lock_guard<std::mutex> locker(mutex_);if (!initialized_){return -1;}auto iter = audio_input_info_.find(index);if (iter == audio_input_info_.end()){return -1;}if(inBuf && size > 0) {std::shared_ptr<AVFrame> avFrame(av_frame_alloc(), [](AVFrame *ptr) { av_frame_free(&ptr); });avFrame->sample_rate = iter->second.samplerate;avFrame->format = iter->second.format;avFrame->channel_layout = av_get_default_channel_layout(iter->second.channels);avFrame->nb_samples = size * 8 / iter->second.bitsPerSample / iter->second.channels;av_frame_get_buffer(avFrame.get(), 1);memcpy(avFrame->extended_data[0], inBuf, size);if (av_buffersrc_add_frame(iter->second.filterCtx, avFrame.get()) != 0){return -1;}} else {if (av_buffersrc_add_frame(iter->second.filterCtx, NULL) != 0){return -1;}}return 0;
}int AudioMixer::getFrame(uint8_t *outBuf, uint32_t maxOutBufSize)
{std::lock_guard<std::mutex> locker(mutex_);if (!initialized_){return -1;}std::shared_ptr<AVFrame> avFrame(av_frame_alloc(), [](AVFrame *ptr) { av_frame_free(&ptr); });int ret = av_buffersink_get_frame(audio_sink_info_->filterCtx, avFrame.get());if (ret < 0){//        printf("ret = %d, %d\n", ret, AVERROR(EAGAIN));return -1;}int size = av_samples_get_buffer_size(NULL, avFrame->channels, avFrame->nb_samples, (AVSampleFormat)avFrame->format, 1);if (size > (int)maxOutBufSize){return 0;}memcpy(outBuf, avFrame->extended_data[0], size);return size;
}

main.cpp

#include "audiomixer.h"//ffmpeg -i buweishui_1m.mp3 -i huiguniang.mp3 -filter_complex amix=inputs=2:duration=longest:dropout_transition=3 out.mp3 -y#define PCM1_FRAME_SIZE (4096*2)        // 需要和格式匹配 4*1024*2*
#define PCM2_FRAME_SIZE (4096)
#define PCM_OUT_FRAME_SIZE (40000)
int main(int argc, char **argv)
{AudioMixer amix;// 输入流amix.addAudioInput(0, 48000, 2, 32, AV_SAMPLE_FMT_FLT); // 48000_2_f32le.pcmamix.addAudioInput(1, 48000, 2, 16, AV_SAMPLE_FMT_S16); // 48000_2_s16le.pcm// 输出流amix.addAudioOutput(96000, 2, 16, AV_SAMPLE_FMT_S16);// init之前,要先添加输入源和输出源if (amix.init("longest") < 0) {return -1;}int len1 = 0, len2 = 0;uint8_t buf1[PCM1_FRAME_SIZE];uint8_t buf2[PCM2_FRAME_SIZE];FILE *file1 = fopen("48000_2_f32le.pcm", "rb");if(!file1) {printf("fopen 48000_2_f32le.pcm failed\n");return -1;}FILE *file2 = fopen("48000_2_s16le.pcm", "rb");if(!file2) {printf("fopen 48000_2_s16le.pcm failed\n");return -1;}FILE* file_out = fopen("output.pcm", "wb");if(!file_out) {printf("fopen output.pcm failed\n");return -1;}uint8_t out_buf[PCM_OUT_FRAME_SIZE];uint32_t out_size = 0;int file1_finish = 0;int file2_finish = 0;while (1) {len1 = fread(buf1, 1, PCM1_FRAME_SIZE, file1);len2 = fread(buf2, 1, PCM2_FRAME_SIZE, file2);if (len1 > 0 || len2 > 0) {if (len1 > 0) {if(amix.addFrame(0, buf1, len1) < 0) {printf("amix.addFrame(0, buf1, len1) failed\n");break;}} else {if(file1_finish == 0) {file1_finish = 1;if(amix.addFrame(0, NULL, 0) < 0) {     // 空包冲刷,人家才知道你某一路某一数据printf("amix.addFrame(0, buf1, len1) failed\n");}}}if (len2 > 0){if(amix.addFrame(1, buf2, len2) < 0) {   // 空包冲刷,人家才知道你某一路某一数据printf("amix.addFrame(1, buf2, len2) failed\n");break;}} else {if(file2_finish == 0) {file2_finish = 1;if(amix.addFrame(1, NULL, 0) < 0) {printf("amix.addFrame(1, buf2, len2) failed\n");}}}int ret = 0; //amix.getFrame(out_buf, 10240);while ((ret = amix.getFrame(out_buf, 10240)) >=0) {out_size += ret;if(out_size % (1024*1024) ==0)printf("mix audio: %d, out_size:%u\n", ret, out_size);fwrite(out_buf, 1, ret, file_out);}}else{printf("two file finish\n");break;}}printf("end, out_size:%u\n", out_size);amix.exit();if(file_out)fclose(file_out);if(file1)fclose(file1);if(file2)fclose(file2);getchar();return 0;
}

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

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

相关文章

0基础如何进入IT行业?

0基础如何进入IT行业&#xff1f; 简介&#xff1a;对于没有任何相关背景知识的人来说&#xff0c;如何才能成功进入IT行业&#xff1f;是否有一些特定的方法或技巧可以帮助他们实现这一目标&#xff1f;我不知道&#xff0c;我的行业算不算是IT&#xff0c;或者最多是半个IT行…

重磅!openGauss6.0创新版本,带着新特性正式发布了!

&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&#x1f61c;&#x1f61c; 中国DBA联盟(ACD…

Tokio强大的Rust异步框架

一、简介 异步是现在编写高性能应用的核心要素之一&#xff0c;很多语言都已经纷纷加入异步的特效。C#应该是最先做出响应的语言。连微软这位软件大佬都非常重视异步&#xff0c;可见异步编程重要性。 在Rust中同样支持异步编程&#xff0c;只不过一般我们不会使用Rust官方&a…

从“量子”到分子:探索计算的无限可能 | 综述荐读

在2023年年末&#xff0c;两篇划时代的研究报告在《科学》&#xff08;Science&#xff09;杂志上引发了广泛关注。这两篇论文分别来自两个研究小组&#xff0c;它们共同揭示了单氟化钙分子间相互作用的研究成果&#xff0c;成功地在这些分子间创造出了分子量子比特。这一成就不…

算法练习—day1

title: 算法练习—day1 date: 2024-04-03 21:49:55 tags: 算法 categories:LeetCode typora-root-url: 算法练习—day1 网址&#xff1a;https://red568.github.io 704. 二分查找 题目&#xff1a; 题目分析&#xff1a; 左右指针分别为[left,right]&#xff0c;每次都取中…

【机器学习】K-近邻算法(KNN)介绍、应用及文本分类实现

一、引言 1.1 K-近邻算法&#xff08;KNN&#xff09;的基本概念 K-近邻算法&#xff08;K-Nearest Neighbors&#xff0c;简称KNN&#xff09;是一种基于实例的学习算法&#xff0c;它利用训练数据集中与待分类样本最相似的K个样本的类别来判断待分类样本所属的类别。KNN算法…

ArcGIS Pro导出布局时去除在线地图水印

目录 一、背景 二、解决方法 一、背景 在ArcGIS Pro中经常会用到软件自带的在线地图&#xff0c;但是在导出布局时&#xff0c;图片右下方会自带地图的水印 二、解决方法 解决方法&#xff1a;添加动态文本--服务图层制作者名单&#xff0c;然后在布局中选定位置添加 在状…

FPGA_mipi

1 mipi接口 mipi(移动行业处理器接口&#xff0c;是为高速数据传输量身定做的&#xff0c;旨在解决日益增长的高清图像(视频)传输的高带宽要求与传统接口低速率之间的矛盾。 采用差分信号传输&#xff0c;在设计时需要按照差分设计的一般规则进行严格的设计。 mipi协议提出之际…

dhcp中继代理

不同过路由器分配ip了&#xff0c;通过一台服务器来代替&#xff0c;路由器充当中继代理功能&#xff0c;如下图 服务器地址&#xff1a;172.10.1.1/24 配置流程&#xff1a; 1.使能dhcp功能 2.各个接口网关地址&#xff0c;配置dhcp中继功能 dhcp select relay &#xff0…

Qt | 发布程序(以 minGW 编译器为例)

1、注意:修改 pro 文件后,最好执行“构建”>“重新构建项目”,否则 pro 文件的更改将不会反应到程序上。 2、发布程序的目的:就是让编译后生成的可执行文件(如 exe 文件),能在其他计算机上运行。 一、编译后生成的各种文件简介 Qt Creator 构建项目后产生的文件及目录…

实时渲染 -- 材质(Materials)

一、自然界中的材质 首先了解下自然界中的材质 如上这幅图&#xff0c;不同的物体、场景、组合&#xff0c;会让我们看到不同的效果。 我们通常认为物体由其表面定义&#xff0c;表面是物体和其他物体或周围介质之间的边界面。但是物体内部的材质也会影响光照效果。我们目前只…

C++ 程序自动重启(windows 有源码)

初级代码游戏的专栏介绍与文章目录-CSDN博客 程序长时间运行&#xff0c;内存泄漏&#xff0c;最后崩溃&#xff0c;怎么办&#xff1f; 程序24小时运行&#xff0c;偶发随机崩溃&#xff0c;怎么办&#xff1f; 啃代码、内存泄漏检查工具、分析线程交互……没人敢承诺解决问题…

续二叉搜索树递归玩法

文章目录 一、插入递归二、寻找递归&#xff08;非常简单&#xff0c;走流程就行&#xff09;三、插入递归&#xff08;理解起来比较麻烦&#xff09; 先赞后看&#xff0c;养成习惯&#xff01;&#xff01;&#xff01;^ _ ^<3 ❤️ ❤️ ❤️ 码字不易&#xff0c;大家的…

第六题:标题统计

题目描述 凯凯刚写了一篇美妙的作文&#xff0c;请问这篇作文的标题中有多少个字符&#xff1f; 注意&#xff1a;标题中可能包含大、小写英文字母、数字字符、空格和换行符。统计标题字符数时&#xff0c;空格和换行符不计算在内。 输入描述 输入文件只有一行&#xff0c;…

Java中IO、BIO、NIO、AIO分别是什么?

在Java中&#xff0c;IO、BIO、NIO、和AIO分别指不同的输入/输出处理模型。这些模型在处理数据流和网络通信时有各自的特点和使用场景。它们之间的区别&#xff1a; BIO (Blocking IO) - 同步阻塞IO 想象你在餐厅点餐。BIO就像是一对一的服务方式&#xff0c;即一个服务员为一…

ROS 2边学边练(11)-- colcon的使用

从此篇开始我们即将进入client library系列&#xff0c;主要包含包的创建、主题、服务、参数、消息等功能的自定义实现&#xff0c;开始真正进入ROS的大门咯。 前言 从ROS 1到ROS 2&#xff0c;对应的构建工具集由 catkin_make -> catkin_make_isolated ->catkin_tools …

Redis监控方案以及相关黄金指标提升稳定性和可靠性

Redis监控方案以及相关黄金指标提升稳定性和可靠性 1. 需要了解的词2. 「基准性能」相关指标2.1 Latency2.2 最大响应延迟2.3 平均响应延迟2.4 OPS(instantaneous_ops_per_sec)2.5 Hit Rate 3. 「内存」相关指标3.1 内存使用量(used_memory)3.2 内存碎片率(mem_fragmentation_r…

day18-二叉树part05

513.找树左下角的值 class Solution {public int findBottomLeftValue(TreeNode root) {int res 0;if(root null){return res;}Queue<TreeNode> que new LinkedList<>();que.add(root);while(!que.isEmpty()){int size que.size();for(int i 0;i < size;…

文件操作(详解)

该片博客有点长大家可以通过目录选择性阅读 这是个人主页 敲上瘾-CSDN博客 目录 1. 为什么使⽤⽂件&#xff1f; 2. 什么是⽂件&#xff1f; 2.1 程序⽂件 2.2 数据⽂件 2.3 ⽂件名 3. ⼆进制⽂件和⽂本⽂件&#xff1f; 4. ⽂件的打开和关闭 4.1 流和标准流 4.1.1 流…

【c/c++】深入探秘:C++内存管理的机制

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 朋友们大家好&#xff0c;本篇文章我们详细讲解c中的动态内存管理 目录 1.C/C内存分布2.C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free3.c内存管理方式3.1new/delete对内…