diff --git a/content/browser/media/capture/web_contents_audio_input_stream_unittest.cc b/content/browser/media/capture/web_contents_audio_input_stream_unittest.cc index c9afbf1e0f9a17..72f6109029d1b6 100644 --- a/content/browser/media/capture/web_contents_audio_input_stream_unittest.cc +++ b/content/browser/media/capture/web_contents_audio_input_stream_unittest.cc @@ -161,9 +161,11 @@ class MockAudioInputCallback : public AudioInputStream::AudioInputCallback { public: MockAudioInputCallback() {} - MOCK_METHOD5(OnData, void(AudioInputStream* stream, const uint8* src, - uint32 size, uint32 hardware_delay_bytes, - double volume)); + MOCK_METHOD4(OnData, + void(AudioInputStream* stream, + const media::AudioBus* src, + uint32 hardware_delay_bytes, + double volume)); MOCK_METHOD1(OnError, void(AudioInputStream* stream)); private: @@ -238,7 +240,7 @@ class WebContentsAudioInputStreamTest : public testing::Test { static_cast(NULL))) .RetiresOnSaturation(); - EXPECT_CALL(mock_input_callback_, OnData(NotNull(), NotNull(), _, _, _)) + EXPECT_CALL(mock_input_callback_, OnData(NotNull(), NotNull(), _, _)) .WillRepeatedly( InvokeWithoutArgs(&on_data_event_, &base::WaitableEvent::Signal)); diff --git a/content/browser/renderer_host/media/audio_input_renderer_host.cc b/content/browser/renderer_host/media/audio_input_renderer_host.cc index e5d4f78db9f036..2dcc56d0918090 100644 --- a/content/browser/renderer_host/media/audio_input_renderer_host.cc +++ b/content/browser/renderer_host/media/audio_input_renderer_host.cc @@ -17,6 +17,7 @@ #include "content/browser/renderer_host/media/audio_input_sync_writer.h" #include "content/browser/renderer_host/media/media_stream_manager.h" #include "media/audio/audio_manager_base.h" +#include "media/base/audio_bus.h" namespace content { @@ -113,8 +114,7 @@ void AudioInputRendererHost::OnError(media::AudioInputController* controller, } void AudioInputRendererHost::OnData(media::AudioInputController* controller, - const uint8* data, - uint32 size) { + const media::AudioBus* data) { NOTREACHED() << "Only low-latency mode is supported."; } @@ -292,8 +292,9 @@ void AudioInputRendererHost::OnCreateStream( // Create a new AudioEntry structure. scoped_ptr entry(new AudioEntry()); - const uint32 segment_size = (sizeof(media::AudioInputBufferParameters) + - audio_params.GetBytesPerBuffer()); + const uint32 segment_size = + (sizeof(media::AudioInputBufferParameters) + + media::AudioBus::CalculateMemorySize(audio_params)); entry->shared_memory_segment_count = config.shared_memory_count; // Create the shared memory and share it with the renderer process @@ -307,9 +308,8 @@ void AudioInputRendererHost::OnCreateStream( return; } - scoped_ptr writer( - new AudioInputSyncWriter(&entry->shared_memory, - entry->shared_memory_segment_count)); + scoped_ptr writer(new AudioInputSyncWriter( + &entry->shared_memory, entry->shared_memory_segment_count, audio_params)); if (!writer->Init()) { SendErrorMessage(stream_id, SYNC_WRITER_INIT_FAILED); diff --git a/content/browser/renderer_host/media/audio_input_renderer_host.h b/content/browser/renderer_host/media/audio_input_renderer_host.h index 75a78e8ad78649..7bd6692eabb28a 100644 --- a/content/browser/renderer_host/media/audio_input_renderer_host.h +++ b/content/browser/renderer_host/media/audio_input_renderer_host.h @@ -117,8 +117,7 @@ class CONTENT_EXPORT AudioInputRendererHost virtual void OnError(media::AudioInputController* controller, media::AudioInputController::ErrorCode error_code) OVERRIDE; virtual void OnData(media::AudioInputController* controller, - const uint8* data, - uint32 size) OVERRIDE; + const media::AudioBus* data) OVERRIDE; virtual void OnLog(media::AudioInputController* controller, const std::string& message) OVERRIDE; diff --git a/content/browser/renderer_host/media/audio_input_sync_writer.cc b/content/browser/renderer_host/media/audio_input_sync_writer.cc index 99b91b7b9f6b8f..a043500a716a49 100644 --- a/content/browser/renderer_host/media/audio_input_sync_writer.cc +++ b/content/browser/renderer_host/media/audio_input_sync_writer.cc @@ -9,19 +9,36 @@ #include "base/memory/shared_memory.h" #include "content/browser/renderer_host/media/media_stream_manager.h" +using media::AudioBus; + namespace content { -AudioInputSyncWriter::AudioInputSyncWriter( - base::SharedMemory* shared_memory, - int shared_memory_segment_count) +AudioInputSyncWriter::AudioInputSyncWriter(base::SharedMemory* shared_memory, + int shared_memory_segment_count, + const media::AudioParameters& params) : shared_memory_(shared_memory), shared_memory_segment_count_(shared_memory_segment_count), current_segment_id_(0), - creation_time_(base::Time::Now()) { + creation_time_(base::Time::Now()), + audio_bus_memory_size_(AudioBus::CalculateMemorySize(params)) { DCHECK_GT(shared_memory_segment_count, 0); DCHECK_EQ(shared_memory->requested_size() % shared_memory_segment_count, 0u); shared_memory_segment_size_ = shared_memory->requested_size() / shared_memory_segment_count; + DVLOG(1) << "SharedMemory::requested_size: " + << shared_memory->requested_size(); + DVLOG(1) << "shared_memory_segment_count: " << shared_memory_segment_count; + + // Create vector of audio buses by wrapping existing blocks of memory. + uint8* ptr = static_cast(shared_memory_->memory()); + for (int i = 0; i < shared_memory_segment_count; ++i) { + media::AudioInputBuffer* buffer = + reinterpret_cast(ptr); + scoped_ptr audio_bus = + media::AudioBus::WrapMemory(params, buffer->audio); + audio_buses_.push_back(audio_bus.release()); + ptr += shared_memory_segment_size_; + } } AudioInputSyncWriter::~AudioInputSyncWriter() {} @@ -31,10 +48,9 @@ void AudioInputSyncWriter::UpdateRecordedBytes(uint32 bytes) { socket_->Send(&bytes, sizeof(bytes)); } -uint32 AudioInputSyncWriter::Write(const void* data, - uint32 size, - double volume, - bool key_pressed) { +void AudioInputSyncWriter::Write(const media::AudioBus* data, + double volume, + bool key_pressed) { #if !defined(OS_ANDROID) static const base::TimeDelta kLogDelayThreadhold = base::TimeDelta::FromMilliseconds(500); @@ -59,19 +75,22 @@ uint32 AudioInputSyncWriter::Write(const void* data, last_write_time_ = base::Time::Now(); #endif + // Write audio parameters to shared memory. uint8* ptr = static_cast(shared_memory_->memory()); ptr += current_segment_id_ * shared_memory_segment_size_; media::AudioInputBuffer* buffer = reinterpret_cast(ptr); buffer->params.volume = volume; - buffer->params.size = size; + buffer->params.size = audio_bus_memory_size_; buffer->params.key_pressed = key_pressed; - memcpy(buffer->audio, data, size); + + // Copy data from the native audio layer into shared memory using pre- + // allocated audio buses. + media::AudioBus* audio_bus = audio_buses_[current_segment_id_]; + data->CopyTo(audio_bus); if (++current_segment_id_ >= shared_memory_segment_count_) current_segment_id_ = 0; - - return size; } void AudioInputSyncWriter::Close() { diff --git a/content/browser/renderer_host/media/audio_input_sync_writer.h b/content/browser/renderer_host/media/audio_input_sync_writer.h index 75b2bbf1e2efba..424d7907f11326 100644 --- a/content/browser/renderer_host/media/audio_input_sync_writer.h +++ b/content/browser/renderer_host/media/audio_input_sync_writer.h @@ -5,10 +5,13 @@ #ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_AUDIO_INPUT_SYNC_WRITER_H_ #define CONTENT_BROWSER_RENDERER_HOST_MEDIA_AUDIO_INPUT_SYNC_WRITER_H_ +#include "base/memory/scoped_vector.h" #include "base/process/process.h" #include "base/sync_socket.h" #include "base/time/time.h" #include "media/audio/audio_input_controller.h" +#include "media/audio/audio_parameters.h" +#include "media/base/audio_bus.h" #if defined(OS_POSIX) #include "base/file_descriptor_posix.h" @@ -26,16 +29,16 @@ namespace content { class AudioInputSyncWriter : public media::AudioInputController::SyncWriter { public: explicit AudioInputSyncWriter(base::SharedMemory* shared_memory, - int shared_memory_segment_count); + int shared_memory_segment_count, + const media::AudioParameters& params); virtual ~AudioInputSyncWriter(); - // media::AudioOutputController::SyncWriter implementation. + // media::AudioInputController::SyncWriter implementation. virtual void UpdateRecordedBytes(uint32 bytes) OVERRIDE; - virtual uint32 Write(const void* data, - uint32 size, - double volume, - bool key_pressed) OVERRIDE; + virtual void Write(const media::AudioBus* data, + double volume, + bool key_pressed) OVERRIDE; virtual void Close() OVERRIDE; bool Init(); @@ -65,6 +68,13 @@ class AudioInputSyncWriter : public media::AudioInputController::SyncWriter { // The time of the last Write call. base::Time last_write_time_; + // Size in bytes of each audio bus. + const int audio_bus_memory_size_; + + // Vector of audio buses allocated during construction and deleted in the + // destructor. + ScopedVector audio_buses_; + DISALLOW_IMPLICIT_CONSTRUCTORS(AudioInputSyncWriter); }; diff --git a/content/browser/speech/speech_recognition_browsertest.cc b/content/browser/speech/speech_recognition_browsertest.cc index e7348431c7291c..5871aa193f8a6d 100644 --- a/content/browser/speech/speech_recognition_browsertest.cc +++ b/content/browser/speech/speech_recognition_browsertest.cc @@ -133,6 +133,7 @@ class SpeechRecognitionBrowserTest : size_t buffer_size, bool fill_with_noise) { DCHECK(controller.get()); + const media::AudioParameters& audio_params = controller->audio_parameters(); scoped_ptr audio_buffer(new uint8[buffer_size]); if (fill_with_noise) { for (size_t i = 0; i < buffer_size; ++i) @@ -141,9 +142,13 @@ class SpeechRecognitionBrowserTest : } else { memset(audio_buffer.get(), 0, buffer_size); } - controller->event_handler()->OnData(controller, - audio_buffer.get(), - buffer_size); + + scoped_ptr audio_bus = + media::AudioBus::Create(audio_params); + audio_bus->FromInterleaved(&audio_buffer.get()[0], + audio_bus->frames(), + audio_params.bits_per_sample() / 8); + controller->event_handler()->OnData(controller, audio_bus.get()); } void FeedAudioController(int duration_ms, bool feed_with_noise) { diff --git a/content/browser/speech/speech_recognizer_impl.cc b/content/browser/speech/speech_recognizer_impl.cc index 3ba1f08d88ffa4..e49d30139aa14b 100644 --- a/content/browser/speech/speech_recognizer_impl.cc +++ b/content/browser/speech/speech_recognizer_impl.cc @@ -37,10 +37,10 @@ class SpeechRecognizerImpl::OnDataConverter const AudioParameters& output_params); virtual ~OnDataConverter(); - // Converts input |data| buffer into an AudioChunk where the input format + // Converts input audio |data| bus into an AudioChunk where the input format // is given by |input_parameters_| and the output format by // |output_parameters_|. - scoped_refptr Convert(const uint8* data, size_t size); + scoped_refptr Convert(const AudioBus* data); private: // media::AudioConverter::InputCallback implementation. @@ -132,11 +132,10 @@ SpeechRecognizerImpl::OnDataConverter::~OnDataConverter() { } scoped_refptr SpeechRecognizerImpl::OnDataConverter::Convert( - const uint8* data, size_t size) { - CHECK_EQ(size, static_cast(input_parameters_.GetBytesPerBuffer())); + const AudioBus* data) { + CHECK_EQ(data->frames(), input_parameters_.frames_per_buffer()); - input_bus_->FromInterleaved( - data, input_bus_->frames(), input_parameters_.bits_per_sample() / 8); + data->CopyTo(input_bus_.get()); waiting_for_input_ = true; audio_converter_.Convert(output_bus_.get()); @@ -272,13 +271,10 @@ void SpeechRecognizerImpl::OnError(AudioInputController* controller, } void SpeechRecognizerImpl::OnData(AudioInputController* controller, - const uint8* data, uint32 size) { - if (size == 0) // This could happen when audio capture stops and is normal. - return; - + const AudioBus* data) { // Convert audio from native format to fixed format used by WebSpeech. FSMEventArgs event_args(EVENT_AUDIO_DATA); - event_args.audio_data = audio_converter_->Convert(data, size); + event_args.audio_data = audio_converter_->Convert(data); BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(&SpeechRecognizerImpl::DispatchEvent, diff --git a/content/browser/speech/speech_recognizer_impl.h b/content/browser/speech/speech_recognizer_impl.h index abd3ab433b2ed8..55e07f04ef6ffe 100644 --- a/content/browser/speech/speech_recognizer_impl.h +++ b/content/browser/speech/speech_recognizer_impl.h @@ -16,6 +16,7 @@ #include "net/url_request/url_request_context_getter.h" namespace media { +class AudioBus; class AudioManager; } @@ -132,7 +133,7 @@ class CONTENT_EXPORT SpeechRecognizerImpl virtual void OnError(media::AudioInputController* controller, media::AudioInputController::ErrorCode error_code) OVERRIDE; virtual void OnData(media::AudioInputController* controller, - const uint8* data, uint32 size) OVERRIDE; + const media::AudioBus* data) OVERRIDE; virtual void OnLog(media::AudioInputController* controller, const std::string& message) OVERRIDE {} diff --git a/content/browser/speech/speech_recognizer_impl_unittest.cc b/content/browser/speech/speech_recognizer_impl_unittest.cc index 3709024420c985..13d5eba7dfc14a 100644 --- a/content/browser/speech/speech_recognizer_impl_unittest.cc +++ b/content/browser/speech/speech_recognizer_impl_unittest.cc @@ -13,6 +13,7 @@ #include "media/audio/fake_audio_output_stream.h" #include "media/audio/mock_audio_manager.h" #include "media/audio/test_audio_input_controller_factory.h" +#include "media/base/audio_bus.h" #include "net/base/net_errors.h" #include "net/url_request/test_url_fetcher_factory.h" #include "net/url_request/url_request_status.h" @@ -66,6 +67,13 @@ class SpeechRecognizerImplTest : public SpeechRecognitionEventListener, ChannelLayoutToChannelCount(SpeechRecognizerImpl::kChannelLayout) * SpeechRecognizerImpl::kNumBitsPerAudioSample) / (8 * 1000); audio_packet_.resize(audio_packet_length_bytes); + + const int channels = + ChannelLayoutToChannelCount(SpeechRecognizerImpl::kChannelLayout); + bytes_per_sample_ = SpeechRecognizerImpl::kNumBitsPerAudioSample / 8; + const int frames = audio_packet_length_bytes / channels / bytes_per_sample_; + audio_bus_ = media::AudioBus::Create(channels, frames); + audio_bus_->Zero(); } void CheckEventsConsistency() { @@ -147,10 +155,17 @@ class SpeechRecognizerImplTest : public SpeechRecognitionEventListener, AudioInputController::set_factory_for_testing(NULL); } + void CopyPacketToAudioBus() { + // Copy the created signal into an audio bus in a deinterleaved format. + audio_bus_->FromInterleaved( + &audio_packet_[0], audio_bus_->frames(), bytes_per_sample_); + } + void FillPacketWithTestWaveform() { // Fill the input with a simple pattern, a 125Hz sawtooth waveform. for (size_t i = 0; i < audio_packet_.size(); ++i) audio_packet_[i] = static_cast(i); + CopyPacketToAudioBus(); } void FillPacketWithNoise() { @@ -160,6 +175,7 @@ class SpeechRecognizerImplTest : public SpeechRecognitionEventListener, value += factor; audio_packet_[i] = value % 100; } + CopyPacketToAudioBus(); } protected: @@ -178,6 +194,8 @@ class SpeechRecognizerImplTest : public SpeechRecognitionEventListener, net::TestURLFetcherFactory url_fetcher_factory_; TestAudioInputControllerFactory audio_input_controller_factory_; std::vector audio_packet_; + scoped_ptr audio_bus_; + int bytes_per_sample_; float volume_; float noise_volume_; }; @@ -222,8 +240,7 @@ TEST_F(SpeechRecognizerImplTest, StopWithData) { // full recording to complete. const size_t kNumChunks = 5; for (size_t i = 0; i < kNumChunks; ++i) { - controller->event_handler()->OnData(controller, &audio_packet_[0], - audio_packet_.size()); + controller->event_handler()->OnData(controller, audio_bus_.get()); base::MessageLoop::current()->RunUntilIdle(); net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0); ASSERT_TRUE(fetcher); @@ -264,8 +281,7 @@ TEST_F(SpeechRecognizerImplTest, CancelWithData) { TestAudioInputController* controller = audio_input_controller_factory_.controller(); ASSERT_TRUE(controller); - controller->event_handler()->OnData(controller, &audio_packet_[0], - audio_packet_.size()); + controller->event_handler()->OnData(controller, audio_bus_.get()); base::MessageLoop::current()->RunUntilIdle(); recognizer_->AbortRecognition(); base::MessageLoop::current()->RunUntilIdle(); @@ -285,8 +301,7 @@ TEST_F(SpeechRecognizerImplTest, ConnectionError) { TestAudioInputController* controller = audio_input_controller_factory_.controller(); ASSERT_TRUE(controller); - controller->event_handler()->OnData(controller, &audio_packet_[0], - audio_packet_.size()); + controller->event_handler()->OnData(controller, audio_bus_.get()); base::MessageLoop::current()->RunUntilIdle(); net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0); ASSERT_TRUE(fetcher); @@ -323,8 +338,7 @@ TEST_F(SpeechRecognizerImplTest, ServerError) { TestAudioInputController* controller = audio_input_controller_factory_.controller(); ASSERT_TRUE(controller); - controller->event_handler()->OnData(controller, &audio_packet_[0], - audio_packet_.size()); + controller->event_handler()->OnData(controller, audio_bus_.get()); base::MessageLoop::current()->RunUntilIdle(); net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0); ASSERT_TRUE(fetcher); @@ -377,8 +391,7 @@ TEST_F(SpeechRecognizerImplTest, AudioControllerErrorWithData) { TestAudioInputController* controller = audio_input_controller_factory_.controller(); ASSERT_TRUE(controller); - controller->event_handler()->OnData(controller, &audio_packet_[0], - audio_packet_.size()); + controller->event_handler()->OnData(controller, audio_bus_.get()); controller->event_handler()->OnError(controller, AudioInputController::UNKNOWN_ERROR); base::MessageLoop::current()->RunUntilIdle(); @@ -403,8 +416,7 @@ TEST_F(SpeechRecognizerImplTest, NoSpeechCallbackIssued) { GoogleOneShotRemoteEngine::kAudioPacketIntervalMs + 1; // The vector is already filled with zero value samples on create. for (int i = 0; i < num_packets; ++i) { - controller->event_handler()->OnData(controller, &audio_packet_[0], - audio_packet_.size()); + controller->event_handler()->OnData(controller, audio_bus_.get()); } base::MessageLoop::current()->RunUntilIdle(); EXPECT_TRUE(recognition_started_); @@ -432,14 +444,12 @@ TEST_F(SpeechRecognizerImplTest, NoSpeechCallbackNotIssued) { // The vector is already filled with zero value samples on create. for (int i = 0; i < num_packets / 2; ++i) { - controller->event_handler()->OnData(controller, &audio_packet_[0], - audio_packet_.size()); + controller->event_handler()->OnData(controller, audio_bus_.get()); } FillPacketWithTestWaveform(); for (int i = 0; i < num_packets / 2; ++i) { - controller->event_handler()->OnData(controller, &audio_packet_[0], - audio_packet_.size()); + controller->event_handler()->OnData(controller, audio_bus_.get()); } base::MessageLoop::current()->RunUntilIdle(); @@ -470,21 +480,18 @@ TEST_F(SpeechRecognizerImplTest, SetInputVolumeCallback) { GoogleOneShotRemoteEngine::kAudioPacketIntervalMs; FillPacketWithNoise(); for (int i = 0; i < num_packets; ++i) { - controller->event_handler()->OnData(controller, &audio_packet_[0], - audio_packet_.size()); + controller->event_handler()->OnData(controller, audio_bus_.get()); } base::MessageLoop::current()->RunUntilIdle(); EXPECT_EQ(-1.0f, volume_); // No audio volume set yet. // The vector is already filled with zero value samples on create. - controller->event_handler()->OnData(controller, &audio_packet_[0], - audio_packet_.size()); + controller->event_handler()->OnData(controller, audio_bus_.get()); base::MessageLoop::current()->RunUntilIdle(); EXPECT_FLOAT_EQ(0.74939233f, volume_); FillPacketWithTestWaveform(); - controller->event_handler()->OnData(controller, &audio_packet_[0], - audio_packet_.size()); + controller->event_handler()->OnData(controller, audio_bus_.get()); base::MessageLoop::current()->RunUntilIdle(); EXPECT_NEAR(0.89926866f, volume_, 0.00001f); EXPECT_FLOAT_EQ(0.75071919f, noise_volume_); diff --git a/media/audio/agc_audio_stream.h b/media/audio/agc_audio_stream.h index b289a0b15e9c76..940d96412c2ba4 100644 --- a/media/audio/agc_audio_stream.h +++ b/media/audio/agc_audio_stream.h @@ -73,12 +73,10 @@ class MEDIA_EXPORT AgcAudioStream : public AudioInterface { AgcAudioStream() : agc_is_enabled_(false), max_volume_(0.0), normalized_volume_(0.0) { - DVLOG(1) << __FUNCTION__; } virtual ~AgcAudioStream() { DCHECK(thread_checker_.CalledOnValidThread()); - DVLOG(1) << __FUNCTION__; } protected: @@ -87,7 +85,6 @@ class MEDIA_EXPORT AgcAudioStream : public AudioInterface { // The timer is only started if AGC mode is first enabled using the // SetAutomaticGainControl() method. void StartAgc() { - DVLOG(1) << "StartAgc()"; DCHECK(thread_checker_.CalledOnValidThread()); if (!agc_is_enabled_ || timer_.IsRunning()) return; @@ -105,7 +102,6 @@ class MEDIA_EXPORT AgcAudioStream : public AudioInterface { // Stops the periodic timer which periodically checks and updates the // current microphone volume level. void StopAgc() { - DVLOG(1) << "StopAgc()"; DCHECK(thread_checker_.CalledOnValidThread()); if (timer_.IsRunning()) timer_.Stop(); diff --git a/media/audio/alsa/alsa_input.cc b/media/audio/alsa/alsa_input.cc index 9cde236b59154f..0bc9f314d45e26 100644 --- a/media/audio/alsa/alsa_input.cc +++ b/media/audio/alsa/alsa_input.cc @@ -43,7 +43,9 @@ AlsaPcmInputStream::AlsaPcmInputStream(AudioManagerBase* audio_manager, mixer_handle_(NULL), mixer_element_handle_(NULL), read_callback_behind_schedule_(false), - weak_factory_(this) {} + audio_bus_(AudioBus::Create(params)), + weak_factory_(this) { +} AlsaPcmInputStream::~AlsaPcmInputStream() {} @@ -208,8 +210,11 @@ void AlsaPcmInputStream::ReadAudio() { int frames_read = wrapper_->PcmReadi(device_handle_, audio_buffer_.get(), params_.frames_per_buffer()); if (frames_read == params_.frames_per_buffer()) { - callback_->OnData(this, audio_buffer_.get(), bytes_per_buffer_, - hardware_delay_bytes, normalized_volume); + audio_bus_->FromInterleaved(audio_buffer_.get(), + audio_bus_->frames(), + params_.bits_per_sample() / 8); + callback_->OnData( + this, audio_bus_.get(), hardware_delay_bytes, normalized_volume); } else { LOG(WARNING) << "PcmReadi returning less than expected frames: " << frames_read << " vs. " << params_.frames_per_buffer() diff --git a/media/audio/alsa/alsa_input.h b/media/audio/alsa/alsa_input.h index 90c388382a0b68..c26f3e2b66eb22 100644 --- a/media/audio/alsa/alsa_input.h +++ b/media/audio/alsa/alsa_input.h @@ -82,6 +82,7 @@ class AlsaPcmInputStream : public AgcAudioStream { snd_mixer_elem_t* mixer_element_handle_; // Handle to the capture element. scoped_ptr audio_buffer_; // Buffer used for reading audio data. bool read_callback_behind_schedule_; + scoped_ptr audio_bus_; // NOTE: Weak pointers must be invalidated before all other member variables. base::WeakPtrFactory weak_factory_; diff --git a/media/audio/android/audio_android_unittest.cc b/media/audio/android/audio_android_unittest.cc index 06df7f09aebff1..a356d9c25deb6f 100644 --- a/media/audio/android/audio_android_unittest.cc +++ b/media/audio/android/audio_android_unittest.cc @@ -148,10 +148,9 @@ std::ostream& operator<<(std::ostream& os, const AudioParameters& params) { // Gmock implementation of AudioInputStream::AudioInputCallback. class MockAudioInputCallback : public AudioInputStream::AudioInputCallback { public: - MOCK_METHOD5(OnData, + MOCK_METHOD4(OnData, void(AudioInputStream* stream, - const uint8* src, - uint32 size, + const AudioBus* src, uint32 hardware_delay_bytes, double volume)); MOCK_METHOD1(OnError, void(AudioInputStream* stream)); @@ -264,14 +263,19 @@ class FileAudioSink : public AudioInputStream::AudioInputCallback { // AudioInputStream::AudioInputCallback implementation. virtual void OnData(AudioInputStream* stream, - const uint8* src, - uint32 size, + const AudioBus* src, uint32 hardware_delay_bytes, double volume) OVERRIDE { + const int num_samples = src->frames() * src->channels(); + scoped_ptr interleaved(new int16[num_samples]); + const int bytes_per_sample = sizeof(*interleaved); + src->ToInterleaved(src->frames(), bytes_per_sample, interleaved.get()); + // Store data data in a temporary buffer to avoid making blocking // fwrite() calls in the audio callback. The complete buffer will be // written to file in the destructor. - if (!buffer_->Append(src, size)) + const int size = bytes_per_sample * num_samples; + if (!buffer_->Append((const uint8*)interleaved.get(), size)) event_->Signal(); } @@ -307,13 +311,19 @@ class FullDuplexAudioSinkSource // AudioInputStream::AudioInputCallback implementation virtual void OnData(AudioInputStream* stream, - const uint8* src, - uint32 size, + const AudioBus* src, uint32 hardware_delay_bytes, double volume) OVERRIDE { const base::TimeTicks now_time = base::TimeTicks::Now(); const int diff = (now_time - previous_time_).InMilliseconds(); + EXPECT_EQ(params_.bits_per_sample(), 16); + const int num_samples = src->frames() * src->channels(); + scoped_ptr interleaved(new int16[num_samples]); + const int bytes_per_sample = sizeof(*interleaved); + src->ToInterleaved(src->frames(), bytes_per_sample, interleaved.get()); + const int size = bytes_per_sample * num_samples; + base::AutoLock lock(lock_); if (diff > 1000) { started_ = true; @@ -334,7 +344,7 @@ class FullDuplexAudioSinkSource // Append new data to the FIFO and extend the size if the max capacity // was exceeded. Flush the FIFO when extended just in case. - if (!fifo_->Append(src, size)) { + if (!fifo_->Append((const uint8*)interleaved.get(), size)) { fifo_->set_forward_capacity(2 * fifo_->forward_capacity()); fifo_->Clear(); } @@ -640,14 +650,10 @@ class AudioAndroidInputTest : public AudioAndroidOutputTest, int count = 0; MockAudioInputCallback sink; - EXPECT_CALL(sink, - OnData(audio_input_stream_, - NotNull(), - params. - GetBytesPerBuffer(), _, _)) + EXPECT_CALL(sink, OnData(audio_input_stream_, NotNull(), _, _)) .Times(AtLeast(num_callbacks)) .WillRepeatedly( - CheckCountAndPostQuitTask(&count, num_callbacks, loop())); + CheckCountAndPostQuitTask(&count, num_callbacks, loop())); EXPECT_CALL(sink, OnError(audio_input_stream_)).Times(0); OpenAndStartAudioInputStreamOnAudioThread(&sink); diff --git a/media/audio/android/audio_record_input.cc b/media/audio/android/audio_record_input.cc index 583239d6502fa9..3f19588b4a6ab7 100644 --- a/media/audio/android/audio_record_input.cc +++ b/media/audio/android/audio_record_input.cc @@ -7,14 +7,18 @@ #include "base/logging.h" #include "jni/AudioRecordInput_jni.h" #include "media/audio/android/audio_manager_android.h" +#include "media/base/audio_bus.h" namespace media { AudioRecordInputStream::AudioRecordInputStream( - AudioManagerAndroid* audio_manager, const AudioParameters& params) + AudioManagerAndroid* audio_manager, + const AudioParameters& params) : audio_manager_(audio_manager), callback_(NULL), - direct_buffer_address_(NULL) { + direct_buffer_address_(NULL), + audio_bus_(media::AudioBus::Create(params)), + bytes_per_sample_(params.bits_per_sample() / 8) { DVLOG(2) << __PRETTY_FUNCTION__; DCHECK(params.IsValid()); j_audio_record_.Reset( @@ -48,10 +52,13 @@ bool AudioRecordInputStream::RegisterAudioRecordInput(JNIEnv* env) { void AudioRecordInputStream::OnData(JNIEnv* env, jobject obj, jint size, jint hardware_delay_bytes) { DCHECK(direct_buffer_address_); + DCHECK_EQ(size, + audio_bus_->frames() * audio_bus_->channels() * bytes_per_sample_); // Passing zero as the volume parameter indicates there is no access to a // hardware volume slider. - callback_->OnData(this, direct_buffer_address_, size, hardware_delay_bytes, - 0.0); + audio_bus_->FromInterleaved( + direct_buffer_address_, audio_bus_->frames(), bytes_per_sample_); + callback_->OnData(this, audio_bus_.get(), hardware_delay_bytes, 0.0); } bool AudioRecordInputStream::Open() { diff --git a/media/audio/android/audio_record_input.h b/media/audio/android/audio_record_input.h index 0a2578b1079375..c240038360b772 100644 --- a/media/audio/android/audio_record_input.h +++ b/media/audio/android/audio_record_input.h @@ -12,6 +12,7 @@ namespace media { +class AudioBus; class AudioManagerAndroid; // Implements PCM audio input support for Android using the Java AudioRecord @@ -64,6 +65,9 @@ class MEDIA_EXPORT AudioRecordInputStream : public AudioInputStream { // Owned by j_audio_record_. uint8* direct_buffer_address_; + scoped_ptr audio_bus_; + int bytes_per_sample_; + DISALLOW_COPY_AND_ASSIGN(AudioRecordInputStream); }; diff --git a/media/audio/android/opensles_input.cc b/media/audio/android/opensles_input.cc index b927edb6f7ddb4..1ef3aaca5ef917 100644 --- a/media/audio/android/opensles_input.cc +++ b/media/audio/android/opensles_input.cc @@ -7,6 +7,7 @@ #include "base/debug/trace_event.h" #include "base/logging.h" #include "media/audio/android/audio_manager_android.h" +#include "media/base/audio_bus.h" #define LOG_ON_FAILURE_AND_RETURN(op, ...) \ do { \ @@ -27,7 +28,8 @@ OpenSLESInputStream::OpenSLESInputStream(AudioManagerAndroid* audio_manager, simple_buffer_queue_(NULL), active_buffer_index_(0), buffer_size_bytes_(0), - started_(false) { + started_(false), + audio_bus_(media::AudioBus::Create(params)) { DVLOG(2) << __PRETTY_FUNCTION__; format_.formatType = SL_DATAFORMAT_PCM; format_.numChannels = static_cast(params.channels()); @@ -295,13 +297,14 @@ void OpenSLESInputStream::ReadBufferQueue() { TRACE_EVENT0("audio", "OpenSLESOutputStream::ReadBufferQueue"); + // Convert from interleaved format to deinterleaved audio bus format. + audio_bus_->FromInterleaved(audio_data_[active_buffer_index_], + audio_bus_->frames(), + format_.bitsPerSample / 8); + // TODO(henrika): Investigate if it is possible to get an accurate // delay estimation. - callback_->OnData(this, - audio_data_[active_buffer_index_], - buffer_size_bytes_, - buffer_size_bytes_, - 0.0); + callback_->OnData(this, audio_bus_.get(), buffer_size_bytes_, 0.0); // Done with this buffer. Send it to device for recording. SLresult err = diff --git a/media/audio/android/opensles_input.h b/media/audio/android/opensles_input.h index cb07d51f78b75b..288ab43425e9e3 100644 --- a/media/audio/android/opensles_input.h +++ b/media/audio/android/opensles_input.h @@ -17,6 +17,7 @@ namespace media { +class AudioBus; class AudioManagerAndroid; // Implements PCM audio input support for Android using the OpenSLES API. @@ -94,6 +95,8 @@ class OpenSLESInputStream : public AudioInputStream { bool started_; + scoped_ptr audio_bus_; + DISALLOW_COPY_AND_ASSIGN(OpenSLESInputStream); }; diff --git a/media/audio/audio_input_controller.cc b/media/audio/audio_input_controller.cc index e74b0a1e75de74..490c62b3c16df6 100644 --- a/media/audio/audio_input_controller.cc +++ b/media/audio/audio_input_controller.cc @@ -202,7 +202,6 @@ void AudioInputController::DoCreate(AudioManager* audio_manager, audio_level_.reset(new media::AudioPowerMonitor( params.sample_rate(), TimeDelta::FromMilliseconds(kPowerMeasurementTimeConstantMilliseconds))); - audio_bus_ = AudioBus::Create(params); audio_params_ = params; #endif @@ -383,8 +382,7 @@ void AudioInputController::DoCheckForNoData() { } void AudioInputController::OnData(AudioInputStream* stream, - const uint8* data, - uint32 size, + const AudioBus* source, uint32 hardware_delay_bytes, double volume) { // Mark data as active to ensure that the periodic calls to @@ -408,7 +406,7 @@ void AudioInputController::OnData(AudioInputStream* stream, // Use SharedMemory and SyncSocket if the client has created a SyncWriter. // Used by all low-latency clients except WebSpeech. if (SharedMemoryAndSyncSocketMode()) { - sync_writer_->Write(data, size, volume, key_pressed); + sync_writer_->Write(source, volume, key_pressed); sync_writer_->UpdateRecordedBytes(hardware_delay_bytes); #if defined(AUDIO_POWER_MONITORING) @@ -424,9 +422,7 @@ void AudioInputController::OnData(AudioInputStream* stream, // Wrap data into an AudioBus to match AudioPowerMonitor::Scan. // TODO(henrika): remove this section when capture side uses AudioBus. // See http://crbug.com/375155 for details. - audio_bus_->FromInterleaved( - data, audio_bus_->frames(), audio_params_.bits_per_sample() / 8); - audio_level_->Scan(*audio_bus_, audio_bus_->frames()); + audio_level_->Scan(*source, source->frames()); // Get current average power level and add it to the log. // Possible range is given by [-inf, 0] dBFS. @@ -445,26 +441,28 @@ void AudioInputController::OnData(AudioInputStream* stream, audio_level_->Reset(); } #endif - return; } // TODO(henrika): Investigate if we can avoid the extra copy here. // (see http://crbug.com/249316 for details). AFAIK, this scope is only // active for WebSpeech clients. - scoped_ptr audio_data(new uint8[size]); - memcpy(audio_data.get(), data, size); + scoped_ptr audio_data = + AudioBus::Create(source->channels(), source->frames()); + source->CopyTo(audio_data.get()); // Ownership of the audio buffer will be with the callback until it is run, // when ownership is passed to the callback function. - task_runner_->PostTask(FROM_HERE, base::Bind( - &AudioInputController::DoOnData, this, base::Passed(&audio_data), size)); + task_runner_->PostTask( + FROM_HERE, + base::Bind( + &AudioInputController::DoOnData, this, base::Passed(&audio_data))); } -void AudioInputController::DoOnData(scoped_ptr data, uint32 size) { +void AudioInputController::DoOnData(scoped_ptr data) { DCHECK(task_runner_->BelongsToCurrentThread()); if (handler_) - handler_->OnData(this, data.get(), size); + handler_->OnData(this, data.get()); } void AudioInputController::DoLogAudioLevel(float level_dbfs) { diff --git a/media/audio/audio_input_controller.h b/media/audio/audio_input_controller.h index 35ec1b77fc72f8..f2771c7e9c2bdf 100644 --- a/media/audio/audio_input_controller.h +++ b/media/audio/audio_input_controller.h @@ -117,8 +117,8 @@ class MEDIA_EXPORT AudioInputController virtual void OnRecording(AudioInputController* controller) = 0; virtual void OnError(AudioInputController* controller, ErrorCode error_code) = 0; - virtual void OnData(AudioInputController* controller, const uint8* data, - uint32 size) = 0; + virtual void OnData(AudioInputController* controller, + const AudioBus* data) = 0; virtual void OnLog(AudioInputController* controller, const std::string& message) = 0; @@ -136,12 +136,10 @@ class MEDIA_EXPORT AudioInputController // soundcard which has been recorded. virtual void UpdateRecordedBytes(uint32 bytes) = 0; - // Write certain amount of data from |data|. This method returns - // number of written bytes. - virtual uint32 Write(const void* data, - uint32 size, - double volume, - bool key_pressed) = 0; + // Write certain amount of data from |data|. + virtual void Write(const AudioBus* data, + double volume, + bool key_pressed) = 0; // Close this synchronous writer. virtual void Close() = 0; @@ -230,8 +228,10 @@ class MEDIA_EXPORT AudioInputController // AudioInputCallback implementation. Threading details depends on the // device-specific implementation. - virtual void OnData(AudioInputStream* stream, const uint8* src, uint32 size, - uint32 hardware_delay_bytes, double volume) OVERRIDE; + virtual void OnData(AudioInputStream* stream, + const AudioBus* source, + uint32 hardware_delay_bytes, + double volume) OVERRIDE; virtual void OnError(AudioInputStream* stream) OVERRIDE; bool SharedMemoryAndSyncSocketMode() const { return sync_writer_ != NULL; } @@ -261,7 +261,7 @@ class MEDIA_EXPORT AudioInputController void DoReportError(); void DoSetVolume(double volume); void DoSetAutomaticGainControl(bool enabled); - void DoOnData(scoped_ptr data, uint32 size); + void DoOnData(scoped_ptr data); void DoLogAudioLevel(float level_dbfs); // Method to check if we get recorded data after a stream was started, @@ -325,7 +325,6 @@ class MEDIA_EXPORT AudioInputController scoped_ptr audio_level_; // We need these to be able to feed data to the AudioPowerMonitor. - scoped_ptr audio_bus_; media::AudioParameters audio_params_; base::TimeTicks last_audio_level_log_time_; #endif diff --git a/media/audio/audio_input_controller_unittest.cc b/media/audio/audio_input_controller_unittest.cc index 4e11e350fc8700..e71232d5730597 100644 --- a/media/audio/audio_input_controller_unittest.cc +++ b/media/audio/audio_input_controller_unittest.cc @@ -53,8 +53,8 @@ class MockAudioInputControllerEventHandler MOCK_METHOD1(OnRecording, void(AudioInputController* controller)); MOCK_METHOD2(OnError, void(AudioInputController* controller, AudioInputController::ErrorCode error_code)); - MOCK_METHOD3(OnData, void(AudioInputController* controller, - const uint8* data, uint32 size)); + MOCK_METHOD2(OnData, + void(AudioInputController* controller, const AudioBus* data)); MOCK_METHOD2(OnLog, void(AudioInputController* controller, const std::string& message)); @@ -117,10 +117,10 @@ TEST_F(AudioInputControllerTest, RecordAndClose) { .Times(Exactly(1)); // OnData() shall be called ten times. - EXPECT_CALL(event_handler, OnData(NotNull(), NotNull(), _)) + EXPECT_CALL(event_handler, OnData(NotNull(), NotNull())) .Times(AtLeast(10)) - .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, - message_loop_.message_loop_proxy())); + .WillRepeatedly(CheckCountAndPostQuitTask( + &count, 10, message_loop_.message_loop_proxy())); scoped_ptr audio_manager(AudioManager::CreateForTesting()); AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout, @@ -163,10 +163,10 @@ TEST_F(AudioInputControllerTest, DISABLED_RecordAndError) { .Times(Exactly(1)); // OnData() shall be called ten times. - EXPECT_CALL(event_handler, OnData(NotNull(), NotNull(), _)) + EXPECT_CALL(event_handler, OnData(NotNull(), NotNull())) .Times(AtLeast(10)) - .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, - message_loop_.message_loop_proxy())); + .WillRepeatedly(CheckCountAndPostQuitTask( + &count, 10, message_loop_.message_loop_proxy())); // OnError() will be called after the data stream stops while the // controller is in a recording state. diff --git a/media/audio/audio_input_device.cc b/media/audio/audio_input_device.cc index f57284c187f7af..bf140cbad4e83b 100644 --- a/media/audio/audio_input_device.cc +++ b/media/audio/audio_input_device.cc @@ -6,6 +6,7 @@ #include "base/basictypes.h" #include "base/bind.h" +#include "base/memory/scoped_vector.h" #include "base/threading/thread_restrictions.h" #include "base/time/time.h" #include "media/audio/audio_manager_base.h" @@ -39,8 +40,9 @@ class AudioInputDevice::AudioThreadCallback private: int current_segment_id_; + ScopedVector audio_buses_; CaptureCallback* capture_callback_; - scoped_ptr audio_bus_; + DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback); }; @@ -273,7 +275,6 @@ AudioInputDevice::AudioThreadCallback::AudioThreadCallback( total_segments), current_segment_id_(0), capture_callback_(capture_callback) { - audio_bus_ = AudioBus::Create(audio_parameters_); } AudioInputDevice::AudioThreadCallback::~AudioThreadCallback() { @@ -281,6 +282,17 @@ AudioInputDevice::AudioThreadCallback::~AudioThreadCallback() { void AudioInputDevice::AudioThreadCallback::MapSharedMemory() { shared_memory_.Map(memory_length_); + + // Create vector of audio buses by wrapping existing blocks of memory. + uint8* ptr = static_cast(shared_memory_.memory()); + for (int i = 0; i < total_segments_; ++i) { + media::AudioInputBuffer* buffer = + reinterpret_cast(ptr); + scoped_ptr audio_bus = + media::AudioBus::WrapMemory(audio_parameters_, buffer->audio); + audio_buses_.push_back(audio_bus.release()); + ptr += segment_length_; + } } void AudioInputDevice::AudioThreadCallback::Process(int pending_data) { @@ -297,21 +309,17 @@ void AudioInputDevice::AudioThreadCallback::Process(int pending_data) { double volume = buffer->params.volume; bool key_pressed = buffer->params.key_pressed; - int audio_delay_milliseconds = pending_data / bytes_per_ms_; - int16* memory = reinterpret_cast(&buffer->audio[0]); - const int bytes_per_sample = sizeof(memory[0]); - - if (++current_segment_id_ >= total_segments_) - current_segment_id_ = 0; - - // Deinterleave each channel and convert to 32-bit floating-point - // with nominal range -1.0 -> +1.0. - audio_bus_->FromInterleaved(memory, audio_bus_->frames(), bytes_per_sample); + // Use pre-allocated audio bus wrapping existing block of shared memory. + media::AudioBus* audio_bus = audio_buses_[current_segment_id_]; // Deliver captured data to the client in floating point format // and update the audio-delay measurement. + int audio_delay_milliseconds = pending_data / bytes_per_ms_; capture_callback_->Capture( - audio_bus_.get(), audio_delay_milliseconds, volume, key_pressed); + audio_bus, audio_delay_milliseconds, volume, key_pressed); + + if (++current_segment_id_ >= total_segments_) + current_segment_id_ = 0; } } // namespace media diff --git a/media/audio/audio_input_unittest.cc b/media/audio/audio_input_unittest.cc index f972e441337cca..0bae9db7c4e779 100644 --- a/media/audio/audio_input_unittest.cc +++ b/media/audio/audio_input_unittest.cc @@ -24,8 +24,7 @@ class TestInputCallback : public AudioInputStream::AudioInputCallback { had_error_(0) { } virtual void OnData(AudioInputStream* stream, - const uint8* data, - uint32 size, + const AudioBus* source, uint32 hardware_delay_bytes, double volume) OVERRIDE { ++callback_count_; diff --git a/media/audio/audio_io.h b/media/audio/audio_io.h index f6151e094e13af..d445584fd6f3c7 100644 --- a/media/audio/audio_io.h +++ b/media/audio/audio_io.h @@ -109,8 +109,9 @@ class MEDIA_EXPORT AudioInputStream { // Called by the audio recorder when a full packet of audio data is // available. This is called from a special audio thread and the // implementation should return as soon as possible. - virtual void OnData(AudioInputStream* stream, const uint8* src, - uint32 size, uint32 hardware_delay_bytes, + virtual void OnData(AudioInputStream* stream, + const AudioBus* source, + uint32 hardware_delay_bytes, double volume) = 0; // There was an error while recording audio. The audio sink cannot be diff --git a/media/audio/audio_low_latency_input_output_unittest.cc b/media/audio/audio_low_latency_input_output_unittest.cc index 2ae700a874bd8d..eefd3800aabd18 100644 --- a/media/audio/audio_low_latency_input_output_unittest.cc +++ b/media/audio/audio_low_latency_input_output_unittest.cc @@ -184,7 +184,7 @@ class FullDuplexAudioSinkSource // AudioInputStream::AudioInputCallback. virtual void OnData(AudioInputStream* stream, - const uint8* src, uint32 size, + const AudioBus* src, uint32 hardware_delay_bytes, double volume) OVERRIDE { base::AutoLock lock(lock_); @@ -203,14 +203,15 @@ class FullDuplexAudioSinkSource ++input_elements_to_write_; } + // TODO(henrika): fix this and use AudioFifo instead. // Store the captured audio packet in a seekable media buffer. - if (!buffer_->Append(src, size)) { - // An attempt to write outside the buffer limits has been made. - // Double the buffer capacity to ensure that we have a buffer large - // enough to handle the current sample test scenario. - buffer_->set_forward_capacity(2 * buffer_->forward_capacity()); - buffer_->Clear(); - } + // if (!buffer_->Append(src, size)) { + // An attempt to write outside the buffer limits has been made. + // Double the buffer capacity to ensure that we have a buffer large + // enough to handle the current sample test scenario. + // buffer_->set_forward_capacity(2 * buffer_->forward_capacity()); + // buffer_->Clear(); + // } } virtual void OnError(AudioInputStream* stream) OVERRIDE {} diff --git a/media/audio/cras/cras_input.cc b/media/audio/cras/cras_input.cc index 0181d88a273490..60859472a2c214 100644 --- a/media/audio/cras/cras_input.cc +++ b/media/audio/cras/cras_input.cc @@ -27,6 +27,7 @@ CrasInputStream::CrasInputStream(const AudioParameters& params, stream_direction_(device_id == AudioManagerBase::kLoopbackInputDeviceId ? CRAS_STREAM_POST_MIX_PRE_DSP : CRAS_STREAM_INPUT) { DCHECK(audio_manager_); + audio_bus_ = AudioBus::Create(params_); } CrasInputStream::~CrasInputStream() { @@ -222,8 +223,11 @@ void CrasInputStream::ReadAudio(size_t frames, double normalized_volume = 0.0; GetAgcVolume(&normalized_volume); + audio_bus_->FromInterleaved(buffer, + audio_bus_->frames(), + params_.bits_per_sample() / 8); callback_->OnData(this, - buffer, + audio_bus_.get(), frames * bytes_per_frame_, bytes_latency, normalized_volume); diff --git a/media/audio/cras/cras_input.h b/media/audio/cras/cras_input.h index 36bf9b140fc1f4..1919224d9a9b2f 100644 --- a/media/audio/cras/cras_input.h +++ b/media/audio/cras/cras_input.h @@ -98,6 +98,8 @@ class CrasInputStream : public AgcAudioStream { // Direction of the stream. const CRAS_STREAM_DIRECTION stream_direction_; + scoped_ptr audio_bus_; + DISALLOW_COPY_AND_ASSIGN(CrasInputStream); }; diff --git a/media/audio/cras/cras_input_unittest.cc b/media/audio/cras/cras_input_unittest.cc index 77f951cbb28c46..eed679a013629f 100644 --- a/media/audio/cras/cras_input_unittest.cc +++ b/media/audio/cras/cras_input_unittest.cc @@ -28,8 +28,8 @@ namespace media { class MockAudioInputCallback : public AudioInputStream::AudioInputCallback { public: - MOCK_METHOD5(OnData, void( - AudioInputStream*, const uint8*, uint32, uint32, double)); + MOCK_METHOD4(OnData, void( + AudioInputStream*, const AudioBus*, uint32, double)); MOCK_METHOD1(OnError, void(AudioInputStream*)); }; @@ -85,14 +85,10 @@ class CrasInputStreamTest : public testing::Test { // samples can be provided when doing non-integer SRC. For example // converting from 192k to 44.1k is a ratio of 4.35 to 1. MockAudioInputCallback mock_callback; - unsigned int expected_size = (kTestFramesPerPacket - 8) * - params.channels() * - params.bits_per_sample() / 8; - base::WaitableEvent event(false, false); EXPECT_CALL(mock_callback, - OnData(test_stream, _, Ge(expected_size), _, _)) + OnData(test_stream, _, _, _)) .WillOnce(InvokeWithoutArgs(&event, &base::WaitableEvent::Signal)); test_stream->Start(&mock_callback); diff --git a/media/audio/fake_audio_input_stream.cc b/media/audio/fake_audio_input_stream.cc index e05b257997e300..384adcb411cbd3 100644 --- a/media/audio/fake_audio_input_stream.cc +++ b/media/audio/fake_audio_input_stream.cc @@ -7,6 +7,7 @@ #include "base/bind.h" #include "base/lazy_instance.h" #include "media/audio/audio_manager_base.h" +#include "media/base/audio_bus.h" using base::TimeTicks; using base::TimeDelta; @@ -48,17 +49,20 @@ FakeAudioInputStream::FakeAudioInputStream(AudioManagerBase* manager, : audio_manager_(manager), callback_(NULL), buffer_size_((params.channels() * params.bits_per_sample() * - params.frames_per_buffer()) / 8), + params.frames_per_buffer()) / + 8), params_(params), thread_("FakeAudioRecordingThread"), callback_interval_(base::TimeDelta::FromMilliseconds( (params.frames_per_buffer() * 1000) / params.sample_rate())), - beep_duration_in_buffers_( - kBeepDurationMilliseconds * params.sample_rate() / - params.frames_per_buffer() / 1000), + beep_duration_in_buffers_(kBeepDurationMilliseconds * + params.sample_rate() / + params.frames_per_buffer() / + 1000), beep_generated_in_buffers_(0), beep_period_in_frames_(params.sample_rate() / kBeepFrequency), - frames_elapsed_(0) { + frames_elapsed_(0), + audio_bus_(AudioBus::Create(params)) { } FakeAudioInputStream::~FakeAudioInputStream() {} @@ -66,6 +70,7 @@ FakeAudioInputStream::~FakeAudioInputStream() {} bool FakeAudioInputStream::Open() { buffer_.reset(new uint8[buffer_size_]); memset(buffer_.get(), 0, buffer_size_); + audio_bus_->Zero(); return true; } @@ -141,7 +146,9 @@ void FakeAudioInputStream::DoCallback() { beep_generated_in_buffers_ = 0; } - callback_->OnData(this, buffer_.get(), buffer_size_, buffer_size_, 1.0); + audio_bus_->FromInterleaved( + buffer_.get(), audio_bus_->frames(), params_.bits_per_sample() / 8); + callback_->OnData(this, audio_bus_.get(), buffer_size_, 1.0); frames_elapsed_ += params_.frames_per_buffer(); thread_.message_loop()->PostDelayedTask( diff --git a/media/audio/fake_audio_input_stream.h b/media/audio/fake_audio_input_stream.h index 8dc24271dbedd0..e6c625e6b3a2dd 100644 --- a/media/audio/fake_audio_input_stream.h +++ b/media/audio/fake_audio_input_stream.h @@ -18,6 +18,7 @@ namespace media { +class AudioBus; class AudioManagerBase; class MEDIA_EXPORT FakeAudioInputStream @@ -68,6 +69,7 @@ class MEDIA_EXPORT FakeAudioInputStream int beep_generated_in_buffers_; int beep_period_in_frames_; int frames_elapsed_; + scoped_ptr audio_bus_; DISALLOW_COPY_AND_ASSIGN(FakeAudioInputStream); }; diff --git a/media/audio/mac/audio_input_mac.cc b/media/audio/mac/audio_input_mac.cc index 94dc9440ddba14..b7f6e17310954e 100644 --- a/media/audio/mac/audio_input_mac.cc +++ b/media/audio/mac/audio_input_mac.cc @@ -10,16 +10,19 @@ #include "base/logging.h" #include "base/mac/mac_logging.h" #include "media/audio/mac/audio_manager_mac.h" +#include "media/base/audio_bus.h" namespace media { PCMQueueInAudioInputStream::PCMQueueInAudioInputStream( - AudioManagerMac* manager, const AudioParameters& params) + AudioManagerMac* manager, + const AudioParameters& params) : manager_(manager), callback_(NULL), audio_queue_(NULL), buffer_size_bytes_(0), - started_(false) { + started_(false), + audio_bus_(media::AudioBus::Create(params)) { // We must have a manager. DCHECK(manager_); // A frame is one sample across all channels. In interleaved audio the per @@ -215,11 +218,11 @@ void PCMQueueInAudioInputStream::HandleInputBuffer( if (elapsed < kMinDelay) base::PlatformThread::Sleep(kMinDelay - elapsed); - callback_->OnData(this, - reinterpret_cast(audio_buffer->mAudioData), - audio_buffer->mAudioDataByteSize, - audio_buffer->mAudioDataByteSize, - 0.0); + uint8* audio_data = reinterpret_cast(audio_buffer->mAudioData); + audio_bus_->FromInterleaved( + audio_data, audio_bus_->frames(), format_.mBitsPerChannel / 8); + callback_->OnData( + this, audio_bus_.get(), audio_buffer->mAudioDataByteSize, 0.0); last_fill_ = base::TimeTicks::Now(); } diff --git a/media/audio/mac/audio_input_mac.h b/media/audio/mac/audio_input_mac.h index e8f77c706aeaf4..a6e897e061080b 100644 --- a/media/audio/mac/audio_input_mac.h +++ b/media/audio/mac/audio_input_mac.h @@ -16,6 +16,7 @@ namespace media { +class AudioBus; class AudioManagerMac; // Implementation of AudioInputStream for Mac OS X using the audio queue service @@ -83,6 +84,8 @@ class PCMQueueInAudioInputStream : public AudioInputStream { // Used to defer Start() to workaround http://crbug.com/160920. base::CancelableClosure deferred_start_cb_; + scoped_ptr audio_bus_; + DISALLOW_COPY_AND_ASSIGN(PCMQueueInAudioInputStream); }; diff --git a/media/audio/mac/audio_low_latency_input_mac.cc b/media/audio/mac/audio_low_latency_input_mac.cc index bf93358123aea7..d7a3430f6d85ed 100644 --- a/media/audio/mac/audio_low_latency_input_mac.cc +++ b/media/audio/mac/audio_low_latency_input_mac.cc @@ -10,6 +10,7 @@ #include "base/logging.h" #include "base/mac/mac_logging.h" #include "media/audio/mac/audio_manager_mac.h" +#include "media/base/audio_bus.h" #include "media/base/data_buffer.h" namespace media { @@ -31,11 +32,10 @@ static std::ostream& operator<<(std::ostream& os, // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html // for more details and background regarding this implementation. -AUAudioInputStream::AUAudioInputStream( - AudioManagerMac* manager, - const AudioParameters& input_params, - const AudioParameters& output_params, - AudioDeviceID audio_device_id) +AUAudioInputStream::AUAudioInputStream(AudioManagerMac* manager, + const AudioParameters& input_params, + const AudioParameters& output_params, + AudioDeviceID audio_device_id) : manager_(manager), sink_(NULL), audio_unit_(0), @@ -43,7 +43,8 @@ AUAudioInputStream::AUAudioInputStream( started_(false), hardware_latency_frames_(0), fifo_delay_bytes_(0), - number_of_channels_in_frame_(0) { + number_of_channels_in_frame_(0), + audio_bus_(media::AudioBus::Create(input_params)) { DCHECK(manager_); // Set up the desired (output) format specified by the client. @@ -542,12 +543,13 @@ OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames, // Read from FIFO into temporary data buffer. fifo_->Read(data_->writable_data(), requested_size_bytes_); + // Copy captured (and interleaved) data into deinterleaved audio bus. + audio_bus_->FromInterleaved( + data_->data(), audio_bus_->frames(), format_.mBitsPerChannel / 8); + // Deliver data packet, delay estimation and volume level to the user. - sink_->OnData(this, - data_->data(), - requested_size_bytes_, - capture_delay_bytes, - normalized_volume); + sink_->OnData( + this, audio_bus_.get(), capture_delay_bytes, normalized_volume); } return noErr; diff --git a/media/audio/mac/audio_low_latency_input_mac.h b/media/audio/mac/audio_low_latency_input_mac.h index ae0c4057b94563..7726227eae5efc 100644 --- a/media/audio/mac/audio_low_latency_input_mac.h +++ b/media/audio/mac/audio_low_latency_input_mac.h @@ -49,6 +49,7 @@ namespace media { +class AudioBus; class AudioManagerMac; class DataBuffer; @@ -165,6 +166,10 @@ class AUAudioInputStream : public AgcAudioStream { // Used to defer Start() to workaround http://crbug.com/160920. base::CancelableClosure deferred_start_cb_; + // Extra audio bus used for storage of deinterleaved data for the OnData + // callback. + scoped_ptr audio_bus_; + DISALLOW_COPY_AND_ASSIGN(AUAudioInputStream); }; diff --git a/media/audio/mac/audio_low_latency_input_mac_unittest.cc b/media/audio/mac/audio_low_latency_input_mac_unittest.cc index fdd7c05e58ce8b..79721d4f37b679 100644 --- a/media/audio/mac/audio_low_latency_input_mac_unittest.cc +++ b/media/audio/mac/audio_low_latency_input_mac_unittest.cc @@ -31,9 +31,11 @@ ACTION_P4(CheckCountAndPostQuitTask, count, limit, loop, closure) { class MockAudioInputCallback : public AudioInputStream::AudioInputCallback { public: - MOCK_METHOD5(OnData, void(AudioInputStream* stream, - const uint8* src, uint32 size, - uint32 hardware_delay_bytes, double volume)); + MOCK_METHOD4(OnData, + void(AudioInputStream* stream, + const AudioBus* src, + uint32 hardware_delay_bytes, + double volume)); MOCK_METHOD1(OnError, void(AudioInputStream* stream)); }; @@ -74,12 +76,19 @@ class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback { // AudioInputStream::AudioInputCallback implementation. virtual void OnData(AudioInputStream* stream, - const uint8* src, uint32 size, - uint32 hardware_delay_bytes, double volume) OVERRIDE { + const AudioBus* src, + uint32 hardware_delay_bytes, + double volume) OVERRIDE { + const int num_samples = src->frames() * src->channels(); + scoped_ptr interleaved(new int16[num_samples]); + const int bytes_per_sample = sizeof(*interleaved); + src->ToInterleaved(src->frames(), bytes_per_sample, interleaved.get()); + // Store data data in a temporary buffer to avoid making blocking // fwrite() calls in the audio callback. The complete buffer will be // written to file in the destructor. - if (buffer_.Append(src, size)) { + const int size = bytes_per_sample * num_samples; + if (buffer_.Append((const uint8*)interleaved.get(), size)) { bytes_to_write_ += size; } } @@ -224,18 +233,13 @@ TEST_F(MacAudioInputTest, AUAudioInputStreamVerifyMonoRecording) { AudioInputStream* ais = CreateAudioInputStream(CHANNEL_LAYOUT_MONO); EXPECT_TRUE(ais->Open()); - int fs = static_cast(AUAudioInputStream::HardwareSampleRate()); - int samples_per_packet = fs / 100; - int bits_per_sample = 16; - uint32 bytes_per_packet = samples_per_packet * (bits_per_sample / 8); - MockAudioInputCallback sink; // We use 10ms packets and will run the test until ten packets are received. // All should contain valid packets of the same size and a valid delay // estimate. base::RunLoop run_loop; - EXPECT_CALL(sink, OnData(ais, NotNull(), bytes_per_packet, _, _)) + EXPECT_CALL(sink, OnData(ais, NotNull(), _, _)) .Times(AtLeast(10)) .WillRepeatedly(CheckCountAndPostQuitTask( &count, 10, &message_loop_, run_loop.QuitClosure())); @@ -256,11 +260,6 @@ TEST_F(MacAudioInputTest, AUAudioInputStreamVerifyStereoRecording) { AudioInputStream* ais = CreateAudioInputStream(CHANNEL_LAYOUT_STEREO); EXPECT_TRUE(ais->Open()); - int fs = static_cast(AUAudioInputStream::HardwareSampleRate()); - int samples_per_packet = fs / 100; - int bits_per_sample = 16; - uint32 bytes_per_packet = 2 * samples_per_packet * (bits_per_sample / 8); - MockAudioInputCallback sink; // We use 10ms packets and will run the test until ten packets are received. @@ -274,7 +273,7 @@ TEST_F(MacAudioInputTest, AUAudioInputStreamVerifyStereoRecording) { // ensure that we can land the patch but will revisit this test again when // more analysis of the delay estimates are done. base::RunLoop run_loop; - EXPECT_CALL(sink, OnData(ais, NotNull(), bytes_per_packet, _, _)) + EXPECT_CALL(sink, OnData(ais, NotNull(), _, _)) .Times(AtLeast(10)) .WillRepeatedly(CheckCountAndPostQuitTask( &count, 10, &message_loop_, run_loop.QuitClosure())); diff --git a/media/audio/pulse/pulse_input.cc b/media/audio/pulse/pulse_input.cc index 9d31cddd735ff2..d5cb94ece22fa0 100644 --- a/media/audio/pulse/pulse_input.cc +++ b/media/audio/pulse/pulse_input.cc @@ -34,6 +34,8 @@ PulseAudioInputStream::PulseAudioInputStream(AudioManagerPulse* audio_manager, context_state_changed_(false) { DCHECK(mainloop); DCHECK(context); + CHECK(params_.IsValid()); + audio_bus_ = AudioBus::Create(params_); } PulseAudioInputStream::~PulseAudioInputStream() { @@ -272,8 +274,11 @@ void PulseAudioInputStream::ReadData() { int packet_size = params_.GetBytesPerBuffer(); while (buffer_->forward_bytes() >= packet_size) { buffer_->Read(audio_data_buffer_.get(), packet_size); - callback_->OnData(this, audio_data_buffer_.get(), packet_size, - hardware_delay, normalized_volume); + audio_bus_->FromInterleaved(audio_data_buffer_.get(), + audio_bus_->frames(), + params_.bits_per_sample() / 8); + callback_->OnData( + this, audio_bus_.get(), hardware_delay, normalized_volume); if (buffer_->forward_bytes() < packet_size) break; diff --git a/media/audio/pulse/pulse_input.h b/media/audio/pulse/pulse_input.h index 7566eacf10ba29..7e64bb296a8749 100644 --- a/media/audio/pulse/pulse_input.h +++ b/media/audio/pulse/pulse_input.h @@ -75,6 +75,8 @@ class PulseAudioInputStream : public AgcAudioStream { // Flag indicating the state of the context has been changed. bool context_state_changed_; + scoped_ptr audio_bus_; + base::ThreadChecker thread_checker_; DISALLOW_COPY_AND_ASSIGN(PulseAudioInputStream); diff --git a/media/audio/pulse/pulse_output.cc b/media/audio/pulse/pulse_output.cc index a67ee7f7410657..19fc47b8be5a15 100644 --- a/media/audio/pulse/pulse_output.cc +++ b/media/audio/pulse/pulse_output.cc @@ -49,8 +49,6 @@ PulseAudioOutputStream::PulseAudioOutputStream(const AudioParameters& params, pa_stream_(NULL), volume_(1.0f), source_callback_(NULL) { - DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread()); - CHECK(params_.IsValid()); audio_bus_ = AudioBus::Create(params_); } @@ -64,7 +62,7 @@ PulseAudioOutputStream::~PulseAudioOutputStream() { } bool PulseAudioOutputStream::Open() { - DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread()); + DCHECK(thread_checker_.CalledOnValidThread()); return pulse::CreateOutputStream(&pa_mainloop_, &pa_context_, &pa_stream_, params_, device_id_, &StreamNotifyCallback, &StreamRequestCallback, this); @@ -109,7 +107,7 @@ void PulseAudioOutputStream::Reset() { } void PulseAudioOutputStream::Close() { - DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread()); + DCHECK(thread_checker_.CalledOnValidThread()); Reset(); @@ -159,7 +157,7 @@ void PulseAudioOutputStream::FulfillWriteRequest(size_t requested_bytes) { } void PulseAudioOutputStream::Start(AudioSourceCallback* callback) { - DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread()); + DCHECK(thread_checker_.CalledOnValidThread()); CHECK(callback); CHECK(pa_stream_); @@ -181,7 +179,7 @@ void PulseAudioOutputStream::Start(AudioSourceCallback* callback) { } void PulseAudioOutputStream::Stop() { - DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread()); + DCHECK(thread_checker_.CalledOnValidThread()); // Cork (pause) the stream. Waiting for the main loop lock will ensure // outstanding callbacks have completed. @@ -204,13 +202,13 @@ void PulseAudioOutputStream::Stop() { } void PulseAudioOutputStream::SetVolume(double volume) { - DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread()); + DCHECK(thread_checker_.CalledOnValidThread()); volume_ = static_cast(volume); } void PulseAudioOutputStream::GetVolume(double* volume) { - DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread()); + DCHECK(thread_checker_.CalledOnValidThread()); *volume = volume_; } diff --git a/media/audio/pulse/pulse_output.h b/media/audio/pulse/pulse_output.h index 437e28cb0ec0b7..e1c00455563974 100644 --- a/media/audio/pulse/pulse_output.h +++ b/media/audio/pulse/pulse_output.h @@ -23,6 +23,7 @@ #include #include "base/memory/scoped_ptr.h" +#include "base/threading/thread_checker.h" #include "media/audio/audio_io.h" #include "media/audio/audio_parameters.h" @@ -90,6 +91,8 @@ class PulseAudioOutputStream : public AudioOutputStream { // Container for retrieving data from AudioSourceCallback::OnMoreData(). scoped_ptr audio_bus_; + base::ThreadChecker thread_checker_; + DISALLOW_COPY_AND_ASSIGN(PulseAudioOutputStream); }; diff --git a/media/audio/virtual_audio_input_stream.cc b/media/audio/virtual_audio_input_stream.cc index f632d3bd995fec..f660b9c9521245 100644 --- a/media/audio/virtual_audio_input_stream.cc +++ b/media/audio/virtual_audio_input_stream.cc @@ -139,14 +139,7 @@ void VirtualAudioInputStream::PumpAudio(AudioBus* audio_bus) { base::AutoLock scoped_lock(converter_network_lock_); mixer_.Convert(audio_bus); } - audio_bus->ToInterleaved(params_.frames_per_buffer(), - params_.bits_per_sample() / 8, - buffer_.get()); - callback_->OnData(this, - buffer_.get(), - params_.GetBytesPerBuffer(), - params_.GetBytesPerBuffer(), - 1.0); + callback_->OnData(this, audio_bus, params_.GetBytesPerBuffer(), 1.0); } void VirtualAudioInputStream::Close() { diff --git a/media/audio/virtual_audio_input_stream_unittest.cc b/media/audio/virtual_audio_input_stream_unittest.cc index 07c4948e6de39c..3aa87b0a179f5b 100644 --- a/media/audio/virtual_audio_input_stream_unittest.cc +++ b/media/audio/virtual_audio_input_stream_unittest.cc @@ -32,16 +32,17 @@ class MockInputCallback : public AudioInputStream::AudioInputCallback { public: MockInputCallback() : data_pushed_(false, false) { - ON_CALL(*this, OnData(_, _, _, _, _)) - .WillByDefault(InvokeWithoutArgs(&data_pushed_, - &base::WaitableEvent::Signal)); + ON_CALL(*this, OnData(_, _, _, _)).WillByDefault( + InvokeWithoutArgs(&data_pushed_, &base::WaitableEvent::Signal)); } virtual ~MockInputCallback() {} - MOCK_METHOD5(OnData, void(AudioInputStream* stream, const uint8* data, - uint32 size, uint32 hardware_delay_bytes, - double volume)); + MOCK_METHOD4(OnData, + void(AudioInputStream* stream, + const AudioBus* source, + uint32 hardware_delay_bytes, + double volume)); MOCK_METHOD1(OnError, void(AudioInputStream* stream)); void WaitForDataPushes() { @@ -113,8 +114,7 @@ class VirtualAudioInputStreamTest : public testing::TestWithParam { } void Start() { - EXPECT_CALL(input_callback_, OnData(_, NotNull(), _, _, _)) - .Times(AtLeast(1)); + EXPECT_CALL(input_callback_, OnData(_, NotNull(), _, _)).Times(AtLeast(1)); ASSERT_TRUE(!!stream_); stream_->Start(&input_callback_); diff --git a/media/audio/win/audio_low_latency_input_win.cc b/media/audio/win/audio_low_latency_input_win.cc index 0219ca4f900cbf..c43ed22977c648 100644 --- a/media/audio/win/audio_low_latency_input_win.cc +++ b/media/audio/win/audio_low_latency_input_win.cc @@ -9,6 +9,7 @@ #include "base/strings/utf_string_conversions.h" #include "media/audio/win/audio_manager_win.h" #include "media/audio/win/avrt_wrapper_win.h" +#include "media/base/audio_bus.h" using base::win::ScopedComPtr; using base::win::ScopedCOMInitializer; @@ -33,10 +34,9 @@ bool IsDefaultCommunicationDevice(IMMDeviceEnumerator* enumerator, } // namespace -WASAPIAudioInputStream::WASAPIAudioInputStream( - AudioManagerWin* manager, - const AudioParameters& params, - const std::string& device_id) +WASAPIAudioInputStream::WASAPIAudioInputStream(AudioManagerWin* manager, + const AudioParameters& params, + const std::string& device_id) : manager_(manager), capture_thread_(NULL), opened_(false), @@ -49,7 +49,8 @@ WASAPIAudioInputStream::WASAPIAudioInputStream( device_id_(device_id), perf_count_to_100ns_units_(0.0), ms_to_frame_count_(0.0), - sink_(NULL) { + sink_(NULL), + audio_bus_(media::AudioBus::Create(params)) { DCHECK(manager_); // Load the Avrt DLL if not already loaded. Required to support MMCSS. @@ -436,16 +437,15 @@ void WASAPIAudioInputStream::Run() { // size which was specified at construction. uint32 delay_frames = static_cast(audio_delay_frames + 0.5); while (buffer_frame_index >= packet_size_frames_) { - uint8* audio_data = - reinterpret_cast(capture_buffer.get()); + // Copy data to audio bus to match the OnData interface. + uint8* audio_data = reinterpret_cast(capture_buffer.get()); + audio_bus_->FromInterleaved( + audio_data, audio_bus_->frames(), format_.wBitsPerSample / 8); // Deliver data packet, delay estimation and volume level to // the user. - sink_->OnData(this, - audio_data, - packet_size_bytes_, - delay_frames * frame_size_, - volume); + sink_->OnData( + this, audio_bus_.get(), delay_frames * frame_size_, volume); // Store parts of the recorded data which can't be delivered // using the current packet size. The stored section will be used diff --git a/media/audio/win/audio_low_latency_input_win.h b/media/audio/win/audio_low_latency_input_win.h index 948a3fb812cb3b..a33a582c976a38 100644 --- a/media/audio/win/audio_low_latency_input_win.h +++ b/media/audio/win/audio_low_latency_input_win.h @@ -75,6 +75,7 @@ namespace media { +class AudioBus; class AudioManagerWin; // AudioInputStream implementation using Windows Core Audio APIs. @@ -211,6 +212,10 @@ class MEDIA_EXPORT WASAPIAudioInputStream // This event will be signaled when capturing shall stop. base::win::ScopedHandle stop_capture_event_; + // Extra audio bus used for storage of deinterleaved data for the OnData + // callback. + scoped_ptr audio_bus_; + DISALLOW_COPY_AND_ASSIGN(WASAPIAudioInputStream); }; diff --git a/media/audio/win/audio_low_latency_input_win_unittest.cc b/media/audio/win/audio_low_latency_input_win_unittest.cc index 34a016fb8a81a5..eee18873f6c7dc 100644 --- a/media/audio/win/audio_low_latency_input_win_unittest.cc +++ b/media/audio/win/audio_low_latency_input_win_unittest.cc @@ -38,21 +38,23 @@ ACTION_P3(CheckCountAndPostQuitTask, count, limit, loop) { class MockAudioInputCallback : public AudioInputStream::AudioInputCallback { public: - MOCK_METHOD5(OnData, void(AudioInputStream* stream, - const uint8* src, uint32 size, - uint32 hardware_delay_bytes, double volume)); + MOCK_METHOD4(OnData, + void(AudioInputStream* stream, + const AudioBus* src, + uint32 hardware_delay_bytes, + double volume)); MOCK_METHOD1(OnError, void(AudioInputStream* stream)); }; class FakeAudioInputCallback : public AudioInputStream::AudioInputCallback { public: FakeAudioInputCallback() - : error_(false), - data_event_(false, false) { - } + : error_(false), + data_event_(false, false), + num_received_audio_frames_(0) {} - const std::vector& received_data() const { return received_data_; } bool error() const { return error_; } + int num_received_audio_frames() const { return num_received_audio_frames_; } // Waits until OnData() is called on another thread. void WaitForData() { @@ -60,9 +62,11 @@ class FakeAudioInputCallback : public AudioInputStream::AudioInputCallback { } virtual void OnData(AudioInputStream* stream, - const uint8* src, uint32 size, - uint32 hardware_delay_bytes, double volume) OVERRIDE { - received_data_.insert(received_data_.end(), src, src + size); + const AudioBus* src, + uint32 hardware_delay_bytes, + double volume) OVERRIDE { + EXPECT_NE(hardware_delay_bytes, 0u); + num_received_audio_frames_ += src->frames(); data_event_.Signal(); } @@ -71,7 +75,7 @@ class FakeAudioInputCallback : public AudioInputStream::AudioInputCallback { } private: - std::vector received_data_; + int num_received_audio_frames_; base::WaitableEvent data_event_; bool error_; @@ -86,8 +90,9 @@ class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback { // 2 bytes per sample, 2 channels, 10ms @ 48kHz, 10 seconds <=> 1920000 bytes. static const size_t kMaxBufferSize = 2 * 2 * 480 * 100 * 10; - explicit WriteToFileAudioSink(const char* file_name) - : buffer_(0, kMaxBufferSize), + explicit WriteToFileAudioSink(const char* file_name, int bits_per_sample) + : bits_per_sample_(bits_per_sample), + buffer_(0, kMaxBufferSize), bytes_to_write_(0) { base::FilePath file_path; EXPECT_TRUE(PathService::Get(base::DIR_EXE, &file_path)); @@ -95,6 +100,7 @@ class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback { binary_file_ = base::OpenFile(file_path, "wb"); DLOG_IF(ERROR, !binary_file_) << "Failed to open binary PCM data file."; VLOG(0) << ">> Output file: " << file_path.value() << " has been created."; + VLOG(0) << "bits_per_sample_:" << bits_per_sample_; } virtual ~WriteToFileAudioSink() { @@ -117,14 +123,20 @@ class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback { // AudioInputStream::AudioInputCallback implementation. virtual void OnData(AudioInputStream* stream, - const uint8* src, - uint32 size, + const AudioBus* src, uint32 hardware_delay_bytes, double volume) { + EXPECT_EQ(bits_per_sample_, 16); + const int num_samples = src->frames() * src->channels(); + scoped_ptr interleaved(new int16[num_samples]); + const int bytes_per_sample = sizeof(*interleaved); + src->ToInterleaved(src->frames(), bytes_per_sample, interleaved.get()); + // Store data data in a temporary buffer to avoid making blocking // fwrite() calls in the audio callback. The complete buffer will be // written to file in the destructor. - if (buffer_.Append(src, size)) { + const int size = bytes_per_sample * num_samples; + if (buffer_.Append((const uint8*)interleaved.get(), size)) { bytes_to_write_ += size; } } @@ -132,6 +144,7 @@ class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback { virtual void OnError(AudioInputStream* stream) {} private: + int bits_per_sample_; media::SeekableBuffer buffer_; FILE* binary_file_; size_t bytes_to_write_; @@ -376,8 +389,7 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) { // We use 10ms packets and will run the test until ten packets are received. // All should contain valid packets of the same size and a valid delay // estimate. - EXPECT_CALL(sink, OnData( - ais.get(), NotNull(), bytes_per_packet, Gt(bytes_per_packet), _)) + EXPECT_CALL(sink, OnData(ais.get(), NotNull(), Gt(bytes_per_packet), _)) .Times(AtLeast(10)) .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop)); ais->Start(&sink); @@ -397,8 +409,7 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) { bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() * (aisw.bits_per_sample() / 8); - EXPECT_CALL(sink, OnData( - ais.get(), NotNull(), bytes_per_packet, Gt(bytes_per_packet), _)) + EXPECT_CALL(sink, OnData(ais.get(), NotNull(), Gt(bytes_per_packet), _)) .Times(AtLeast(10)) .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop)); ais->Start(&sink); @@ -414,8 +425,7 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) { bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() * (aisw.bits_per_sample() / 8); - EXPECT_CALL(sink, OnData( - ais.get(), NotNull(), bytes_per_packet, Gt(bytes_per_packet), _)) + EXPECT_CALL(sink, OnData(ais.get(), NotNull(), Gt(bytes_per_packet), _)) .Times(AtLeast(10)) .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop)); ais->Start(&sink); @@ -424,7 +434,7 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) { ais.Close(); } -// Test that we can capture loopback stream. +// Test that we can capture a stream in loopback. TEST(WinAudioInputTest, WASAPIAudioInputStreamLoopback) { scoped_ptr audio_manager(AudioManager::CreateForTesting()); if (!audio_manager->HasAudioOutputDevices() || !CoreAudioUtil::IsSupported()) @@ -449,7 +459,7 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamLoopback) { sink.WaitForData(); stream.Close(); - EXPECT_FALSE(sink.received_data().empty()); + EXPECT_GT(sink.num_received_audio_frames(), 0); EXPECT_FALSE(sink.error()); } @@ -474,7 +484,7 @@ TEST(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) { EXPECT_TRUE(ais->Open()); VLOG(0) << ">> Sample rate: " << aisw.sample_rate() << " [Hz]"; - WriteToFileAudioSink file_sink(file_name); + WriteToFileAudioSink file_sink(file_name, aisw.bits_per_sample()); VLOG(0) << ">> Speak into the default microphone while recording."; ais->Start(&file_sink); base::PlatformThread::Sleep(TestTimeouts::action_timeout()); diff --git a/media/audio/win/wavein_input_win.cc b/media/audio/win/wavein_input_win.cc index 43a356f908534c..f12bcf244c54c2 100644 --- a/media/audio/win/wavein_input_win.cc +++ b/media/audio/win/wavein_input_win.cc @@ -10,6 +10,7 @@ #include "media/audio/audio_io.h" #include "media/audio/win/audio_manager_win.h" #include "media/audio/win/device_enumeration_win.h" +#include "media/base/audio_bus.h" namespace media { @@ -20,7 +21,9 @@ static WAVEHDR* GetNextBuffer(WAVEHDR* current) { } PCMWaveInAudioInputStream::PCMWaveInAudioInputStream( - AudioManagerWin* manager, const AudioParameters& params, int num_buffers, + AudioManagerWin* manager, + const AudioParameters& params, + int num_buffers, const std::string& device_id) : state_(kStateEmpty), manager_(manager), @@ -29,7 +32,8 @@ PCMWaveInAudioInputStream::PCMWaveInAudioInputStream( callback_(NULL), num_buffers_(num_buffers), buffer_(NULL), - channels_(params.channels()) { + channels_(params.channels()), + audio_bus_(media::AudioBus::Create(params)) { DCHECK_GT(num_buffers_, 0); format_.wFormatTag = WAVE_FORMAT_PCM; format_.nChannels = params.channels() > 2 ? 2 : params.channels(); @@ -290,11 +294,11 @@ void PCMWaveInAudioInputStream::WaveCallback(HWAVEIN hwi, UINT msg, // there is currently no support for controlling the microphone volume // level. WAVEHDR* buffer = reinterpret_cast(param1); - obj->callback_->OnData(obj, - reinterpret_cast(buffer->lpData), - buffer->dwBytesRecorded, - buffer->dwBytesRecorded, - 0.0); + obj->audio_bus_->FromInterleaved(reinterpret_cast(buffer->lpData), + obj->audio_bus_->frames(), + obj->format_.wBitsPerSample / 8); + obj->callback_->OnData( + obj, obj->audio_bus_.get(), buffer->dwBytesRecorded, 0.0); // Queue the finished buffer back with the audio driver. Since we are // reusing the same buffers we can get away without calling diff --git a/media/audio/win/wavein_input_win.h b/media/audio/win/wavein_input_win.h index df5ce4d129bcf2..5b1edd59fb363f 100644 --- a/media/audio/win/wavein_input_win.h +++ b/media/audio/win/wavein_input_win.h @@ -20,6 +20,7 @@ namespace media { +class AudioBus; class AudioManagerWin; class PCMWaveInAudioInputStream : public AudioInputStream { @@ -123,6 +124,10 @@ class PCMWaveInAudioInputStream : public AudioInputStream { // Lock used to avoid conflicts when Stop() is called during a callback. base::Lock lock_; + // Extra audio bus used for storage of deinterleaved data for the OnData + // callback. + scoped_ptr audio_bus_; + DISALLOW_COPY_AND_ASSIGN(PCMWaveInAudioInputStream); }; diff --git a/media/base/audio_bus.h b/media/base/audio_bus.h index 56ef9777d80005..c5b161f0236082 100644 --- a/media/base/audio_bus.h +++ b/media/base/audio_bus.h @@ -108,10 +108,9 @@ class MEDIA_EXPORT AudioBus { // the channels are valid. void SwapChannels(int a, int b); - private: - friend struct base::DefaultDeleter; - ~AudioBus(); + virtual ~AudioBus(); + private: AudioBus(int channels, int frames); AudioBus(int channels, int frames, float* data); AudioBus(int frames, const std::vector& channel_data);