Resolves issue with multiple calls to audio unit initialization
BUG=webrtc:5166 R=tkchin@webrtc.org Review URL: https://codereview.webrtc.org/1472833002 . Cr-Commit-Position: refs/heads/master@{#10865}
This commit is contained in:
@ -86,6 +86,12 @@ const UInt32 kBytesPerSample = 2;
|
|||||||
// Can most likely be removed.
|
// Can most likely be removed.
|
||||||
const UInt16 kFixedPlayoutDelayEstimate = 30;
|
const UInt16 kFixedPlayoutDelayEstimate = 30;
|
||||||
const UInt16 kFixedRecordDelayEstimate = 30;
|
const UInt16 kFixedRecordDelayEstimate = 30;
|
||||||
|
// Calls to AudioUnitInitialize() can fail if called back-to-back on different
|
||||||
|
// ADM instances. A fall-back solution is to allow multiple sequential calls
|
||||||
|
// with as small delay between each. This factor sets the max number of allowed
|
||||||
|
// initialization attempts.
|
||||||
|
const int kMaxNumberOfAudioUnitInitializeAttempts = 5;
|
||||||
|
|
||||||
|
|
||||||
using ios::CheckAndLogError;
|
using ios::CheckAndLogError;
|
||||||
|
|
||||||
@ -741,6 +747,7 @@ bool AudioDeviceIOS::SetupAndInitializeVoiceProcessingAudioUnit() {
|
|||||||
vpio_unit_description.componentManufacturer = kAudioUnitManufacturer_Apple;
|
vpio_unit_description.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||||
vpio_unit_description.componentFlags = 0;
|
vpio_unit_description.componentFlags = 0;
|
||||||
vpio_unit_description.componentFlagsMask = 0;
|
vpio_unit_description.componentFlagsMask = 0;
|
||||||
|
|
||||||
// Obtain an audio unit instance given the description.
|
// Obtain an audio unit instance given the description.
|
||||||
AudioComponent found_vpio_unit_ref =
|
AudioComponent found_vpio_unit_ref =
|
||||||
AudioComponentFindNext(nullptr, &vpio_unit_description);
|
AudioComponentFindNext(nullptr, &vpio_unit_description);
|
||||||
@ -876,17 +883,28 @@ bool AudioDeviceIOS::SetupAndInitializeVoiceProcessingAudioUnit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the Voice-Processing I/O unit instance.
|
// Initialize the Voice-Processing I/O unit instance.
|
||||||
|
// Calls to AudioUnitInitialize() can fail if called back-to-back on
|
||||||
|
// different ADM instances. The error message in this case is -66635 which is
|
||||||
|
// undocumented. Tests have shown that calling AudioUnitInitialize a second
|
||||||
|
// time, after a short sleep, avoids this issue.
|
||||||
|
// See webrtc:5166 for details.
|
||||||
|
int failed_initalize_attempts = 0;
|
||||||
result = AudioUnitInitialize(vpio_unit_);
|
result = AudioUnitInitialize(vpio_unit_);
|
||||||
if (result != noErr) {
|
while (result != noErr) {
|
||||||
result = AudioUnitUninitialize(vpio_unit_);
|
|
||||||
if (result != noErr) {
|
|
||||||
LOG_F(LS_ERROR) << "AudioUnitUninitialize failed: " << result;
|
|
||||||
}
|
|
||||||
DisposeAudioUnit();
|
|
||||||
LOG(LS_ERROR) << "Failed to initialize the Voice-Processing I/O unit: "
|
LOG(LS_ERROR) << "Failed to initialize the Voice-Processing I/O unit: "
|
||||||
<< result;
|
<< result;
|
||||||
|
++failed_initalize_attempts;
|
||||||
|
if (failed_initalize_attempts == kMaxNumberOfAudioUnitInitializeAttempts) {
|
||||||
|
// Max number of initialization attempts exceeded, hence abort.
|
||||||
|
LOG(LS_WARNING) << "Too many initialization attempts";
|
||||||
|
DisposeAudioUnit();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
LOG(LS_INFO) << "pause 100ms and try audio unit initialization again...";
|
||||||
|
[NSThread sleepForTimeInterval:0.1f];
|
||||||
|
result = AudioUnitInitialize(vpio_unit_);
|
||||||
|
}
|
||||||
|
LOG(LS_INFO) << "Voice-Processing I/O unit is now initialized";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -636,6 +636,55 @@ TEST_F(AudioDeviceTest, StopPlayoutRequiresInitToRestart) {
|
|||||||
EXPECT_FALSE(audio_device()->PlayoutIsInitialized());
|
EXPECT_FALSE(audio_device()->PlayoutIsInitialized());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify that we can create two ADMs and start playing on the second ADM.
|
||||||
|
// Only the first active instance shall activate an audio session and the
|
||||||
|
// last active instace shall deactivate the audio session.
|
||||||
|
TEST_F(AudioDeviceTest, StartPlayoutOnTwoInstances) {
|
||||||
|
// Create and initialize a second/extra ADM instance. The default ADM is
|
||||||
|
// created by the test harness.
|
||||||
|
rtc::scoped_refptr<AudioDeviceModule> second_audio_device =
|
||||||
|
CreateAudioDevice(AudioDeviceModule::kPlatformDefaultAudio);
|
||||||
|
EXPECT_NE(second_audio_device.get(), nullptr);
|
||||||
|
EXPECT_EQ(0, second_audio_device->Init());
|
||||||
|
|
||||||
|
// Start playout for the default ADM. Ignore the callback sequence.
|
||||||
|
NiceMock<MockAudioTransport> mock(kPlayout);
|
||||||
|
EXPECT_EQ(0, audio_device()->RegisterAudioCallback(&mock));
|
||||||
|
StartPlayout();
|
||||||
|
|
||||||
|
// Initialize playout for the second ADM. If all is OK, the second ADM shall
|
||||||
|
// reuse the audio session activated when the first ADM started playing.
|
||||||
|
// This call will also ensure that we avoid a problem related to initializing
|
||||||
|
// two different audio unit instances back to back (see webrtc:5166 for
|
||||||
|
// details).
|
||||||
|
EXPECT_EQ(0, second_audio_device->InitPlayout());
|
||||||
|
EXPECT_TRUE(second_audio_device->PlayoutIsInitialized());
|
||||||
|
|
||||||
|
// Stop playout for the default ADM. The audio session shall not be
|
||||||
|
// deactivated since it is used by the second ADM.
|
||||||
|
StopPlayout();
|
||||||
|
|
||||||
|
// Start playout for the second ADM and verify that it starts as intended.
|
||||||
|
// Passing this test ensures that initialization of the second audio unit
|
||||||
|
// has been done successfully.
|
||||||
|
MockAudioTransport mock2(kPlayout);
|
||||||
|
mock2.HandleCallbacks(test_is_done_.get(), nullptr, kNumCallbacks);
|
||||||
|
EXPECT_CALL(
|
||||||
|
mock2, NeedMorePlayData(playout_frames_per_10ms_buffer(), kBytesPerSample,
|
||||||
|
playout_channels(), playout_sample_rate(),
|
||||||
|
NotNull(), _, _, _))
|
||||||
|
.Times(AtLeast(kNumCallbacks));
|
||||||
|
EXPECT_EQ(0, second_audio_device->RegisterAudioCallback(&mock2));
|
||||||
|
EXPECT_EQ(0, second_audio_device->StartPlayout());
|
||||||
|
EXPECT_TRUE(second_audio_device->Playing());
|
||||||
|
test_is_done_->Wait(kTestTimeOutInMilliseconds);
|
||||||
|
EXPECT_EQ(0, second_audio_device->StopPlayout());
|
||||||
|
EXPECT_FALSE(second_audio_device->Playing());
|
||||||
|
EXPECT_FALSE(second_audio_device->PlayoutIsInitialized());
|
||||||
|
|
||||||
|
EXPECT_EQ(0, second_audio_device->Terminate());
|
||||||
|
}
|
||||||
|
|
||||||
// Start playout and verify that the native audio layer starts asking for real
|
// Start playout and verify that the native audio layer starts asking for real
|
||||||
// audio samples to play out using the NeedMorePlayData callback.
|
// audio samples to play out using the NeedMorePlayData callback.
|
||||||
TEST_F(AudioDeviceTest, StartPlayoutVerifyCallbacks) {
|
TEST_F(AudioDeviceTest, StartPlayoutVerifyCallbacks) {
|
||||||
|
Reference in New Issue
Block a user