ffmpeg进行混音,将两路音频pcm数据合成一路输出

ffmpeg进行混音,将两路音频pcm数据合成一路输出
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;}AVFilterContext *filterCtx;uint32_t samplerate;uint32_t channels;uint32_t bitsPerSample;AVSampleFormat format;std::string name;};AVFilterGraph* filterGraph;bool inited;std::mutex mutex;//输入std::map<uint32_t, AudioInfo> audio_input_infos;//转换格式std::shared_ptr<AudioInfo> audio_output_info_ptr;//输出std::shared_ptr<AudioInfo> audio_sink_info_ptr;//混音std::shared_ptr<AudioInfo> audio_mix_info_ptr;};#endif // AUDIOMIXER_H

audiomixer.cpp

#include "audiomixer.h"
#include <iostream>AudioMixer::AudioMixer():inited(false),filterGraph(nullptr),audio_output_info_ptr(nullptr)
{//初始化重置智能指针audio_mix_info_ptr.reset(new AudioInfo);audio_mix_info_ptr->name = "amix";//混音audio_sink_info_ptr.reset(new AudioInfo);audio_sink_info_ptr->name = "sink";//输出}AudioMixer::~AudioMixer()
{if(inited){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(inited){std::cout<< __PRETTY_FUNCTION__ << "inited return -1!" << std::endl;return -1;}//根据index保存是否已经存在if(audio_input_infos.find(index) != audio_input_infos.end()){return -1;}//auto& filterInfo = audio_input_infos[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(inited){std::cout<< __PRETTY_FUNCTION__ << "inited return -1!" << std::endl;return -1;}//设置音频相关参数audio_output_info_ptr.reset(new AudioInfo);audio_output_info_ptr->samplerate = samplerate;audio_output_info_ptr->channels = channels;audio_output_info_ptr->bitsPerSample = bitsPerSample;audio_output_info_ptr->format = format;audio_output_info_ptr->name = "output";return 0;
}int AudioMixer::init(const char *duration)
{std::lock_guard<std::mutex> locker(mutex);if(inited){std::cout<< __PRETTY_FUNCTION__ << "inited return -1!" << std::endl;return -1;}if(!audio_output_info_ptr){std::cout<< __PRETTY_FUNCTION__ << "audio_output_info_ptr return -1!" << std::endl;return -1;}if(audio_input_infos.size() == 0){std::cout<< __PRETTY_FUNCTION__ << "audio_input_infos.size() == 0 return -1!" << std::endl;return -1;}//用于整个过滤流程的一个封装filterGraph = avfilter_graph_alloc();if(filterGraph == nullptr){return -1;}char args[512] = {0};//混音const AVFilter* amix = avfilter_get_by_name("amix");audio_mix_info_ptr->filterCtx = avfilter_graph_alloc_filter(filterGraph, amix, "amix");//inputs=输入流数量//duration=决定流的结束(longest最长输入时间,shortest最短,first第一个输入持续的时间)//dropout_transition= 输入流结束时,音量重整时间snprintf(args, sizeof(args), "inputs=%d:duration=%s:dropout_transition=0",audio_input_infos.size(), duration);if(avfilter_init_str(audio_mix_info_ptr->filterCtx, args) != 0){std::cout<< __PRETTY_FUNCTION__ << "avfilter_init_str audio_mix_info_ptr failed!" << std::endl;return -1;}//输出const AVFilter* abuffersink = avfilter_get_by_name("abuffersink");audio_sink_info_ptr->filterCtx = avfilter_graph_alloc_filter(filterGraph, abuffersink, "sink");if(avfilter_init_str(audio_sink_info_ptr->filterCtx, nullptr) != 0){std::cout<< __PRETTY_FUNCTION__ << "avfilter_init_str audio_sink_info_ptr failed!" << std::endl;return -1;}//输入for(auto& iter : audio_input_infos){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));iter.second.filterCtx = avfilter_graph_alloc_filter(filterGraph, abuffer, audio_output_info_ptr->name.c_str());if(avfilter_init_str(iter.second.filterCtx, args) != 0){std::cout<< __PRETTY_FUNCTION__ << "avfilter_init_str iter.second.filterCtx failed!" << std::endl;return -1;}//audio_input_infos[index] -> audio_min_info_ptr[index]if(avfilter_link(iter.second.filterCtx, 0, audio_mix_info_ptr->filterCtx, iter.first) != 0){std::cout<< __PRETTY_FUNCTION__ << "avfilter_link audio_input_infos[index] -> audio_min_info_ptr[index] failed!" << std::endl;return -1;}}if(audio_output_info_ptr != 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_ptr->samplerate,av_get_sample_fmt_name(audio_output_info_ptr->format),av_get_default_channel_layout(audio_output_info_ptr->channels));audio_output_info_ptr->filterCtx = avfilter_graph_alloc_filter(filterGraph,aformat,"aformat");if(avfilter_init_str(audio_output_info_ptr->filterCtx, args) != 0){std::cout<< __PRETTY_FUNCTION__ << "avfilter_init_str audio_output_info_ptr failed!" << std::endl;return -1;}//audio_mix_info_ptr -> audio_output_info_ptrif(avfilter_link(audio_mix_info_ptr->filterCtx, 0, audio_output_info_ptr->filterCtx, 0) != 0){std::cout<< __PRETTY_FUNCTION__ << "avfilter_link audio_mix_info_ptr -> audio_output_info_ptr failed!" << std::endl;return -1;}//audio_output_info_ptr -> audio_sink_info_ptrif(avfilter_link(audio_output_info_ptr->filterCtx, 0, audio_sink_info_ptr->filterCtx, 0) != 0){std::cout<< __PRETTY_FUNCTION__ << "avfilter_link audio_output_info_ptr -> audio_sink_info_ptr failed!" << std::endl;return -1;}}if(avfilter_graph_config(filterGraph, NULL) < 0){std::cout<< __PRETTY_FUNCTION__ << "avfilter_graph_config failed!" << std::endl;return -1;}inited = true;return 0;
}int AudioMixer::exit()
{std::lock_guard<std::mutex> locker(mutex);if(inited){//释放输入for(auto iter : audio_input_infos){if(iter.second.filterCtx != nullptr){avfilter_free(iter.second.filterCtx);}}audio_input_infos.clear();//释放格式转换if(audio_output_info_ptr && audio_output_info_ptr->filterCtx){avfilter_free(audio_output_info_ptr->filterCtx);audio_output_info_ptr->filterCtx = nullptr;}//释放混音if (audio_mix_info_ptr->filterCtx){avfilter_free(audio_mix_info_ptr->filterCtx);audio_mix_info_ptr->filterCtx = nullptr;}//释放输出if (audio_sink_info_ptr->filterCtx){avfilter_free(audio_sink_info_ptr->filterCtx);audio_sink_info_ptr->filterCtx = nullptr;}avfilter_graph_free(&filterGraph);filterGraph = nullptr;inited = false;}return 0;
}//添加一帧,根据index添加相应的输入流
int AudioMixer::addFrame(uint32_t index, uint8_t *inBuf, uint32_t size)
{std::lock_guard<std::mutex> locker(mutex);if(!inited){std::cout<< __PRETTY_FUNCTION__ << "inited return -1!" << std::endl;return -1;}auto iter = audio_input_infos.find(index);if(iter == audio_input_infos.end()){std::cout<< __PRETTY_FUNCTION__ << "audio_input_infos.find(index) return -1!" << std::endl;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->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(!inited){std::cout<< __PRETTY_FUNCTION__ << "inited return -1!" << std::endl;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_ptr->filterCtx, avFrame.get());if(ret < 0){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->data[0], size);return size;
}

main.cpp

#include <iostream>
#include "audiomixer.h"using namespace std;#define PCM1_FRAME_SIZE (4096*2)
#define PCM2_FRAME_SIZE (4096)
#define PCM_OUT_FRAME_SIZE (40000)int main()
{cout << "Hello Audiomixer!" << endl;AudioMixer audioMix;//添加输入流//参数根据实际pcm数据设置audioMix.addAudioInput(0, 48000, 2, 32, AV_SAMPLE_FMT_FLT);audioMix.addAudioInput(1, 48000, 2, 16, AV_SAMPLE_FMT_S16);//添加输出流audioMix.addAudioOutput(44100, 2, 16, AV_SAMPLE_FMT_S16);//初始化if(audioMix.init() < 0){printf("audioMix.init() failed!\n");return -1;}int len1 , len2 = 0;uint8_t buf1[PCM1_FRAME_SIZE];uint8_t buf2[PCM2_FRAME_SIZE];FILE* file1 = fopen("pcm1.pcm", "rb");if(!file1) {printf("fopen pcm1.pcm failed\n");return -1;}FILE *file2 = fopen("pcm2.pcm", "rb");if(!file2) {printf("fopen pcm2.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 , 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){//通道1if(len1 > 0){if(audioMix.addFrame(0, buf1, len1) < 0){printf("audioMix.addFrame(0, buf1, len1) failed!\n");break;}}else{if(file1_finish == 0){file1_finish = 1;if(audioMix.addFrame(0, NULL, 0) < 0){printf("audioMix.addFrame(0, NULL, 0) failed!\n");}}}//通道2if(len2 > 0){if(audioMix.addFrame(1, buf2, len2) < 0){printf("audioMix.addFrame(1, buf2, len2) failed!\n");break;}}else{if(file2_finish == 0){file2_finish = 1;if(audioMix.addFrame(1, NULL, 0) < 0){printf("audioMix.addFrame(1, NULL, 0) failed!\n");}}}int ret = 0;while ((ret = audioMix.getFrame(out_buf, 10240)) >= 0){//写入文件fwrite(out_buf, 1, ret, file_out);}}else{printf("read file end!\n");break;}}audioMix.exit();if(file_out)fclose(file_out);if(file1)fclose(file1);if(file2)fclose(file2);cout << "End Audiomixer!" << endl;return 0;
}

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

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

相关文章

python sep函数_Python中带有print()函数的sep参数

python sep函数sep parameter stands for separator, it uses with the print() function to specify the separator between the arguments. sep参数代表分隔符&#xff0c;它与print()函数一起使用以指定参数之间的分隔符。 The default value is space i.e. if we dont us…

关于 MySQL 主从复制的配置(转)

来源&#xff1a;http://www.oschina.net/bbs/thread/10388设置Mysql的主从设置很重要&#xff0c;有如下几点用处&#xff1a;1 做备份机器&#xff0c;一旦主服务器崩溃&#xff0c;可以直接启用从服务器作为主服务器2 可以直接锁定从服务器的表只读&#xff0c;然后做备份数…

Silverlight 同域WCF免跨域文件

在sl3使用wcf时常常会因为sl中调用了不同域的wcf服务而导至调用服务失败&#xff0c;记得在很久以前sl当是只支持同域的访问&#xff0c;那么让我有一个想法&#xff0c;就是在sl引用时可以动态地取得当前sl所在的域&#xff0c;而wcf服务也必须同时部署到这个域下边&#xff0…

使用ffmpeg 的 filter 给图片添加水印

使用ffmpeg 的 filter 给图片添加水印。 main.c #include <stdio.h>#include <libavfilter/avfilter.h> #include <libavfilter/buffersrc.h> #include <libavfilter/buffersink.h> #include <libavformat/avformat.h> #include <libavcodec…

程序崩溃 分析工具_程序分析工具| 软件工程

程序崩溃 分析工具A program analysis tool implies an automatic tool that takes the source code or the executable code of a program as information and produces reports with respect to a few significant attributes of the program, for example, its size, multif…

28335接两个spi设备_IIC和SPI如此流行,谁才是嵌入式工程师的必备工具?

IICvs SPI现今&#xff0c;在低端数字通信应用领域&#xff0c;我们随处可见 IIC (Inter-Integrated Circuit) 和 SPI (Serial Peripheral Interface)的身影。原因是这两种通信协议非常适合近距离低速芯片间通信。Philips(for IIC)和 Motorola(for SPI) 出于不同背景和市场需求…

线性表15|魔术师发牌问题和拉丁方阵 - 数据结构和算法20

线性表15 : 魔术师发牌问题和拉丁方阵 让编程改变世界 Change the world by program 题外话 今天小甲鱼看到到微博有朋友在问&#xff0c;这个《数据结构和算法》系列课程有木有JAVA版本的&#xff1f; 因为这个问题之前也有一些朋友问过&#xff0c;所以咱在这里统一说下哈…

[ZT]Three ways to tell if a .NET Assembly is Strongly Named (or has Strong Name)

Here are several convenient ways to tell whether a .NET assembly is strongly named. (English language note: I assume the form “strongly named” is preferred over “strong named” since that’s the form used in the output of the sn.exe tool shown immediat…

最佳页面置换算法

在一个请求分页系统中&#xff0c;采用最佳页面置换算法时&#xff0c;假如一个作业的页面走向为4、3、2、1、4、3、5、4、3、2、1、5&#xff0c;当分配给该作业的物理块数M分别为3和4时&#xff0c;试计算在访问过程中所发生的缺页次数和缺页率。请给出分析过程。 解析&…

网络名称 转换 网络地址_网络地址转换| 计算机网络

网络名称 转换 网络地址At the time of classful addressing, the number of household users and small businesses that want to use the Internet kept increasing. In the beginning, a user was connected to the Internet with a dial-up line, for a specific period of…

rstudio 修改代码间距_第一章 R和RStudio

R与RStudioR是一种统计学编程语言&#xff0c;在科学计算领域非常流行。它是由Ross Ihaka和Robert Gentleman开发的&#xff0c;是 "S "编程语言的开源实现。R也是使用这种语言进行统计计算的软件的名字。它有一个庞大的在线支持社区和专门的软件包&#xff0c;可以为…

ubuntu下最稳定的QQ

一、安装好 Wine 1.2&#xff08;1.2 版安装好就支持中文界面的了&#xff09; 当然得有WINE 了 当然我的有 如果没有可以如下方法得到&#xff1a; 第一种方法&#xff1a;如果你已经安装过 Wine 的老版本&#xff0c;那么只要添加 Wine 1.2 的软件源&#xff0c;然后去新立得…

字体Times New Roman

Windows系统中的字体是Monotype公司为微软公司制作的Times New Roman PS&#xff08;TrueType字体&#xff09;&#xff0c;视窗系统从3.1版本开始就一直附带这个字体。而在苹果电脑公司的麦金塔系统中使用的是Linotype公司的 Times Roman (在Macintosh系统中直接简称为‘Times…

最近最久未使用页面置换算法

在一个请求分页系统中&#xff0c;采用最近最久未使用页面置换算法时&#xff0c;假如一个作业的页面走向为4、3、2、1、4、3、5、4、3、2、1、5&#xff0c;当分配给该作业的物理块数M分别为3和4时&#xff0c;试计算在访问过程中所发生的缺页次数和缺页率。请给出分析过程。 …

ffplay的数据结构分析

《ffplay分析&#xff08;从启动到读取线程的操作&#xff09;》 《ffplay分析&#xff08;视频解码线程的操作&#xff09;》 《ffplay分析&#xff08;音频解码线程的操作&#xff09;》 《ffplay 分析&#xff08;音频从Frame(解码后)队列取数据到SDL输出&#xff09;》 《f…

tolowercase_Java String toLowerCase()方法与示例

tolowercase字符串toLowerCase()方法 (String toLowerCase() Method) toLowerCase() method is a String class method, it is used to convert given string into the lowercase. toLowerCase()方法是String类方法&#xff0c;用于将给定的字符串转换为小写。 Syntax: 句法&a…

python web 服务器实时监控 websocket_python websocket网页实时显示远程服务器日志信息...

功能&#xff1a;用websocket技术&#xff0c;在运维工具的浏览器上实时显示远程服务器上的日志信息一般我们在运维工具部署环境的时候&#xff0c;需要实时展现部署过程中的信息&#xff0c;或者在浏览器中实时显示程序日志给开发人员看。你还在用ajax每隔段时间去获取服务器日…

磁盘调度算法

1&#xff0c;假设磁头当前位于第105道&#xff0c;正在向磁道序号增加的方向移动&#xff0c;现有一个磁道访问请求序列为&#xff1a;35&#xff0c;45&#xff0c;12&#xff0c;68&#xff0c;100&#xff0c;180&#xff0c;170&#xff0c;195&#xff0c;试用先来先服务…