diff options
author | dalecurtis@chromium.org <dalecurtis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-05 13:39:21 +0000 |
---|---|---|
committer | dalecurtis@chromium.org <dalecurtis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-05 13:39:21 +0000 |
commit | dfc48e4ce465db7e44e7cb7a4d2628958e1787a0 (patch) | |
tree | d387f8da52225eb08fe29d6bc17808d04739b26c | |
parent | 42e66e5f9dc55b57bfc95f8a49836fba59ad0cba (diff) | |
download | chromium_src-dfc48e4ce465db7e44e7cb7a4d2628958e1787a0.zip chromium_src-dfc48e4ce465db7e44e7cb7a4d2628958e1787a0.tar.gz chromium_src-dfc48e4ce465db7e44e7cb7a4d2628958e1787a0.tar.bz2 |
Switch OnMoreData() to use AudioBus.
As titled, with this change we're now piping float data around the pipeline from
end to end. This change is in preparation for browser side channel remixing and
resampling.
As a consequence of this change the shared memory now represents the
contents of an AudioBus object, which is essentially audio data in a float
planar format.
BUG=114700
TEST=Should be no audible change. Ran all existing tests. Compiled ran
WebAudio/HTML5/WebRTC on all platforms and PPAPI on Linux.
Review URL: https://chromiumcodereview.appspot.com/10832285
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@154951 0039d316-1c4b-4281-b951-d872f2087c98
51 files changed, 597 insertions, 602 deletions
diff --git a/content/browser/renderer_host/media/audio_renderer_host.cc b/content/browser/renderer_host/media/audio_renderer_host.cc index 4c1540e..8cd776b 100644 --- a/content/browser/renderer_host/media/audio_renderer_host.cc +++ b/content/browser/renderer_host/media/audio_renderer_host.cc @@ -13,6 +13,8 @@ #include "content/common/media/audio_messages.h" #include "content/public/browser/media_observer.h" #include "media/audio/shared_memory_util.h" +#include "media/base/audio_bus.h" +#include "media/base/limits.h" using content::BrowserMessageFilter; using content::BrowserThread; @@ -197,9 +199,10 @@ void AudioRendererHost::OnCreateStream( DCHECK(LookupById(stream_id) == NULL); media::AudioParameters audio_params(params); - DCHECK_GT(audio_params.frames_per_buffer(), 0); - - uint32 buffer_size = audio_params.GetBytesPerBuffer(); + uint32 buffer_size = media::AudioBus::CalculateMemorySize(audio_params); + DCHECK_GT(buffer_size, 0U); + DCHECK_LE(buffer_size, + static_cast<uint32>(media::limits::kMaxPacketSizeInBytes)); scoped_ptr<AudioEntry> entry(new AudioEntry()); @@ -214,7 +217,7 @@ void AudioRendererHost::OnCreateStream( // Create sync reader and try to initialize it. scoped_ptr<AudioSyncReader> reader( - new AudioSyncReader(&entry->shared_memory)); + new AudioSyncReader(&entry->shared_memory, params)); if (!reader->Init()) { SendErrorMessage(stream_id); diff --git a/content/browser/renderer_host/media/audio_renderer_host_unittest.cc b/content/browser/renderer_host/media/audio_renderer_host_unittest.cc index 6f2aa5c..bab2e8b 100644 --- a/content/browser/renderer_host/media/audio_renderer_host_unittest.cc +++ b/content/browser/renderer_host/media/audio_renderer_host_unittest.cc @@ -161,6 +161,9 @@ class AudioRendererHostTest : public testing::Test { observer_.reset(new MockMediaObserver()); host_ = new MockAudioRendererHost(audio_manager_.get(), observer_.get()); + // Expect the audio stream will be deleted. + EXPECT_CALL(*observer_, OnDeleteAudioStream(_, kStreamId)); + // Simulate IPC channel connected. host_->OnChannelConnected(base::GetCurrentProcId()); } @@ -259,9 +262,6 @@ class AudioRendererHostTest : public testing::Test { // Expect an error signal sent through IPC. EXPECT_CALL(*host_, OnStreamError(kStreamId)); - // Expect the audio stream will be deleted. - EXPECT_CALL(*observer_, OnDeleteAudioStream(_, kStreamId)); - // Simulate an error sent from the audio device. host_->OnError(controller, 0); SyncWithAudioThread(); diff --git a/content/browser/renderer_host/media/audio_sync_reader.cc b/content/browser/renderer_host/media/audio_sync_reader.cc index cd6db16..bb6d1e7 100644 --- a/content/browser/renderer_host/media/audio_sync_reader.cc +++ b/content/browser/renderer_host/media/audio_sync_reader.cc @@ -10,23 +10,26 @@ #include "base/shared_memory.h" #include "base/threading/platform_thread.h" #include "media/audio/audio_buffers_state.h" +#include "media/audio/audio_parameters.h" #include "media/audio/shared_memory_util.h" #if defined(OS_WIN) const int kMinIntervalBetweenReadCallsInMs = 10; #endif -AudioSyncReader::AudioSyncReader(base::SharedMemory* shared_memory) +AudioSyncReader::AudioSyncReader(base::SharedMemory* shared_memory, + const media::AudioParameters& params) : shared_memory_(shared_memory) { + packet_size_ = media::PacketSizeInBytes(shared_memory_->created_size()); + DCHECK_EQ(packet_size_, media::AudioBus::CalculateMemorySize(params)); + audio_bus_ = media::AudioBus::WrapMemory(params, shared_memory->memory()); } AudioSyncReader::~AudioSyncReader() { } bool AudioSyncReader::DataReady() { - return !media::IsUnknownDataSize( - shared_memory_, - media::PacketSizeInBytes(shared_memory_->created_size())); + return !media::IsUnknownDataSize(shared_memory_, packet_size_); } // media::AudioOutputController::SyncReader implementations. @@ -34,9 +37,7 @@ void AudioSyncReader::UpdatePendingBytes(uint32 bytes) { if (bytes != static_cast<uint32>(media::kPauseMark)) { // Store unknown length of data into buffer, so we later // can find out if data became available. - media::SetUnknownDataSize( - shared_memory_, - media::PacketSizeInBytes(shared_memory_->created_size())); + media::SetUnknownDataSize(shared_memory_, packet_size_); } if (socket_.get()) { @@ -44,10 +45,7 @@ void AudioSyncReader::UpdatePendingBytes(uint32 bytes) { } } -uint32 AudioSyncReader::Read(void* data, uint32 size) { - uint32 max_size = media::PacketSizeInBytes( - shared_memory_->created_size()); - +int AudioSyncReader::Read(media::AudioBus* audio_bus) { #if defined(OS_WIN) // HACK: yield if reader is called too often. // Problem is lack of synchronization between host and renderer. We cannot be @@ -64,25 +62,40 @@ uint32 AudioSyncReader::Read(void* data, uint32 size) { previous_call_time_ = base::Time::Now(); #endif - uint32 read_size = std::min(media::GetActualDataSizeInBytes(shared_memory_, - max_size), - size); - - // Get the data from the buffer. - memcpy(data, shared_memory_->memory(), read_size); - - // If amount read was less than requested, then zero out the remainder. - if (read_size < size) - memset(static_cast<char*>(data) + read_size, 0, size - read_size); + // Retrieve the actual number of bytes available from the shared memory. If + // the renderer has not completed rendering this value will be invalid (still + // the marker stored in UpdatePendingBytes() above) and must be sanitized. + // TODO(dalecurtis): Technically this is not the exact size. Due to channel + // padding for alignment, there may be more data available than this; AudioBus + // will automatically do the right thing during CopyTo(). Rename this method + // to GetActualFrameCount(). + uint32 size = media::GetActualDataSizeInBytes(shared_memory_, packet_size_); + + // Compute the actual number of frames read. It's important to sanitize this + // value for a couple reasons. One, it might still be the unknown data size + // marker. Two, shared memory comes from a potentially untrusted source. + int frames = + size / (sizeof(*audio_bus_->channel(0)) * audio_bus_->channels()); + if (frames < 0) + frames = 0; + else if (frames > audio_bus_->frames()) + frames = audio_bus_->frames(); + + // Copy data from the shared memory into the caller's AudioBus. + audio_bus_->CopyTo(audio_bus); + + // Zero out any unfilled frames in the destination bus. + audio_bus->ZeroFramesPartial(frames, audio_bus->frames() - frames); // Zero out the entire buffer. - memset(shared_memory_->memory(), 0, max_size); + memset(shared_memory_->memory(), 0, packet_size_); // Store unknown length of data into buffer, in case renderer does not store // the length itself. It also helps in decision if we need to yield. - media::SetUnknownDataSize(shared_memory_, max_size); + media::SetUnknownDataSize(shared_memory_, packet_size_); - return read_size; + // Return the actual number of frames read. + return frames; } void AudioSyncReader::Close() { diff --git a/content/browser/renderer_host/media/audio_sync_reader.h b/content/browser/renderer_host/media/audio_sync_reader.h index eec238c..746e375 100644 --- a/content/browser/renderer_host/media/audio_sync_reader.h +++ b/content/browser/renderer_host/media/audio_sync_reader.h @@ -11,6 +11,7 @@ #include "base/synchronization/lock.h" #include "base/time.h" #include "media/audio/audio_output_controller.h" +#include "media/base/audio_bus.h" namespace base { class SharedMemory; @@ -22,13 +23,14 @@ class SharedMemory; // process. class AudioSyncReader : public media::AudioOutputController::SyncReader { public: - explicit AudioSyncReader(base::SharedMemory* shared_memory); + AudioSyncReader(base::SharedMemory* shared_memory, + const media::AudioParameters& params); virtual ~AudioSyncReader(); // media::AudioOutputController::SyncReader implementations. virtual void UpdatePendingBytes(uint32 bytes) OVERRIDE; - virtual uint32 Read(void* data, uint32 size) OVERRIDE; + virtual int Read(media::AudioBus* audio_bus) OVERRIDE; virtual void Close() OVERRIDE; virtual bool DataReady() OVERRIDE; @@ -51,6 +53,12 @@ class AudioSyncReader : public media::AudioOutputController::SyncReader { // PrepareForeignSocketHandle() is called and ran successfully. scoped_ptr<base::CancelableSyncSocket> foreign_socket_; + // Shared memory wrapper used for transferring audio data to Read() callers. + scoped_ptr<media::AudioBus> audio_bus_; + + // Maximum amount of audio data which can be transferred in one Read() call. + int packet_size_; + DISALLOW_COPY_AND_ASSIGN(AudioSyncReader); }; diff --git a/media/audio/android/opensles_output.cc b/media/audio/android/opensles_output.cc index 3b5ee64..26ae25b 100644 --- a/media/audio/android/opensles_output.cc +++ b/media/audio/android/opensles_output.cc @@ -35,6 +35,7 @@ OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager, NOTREACHED() << "Unsupported number of channels: " << format_.numChannels; buffer_size_bytes_ = params.GetBytesPerBuffer(); + audio_bus_ = AudioBus::Create(params); memset(&audio_data_, 0, sizeof(audio_data_)); } @@ -257,17 +258,21 @@ void OpenSLESOutputStream::FillBufferQueue() { // Read data from the registered client source. // TODO(xians): Get an accurate delay estimation. uint32 hardware_delay = buffer_size_bytes_; - size_t num_filled_bytes = callback_->OnMoreData( - audio_data_[active_queue_], - buffer_size_bytes_, - AudioBuffersState(0, hardware_delay)); - DCHECK(num_filled_bytes <= buffer_size_bytes_); + int frames_filled = callback_->OnMoreData( + audio_bus_.get(), AudioBuffersState(0, hardware_delay)); + int num_filled_bytes = + frames_filled * audio_bus_->channels() * format_.bitsPerSample / 8; + DCHECK_LE(static_cast<size_t>(num_filled_bytes), buffer_size_bytes_); + // Note: If this ever changes to output raw float the data must be clipped and + // sanitized since it may come from an untrusted source such as NaCl. + audio_bus_->ToInterleaved( + frames_filled, format_.bitsPerSample / 8, audio_data_[active_queue_]); // Perform in-place, software-volume adjustments. media::AdjustVolume(audio_data_[active_queue_], num_filled_bytes, format_.numChannels, - format_.containerSize >> 3, + format_.bitsPerSample / 8, volume_); // Enqueue the buffer for playback. diff --git a/media/audio/android/opensles_output.h b/media/audio/android/opensles_output.h index f37283a..9ecfb6c 100644 --- a/media/audio/android/opensles_output.h +++ b/media/audio/android/opensles_output.h @@ -80,6 +80,9 @@ class OpenSLESOutputStream : public AudioOutputStream { // Volume level from 0 to 1. float volume_; + // Container for retrieving data from AudioSourceCallback::OnMoreData(). + scoped_ptr<AudioBus> audio_bus_; + DISALLOW_COPY_AND_ASSIGN(OpenSLESOutputStream); }; diff --git a/media/audio/audio_device_thread.cc b/media/audio/audio_device_thread.cc index 3e14d3d..c592acc 100644 --- a/media/audio/audio_device_thread.cc +++ b/media/audio/audio_device_thread.cc @@ -192,14 +192,8 @@ AudioDeviceThread::Callback::Callback( AudioDeviceThread::Callback::~Callback() {} void AudioDeviceThread::Callback::InitializeOnAudioThread() { - DCHECK(!audio_bus_.get()); - MapSharedMemory(); DCHECK(shared_memory_.memory() != NULL); - - // TODO(dalecurtis): Instead of creating a new AudioBus and memcpy'ing into - // the shared memory we should wrap the shared memory. - audio_bus_ = AudioBus::Create(audio_parameters_); } } // namespace media. diff --git a/media/audio/audio_device_thread.h b/media/audio/audio_device_thread.h index d229613..492c1fb 100644 --- a/media/audio/audio_device_thread.h +++ b/media/audio/audio_device_thread.h @@ -13,6 +13,7 @@ #include "base/synchronization/lock.h" #include "media/base/media_export.h" #include "media/audio/audio_parameters.h" +#include "media/audio/shared_memory_util.h" class MessageLoop; @@ -60,7 +61,6 @@ class MEDIA_EXPORT AudioDeviceThread { // Audio buffers that are allocated in InitializeOnAudioThread() based on // info from audio_parameters_. - scoped_ptr<AudioBus> audio_bus_; base::SharedMemory shared_memory_; const int memory_length_; diff --git a/media/audio/audio_input_device.cc b/media/audio/audio_input_device.cc index 41bd491..0a161db 100644 --- a/media/audio/audio_input_device.cc +++ b/media/audio/audio_input_device.cc @@ -35,6 +35,7 @@ class AudioInputDevice::AudioThreadCallback private: CaptureCallback* capture_callback_; + scoped_ptr<AudioBus> audio_bus_; DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback); }; @@ -307,6 +308,7 @@ AudioInputDevice::AudioThreadCallback::AudioThreadCallback( CaptureCallback* capture_callback) : AudioDeviceThread::Callback(audio_parameters, memory, memory_length), capture_callback_(capture_callback) { + audio_bus_ = AudioBus::Create(audio_parameters_); } AudioInputDevice::AudioThreadCallback::~AudioThreadCallback() { diff --git a/media/audio/audio_io.h b/media/audio/audio_io.h index 50be7cd..c1d7fbb 100644 --- a/media/audio/audio_io.h +++ b/media/audio/audio_io.h @@ -7,6 +7,7 @@ #include "base/basictypes.h" #include "media/audio/audio_buffers_state.h" +#include "media/base/audio_bus.h" // Low-level audio output support. To make sound there are 3 objects involved: // - AudioSource : produces audio samples on a pull model. Implements @@ -41,7 +42,7 @@ // Because we support more audio streams than physically available channels // a given AudioOutputStream might or might not talk directly to hardware. // An audio stream allocates several buffers for audio data and calls -// AudioSourceCallback::OnModeData() periodically to fill these buffers, +// AudioSourceCallback::OnMoreData() periodically to fill these buffers, // as the data is written to the audio device. Size of each packet is determined // by |samples_per_packet| specified in AudioParameters when the stream is // created. @@ -56,16 +57,11 @@ class MEDIA_EXPORT AudioOutputStream { // itself such as creating Windows or initializing COM. class MEDIA_EXPORT AudioSourceCallback { public: - // Provide more data by filling |dest| up to |max_size| bytes. The provided - // buffer size is determined by the |samples_per_packet| specified in - // AudioParameters when the stream is created. The source will return - // the number of bytes it filled. The expected structure of |dest| is - // platform and format specific. - // |buffers_state| contains current state of the buffers, and can be used - // by the source to calculate delay. - virtual uint32 OnMoreData(uint8* dest, - uint32 max_size, - AudioBuffersState buffers_state) = 0; + // Provide more data by fully filling |audio_bus|. The source will return + // the number of frames it filled. |buffers_state| contains current state + // of the buffers, and can be used by the source to calculate delay. + virtual int OnMoreData(AudioBus* audio_bus, + AudioBuffersState buffers_state) = 0; // There was an error while playing a buffer. Audio source cannot be // destroyed yet. No direct action needed by the AudioStream, but it is diff --git a/media/audio/audio_low_latency_input_output_unittest.cc b/media/audio/audio_low_latency_input_output_unittest.cc index 9cb4f5e..d56854e 100644 --- a/media/audio/audio_low_latency_input_output_unittest.cc +++ b/media/audio/audio_low_latency_input_output_unittest.cc @@ -218,9 +218,8 @@ class FullDuplexAudioSinkSource virtual void OnError(AudioInputStream* stream, int code) OVERRIDE {} // AudioOutputStream::AudioSourceCallback. - virtual uint32 OnMoreData(uint8* dest, - uint32 max_size, - AudioBuffersState buffers_state) OVERRIDE { + virtual int OnMoreData(AudioBus* audio_bus, + AudioBuffersState buffers_state) OVERRIDE { base::AutoLock lock(lock_); // Update one component in the AudioDelayState for the packet @@ -240,9 +239,21 @@ class FullDuplexAudioSinkSource ++output_elements_to_write_; } + int size; + const uint8* source; // Read the data from the seekable media buffer which contains // captured data at the same size and sample rate as the output side. - return buffer_->Read(dest, max_size); + if (buffer_->GetCurrentChunk(&source, &size) && size > 0) { + EXPECT_EQ(channels_, audio_bus->channels()); + size = std::min(audio_bus->frames() * frame_size_, size); + EXPECT_EQ(static_cast<size_t>(size) % sizeof(*audio_bus->channel(0)), 0U); + audio_bus->FromInterleaved( + source, size / frame_size_, frame_size_ / channels_); + buffer_->Seek(size); + return size / frame_size_; + } + + return 0; } virtual void OnError(AudioOutputStream* stream, int code) OVERRIDE {} @@ -261,7 +272,7 @@ class FullDuplexAudioSinkSource int sample_rate_; int samples_per_packet_; int channels_; - size_t frame_size_; + int frame_size_; double frames_to_ms_; scoped_array<AudioDelayState> delay_states_; size_t input_elements_to_write_; diff --git a/media/audio/audio_output_controller.cc b/media/audio/audio_output_controller.cc index 69b7797..efc86e4 100644 --- a/media/audio/audio_output_controller.cc +++ b/media/audio/audio_output_controller.cc @@ -24,7 +24,8 @@ const int AudioOutputController::kPollNumAttempts = 3; const int AudioOutputController::kPollPauseInMilliseconds = 3; AudioOutputController::AudioOutputController(EventHandler* handler, - SyncReader* sync_reader) + SyncReader* sync_reader, + const AudioParameters& params) : handler_(handler), stream_(NULL), volume_(1.0), @@ -32,6 +33,7 @@ AudioOutputController::AudioOutputController(EventHandler* handler, sync_reader_(sync_reader), message_loop_(NULL), number_polling_attempts_left_(0), + params_(params), ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)) { } @@ -68,12 +70,12 @@ scoped_refptr<AudioOutputController> AudioOutputController::Create( // Starts the audio controller thread. scoped_refptr<AudioOutputController> controller(new AudioOutputController( - event_handler, sync_reader)); + event_handler, sync_reader, params)); controller->message_loop_ = audio_manager->GetMessageLoop(); controller->message_loop_->PostTask(FROM_HERE, base::Bind( &AudioOutputController::DoCreate, controller, - base::Unretained(audio_manager), params)); + base::Unretained(audio_manager))); return controller; } @@ -108,8 +110,7 @@ void AudioOutputController::SetVolume(double volume) { &AudioOutputController::DoSetVolume, this, volume)); } -void AudioOutputController::DoCreate(AudioManager* audio_manager, - const AudioParameters& params) { +void AudioOutputController::DoCreate(AudioManager* audio_manager) { DCHECK(message_loop_->BelongsToCurrentThread()); // Close() can be called before DoCreate() is executed. @@ -118,7 +119,7 @@ void AudioOutputController::DoCreate(AudioManager* audio_manager, DCHECK_EQ(kEmpty, state_); DoStopCloseAndClearStream(NULL); - stream_ = audio_manager->MakeAudioOutputStreamProxy(params); + stream_ = audio_manager->MakeAudioOutputStreamProxy(params_); if (!stream_) { // TODO(hclam): Define error types. handler_->OnError(this, 0); @@ -282,9 +283,8 @@ void AudioOutputController::DoReportError(int code) { handler_->OnError(this, code); } -uint32 AudioOutputController::OnMoreData(uint8* dest, - uint32 max_size, - AudioBuffersState buffers_state) { +int AudioOutputController::OnMoreData(AudioBus* audio_bus, + AudioBuffersState buffers_state) { TRACE_EVENT0("audio", "AudioOutputController::OnMoreData"); { @@ -295,9 +295,10 @@ uint32 AudioOutputController::OnMoreData(uint8* dest, return 0; } } - uint32 size = sync_reader_->Read(dest, max_size); - sync_reader_->UpdatePendingBytes(buffers_state.total_bytes() + size); - return size; + int frames = sync_reader_->Read(audio_bus); + sync_reader_->UpdatePendingBytes( + buffers_state.total_bytes() + frames * params_.GetBytesPerFrame()); + return frames; } void AudioOutputController::WaitTillDataReady() { diff --git a/media/audio/audio_output_controller.h b/media/audio/audio_output_controller.h index 03b0959..3741c09 100644 --- a/media/audio/audio_output_controller.h +++ b/media/audio/audio_output_controller.h @@ -92,9 +92,9 @@ class MEDIA_EXPORT AudioOutputController // prepare more data and perform synchronization. virtual void UpdatePendingBytes(uint32 bytes) = 0; - // Read certain amount of data into |data|. This method returns if some - // data is available. - virtual uint32 Read(void* data, uint32 size) = 0; + // Attempt to completely fill |audio_bus|, return the actual number of + // frames that could be read. + virtual int Read(AudioBus* audio_bus) = 0; // Close this synchronous reader. virtual void Close() = 0; @@ -144,9 +144,8 @@ class MEDIA_EXPORT AudioOutputController /////////////////////////////////////////////////////////////////////////// // AudioSourceCallback methods. - virtual uint32 OnMoreData(uint8* dest, - uint32 max_size, - AudioBuffersState buffers_state) OVERRIDE; + virtual int OnMoreData(AudioBus* audio_bus, + AudioBuffersState buffers_state) OVERRIDE; virtual void OnError(AudioOutputStream* stream, int code) OVERRIDE; virtual void WaitTillDataReady() OVERRIDE; @@ -172,10 +171,11 @@ class MEDIA_EXPORT AudioOutputController static const int kPollPauseInMilliseconds; AudioOutputController(EventHandler* handler, - SyncReader* sync_reader); + SyncReader* sync_reader, + const AudioParameters& params); // The following methods are executed on the audio manager thread. - void DoCreate(AudioManager* audio_manager, const AudioParameters& params); + void DoCreate(AudioManager* audio_manager); void DoPlay(); void PollAndStartIfDataReady(); void DoPause(); @@ -217,6 +217,8 @@ class MEDIA_EXPORT AudioOutputController // Number of times left. int number_polling_attempts_left_; + AudioParameters params_; + // Used to post delayed tasks to ourselves that we can cancel. // We don't want the tasks to hold onto a reference as it will slow down // shutdown and force it to wait for the most delayed task. diff --git a/media/audio/audio_output_controller_unittest.cc b/media/audio/audio_output_controller_unittest.cc index 6fe2499..70edb14 100644 --- a/media/audio/audio_output_controller_unittest.cc +++ b/media/audio/audio_output_controller_unittest.cc @@ -52,7 +52,7 @@ class MockAudioOutputControllerSyncReader MockAudioOutputControllerSyncReader() {} MOCK_METHOD1(UpdatePendingBytes, void(uint32 bytes)); - MOCK_METHOD2(Read, uint32(void* data, uint32 size)); + MOCK_METHOD1(Read, int(AudioBus* audio_bus)); MOCK_METHOD0(Close, void()); MOCK_METHOD0(DataReady, bool()); @@ -125,7 +125,7 @@ TEST_F(AudioOutputControllerTest, PlayPauseClose) { MockAudioOutputControllerSyncReader sync_reader; EXPECT_CALL(sync_reader, UpdatePendingBytes(_)) .Times(AtLeast(2)); - EXPECT_CALL(sync_reader, Read(_, kHardwareBufferSize)) + EXPECT_CALL(sync_reader, Read(_)) .WillRepeatedly(DoAll(SignalEvent(&event), Return(4))); EXPECT_CALL(sync_reader, DataReady()) @@ -197,7 +197,7 @@ TEST_F(AudioOutputControllerTest, PlayPausePlayClose) { MockAudioOutputControllerSyncReader sync_reader; EXPECT_CALL(sync_reader, UpdatePendingBytes(_)) .Times(AtLeast(1)); - EXPECT_CALL(sync_reader, Read(_, kHardwareBufferSize)) + EXPECT_CALL(sync_reader, Read(_)) .WillRepeatedly(DoAll(SignalEvent(&event), Return(4))); EXPECT_CALL(sync_reader, DataReady()) .WillRepeatedly(Return(true)); diff --git a/media/audio/audio_output_device.cc b/media/audio/audio_output_device.cc index 34ff54e..e9119cb 100644 --- a/media/audio/audio_output_device.cc +++ b/media/audio/audio_output_device.cc @@ -33,6 +33,7 @@ class AudioOutputDevice::AudioThreadCallback private: AudioRendererSink::RenderCallback* render_callback_; + scoped_ptr<AudioBus> audio_bus_; DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback); }; @@ -49,7 +50,7 @@ AudioOutputDevice::AudioOutputDevice( } void AudioOutputDevice::Initialize(const AudioParameters& params, - RenderCallback* callback) { + RenderCallback* callback) { CHECK_EQ(0, stream_id_) << "AudioOutputDevice::Initialize() must be called before Start()"; @@ -194,7 +195,6 @@ void AudioOutputDevice::OnStreamCreated( base::SyncSocket::Handle socket_handle, int length) { DCHECK(message_loop()->BelongsToCurrentThread()); - DCHECK_GE(length, audio_parameters_.GetBytesPerBuffer()); #if defined(OS_WIN) DCHECK(handle); DCHECK(socket_handle); @@ -250,6 +250,8 @@ AudioOutputDevice::AudioThreadCallback::~AudioThreadCallback() { void AudioOutputDevice::AudioThreadCallback::MapSharedMemory() { shared_memory_.Map(TotalSharedMemorySizeInBytes(memory_length_)); + DCHECK_EQ(memory_length_, AudioBus::CalculateMemorySize(audio_parameters_)); + audio_bus_ = AudioBus::WrapMemory(audio_parameters_, shared_memory_.memory()); } // Called whenever we receive notifications about pending data. @@ -266,20 +268,20 @@ void AudioOutputDevice::AudioThreadCallback::Process(int pending_data) { TRACE_EVENT0("audio", "AudioOutputDevice::FireRenderCallback"); - // Update the audio-delay measurement then ask client to render audio. + // Update the audio-delay measurement then ask client to render audio. Since + // |audio_bus_| is wrapping the shared memory the Render() call is writing + // directly into the shared memory. size_t num_frames = render_callback_->Render( audio_bus_.get(), audio_delay_milliseconds); - // Interleave, scale, and clip to int. - // TODO(dalecurtis): Remove this when we have float everywhere: - // http://crbug.com/114700 - audio_bus_->ToInterleaved(num_frames, audio_parameters_.bits_per_sample() / 8, - shared_memory_.memory()); - // Let the host know we are done. + // TODO(dalecurtis): Technically this is not always correct. Due to channel + // padding for alignment, there may be more data available than this. We're + // relying on AudioSyncReader::Read() to parse this with that in mind. Rename + // these methods to Set/GetActualFrameCount(). SetActualDataSizeInBytes( &shared_memory_, memory_length_, - num_frames * audio_parameters_.GetBytesPerFrame()); + num_frames * sizeof(*audio_bus_->channel(0)) * audio_bus_->channels()); } } // namespace media. diff --git a/media/audio/audio_output_device_unittest.cc b/media/audio/audio_output_device_unittest.cc index 14a7a35..a2aea89 100644 --- a/media/audio/audio_output_device_unittest.cc +++ b/media/audio/audio_output_device_unittest.cc @@ -173,7 +173,7 @@ TEST_F(AudioOutputDeviceTest, CreateStream) { // than just the audio data, so we must call TotalSharedMemorySizeInBytes() // to get the actual size needed to fit the audio data plus the extra data. int memory_size = TotalSharedMemorySizeInBytes( - default_audio_parameters_.GetBytesPerBuffer()); + AudioBus::CalculateMemorySize(default_audio_parameters_)); SharedMemory shared_memory; ASSERT_TRUE(shared_memory.CreateAndMapAnonymous(memory_size)); memset(shared_memory.memory(), 0xff, memory_size); @@ -215,7 +215,7 @@ TEST_F(AudioOutputDeviceTest, CreateStream) { Return(kNumberOfFramesToProcess))); audio_device->OnStreamCreated(duplicated_memory_handle, audio_device_socket, - memory_size); + PacketSizeInBytes(memory_size)); io_loop_.PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(), TestTimeouts::action_timeout()); diff --git a/media/audio/audio_output_proxy_unittest.cc b/media/audio/audio_output_proxy_unittest.cc index 035a64e..b928b7c 100644 --- a/media/audio/audio_output_proxy_unittest.cc +++ b/media/audio/audio_output_proxy_unittest.cc @@ -27,6 +27,7 @@ using ::testing::Mock; using ::testing::NotNull; using ::testing::Return; using ::testing::SetArrayArgument; +using media::AudioBus; using media::AudioBuffersState; using media::AudioInputStream; using media::AudioManager; @@ -78,8 +79,8 @@ class MockAudioManager : public AudioManager { class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback { public: - MOCK_METHOD3(OnMoreData, uint32(uint8* dest, uint32 max_size, - AudioBuffersState buffers_state)); + MOCK_METHOD2(OnMoreData, int(AudioBus* audio_bus, + AudioBuffersState buffers_state)); MOCK_METHOD2(OnError, void(AudioOutputStream* stream, int code)); }; diff --git a/media/audio/fake_audio_output_stream.cc b/media/audio/fake_audio_output_stream.cc index aaa6158..f09fe5d 100644 --- a/media/audio/fake_audio_output_stream.cc +++ b/media/audio/fake_audio_output_stream.cc @@ -24,9 +24,6 @@ AudioOutputStream* FakeAudioOutputStream::MakeFakeStream( } bool FakeAudioOutputStream::Open() { - if (bytes_per_buffer_ < sizeof(int16)) - return false; - buffer_.reset(new uint8[bytes_per_buffer_]); return true; } @@ -37,9 +34,8 @@ FakeAudioOutputStream* FakeAudioOutputStream::GetCurrentFakeStream() { void FakeAudioOutputStream::Start(AudioSourceCallback* callback) { callback_ = callback; - memset(buffer_.get(), 0, bytes_per_buffer_); - callback_->OnMoreData(buffer_.get(), bytes_per_buffer_, - AudioBuffersState(0, 0)); + audio_bus_->Zero(); + callback_->OnMoreData(audio_bus_.get(), AudioBuffersState(0, 0)); } void FakeAudioOutputStream::Stop() { @@ -64,8 +60,8 @@ FakeAudioOutputStream::FakeAudioOutputStream(AudioManagerBase* manager, : audio_manager_(manager), volume_(0), callback_(NULL), - bytes_per_buffer_(params.GetBytesPerBuffer()), closed_(false) { + audio_bus_ = AudioBus::Create(params); } FakeAudioOutputStream::~FakeAudioOutputStream() { diff --git a/media/audio/fake_audio_output_stream.h b/media/audio/fake_audio_output_stream.h index 66f15d1..f4c52e2 100644 --- a/media/audio/fake_audio_output_stream.h +++ b/media/audio/fake_audio_output_stream.h @@ -33,7 +33,7 @@ class MEDIA_EXPORT FakeAudioOutputStream : public AudioOutputStream { virtual void GetVolume(double* volume) OVERRIDE; virtual void Close() OVERRIDE; - uint8* buffer() { return buffer_.get(); } + AudioBus* audio_bus() { return audio_bus_.get(); } private: explicit FakeAudioOutputStream(AudioManagerBase* manager, @@ -46,8 +46,7 @@ class MEDIA_EXPORT FakeAudioOutputStream : public AudioOutputStream { AudioManagerBase* audio_manager_; double volume_; AudioSourceCallback* callback_; - scoped_array<uint8> buffer_; - uint32 bytes_per_buffer_; + scoped_ptr<AudioBus> audio_bus_; bool closed_; DISALLOW_COPY_AND_ASSIGN(FakeAudioOutputStream); diff --git a/media/audio/linux/alsa_output.cc b/media/audio/linux/alsa_output.cc index b230b65..c51a27c 100644 --- a/media/audio/linux/alsa_output.cc +++ b/media/audio/linux/alsa_output.cc @@ -91,7 +91,7 @@ static const int kAlsaMaxSampleRate = 48000; // (which is also 5 channels). // // TODO(ajwong): The source data should have enough info to tell us if we want -// surround41 versus surround51, etc., instead of needing us to guess base don +// surround41 versus surround51, etc., instead of needing us to guess based on // channel number. Fix API to pass that data down. static const char* GuessSpecificDeviceName(uint32 channels) { switch (channels) { @@ -175,8 +175,10 @@ AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name, ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), state_(kCreated), volume_(1.0f), - source_callback_(NULL) { + source_callback_(NULL), + audio_bus_(AudioBus::Create(params)) { DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); + DCHECK_EQ(audio_bus_->frames() * bytes_per_frame_, packet_size_); // Sanity check input values. if (params.sample_rate() > kAlsaMaxSampleRate || @@ -376,22 +378,20 @@ void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) { scoped_refptr<media::DataBuffer> packet = new media::DataBuffer(packet_size_); - int packet_size = RunDataCallback(packet->GetWritableData(), - packet->GetBufferSize(), - AudioBuffersState(buffer_delay, - hardware_delay)); - CHECK_LE(packet_size, packet->GetBufferSize()); + int frames_filled = RunDataCallback( + audio_bus_.get(), AudioBuffersState(buffer_delay, hardware_delay)); + size_t packet_size = frames_filled * bytes_per_frame_; + DCHECK_LE(packet_size, packet_size_); + // Note: If this ever changes to output raw float the data must be clipped + // and sanitized since it may come from an untrusted source such as NaCl. + audio_bus_->ToInterleaved( + frames_filled, bytes_per_sample_, packet->GetWritableData()); // Reset the |last_fill_time| to avoid back to back RunDataCallback(). last_fill_time_ = base::Time::Now(); - // This should not happen, but in case it does, drop any trailing bytes - // that aren't large enough to make a frame. Without this, packet writing - // may stall because the last few bytes in the packet may never get used by - // WritePacket. - DCHECK_EQ(0u, packet_size % bytes_per_frame_); - packet_size = (packet_size / bytes_per_frame_) * bytes_per_frame_; - + // TODO(dalecurtis): Channel downmixing, upmixing, should be done in mixer; + // volume adjust should use SSE optimized vector_fmul() prior to interleave. if (should_downmix_) { if (media::FoldChannels(packet->GetWritableData(), packet_size, @@ -784,13 +784,12 @@ bool AlsaPcmOutputStream::IsOnAudioThread() const { return message_loop_ && message_loop_ == MessageLoop::current(); } -uint32 AlsaPcmOutputStream::RunDataCallback(uint8* dest, - uint32 max_size, - AudioBuffersState buffers_state) { +int AlsaPcmOutputStream::RunDataCallback(AudioBus* audio_bus, + AudioBuffersState buffers_state) { TRACE_EVENT0("audio", "AlsaPcmOutputStream::RunDataCallback"); if (source_callback_) - return source_callback_->OnMoreData(dest, max_size, buffers_state); + return source_callback_->OnMoreData(audio_bus, buffers_state); return 0; } diff --git a/media/audio/linux/alsa_output.h b/media/audio/linux/alsa_output.h index 7415821..736015e 100644 --- a/media/audio/linux/alsa_output.h +++ b/media/audio/linux/alsa_output.h @@ -152,9 +152,7 @@ class MEDIA_EXPORT AlsaPcmOutputStream : public AudioOutputStream { // is passed into the output stream, but ownership is not transfered which // requires a synchronization on access of the |source_callback_| to avoid // using a deleted callback. - uint32 RunDataCallback(uint8* dest, - uint32 max_size, - AudioBuffersState buffers_state); + int RunDataCallback(AudioBus* audio_bus, AudioBuffersState buffers_state); void RunErrorCallback(int code); // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to @@ -213,6 +211,9 @@ class MEDIA_EXPORT AlsaPcmOutputStream : public AudioOutputStream { base::Time last_fill_time_; // Time for the last OnMoreData() callback. + // Container for retrieving data from AudioSourceCallback::OnMoreData(). + scoped_ptr<AudioBus> audio_bus_; + DISALLOW_COPY_AND_ASSIGN(AlsaPcmOutputStream); }; diff --git a/media/audio/linux/alsa_output_unittest.cc b/media/audio/linux/alsa_output_unittest.cc index 12c13e6..65b7c0f 100644 --- a/media/audio/linux/alsa_output_unittest.cc +++ b/media/audio/linux/alsa_output_unittest.cc @@ -68,8 +68,8 @@ class MockAlsaWrapper : public AlsaWrapper { class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback { public: - MOCK_METHOD3(OnMoreData, uint32(uint8* dest, uint32 max_size, - AudioBuffersState buffers_state)); + MOCK_METHOD2(OnMoreData, int(AudioBus* audio_bus, + AudioBuffersState buffers_state)); MOCK_METHOD2(OnError, void(AudioOutputStream* stream, int code)); }; @@ -428,8 +428,8 @@ TEST_F(AlsaPcmOutputStreamTest, StartStop) { .WillRepeatedly(Return(SND_PCM_STATE_RUNNING)); EXPECT_CALL(mock_alsa_wrapper_, PcmDelay(kFakeHandle, _)) .WillRepeatedly(DoAll(SetArgumentPointee<1>(0), Return(0))); - EXPECT_CALL(mock_callback, OnMoreData(_, kTestPacketSize, _)) - .WillRepeatedly(Return(kTestPacketSize)); + EXPECT_CALL(mock_callback, OnMoreData(_, _)) + .WillRepeatedly(Return(kTestFramesPerPacket)); EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(kFakeHandle, _, _)) .WillRepeatedly(Return(kTestFramesPerPacket)); @@ -587,15 +587,15 @@ TEST_F(AlsaPcmOutputStreamTest, BufferPacket) { .WillRepeatedly(Return(0)); // Buffer is full. // Return a partially filled packet. - EXPECT_CALL(mock_callback, OnMoreData(_, _, _)) - .WillOnce(Return(10)); + EXPECT_CALL(mock_callback, OnMoreData(_, _)) + .WillOnce(Return(kTestFramesPerPacket / 2)); bool source_exhausted; test_stream->set_source_callback(&mock_callback); test_stream->packet_size_ = kTestPacketSize; test_stream->BufferPacket(&source_exhausted); - EXPECT_EQ(10, test_stream->buffer_->forward_bytes()); + EXPECT_EQ(kTestPacketSize / 2, test_stream->buffer_->forward_bytes()); EXPECT_FALSE(source_exhausted); test_stream->Close(); } @@ -613,15 +613,15 @@ TEST_F(AlsaPcmOutputStreamTest, BufferPacket_Negative) { .WillOnce(DoAll(SetArgumentPointee<1>(-1), Return(0))); EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(_)) .WillRepeatedly(Return(0)); // Buffer is full. - EXPECT_CALL(mock_callback, OnMoreData(_, _, _)) - .WillOnce(Return(10)); + EXPECT_CALL(mock_callback, OnMoreData(_, _)) + .WillOnce(Return(kTestFramesPerPacket / 2)); bool source_exhausted; test_stream->set_source_callback(&mock_callback); test_stream->packet_size_ = kTestPacketSize; test_stream->BufferPacket(&source_exhausted); - EXPECT_EQ(10, test_stream->buffer_->forward_bytes()); + EXPECT_EQ(kTestPacketSize / 2, test_stream->buffer_->forward_bytes()); EXPECT_FALSE(source_exhausted); test_stream->Close(); } @@ -634,21 +634,21 @@ TEST_F(AlsaPcmOutputStreamTest, BufferPacket_Underrun) { // If ALSA has underrun then we should assume a delay of zero. MockAudioSourceCallback mock_callback; EXPECT_CALL(mock_alsa_wrapper_, PcmState(_)) - .WillOnce(Return(SND_PCM_STATE_XRUN)); + .WillOnce(Return(SND_PCM_STATE_XRUN)); EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(_)) .WillRepeatedly(Return(0)); // Buffer is full. EXPECT_CALL(mock_callback, - OnMoreData(_, _, AllOf( + OnMoreData(_, AllOf( Field(&AudioBuffersState::pending_bytes, 0), Field(&AudioBuffersState::hardware_delay_bytes, 0)))) - .WillOnce(Return(10)); + .WillOnce(Return(kTestFramesPerPacket / 2)); bool source_exhausted; test_stream->set_source_callback(&mock_callback); test_stream->packet_size_ = kTestPacketSize; test_stream->BufferPacket(&source_exhausted); - EXPECT_EQ(10, test_stream->buffer_->forward_bytes()); + EXPECT_EQ(kTestPacketSize / 2, test_stream->buffer_->forward_bytes()); EXPECT_FALSE(source_exhausted); test_stream->Close(); } diff --git a/media/audio/linux/cras_output.cc b/media/audio/linux/cras_output.cc index 77eb68f..e27282c 100644 --- a/media/audio/linux/cras_output.cc +++ b/media/audio/linux/cras_output.cc @@ -72,7 +72,8 @@ CrasOutputStream::CrasOutputStream(const AudioParameters& params, state_(kCreated), volume_(1.0), manager_(manager), - source_callback_(NULL) { + source_callback_(NULL), + audio_bus_(AudioBus::Create(params)) { // We must have a manager. DCHECK(manager_); @@ -278,9 +279,14 @@ uint32 CrasOutputStream::Render(size_t frames, uint32 frames_latency = latency_usec * frame_rate_ / 1000000; uint32 bytes_latency = frames_latency * bytes_per_frame_; - uint32 rendered = source_callback_->OnMoreData( - buffer, frames * bytes_per_frame_, AudioBuffersState(0, bytes_latency)); - return rendered / bytes_per_frame_; + DCHECK_EQ(frames, static_cast<size_t>(audio_bus_->frames())); + int frames_filled = source_callback_->OnMoreData( + audio_bus_.get(), AudioBuffersState(0, bytes_latency)); + // Note: If this ever changes to output raw float the data must be clipped and + // sanitized since it may come from an untrusted source such as NaCl. + audio_bus_->ToInterleaved( + frames_filled, bytes_per_frame_ / (frames * num_channels_), buffer); + return frames_filled; } void CrasOutputStream::NotifyStreamError(int err) { diff --git a/media/audio/linux/cras_output.h b/media/audio/linux/cras_output.h index 7d6034b..8dffbce 100644 --- a/media/audio/linux/cras_output.h +++ b/media/audio/linux/cras_output.h @@ -116,6 +116,9 @@ class MEDIA_EXPORT CrasOutputStream : public AudioOutputStream { // Callback to get audio samples. AudioSourceCallback* source_callback_; + // Container for retrieving data from AudioSourceCallback::OnMoreData(). + scoped_ptr<AudioBus> audio_bus_; + DISALLOW_COPY_AND_ASSIGN(CrasOutputStream); }; diff --git a/media/audio/linux/cras_output_unittest.cc b/media/audio/linux/cras_output_unittest.cc index af56ec4..a54437a 100644 --- a/media/audio/linux/cras_output_unittest.cc +++ b/media/audio/linux/cras_output_unittest.cc @@ -19,8 +19,8 @@ namespace media { class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback { public: - MOCK_METHOD3(OnMoreData, uint32(uint8* dest, uint32 max_size, - AudioBuffersState buffers_state)); + MOCK_METHOD2(OnMoreData, int(AudioBus* audio_bus, + AudioBuffersState buffers_state)); MOCK_METHOD2(OnError, void(AudioOutputStream* stream, int code)); }; @@ -193,16 +193,14 @@ TEST_F(CrasOutputStreamTest, StartStop) { TEST_F(CrasOutputStreamTest, RenderFrames) { CrasOutputStream* test_stream = CreateStream(CHANNEL_LAYOUT_MONO); MockAudioSourceCallback mock_callback; - const uint32 amount_rendered_return = 2048; // Open the stream. ASSERT_TRUE(test_stream->Open()); EXPECT_EQ(CrasOutputStream::kIsOpened, test_stream->state()); // Render Callback. - EXPECT_CALL(mock_callback, OnMoreData(_, - kTestFramesPerPacket * kTestBytesPerFrame, _)) - .WillRepeatedly(Return(amount_rendered_return)); + EXPECT_CALL(mock_callback, OnMoreData(_, _)) + .WillRepeatedly(Return(kTestFramesPerPacket)); // Start. test_stream->Start(&mock_callback); diff --git a/media/audio/mac/audio_low_latency_output_mac.cc b/media/audio/mac/audio_low_latency_output_mac.cc index d9127f8..b9248dc 100644 --- a/media/audio/mac/audio_low_latency_output_mac.cc +++ b/media/audio/mac/audio_low_latency_output_mac.cc @@ -53,7 +53,8 @@ AUAudioOutputStream::AUAudioOutputStream( output_device_id_(kAudioObjectUnknown), volume_(1), hardware_latency_frames_(0), - stopped_(false) { + stopped_(false), + audio_bus_(AudioBus::Create(params)) { // We must have a manager. DCHECK(manager_); // A frame is one sample across all channels. In interleaved audio the per @@ -226,11 +227,19 @@ OSStatus AUAudioOutputStream::Render(UInt32 number_of_frames, uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData); uint32 hardware_pending_bytes = static_cast<uint32> ((playout_latency_frames + 0.5) * format_.mBytesPerFrame); - uint32 filled = source_->OnMoreData( - audio_data, buffer.mDataByteSize, - AudioBuffersState(0, hardware_pending_bytes)); + + DCHECK_EQ(number_of_frames, static_cast<UInt32>(audio_bus_->frames())); + int frames_filled = source_->OnMoreData( + audio_bus_.get(), AudioBuffersState(0, hardware_pending_bytes)); + // Note: If this ever changes to output raw float the data must be clipped and + // sanitized since it may come from an untrusted source such as NaCl. + audio_bus_->ToInterleaved( + frames_filled, format_.mBitsPerChannel / 8, audio_data); + uint32 filled = frames_filled * format_.mBytesPerFrame; // Handle channel order for 5.1 audio. + // TODO(dalecurtis): Channel downmixing, upmixing, should be done in mixer; + // volume adjust should use SSE optimized vector_fmul() prior to interleave. if (format_.mChannelsPerFrame == 6) { if (format_.mBitsPerChannel == 8) { SwizzleCoreAudioLayout5_1(reinterpret_cast<uint8*>(audio_data), filled); diff --git a/media/audio/mac/audio_low_latency_output_mac.h b/media/audio/mac/audio_low_latency_output_mac.h index 81187a9..4ceb4af 100644 --- a/media/audio/mac/audio_low_latency_output_mac.h +++ b/media/audio/mac/audio_low_latency_output_mac.h @@ -99,6 +99,9 @@ class AUAudioOutputStream : public AudioOutputStream { // The flag used to stop the streaming. bool stopped_; + // Container for retrieving data from AudioSourceCallback::OnMoreData(). + scoped_ptr<AudioBus> audio_bus_; + DISALLOW_COPY_AND_ASSIGN(AUAudioOutputStream); }; diff --git a/media/audio/mac/audio_output_mac.cc b/media/audio/mac/audio_output_mac.cc index 7f96735..0a0c321 100644 --- a/media/audio/mac/audio_output_mac.cc +++ b/media/audio/mac/audio_output_mac.cc @@ -53,7 +53,8 @@ PCMQueueOutAudioOutputStream::PCMQueueOutAudioOutputStream( should_swizzle_(false), should_down_mix_(false), stopped_event_(true /* manual reset */, false /* initial state */), - num_buffers_left_(kNumBuffers) { + num_buffers_left_(kNumBuffers), + audio_bus_(AudioBus::Create(params)) { // We must have a manager. DCHECK(manager_); // A frame is one sample across all channels. In interleaved audio the per @@ -409,11 +410,20 @@ void PCMQueueOutAudioOutputStream::RenderCallback(void* p_this, // Adjust the number of pending bytes by subtracting the amount played. if (!static_cast<AudioQueueUserData*>(buffer->mUserData)->empty_buffer) audio_stream->pending_bytes_ -= buffer->mAudioDataByteSize; + uint32 capacity = buffer->mAudioDataBytesCapacity; + AudioBus* audio_bus = audio_stream->audio_bus_.get(); + DCHECK_EQ( + audio_bus->frames() * audio_stream->format_.mBytesPerFrame, capacity); // TODO(sergeyu): Specify correct hardware delay for AudioBuffersState. - uint32 filled = source->OnMoreData( - reinterpret_cast<uint8*>(buffer->mAudioData), capacity, - AudioBuffersState(audio_stream->pending_bytes_, 0)); + int frames_filled = source->OnMoreData( + audio_bus, AudioBuffersState(audio_stream->pending_bytes_, 0)); + uint32 filled = frames_filled * audio_stream->format_.mBytesPerFrame; + // Note: If this ever changes to output raw float the data must be clipped and + // sanitized since it may come from an untrusted source such as NaCl. + audio_bus->ToInterleaved( + frames_filled, audio_stream->format_.mBitsPerChannel / 8, + buffer->mAudioData); // In order to keep the callback running, we need to provide a positive amount // of data to the audio queue. To simulate the behavior of Windows, we write diff --git a/media/audio/mac/audio_output_mac.h b/media/audio/mac/audio_output_mac.h index a7635df..a11433f 100644 --- a/media/audio/mac/audio_output_mac.h +++ b/media/audio/mac/audio_output_mac.h @@ -105,6 +105,9 @@ class PCMQueueOutAudioOutputStream : public AudioOutputStream { // signal "stop completed" from the last buffer's callback. int num_buffers_left_; + // Container for retrieving data from AudioSourceCallback::OnMoreData(). + scoped_ptr<AudioBus> audio_bus_; + DISALLOW_COPY_AND_ASSIGN(PCMQueueOutAudioOutputStream); }; diff --git a/media/audio/mac/audio_output_mac_unittest.cc b/media/audio/mac/audio_output_mac_unittest.cc index bc0a7ce..fc24d30 100644 --- a/media/audio/mac/audio_output_mac_unittest.cc +++ b/media/audio/mac/audio_output_mac_unittest.cc @@ -24,29 +24,11 @@ namespace media { class MockAudioSource : public AudioOutputStream::AudioSourceCallback { public: - MOCK_METHOD3(OnMoreData, uint32(uint8* dest, - uint32 max_size, - AudioBuffersState buffers_state)); + MOCK_METHOD2(OnMoreData, int(AudioBus* audio_bus, + AudioBuffersState buffers_state)); MOCK_METHOD2(OnError, void(AudioOutputStream* stream, int code)); }; -// Validate that the SineWaveAudioSource writes the expected values for -// the FORMAT_16BIT_MONO. -TEST(MacAudioTest, SineWaveAudio16MonoTest) { - const uint32 samples = 1024; - const int freq = 200; - - SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, - freq, AudioParameters::kTelephoneSampleRate); - - // TODO(cpu): Put the real test when the mock renderer is ported. - uint16 buffer[samples] = { 0xffff }; - source.OnMoreData(reinterpret_cast<uint8*>(buffer), sizeof(buffer), - AudioBuffersState(0, 0)); - EXPECT_EQ(0, buffer[0]); - EXPECT_EQ(5126, buffer[1]); -} - // =========================================================================== // Validation of AudioParameters::AUDIO_PCM_LINEAR // @@ -91,8 +73,7 @@ TEST(MacAudioTest, PCMWaveStreamPlay200HzTone44KssMono) { ASSERT_TRUE(NULL != oas); EXPECT_TRUE(oas->Open()); - SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, - 200.0, AudioParameters::kAudioCDSampleRate); + SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate); oas->SetVolume(0.5); oas->Start(&source); usleep(500000); @@ -121,8 +102,7 @@ TEST(MacAudioTest, PCMWaveStreamPlay200HzTone22KssMono) { frames_100_ms)); ASSERT_TRUE(NULL != oas); - SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, - 200.0, AudioParameters::kAudioCDSampleRate/2); + SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate/2); EXPECT_TRUE(oas->Open()); oas->Start(&source); usleep(1500000); @@ -131,10 +111,10 @@ TEST(MacAudioTest, PCMWaveStreamPlay200HzTone22KssMono) { } // Custom action to clear a memory buffer. -static void ClearBuffer(uint8* dest, - uint32 max_size, - AudioBuffersState buffers_state) { - memset(dest, 0, max_size); +static int ClearBuffer(AudioBus* audio_bus, + AudioBuffersState buffers_state) { + audio_bus->Zero(); + return audio_bus->frames(); } TEST(MacAudioTest, PCMWaveStreamPendingBytes) { @@ -158,18 +138,18 @@ TEST(MacAudioTest, PCMWaveStreamPendingBytes) { // And then we will try to provide zero data so the amount of pending bytes // will go down and eventually read zero. InSequence s; - EXPECT_CALL(source, OnMoreData(NotNull(), bytes_100_ms, + EXPECT_CALL(source, OnMoreData(NotNull(), Field(&AudioBuffersState::pending_bytes, 0))) - .WillOnce(DoAll(Invoke(&ClearBuffer), Return(bytes_100_ms))); - EXPECT_CALL(source, OnMoreData(NotNull(), bytes_100_ms, + .WillOnce(Invoke(ClearBuffer)); + EXPECT_CALL(source, OnMoreData(NotNull(), Field(&AudioBuffersState::pending_bytes, bytes_100_ms))) - .WillOnce(DoAll(Invoke(&ClearBuffer), Return(bytes_100_ms))); - EXPECT_CALL(source, OnMoreData(NotNull(), bytes_100_ms, + .WillOnce(Invoke(ClearBuffer)); + EXPECT_CALL(source, OnMoreData(NotNull(), Field(&AudioBuffersState::pending_bytes, bytes_100_ms))) .WillOnce(Return(0)); - EXPECT_CALL(source, OnMoreData(NotNull(), bytes_100_ms, _)) + EXPECT_CALL(source, OnMoreData(NotNull(), _)) .Times(AnyNumber()) .WillRepeatedly(Return(0)); diff --git a/media/audio/pulse/pulse_output.cc b/media/audio/pulse/pulse_output.cc index 98f1f58..bdd29c0 100644 --- a/media/audio/pulse/pulse_output.cc +++ b/media/audio/pulse/pulse_output.cc @@ -310,10 +310,16 @@ bool PulseAudioOutputStream::BufferPacketFromSource() { // to happen in practice though. scoped_refptr<media::DataBuffer> packet = new media::DataBuffer(packet_size_); - size_t packet_size = RunDataCallback(packet->GetWritableData(), - packet->GetBufferSize(), - AudioBuffersState(buffer_delay, - hardware_delay)); + int frames_filled = RunDataCallback( + audio_bus_.get(), AudioBuffersState(buffer_delay, hardware_delay)); + size_t packet_size = frames_filled * bytes_per_frame_; + + DCHECK_LE(packet_size, packet_size_); + // Note: If this ever changes to output raw float the data must be clipped and + // sanitized since it may come from an untrusted source such as NaCl. + audio_bus_->ToInterleaved( + frames_filled, bytes_per_frame_ / channel_count_, + packet->GetWritableData()); if (packet_size == 0) return false; @@ -421,10 +427,10 @@ void PulseAudioOutputStream::GetVolume(double* volume) { *volume = volume_; } -uint32 PulseAudioOutputStream::RunDataCallback( - uint8* dest, uint32 max_size, AudioBuffersState buffers_state) { +int PulseAudioOutputStream::RunDataCallback( + AudioBus* audio_bus, AudioBuffersState buffers_state) { if (source_callback_) - return source_callback_->OnMoreData(dest, max_size, buffers_state); + return source_callback_->OnMoreData(audio_bus, buffers_state); return 0; } diff --git a/media/audio/pulse/pulse_output.h b/media/audio/pulse/pulse_output.h index 3c1cae6..1d39af4 100644 --- a/media/audio/pulse/pulse_output.h +++ b/media/audio/pulse/pulse_output.h @@ -28,8 +28,6 @@ namespace media { -class SeekableBuffer; - #if defined(OS_LINUX) class AudioManagerLinux; typedef AudioManagerLinux AudioManagerPulse; @@ -41,6 +39,7 @@ typedef AudioManagerOpenBSD AudioManagerPulse; #endif class AudioParameters; +class SeekableBuffer; class PulseAudioOutputStream : public AudioOutputStream { public: @@ -50,12 +49,12 @@ class PulseAudioOutputStream : public AudioOutputStream { virtual ~PulseAudioOutputStream(); // Implementation of AudioOutputStream. - virtual bool Open(); - virtual void Close(); - virtual void Start(AudioSourceCallback* callback); - virtual void Stop(); - virtual void SetVolume(double volume); - virtual void GetVolume(double* volume); + virtual bool Open() OVERRIDE; + virtual void Close() OVERRIDE; + virtual void Start(AudioSourceCallback* callback) OVERRIDE; + virtual void Stop() OVERRIDE; + virtual void SetVolume(double volume) OVERRIDE; + virtual void GetVolume(double* volume) OVERRIDE; private: // PulseAudio Callbacks. @@ -77,8 +76,7 @@ class PulseAudioOutputStream : public AudioOutputStream { void WriteToStream(size_t bytes_to_write, size_t* bytes_written); // API for Proxying calls to the AudioSourceCallback provided during Start(). - uint32 RunDataCallback(uint8* dest, uint32 max_size, - AudioBuffersState buffers_state); + int RunDataCallback(AudioBus* audio_bus, AudioBuffersState buffers_state); // Close() helper function to free internal structs. void Reset(); @@ -127,6 +125,9 @@ class PulseAudioOutputStream : public AudioOutputStream { // Callback to audio data source. AudioSourceCallback* source_callback_; + // Container for retrieving data from AudioSourceCallback::OnMoreData(). + scoped_ptr<AudioBus> audio_bus_; + DISALLOW_COPY_AND_ASSIGN(PulseAudioOutputStream); }; diff --git a/media/audio/simple_sources.cc b/media/audio/simple_sources.cc index f33664e..00ec1f0 100644 --- a/media/audio/simple_sources.cc +++ b/media/audio/simple_sources.cc @@ -1,95 +1,64 @@ // Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// MSVC++ requires this to be set before any other includes to get M_PI. +#define _USE_MATH_DEFINES +#include <cmath> #include "media/audio/simple_sources.h" -#include <math.h> #include <algorithm> -#include "base/basictypes.h" #include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "media/audio/audio_io.h" -#include "media/base/data_buffer.h" +#include "media/audio/audio_util.h" namespace media { ////////////////////////////////////////////////////////////////////////////// // SineWaveAudioSource implementation. -SineWaveAudioSource::SineWaveAudioSource(Format format, int channels, +SineWaveAudioSource::SineWaveAudioSource(int channels, double freq, double sample_freq) - : format_(format), - channels_(channels), - freq_(freq), - sample_freq_(sample_freq), - time_state_(0) { - // TODO(cpu): support other formats. - DCHECK((format_ == FORMAT_16BIT_LINEAR_PCM) && (channels_ == 1)); + : channels_(channels), + f_(freq / sample_freq), + time_state_(0), + cap_(0) { } // The implementation could be more efficient if a lookup table is constructed // but it is efficient enough for our simple needs. -uint32 SineWaveAudioSource::OnMoreData( - uint8* dest, uint32 max_size, AudioBuffersState audio_buffers) { - const double kTwoPi = 2.0 * 3.141592653589; - double f = freq_ / sample_freq_; - int16* sin_tbl = reinterpret_cast<int16*>(dest); - uint32 len = max_size / sizeof(int16); +int SineWaveAudioSource::OnMoreData(AudioBus* audio_bus, + AudioBuffersState audio_buffers) { + base::AutoLock auto_lock(time_lock_); // The table is filled with s(t) = kint16max*sin(Theta*t), // where Theta = 2*PI*fs. // We store the discrete time value |t| in a member to ensure that the // next pass starts at a correct state. - for (uint32 n = 0; n < len; ++n) { - double theta = kTwoPi * f; - double ksinx = kint16max * sin(theta * time_state_); - sin_tbl[n] = (ksinx > 0.0f) ? static_cast<int16>(ksinx + 0.5) : - static_cast<int16>(ksinx - 0.5); - ++time_state_; + int max_frames = cap_ > 0 ? + std::min(audio_bus->frames(), cap_ - time_state_) : audio_bus->frames(); + for (int i = 0; i < max_frames; ++i) + audio_bus->channel(0)[i] = sin(2.0 * M_PI * f_ * time_state_++); + for (int i = 1; i < audio_bus->channels(); ++i) { + memcpy(audio_bus->channel(i), audio_bus->channel(0), + max_frames * sizeof(*audio_bus->channel(i))); } - return max_size; + return max_frames; } void SineWaveAudioSource::OnError(AudioOutputStream* stream, int code) { NOTREACHED(); } -////////////////////////////////////////////////////////////////////////////// -// PushSource implementation. - -PushSource::PushSource() - : buffer_(0, 0) { -} - -PushSource::~PushSource() { } - -uint32 PushSource::OnMoreData( - uint8* dest, uint32 max_size, AudioBuffersState buffers_state) { - return buffer_.Read(dest, max_size); -} - -void PushSource::OnError(AudioOutputStream* stream, int code) { - NOTREACHED(); -} - -// TODO(cpu): Manage arbitrary large sizes. -bool PushSource::Write(const void *data, uint32 len) { - if (len == 0) { - NOTREACHED(); - return false; - } - buffer_.Append(static_cast<const uint8*>(data), len); - return true; -} - -uint32 PushSource::UnProcessedBytes() { - return buffer_.forward_bytes(); +void SineWaveAudioSource::CapSamples(int cap) { + base::AutoLock auto_lock(time_lock_); + DCHECK_GT(cap, 0); + cap_ = cap; } -void PushSource::CleanUp() { - buffer_.Clear(); +void SineWaveAudioSource::Reset() { + base::AutoLock auto_lock(time_lock_); + time_state_ = 0; } } // namespace media diff --git a/media/audio/simple_sources.h b/media/audio/simple_sources.h index f68c2be..5fbcded 100644 --- a/media/audio/simple_sources.h +++ b/media/audio/simple_sources.h @@ -5,8 +5,7 @@ #ifndef MEDIA_AUDIO_SIMPLE_SOURCES_H_ #define MEDIA_AUDIO_SIMPLE_SOURCES_H_ -#include <list> - +#include "base/synchronization/lock.h" #include "media/audio/audio_io.h" #include "media/base/seekable_buffer.h" @@ -16,73 +15,28 @@ namespace media { class MEDIA_EXPORT SineWaveAudioSource : public AudioOutputStream::AudioSourceCallback { public: - enum Format { - FORMAT_8BIT_LINEAR_PCM, - FORMAT_16BIT_LINEAR_PCM, - }; // |channels| is the number of audio channels, |freq| is the frequency in // hertz and it has to be less than half of the sampling frequency // |sample_freq| or else you will get aliasing. - SineWaveAudioSource(Format format, int channels, - double freq, double sample_freq); + SineWaveAudioSource(int channels, double freq, double sample_freq); virtual ~SineWaveAudioSource() {} + // Return up to |cap| samples of data via OnMoreData(). Use Reset() to + // allow more data to be served. + void CapSamples(int cap); + void Reset(); + // Implementation of AudioSourceCallback. - virtual uint32 OnMoreData( - uint8* dest, uint32 max_size, AudioBuffersState audio_buffers) OVERRIDE; + virtual int OnMoreData(AudioBus* audio_bus, + AudioBuffersState audio_buffers) OVERRIDE; virtual void OnError(AudioOutputStream* stream, int code) OVERRIDE; protected: - Format format_; int channels_; - double freq_; - double sample_freq_; + double f_; int time_state_; -}; - -// Defines an interface for pushing audio output. In contrast, the interfaces -// defined by AudioSourceCallback are pull model only. -class MEDIA_EXPORT PushAudioOutput { - public: - virtual ~PushAudioOutput() {} - - // Write audio data to the audio device. It will be played eventually. - // Returns false on failure. - virtual bool Write(const void* data, uint32 len) = 0; - - // Returns the number of bytes that have been buffered but not yet given - // to the audio device. - virtual uint32 UnProcessedBytes() = 0; -}; - -// A fairly basic class to connect a push model provider PushAudioOutput to -// a pull model provider AudioSourceCallback. Fundamentally it manages a series -// of audio buffers and is unaware of the actual audio format. -// Note that the PushSource is not thread safe and user need to provide locking. -class MEDIA_EXPORT PushSource - : public AudioOutputStream::AudioSourceCallback, - public PushAudioOutput { - public: - PushSource(); - virtual ~PushSource(); - - // Write one buffer. - virtual bool Write(const void* data, uint32 len) OVERRIDE; - - // Return the total number of bytes not given to the audio device yet. - virtual uint32 UnProcessedBytes() OVERRIDE; - - // Implementation of AudioSourceCallback. - virtual uint32 OnMoreData(uint8* dest, - uint32 max_size, - AudioBuffersState buffers_state) OVERRIDE; - virtual void OnError(AudioOutputStream* stream, int code) OVERRIDE; - - private: - // Free acquired resources. - void CleanUp(); - - media::SeekableBuffer buffer_; + int cap_; + base::Lock time_lock_; }; } // namespace media diff --git a/media/audio/simple_sources_unittest.cc b/media/audio/simple_sources_unittest.cc index 74d19f2..47f18b4 100644 --- a/media/audio/simple_sources_unittest.cc +++ b/media/audio/simple_sources_unittest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include <algorithm> // std::min +#include <limits> #include "base/logging.h" #include "base/basictypes.h" @@ -11,66 +12,18 @@ #include "media/audio/audio_manager.h" #include "media/audio/fake_audio_output_stream.h" #include "media/audio/simple_sources.h" +#include "media/base/audio_bus.h" #include "testing/gtest/include/gtest/gtest.h" namespace media { -static void GenerateRandomData(char* buffer, uint32 len) { - static bool called = false; - if (!called) { - called = true; - int seed = static_cast<int>(base::Time::Now().ToInternalValue()); - srand(seed); - VLOG(1) << "Random seed: " << seed; - } - - for (uint32 i = 0; i < len; i++) - buffer[i] = static_cast<char>(rand()); // NOLINT -} - -// To test write size smaller than read size. -TEST(SimpleSourcesTest, PushSourceSmallerWrite) { - const uint32 kDataSize = 40960; - scoped_array<char> data(new char[kDataSize]); - GenerateRandomData(data.get(), kDataSize); - - // Choose two prime numbers for read and write sizes. - const uint32 kWriteSize = 283; - const uint32 kReadSize = 293; - scoped_array<uint8> read_data(new uint8[kReadSize]); - - // Create a PushSource. - PushSource push_source; - EXPECT_EQ(0u, push_source.UnProcessedBytes()); - - // Write everything into this push source. - for (uint32 i = 0; i < kDataSize; i += kWriteSize) { - uint32 size = std::min(kDataSize - i, kWriteSize); - EXPECT_TRUE(push_source.Write(data.get() + i, size)); - } - EXPECT_EQ(kDataSize, push_source.UnProcessedBytes()); - - // Read everything from the push source. - for (uint32 i = 0; i < kDataSize; i += kReadSize) { - uint32 size = std::min(kDataSize - i , kReadSize); - EXPECT_EQ(size, push_source.OnMoreData(read_data.get(), size, - AudioBuffersState())); - EXPECT_EQ(0, memcmp(data.get() + i, read_data.get(), size)); - } - EXPECT_EQ(0u, push_source.UnProcessedBytes()); -} - -// Validate that the SineWaveAudioSource writes the expected values for -// the FORMAT_16BIT_MONO. The values are carefully selected so rounding issues -// do not affect the result. We also test that AudioManager::GetLastMockBuffer -// works. -TEST(SimpleSources, SineWaveAudio16MonoTest) { +// Validate that the SineWaveAudioSource writes the expected values. +TEST(SimpleSources, SineWaveAudioSource) { const uint32 samples = 1024; const uint32 bytes_per_sample = 2; const int freq = 200; - SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, - freq, AudioParameters::kTelephoneSampleRate); + SineWaveAudioSource source(1, freq, AudioParameters::kTelephoneSampleRate); scoped_ptr<AudioManager> audio_man(AudioManager::Create()); AudioParameters params( @@ -84,24 +37,43 @@ TEST(SimpleSources, SineWaveAudio16MonoTest) { oas->Stop(); ASSERT_TRUE(FakeAudioOutputStream::GetCurrentFakeStream()); - const int16* last_buffer = - reinterpret_cast<int16*>( - FakeAudioOutputStream::GetCurrentFakeStream()->buffer()); - ASSERT_TRUE(NULL != last_buffer); + const AudioBus* last_audio_bus = + FakeAudioOutputStream::GetCurrentFakeStream()->audio_bus(); + ASSERT_TRUE(NULL != last_audio_bus); uint32 half_period = AudioParameters::kTelephoneSampleRate / (freq * 2); // Spot test positive incursion of sine wave. - EXPECT_EQ(0, last_buffer[0]); - EXPECT_EQ(5126, last_buffer[1]); - EXPECT_TRUE(last_buffer[1] < last_buffer[2]); - EXPECT_TRUE(last_buffer[2] < last_buffer[3]); + EXPECT_NEAR(0, last_audio_bus->channel(0)[0], + std::numeric_limits<float>::epsilon()); + EXPECT_FLOAT_EQ(0.15643446f, last_audio_bus->channel(0)[1]); + EXPECT_LT(last_audio_bus->channel(0)[1], last_audio_bus->channel(0)[2]); + EXPECT_LT(last_audio_bus->channel(0)[2], last_audio_bus->channel(0)[3]); // Spot test negative incursion of sine wave. - EXPECT_EQ(0, last_buffer[half_period]); - EXPECT_EQ(-5126, last_buffer[half_period + 1]); - EXPECT_TRUE(last_buffer[half_period + 1] > last_buffer[half_period + 2]); - EXPECT_TRUE(last_buffer[half_period + 2] > last_buffer[half_period + 3]); + EXPECT_NEAR(0, last_audio_bus->channel(0)[half_period], + std::numeric_limits<float>::epsilon()); + EXPECT_FLOAT_EQ(-0.15643446f, last_audio_bus->channel(0)[half_period + 1]); + EXPECT_GT(last_audio_bus->channel(0)[half_period + 1], + last_audio_bus->channel(0)[half_period + 2]); + EXPECT_GT(last_audio_bus->channel(0)[half_period + 2], + last_audio_bus->channel(0)[half_period + 3]); + oas->Close(); } +TEST(SimpleSources, SineWaveAudioCapped) { + SineWaveAudioSource source(1, 200, AudioParameters::kTelephoneSampleRate); + + static const int kSampleCap = 100; + source.CapSamples(kSampleCap); + + scoped_ptr<AudioBus> audio_bus = AudioBus::Create(1, 2 * kSampleCap); + EXPECT_EQ(source.OnMoreData( + audio_bus.get(), AudioBuffersState()), kSampleCap); + EXPECT_EQ(source.OnMoreData(audio_bus.get(), AudioBuffersState()), 0); + source.Reset(); + EXPECT_EQ(source.OnMoreData( + audio_bus.get(), AudioBuffersState()), kSampleCap); +} + } // namespace media diff --git a/media/audio/win/audio_low_latency_output_win.cc b/media/audio/win/audio_low_latency_output_win.cc index 78c823f..a5ef19d 100644 --- a/media/audio/win/audio_low_latency_output_win.cc +++ b/media/audio/win/audio_low_latency_output_win.cc @@ -338,7 +338,8 @@ WASAPIAudioOutputStream::WASAPIAudioOutputStream(AudioManagerWin* manager, share_mode_(GetShareMode()), client_channel_count_(params.channels()), num_written_frames_(0), - source_(NULL) { + source_(NULL), + audio_bus_(AudioBus::Create(params)) { CHECK(com_init_.succeeded()); DCHECK(manager_); @@ -821,9 +822,15 @@ void WASAPIAudioOutputStream::Run() { if (channel_factor() == 1) { // Case I: no up-mixing. - num_filled_bytes = source_->OnMoreData( - audio_data, packet_size_bytes_, - AudioBuffersState(0, audio_delay_bytes)); + int frames_filled = source_->OnMoreData( + audio_bus_.get(), AudioBuffersState(0, audio_delay_bytes)); + num_filled_bytes = frames_filled * frame_size_; + DCHECK_LE(num_filled_bytes, packet_size_bytes_); + // Note: If this ever changes to output raw float the data must be + // clipped and sanitized since it may come from an untrusted + // source such as NaCl. + audio_bus_->ToInterleaved( + frames_filled, bytes_per_sample, audio_data); } else { // Case II: up-mixing. const int audio_source_size_bytes = @@ -831,9 +838,17 @@ void WASAPIAudioOutputStream::Run() { scoped_array<uint8> buffer; buffer.reset(new uint8[audio_source_size_bytes]); - num_filled_bytes = source_->OnMoreData( - buffer.get(), audio_source_size_bytes, - AudioBuffersState(0, audio_delay_bytes)); + int frames_filled = source_->OnMoreData( + audio_bus_.get(), AudioBuffersState(0, audio_delay_bytes)); + num_filled_bytes = + frames_filled * bytes_per_sample * audio_bus_->channels(); + DCHECK_LE(num_filled_bytes, + static_cast<size_t>(audio_source_size_bytes)); + // Note: If this ever changes to output raw float the data must be + // clipped and sanitized since it may come from an untrusted + // source such as NaCl. + audio_bus_->ToInterleaved( + frames_filled, bytes_per_sample, buffer.get()); // Do channel up-mixing on 16-bit PCM samples. num_filled_bytes = ChannelUpMix(buffer.get(), diff --git a/media/audio/win/audio_low_latency_output_win.h b/media/audio/win/audio_low_latency_output_win.h index 2e6e387..053be13 100644 --- a/media/audio/win/audio_low_latency_output_win.h +++ b/media/audio/win/audio_low_latency_output_win.h @@ -393,6 +393,9 @@ class MEDIA_EXPORT WASAPIAudioOutputStream // This event will be signaled when stream switching shall take place. base::win::ScopedHandle stream_switch_event_; + // Container for retrieving data from AudioSourceCallback::OnMoreData(). + scoped_ptr<AudioBus> audio_bus_; + DISALLOW_COPY_AND_ASSIGN(WASAPIAudioOutputStream); }; diff --git a/media/audio/win/audio_low_latency_output_win_unittest.cc b/media/audio/win/audio_low_latency_output_win_unittest.cc index 5a935e5..666d0fd 100644 --- a/media/audio/win/audio_low_latency_output_win_unittest.cc +++ b/media/audio/win/audio_low_latency_output_win_unittest.cc @@ -44,6 +44,7 @@ static const char kSpeechFile_16b_m_48k[] = "speech_16b_mono_48kHz.raw"; static const char kSpeechFile_16b_m_44k[] = "speech_16b_mono_44kHz.raw"; static const size_t kFileDurationMs = 20000; static const size_t kNumFileSegments = 2; +static const int kBitsPerSample = 16; static const size_t kMaxDeltaSamples = 1000; static const char* kDeltaTimeMsFileName = "delta_times_ms.txt"; @@ -63,9 +64,8 @@ ACTION_P(QuitLoop, loop) { class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback { public: - MOCK_METHOD3(OnMoreData, uint32(uint8* dest, - uint32 max_size, - AudioBuffersState buffers_state)); + MOCK_METHOD2(OnMoreData, int(AudioBus* audio_bus, + AudioBuffersState buffers_state)); MOCK_METHOD2(OnError, void(AudioOutputStream* stream, int code)); }; @@ -110,9 +110,8 @@ class ReadFromFileAudioSource : public AudioOutputStream::AudioSourceCallback { } // AudioOutputStream::AudioSourceCallback implementation. - virtual uint32 OnMoreData(uint8* dest, - uint32 max_size, - AudioBuffersState buffers_state) { + virtual int OnMoreData(AudioBus* audio_bus, + AudioBuffersState buffers_state) { // Store time difference between two successive callbacks in an array. // These values will be written to a file in the destructor. int diff = (base::Time::Now() - previous_call_time_).InMilliseconds(); @@ -122,15 +121,20 @@ class ReadFromFileAudioSource : public AudioOutputStream::AudioSourceCallback { ++elements_to_write_; } + int max_size = + audio_bus->frames() * audio_bus->channels() * kBitsPerSample / 8; + // Use samples read from a data file and fill up the audio buffer // provided to us in the callback. if (pos_ + static_cast<int>(max_size) > file_size()) max_size = file_size() - pos_; + int frames = max_size / (audio_bus->channels() * kBitsPerSample / 8); if (max_size) { - memcpy(dest, file_->GetData() + pos_, max_size); + audio_bus->FromInterleaved( + file_->GetData() + pos_, frames, kBitsPerSample / 8); pos_ += max_size; } - return max_size; + return frames; } virtual void OnError(AudioOutputStream* stream, int code) {} @@ -176,7 +180,7 @@ class AudioOutputStreamWrapper { audio_man_(audio_manager), format_(AudioParameters::AUDIO_PCM_LOW_LATENCY), channel_layout_(CHANNEL_LAYOUT_STEREO), - bits_per_sample_(16) { + bits_per_sample_(kBitsPerSample) { // Use native/mixing sample rate and 10ms frame size as default. sample_rate_ = static_cast<int>( WASAPIAudioOutputStream::HardwareSampleRate(eConsole)); @@ -461,11 +465,10 @@ TEST(WASAPIAudioOutputStreamTest, PacketSizeInMilliseconds) { AudioBuffersState state(0, bytes_per_packet); // Wait for the first callback and verify its parameters. - EXPECT_CALL(source, OnMoreData(NotNull(), bytes_per_packet, - HasValidDelay(state))) + EXPECT_CALL(source, OnMoreData(NotNull(), HasValidDelay(state))) .WillOnce(DoAll( QuitLoop(loop.message_loop_proxy()), - Return(bytes_per_packet))); + Return(aosw.samples_per_packet()))); aos->Start(&source); loop.PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(), @@ -500,12 +503,11 @@ TEST(WASAPIAudioOutputStreamTest, PacketSizeInSamples) { AudioBuffersState state(0, bytes_per_packet); // Ensure that callbacks start correctly. - EXPECT_CALL(source, OnMoreData(NotNull(), bytes_per_packet, - HasValidDelay(state))) + EXPECT_CALL(source, OnMoreData(NotNull(), HasValidDelay(state))) .WillOnce(DoAll( QuitLoop(loop.message_loop_proxy()), - Return(bytes_per_packet))) - .WillRepeatedly(Return(bytes_per_packet)); + Return(aosw.samples_per_packet()))) + .WillRepeatedly(Return(aosw.samples_per_packet())); aos->Start(&source); loop.PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(), @@ -536,12 +538,11 @@ TEST(WASAPIAudioOutputStreamTest, Mono) { // Set up expected minimum delay estimation. AudioBuffersState state(0, bytes_per_packet); - EXPECT_CALL(source, OnMoreData(NotNull(), bytes_per_packet, - HasValidDelay(state))) + EXPECT_CALL(source, OnMoreData(NotNull(), HasValidDelay(state))) .WillOnce(DoAll( QuitLoop(loop.message_loop_proxy()), - Return(bytes_per_packet))) - .WillRepeatedly(Return(bytes_per_packet)); + Return(aosw.samples_per_packet()))) + .WillRepeatedly(Return(aosw.samples_per_packet())); aos->Start(&source); loop.PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(), @@ -789,12 +790,11 @@ TEST(WASAPIAudioOutputStreamTest, ExclusiveModeMinBufferSizeAt48kHz) { AudioBuffersState state(0, bytes_per_packet); // Wait for the first callback and verify its parameters. - EXPECT_CALL(source, OnMoreData(NotNull(), bytes_per_packet, - HasValidDelay(state))) + EXPECT_CALL(source, OnMoreData(NotNull(), HasValidDelay(state))) .WillOnce(DoAll( QuitLoop(loop.message_loop_proxy()), - Return(bytes_per_packet))) - .WillRepeatedly(Return(bytes_per_packet)); + Return(aosw.samples_per_packet()))) + .WillRepeatedly(Return(aosw.samples_per_packet())); aos->Start(&source); loop.PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(), @@ -831,12 +831,11 @@ TEST(WASAPIAudioOutputStreamTest, ExclusiveModeMinBufferSizeAt44kHz) { AudioBuffersState state(0, bytes_per_packet); // Wait for the first callback and verify its parameters. - EXPECT_CALL(source, OnMoreData(NotNull(), bytes_per_packet, - HasValidDelay(state))) + EXPECT_CALL(source, OnMoreData(NotNull(), HasValidDelay(state))) .WillOnce(DoAll( QuitLoop(loop.message_loop_proxy()), - Return(bytes_per_packet))) - .WillRepeatedly(Return(bytes_per_packet)); + Return(aosw.samples_per_packet()))) + .WillRepeatedly(Return(aosw.samples_per_packet())); aos->Start(&source); loop.PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(), diff --git a/media/audio/win/audio_output_win_unittest.cc b/media/audio/win/audio_output_win_unittest.cc index 76aea2d..b3d6a38 100644 --- a/media/audio/win/audio_output_win_unittest.cc +++ b/media/audio/win/audio_output_win_unittest.cc @@ -8,6 +8,7 @@ #include "base/basictypes.h" #include "base/base_paths.h" #include "base/file_util.h" +#include "base/memory/aligned_memory.h" #include "base/path_service.h" #include "base/sync_socket.h" #include "base/win/scoped_com_initializer.h" @@ -24,6 +25,7 @@ using ::testing::_; using ::testing::AnyNumber; using ::testing::DoAll; using ::testing::Field; +using ::testing::Invoke; using ::testing::InSequence; using ::testing::NiceMock; using ::testing::NotNull; @@ -45,14 +47,12 @@ class TestSourceBasic : public AudioOutputStream::AudioSourceCallback { had_error_(0) { } // AudioSourceCallback::OnMoreData implementation: - virtual uint32 OnMoreData(uint8* dest, - uint32 max_size, - AudioBuffersState buffers_state) { + virtual int OnMoreData(AudioBus* audio_bus, + AudioBuffersState buffers_state) { ++callback_count_; - // Touch the first byte to make sure memory is good. - if (max_size) - reinterpret_cast<char*>(dest)[0] = 1; - return max_size; + // Touch the channel memory value to make sure memory is good. + audio_bus->Zero(); + return audio_bus->frames(); } // AudioSourceCallback::OnError implementation: virtual void OnError(AudioOutputStream* stream, int code) { @@ -77,46 +77,6 @@ class TestSourceBasic : public AudioOutputStream::AudioSourceCallback { }; const int kMaxNumBuffers = 3; -// Specializes TestSourceBasic to detect that the AudioStream is using -// triple buffering correctly. -class TestSourceTripleBuffer : public TestSourceBasic { - public: - TestSourceTripleBuffer() { - buffer_address_[0] = NULL; - buffer_address_[1] = NULL; - buffer_address_[2] = NULL; - } - // Override of TestSourceBasic::OnMoreData. - virtual uint32 OnMoreData(uint8* dest, - uint32 max_size, - AudioBuffersState buffers_state) { - // Call the base, which increments the callback_count_. - TestSourceBasic::OnMoreData(dest, max_size, buffers_state); - if (callback_count() % NumberOfWaveOutBuffers() == 2) { - set_error(!CompareExistingIfNotNULL(2, dest)); - } else if (callback_count() % NumberOfWaveOutBuffers() == 1) { - set_error(!CompareExistingIfNotNULL(1, dest)); - } else { - set_error(!CompareExistingIfNotNULL(0, dest)); - } - if (callback_count() > kMaxNumBuffers) { - set_error(buffer_address_[0] == buffer_address_[1]); - set_error(buffer_address_[1] == buffer_address_[2]); - } - return max_size; - } - - private: - bool CompareExistingIfNotNULL(uint32 index, void* address) { - void*& entry = buffer_address_[index]; - if (!entry) - entry = address; - return (entry == address); - } - - void* buffer_address_[kMaxNumBuffers]; -}; - // Specializes TestSourceBasic to simulate a source that blocks for some time // in the OnMoreData callback. class TestSourceLaggy : public TestSourceBasic { @@ -124,15 +84,14 @@ class TestSourceLaggy : public TestSourceBasic { TestSourceLaggy(int laggy_after_buffer, int lag_in_ms) : laggy_after_buffer_(laggy_after_buffer), lag_in_ms_(lag_in_ms) { } - virtual uint32 OnMoreData(uint8* dest, - uint32 max_size, - AudioBuffersState buffers_state) { + virtual int OnMoreData(AudioBus* audio_bus, + AudioBuffersState buffers_state) { // Call the base, which increments the callback_count_. - TestSourceBasic::OnMoreData(dest, max_size, buffers_state); + TestSourceBasic::OnMoreData(audio_bus, buffers_state); if (callback_count() > kMaxNumBuffers) { ::Sleep(lag_in_ms_); } - return max_size; + return audio_bus->frames(); } private: int laggy_after_buffer_; @@ -141,10 +100,14 @@ class TestSourceLaggy : public TestSourceBasic { class MockAudioSource : public AudioOutputStream::AudioSourceCallback { public: - MOCK_METHOD3(OnMoreData, uint32(uint8* dest, - uint32 max_size, - AudioBuffersState buffers_state)); + MOCK_METHOD2(OnMoreData, int(AudioBus* audio_bus, + AudioBuffersState buffers_state)); MOCK_METHOD2(OnError, void(AudioOutputStream* stream, int code)); + + static int ClearData(AudioBus* audio_bus, AudioBuffersState buffers_state) { + audio_bus->Zero(); + return audio_bus->frames(); + } }; // Helper class to memory map an entire file. The mapping is read-only. Don't @@ -295,30 +258,6 @@ TEST(WinAudioTest, PCMWaveStreamOpenLimit) { oas->Close(); } -// Test that it uses the triple buffers correctly. Because it uses the actual -// audio device, you might hear a short pop noise for a short time. -TEST(WinAudioTest, PCMWaveStreamTripleBuffer) { - scoped_ptr<AudioManager> audio_man(AudioManager::Create()); - if (!audio_man->HasAudioOutputDevices()) { - LOG(WARNING) << "No output device detected."; - return; - } - - AudioOutputStream* oas = audio_man->MakeAudioOutputStream( - AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, - 16000, 16, 256)); - ASSERT_TRUE(NULL != oas); - TestSourceTripleBuffer test_triple_buffer; - EXPECT_TRUE(oas->Open()); - oas->Start(&test_triple_buffer); - ::Sleep(300); - EXPECT_GT(test_triple_buffer.callback_count(), kMaxNumBuffers); - EXPECT_FALSE(test_triple_buffer.had_error()); - oas->Stop(); - ::Sleep(500); - oas->Close(); -} - // Test potential deadlock situation if the source is slow or blocks for some // time. The actual EXPECT_GT are mostly meaningless and the real test is that // the test completes in reasonable time. @@ -362,8 +301,7 @@ TEST(WinAudioTest, PCMWaveStreamPlaySlowLoop) { AudioParameters::kAudioCDSampleRate, 16, samples_100_ms)); ASSERT_TRUE(NULL != oas); - SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, - 200.0, AudioParameters::kAudioCDSampleRate); + SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate); EXPECT_TRUE(oas->Open()); oas->SetVolume(1.0); @@ -393,8 +331,7 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzTone44Kss) { AudioParameters::kAudioCDSampleRate, 16, samples_100_ms)); ASSERT_TRUE(NULL != oas); - SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, - 200.0, AudioParameters::kAudioCDSampleRate); + SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate); EXPECT_TRUE(oas->Open()); oas->SetVolume(1.0); @@ -422,8 +359,7 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzTone22Kss) { samples_100_ms)); ASSERT_TRUE(NULL != oas); - SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, - 200.0, AudioParameters::kAudioCDSampleRate/2); + SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate/2); EXPECT_TRUE(oas->Open()); @@ -440,11 +376,9 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzTone22Kss) { oas->Close(); } -// Uses the PushSource to play a 2 seconds file clip for about 5 seconds. We +// Uses a restricted source to play ~2 seconds of audio for about 5 seconds. We // try hard to generate situation where the two threads are accessing the -// object roughly at the same time. What you hear is a sweeping tone from 1KHz -// to 2KHz with a bit of fade out at the end for one second. The file is two -// of these sweeping tones back to back. +// object roughly at the same time. TEST(WinAudioTest, PushSourceFile16KHz) { scoped_ptr<AudioManager> audio_man(AudioManager::Create()); if (!audio_man->HasAudioOutputDevices()) { @@ -452,44 +386,29 @@ TEST(WinAudioTest, PushSourceFile16KHz) { return; } - // Open sweep02_16b_mono_16KHz.raw which has no format. It contains the - // raw 16 bit samples for a single channel in little-endian format. The - // creation sample rate is 16KHz. - FilePath audio_file; - ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &audio_file)); - audio_file = audio_file.Append(kAudioFile1_16b_m_16K); - // Map the entire file in memory. - ReadOnlyMappedFile file_reader(audio_file.value().c_str()); - ASSERT_TRUE(file_reader.is_valid()); - + static const int kSampleRate = 16000; + SineWaveAudioSource source(1, 200.0, kSampleRate); // Compute buffer size for 100ms of audio. - const uint32 kSamples100ms = (16000 / 1000) * 100; - const uint32 kSize100ms = kSamples100ms * 2; + const uint32 kSamples100ms = (kSampleRate / 1000) * 100; + // Restrict SineWaveAudioSource to 100ms of samples. + source.CapSamples(kSamples100ms); AudioOutputStream* oas = audio_man->MakeAudioOutputStream( AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, - 16000, 16, kSamples100ms)); + kSampleRate, 16, kSamples100ms)); ASSERT_TRUE(NULL != oas); EXPECT_TRUE(oas->Open()); - uint32 offset = 0; - const uint32 kMaxStartOffset = file_reader.size() - kSize100ms; + oas->SetVolume(1.0); + oas->Start(&source); // We buffer and play at the same time, buffering happens every ~10ms and the // consuming of the buffer happens every ~100ms. We do 100 buffers which // effectively wrap around the file more than once. - PushSource push_source; for (uint32 ix = 0; ix != 100; ++ix) { - push_source.Write(file_reader.GetChunkAt(offset), kSize100ms); - if (ix == 2) { - // For glitch free, start playing after some buffers are in. - oas->Start(&push_source); - } ::Sleep(10); - offset += kSize100ms; - if (offset > kMaxStartOffset) - offset = 0; + source.Reset(); } // Play a little bit more of the file. @@ -515,8 +434,7 @@ TEST(WinAudioTest, PCMWaveStreamPlayTwice200HzTone44Kss) { AudioParameters::kAudioCDSampleRate, 16, samples_100_ms)); ASSERT_TRUE(NULL != oas); - SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, - 200.0, AudioParameters::kAudioCDSampleRate); + SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate); EXPECT_TRUE(oas->Open()); oas->SetVolume(1.0); @@ -561,8 +479,7 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzToneLowLatency) { 16, n * samples_10_ms)); ASSERT_TRUE(NULL != oas); - SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, - 200.0, sample_rate); + SineWaveAudioSource source(1, 200, sample_rate); bool opened = oas->Open(); if (!opened) { @@ -608,36 +525,37 @@ TEST(WinAudioTest, PCMWaveStreamPendingBytes) { // pending bytes will go down and eventually read zero. InSequence s; - EXPECT_CALL(source, OnMoreData(NotNull(), bytes_100_ms, + EXPECT_CALL(source, OnMoreData(NotNull(), Field(&AudioBuffersState::pending_bytes, 0))) - .WillOnce(Return(bytes_100_ms)); + .WillOnce(Invoke(MockAudioSource::ClearData)); switch (NumberOfWaveOutBuffers()) { case 2: break; // Calls are the same as at end of 3-buffer scheme. case 3: - EXPECT_CALL(source, OnMoreData(NotNull(), bytes_100_ms, + EXPECT_CALL(source, OnMoreData(NotNull(), Field(&AudioBuffersState::pending_bytes, bytes_100_ms))) - .WillOnce(Return(bytes_100_ms)); - EXPECT_CALL(source, OnMoreData(NotNull(), bytes_100_ms, + .WillOnce(Invoke(MockAudioSource::ClearData)); + EXPECT_CALL(source, OnMoreData(NotNull(), Field(&AudioBuffersState::pending_bytes, 2 * bytes_100_ms))) - .WillOnce(Return(bytes_100_ms)); - EXPECT_CALL(source, OnMoreData(NotNull(), bytes_100_ms, + .WillOnce(Invoke(MockAudioSource::ClearData)); + EXPECT_CALL(source, OnMoreData(NotNull(), Field(&AudioBuffersState::pending_bytes, 2 * bytes_100_ms))) .Times(AnyNumber()) .WillRepeatedly(Return(0)); break; default: - ASSERT_TRUE(false) << "Unexpected number of buffers"; + ASSERT_TRUE(false) + << "Unexpected number of buffers: " << NumberOfWaveOutBuffers(); } - EXPECT_CALL(source, OnMoreData(NotNull(), bytes_100_ms, + EXPECT_CALL(source, OnMoreData(NotNull(), Field(&AudioBuffersState::pending_bytes, bytes_100_ms))) .Times(AnyNumber()) .WillRepeatedly(Return(0)); - EXPECT_CALL(source, OnMoreData(NotNull(), bytes_100_ms, + EXPECT_CALL(source, OnMoreData(NotNull(), Field(&AudioBuffersState::pending_bytes, 0))) .Times(AnyNumber()) .WillRepeatedly(Return(0)); @@ -652,19 +570,24 @@ TEST(WinAudioTest, PCMWaveStreamPendingBytes) { // from a potentially remote thread. class SyncSocketSource : public AudioOutputStream::AudioSourceCallback { public: - explicit SyncSocketSource(base::SyncSocket* socket) - : socket_(socket) {} - - ~SyncSocketSource() { + SyncSocketSource(base::SyncSocket* socket, const AudioParameters& params) + : socket_(socket) { + // Setup AudioBus wrapping data we'll receive over the sync socket. + data_size_ = AudioBus::CalculateMemorySize(params); + data_.reset(static_cast<float*>( + base::AlignedAlloc(data_size_, AudioBus::kChannelAlignment))); + audio_bus_ = AudioBus::WrapMemory(params, data_.get()); } + ~SyncSocketSource() {} // AudioSourceCallback::OnMoreData implementation: - virtual uint32 OnMoreData(uint8* dest, - uint32 max_size, - AudioBuffersState buffers_state) { + virtual int OnMoreData(AudioBus* audio_bus, + AudioBuffersState buffers_state) { socket_->Send(&buffers_state, sizeof(buffers_state)); - uint32 got = socket_->Receive(dest, max_size); - return got; + uint32 size = socket_->Receive(data_.get(), data_size_); + DCHECK_EQ(static_cast<size_t>(size) % sizeof(*audio_bus_->channel(0)), 0U); + audio_bus_->CopyTo(audio_bus); + return audio_bus_->frames(); } // AudioSourceCallback::OnError implementation: virtual void OnError(AudioOutputStream* stream, int code) { @@ -672,11 +595,16 @@ class SyncSocketSource : public AudioOutputStream::AudioSourceCallback { private: base::SyncSocket* socket_; + int data_size_; + scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> data_; + scoped_ptr<AudioBus> audio_bus_; }; struct SyncThreadContext { base::SyncSocket* socket; int sample_rate; + int channels; + int frames; double sine_freq; uint32 packet_size_bytes; }; @@ -690,24 +618,26 @@ struct SyncThreadContext { DWORD __stdcall SyncSocketThread(void* context) { SyncThreadContext& ctx = *(reinterpret_cast<SyncThreadContext*>(context)); - const int kTwoSecBytes = - AudioParameters::kAudioCDSampleRate * 2 * sizeof(uint16); // NOLINT - uint8* buffer = new uint8[kTwoSecBytes]; - SineWaveAudioSource sine(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, - 1, ctx.sine_freq, ctx.sample_rate); - sine.OnMoreData(buffer, kTwoSecBytes, AudioBuffersState()); + // Setup AudioBus wrapping data we'll pass over the sync socket. + scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> data(static_cast<float*>( + base::AlignedAlloc(ctx.packet_size_bytes, AudioBus::kChannelAlignment))); + scoped_ptr<AudioBus> audio_bus = AudioBus::WrapMemory( + ctx.channels, ctx.frames, data.get()); + + SineWaveAudioSource sine(1, ctx.sine_freq, ctx.sample_rate); + const int kTwoSecFrames = ctx.sample_rate * 2; AudioBuffersState buffers_state; int times = 0; - for (int ix = 0; ix < kTwoSecBytes; ix += ctx.packet_size_bytes) { + for (int ix = 0; ix < kTwoSecFrames; ix += ctx.frames) { if (ctx.socket->Receive(&buffers_state, sizeof(buffers_state)) == 0) break; if ((times > 0) && (buffers_state.pending_bytes < 1000)) __debugbreak(); - ctx.socket->Send(&buffer[ix], ctx.packet_size_bytes); + sine.OnMoreData(audio_bus.get(), buffers_state); + ctx.socket->Send(data.get(), ctx.packet_size_bytes); ++times; } - delete buffer; return 0; } @@ -726,11 +656,13 @@ TEST(WinAudioTest, SyncSocketBasic) { return; } - int sample_rate = AudioParameters::kAudioCDSampleRate; - const uint32 kSamples20ms = sample_rate / 50; - AudioOutputStream* oas = audio_man->MakeAudioOutputStream( - AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, - CHANNEL_LAYOUT_MONO, sample_rate, 16, kSamples20ms)); + static const int sample_rate = AudioParameters::kAudioCDSampleRate; + static const uint32 kSamples20ms = sample_rate / 50; + AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, + CHANNEL_LAYOUT_MONO, sample_rate, 16, kSamples20ms); + + + AudioOutputStream* oas = audio_man->MakeAudioOutputStream(params); ASSERT_TRUE(NULL != oas); ASSERT_TRUE(oas->Open()); @@ -738,12 +670,14 @@ TEST(WinAudioTest, SyncSocketBasic) { base::SyncSocket sockets[2]; ASSERT_TRUE(base::SyncSocket::CreatePair(&sockets[0], &sockets[1])); - SyncSocketSource source(&sockets[0]); + SyncSocketSource source(&sockets[0], params); SyncThreadContext thread_context; - thread_context.sample_rate = sample_rate; + thread_context.sample_rate = params.sample_rate(); thread_context.sine_freq = 200.0; - thread_context.packet_size_bytes = kSamples20ms * 2; + thread_context.packet_size_bytes = AudioBus::CalculateMemorySize(params); + thread_context.frames = params.frames_per_buffer(); + thread_context.channels = params.channels(); thread_context.socket = &sockets[1]; HANDLE thread = ::CreateThread(NULL, 0, SyncSocketThread, diff --git a/media/audio/win/waveout_output_win.cc b/media/audio/win/waveout_output_win.cc index 75bfed4..faa9901 100644 --- a/media/audio/win/waveout_output_win.cc +++ b/media/audio/win/waveout_output_win.cc @@ -90,7 +90,8 @@ PCMWaveOutAudioOutputStream::PCMWaveOutAudioOutputStream( volume_(1), channels_(params.channels()), pending_bytes_(0), - waiting_handle_(NULL) { + waiting_handle_(NULL), + audio_bus_(AudioBus::Create(params)) { format_.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; format_.Format.nChannels = params.channels(); format_.Format.nSamplesPerSec = params.sample_rate(); @@ -341,10 +342,17 @@ void PCMWaveOutAudioOutputStream::QueueNextPacket(WAVEHDR *buffer) { uint32 scaled_pending_bytes = pending_bytes_ * channels_ / format_.Format.nChannels; // TODO(sergeyu): Specify correct hardware delay for AudioBuffersState. - uint32 used = callback_->OnMoreData( - reinterpret_cast<uint8*>(buffer->lpData), buffer_size_, - AudioBuffersState(scaled_pending_bytes, 0)); + int frames_filled = callback_->OnMoreData( + audio_bus_.get(), AudioBuffersState(scaled_pending_bytes, 0)); + uint32 used = frames_filled * audio_bus_->channels() * + format_.Format.wBitsPerSample / 8; + if (used <= buffer_size_) { + // Note: If this ever changes to output raw float the data must be clipped + // and sanitized since it may come from an untrusted source such as NaCl. + audio_bus_->ToInterleaved( + frames_filled, format_.Format.wBitsPerSample / 8, buffer->lpData); + buffer->dwBufferLength = used * format_.Format.nChannels / channels_; if (channels_ > 2 && format_.Format.nChannels == 2) { media::FoldChannels(buffer->lpData, used, diff --git a/media/audio/win/waveout_output_win.h b/media/audio/win/waveout_output_win.h index cbf7219..a62fcfc 100644 --- a/media/audio/win/waveout_output_win.h +++ b/media/audio/win/waveout_output_win.h @@ -130,6 +130,9 @@ class PCMWaveOutAudioOutputStream : public AudioOutputStream { // Lock used to avoid the conflict when callbacks are called simultaneously. base::Lock lock_; + // Container for retrieving data from AudioSourceCallback::OnMoreData(). + scoped_ptr<AudioBus> audio_bus_; + DISALLOW_COPY_AND_ASSIGN(PCMWaveOutAudioOutputStream); }; diff --git a/media/base/audio_bus.h b/media/base/audio_bus.h index ea7e5f8..544f7cb 100644 --- a/media/base/audio_bus.h +++ b/media/base/audio_bus.h @@ -66,7 +66,8 @@ class MEDIA_EXPORT AudioBus { void CopyTo(AudioBus* dest) const; // Returns a raw pointer to the requested channel. Pointer is guaranteed to - // have a 16-byte alignment. + // have a 16-byte alignment. Warning: Do not rely on having sane (i.e. not + // inf, nan, or between [-1.0, 1.0]) values in the channel data. float* channel(int channel) { return channel_data_[channel]; } const float* channel(int channel) const { return channel_data_[channel]; } diff --git a/media/base/limits.h b/media/base/limits.h index 2f5b801..e3b69a6 100644 --- a/media/base/limits.h +++ b/media/base/limits.h @@ -36,6 +36,8 @@ enum { kMaxChannels = 32, kMaxBitsPerSample = 64, kMaxSamplesPerPacket = kMaxSampleRate, + kMaxPacketSizeInBytes = + (kMaxBitsPerSample / 8) * kMaxChannels * kMaxSamplesPerPacket, }; } // namespace limits @@ -1,4 +1,5 @@ include_rules = [ + "+media", "+third_party/skia", "+third_party/WebKit/Source/WebKit/chromium/public", ] diff --git a/ppapi/native_client/src/shared/ppapi_proxy/plugin_ppb_audio.cc b/ppapi/native_client/src/shared/ppapi_proxy/plugin_ppb_audio.cc index 18558e6..d041b29 100644 --- a/ppapi/native_client/src/shared/ppapi_proxy/plugin_ppb_audio.cc +++ b/ppapi/native_client/src/shared/ppapi_proxy/plugin_ppb_audio.cc @@ -12,6 +12,7 @@ #include "native_client/src/include/nacl_scoped_ptr.h" #include "native_client/src/include/portability.h" #include "native_client/src/shared/ppapi_proxy/plugin_globals.h" +#include "native_client/src/shared/ppapi_proxy/plugin_ppb_audio_config.h" #include "native_client/src/shared/ppapi_proxy/plugin_resource.h" #include "native_client/src/shared/ppapi_proxy/utility.h" #include "native_client/src/shared/srpc/nacl_srpc.h" @@ -30,6 +31,10 @@ size_t ceil64k(size_t n) { return (n + 0xFFFF) & (~0xFFFF); } +// Hard coded values from PepperPlatformAudioOutputImpl. +// TODO(dalecurtis): PPAPI shouldn't hard code these values for all clients. +enum { kChannels = 2, kBytesPerSample = 2 }; + } // namespace PluginAudio::PluginAudio() : @@ -42,7 +47,8 @@ PluginAudio::PluginAudio() : thread_id_(), thread_active_(false), user_callback_(NULL), - user_data_(NULL) { + user_data_(NULL), + client_buffer_size_bytes_(0) { DebugPrintf("PluginAudio::PluginAudio\n"); } @@ -53,10 +59,13 @@ PluginAudio::~PluginAudio() { GetInterface()->StopPlayback(resource_); // Unmap the shared memory buffer, if present. if (shm_buffer_) { + audio_bus_.reset(); + client_buffer_.reset(); munmap(shm_buffer_, ceil64k(media::TotalSharedMemorySizeInBytes(shm_size_))); shm_buffer_ = NULL; shm_size_ = 0; + client_buffer_size_bytes_ = 0; } // Close the handles. if (shm_ != -1) { @@ -79,6 +88,8 @@ bool PluginAudio::InitFromBrowserResource(PP_Resource resource) { void PluginAudio::AudioThread(void* self) { PluginAudio* audio = static_cast<PluginAudio*>(self); DebugPrintf("PluginAudio::AudioThread: self=%p\n", self); + const int bytes_per_frame = + sizeof(*(audio->audio_bus_->channel(0))) * audio->audio_bus_->channels(); while (true) { int32_t sync_value; // Block on socket read. @@ -87,14 +98,24 @@ void PluginAudio::AudioThread(void* self) { if ((sizeof(sync_value) != r) || (-1 == sync_value)) break; // Invoke user callback, get next buffer of audio data. - audio->user_callback_(audio->shm_buffer_, - audio->shm_size_, + audio->user_callback_(audio->client_buffer_.get(), + audio->client_buffer_size_bytes_, audio->user_data_); + + // Deinterleave the audio data into the shared memory as float. + audio->audio_bus_->FromInterleaved( + audio->client_buffer_.get(), audio->audio_bus_->frames(), + kBytesPerSample); + // Signal audio backend by writing buffer length at end of buffer. // (Note: NaCl applications will always write the entire buffer.) - media::SetActualDataSizeInBytes(audio->shm_buffer_, - audio->shm_size_, - audio->shm_size_); + // TODO(dalecurtis): Technically this is not the exact size. Due to channel + // padding for alignment, there may be more data available than this. We're + // relying on AudioSyncReader::Read() to parse this with that in mind. + // Rename these methods to Set/GetActualFrameCount(). + media::SetActualDataSizeInBytes( + audio->shm_buffer_, audio->shm_size_, + audio->audio_bus_->frames() * bytes_per_frame); } } @@ -112,6 +133,15 @@ void PluginAudio::StreamCreated(NaClSrpcImcDescType socket, shm, 0); if (MAP_FAILED != shm_buffer_) { + PP_Resource ac = GetInterface()->GetCurrentConfig(resource_); + int frames = PluginAudioConfig::GetInterface()->GetSampleFrameCount(ac); + + audio_bus_ = media::AudioBus::WrapMemory(kChannels, frames, shm_buffer_); + // Setup integer audio buffer for user audio data. + client_buffer_size_bytes_ = + audio_bus_->frames() * audio_bus_->channels() * kBytesPerSample; + client_buffer_.reset(new uint8_t[client_buffer_size_bytes_]); + if (state() == AUDIO_PENDING) { StartAudioThread(); } else { @@ -126,6 +156,7 @@ bool PluginAudio::StartAudioThread() { // clear contents of shm buffer before spinning up audio thread DebugPrintf("PluginAudio::StartAudioThread\n"); memset(shm_buffer_, 0, shm_size_); + memset(client_buffer_.get(), 0, client_buffer_size_bytes_); const struct PP_ThreadFunctions* thread_funcs = GetThreadCreator(); if (NULL == thread_funcs->thread_create || NULL == thread_funcs->thread_join) { diff --git a/ppapi/native_client/src/shared/ppapi_proxy/plugin_ppb_audio.h b/ppapi/native_client/src/shared/ppapi_proxy/plugin_ppb_audio.h index 07d5a7d..28713d0 100644 --- a/ppapi/native_client/src/shared/ppapi_proxy/plugin_ppb_audio.h +++ b/ppapi/native_client/src/shared/ppapi_proxy/plugin_ppb_audio.h @@ -12,6 +12,7 @@ #include "native_client/src/include/ref_counted.h" #include "native_client/src/shared/ppapi_proxy/plugin_resource.h" #include "native_client/src/shared/srpc/nacl_srpc.h" +#include "media/base/audio_bus.h" #include "ppapi/c/pp_resource.h" #include "ppapi/c/ppb_audio.h" @@ -59,6 +60,11 @@ class PluginAudio : public PluginResource { bool thread_active_; PPB_Audio_Callback user_callback_; void* user_data_; + // AudioBus for shuttling data across the shared memory. + scoped_ptr<media::AudioBus> audio_bus_; + // Internal buffer for client's integer audio data. + int client_buffer_size_bytes_; + scoped_array<uint8_t> client_buffer_; IMPLEMENT_RESOURCE(PluginAudio); NACL_DISALLOW_COPY_AND_ASSIGN(PluginAudio); diff --git a/ppapi/proxy/ppb_audio_proxy.cc b/ppapi/proxy/ppb_audio_proxy.cc index 2bd5c89..2f91fc7 100644 --- a/ppapi/proxy/ppb_audio_proxy.cc +++ b/ppapi/proxy/ppb_audio_proxy.cc @@ -323,6 +323,8 @@ void PPB_Audio_Proxy::OnMsgNotifyAudioStreamCreated( IPC::PlatformFileForTransitToPlatformFile(socket_handle.descriptor())); base::SharedMemory temp_mem(handle.shmem(), false); } else { + EnterResourceNoLock<PPB_AudioConfig_API> config( + static_cast<Audio*>(enter.object())->GetCurrentConfig(), true); // See the comment above about how we must call // TotalSharedMemorySizeInBytes to get the actual size of the buffer. Here, // we must call PacketSizeInBytes to get back the size of the audio buffer, @@ -330,7 +332,8 @@ void PPB_Audio_Proxy::OnMsgNotifyAudioStreamCreated( static_cast<Audio*>(enter.object())->SetStreamInfo( enter.resource()->pp_instance(), handle.shmem(), media::PacketSizeInBytes(handle.size()), - IPC::PlatformFileForTransitToPlatformFile(socket_handle.descriptor())); + IPC::PlatformFileForTransitToPlatformFile(socket_handle.descriptor()), + config.object()->GetSampleFrameCount()); } } diff --git a/ppapi/shared_impl/ppb_audio_shared.cc b/ppapi/shared_impl/ppb_audio_shared.cc index d131bb3..f275559 100644 --- a/ppapi/shared_impl/ppb_audio_shared.cc +++ b/ppapi/shared_impl/ppb_audio_shared.cc @@ -8,6 +8,10 @@ #include "media/audio/shared_memory_util.h" #include "ppapi/shared_impl/ppapi_globals.h" +// Hard coded values from PepperPlatformAudioOutputImpl. +// TODO(dalecurtis): PPAPI shouldn't hard code these values for all clients. +enum { kChannels = 2, kBytesPerSample = 2 }; + namespace ppapi { #if defined(OS_NACL) @@ -25,7 +29,8 @@ PPB_Audio_Shared::PPB_Audio_Shared() thread_active_(false), #endif callback_(NULL), - user_data_(NULL) { + user_data_(NULL), + client_buffer_size_bytes_(0) { } PPB_Audio_Shared::~PPB_Audio_Shared() { @@ -67,7 +72,8 @@ void PPB_Audio_Shared::SetStreamInfo( PP_Instance instance, base::SharedMemoryHandle shared_memory_handle, size_t shared_memory_size, - base::SyncSocket::Handle socket_handle) { + base::SyncSocket::Handle socket_handle, + int sample_frame_count) { socket_.reset(new base::CancelableSyncSocket(socket_handle)); shared_memory_.reset(new base::SharedMemory(shared_memory_handle, false)); shared_memory_size_ = shared_memory_size; @@ -76,6 +82,13 @@ void PPB_Audio_Shared::SetStreamInfo( media::TotalSharedMemorySizeInBytes(shared_memory_size_))) { PpapiGlobals::Get()->LogWithSource(instance, PP_LOGLEVEL_WARNING, "", "Failed to map shared memory for PPB_Audio_Shared."); + } else { + audio_bus_ = media::AudioBus::WrapMemory( + kChannels, sample_frame_count, shared_memory_->memory()); + // Setup integer audio buffer for user audio data. + client_buffer_size_bytes_ = + audio_bus_->frames() * audio_bus_->channels() * kBytesPerSample; + client_buffer_.reset(new uint8_t[client_buffer_size_bytes_]); } StartThread(); @@ -83,12 +96,14 @@ void PPB_Audio_Shared::SetStreamInfo( void PPB_Audio_Shared::StartThread() { // Don't start the thread unless all our state is set up correctly. - if (!playing_ || !callback_ || !socket_.get() || !shared_memory_->memory()) + if (!playing_ || !callback_ || !socket_.get() || !shared_memory_->memory() || + !audio_bus_.get() || !client_buffer_.get()) return; // Clear contents of shm buffer before starting audio thread. This will // prevent a burst of static if for some reason the audio thread doesn't // start up quickly enough. memset(shared_memory_->memory(), 0, shared_memory_size_); + memset(client_buffer_.get(), 0, client_buffer_size_bytes_); #if !defined(OS_NACL) DCHECK(!audio_thread_.get()); audio_thread_.reset(new base::DelegateSimpleThread( @@ -140,16 +155,26 @@ void PPB_Audio_Shared::CallRun(void* self) { void PPB_Audio_Shared::Run() { int pending_data; - void* buffer = shared_memory_->memory(); + const int bytes_per_frame = + sizeof(*audio_bus_->channel(0)) * audio_bus_->channels(); while (sizeof(pending_data) == socket_->Receive(&pending_data, sizeof(pending_data)) && pending_data != media::kPauseMark) { - callback_(buffer, shared_memory_size_, user_data_); + callback_(client_buffer_.get(), client_buffer_size_bytes_, user_data_); + + // Deinterleave the audio data into the shared memory as float. + audio_bus_->FromInterleaved( + client_buffer_.get(), audio_bus_->frames(), kBytesPerSample); // Let the host know we are done. + // TODO(dalecurtis): Technically this is not the exact size. Due to channel + // padding for alignment, there may be more data available than this. We're + // relying on AudioSyncReader::Read() to parse this with that in mind. + // Rename these methods to Set/GetActualFrameCount(). media::SetActualDataSizeInBytes( - shared_memory_.get(), shared_memory_size_, shared_memory_size_); + shared_memory_.get(), shared_memory_size_, + audio_bus_->frames() * bytes_per_frame); } } diff --git a/ppapi/shared_impl/ppb_audio_shared.h b/ppapi/shared_impl/ppb_audio_shared.h index ed3a5c2..0b6afd4 100644 --- a/ppapi/shared_impl/ppb_audio_shared.h +++ b/ppapi/shared_impl/ppb_audio_shared.h @@ -9,6 +9,7 @@ #include "base/shared_memory.h" #include "base/sync_socket.h" #include "base/threading/simple_thread.h" +#include "media/base/audio_bus.h" #include "ppapi/c/ppb_audio.h" #include "ppapi/shared_impl/resource.h" #include "ppapi/thunk/ppb_audio_api.h" @@ -53,7 +54,8 @@ class PPAPI_SHARED_EXPORT PPB_Audio_Shared void SetStreamInfo(PP_Instance instance, base::SharedMemoryHandle shared_memory_handle, size_t shared_memory_size, - base::SyncSocket::Handle socket_handle); + base::SyncSocket::Handle socket_handle, + int sample_frame_count); #if defined(OS_NACL) // NaCl has a special API for IRT code to create threads that can call back @@ -102,6 +104,13 @@ class PPAPI_SHARED_EXPORT PPB_Audio_Shared // User data pointer passed verbatim to the callback function. void* user_data_; + // AudioBus for shuttling data across the shared memory. + scoped_ptr<media::AudioBus> audio_bus_; + + // Internal buffer for client's integer audio data. + int client_buffer_size_bytes_; + scoped_array<uint8_t> client_buffer_; + DISALLOW_COPY_AND_ASSIGN(PPB_Audio_Shared); }; diff --git a/webkit/plugins/ppapi/ppb_audio_impl.cc b/webkit/plugins/ppapi/ppb_audio_impl.cc index 96504aa..aea9005 100644 --- a/webkit/plugins/ppapi/ppb_audio_impl.cc +++ b/webkit/plugins/ppapi/ppb_audio_impl.cc @@ -30,7 +30,8 @@ namespace ppapi { PPB_Audio_Impl::PPB_Audio_Impl(PP_Instance instance) : Resource(::ppapi::OBJECT_IS_IMPL, instance), - audio_(NULL) { + audio_(NULL), + sample_frame_count_(0) { } PPB_Audio_Impl::~PPB_Audio_Impl() { @@ -81,6 +82,7 @@ bool PPB_Audio_Impl::Init(PP_Resource config, audio_ = plugin_delegate->CreateAudioOutput( enter.object()->GetSampleRate(), enter.object()->GetSampleFrameCount(), this); + sample_frame_count_ = enter.object()->GetSampleFrameCount(); return audio_ != NULL; } @@ -153,7 +155,7 @@ void PPB_Audio_Impl::OnSetStreamInfo( size_t shared_memory_size, base::SyncSocket::Handle socket_handle) { SetStreamInfo(pp_instance(), shared_memory_handle, shared_memory_size, - socket_handle); + socket_handle, sample_frame_count_); } } // namespace ppapi diff --git a/webkit/plugins/ppapi/ppb_audio_impl.h b/webkit/plugins/ppapi/ppb_audio_impl.h index be41e36..9f9c35b 100644 --- a/webkit/plugins/ppapi/ppb_audio_impl.h +++ b/webkit/plugins/ppapi/ppb_audio_impl.h @@ -76,6 +76,9 @@ class PPB_Audio_Impl : public ::ppapi::Resource, // own this pointer but are responsible for calling Shutdown on it. PluginDelegate::PlatformAudioOutput* audio_; + // Track frame count for passing on to PPB_Audio_Shared::SetStreamInfo(). + int sample_frame_count_; + DISALLOW_COPY_AND_ASSIGN(PPB_Audio_Impl); }; |