Index: media/audio/mac/audio_auhal_mac.cc |
diff --git a/media/audio/mac/audio_auhal_mac.cc b/media/audio/mac/audio_auhal_mac.cc |
index 6badbe9d285a20bbf54f9eb1e0f5c6802b241a65..9fcd46a6a95a71fdd63a3680bb76d91f6277c487 100644 |
--- a/media/audio/mac/audio_auhal_mac.cc |
+++ b/media/audio/mac/audio_auhal_mac.cc |
@@ -7,12 +7,11 @@ |
#include <CoreServices/CoreServices.h> |
#include "base/basictypes.h" |
-#include "base/command_line.h" |
#include "base/debug/trace_event.h" |
#include "base/logging.h" |
#include "base/mac/mac_logging.h" |
#include "media/audio/mac/audio_manager_mac.h" |
-#include "media/base/media_switches.h" |
+#include "media/base/audio_pull_fifo.h" |
namespace media { |
@@ -58,8 +57,8 @@ AUHALStream::AUHALStream( |
volume_(1), |
hardware_latency_frames_(0), |
stopped_(false), |
- notified_for_possible_device_change_(false), |
- input_buffer_list_(NULL) { |
+ input_buffer_list_(NULL), |
+ current_hardware_pending_bytes_(0) { |
// We must have a manager. |
DCHECK(manager_); |
@@ -150,7 +149,7 @@ void AUHALStream::Start(AudioSourceCallback* callback) { |
} |
stopped_ = false; |
- notified_for_possible_device_change_ = false; |
+ audio_fifo_.reset(); |
{ |
base::AutoLock auto_lock(source_lock_); |
source_ = callback; |
@@ -200,63 +199,69 @@ OSStatus AUHALStream::Render( |
AudioBufferList* io_data) { |
TRACE_EVENT0("audio", "AUHALStream::Render"); |
+ // If the stream parameters change for any reason, we need to insert a FIFO |
+ // since the OnMoreData() pipeline can't handle frame size changes. Generally |
+ // this is a temporary situation which can occur after a device change has |
+ // occurred but the AudioManager hasn't received the notification yet. |
if (number_of_frames != number_of_frames_) { |
- // This can happen if we've suddenly changed sample-rates. |
- // The stream should be stopping very soon. |
- // |
- // Unfortunately AUAudioInputStream and AUHALStream share the frame |
- // size set by kAudioDevicePropertyBufferFrameSize above on a per process |
- // basis. What this means is that the |number_of_frames| value may be |
- // larger or smaller than the value set during ConfigureAUHAL(). |
- // In this case either audio input or audio output will be broken, |
- // so just output silence. |
- ZeroBufferList(io_data); |
- return noErr; |
- } |
+ // Create a FIFO on the fly to handle any discrepancies in callback rates. |
+ if (!audio_fifo_) { |
+ VLOG(1) << "Audio frame size change detected; adding FIFO to compensate."; |
+ audio_fifo_.reset(new AudioPullFifo( |
+ output_channels_, |
+ number_of_frames_, |
+ base::Bind(&AUHALStream::ProvideInput, base::Unretained(this)))); |
+ } |
- if (input_channels_ > 0 && input_buffer_list_) { |
- // Get the input data. |input_buffer_list_| is wrapped |
- // to point to the data allocated in |input_bus_|. |
- OSStatus result = AudioUnitRender( |
- audio_unit_, |
- flags, |
- output_time_stamp, |
- 1, |
- number_of_frames, |
- input_buffer_list_); |
- if (result != noErr) |
- ZeroBufferList(input_buffer_list_); |
+ // Synchronous IO is not supported in this state. |
+ if (input_channels_ > 0) |
+ input_bus_->Zero(); |
+ } else { |
+ if (input_channels_ > 0 && input_buffer_list_) { |
+ // Get the input data. |input_buffer_list_| is wrapped |
+ // to point to the data allocated in |input_bus_|. |
+ OSStatus result = AudioUnitRender(audio_unit_, |
+ flags, |
+ output_time_stamp, |
+ 1, |
+ number_of_frames, |
+ input_buffer_list_); |
+ if (result != noErr) |
+ ZeroBufferList(input_buffer_list_); |
+ } |
} |
// Make |output_bus_| wrap the output AudioBufferList. |
WrapBufferList(io_data, output_bus_.get(), number_of_frames); |
// Update the playout latency. |
- double playout_latency_frames = GetPlayoutLatency(output_time_stamp); |
+ const double playout_latency_frames = GetPlayoutLatency(output_time_stamp); |
+ current_hardware_pending_bytes_ = static_cast<uint32>( |
+ (playout_latency_frames + 0.5) * params_.GetBytesPerFrame()); |
- uint32 hardware_pending_bytes = static_cast<uint32> |
- ((playout_latency_frames + 0.5) * output_format_.mBytesPerFrame); |
+ if (audio_fifo_) |
+ audio_fifo_->Consume(output_bus_.get(), output_bus_->frames()); |
+ else |
+ ProvideInput(0, output_bus_.get()); |
- { |
- // Render() shouldn't be called except between AudioOutputUnitStart() and |
- // AudioOutputUnitStop() calls, but crash reports have shown otherwise: |
- // http://crbug.com/178765. We use |source_lock_| to prevent races and |
- // crashes in Render() when |source_| is cleared. |
- base::AutoLock auto_lock(source_lock_); |
- if (!source_) { |
- ZeroBufferList(io_data); |
- return noErr; |
- } |
+ return noErr; |
+} |
- // Supply the input data and render the output data. |
- source_->OnMoreIOData( |
- input_bus_.get(), |
- output_bus_.get(), |
- AudioBuffersState(0, hardware_pending_bytes)); |
- output_bus_->Scale(volume_); |
+void AUHALStream::ProvideInput(int frame_delay, AudioBus* dest) { |
+ base::AutoLock auto_lock(source_lock_); |
+ if (!source_) { |
+ dest->Zero(); |
+ return; |
} |
- return noErr; |
+ // Supply the input data and render the output data. |
+ source_->OnMoreIOData( |
+ input_bus_.get(), |
+ dest, |
+ AudioBuffersState(0, |
+ current_hardware_pending_bytes_ + |
+ frame_delay * params_.GetBytesPerFrame())); |
+ dest->Scale(volume_); |
} |
// AUHAL callback. |