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;
}

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

  1. js实现音频PCM数据合并、拼接、裁剪、调节音量等功能

    关于音频的内容,我边学习,边实践也总结了一些,从最开始实现一个简单的web音乐播放器的自定义工具栏,到后来的实现简单的音频频谱图.直到今天的对音频数据进行的进一步操作,我也是一点点的在进步.虽然很多地 ...

  2. FFMPEG 实现混音,加水印,加文字,模糊水印任意滤镜组合

    一共15种组合一下搞定:先伪代码没逻辑错误,然后就撸正式代码,后面测试就有点小顺利了 伪代码: 根据参数构造ffmpeg参数命令 构造元素视频方面有水印,文字,去水印:音频方面有混音一个 构造顺序: ...

  3. c语言蓝牙接收6,终于搞定了通过两路蓝牙接收数据

    一直想做无线传感器,通过蓝牙来接收数据,无奈因为arduino接收串口数据的一些问题,一直搁到现在.因为学校里给学生开了选修课,所以手边有一些nano和mega可以使用,所以就做了用两个nano加上两 ...

  4. 两路语音 两路计算机数据综合,两路语音PCM时分复用系统的设计.DOC

    PAGE II 摘要 数字通信系统是采用数字信号来传递信息的通信系统,数字通信过程中主要涉及信源编码与译码.信道编码与译码.数字调制与解调等技术问题.而脉冲编码调制就是一种常用的信源编码方法,将模拟信 ...

  5. 使用FFMpeg将音频PCM数据生成WAV和MP3文件

    文章目录 1. 获取编码器和创建解码器上下文 2. 创建音频流和输出封装上下文 3. 编码原始数据写入到文件中 WAV音频封装格式可以存储无编码的PCM数据,而MP3封装格式中不能直接存储PCM数据, ...

  6. AudioRecord 采集音频PCM数据

    AudioRecord 可以用来采集音频原始数据(PCM)格式,使用起来非常简单. 主要就是构造函数的定义 AudioRecord(int audioSource, int sampleRateInH ...

  7. 关于音频PCM数据2字节(16位)byte与64位double之间的转换

    1 致谢 感谢kimmking网友提供的资料 原文链接如下:http://blog.csdn.net/kimmking/article/details/8752737 2 问题描述 今天遇到一个问题 ...

  8. 记录一个音频PCM数据由双声道转单声道出错问题

    引言 工作需要将一份 双声道的PCM数据转换成单声道数据,我采用的是将左右声道样点值对应相加求平均样点值的办法. 计算式如下: typedef int s32; typedef unsigned in ...

  9. [Android] [音视频系列]在 Android 平台使用 AudioRecord 和 AudioTrack API 完成音频 PCM 数据的采集和播放,并实现读写音频 wav 文件

    参考 官方文档地址:https://developer.android.google.cn/reference/android/media/AudioRecord GitHub 地址:https:// ...

最新文章

  1. c语言初学 循环 的灵活使用小案例
  2. 数据分析 python 用途-python数据统计分析
  3. UrlPager免费分页控件2.0版发布!
  4. 4道Python基础字典练习题
  5. python画一个点_pygame学习笔记(2):画点的三种方法和动画实例
  6. 关于计算机的使用方法中心,关于新校区行政楼和活动中心楼网络使用的说明
  7. Java字节码反编译工具
  8. 学界 | CVPR 2018颁布五大奖项,何恺明获年轻学者奖
  9. virtio、vhost和vhost-user比较
  10. 年近30,我的职业回顾与思考
  11. javaweb使用quartz
  12. 牛客 处女座的约会 规律
  13. 电子设计大赛-室内可见光定位装置
  14. 记一次前端优化首屏加载
  15. 最详细的【微信小程序+阿里云Web服务】开发部署指引(三):开通阿里云主机
  16. H.266/VVC代码学习:帧内预测之角度预测函数(predIntraAng、xPredIntraAng)
  17. 在anti-spoofing中,在OULU数据集上求APCER,BPCER,ACER上的一个注意事项
  18. 适合公司用的电子邮箱哪家好?企业邮箱最全功能介绍~
  19. 2018互联网金融公司排名——Top100(附完整榜单)
  20. Android 调起系统相机拍照

热门文章

  1. angularjs的$http请求方式
  2. 开源|蚂蚁金服开源AntV F2:一个专注于移动,开箱即用的可视
  3. 为input输入框添加圆角并去除阴影
  4. H5_ 多媒体video,autio使用示例
  5. python执行linux和window的命令
  6. 关于数据库名、实例名
  7. 移动架构-数据库分库和全版本升级
  8. UIImage 压缩
  9. VS2010 运行库设置
  10. linux date命令设置时间