2019 WebRtc AudioMixer混音流程

本文簡要說明最新版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播放。

相關文章
相關標籤/搜索