本文简要说明最新版WebRtc AudioMixer混音流程。
本程序使用4个16KHz 单声道时长均大于10秒的Wav文件作为混音源,只合成前10秒的音频,输出也是16KHz单声道音频。
输入和输出的采样率都是16000,每10ms音频长度采样点数为160,每个采样点为16bit,两字节大小。
使用的WebRTC代码日期为2019-05-08。
代码如下:
#include "stdafx.h"
#include <string>
#include <iostream>
#include <memory>
#include <thread>
#include <chrono>
#include "webrtc\modules\audio_mixer\audio_mixer_impl.h"
#include "webrtc\api\audio\audio_frame.h"
#include "webrtc\modules\audio_mixer\output_rate_calculator.h" using namespace std::literals::chrono_literals; #define WAV_HEADER_SIZE 44
#define SAMPLE_RATE 16000 int16_t s_buf[] = { }; class AudioSrc : public webrtc::AudioMixer::Source
{
public:
AudioSrc(int ssrc, int sample, const std::string& file)
: mSsrc(ssrc)
, mSample(sample)
, mFile(nullptr)
{
fopen_s(&mFile, file.c_str(), "rb"); if (mFile)
{
fseek(mFile, , SEEK_SET);
}
} virtual ~AudioSrc()
{
if (mFile)
{
fclose(mFile);
mFile = nullptr;
}
} virtual AudioFrameInfo GetAudioFrameWithInfo(int sample_rate_hz,
webrtc::AudioFrame* audio_frame)
{
if (mFile)
{
fread(s_buf, * , , mFile);//16KHz, 10ms buf
} std::thread::id tid = std::this_thread::get_id();
std::cout << "thread id ";
tid._To_text(std::cout); //copy s_buf to audio_frame inner buf
audio_frame->UpdateFrame(, s_buf, , SAMPLE_RATE, webrtc::AudioFrame::SpeechType::kNormalSpeech, webrtc::AudioFrame::VADActivity::kVadUnknown, );
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_); return AudioFrameInfo::kNormal;
} // A way for a mixer implementation to distinguish participants.
virtual int Ssrc() const
{
return mSsrc;
} // A way for this source to say that GetAudioFrameWithInfo called
// with this sample rate or higher will not cause quality loss.
virtual int PreferredSampleRate() const
{
return mSample;
} private:
int mSsrc;
int mSample;
FILE* mFile;
}; class AudioOutput : public webrtc::OutputRateCalculator
{
virtual int CalculateOutputRate(
const std::vector<int>& preferred_sample_rates)
{
return SAMPLE_RATE;
} }; int MixText()
{
auto mixptr = webrtc::AudioMixerImpl::Create(std::make_unique<AudioOutput>(), true);
AudioSrc src1(, SAMPLE_RATE, "e:\\Media\\Audio\\16k_1.wav");
AudioSrc src2(, SAMPLE_RATE, "e:\\Media\\Audio\\16k_2.wav");
AudioSrc src3(, SAMPLE_RATE, "e:\\Media\\Audio\\16k_3.wav");
AudioSrc src4(, SAMPLE_RATE, "e:\\Media\\Audio\\16k_4.wav"); mixptr->AddSource(&src1);
mixptr->AddSource(&src2);
mixptr->AddSource(&src3);
mixptr->AddSource(&src4); std::thread::id tid = std::this_thread::get_id();
std::cout << "main thread id: ";
tid._To_text(std::cout);
std::cout << std::endl; webrtc::AudioFrame frame; FILE* fOut = nullptr;
fopen_s(&fOut, "f:/download/outmix.pcm", "wb"); for (int i = ; i < *; ++i) // only mix first 10 seconds audio
{
mixptr->Mix(, &frame);
if (fOut)
{
fwrite(frame.data(), * , , fOut);
}
std::this_thread::sleep_for(10ms);
} fclose(fOut);
fOut = nullptr; mixptr->RemoveSource(&src1);
mixptr->RemoveSource(&src2);
mixptr->RemoveSource(&src3);
mixptr->RemoveSource(&src4); tid = std::this_thread::get_id();
std::cout << "exit main thread id: ";
tid._To_text(std::cout);
std::cout << std::endl; return ;
} int main(int argc, char* argv[])
{
MixText(); return ;
}
代码大体介绍:
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(, SAMPLE_RATE, "e:\\Media\\Audio\\16k_1.wav");
AudioSrc src2(, SAMPLE_RATE, "e:\\Media\\Audio\\16k_2.wav");
AudioSrc src3(, SAMPLE_RATE, "e:\\Media\\Audio\\16k_3.wav");
AudioSrc src4(, 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 = ; i < *; ++i) // only mix first 10 seconds audio
{
mixptr->Mix(, &frame);
if (fOut)
{
fwrite(frame.data(), * , , fOut);
}
std::this_thread::sleep_for(10ms);
}
此循环调用1000次,每次混音10毫秒长度音频,总共混音10秒钟长度的音频,
mixptr->Mix(, &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播放。