Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,4 @@ - (void)audioSession:(RTC_OBJC_TYPE(RTCAudioSession) *)audioSession
_observer->OnChangedOutputVolume();
}

- (void)audioSessionDidChangeRecordingEnabled:(RTC_OBJC_TYPE(RTCAudioSession) *)session {
// re-trigger audio unit init, by using interrupt ended callback
_observer->OnChangedRecordingEnabled();
}

@end
16 changes: 9 additions & 7 deletions sdk/objc/native/src/audio/audio_device_ios.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ class AudioDeviceIOS : public AudioDeviceGeneric,
void OnValidRouteChange() override;
void OnCanPlayOrRecordChange(bool can_play_or_record) override;
void OnChangedOutputVolume() override;
void OnChangedRecordingEnabled() override;

// VoiceProcessingAudioUnitObserver methods.
OSStatus OnDeliverRecordedData(AudioUnitRenderActionFlags* flags,
Expand Down Expand Up @@ -175,7 +174,8 @@ class AudioDeviceIOS : public AudioDeviceGeneric,
void HandleSampleRateChange();
void HandlePlayoutGlitchDetected();
void HandleOutputVolumeChange();
void HandleAudioSessionRecordingEnabledChange();

bool RestartAudioUnit(bool enable_input);

// Uses current `playout_parameters_` and `record_parameters_` to inform the
// audio device buffer (ADB) about our internal audio parameters.
Expand Down Expand Up @@ -205,7 +205,7 @@ class AudioDeviceIOS : public AudioDeviceGeneric,

// Activates our audio session, creates and initializes the voice-processing
// audio unit and verifies that we got the preferred native audio parameters.
bool InitPlayOrRecord();
bool InitPlayOrRecord(bool enable_input);

// Closes and deletes the voice-processing I/O unit.
void ShutdownPlayOrRecord();
Expand Down Expand Up @@ -269,19 +269,21 @@ class AudioDeviceIOS : public AudioDeviceGeneric,
// will be changed dynamically to account for this behavior.
rtc::BufferT<int16_t> record_audio_buffer_;

// Set to 1 when recording is initialized and 0 otherwise.
volatile int recording_is_initialized_;

// Set to 1 when recording is active and 0 otherwise.
volatile int recording_;

// Set to 1 when playout is initialized and 0 otherwise.
volatile int playout_is_initialized_;

// Set to 1 when playout is active and 0 otherwise.
volatile int playing_;

// Set to true after successful call to Init(), false otherwise.
bool initialized_ RTC_GUARDED_BY(thread_checker_);

// Set to true after successful call to InitRecording() or InitPlayout(),
// false otherwise.
bool audio_is_initialized_;

// Set to true if audio session is interrupted, false otherwise.
bool is_interrupted_;

Expand Down
104 changes: 61 additions & 43 deletions sdk/objc/native/src/audio/audio_device_ios.mm
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@
kMessageTypeCanPlayOrRecordChange,
kMessageTypePlayoutGlitchDetected,
kMessageOutputVolumeChange,
kMessageTypeRecordingEnabledChange,
};

using ios::CheckAndLogError;
Expand Down Expand Up @@ -104,10 +103,11 @@ static void LogDeviceInfo() {
: bypass_voice_processing_(bypass_voice_processing),
audio_device_buffer_(nullptr),
audio_unit_(nullptr),
recording_is_initialized_(0),
recording_(0),
playout_is_initialized_(0),
playing_(0),
initialized_(false),
audio_is_initialized_(false),
is_interrupted_(false),
has_configured_session_(false),
num_detected_playout_glitches_(0),
Expand Down Expand Up @@ -188,48 +188,62 @@ static void LogDeviceInfo() {
LOGI() << "InitPlayout";
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK(initialized_);
RTC_DCHECK(!audio_is_initialized_);
RTC_DCHECK(!playout_is_initialized_);
RTC_DCHECK(!playing_);
if (!audio_is_initialized_) {
if (!InitPlayOrRecord()) {

if (!recording_is_initialized_) {
// recording not initialized yet, init with no input
if (!InitPlayOrRecord(false)) {
RTC_LOG_F(LS_ERROR) << "InitPlayOrRecord failed for InitPlayout!";
return -1;
}
} else {
// recording is already initialized
// RestartAudioUnit(false);
}
audio_is_initialized_ = true;

rtc::AtomicOps::ReleaseStore(&playout_is_initialized_, 1);

return 0;
}

bool AudioDeviceIOS::PlayoutIsInitialized() const {
RTC_DCHECK_RUN_ON(&thread_checker_);
return audio_is_initialized_;
return playout_is_initialized_;
}

bool AudioDeviceIOS::RecordingIsInitialized() const {
RTC_DCHECK_RUN_ON(&thread_checker_);
return audio_is_initialized_;
return recording_is_initialized_;
}

int32_t AudioDeviceIOS::InitRecording() {
LOGI() << "InitRecording";
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK(initialized_);
RTC_DCHECK(!audio_is_initialized_);
RTC_DCHECK(!recording_is_initialized_);
RTC_DCHECK(!recording_);
if (!audio_is_initialized_) {
if (!InitPlayOrRecord()) {

if (!playout_is_initialized_) {
// playout not initialized yet, init with input
if (!InitPlayOrRecord(true)) {
RTC_LOG_F(LS_ERROR) << "InitPlayOrRecord failed for InitRecording!";
return -1;
}
} else {
// playout already initialized, restart audio unit with input
RestartAudioUnit(true);
}
audio_is_initialized_ = true;

rtc::AtomicOps::ReleaseStore(&recording_is_initialized_, 1);

return 0;
}

int32_t AudioDeviceIOS::StartPlayout() {
LOGI() << "StartPlayout";
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK(audio_is_initialized_);
RTC_DCHECK(playout_is_initialized_);
RTC_DCHECK(!playing_);
RTC_DCHECK(audio_unit_);
if (fine_audio_buffer_) {
Expand All @@ -254,14 +268,19 @@ static void LogDeviceInfo() {
int32_t AudioDeviceIOS::StopPlayout() {
LOGI() << "StopPlayout";
RTC_DCHECK_RUN_ON(&thread_checker_);
if (!audio_is_initialized_ || !playing_) {

if (!playout_is_initialized_ || !playing_) {
return 0;
}

if (!recording_) {
ShutdownPlayOrRecord();
audio_is_initialized_ = false;

rtc::AtomicOps::ReleaseStore(&recording_is_initialized_, 0);
}

rtc::AtomicOps::ReleaseStore(&playing_, 0);
rtc::AtomicOps::ReleaseStore(&playout_is_initialized_, 0);

// Derive average number of calls to OnGetPlayoutData() between detected
// audio glitches and add the result to a histogram.
Expand All @@ -285,7 +304,7 @@ static void LogDeviceInfo() {
int32_t AudioDeviceIOS::StartRecording() {
LOGI() << "StartRecording";
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK(audio_is_initialized_);
RTC_DCHECK(recording_is_initialized_);
RTC_DCHECK(!recording_);
RTC_DCHECK(audio_unit_);
if (fine_audio_buffer_) {
Expand All @@ -308,14 +327,23 @@ static void LogDeviceInfo() {
int32_t AudioDeviceIOS::StopRecording() {
LOGI() << "StopRecording";
RTC_DCHECK_RUN_ON(&thread_checker_);
if (!audio_is_initialized_ || !recording_) {

if (!recording_is_initialized_ || !recording_) {
return 0;
}

if (!playing_) {
ShutdownPlayOrRecord();
audio_is_initialized_ = false;

rtc::AtomicOps::ReleaseStore(&playout_is_initialized_, 0);
} else if (playout_is_initialized_) {
// restart audio unit with no input
RestartAudioUnit(false);
}

rtc::AtomicOps::ReleaseStore(&recording_, 0);
rtc::AtomicOps::ReleaseStore(&recording_is_initialized_, 0);

return 0;
}

Expand Down Expand Up @@ -374,11 +402,6 @@ static void LogDeviceInfo() {
thread_->Post(RTC_FROM_HERE, this, kMessageOutputVolumeChange);
}

void AudioDeviceIOS::OnChangedRecordingEnabled() {
RTC_DCHECK(thread_);
thread_->Post(RTC_FROM_HERE, this, kMessageTypeRecordingEnabledChange);
}

OSStatus AudioDeviceIOS::OnDeliverRecordedData(AudioUnitRenderActionFlags* flags,
const AudioTimeStamp* time_stamp,
UInt32 bus_number,
Expand Down Expand Up @@ -509,9 +532,6 @@ static void LogDeviceInfo() {
case kMessageOutputVolumeChange:
HandleOutputVolumeChange();
break;
case kMessageTypeRecordingEnabledChange:
HandleAudioSessionRecordingEnabledChange();
break;
}
}

Expand Down Expand Up @@ -627,7 +647,7 @@ static void LogDeviceInfo() {
SetupAudioBuffersForActiveAudioSession();

// Initialize the audio unit again with the new sample rate.
if (!audio_unit_->Initialize(playout_parameters_.sample_rate())) {
if (!audio_unit_->Initialize(playout_parameters_.sample_rate(), recording_is_initialized_)) {
RTCLogError(@"Failed to initialize the audio unit with sample rate: %d",
playout_parameters_.sample_rate());
return;
Expand Down Expand Up @@ -681,15 +701,15 @@ static void LogDeviceInfo() {
last_output_volume_change_time_ = rtc::TimeMillis();
}

void AudioDeviceIOS::HandleAudioSessionRecordingEnabledChange() {
bool AudioDeviceIOS::RestartAudioUnit(bool enable_input) {
RTC_DCHECK_RUN_ON(&thread_checker_);

LOGI() << "HandleAudioSessionRecordingEnabledChange";
LOGI() << "RestartAudioUnit";

// If we don't have an audio unit yet, or the audio unit is uninitialized,
// there is no work to do.
if (!audio_unit_ || audio_unit_->GetState() < VoiceProcessingAudioUnit::kInitialized) {
return;
return false;
}

// The audio unit is already initialized or started.
Expand All @@ -700,19 +720,16 @@ static void LogDeviceInfo() {
// Extra sanity check to ensure that the new sample rate is valid.
if (session_sample_rate <= 0.0) {
RTCLogError(@"Sample rate is invalid: %f", session_sample_rate);
LOGI() << "Sample rate is invalid " << session_sample_rate;
return;
return false;
}
// We need to adjust our format and buffer sizes.
// The stream format is about to be changed and it requires that we first
// stop and uninitialize the audio unit to deallocate its resources.
RTCLog(@"Stopping and uninitializing audio unit to adjust buffers.");

bool restart_audio_unit = false;
if (audio_unit_->GetState() == VoiceProcessingAudioUnit::kStarted) {
audio_unit_->Stop();
restart_audio_unit = true;
PrepareForNewStart();
}

if (audio_unit_->GetState() == VoiceProcessingAudioUnit::kInitialized) {
audio_unit_->Uninitialize();
}
Expand All @@ -722,18 +739,19 @@ static void LogDeviceInfo() {

// Initialize the audio unit again with the new sample rate.
RTC_DCHECK_EQ(playout_parameters_.sample_rate(), session_sample_rate);
if (!audio_unit_->Initialize(session_sample_rate)) {
if (!audio_unit_->Initialize(session_sample_rate, enable_input)) {
RTCLogError(@"Failed to initialize the audio unit with sample rate: %f", session_sample_rate);
return;
return false;
}

// Restart the audio unit if it was already running.
if (restart_audio_unit && !audio_unit_->Start()) {
RTCLogError(@"Failed to start audio unit with sample rate: %f", session_sample_rate);
return;
return false;
}

LOGI() << "Successfully enabled audio unit for recording.";
return true;
}

void AudioDeviceIOS::UpdateAudioDeviceBuffer() {
Expand Down Expand Up @@ -829,7 +847,7 @@ static void LogDeviceInfo() {

// If we're not initialized we don't need to do anything. Audio unit will
// be initialized on initialization.
if (!audio_is_initialized_) return;
if (!playout_is_initialized_ && !recording_is_initialized_) return;

// If we're initialized, we must have an audio unit.
RTC_DCHECK(audio_unit_);
Expand Down Expand Up @@ -866,7 +884,7 @@ static void LogDeviceInfo() {
RTCLog(@"Initializing audio unit for UpdateAudioUnit");
ConfigureAudioSession();
SetupAudioBuffersForActiveAudioSession();
if (!audio_unit_->Initialize(playout_parameters_.sample_rate())) {
if (!audio_unit_->Initialize(playout_parameters_.sample_rate(), recording_is_initialized_)) {
RTCLogError(@"Failed to initialize audio unit.");
return;
}
Expand Down Expand Up @@ -956,7 +974,7 @@ static void LogDeviceInfo() {
RTCLog(@"Unconfigured audio session.");
}

bool AudioDeviceIOS::InitPlayOrRecord() {
bool AudioDeviceIOS::InitPlayOrRecord(bool enable_input) {
LOGI() << "InitPlayOrRecord";
RTC_DCHECK_RUN_ON(&thread_checker_);

Expand Down Expand Up @@ -992,7 +1010,7 @@ static void LogDeviceInfo() {
return false;
}
SetupAudioBuffersForActiveAudioSession();
audio_unit_->Initialize(playout_parameters_.sample_rate());
audio_unit_->Initialize(playout_parameters_.sample_rate(), enable_input);
}

// Release the lock.
Expand Down
2 changes: 0 additions & 2 deletions sdk/objc/native/src/audio/audio_session_observer.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ class AudioSessionObserver {

virtual void OnChangedOutputVolume() = 0;

virtual void OnChangedRecordingEnabled() = 0;

protected:
virtual ~AudioSessionObserver() {}
};
Expand Down
2 changes: 1 addition & 1 deletion sdk/objc/native/src/audio/voice_processing_audio_unit.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class VoiceProcessingAudioUnit {
VoiceProcessingAudioUnit::State GetState() const;

// Initializes the underlying audio unit with the given sample rate.
bool Initialize(Float64 sample_rate);
bool Initialize(Float64 sample_rate, bool enable_input);

// Starts the underlying audio unit.
OSStatus Start();
Expand Down
18 changes: 5 additions & 13 deletions sdk/objc/native/src/audio/voice_processing_audio_unit.mm
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ static OSStatus GetAGCState(AudioUnit audio_unit, UInt32* enabled) {
return state_;
}

bool VoiceProcessingAudioUnit::Initialize(Float64 sample_rate) {
bool VoiceProcessingAudioUnit::Initialize(Float64 sample_rate, bool enable_input) {
RTC_DCHECK_GE(state_, kUninitialized);
RTCLog(@"Initializing audio unit with sample rate: %f", sample_rate);

Expand All @@ -191,19 +191,11 @@ static OSStatus GetAGCState(AudioUnit audio_unit, UInt32* enabled) {
LogStreamDescription(format);
#endif

// Enable input on the input scope of the input element.
// keep it disabled if audio session is configured for playback only
AVAudioSession* session = [AVAudioSession sharedInstance];
UInt32 enable_input = 0;
if ([session.category isEqualToString: AVAudioSessionCategoryPlayAndRecord] ||
[session.category isEqualToString: AVAudioSessionCategoryRecord]) {
enable_input = 1;
}
RTCLog(@"Initializing AudioUnit, category=%@, enable_input=%d", session.category, (int) enable_input);
// LOGI() << "Initialize" << session.category << ", enable_input=" << enable_input;
UInt32 _enable_input = enable_input ? 1 : 0;
RTCLog(@"Initializing AudioUnit, _enable_input=%d", (int) _enable_input);
result = AudioUnitSetProperty(vpio_unit_, kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input, kInputBus, &enable_input,
sizeof(enable_input));
kAudioUnitScope_Input, kInputBus, &_enable_input,
sizeof(_enable_input));
if (result != noErr) {
DisposeAudioUnit();
RTCLogError(@"Failed to enable input on input scope of input element. "
Expand Down