如何在webrtc中切换音频输出设备
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_DEVICERTC_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 channelsbool 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 channelsbool 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;
并在src\pc\peer_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中切换音频输出设备相关推荐
- mac声音输出设备路径_如何在Mac上切换声音输出设备
mac声音输出设备路径 If you're not hearing system sound from a certain device connected to your Mac-such as a ...
- 如何在cmd中切换python版本总结
如何在cmd中切换Python版本总结 前言 尝试一:更改系统变量 尝试二.更改解释器名称 前言 由于学习nao机器人编程参加比赛,因此安装了python2.7版本.而之前安装的都为python3.9 ...
- android音频系统(7):通话过程中的音频输出设备切换
前言:由于通话比较特殊,Android对于通话过程中音频输出设备的切换做了特殊处理,它在上层也是通过切换音频播放状态来完成切换操作的,android用CallAudioState来封装通话过程中的音频 ...
- excelexportentity中设置null不显示的方法_如何在 Creator3D 中切换模型贴图,超级简单!...
效果预览 前两天有伙伴在 QQ 上询问,如何在 Creator 3D 中切换模型贴图.Shawn 之前也没尝试过,不过根据之前 Cocos Creator 的经验以及这几天对 Creator 3D 的 ...
- 如何在 Creator3D 中切换模型贴图,超级简单
1 效果预览 前两天有伙伴在 QQ 上询问,如何在 Creator 3D 中切换模型贴图.Shawn 之前也没尝试过,不过根据之前 Cocos Creator 的经验以及这几天对 Creator 3D ...
- 如何在 Creator3D 中切换模型贴图,超级简单!
效果预览 前两天有伙伴在 QQ 上询问,如何在 Creator 3D 中切换模型贴图.Shawn 之前也没尝试过,不过根据之前 Cocos Creator 的经验以及这几天对 Creator 3D 的 ...
- git 怎么切换分支命令_如何在Git中切换分支
本指南向你展示了如何在Git项目中切换分支. 前提条件Git项目访问终端窗口/Linux系统命令行 签出命令以切换分支 访问命令行并使用checkout命令签出要使用的分支:git checkout ...
- 【Python】Windows如何在cmd中切换python版本
相信很多小伙伴都会有像我一样经历,在windows中装了很多python版本,那么如果我们正式使用的时候应该如何切换呢? [方法一]从环境变量中切换python 第一步: 打开环境变量 第二步:打开系 ...
- 如何在psql中切换数据库?
在MySQL中,我使用use database_name; 什么是psql等效项? #1楼 与psql连接时,可以选择数据库. 从脚本中使用它很方便: sudo -u postgres psql -c ...
最新文章
- API网关与zuul1.x与springcloud的三角关系
- linux关闭硬件蜂鸣器,linux 关闭主板上的蜂鸣器声音
- 汇编逻辑运算指令笔记
- C语言宏定义值为函数返回值
- 老也有错?科技行业对大龄程序员的歧视
- Android ViewPager + Fragment实现滑动页面
- Wacom发布Cintiq Companion 2
- 如何移植Android源码里面的东西到NDK
- centos7安装nexus3
- 手机扫描电脑二维码登录原理
- CSDN如何收藏别人的博客文章
- shell中let 命令与Expr命令介绍
- 并行计算:openMP(一)—— parallel,for,sections指令的用法
- image generation from scene graphs 论文+code复现总结
- c语言中大写字母A转小写,C语言 | 大写字母A转换为小写a
- 重要主机信息安全概略
- 基于 Linux 集群环境上 GPFS 的问题诊断
- 京东2019春招编程题
- Linux网络服务之----DHCP篇
- 【LeetCode】1337. 矩阵中战斗力最弱的 K 行(C++)
热门文章
- linux pci 网卡驱动,linux网络设备驱动_pci网卡
- 病毒消灭战-第13届蓝桥杯Scratch选拔赛真题精选
- 防病毒的四种防护技术
- 收集中文长尾关键词的方法
- 乌云: 人生苦短,我用 Python:)做mixin app开发教程 - Mixin Network开发者访谈系列...
- 【手把手教学】开启iOS app调试模式
- linux 渗透的艺术 pdf,《Kali Linux渗透测试的艺术》迷你书
- MongoDB KB2731284 补丁
- 贵阳大数据那么热 有多少落在了实处?
- GTJ2018如何导出全部工程量_广联达GTJ2018常10个问题