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

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

Issue 1809343002: Refactor AudioUnit code into its own class. (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: CR comments Created 4 years, 9 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 6f20c6a8b730243954bb4f47388d558b6e6c2223..860c1a280057d075ed50cd2641c8193ed29bb2a3 100644
--- a/webrtc/modules/audio_device/ios/audio_device_ios.mm
+++ b/webrtc/modules/audio_device/ios/audio_device_ios.mm
@@ -55,40 +55,15 @@ namespace webrtc {
} while (0)
-// Number of bytes per audio sample for 16-bit signed integer representation.
-const UInt32 kBytesPerSample = 2;
// Hardcoded delay estimates based on real measurements.
// TODO(henrika): these value is not used in combination with built-in AEC.
// Can most likely be removed.
const UInt16 kFixedPlayoutDelayEstimate = 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;
#if !defined(NDEBUG)
-// Helper method for printing out an AudioStreamBasicDescription structure.
-static void LogABSD(AudioStreamBasicDescription absd) {
- char formatIDString[5];
- UInt32 formatID = CFSwapInt32HostToBig(absd.mFormatID);
- bcopy(&formatID, formatIDString, 4);
- formatIDString[4] = '\0';
- LOG(LS_INFO) << "LogABSD";
- LOG(LS_INFO) << " sample rate: " << absd.mSampleRate;
- LOG(LS_INFO) << " format ID: " << formatIDString;
- LOG(LS_INFO) << " format flags: " << std::hex << absd.mFormatFlags;
- LOG(LS_INFO) << " bytes per packet: " << absd.mBytesPerPacket;
- LOG(LS_INFO) << " frames per packet: " << absd.mFramesPerPacket;
- LOG(LS_INFO) << " bytes per frame: " << absd.mBytesPerFrame;
- LOG(LS_INFO) << " channels per packet: " << absd.mChannelsPerFrame;
- LOG(LS_INFO) << " bits per channel: " << absd.mBitsPerChannel;
- LOG(LS_INFO) << " reserved: " << absd.mReserved;
-}
-
// Helper method that logs essential device information strings.
static void LogDeviceInfo() {
LOG(LS_INFO) << "LogDeviceInfo";
@@ -110,15 +85,15 @@ static void LogDeviceInfo() {
#endif // !defined(NDEBUG)
AudioDeviceIOS::AudioDeviceIOS()
- : async_invoker_(new rtc::AsyncInvoker()),
- audio_device_buffer_(nullptr),
- vpio_unit_(nullptr),
- recording_(0),
- playing_(0),
- initialized_(false),
- rec_is_initialized_(false),
- play_is_initialized_(false),
- is_interrupted_(false) {
+ : async_invoker_(new rtc::AsyncInvoker()),
+ audio_device_buffer_(nullptr),
+ audio_unit_(nullptr),
+ recording_(0),
+ playing_(0),
+ initialized_(false),
+ rec_is_initialized_(false),
+ play_is_initialized_(false),
+ is_interrupted_(false) {
LOGI() << "ctor" << ios::GetCurrentThreadDescription();
thread_ = rtc::Thread::Current();
audio_session_observer_ =
@@ -218,10 +193,8 @@ int32_t AudioDeviceIOS::StartPlayout() {
RTC_DCHECK(!playing_);
fine_audio_buffer_->ResetPlayout();
if (!recording_) {
- OSStatus result = AudioOutputUnitStart(vpio_unit_);
- if (result != noErr) {
- LOG_F(LS_ERROR) << "AudioOutputUnitStart failed for StartPlayout: "
- << result;
+ if (!audio_unit_->Start()) {
+ RTCLogError(@"StartPlayout failed to start audio unit.");
return -1;
}
LOG(LS_INFO) << "Voice-Processing I/O audio unit is now started";
@@ -251,10 +224,8 @@ int32_t AudioDeviceIOS::StartRecording() {
RTC_DCHECK(!recording_);
fine_audio_buffer_->ResetRecord();
if (!playing_) {
- OSStatus result = AudioOutputUnitStart(vpio_unit_);
- if (result != noErr) {
- LOG_F(LS_ERROR) << "AudioOutputUnitStart failed for StartRecording: "
- << result;
+ if (!audio_unit_->Start()) {
+ RTCLogError(@"StartRecording failed to start audio unit.");
return -1;
}
LOG(LS_INFO) << "Voice-Processing I/O audio unit is now started";
@@ -376,19 +347,103 @@ void AudioDeviceIOS::OnValidRouteChange() {
rtc::Bind(&webrtc::AudioDeviceIOS::HandleValidRouteChange, this));
}
+OSStatus AudioDeviceIOS::OnDeliverRecordedData(
+ AudioUnitRenderActionFlags* flags,
+ const AudioTimeStamp* time_stamp,
+ UInt32 bus_number,
+ UInt32 num_frames,
+ AudioBufferList* /* io_data */) {
+ OSStatus result = noErr;
+ // Simply return if recording is not enabled.
+ if (!rtc::AtomicOps::AcquireLoad(&recording_))
+ return result;
+
+ size_t frames_per_buffer = record_parameters_.frames_per_buffer();
+ if (num_frames != frames_per_buffer) {
+ // We have seen short bursts (1-2 frames) where |in_number_frames| changes.
+ // Add a log to keep track of longer sequences if that should ever happen.
+ // Also return since calling AudioUnitRender in this state will only result
+ // in kAudio_ParamError (-50) anyhow.
+ RTCLogWarning(@"Expected %u frames but got %u",
+ static_cast<unsigned int>(frames_per_buffer),
+ static_cast<unsigned int>(num_frames));
+ return result;
+ }
+
+ // Obtain the recorded audio samples by initiating a rendering cycle.
+ // Since it happens on the input bus, the |io_data| parameter is a reference
+ // to the preallocated audio buffer list that the audio unit renders into.
+ // We can make the audio unit provide a buffer instead in io_data, but we
+ // currently just use our own.
+ // TODO(henrika): should error handling be improved?
+ AudioBufferList* io_data = &audio_record_buffer_list_;
+ result =
+ audio_unit_->Render(flags, time_stamp, bus_number, num_frames, io_data);
+ if (result != noErr) {
+ RTCLogError(@"Failed to render audio.");
+ return result;
+ }
+
+ // Get a pointer to the recorded audio and send it to the WebRTC ADB.
+ // Use the FineAudioBuffer instance to convert between native buffer size
+ // and the 10ms buffer size used by WebRTC.
+ AudioBuffer* audio_buffer = &io_data->mBuffers[0];
+ const size_t size_in_bytes = audio_buffer->mDataByteSize;
+ RTC_CHECK_EQ(size_in_bytes / VoiceProcessingAudioUnit::kBytesPerSample,
+ num_frames);
+ int8_t* data = static_cast<int8_t*>(audio_buffer->mData);
+ fine_audio_buffer_->DeliverRecordedData(data, size_in_bytes,
+ kFixedPlayoutDelayEstimate,
+ kFixedRecordDelayEstimate);
+ return noErr;
+}
+
+OSStatus AudioDeviceIOS::OnGetPlayoutData(AudioUnitRenderActionFlags* flags,
+ const AudioTimeStamp* time_stamp,
+ UInt32 bus_number,
+ UInt32 num_frames,
+ AudioBufferList* io_data) {
+ // Verify 16-bit, noninterleaved mono PCM signal format.
+ RTC_DCHECK_EQ(1u, io_data->mNumberBuffers);
+ AudioBuffer* audio_buffer = &io_data->mBuffers[0];
+ RTC_DCHECK_EQ(1u, audio_buffer->mNumberChannels);
+ // Get pointer to internal audio buffer to which new audio data shall be
+ // written.
+ const size_t size_in_bytes = audio_buffer->mDataByteSize;
+ RTC_CHECK_EQ(size_in_bytes / VoiceProcessingAudioUnit::kBytesPerSample,
+ num_frames);
+ int8_t* destination = reinterpret_cast<int8_t*>(audio_buffer->mData);
+ // Produce silence and give audio unit a hint about it if playout is not
+ // activated.
+ if (!rtc::AtomicOps::AcquireLoad(&playing_)) {
+ *flags |= kAudioUnitRenderAction_OutputIsSilence;
+ memset(destination, 0, size_in_bytes);
+ return noErr;
+ }
+ // Read decoded 16-bit PCM samples from WebRTC (using a size that matches
+ // the native I/O audio unit) to a preallocated intermediate buffer and
+ // copy the result to the audio buffer in the |io_data| destination.
+ int8_t* source = playout_audio_buffer_.get();
+ fine_audio_buffer_->GetPlayoutData(source);
+ memcpy(destination, source, size_in_bytes);
+ return noErr;
+}
+
void AudioDeviceIOS::HandleInterruptionBegin() {
RTC_DCHECK(thread_checker_.CalledOnValidThread());
RTCLog(@"Stopping the audio unit due to interruption begin.");
- LOG_IF_ERROR(AudioOutputUnitStop(vpio_unit_),
- "Failed to stop the the Voice-Processing I/O unit");
+ if (!audio_unit_->Stop()) {
+ RTCLogError(@"Failed to stop the audio unit.");
+ }
is_interrupted_ = true;
}
void AudioDeviceIOS::HandleInterruptionEnd() {
RTC_DCHECK(thread_checker_.CalledOnValidThread());
RTCLog(@"Starting the audio unit due to interruption end.");
- LOG_IF_ERROR(AudioOutputUnitStart(vpio_unit_),
- "Failed to start the the Voice-Processing I/O unit");
+ if (!audio_unit_->Start()) {
+ RTCLogError(@"Failed to start the audio unit.");
+ }
is_interrupted_ = false;
}
@@ -408,7 +463,7 @@ void AudioDeviceIOS::HandleValidRouteChange() {
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 (!RestartAudioUnitWithNewFormat(session_sample_rate)) {
+ if (!RestartAudioUnit(session_sample_rate)) {
RTCLogError(@"Audio restart failed.");
}
}
@@ -433,12 +488,7 @@ void AudioDeviceIOS::SetupAudioBuffersForActiveAudioSession() {
RTCAudioSession* session = [RTCAudioSession sharedInstance];
double sample_rate = session.sampleRate;
NSTimeInterval io_buffer_duration = session.IOBufferDuration;
- LOG(LS_INFO) << " sample rate: " << sample_rate;
- LOG(LS_INFO) << " IO buffer duration: " << io_buffer_duration;
- LOG(LS_INFO) << " output channels: " << session.outputNumberOfChannels;
- LOG(LS_INFO) << " input channels: " << session.inputNumberOfChannels;
- LOG(LS_INFO) << " output latency: " << session.outputLatency;
- LOG(LS_INFO) << " input latency: " << session.inputLatency;
+ RTCLog(@"%@", session);
// Log a warning message for the case when we are unable to set the preferred
// hardware sample rate but continue and use the non-ideal sample rate after
@@ -501,211 +551,52 @@ void AudioDeviceIOS::SetupAudioBuffersForActiveAudioSession() {
audio_buffer->mData = record_audio_buffer_.get();
}
-bool AudioDeviceIOS::SetupAndInitializeVoiceProcessingAudioUnit() {
- LOGI() << "SetupAndInitializeVoiceProcessingAudioUnit";
- RTC_DCHECK(!vpio_unit_) << "VoiceProcessingIO audio unit already exists";
- // Create an audio component description to identify the Voice-Processing
- // I/O audio unit.
- AudioComponentDescription vpio_unit_description;
- vpio_unit_description.componentType = kAudioUnitType_Output;
- vpio_unit_description.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
- vpio_unit_description.componentManufacturer = kAudioUnitManufacturer_Apple;
- vpio_unit_description.componentFlags = 0;
- vpio_unit_description.componentFlagsMask = 0;
+bool AudioDeviceIOS::CreateAudioUnit() {
+ RTC_DCHECK(!audio_unit_);
- // Obtain an audio unit instance given the description.
- AudioComponent found_vpio_unit_ref =
- AudioComponentFindNext(nullptr, &vpio_unit_description);
-
- // Create a Voice-Processing IO audio unit.
- OSStatus result = noErr;
- result = AudioComponentInstanceNew(found_vpio_unit_ref, &vpio_unit_);
- if (result != noErr) {
- vpio_unit_ = nullptr;
- LOG(LS_ERROR) << "AudioComponentInstanceNew failed: " << result;
+ audio_unit_.reset(new VoiceProcessingAudioUnit(this));
+ if (!audio_unit_->Init()) {
+ audio_unit_.reset();
return false;
}
- // A VP I/O unit's bus 1 connects to input hardware (microphone). Enable
- // input on the input scope of the input element.
- AudioUnitElement input_bus = 1;
- UInt32 enable_input = 1;
- result = AudioUnitSetProperty(vpio_unit_, kAudioOutputUnitProperty_EnableIO,
- kAudioUnitScope_Input, input_bus, &enable_input,
- sizeof(enable_input));
- if (result != noErr) {
- DisposeAudioUnit();
- LOG(LS_ERROR) << "Failed to enable input on input scope of input element: "
- << result;
- return false;
- }
-
- // A VP I/O unit's bus 0 connects to output hardware (speaker). Enable
- // output on the output scope of the output element.
- AudioUnitElement output_bus = 0;
- UInt32 enable_output = 1;
- result = AudioUnitSetProperty(vpio_unit_, kAudioOutputUnitProperty_EnableIO,
- kAudioUnitScope_Output, output_bus,
- &enable_output, sizeof(enable_output));
- if (result != noErr) {
- DisposeAudioUnit();
- LOG(LS_ERROR)
- << "Failed to enable output on output scope of output element: "
- << result;
- return false;
- }
-
- // Set the application formats for input and output:
- // - use same format in both directions
- // - avoid resampling in the I/O unit by using the hardware sample rate
- // - linear PCM => noncompressed audio data format with one frame per packet
- // - no need to specify interleaving since only mono is supported
- AudioStreamBasicDescription application_format = {0};
- UInt32 size = sizeof(application_format);
- RTC_DCHECK_EQ(playout_parameters_.sample_rate(),
- record_parameters_.sample_rate());
- RTC_DCHECK_EQ(1, kRTCAudioSessionPreferredNumberOfChannels);
- application_format.mSampleRate = playout_parameters_.sample_rate();
- application_format.mFormatID = kAudioFormatLinearPCM;
- application_format.mFormatFlags =
- kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
- application_format.mBytesPerPacket = kBytesPerSample;
- application_format.mFramesPerPacket = 1; // uncompressed
- application_format.mBytesPerFrame = kBytesPerSample;
- application_format.mChannelsPerFrame =
- kRTCAudioSessionPreferredNumberOfChannels;
- application_format.mBitsPerChannel = 8 * kBytesPerSample;
- // Store the new format.
- application_format_ = application_format;
-#if !defined(NDEBUG)
- LogABSD(application_format_);
-#endif
+ return true;
+}
- // Set the application format on the output scope of the input element/bus.
- result = AudioUnitSetProperty(vpio_unit_, kAudioUnitProperty_StreamFormat,
- kAudioUnitScope_Output, input_bus,
- &application_format, size);
- if (result != noErr) {
- DisposeAudioUnit();
- LOG(LS_ERROR)
- << "Failed to set application format on output scope of input bus: "
- << result;
- return false;
- }
+bool AudioDeviceIOS::RestartAudioUnit(float sample_rate) {
+ RTCLog(@"Restarting audio unit with new sample rate: %f", sample_rate);
- // Set the application format on the input scope of the output element/bus.
- result = AudioUnitSetProperty(vpio_unit_, kAudioUnitProperty_StreamFormat,
- kAudioUnitScope_Input, output_bus,
- &application_format, size);
- if (result != noErr) {
- DisposeAudioUnit();
- LOG(LS_ERROR)
- << "Failed to set application format on input scope of output bus: "
- << result;
+ // Stop the active audio unit.
+ if (!audio_unit_->Stop()) {
+ RTCLogError(@"Failed to stop the audio unit.");
return false;
}
- // Specify the callback function that provides audio samples to the audio
- // unit.
- AURenderCallbackStruct render_callback;
- render_callback.inputProc = GetPlayoutData;
- render_callback.inputProcRefCon = this;
- result = AudioUnitSetProperty(
- vpio_unit_, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
- output_bus, &render_callback, sizeof(render_callback));
- if (result != noErr) {
- DisposeAudioUnit();
- LOG(LS_ERROR) << "Failed to specify the render callback on the output bus: "
- << result;
+ // The stream format is about to be changed and it requires that we first
+ // uninitialize it to deallocate its resources.
+ if (!audio_unit_->Uninitialize()) {
+ RTCLogError(@"Failed to uninitialize the audio unit.");
return false;
}
- // Disable AU buffer allocation for the recorder, we allocate our own.
- // TODO(henrika): not sure that it actually saves resource to make this call.
- UInt32 flag = 0;
- result = AudioUnitSetProperty(
- vpio_unit_, kAudioUnitProperty_ShouldAllocateBuffer,
- kAudioUnitScope_Output, input_bus, &flag, sizeof(flag));
- if (result != noErr) {
- DisposeAudioUnit();
- LOG(LS_ERROR) << "Failed to disable buffer allocation on the input bus: "
- << result;
- }
+ // Allocate new buffers given the new stream format.
+ SetupAudioBuffersForActiveAudioSession();
- // Specify the callback to be called by the I/O thread to us when input audio
- // is available. The recorded samples can then be obtained by calling the
- // AudioUnitRender() method.
- AURenderCallbackStruct input_callback;
- input_callback.inputProc = RecordedDataIsAvailable;
- input_callback.inputProcRefCon = this;
- result = AudioUnitSetProperty(vpio_unit_,
- kAudioOutputUnitProperty_SetInputCallback,
- kAudioUnitScope_Global, input_bus,
- &input_callback, sizeof(input_callback));
- if (result != noErr) {
- DisposeAudioUnit();
- LOG(LS_ERROR) << "Failed to specify the input callback on the input bus: "
- << result;
+ // Initialize the audio unit again with the new sample rate.
+ RTC_DCHECK_EQ(playout_parameters_.sample_rate(), sample_rate);
+ if (!audio_unit_->Initialize(sample_rate)) {
+ RTCLogError(@"Failed to initialize the audio unit with sample rate: %f",
+ sample_rate);
+ return false;
}
- // 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_);
- while (result != noErr) {
- LOG(LS_ERROR) << "Failed to initialize the Voice-Processing I/O unit: "
- << 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;
- }
- LOG(LS_INFO) << "pause 100ms and try audio unit initialization again...";
- [NSThread sleepForTimeInterval:0.1f];
- result = AudioUnitInitialize(vpio_unit_);
+ // Restart the audio unit.
+ if (!audio_unit_->Start()) {
+ RTCLogError(@"Failed to start audio unit.");
+ return false;
}
- LOG(LS_INFO) << "Voice-Processing I/O unit is now initialized";
- return true;
-}
-
-bool AudioDeviceIOS::RestartAudioUnitWithNewFormat(float sample_rate) {
- LOGI() << "RestartAudioUnitWithNewFormat(sample_rate=" << sample_rate << ")";
- // Stop the active audio unit.
- LOG_AND_RETURN_IF_ERROR(AudioOutputUnitStop(vpio_unit_),
- "Failed to stop the the Voice-Processing I/O unit");
+ RTCLog(@"Successfully restarted audio unit.");
- // The stream format is about to be changed and it requires that we first
- // uninitialize it to deallocate its resources.
- LOG_AND_RETURN_IF_ERROR(
- AudioUnitUninitialize(vpio_unit_),
- "Failed to uninitialize the the Voice-Processing I/O unit");
-
- // Allocate new buffers given the new stream format.
- SetupAudioBuffersForActiveAudioSession();
-
- // Update the existing application format using the new sample rate.
- application_format_.mSampleRate = playout_parameters_.sample_rate();
- UInt32 size = sizeof(application_format_);
- AudioUnitSetProperty(vpio_unit_, kAudioUnitProperty_StreamFormat,
- kAudioUnitScope_Output, 1, &application_format_, size);
- AudioUnitSetProperty(vpio_unit_, kAudioUnitProperty_StreamFormat,
- kAudioUnitScope_Input, 0, &application_format_, size);
-
- // Prepare the audio unit to render audio again.
- LOG_AND_RETURN_IF_ERROR(AudioUnitInitialize(vpio_unit_),
- "Failed to initialize the Voice-Processing I/O unit");
- LOG(LS_INFO) << "Voice-Processing I/O unit is now reinitialized";
-
- // Start rendering audio using the new format.
- LOG_AND_RETURN_IF_ERROR(AudioOutputUnitStart(vpio_unit_),
- "Failed to start the Voice-Processing I/O unit");
- LOG(LS_INFO) << "Voice-Processing I/O unit is now restarted";
return true;
}
@@ -731,29 +622,26 @@ bool AudioDeviceIOS::InitPlayOrRecord() {
SetupAudioBuffersForActiveAudioSession();
// Create, setup and initialize a new Voice-Processing I/O unit.
- if (!SetupAndInitializeVoiceProcessingAudioUnit()) {
+ // TODO(tkchin): Delay the initialization when needed.
+ if (!CreateAudioUnit() ||
+ !audio_unit_->Initialize(playout_parameters_.sample_rate())) {
[session setActive:NO error:nil];
[session unlockForConfiguration];
return false;
}
[session unlockForConfiguration];
+
return true;
}
void AudioDeviceIOS::ShutdownPlayOrRecord() {
LOGI() << "ShutdownPlayOrRecord";
+
// Close and delete the voice-processing I/O unit.
- OSStatus result = -1;
- if (nullptr != vpio_unit_) {
- result = AudioOutputUnitStop(vpio_unit_);
- if (result != noErr) {
- LOG_F(LS_ERROR) << "AudioOutputUnitStop failed: " << result;
- }
- result = AudioUnitUninitialize(vpio_unit_);
- if (result != noErr) {
- LOG_F(LS_ERROR) << "AudioUnitUninitialize failed: " << result;
- }
- DisposeAudioUnit();
+ if (audio_unit_) {
+ audio_unit_->Stop();
+ audio_unit_->Uninitialize();
+ audio_unit_.reset();
}
// Remove audio session notification observers.
@@ -767,112 +655,4 @@ void AudioDeviceIOS::ShutdownPlayOrRecord() {
[session unlockForConfiguration];
}
-void AudioDeviceIOS::DisposeAudioUnit() {
- if (nullptr == vpio_unit_)
- return;
- OSStatus result = AudioComponentInstanceDispose(vpio_unit_);
- if (result != noErr) {
- LOG(LS_ERROR) << "AudioComponentInstanceDispose failed:" << result;
- }
- vpio_unit_ = nullptr;
-}
-
-OSStatus AudioDeviceIOS::RecordedDataIsAvailable(
- void* in_ref_con,
- AudioUnitRenderActionFlags* io_action_flags,
- const AudioTimeStamp* in_time_stamp,
- UInt32 in_bus_number,
- UInt32 in_number_frames,
- AudioBufferList* io_data) {
- RTC_DCHECK_EQ(1u, in_bus_number);
- RTC_DCHECK(
- !io_data); // no buffer should be allocated for input at this stage
- AudioDeviceIOS* audio_device_ios = static_cast<AudioDeviceIOS*>(in_ref_con);
- return audio_device_ios->OnRecordedDataIsAvailable(
- io_action_flags, in_time_stamp, in_bus_number, in_number_frames);
-}
-
-OSStatus AudioDeviceIOS::OnRecordedDataIsAvailable(
- AudioUnitRenderActionFlags* io_action_flags,
- const AudioTimeStamp* in_time_stamp,
- UInt32 in_bus_number,
- UInt32 in_number_frames) {
- OSStatus result = noErr;
- // Simply return if recording is not enabled.
- if (!rtc::AtomicOps::AcquireLoad(&recording_))
- return result;
- if (in_number_frames != record_parameters_.frames_per_buffer()) {
- // We have seen short bursts (1-2 frames) where |in_number_frames| changes.
- // Add a log to keep track of longer sequences if that should ever happen.
- // Also return since calling AudioUnitRender in this state will only result
- // in kAudio_ParamError (-50) anyhow.
- LOG(LS_WARNING) << "in_number_frames (" << in_number_frames
- << ") != " << record_parameters_.frames_per_buffer();
- return noErr;
- }
- // Obtain the recorded audio samples by initiating a rendering cycle.
- // Since it happens on the input bus, the |io_data| parameter is a reference
- // to the preallocated audio buffer list that the audio unit renders into.
- // TODO(henrika): should error handling be improved?
- AudioBufferList* io_data = &audio_record_buffer_list_;
- result = AudioUnitRender(vpio_unit_, io_action_flags, in_time_stamp,
- in_bus_number, in_number_frames, io_data);
- if (result != noErr) {
- LOG_F(LS_ERROR) << "AudioUnitRender failed: " << result;
- return result;
- }
- // Get a pointer to the recorded audio and send it to the WebRTC ADB.
- // Use the FineAudioBuffer instance to convert between native buffer size
- // and the 10ms buffer size used by WebRTC.
- const UInt32 data_size_in_bytes = io_data->mBuffers[0].mDataByteSize;
- RTC_CHECK_EQ(data_size_in_bytes / kBytesPerSample, in_number_frames);
- SInt8* data = static_cast<SInt8*>(io_data->mBuffers[0].mData);
- fine_audio_buffer_->DeliverRecordedData(data, data_size_in_bytes,
- kFixedPlayoutDelayEstimate,
- kFixedRecordDelayEstimate);
- return noErr;
-}
-
-OSStatus AudioDeviceIOS::GetPlayoutData(
- void* in_ref_con,
- AudioUnitRenderActionFlags* io_action_flags,
- const AudioTimeStamp* in_time_stamp,
- UInt32 in_bus_number,
- UInt32 in_number_frames,
- AudioBufferList* io_data) {
- RTC_DCHECK_EQ(0u, in_bus_number);
- RTC_DCHECK(io_data);
- AudioDeviceIOS* audio_device_ios = static_cast<AudioDeviceIOS*>(in_ref_con);
- return audio_device_ios->OnGetPlayoutData(io_action_flags, in_number_frames,
- io_data);
-}
-
-OSStatus AudioDeviceIOS::OnGetPlayoutData(
- AudioUnitRenderActionFlags* io_action_flags,
- UInt32 in_number_frames,
- AudioBufferList* io_data) {
- // Verify 16-bit, noninterleaved mono PCM signal format.
- RTC_DCHECK_EQ(1u, io_data->mNumberBuffers);
- RTC_DCHECK_EQ(1u, io_data->mBuffers[0].mNumberChannels);
- // Get pointer to internal audio buffer to which new audio data shall be
- // written.
- const UInt32 dataSizeInBytes = io_data->mBuffers[0].mDataByteSize;
- RTC_CHECK_EQ(dataSizeInBytes / kBytesPerSample, in_number_frames);
- SInt8* destination = static_cast<SInt8*>(io_data->mBuffers[0].mData);
- // Produce silence and give audio unit a hint about it if playout is not
- // activated.
- if (!rtc::AtomicOps::AcquireLoad(&playing_)) {
- *io_action_flags |= kAudioUnitRenderAction_OutputIsSilence;
- memset(destination, 0, dataSizeInBytes);
- return noErr;
- }
- // Read decoded 16-bit PCM samples from WebRTC (using a size that matches
- // the native I/O audio unit) to a preallocated intermediate buffer and
- // copy the result to the audio buffer in the |io_data| destination.
- SInt8* source = playout_audio_buffer_.get();
- fine_audio_buffer_->GetPlayoutData(source);
- memcpy(destination, source, dataSizeInBytes);
- return noErr;
-}
-
} // namespace webrtc
« no previous file with comments | « webrtc/modules/audio_device/ios/audio_device_ios.h ('k') | webrtc/modules/audio_device/ios/objc/RTCAudioSession.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698