本文簡要說明最新版WebRtc AudioMixer混音流程。ios
本程序使用4個16KHz 單聲道時長均大於10秒的Wav文件做爲混音源,只合成前10秒的音頻,輸出也是16KHz單聲道音頻。web
輸入和輸出的採樣率都是16000,每10ms音頻長度採樣點數爲160,每一個採樣點爲16bit,兩字節大小。api
使用的WebRTC代碼日期爲2019-05-08。函數
代碼以下:ui
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 }
代碼大致介紹:this
class AudioSrc : public webrtc::AudioMixer::Source 定義了混音音頻源的類,
spa
AudioSrc必須實現基類的三個virtual函數,
virtual int Ssrc() const; 返回混音源的ID, AudioMixer調用此函數用於區分每一個音頻源,每一個音頻源的Ssrc必須返回不一樣的值。ssr
virtual int PreferredSampleRate() const; 返回混音源的音頻採樣率,這裏都是16000,AudioMixer調用此函數獲得每一個音頻源的音頻採樣率。指針
virtual AudioFrameInfo GetAudioFrameWithInfo(int sample_rate_hz, webrtc::AudioFrame* audio_frame) 獲取混音源的10ms長度音頻,混音時AudioMixer依次對混音源調用此函數,code
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播放。