2019 WebRtc AudioMixer混音流程
本文简要说明最新版webrtc audiomixer混音流程。
本程序使用4个16khz 单声道时长均大于10秒的wav文件作为混音源,只合成前10秒的音频,输出也是16khz单声道音频。
输入和输出的采样率都是16000,每10ms音频长度采样点数为160,每个采样点为16bit,两字节大小。
使用的webrtc代码日期为2019-05-08。
代码如下:
1 #include "stdafx.h" 2 #include <string> 3 #include <iostream> 4 #include <memory> 5 #include <thread> 6 #include <chrono> 7 #include "webrtc\modules\audio_mixer\audio_mixer_impl.h" 8 #include "webrtc\api\audio\audio_frame.h" 9 #include "webrtc\modules\audio_mixer\output_rate_calculator.h" 10 11 using namespace std::literals::chrono_literals; 12 13 #define wav_header_size 44 14 #define sample_rate 16000 15 16 int16_t s_buf[160] = { 0 }; 17 18 class audiosrc : public webrtc::audiomixer::source 19 { 20 public: 21 audiosrc(int ssrc, int sample, const std::string& file) 22 : mssrc(ssrc) 23 , msample(sample) 24 , mfile(nullptr) 25 { 26 fopen_s(&mfile, file.c_str(), "rb"); 27 28 if (mfile) 29 { 30 fseek(mfile, 44, seek_set); 31 } 32 } 33 34 virtual ~audiosrc() 35 { 36 if (mfile) 37 { 38 fclose(mfile); 39 mfile = nullptr; 40 } 41 } 42 43 virtual audioframeinfo getaudioframewithinfo(int sample_rate_hz, 44 webrtc::audioframe* audio_frame) 45 { 46 if (mfile) 47 { 48 fread(s_buf, 160 * 2, 1, mfile);//16khz, 10ms buf 49 } 50 51 std::thread::id tid = std::this_thread::get_id(); 52 std::cout << "thread id "; 53 tid._to_text(std::cout); 54 55 //copy s_buf to audio_frame inner buf 56 audio_frame->updateframe(0, s_buf, 160, sample_rate, webrtc::audioframe::speechtype::knormalspeech, webrtc::audioframe::vadactivity::kvadunknown, 1); 57 printf(",ssrc %d get audio frame, muted: %d, n %d, s %d\n", mssrc, int(audio_frame->muted()), audio_frame->num_channels_, audio_frame->sample_rate_hz_); 58 59 return audioframeinfo::knormal; 60 } 61 62 // a way for a mixer implementation to distinguish participants. 63 virtual int ssrc() const 64 { 65 return mssrc; 66 } 67 68 // a way for this source to say that getaudioframewithinfo called 69 // with this sample rate or higher will not cause quality loss. 70 virtual int preferredsamplerate() const 71 { 72 return msample; 73 } 74 75 private: 76 int mssrc; 77 int msample; 78 file* mfile; 79 }; 80 81 82 class audiooutput : public webrtc::outputratecalculator 83 { 84 virtual int calculateoutputrate( 85 const std::vector<int>& preferred_sample_rates) 86 { 87 return sample_rate; 88 } 89 90 }; 91 92 int mixtext() 93 { 94 auto mixptr = webrtc::audiomixerimpl::create(std::make_unique<audiooutput>(), true); 95 audiosrc src1(1, sample_rate, "e:\\media\\audio\\16k_1.wav"); 96 audiosrc src2(2, sample_rate, "e:\\media\\audio\\16k_2.wav"); 97 audiosrc src3(3, sample_rate, "e:\\media\\audio\\16k_3.wav"); 98 audiosrc src4(4, sample_rate, "e:\\media\\audio\\16k_4.wav"); 99 100 mixptr->addsource(&src1); 101 mixptr->addsource(&src2); 102 mixptr->addsource(&src3); 103 mixptr->addsource(&src4); 104 105 std::thread::id tid = std::this_thread::get_id(); 106 std::cout << "main thread id: "; 107 tid._to_text(std::cout); 108 std::cout << std::endl; 109 110 webrtc::audioframe frame; 111 112 file* fout = nullptr; 113 fopen_s(&fout, "f:/download/outmix.pcm", "wb"); 114 115 for (int i = 0; i < 100*10; ++i) // only mix first 10 seconds audio 116 { 117 mixptr->mix(1, &frame); 118 if (fout) 119 { 120 fwrite(frame.data(), 160 * 2, 1, fout); 121 } 122 std::this_thread::sleep_for(10ms); 123 } 124 125 fclose(fout); 126 fout = nullptr; 127 128 mixptr->removesource(&src1); 129 mixptr->removesource(&src2); 130 mixptr->removesource(&src3); 131 mixptr->removesource(&src4); 132 133 tid = std::this_thread::get_id(); 134 std::cout << "exit main thread id: "; 135 tid._to_text(std::cout); 136 std::cout << std::endl; 137 138 return 0; 139 } 140 141 int main(int argc, char* argv[]) 142 { 143 mixtext(); 144 145 return 0; 146 }
代码大体介绍:
class audiosrc : public webrtc::audiomixer::source 定义了混音音频源的类,
audiosrc必须实现基类的三个virtual函数,
virtual int ssrc() const; 返回混音源的id, audiomixer调用此函数用于区分每个音频源,每个音频源的ssrc必须返回不同的值。
virtual int preferredsamplerate() const; 返回混音源的音频采样率,这里都是16000,audiomixer调用此函数得到每个音频源的音频采样率。
virtual audioframeinfo getaudioframewithinfo(int sample_rate_hz, webrtc::audioframe* audio_frame) 获取混音源的10ms长度音频,混音时audiomixer依次对混音源调用此函数,
audiosrc先从wav文件读取10ms音频到s_buf中,用audioframe的updateframe函数把刚获取的s_buf中内容更新到audioframe中,audiomixer内部完成混音。
class audiooutput : public webrtc::outputratecalculator 是用于计算混音输出音频采样率的类,必须实现虚函数calculateoutputrate,
参数const std::vector<int>& preferred_sample_rates中元素为每个混音源的音频采样率,返回值为输出音频采样率,这里输出和输入的采样率一样,返回16000。
函数mixtext是调用的代码
auto mixptr = webrtc::audiomixerimpl::create(std::make_unique<audiooutput>(), true);
创建了一个audiomixer智能指针对象mixptr ,
audiosrc src1(1, sample_rate, "e:\\media\\audio\\16k_1.wav"); audiosrc src2(2, sample_rate, "e:\\media\\audio\\16k_2.wav"); audiosrc src3(3, sample_rate, "e:\\media\\audio\\16k_3.wav"); audiosrc src4(4, sample_rate, "e:\\media\\audio\\16k_4.wav");
创建了4个混音源。
mixptr->addsource(&src1); mixptr->addsource(&src2); mixptr->addsource(&src3); mixptr->addsource(&src4);
把4个混音源添加到audiomixer中,
webrtc::audioframe frame;
创建一个audioframe 对象,用于接收混音输出。
file* fout = nullptr; fopen_s(&fout, "f:/download/outmix.pcm", "wb");
打开一个文件,用于写入混音输出。
for (int i = 0; i < 100*10; ++i) // only mix first 10 seconds audio { mixptr->mix(1, &frame); if (fout) { fwrite(frame.data(), 160 * 2, 1, fout); } std::this_thread::sleep_for(10ms); }
此循环调用1000次,每次混音10毫秒长度音频,总共混音10秒钟长度的音频,
mixptr->mix(1, &frame);
mix是实际执行混音的函数,
它内部再依次调用audiosrc::getaudioframewithinfo再完成混音,把混音输出到frame中。
然后用fwrite再把frame.data()中的混音输出写入到输出文件。
std::this_thread::sleep_for(10ms);
sleep等待10毫秒。
混音完成再调用
mixptr->removesource(&src1); mixptr->removesource(&src2); mixptr->removesource(&src3); mixptr->removesource(&src4);
移除混音源。
以上就是混音流程。
注意:由于混音输出写的是pcm文件,没有文件头,一般播放器不能播放,必须用 cooledit 或 audacity 打开pcm播放。