Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(46)

Unified Diff: webrtc/modules/audio_device/ios/audio_device_ios.mm

Issue 1945563003: Provide isAudioEnabled flag to control audio unit. (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Fix some bluetooth issue. Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: webrtc/modules/audio_device/ios/audio_device_ios.mm
diff --git a/webrtc/modules/audio_device/ios/audio_device_ios.mm b/webrtc/modules/audio_device/ios/audio_device_ios.mm
index 0e127c9dc28d99578ffb9600b977096131586a6a..3b566adad13e2c6f9d0ef0fea6641f1e5fbd3805 100644
--- a/webrtc/modules/audio_device/ios/audio_device_ios.mm
+++ b/webrtc/modules/audio_device/ios/audio_device_ios.mm
@@ -61,6 +61,14 @@ namespace webrtc {
const UInt16 kFixedPlayoutDelayEstimate = 30;
const UInt16 kFixedRecordDelayEstimate = 30;
+enum AudioDeviceMessageType : uint32_t {
+ kMessageTypeInterruptionBegin,
+ kMessageTypeInterruptionEnd,
+ kMessageTypeValidRouteChange,
+ kMessageTypeCanPlayOrRecordChange,
+ kMessageTypeSampleRateChange,
+};
+
using ios::CheckAndLogError;
#if !defined(NDEBUG)
@@ -85,15 +93,15 @@ static void LogDeviceInfo() {
#endif // !defined(NDEBUG)
AudioDeviceIOS::AudioDeviceIOS()
- : async_invoker_(new rtc::AsyncInvoker()),
- audio_device_buffer_(nullptr),
+ : audio_device_buffer_(nullptr),
audio_unit_(nullptr),
recording_(0),
playing_(0),
initialized_(false),
rec_is_initialized_(false),
play_is_initialized_(false),
- is_interrupted_(false) {
+ is_interrupted_(false),
+ has_configured_session_(false) {
LOGI() << "ctor" << ios::GetCurrentThreadDescription();
thread_ = rtc::Thread::Current();
audio_session_observer_ =
@@ -318,51 +326,24 @@ int AudioDeviceIOS::GetRecordAudioParameters(AudioParameters* params) const {
}
void AudioDeviceIOS::OnInterruptionBegin() {
- RTC_DCHECK(async_invoker_);
RTC_DCHECK(thread_);
- if (thread_->IsCurrent()) {
- HandleInterruptionBegin();
- return;
- }
- async_invoker_->AsyncInvoke<void>(
- thread_,
- rtc::Bind(&webrtc::AudioDeviceIOS::HandleInterruptionBegin, this));
+ thread_->Post(this, kMessageTypeInterruptionBegin);
}
void AudioDeviceIOS::OnInterruptionEnd() {
- RTC_DCHECK(async_invoker_);
RTC_DCHECK(thread_);
- if (thread_->IsCurrent()) {
- HandleInterruptionEnd();
- return;
- }
- async_invoker_->AsyncInvoke<void>(
- thread_,
- rtc::Bind(&webrtc::AudioDeviceIOS::HandleInterruptionEnd, this));
+ thread_->Post(this, kMessageTypeInterruptionEnd);
}
void AudioDeviceIOS::OnValidRouteChange() {
- RTC_DCHECK(async_invoker_);
RTC_DCHECK(thread_);
- if (thread_->IsCurrent()) {
- HandleValidRouteChange();
- return;
- }
- async_invoker_->AsyncInvoke<void>(
- thread_,
- rtc::Bind(&webrtc::AudioDeviceIOS::HandleValidRouteChange, this));
+ thread_->Post(this, kMessageTypeValidRouteChange);
}
-void AudioDeviceIOS::OnConfiguredForWebRTC() {
- RTC_DCHECK(async_invoker_);
+void AudioDeviceIOS::OnCanPlayOrRecordChange(bool can_play_or_record) {
RTC_DCHECK(thread_);
- if (thread_->IsCurrent()) {
- HandleValidRouteChange();
- return;
- }
- async_invoker_->AsyncInvoke<void>(
- thread_,
- rtc::Bind(&webrtc::AudioDeviceIOS::HandleConfiguredForWebRTC, this));
+ thread_->Post(this, kMessageTypeCanPlayOrRecordChange,
+ new rtc::TypedMessageData<bool>(can_play_or_record));
}
OSStatus AudioDeviceIOS::OnDeliverRecordedData(
@@ -385,6 +366,9 @@ OSStatus AudioDeviceIOS::OnDeliverRecordedData(
RTCLogWarning(@"Expected %u frames but got %u",
static_cast<unsigned int>(frames_per_buffer),
static_cast<unsigned int>(num_frames));
+
+ RTCAudioSession *session = [RTCAudioSession sharedInstance];
+ RTCLogWarning(@"Session:\n %@", session);
return result;
}
@@ -447,6 +431,40 @@ OSStatus AudioDeviceIOS::OnGetPlayoutData(AudioUnitRenderActionFlags* flags,
return noErr;
}
+void AudioDeviceIOS::OnSampleRateChange(float sample_rate) {
+ RTC_DCHECK(thread_);
+ thread_->Post(this, kMessageTypeSampleRateChange,
+ new rtc::TypedMessageData<float>(sample_rate));
+}
+
+void AudioDeviceIOS::OnMessage(rtc::Message *msg) {
henrika_webrtc 2016/05/04 12:33:07 nice!
tkchin_webrtc 2016/05/05 23:23:27 Acknowledged.
+ 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 kMessageTypeSampleRateChange: {
+ rtc::TypedMessageData<float>* data =
+ static_cast<rtc::TypedMessageData<float>*>(msg->pdata);
+ HandleSampleRateChange(data->data());
+ delete data;
+ break;
+ }
+ }
+}
+
void AudioDeviceIOS::HandleInterruptionBegin() {
RTC_DCHECK(thread_checker_.CalledOnValidThread());
@@ -470,27 +488,13 @@ void AudioDeviceIOS::HandleInterruptionEnd() {
void AudioDeviceIOS::HandleValidRouteChange() {
RTC_DCHECK(thread_checker_.CalledOnValidThread());
- // Don't do anything if we're interrupted.
- if (is_interrupted_) {
- return;
- }
-
- // Only restart audio for a valid route change if the session sample rate
- // has changed.
RTCAudioSession* session = [RTCAudioSession sharedInstance];
- const double current_sample_rate = playout_parameters_.sample_rate();
- const double session_sample_rate = session.sampleRate;
- if (current_sample_rate != session_sample_rate) {
- RTCLog(@"Route changed caused sample rate to change from %f to %f. "
- "Restarting audio unit.", current_sample_rate, session_sample_rate);
- if (!RestartAudioUnit(session_sample_rate)) {
- RTCLogError(@"Audio restart failed.");
- }
- }
+ HandleSampleRateChange(session.sampleRate);
}
-void AudioDeviceIOS::HandleConfiguredForWebRTC() {
+void AudioDeviceIOS::HandleCanPlayOrRecordChange(bool can_play_or_record) {
RTC_DCHECK(thread_checker_.CalledOnValidThread());
+ RTCLog(@"Handling CanPlayOrRecordChange to %d", can_play_or_record);
// If we're not initialized we don't need to do anything. Audio unit will
// be initialized on initialization.
@@ -500,26 +504,111 @@ void AudioDeviceIOS::HandleConfiguredForWebRTC() {
// If we're initialized, we must have an audio unit.
RTC_DCHECK(audio_unit_);
- // Use configured audio session's settings to set up audio device buffer.
- // TODO(tkchin): Use RTCAudioSessionConfiguration to pick up settings and
- // pass it along.
- SetupAudioBuffersForActiveAudioSession();
+ bool should_initialize_audio_unit = false;
+ bool should_uninitialize_audio_unit = false;
+ bool should_start_audio_unit = false;
+ bool should_stop_audio_unit = false;
+
+ switch (audio_unit_->GetState()) {
+ case VoiceProcessingAudioUnit::kInitRequired:
+ RTC_NOTREACHED();
+ break;
+ case VoiceProcessingAudioUnit::kUninitialized:
+ should_initialize_audio_unit = can_play_or_record;
+ should_start_audio_unit = should_initialize_audio_unit &&
+ (playing_ || recording_);
+ break;
+ case VoiceProcessingAudioUnit::kInitialized:
+ should_start_audio_unit = can_play_or_record && (playing_ || recording_);
+ should_uninitialize_audio_unit = !can_play_or_record;
+ break;
+ case VoiceProcessingAudioUnit::kStarted:
+ RTC_DCHECK(playing_ || recording_);
+ should_stop_audio_unit = !can_play_or_record;
+ should_uninitialize_audio_unit = should_stop_audio_unit;
+ break;
+ }
+
+ if (should_initialize_audio_unit) {
+ RTCLog(@"Initializing audio unit for CanPlayOrRecordChange");
+ // Mad hack: Sometimes the audio session is already active because the app
henrika_webrtc 2016/05/04 12:33:07 Wow. Do you have a case where you can reliably tes
tkchin_webrtc 2016/05/05 23:23:27 Yup. With my BT headset. I'm backing out the chang
henrika_webrtc 2016/05/06 11:22:16 Acknowledged.
+ // activated it. In this condition, the audio session reports an inaccurate
+ // IOBufferDuration until the audio unit is started, but this causes audio
+ // artifacts. To work around it, deactivate it forcibly and reactivate it.
+ // This only works if the app has only activated the session once.
+ RTCAudioSession *session = [RTCAudioSession sharedInstance];
+ BOOL refreshAudioSession = session.isActive;
+ if (refreshAudioSession) {
+ [session lockForConfiguration];
+ [session setActive:NO error:nil];
+ [session unlockForConfiguration];
+ }
+ ConfigureAudioSession();
+ if (refreshAudioSession) {
+ [session lockForConfiguration];
+ [session setActive:YES error:nil];
+ [session unlockForConfiguration];
+ }
+ SetupAudioBuffersForActiveAudioSession();
+ if (!audio_unit_->Initialize(playout_parameters_.sample_rate())) {
+ RTCLogError(@"Failed to initialize audio unit.");
+ return;
+ }
+ }
- // Initialize the audio unit. This will affect any existing audio playback.
- if (!audio_unit_->Initialize(playout_parameters_.sample_rate())) {
- RTCLogError(@"Failed to initialize audio unit after configuration.");
- return;
+ if (should_start_audio_unit) {
+ RTCLog(@"Starting audio unit for CanPlayOrRecordChange");
+ if (!audio_unit_->Start()) {
+ RTCLogError(@"Failed to start audio unit.");
+ return;
+ }
}
- // If we haven't started playing or recording there's nothing more to do.
- if (!playing_ && !recording_)
- return;
+ if (should_stop_audio_unit) {
+ RTCLog(@"Stopping audio unit for CanPlayOrRecordChange");
+ if (!audio_unit_->Stop()) {
+ RTCLogError(@"Failed to stop audio unit.");
+ return;
+ }
+ }
- // We are in a play or record state, start the audio unit.
- if (!audio_unit_->Start()) {
- RTCLogError(@"Failed to start audio unit after configuration.");
+ if (should_uninitialize_audio_unit) {
+ RTCLog(@"Uninitializing audio unit for CanPlayOrRecordChange");
+ audio_unit_->Uninitialize();
+ UnconfigureAudioSession();
+ }
+}
+
+void AudioDeviceIOS::HandleSampleRateChange(float sample_rate) {
+ RTC_DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Don't do anything if we're interrupted.
+ if (is_interrupted_) {
return;
}
+
+ RTCAudioSession* session = [RTCAudioSession sharedInstance];
+ const double session_sample_rate = session.sampleRate;
+ const NSTimeInterval session_buffer_duration = session.IOBufferDuration;
+ const size_t session_frames_per_buffer =
+ static_cast<size_t>(session_sample_rate * session_buffer_duration + .5);
+ const double current_sample_rate = playout_parameters_.sample_rate();
+ const size_t current_frames_per_buffer =
+ playout_parameters_.frames_per_buffer();
+ RTCLog(@"Handling playout sample rate change to: %f\n"
+ " Session sample rate: %f frames_per_buffer: %lu\n"
+ " ADM sample rate: %f frames_per_buffer: %lu",
+ sample_rate,
+ session_sample_rate, (unsigned long)session_frames_per_buffer,
+ current_sample_rate, (unsigned long)current_frames_per_buffer);;
+
+ if (current_sample_rate != session_sample_rate ||
+ current_frames_per_buffer != session_frames_per_buffer) {
+ RTCLog(@"Restarting audio unit due to frames per buffer change.");
+ if (!RestartAudioUnit(sample_rate)) {
+ RTCLogError(@"Audio restart failed.");
+ }
+ }
}
void AudioDeviceIOS::UpdateAudioDeviceBuffer() {
@@ -597,6 +686,7 @@ void AudioDeviceIOS::SetupAudioBuffersForActiveAudioSession() {
// at each input callback when calling AudioUnitRender().
const int data_byte_size = record_parameters_.GetBytesPerBuffer();
record_audio_buffer_.reset(new SInt8[data_byte_size]);
+ memset(record_audio_buffer_.get(), 0, data_byte_size);
audio_record_buffer_list_.mNumberBuffers = 1;
AudioBuffer* audio_buffer = &audio_record_buffer_list_.mBuffers[0];
audio_buffer->mNumberChannels = record_parameters_.channels();
@@ -653,9 +743,40 @@ bool AudioDeviceIOS::RestartAudioUnit(float sample_rate) {
return true;
}
+void AudioDeviceIOS::ConfigureAudioSession() {
+ RTC_DCHECK(thread_checker_.CalledOnValidThread());
+ RTCLog(@"Configuring audio session.");
+ if (has_configured_session_) {
+ RTCLogWarning(@"Audio session already configured.");
+ return;
+ }
+ RTCAudioSession* session = [RTCAudioSession sharedInstance];
+ [session lockForConfiguration];
+ [session configureWebRTCSession:nil];
+ [session unlockForConfiguration];
+ has_configured_session_ = true;
+ RTCLog(@"Configured audio session.");
+}
+
+void AudioDeviceIOS::UnconfigureAudioSession() {
+ RTC_DCHECK(thread_checker_.CalledOnValidThread());
+ RTCLog(@"Unconfiguring audio session.");
+ if (!has_configured_session_) {
+ RTCLogWarning(@"Audio session already unconfigured.");
+ return;
+ }
+ RTCAudioSession* session = [RTCAudioSession sharedInstance];
+ [session lockForConfiguration];
+ [session unconfigureWebRTCSession:nil];
+ [session unlockForConfiguration];
+ has_configured_session_ = false;
+ RTCLog(@"Unconfigured audio session.");
+}
+
bool AudioDeviceIOS::InitPlayOrRecord() {
LOGI() << "InitPlayOrRecord";
+ // There should be no audio unit at this point.
if (!CreateAudioUnit()) {
return false;
}
@@ -674,14 +795,11 @@ bool AudioDeviceIOS::InitPlayOrRecord() {
return false;
}
- // If we are already configured properly, we can initialize the audio unit.
- if (session.isConfiguredForWebRTC) {
- [session unlockForConfiguration];
+ // If we are ready to play or record, initialize the audio unit.
+ if (session.canPlayOrRecord) {
+ ConfigureAudioSession();
SetupAudioBuffersForActiveAudioSession();
- // Audio session has been marked ready for WebRTC so we can initialize the
- // audio unit now.
audio_unit_->Initialize(playout_parameters_.sample_rate());
- return true;
}
// Release the lock.
@@ -694,17 +812,16 @@ void AudioDeviceIOS::ShutdownPlayOrRecord() {
LOGI() << "ShutdownPlayOrRecord";
// Close and delete the voice-processing I/O unit.
- if (audio_unit_) {
- audio_unit_.reset();
- }
+ audio_unit_.reset();
- // Remove audio session notification observers.
RTCAudioSession* session = [RTCAudioSession sharedInstance];
+ // Remove audio session notification observers.
[session removeDelegate:audio_session_observer_];
// All I/O should be stopped or paused prior to deactivating the audio
// session, hence we deactivate as last action.
[session lockForConfiguration];
+ UnconfigureAudioSession();
[session endWebRTCSession:nil];
[session unlockForConfiguration];
}

Powered by Google App Engine
This is Rietveld 408576698