Customizing / subclassing AudioDeviceModule (device selection, volume control etc)

1,940 views
Skip to first unread message

Andreas Karlsson

unread,
Dec 15, 2016, 4:03:04 AM12/15/16
to discuss-webrtc
Hello,

We're currently using WebRTC to build a native video/audio streaming application using C++ (currently windows, but will soon expand to iOS and Android).

We'd like to be able to let the user select what audio device to use for playback / recording, a quick search leads to this: https://groups.google.com/forum/#!topic/discuss-webrtc/LnXeIA_95lo

Fair enough, I started experimenting with the AudioDeviceModule. My thinking is that I could subclass the default AudioDeviceModuleImpl class to provide the customized functionality I need.

To get started I tried to manually create the AudioDeviceModuleImpl instance and pass it to the PeerConnectionFactory like this:

auto nwThread = rtc::Thread::CreateWithSocketServer().release();
nwThread->Start();
auto wThread = rtc::Thread::Create().release();
wThread->Start();

auto adm = webrtc::AudioDeviceModuleImpl::Create(webrtc::VoEId(1, -1), webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio);

adm->AddRef(); // Just for test, make sure it isn't released

peerConnectionFactory =
webrtc::CreatePeerConnectionFactory(
nwThread,
wThread,
rtc::Thread::Current(),
adm,
nullptr,
nullptr);


This works until I try to so establish a call and playback starts at which point AudioDeviceBuffer::InitPlayback throws an error about not being called on the correct thread (RTC_DCHECK(thread_checker_.CalledOnValidThread());).

My questions are:

1) How do I manually create and inject a working ADM?
2) Is subclassing the AudioDeviceModuleImpl a good/sane idea? If not, what is the alternative to get finer control over the audio device selection (and other things)?

I'm currently on M55 btw.

Regards,

Andreas

Henrik Andreasson

unread,
Dec 15, 2016, 4:24:18 AM12/15/16
to discuss-webrtc
Looks like you are extending the existing ADM implementation and use that to inject. If so, you will have to follow its threading model or modify your implementation to work in your case where the ADM is constructed and used on different threads. Alternatively, write your own ADM from scratch to have full control.

--

---
You received this message because you are subscribed to the Google Groups "discuss-webrtc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to discuss-webrtc+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/discuss-webrtc/de59cc8f-1804-42fe-a990-5a7abff1bca3%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Andreas Karlsson

unread,
Dec 19, 2016, 7:43:58 AM12/19/16
to discuss-webrtc
Ah, I didn't realize that the ADM had to be created on the worker thread. A simple invoke like so:

auto wThread = rtc::Thread::Create().release();

wThread->Start();

auto adm = wThread->Invoke<rtc::scoped_refptr<webrtc::AudioDeviceModule>>(RTC_FROM_HERE,
[]()
{
return webrtc::AudioDeviceModuleImpl::Create(webrtc::VoEId(1, -1), webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio);
});

adm->AddRef();

peerConnectionFactory =
webrtc::CreatePeerConnectionFactory(
nwThread,
wThread,
rtc::Thread::Current(),
adm,
nullptr,
nullptr);

Made everything work!

Thanks,

Andreas
To unsubscribe from this group and stop receiving emails from it, send an email to discuss-webrt...@googlegroups.com.

Chen Yang

unread,
Aug 13, 2018, 11:04:52 PM8/13/18
to discuss-webrtc
Hi, how do you guys release peer connection resources, like udp port and working thread & signaling thread?

在 2016年12月19日星期一 UTC+8下午8:43:58,Andreas Karlsson写道:
Message has been deleted

Andreas Karlsson

unread,
Aug 14, 2018, 6:39:51 AM8/14/18
to discuss-webrtc
Our cleanup code currently looks like this:

// Make sure the AudioDeviceModule is deleted on the same thread as it was created on
// (i.e. the workerThread) to avoid errors on shutdown.
workerThread
->Invoke<void>(
 RTC_FROM_HERE
,
 
[this]()
 
{
   LOG_DEBUG 
<< "Removing audioDeviceModule";
   
this->audioDeviceModule.release();
   
this->audioDeviceModule = nullptr;
 
}
);

networkManager
.reset();
socketFactory
.reset();

peerConnectionFactory 
= nullptr; // Forces release of the factory
rtc
::CleanupSSL();

// Post quit messages
signalThread
->Quit();
signalThread
.reset();
workerThread
->Quit();
workerThread
.reset();
networkThread
->Quit();
networkThread
.reset();

(The resources are stored in std::unique_ptr)
Message has been deleted

Chen Yang

unread,
Aug 15, 2018, 2:32:23 AM8/15/18
to discuss-webrtc
Are your networkManager and socketFactory injected to peerConnectionFactory?There are private fields in peerConnectionFactory.h, how do you do that?
My init and destroy process almost same like yours(exclude networkManager and socketFactory), but when i try to destroy my wrapper object, program will crash with a fatal error in physicalsocketserver.cc, line 1188, last system error: 38,Check failed: dispatchers_.empty().

My init and destroy process as follow:
void Init(std::string audio_card) {
   
RTC_CHECK(rtc::InitializeSSL()) << "Failed to initialize ssl";
    audio_card_id = std::move(audio_card);
    worker_thread = rtc::Thread::Create();
    worker_thread->SetName("worker_thread", nullptr);
    RTC_CHECK(worker_thread->Start()) << "Failed to start thread";
    signaling_thread = rtc::Thread::Create();
    signaling_thread->SetName("signaling_thread", nullptr);
    RTC_CHECK(signaling_thread->Start()) << "Failed to start thread";
    network_thread = rtc::Thread::CreateWithSocketServer();
    network_thread->SetName("network_thread", nullptr);
    RTC_CHECK(network_thread->Start()) << "Failed to start thread";
    audio_device_module = worker_thread->Invoke<rtc::scoped_refptr<webrtc::AudioDeviceModule>>(
           
RTC_FROM_HERE,
            [=]() {
               
auto audio_device =
                       
new AudioDeviceModuleWrapper(webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio);
                audio_device->Init();
                int16_t num = audio_device->RecordingDevices();
                for (int16_t i = 0; i < num; i++) {
                   
char name[webrtc::kAdmMaxDeviceNameSize];
                    char guid[webrtc::kAdmMaxGuidSize];
                    int ret = audio_device->RecordingDeviceName(static_cast<uint16_t>(i), name, guid);
                    std::string name_str(name);
                    if (!audio_card_id.empty() && name_str.find(audio_card_id) == 0) {
                       
is_connect_to_audio_card = true;
                        audio_device->SetDeviceId(static_cast<uint16_t>(i));
                    }
               
}
               
return audio_device;
            });
    peer_connection_factory = webrtc::CreatePeerConnectionFactory(network_thread.get(), worker_thread.get(),
                                                                  signaling_thread.get(),
                                                                  audio_device_module, nullptr, nullptr);
    is_init_finished = true;
}

~RTC() {
   
worker_thread->Invoke<void>(
           
RTC_FROM_HERE,
            [this]()
           
{
               
this->audio_device_module.release();
                this->audio_device_module = nullptr;
            }
   
);
    peer_connection_factory = nullptr;
    RTC_CHECK(rtc::CleanupSSL()) << "Failed to clean up ssl";
    signaling_thread->Quit();
    signaling_thread.reset();
    worker_thread->Quit();
    worker_thread.reset();
    network_thread->Quit();
    network_thread.reset();
}




在 2018年8月14日星期二 UTC+8下午6:39:51,Andreas Karlsson写道:
Message has been deleted

Chen Yang

unread,
Aug 15, 2018, 3:34:13 AM8/15/18
to discuss-webrtc
Do you use socket factory and network manager like follow?
socket_factory.reset(new rtc::BasicPacketSocketFactory(network_thread.get()));
network_manager.reset(new rtc::BasicNetworkManager());


std::unique_ptr<cricket::PortAllocator> port_allocator(
       
new cricket::BasicPortAllocator(network_manager.get(), socket_factory.get()));
port_allocator->SetPortRange(min_port, max_port);
pure_peer_connection = peer_connection_factory->CreatePeerConnection(
        configuration
, std::move(port_allocator), nullptr, peerConnectionObserver);



在 2018年8月14日星期二 UTC+8下午6:39:51,Andreas Karlsson写道:
Our cleanup code currently looks like this:
Reply all
Reply to author
Forward
0 new messages