概述
webrtc中音频模块由虚拟基类AudioDeviceModule 管理,在调用webrtc::CreatePeerConnectionFactory创建peerconnectionFactory实例时会传入音频模块管理指针,如下
peer_connection_factory_ = webrtc::CreatePeerConnectionFactory(
networkThread.release() /* network_thread */, workerThread.release() /* worker_thread */,
signalingThread /* signaling_thread */, nullptr /* default_adm */,
webrtc::CreateBuiltinAudioEncoderFactory(),
webrtc::CreateBuiltinAudioDecoderFactory(),
webrtc::CreateBuiltinVideoEncoderFactory(),
webrtc::CreateBuiltinVideoDecoderFactory(), nullptr /* audio_mixer */,
nullptr /* audio_processing */)
创建连接工厂实例时会外部传入默认的音频管理模块adm,默认我们传入空指针,如果传入的是空指针,在音频引擎初始化时会在工作线程内创建adm,并选择默认的音频输出和音频输入设备。
void WebRtcVoiceEngine::Init() {
RTC_DCHECK(worker_thread_checker_.IsCurrent());
RTC_LOG(LS_INFO) << "WebRtcVoiceEngine::Init";
// TaskQueue expects to be created/destroyed on the same thread.
low_priority_worker_queue_.reset(
new rtc::TaskQueue(task_queue_factory_->CreateTaskQueue(
"rtc-low-prio", webrtc::TaskQueueFactory::Priority::LOW)));
// Load our audio codec lists.
RTC_LOG(LS_VERBOSE) << "Supported send codecs in order of preference:";
send_codecs_ = CollectCodecs(encoder_factory_->GetSupportedEncoders());
for (const AudioCodec& codec : send_codecs_) {
RTC_LOG(LS_VERBOSE) << ToString(codec);
}
RTC_LOG(LS_VERBOSE) << "Supported recv codecs in order of preference:";
recv_codecs_ = CollectCodecs(decoder_factory_->GetSupportedDecoders());
for (const AudioCodec& codec : recv_codecs_) {
RTC_LOG(LS_VERBOSE) << ToString(codec);
}
#if defined(WEBRTC_INCLUDE_INTERNAL_AUDIO_DEVICE)
// No ADM supplied? Create a default one.
if (!adm_) {
adm_ = webrtc::AudioDeviceModule::Create(
webrtc::AudioDeviceModule::kPlatformDefaultAudio, task_queue_factory_);
}
#endif // WEBRTC_INCLUDE_INTERNAL_AUDIO_DEVICE
RTC_CHECK(adm());
webrtc::adm_helpers::Init(adm());
首先音频引擎在初始化时判断外部传入的adm是否空,如果是空,则创建一个音频设备,调用webrtc::AudioDeviceModule::Create创建。音频的实际操作类为AudioDeviceModuleImpl,实现了音频的输入设置,停止,音频的渲染停止,初始化,设置等。因在在webrtc::AudioDeviceModule::Create中是实例化AudioDeviceModuleImpl类,返回基类指针。
adm实例建立后,需要初始化操作
void Init(AudioDeviceModule* adm) {
RTC_DCHECK(adm);
RTC_CHECK_EQ(0, adm->Init()) << "Failed to initialize the ADM.";
// Playout device.
{
if (adm->SetPlayoutDevice(AUDIO_DEVICE_ID) != 0) {
RTC_LOG(LS_ERROR) << "Unable to set playout device.";
return;
}
if (adm->InitSpeaker() != 0) {
RTC_LOG(LS_ERROR) << "Unable to access speaker.";
}
// Set number of channels
bool available = false;
if (adm->StereoPlayoutIsAvailable(&available) != 0) {
RTC_LOG(LS_ERROR) << "Failed to query stereo playout.";
}
if (adm->SetStereoPlayout(available) != 0) {
RTC_LOG(LS_ERROR) << "Failed to set stereo playout mode.";
}
}
// Recording device.
{
if (adm->SetRecordingDevice(AUDIO_DEVICE_ID) != 0) {
RTC_LOG(LS_ERROR) << "Unable to set recording device.";
return;
}
if (adm->InitMicrophone() != 0) {
RTC_LOG(LS_ERROR) << "Unable to access microphone.";
}
// Set number of channels
bool available = false;
if (adm->StereoRecordingIsAvailable(&available) != 0) {
RTC_LOG(LS_ERROR) << "Failed to query stereo recording.";
}
if (adm->SetStereoRecording(available) != 0) {
RTC_LOG(LS_ERROR) << "Failed to set stereo recording mode.";
}
}
}
首先进行adm初始化,依次设置扬声器设备(此处为默认的设备),初始化扬声器,判断扬声器支持的音频声道,设置声道;设置麦克风输入设备(默认),初始化麦克风,判断麦克风支持的音频声道,设置声道。
如果需要我们外面手动设置音频输出设备,第一步我们需要获取到adm实例指针,然后通过实例中的方法SetPlayoutDevice(uint16_t index)或SetPlayoutDevice(WindowsDeviceType device)设置,如何获取adm指针是关键,通过学习,我总结了两种方法:
- 外部传入adm实例
webrtc中有非常严格的线程模型,对不同的模块划分到不同的线程中,有些实例的建立初始化等工作必须在指定的线程中完成,否则webrtc内部断言就会出错导致程序非正常结束。adm的接口创建和初始化步骤都在工作线程中完成,所以传入到webrtc::CreatePeerConnectionFactory之前,在工作线程中创建好adm实例,然后再将adm参数传入,因此前提是我们也需要创建工作线程作为参数传入,否则工作线程与webrtc内部工作线程不同也没用,下面的代码是我在webrtc自带历程peerconnectionclient中修改的:
std::unique_ptr<rtc::Thread> networkThread = rtc::Thread::CreateWithSocketServer();
rtc::Thread* signalingThread = rtc::Thread::Current();
std::unique_ptr<rtc::Thread> workerThread = rtc::Thread::Create();
networkThread->SetName("network_thread", nullptr);
signalingThread->SetName("signaling_thread", nullptr);
workerThread->SetName("worker_thread", nullptr);
if (!networkThread->Start() || !workerThread->Start())
{
int a = 0;
//MSC_THROW_INVALID_STATE_ERROR("thread start errored");
}
rtc::scoped_refptr<webrtc::AudioDeviceModule> adm= workerThread->Invoke<rtc::scoped_refptr<webrtc::AudioDeviceModule>>(
RTC_FROM_HERE, [=] { return Conductor::SetAdm(); });
SetAdm是我定义的一个函数,用来外部生成音频管理模块adm:
rtc::scoped_refptr<webrtc::AudioDeviceModule> Conductor::SetAdm()
{
rtc::scoped_refptr<webrtc::AudioDeviceModule> adm_ = webrtc::AudioDeviceModule::Create(
webrtc::AudioDeviceModule::kPlatformDefaultAudio, webrtc::CreateDefaultTaskQueueFactory().get());
if (adm_)
{
webrtc::adm_helpers::Init(adm_.get());
}
return adm_;
}
首先和webrtc::WebRtcVoiceEngine中创建一样的方法创建adm,然后调用初始化即可。可以通过adm查到本机有多少个音频输出模块,以及每个音频输出对应的索引:
void SetAudioDeviceOut(int indx)
{
char name[128];
char guid[128];
if (_adm)
{
int16_t numAudioOut = 0;
numAudioOut = _adm->PlayoutDevices();
for (int i = 0; i < numAudioOut; i++)
{
_adm->PlayoutDeviceName(i, name, guid);
std::string admName(name);
if (adm_out.find(i) == adm_out.end())
{
adm_out.insert(make_pair(i, admName));
}
}
}
}
然后根据对应的设备index来设置音频输出:
void SetAudioDeviceOut(int indx)
{
rtc::scoped_refptr<webrtc::AudioDeviceModule>adm= peerConnectionFactory->GetAdmPtr();
if (adm)
{
adm->StopPlayout();
adm->SetPlayoutDevice(indx);
if (adm->InitSpeaker() != 0) {
RTC_LOG(LS_ERROR) << "Unable to access speaker.";
}
bool available = false;
if (adm->StereoPlayoutIsAvailable(&available) != 0) {
RTC_LOG(LS_ERROR) << "Failed to query stereo playout.";
}
if (adm->SetStereoPlayout(available) != 0) {
RTC_LOG(LS_ERROR) << "Failed to set stereo playout mode.";
}
adm->InitPlayout();
adm->StartPlayout();
}
}
- 获取内部的adm实例
另一种方法是通过改动webrtc源码,增加几个简单的接口,来获取内部的adm,我认为这种方法是最有效且最安全的,我的修改如下:
在webrtc::PeerConnectionFactoryInterface类中添加虚函数
virtual rtc::scoped_refptr<webrtc::AudioDeviceModule> GetAdmPtr() = 0;
并在srcpcpeer_connection_factory.h的实现类中实现这个函数
rtc::scoped_refptr<webrtc::AudioDeviceModule> PeerConnectionFactory::GetAdmPtr()
{
return this->channel_manager_->media_engine()->voice().GetAdm();
}
此处的GetAdm()是我在VoiceEngineInterface基类中添加的虚函数,
virtual rtc::scoped_refptr <webrtc::AudioDeviceModule> GetAdm() = 0;
在WebRtcVoiceEngine中实现
rtc::scoped_refptr <webrtc::AudioDeviceModule> GetAdm() { return adm_; };
后者如何设置音频输出,和方法一一样。
总结:
方法一适用有局限性,可能会引起线程崩溃,不安全
方法二是最有稳定的方法,不会引起其他未知的问题
最后
以上就是俊逸饼干为你收集整理的如何在webrtc中切换音频输出设备的全部内容,希望文章能够帮你解决如何在webrtc中切换音频输出设备所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复