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.pswp.cn/news/379079.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/379079.shtml
英文地址,請注明出處:http://en.pswp.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;試用先來先服務…