Update ios AudioDevice away from rtc::MessageHandler

Align thread checkers with the class comment,
i.e. ensure AudioDevice is used and destroyed on the same thread it was constructed on, not just the same thread AudioDevice::Init was called.

Bug: webrtc:9702
Change-Id: Ib905978cc8173266151adf26e1b7317f1d3852bc
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/274164
Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org>
Auto-Submit: Danil Chapovalov <danilchap@webrtc.org>
Reviewed-by: Kári Helgason <kthelgason@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#38018}
This commit is contained in:
Danil Chapovalov
2022-09-05 17:41:02 +02:00
committed by WebRTC LUCI CQ
parent 7faf7171b0
commit 4a29edca7d
3 changed files with 53 additions and 91 deletions

View File

@ -270,9 +270,11 @@ if (is_ios || is_mac) {
":audio_session_observer",
":base_objc",
"../api:array_view",
"../api:scoped_refptr",
"../api:sequence_checker",
"../api/task_queue",
"../api/task_queue:default_task_queue_factory",
"../api/task_queue:pending_task_safety_flag",
"../modules/audio_device:audio_device_api",
"../modules/audio_device:audio_device_buffer",
"../modules/audio_device:audio_device_generic",

View File

@ -14,7 +14,9 @@
#include <atomic>
#include <memory>
#include "api/scoped_refptr.h"
#include "api/sequence_checker.h"
#include "api/task_queue/pending_task_safety_flag.h"
#include "audio_session_observer.h"
#include "modules/audio_device/audio_device_generic.h"
#include "rtc_base/buffer.h"
@ -46,8 +48,7 @@ namespace ios_adm {
// same thread.
class AudioDeviceIOS : public AudioDeviceGeneric,
public AudioSessionObserver,
public VoiceProcessingAudioUnitObserver,
public rtc::MessageHandler {
public VoiceProcessingAudioUnitObserver {
public:
explicit AudioDeviceIOS(bool bypass_voice_processing);
~AudioDeviceIOS() override;
@ -159,9 +160,6 @@ class AudioDeviceIOS : public AudioDeviceGeneric,
UInt32 num_frames,
AudioBufferList* io_data) override;
// Handles messages from posts.
void OnMessage(rtc::Message* msg) override;
bool IsInterrupted();
private:
@ -213,10 +211,6 @@ class AudioDeviceIOS : public AudioDeviceGeneric,
// Determines whether voice processing should be enabled or disabled.
const bool bypass_voice_processing_;
// Ensures that methods are called from the same thread as this object is
// created on.
SequenceChecker thread_checker_;
// Native I/O audio thread checker.
SequenceChecker io_thread_checker_;
@ -273,7 +267,7 @@ class AudioDeviceIOS : public AudioDeviceGeneric,
std::atomic<int> playing_;
// Set to true after successful call to Init(), false otherwise.
bool initialized_ RTC_GUARDED_BY(thread_checker_);
bool initialized_ RTC_GUARDED_BY(thread_);
// Set to true after successful call to InitRecording() or InitPlayout(),
// false otherwise.
@ -284,23 +278,27 @@ class AudioDeviceIOS : public AudioDeviceGeneric,
// Audio interruption observer instance.
RTCNativeAudioSessionDelegateAdapter* audio_session_observer_
RTC_GUARDED_BY(thread_checker_);
RTC_GUARDED_BY(thread_);
// Set to true if we've activated the audio session.
bool has_configured_session_ RTC_GUARDED_BY(thread_checker_);
bool has_configured_session_ RTC_GUARDED_BY(thread_);
// Counts number of detected audio glitches on the playout side.
int64_t num_detected_playout_glitches_ RTC_GUARDED_BY(thread_checker_);
int64_t num_detected_playout_glitches_ RTC_GUARDED_BY(thread_);
int64_t last_playout_time_ RTC_GUARDED_BY(io_thread_checker_);
// Counts number of playout callbacks per call.
// The value isupdated on the native I/O thread and later read on the
// creating thread (see thread_checker_) but at this stage no audio is
// active. Hence, it is a "thread safe" design and no lock is needed.
// The value is updated on the native I/O thread and later read on the
// creating `thread_` but at this stage no audio is active.
// Hence, it is a "thread safe" design and no lock is needed.
int64_t num_playout_callbacks_;
// Contains the time for when the last output volume change was detected.
int64_t last_output_volume_change_time_ RTC_GUARDED_BY(thread_checker_);
int64_t last_output_volume_change_time_ RTC_GUARDED_BY(thread_);
// Avoids running pending task after `this` is Terminated.
rtc::scoped_refptr<PendingTaskSafetyFlag> safety_ =
PendingTaskSafetyFlag::Create();
};
} // namespace ios_adm
} // namespace webrtc

View File

@ -16,6 +16,7 @@
#include <cmath>
#include "api/array_view.h"
#include "api/task_queue/pending_task_safety_flag.h"
#include "helpers.h"
#include "modules/audio_device/fine_audio_buffer.h"
#include "rtc_base/checks.h"
@ -60,15 +61,6 @@ namespace ios_adm {
const UInt16 kFixedPlayoutDelayEstimate = 30;
const UInt16 kFixedRecordDelayEstimate = 30;
enum AudioDeviceMessageType : uint32_t {
kMessageTypeInterruptionBegin,
kMessageTypeInterruptionEnd,
kMessageTypeValidRouteChange,
kMessageTypeCanPlayOrRecordChange,
kMessageTypePlayoutGlitchDetected,
kMessageOutputVolumeChange,
};
using ios::CheckAndLogError;
#if !defined(NDEBUG)
@ -115,16 +107,15 @@ AudioDeviceIOS::AudioDeviceIOS(bool bypass_voice_processing)
LOGI() << "ctor" << ios::GetCurrentThreadDescription()
<< ",bypass_voice_processing=" << bypass_voice_processing_;
io_thread_checker_.Detach();
thread_checker_.Detach();
thread_ = rtc::Thread::Current();
audio_session_observer_ = [[RTCNativeAudioSessionDelegateAdapter alloc] initWithObserver:this];
}
AudioDeviceIOS::~AudioDeviceIOS() {
RTC_DCHECK(thread_checker_.IsCurrent());
RTC_DCHECK_RUN_ON(thread_);
LOGI() << "~dtor" << ios::GetCurrentThreadDescription();
thread_->Clear(this);
safety_->SetNotAlive();
Terminate();
audio_session_observer_ = nil;
}
@ -132,16 +123,15 @@ AudioDeviceIOS::~AudioDeviceIOS() {
void AudioDeviceIOS::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
LOGI() << "AttachAudioBuffer";
RTC_DCHECK(audioBuffer);
RTC_DCHECK(thread_checker_.IsCurrent());
RTC_DCHECK_RUN_ON(thread_);
audio_device_buffer_ = audioBuffer;
}
AudioDeviceGeneric::InitStatus AudioDeviceIOS::Init() {
LOGI() << "Init";
io_thread_checker_.Detach();
thread_checker_.Detach();
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_RUN_ON(thread_);
if (initialized_) {
return InitStatus::OK;
}
@ -167,7 +157,7 @@ AudioDeviceGeneric::InitStatus AudioDeviceIOS::Init() {
int32_t AudioDeviceIOS::Terminate() {
LOGI() << "Terminate";
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_RUN_ON(thread_);
if (!initialized_) {
return 0;
}
@ -178,13 +168,13 @@ int32_t AudioDeviceIOS::Terminate() {
}
bool AudioDeviceIOS::Initialized() const {
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_RUN_ON(thread_);
return initialized_;
}
int32_t AudioDeviceIOS::InitPlayout() {
LOGI() << "InitPlayout";
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_RUN_ON(thread_);
RTC_DCHECK(initialized_);
RTC_DCHECK(!audio_is_initialized_);
RTC_DCHECK(!playing_.load());
@ -199,18 +189,18 @@ int32_t AudioDeviceIOS::InitPlayout() {
}
bool AudioDeviceIOS::PlayoutIsInitialized() const {
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_RUN_ON(thread_);
return audio_is_initialized_;
}
bool AudioDeviceIOS::RecordingIsInitialized() const {
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_RUN_ON(thread_);
return audio_is_initialized_;
}
int32_t AudioDeviceIOS::InitRecording() {
LOGI() << "InitRecording";
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_RUN_ON(thread_);
RTC_DCHECK(initialized_);
RTC_DCHECK(!audio_is_initialized_);
RTC_DCHECK(!recording_.load());
@ -226,7 +216,7 @@ int32_t AudioDeviceIOS::InitRecording() {
int32_t AudioDeviceIOS::StartPlayout() {
LOGI() << "StartPlayout";
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_RUN_ON(thread_);
RTC_DCHECK(audio_is_initialized_);
RTC_DCHECK(!playing_.load());
RTC_DCHECK(audio_unit_);
@ -251,7 +241,7 @@ int32_t AudioDeviceIOS::StartPlayout() {
int32_t AudioDeviceIOS::StopPlayout() {
LOGI() << "StopPlayout";
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_RUN_ON(thread_);
if (!audio_is_initialized_ || !playing_.load()) {
return 0;
}
@ -282,7 +272,7 @@ bool AudioDeviceIOS::Playing() const {
int32_t AudioDeviceIOS::StartRecording() {
LOGI() << "StartRecording";
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_RUN_ON(thread_);
RTC_DCHECK(audio_is_initialized_);
RTC_DCHECK(!recording_.load());
RTC_DCHECK(audio_unit_);
@ -305,7 +295,7 @@ int32_t AudioDeviceIOS::StartRecording() {
int32_t AudioDeviceIOS::StopRecording() {
LOGI() << "StopRecording";
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_RUN_ON(thread_);
if (!audio_is_initialized_ || !recording_.load()) {
return 0;
}
@ -329,7 +319,7 @@ int32_t AudioDeviceIOS::PlayoutDelay(uint16_t& delayMS) const {
int AudioDeviceIOS::GetPlayoutAudioParameters(AudioParameters* params) const {
LOGI() << "GetPlayoutAudioParameters";
RTC_DCHECK(playout_parameters_.is_valid());
RTC_DCHECK(thread_checker_.IsCurrent());
RTC_DCHECK_RUN_ON(thread_);
*params = playout_parameters_;
return 0;
}
@ -337,7 +327,7 @@ int AudioDeviceIOS::GetPlayoutAudioParameters(AudioParameters* params) const {
int AudioDeviceIOS::GetRecordAudioParameters(AudioParameters* params) const {
LOGI() << "GetRecordAudioParameters";
RTC_DCHECK(record_parameters_.is_valid());
RTC_DCHECK(thread_checker_.IsCurrent());
RTC_DCHECK_RUN_ON(thread_);
*params = record_parameters_;
return 0;
}
@ -345,31 +335,29 @@ int AudioDeviceIOS::GetRecordAudioParameters(AudioParameters* params) const {
void AudioDeviceIOS::OnInterruptionBegin() {
RTC_DCHECK(thread_);
LOGI() << "OnInterruptionBegin";
thread_->Post(RTC_FROM_HERE, this, kMessageTypeInterruptionBegin);
thread_->PostTask(SafeTask(safety_, [this] { HandleInterruptionBegin(); }));
}
void AudioDeviceIOS::OnInterruptionEnd() {
RTC_DCHECK(thread_);
LOGI() << "OnInterruptionEnd";
thread_->Post(RTC_FROM_HERE, this, kMessageTypeInterruptionEnd);
thread_->PostTask(SafeTask(safety_, [this] { HandleInterruptionEnd(); }));
}
void AudioDeviceIOS::OnValidRouteChange() {
RTC_DCHECK(thread_);
thread_->Post(RTC_FROM_HERE, this, kMessageTypeValidRouteChange);
thread_->PostTask(SafeTask(safety_, [this] { HandleValidRouteChange(); }));
}
void AudioDeviceIOS::OnCanPlayOrRecordChange(bool can_play_or_record) {
RTC_DCHECK(thread_);
thread_->Post(RTC_FROM_HERE,
this,
kMessageTypeCanPlayOrRecordChange,
new rtc::TypedMessageData<bool>(can_play_or_record));
thread_->PostTask(SafeTask(
safety_, [this, can_play_or_record] { HandleCanPlayOrRecordChange(can_play_or_record); }));
}
void AudioDeviceIOS::OnChangedOutputVolume() {
RTC_DCHECK(thread_);
thread_->Post(RTC_FROM_HERE, this, kMessageOutputVolumeChange);
thread_->PostTask(SafeTask(safety_, [this] { HandleOutputVolumeChange(); }));
}
OSStatus AudioDeviceIOS::OnDeliverRecordedData(AudioUnitRenderActionFlags* flags,
@ -464,7 +452,7 @@ OSStatus AudioDeviceIOS::OnGetPlayoutData(AudioUnitRenderActionFlags* flags,
if (glitch_threshold < 120 && delta_time > 120) {
RTCLog(@"Glitch warning is ignored. Probably caused by device switch.");
} else {
thread_->Post(RTC_FROM_HERE, this, kMessageTypePlayoutGlitchDetected);
thread_->PostTask(SafeTask(safety_, [this] { HandlePlayoutGlitchDetected(); }));
}
}
}
@ -479,34 +467,8 @@ OSStatus AudioDeviceIOS::OnGetPlayoutData(AudioUnitRenderActionFlags* flags,
return noErr;
}
void AudioDeviceIOS::OnMessage(rtc::Message* msg) {
switch (msg->message_id) {
case kMessageTypeInterruptionBegin:
HandleInterruptionBegin();
break;
case kMessageTypeInterruptionEnd:
HandleInterruptionEnd();
break;
case kMessageTypeValidRouteChange:
HandleValidRouteChange();
break;
case kMessageTypeCanPlayOrRecordChange: {
rtc::TypedMessageData<bool>* data = static_cast<rtc::TypedMessageData<bool>*>(msg->pdata);
HandleCanPlayOrRecordChange(data->data());
delete data;
break;
}
case kMessageTypePlayoutGlitchDetected:
HandlePlayoutGlitchDetected();
break;
case kMessageOutputVolumeChange:
HandleOutputVolumeChange();
break;
}
}
void AudioDeviceIOS::HandleInterruptionBegin() {
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_RUN_ON(thread_);
RTCLog(@"Interruption begin. IsInterrupted changed from %d to 1.", is_interrupted_);
if (audio_unit_ && audio_unit_->GetState() == VoiceProcessingAudioUnit::kStarted) {
RTCLog(@"Stopping the audio unit due to interruption begin.");
@ -519,7 +481,7 @@ void AudioDeviceIOS::HandleInterruptionBegin() {
}
void AudioDeviceIOS::HandleInterruptionEnd() {
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_RUN_ON(thread_);
RTCLog(@"Interruption ended. IsInterrupted changed from %d to 0. "
"Updating audio unit state.",
is_interrupted_);
@ -542,7 +504,7 @@ void AudioDeviceIOS::HandleInterruptionEnd() {
}
void AudioDeviceIOS::HandleValidRouteChange() {
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_RUN_ON(thread_);
RTC_OBJC_TYPE(RTCAudioSession)* session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance];
RTCLog(@"%@", session);
HandleSampleRateChange();
@ -554,7 +516,7 @@ void AudioDeviceIOS::HandleCanPlayOrRecordChange(bool can_play_or_record) {
}
void AudioDeviceIOS::HandleSampleRateChange() {
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_RUN_ON(thread_);
RTCLog(@"Handling sample rate change.");
// Don't do anything if we're interrupted.
@ -639,7 +601,7 @@ void AudioDeviceIOS::HandleSampleRateChange() {
}
void AudioDeviceIOS::HandlePlayoutGlitchDetected() {
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_RUN_ON(thread_);
// Don't update metrics if we're interrupted since a "glitch" is expected
// in this state.
if (is_interrupted_) {
@ -664,7 +626,7 @@ void AudioDeviceIOS::HandlePlayoutGlitchDetected() {
}
void AudioDeviceIOS::HandleOutputVolumeChange() {
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_RUN_ON(thread_);
RTCLog(@"Output volume change detected.");
// Store time of this detection so it can be used to defer detection of
// glitches too close in time to this event.
@ -752,7 +714,7 @@ bool AudioDeviceIOS::CreateAudioUnit() {
}
void AudioDeviceIOS::UpdateAudioUnit(bool can_play_or_record) {
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_RUN_ON(thread_);
RTCLog(@"Updating audio unit state. CanPlayOrRecord=%d IsInterrupted=%d",
can_play_or_record,
is_interrupted_);
@ -839,7 +801,7 @@ void AudioDeviceIOS::UpdateAudioUnit(bool can_play_or_record) {
}
bool AudioDeviceIOS::ConfigureAudioSession() {
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_RUN_ON(thread_);
RTCLog(@"Configuring audio session.");
if (has_configured_session_) {
RTCLogWarning(@"Audio session already configured.");
@ -859,7 +821,7 @@ bool AudioDeviceIOS::ConfigureAudioSession() {
}
bool AudioDeviceIOS::ConfigureAudioSessionLocked() {
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_RUN_ON(thread_);
RTCLog(@"Configuring audio session.");
if (has_configured_session_) {
RTCLogWarning(@"Audio session already configured.");
@ -877,7 +839,7 @@ bool AudioDeviceIOS::ConfigureAudioSessionLocked() {
}
void AudioDeviceIOS::UnconfigureAudioSession() {
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_RUN_ON(thread_);
RTCLog(@"Unconfiguring audio session.");
if (!has_configured_session_) {
RTCLogWarning(@"Audio session already unconfigured.");
@ -894,7 +856,7 @@ void AudioDeviceIOS::UnconfigureAudioSession() {
bool AudioDeviceIOS::InitPlayOrRecord() {
LOGI() << "InitPlayOrRecord";
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_RUN_ON(thread_);
// There should be no audio unit at this point.
if (!CreateAudioUnit()) {
@ -938,7 +900,7 @@ bool AudioDeviceIOS::InitPlayOrRecord() {
void AudioDeviceIOS::ShutdownPlayOrRecord() {
LOGI() << "ShutdownPlayOrRecord";
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_RUN_ON(thread_);
// Stop the audio unit to prevent any additional audio callbacks.
audio_unit_->Stop();