diff options
Diffstat (limited to 'chromium/media/audio/android')
-rw-r--r-- | chromium/media/audio/android/audio_android_unittest.cc | 533 | ||||
-rw-r--r-- | chromium/media/audio/android/audio_manager_android.cc | 209 | ||||
-rw-r--r-- | chromium/media/audio/android/audio_manager_android.h | 28 | ||||
-rw-r--r-- | chromium/media/audio/android/audio_record_input.cc | 18 | ||||
-rw-r--r-- | chromium/media/audio/android/audio_record_input.h | 4 | ||||
-rw-r--r-- | chromium/media/audio/android/opensles_input.cc | 24 | ||||
-rw-r--r-- | chromium/media/audio/android/opensles_input.h | 3 | ||||
-rw-r--r-- | chromium/media/audio/android/opensles_output.cc | 11 | ||||
-rw-r--r-- | chromium/media/audio/android/opensles_output.h | 7 |
9 files changed, 537 insertions, 300 deletions
diff --git a/chromium/media/audio/android/audio_android_unittest.cc b/chromium/media/audio/android/audio_android_unittest.cc index e7913265269..a356d9c25de 100644 --- a/chromium/media/audio/android/audio_android_unittest.cc +++ b/chromium/media/audio/android/audio_android_unittest.cc @@ -8,6 +8,7 @@ #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/path_service.h" +#include "base/run_loop.h" #include "base/strings/stringprintf.h" #include "base/synchronization/lock.h" #include "base/synchronization/waitable_event.h" @@ -17,6 +18,7 @@ #include "media/audio/android/audio_manager_android.h" #include "media/audio/audio_io.h" #include "media/audio/audio_manager_base.h" +#include "media/audio/mock_audio_source_callback.h" #include "media/base/decoder_buffer.h" #include "media/base/seekable_buffer.h" #include "media/base/test_data_util.h" @@ -85,6 +87,47 @@ static double ExpectedTimeBetweenCallbacks(AudioParameters params) { static_cast<double>(params.sample_rate()))).InMillisecondsF(); } +// Helper method which verifies that the device list starts with a valid +// default device name followed by non-default device names. +static void CheckDeviceNames(const AudioDeviceNames& device_names) { + VLOG(2) << "Got " << device_names.size() << " audio devices."; + if (device_names.empty()) { + // Log a warning so we can see the status on the build bots. No need to + // break the test though since this does successfully test the code and + // some failure cases. + LOG(WARNING) << "No input devices detected"; + return; + } + + AudioDeviceNames::const_iterator it = device_names.begin(); + + // The first device in the list should always be the default device. + EXPECT_EQ(std::string(AudioManagerBase::kDefaultDeviceName), + it->device_name); + EXPECT_EQ(std::string(AudioManagerBase::kDefaultDeviceId), it->unique_id); + ++it; + + // Other devices should have non-empty name and id and should not contain + // default name or id. + while (it != device_names.end()) { + EXPECT_FALSE(it->device_name.empty()); + EXPECT_FALSE(it->unique_id.empty()); + VLOG(2) << "Device ID(" << it->unique_id + << "), label: " << it->device_name; + EXPECT_NE(std::string(AudioManagerBase::kDefaultDeviceName), + it->device_name); + EXPECT_NE(std::string(AudioManagerBase::kDefaultDeviceId), + it->unique_id); + ++it; + } +} + +// We clear the data bus to ensure that the test does not cause noise. +static int RealOnMoreData(AudioBus* dest, AudioBuffersState buffers_state) { + dest->Zero(); + return dest->frames(); +} + std::ostream& operator<<(std::ostream& os, const AudioParameters& params) { using namespace std; os << endl << "format: " << FormatToString(params.format()) << endl @@ -105,34 +148,14 @@ 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(OnClose, void(AudioInputStream* stream)); MOCK_METHOD1(OnError, void(AudioInputStream* stream)); }; -// Gmock implementation of AudioOutputStream::AudioSourceCallback. -class MockAudioOutputCallback : public AudioOutputStream::AudioSourceCallback { - public: - MOCK_METHOD2(OnMoreData, - int(AudioBus* dest, AudioBuffersState buffers_state)); - MOCK_METHOD3(OnMoreIOData, - int(AudioBus* source, - AudioBus* dest, - AudioBuffersState buffers_state)); - MOCK_METHOD1(OnError, void(AudioOutputStream* stream)); - - // We clear the data bus to ensure that the test does not cause noise. - int RealOnMoreData(AudioBus* dest, AudioBuffersState buffers_state) { - dest->Zero(); - return dest->frames(); - } -}; - // Implements AudioOutputStream::AudioSourceCallback and provides audio data // by reading from a data file. class FileAudioSource : public AudioOutputStream::AudioSourceCallback { @@ -183,13 +206,6 @@ class FileAudioSource : public AudioOutputStream::AudioSourceCallback { return frames; } - virtual int OnMoreIOData(AudioBus* source, - AudioBus* dest, - AudioBuffersState buffers_state) OVERRIDE { - NOTREACHED(); - return 0; - } - virtual void OnError(AudioOutputStream* stream) OVERRIDE {} int file_size() { return file_->data_size(); } @@ -247,18 +263,22 @@ 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<int16> 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(); } - virtual void OnClose(AudioInputStream* stream) OVERRIDE {} virtual void OnError(AudioInputStream* stream) OVERRIDE {} private: @@ -291,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<int16> 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; @@ -318,13 +344,12 @@ 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(); } } - virtual void OnClose(AudioInputStream* stream) OVERRIDE {} virtual void OnError(AudioInputStream* stream) OVERRIDE {} // AudioOutputStream::AudioSourceCallback implementation @@ -357,13 +382,6 @@ class FullDuplexAudioSinkSource return dest->frames(); } - virtual int OnMoreIOData(AudioBus* source, - AudioBus* dest, - AudioBuffersState buffers_state) OVERRIDE { - NOTREACHED(); - return 0; - } - virtual void OnError(AudioOutputStream* stream) OVERRIDE {} private: @@ -389,21 +407,76 @@ class FullDuplexAudioSinkSource // Test fixture class for tests which only exercise the output path. class AudioAndroidOutputTest : public testing::Test { public: - AudioAndroidOutputTest() {} - - protected: - virtual void SetUp() { - audio_manager_.reset(AudioManager::CreateForTesting()); - loop_.reset(new base::MessageLoopForUI()); + AudioAndroidOutputTest() + : loop_(new base::MessageLoopForUI()), + audio_manager_(AudioManager::CreateForTesting()), + audio_output_stream_(NULL) { } - virtual void TearDown() {} + virtual ~AudioAndroidOutputTest() { + } + protected: AudioManager* audio_manager() { return audio_manager_.get(); } base::MessageLoopForUI* loop() { return loop_.get(); } + const AudioParameters& audio_output_parameters() { + return audio_output_parameters_; + } - AudioParameters GetDefaultOutputStreamParameters() { - return audio_manager()->GetDefaultOutputStreamParameters(); + // Synchronously runs the provided callback/closure on the audio thread. + void RunOnAudioThread(const base::Closure& closure) { + if (!audio_manager()->GetTaskRunner()->BelongsToCurrentThread()) { + base::WaitableEvent event(false, false); + audio_manager()->GetTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&AudioAndroidOutputTest::RunOnAudioThreadImpl, + base::Unretained(this), + closure, + &event)); + event.Wait(); + } else { + closure.Run(); + } + } + + void RunOnAudioThreadImpl(const base::Closure& closure, + base::WaitableEvent* event) { + DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread()); + closure.Run(); + event->Signal(); + } + + void GetDefaultOutputStreamParametersOnAudioThread() { + RunOnAudioThread( + base::Bind(&AudioAndroidOutputTest::GetDefaultOutputStreamParameters, + base::Unretained(this))); + } + + void MakeAudioOutputStreamOnAudioThread(const AudioParameters& params) { + RunOnAudioThread( + base::Bind(&AudioAndroidOutputTest::MakeOutputStream, + base::Unretained(this), + params)); + } + + void OpenAndCloseAudioOutputStreamOnAudioThread() { + RunOnAudioThread( + base::Bind(&AudioAndroidOutputTest::OpenAndClose, + base::Unretained(this))); + } + + void OpenAndStartAudioOutputStreamOnAudioThread( + AudioOutputStream::AudioSourceCallback* source) { + RunOnAudioThread( + base::Bind(&AudioAndroidOutputTest::OpenAndStart, + base::Unretained(this), + source)); + } + + void StopAndCloseAudioOutputStreamOnAudioThread() { + RunOnAudioThread( + base::Bind(&AudioAndroidOutputTest::StopAndClose, + base::Unretained(this))); } double AverageTimeBetweenCallbacks(int num_callbacks) const { @@ -416,28 +489,25 @@ class AudioAndroidOutputTest : public testing::Test { ExpectedTimeBetweenCallbacks(params); const int num_callbacks = (kCallbackTestTimeMs / expected_time_between_callbacks_ms); - AudioOutputStream* stream = audio_manager()->MakeAudioOutputStream( - params, std::string(), std::string()); - EXPECT_TRUE(stream); + MakeAudioOutputStreamOnAudioThread(params); int count = 0; - MockAudioOutputCallback source; + MockAudioSourceCallback source; EXPECT_CALL(source, OnMoreData(NotNull(), _)) .Times(AtLeast(num_callbacks)) .WillRepeatedly( DoAll(CheckCountAndPostQuitTask(&count, num_callbacks, loop()), - Invoke(&source, &MockAudioOutputCallback::RealOnMoreData))); - EXPECT_CALL(source, OnError(stream)).Times(0); - EXPECT_CALL(source, OnMoreIOData(_, _, _)).Times(0); + Invoke(RealOnMoreData))); + EXPECT_CALL(source, OnError(audio_output_stream_)).Times(0); + + OpenAndStartAudioOutputStreamOnAudioThread(&source); - EXPECT_TRUE(stream->Open()); - stream->Start(&source); start_time_ = base::TimeTicks::Now(); loop()->Run(); end_time_ = base::TimeTicks::Now(); - stream->Stop(); - stream->Close(); + + StopAndCloseAudioOutputStreamOnAudioThread(); double average_time_between_callbacks_ms = AverageTimeBetweenCallbacks(num_callbacks); @@ -448,11 +518,47 @@ class AudioAndroidOutputTest : public testing::Test { EXPECT_GE(average_time_between_callbacks_ms, 0.70 * expected_time_between_callbacks_ms); EXPECT_LE(average_time_between_callbacks_ms, - 1.30 * expected_time_between_callbacks_ms); + 1.35 * expected_time_between_callbacks_ms); + } + + void GetDefaultOutputStreamParameters() { + DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread()); + audio_output_parameters_ = + audio_manager()->GetDefaultOutputStreamParameters(); + EXPECT_TRUE(audio_output_parameters_.IsValid()); + } + + void MakeOutputStream(const AudioParameters& params) { + DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread()); + audio_output_stream_ = audio_manager()->MakeAudioOutputStream( + params, std::string()); + EXPECT_TRUE(audio_output_stream_); + } + + void OpenAndClose() { + DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread()); + EXPECT_TRUE(audio_output_stream_->Open()); + audio_output_stream_->Close(); + audio_output_stream_ = NULL; + } + + void OpenAndStart(AudioOutputStream::AudioSourceCallback* source) { + DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread()); + EXPECT_TRUE(audio_output_stream_->Open()); + audio_output_stream_->Start(source); + } + + void StopAndClose() { + DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread()); + audio_output_stream_->Stop(); + audio_output_stream_->Close(); + audio_output_stream_ = NULL; } scoped_ptr<base::MessageLoopForUI> loop_; scoped_ptr<AudioManager> audio_manager_; + AudioParameters audio_output_parameters_; + AudioOutputStream* audio_output_stream_; base::TimeTicks start_time_; base::TimeTicks end_time_; @@ -476,53 +582,87 @@ std::vector<bool> RunAudioRecordInputPathTests() { class AudioAndroidInputTest : public AudioAndroidOutputTest, public testing::WithParamInterface<bool> { public: - AudioAndroidInputTest() {} + AudioAndroidInputTest() : audio_input_stream_(NULL) {} protected: + const AudioParameters& audio_input_parameters() { + return audio_input_parameters_; + } + AudioParameters GetInputStreamParameters() { - AudioParameters input_params = audio_manager()->GetInputStreamParameters( - AudioManagerBase::kDefaultDeviceId); + GetDefaultInputStreamParametersOnAudioThread(); + // Override the platform effects setting to use the AudioRecord or OpenSLES // path as requested. int effects = GetParam() ? AudioParameters::ECHO_CANCELLER : AudioParameters::NO_EFFECTS; - AudioParameters params(input_params.format(), - input_params.channel_layout(), - input_params.input_channels(), - input_params.sample_rate(), - input_params.bits_per_sample(), - input_params.frames_per_buffer(), + AudioParameters params(audio_input_parameters().format(), + audio_input_parameters().channel_layout(), + audio_input_parameters().input_channels(), + audio_input_parameters().sample_rate(), + audio_input_parameters().bits_per_sample(), + audio_input_parameters().frames_per_buffer(), effects); return params; } + void GetDefaultInputStreamParametersOnAudioThread() { + RunOnAudioThread( + base::Bind(&AudioAndroidInputTest::GetDefaultInputStreamParameters, + base::Unretained(this))); + } + + void MakeAudioInputStreamOnAudioThread(const AudioParameters& params) { + RunOnAudioThread( + base::Bind(&AudioAndroidInputTest::MakeInputStream, + base::Unretained(this), + params)); + } + + void OpenAndCloseAudioInputStreamOnAudioThread() { + RunOnAudioThread( + base::Bind(&AudioAndroidInputTest::OpenAndClose, + base::Unretained(this))); + } + + void OpenAndStartAudioInputStreamOnAudioThread( + AudioInputStream::AudioInputCallback* sink) { + RunOnAudioThread( + base::Bind(&AudioAndroidInputTest::OpenAndStart, + base::Unretained(this), + sink)); + } + + void StopAndCloseAudioInputStreamOnAudioThread() { + RunOnAudioThread( + base::Bind(&AudioAndroidInputTest::StopAndClose, + base::Unretained(this))); + } + void StartInputStreamCallbacks(const AudioParameters& params) { double expected_time_between_callbacks_ms = ExpectedTimeBetweenCallbacks(params); const int num_callbacks = (kCallbackTestTimeMs / expected_time_between_callbacks_ms); - AudioInputStream* stream = audio_manager()->MakeAudioInputStream( - params, AudioManagerBase::kDefaultDeviceId); - EXPECT_TRUE(stream); + + MakeAudioInputStreamOnAudioThread(params); int count = 0; MockAudioInputCallback sink; - EXPECT_CALL(sink, - OnData(stream, NotNull(), params.GetBytesPerBuffer(), _, _)) + EXPECT_CALL(sink, OnData(audio_input_stream_, NotNull(), _, _)) .Times(AtLeast(num_callbacks)) .WillRepeatedly( - CheckCountAndPostQuitTask(&count, num_callbacks, loop())); - EXPECT_CALL(sink, OnError(stream)).Times(0); - EXPECT_CALL(sink, OnClose(stream)).Times(1); + CheckCountAndPostQuitTask(&count, num_callbacks, loop())); + EXPECT_CALL(sink, OnError(audio_input_stream_)).Times(0); + + OpenAndStartAudioInputStreamOnAudioThread(&sink); - EXPECT_TRUE(stream->Open()); - stream->Start(&sink); start_time_ = base::TimeTicks::Now(); loop()->Run(); end_time_ = base::TimeTicks::Now(); - stream->Stop(); - stream->Close(); + + StopAndCloseAudioInputStreamOnAudioThread(); double average_time_between_callbacks_ms = AverageTimeBetweenCallbacks(num_callbacks); @@ -536,6 +676,41 @@ class AudioAndroidInputTest : public AudioAndroidOutputTest, 1.30 * expected_time_between_callbacks_ms); } + void GetDefaultInputStreamParameters() { + DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread()); + audio_input_parameters_ = audio_manager()->GetInputStreamParameters( + AudioManagerBase::kDefaultDeviceId); + } + + void MakeInputStream(const AudioParameters& params) { + DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread()); + audio_input_stream_ = audio_manager()->MakeAudioInputStream( + params, AudioManagerBase::kDefaultDeviceId); + EXPECT_TRUE(audio_input_stream_); + } + + void OpenAndClose() { + DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread()); + EXPECT_TRUE(audio_input_stream_->Open()); + audio_input_stream_->Close(); + audio_input_stream_ = NULL; + } + + void OpenAndStart(AudioInputStream::AudioInputCallback* sink) { + DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread()); + EXPECT_TRUE(audio_input_stream_->Open()); + audio_input_stream_->Start(sink); + } + + void StopAndClose() { + DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread()); + audio_input_stream_->Stop(); + audio_input_stream_->Close(); + audio_input_stream_ = NULL; + } + + AudioInputStream* audio_input_stream_; + AudioParameters audio_input_parameters_; private: DISALLOW_COPY_AND_ASSIGN(AudioAndroidInputTest); @@ -545,35 +720,48 @@ class AudioAndroidInputTest : public AudioAndroidOutputTest, TEST_P(AudioAndroidInputTest, GetDefaultInputStreamParameters) { // We don't go through AudioAndroidInputTest::GetInputStreamParameters() here // so that we can log the real (non-overridden) values of the effects. - AudioParameters params = audio_manager()->GetInputStreamParameters( - AudioManagerBase::kDefaultDeviceId); - EXPECT_TRUE(params.IsValid()); - VLOG(1) << params; + GetDefaultInputStreamParametersOnAudioThread(); + EXPECT_TRUE(audio_input_parameters().IsValid()); + VLOG(1) << audio_input_parameters(); } // Get the default audio output parameters and log the result. TEST_F(AudioAndroidOutputTest, GetDefaultOutputStreamParameters) { - AudioParameters params = GetDefaultOutputStreamParameters(); - EXPECT_TRUE(params.IsValid()); - VLOG(1) << params; + GetDefaultOutputStreamParametersOnAudioThread(); + VLOG(1) << audio_output_parameters(); +} + +// Verify input device enumeration. +TEST_F(AudioAndroidInputTest, GetAudioInputDeviceNames) { + if (!audio_manager()->HasAudioInputDevices()) + return; + AudioDeviceNames devices; + RunOnAudioThread( + base::Bind(&AudioManager::GetAudioInputDeviceNames, + base::Unretained(audio_manager()), + &devices)); + CheckDeviceNames(devices); } -// Check if low-latency output is supported and log the result as output. -TEST_F(AudioAndroidOutputTest, IsAudioLowLatencySupported) { - AudioManagerAndroid* manager = - static_cast<AudioManagerAndroid*>(audio_manager()); - bool low_latency = manager->IsAudioLowLatencySupported(); - low_latency ? VLOG(0) << "Low latency output is supported" - : VLOG(0) << "Low latency output is *not* supported"; +// Verify output device enumeration. +TEST_F(AudioAndroidOutputTest, GetAudioOutputDeviceNames) { + if (!audio_manager()->HasAudioOutputDevices()) + return; + AudioDeviceNames devices; + RunOnAudioThread( + base::Bind(&AudioManager::GetAudioOutputDeviceNames, + base::Unretained(audio_manager()), + &devices)); + CheckDeviceNames(devices); } // Ensure that a default input stream can be created and closed. TEST_P(AudioAndroidInputTest, CreateAndCloseInputStream) { AudioParameters params = GetInputStreamParameters(); - AudioInputStream* ais = audio_manager()->MakeAudioInputStream( - params, AudioManagerBase::kDefaultDeviceId); - EXPECT_TRUE(ais); - ais->Close(); + MakeAudioInputStreamOnAudioThread(params); + RunOnAudioThread( + base::Bind(&AudioInputStream::Close, + base::Unretained(audio_input_stream_))); } // Ensure that a default output stream can be created and closed. @@ -581,45 +769,39 @@ TEST_P(AudioAndroidInputTest, CreateAndCloseInputStream) { // to communication mode, and calls RegisterHeadsetReceiver, the first time // it is called? TEST_F(AudioAndroidOutputTest, CreateAndCloseOutputStream) { - AudioParameters params = GetDefaultOutputStreamParameters(); - AudioOutputStream* aos = audio_manager()->MakeAudioOutputStream( - params, std::string(), std::string()); - EXPECT_TRUE(aos); - aos->Close(); + GetDefaultOutputStreamParametersOnAudioThread(); + MakeAudioOutputStreamOnAudioThread(audio_output_parameters()); + RunOnAudioThread( + base::Bind(&AudioOutputStream::Close, + base::Unretained(audio_output_stream_))); } // Ensure that a default input stream can be opened and closed. TEST_P(AudioAndroidInputTest, OpenAndCloseInputStream) { AudioParameters params = GetInputStreamParameters(); - AudioInputStream* ais = audio_manager()->MakeAudioInputStream( - params, AudioManagerBase::kDefaultDeviceId); - EXPECT_TRUE(ais); - EXPECT_TRUE(ais->Open()); - ais->Close(); + MakeAudioInputStreamOnAudioThread(params); + OpenAndCloseAudioInputStreamOnAudioThread(); } // Ensure that a default output stream can be opened and closed. TEST_F(AudioAndroidOutputTest, OpenAndCloseOutputStream) { - AudioParameters params = GetDefaultOutputStreamParameters(); - AudioOutputStream* aos = audio_manager()->MakeAudioOutputStream( - params, std::string(), std::string()); - EXPECT_TRUE(aos); - EXPECT_TRUE(aos->Open()); - aos->Close(); + GetDefaultOutputStreamParametersOnAudioThread(); + MakeAudioOutputStreamOnAudioThread(audio_output_parameters()); + OpenAndCloseAudioOutputStreamOnAudioThread(); } // Start input streaming using default input parameters and ensure that the // callback sequence is sane. -TEST_P(AudioAndroidInputTest, StartInputStreamCallbacks) { - AudioParameters params = GetInputStreamParameters(); - StartInputStreamCallbacks(params); +TEST_P(AudioAndroidInputTest, DISABLED_StartInputStreamCallbacks) { + AudioParameters native_params = GetInputStreamParameters(); + StartInputStreamCallbacks(native_params); } // Start input streaming using non default input parameters and ensure that the // callback sequence is sane. The only change we make in this test is to select // a 10ms buffer size instead of the default size. -// TODO(henrika): possibly add support for more variations. -TEST_P(AudioAndroidInputTest, StartInputStreamCallbacksNonDefaultParameters) { +TEST_P(AudioAndroidInputTest, + DISABLED_StartInputStreamCallbacksNonDefaultParameters) { AudioParameters native_params = GetInputStreamParameters(); AudioParameters params(native_params.format(), native_params.channel_layout(), @@ -634,8 +816,8 @@ TEST_P(AudioAndroidInputTest, StartInputStreamCallbacksNonDefaultParameters) { // Start output streaming using default output parameters and ensure that the // callback sequence is sane. TEST_F(AudioAndroidOutputTest, StartOutputStreamCallbacks) { - AudioParameters params = GetDefaultOutputStreamParameters(); - StartOutputStreamCallbacks(params); + GetDefaultOutputStreamParametersOnAudioThread(); + StartOutputStreamCallbacks(audio_output_parameters()); } // Start output streaming using non default output parameters and ensure that @@ -643,13 +825,13 @@ TEST_F(AudioAndroidOutputTest, StartOutputStreamCallbacks) { // select a 10ms buffer size instead of the default size and to open up the // device in mono. // TODO(henrika): possibly add support for more variations. -TEST_F(AudioAndroidOutputTest, StartOutputStreamCallbacksNonDefaultParameters) { - AudioParameters native_params = GetDefaultOutputStreamParameters(); - AudioParameters params(native_params.format(), +TEST_F(AudioAndroidOutputTest, DISABLED_StartOutputStreamCallbacksNonDefaultParameters) { + GetDefaultOutputStreamParametersOnAudioThread(); + AudioParameters params(audio_output_parameters().format(), CHANNEL_LAYOUT_MONO, - native_params.sample_rate(), - native_params.bits_per_sample(), - native_params.sample_rate() / 100); + audio_output_parameters().sample_rate(), + audio_output_parameters().bits_per_sample(), + audio_output_parameters().sample_rate() / 100); StartOutputStreamCallbacks(params); } @@ -658,13 +840,12 @@ TEST_F(AudioAndroidOutputTest, StartOutputStreamCallbacksNonDefaultParameters) { // NOTE: this test requires user interaction and is not designed to run as an // automatized test on bots. TEST_F(AudioAndroidOutputTest, DISABLED_RunOutputStreamWithFileAsSource) { - AudioParameters params = GetDefaultOutputStreamParameters(); - VLOG(1) << params; - AudioOutputStream* aos = audio_manager()->MakeAudioOutputStream( - params, std::string(), std::string()); - EXPECT_TRUE(aos); + GetDefaultOutputStreamParametersOnAudioThread(); + VLOG(1) << audio_output_parameters(); + MakeAudioOutputStreamOnAudioThread(audio_output_parameters()); std::string file_name; + const AudioParameters params = audio_output_parameters(); if (params.sample_rate() == 48000 && params.channels() == 2) { file_name = kSpeechFile_16b_s_48k; } else if (params.sample_rate() == 48000 && params.channels() == 1) { @@ -681,13 +862,10 @@ TEST_F(AudioAndroidOutputTest, DISABLED_RunOutputStreamWithFileAsSource) { base::WaitableEvent event(false, false); FileAudioSource source(&event, file_name); - EXPECT_TRUE(aos->Open()); - aos->SetVolume(1.0); - aos->Start(&source); + OpenAndStartAudioOutputStreamOnAudioThread(&source); VLOG(0) << ">> Verify that the file is played out correctly..."; EXPECT_TRUE(event.TimedWait(TestTimeouts::action_max_timeout())); - aos->Stop(); - aos->Close(); + StopAndCloseAudioOutputStreamOnAudioThread(); } // Start input streaming and run it for ten seconds while recording to a @@ -697,9 +875,7 @@ TEST_F(AudioAndroidOutputTest, DISABLED_RunOutputStreamWithFileAsSource) { TEST_P(AudioAndroidInputTest, DISABLED_RunSimplexInputStreamWithFileAsSink) { AudioParameters params = GetInputStreamParameters(); VLOG(1) << params; - AudioInputStream* ais = audio_manager()->MakeAudioInputStream( - params, AudioManagerBase::kDefaultDeviceId); - EXPECT_TRUE(ais); + MakeAudioInputStreamOnAudioThread(params); std::string file_name = base::StringPrintf("out_simplex_%d_%d_%d.pcm", params.sample_rate(), @@ -709,12 +885,10 @@ TEST_P(AudioAndroidInputTest, DISABLED_RunSimplexInputStreamWithFileAsSink) { base::WaitableEvent event(false, false); FileAudioSink sink(&event, params, file_name); - EXPECT_TRUE(ais->Open()); - ais->Start(&sink); + OpenAndStartAudioInputStreamOnAudioThread(&sink); VLOG(0) << ">> Speak into the microphone to record audio..."; EXPECT_TRUE(event.TimedWait(TestTimeouts::action_max_timeout())); - ais->Stop(); - ais->Close(); + StopAndCloseAudioInputStreamOnAudioThread(); } // Same test as RunSimplexInputStreamWithFileAsSink but this time output @@ -723,15 +897,12 @@ TEST_P(AudioAndroidInputTest, DISABLED_RunSimplexInputStreamWithFileAsSink) { // automatized test on bots. TEST_P(AudioAndroidInputTest, DISABLED_RunDuplexInputStreamWithFileAsSink) { AudioParameters in_params = GetInputStreamParameters(); - AudioInputStream* ais = audio_manager()->MakeAudioInputStream( - in_params, AudioManagerBase::kDefaultDeviceId); - EXPECT_TRUE(ais); + VLOG(1) << in_params; + MakeAudioInputStreamOnAudioThread(in_params); - AudioParameters out_params = - audio_manager()->GetDefaultOutputStreamParameters(); - AudioOutputStream* aos = audio_manager()->MakeAudioOutputStream( - out_params, std::string(), std::string()); - EXPECT_TRUE(aos); + GetDefaultOutputStreamParametersOnAudioThread(); + VLOG(1) << audio_output_parameters(); + MakeAudioOutputStreamOnAudioThread(audio_output_parameters()); std::string file_name = base::StringPrintf("out_duplex_%d_%d_%d.pcm", in_params.sample_rate(), @@ -740,23 +911,18 @@ TEST_P(AudioAndroidInputTest, DISABLED_RunDuplexInputStreamWithFileAsSink) { base::WaitableEvent event(false, false); FileAudioSink sink(&event, in_params, file_name); - MockAudioOutputCallback source; + MockAudioSourceCallback source; - EXPECT_CALL(source, OnMoreData(NotNull(), _)).WillRepeatedly( - Invoke(&source, &MockAudioOutputCallback::RealOnMoreData)); - EXPECT_CALL(source, OnError(aos)).Times(0); - EXPECT_CALL(source, OnMoreIOData(_, _, _)).Times(0); + EXPECT_CALL(source, OnMoreData(NotNull(), _)) + .WillRepeatedly(Invoke(RealOnMoreData)); + EXPECT_CALL(source, OnError(audio_output_stream_)).Times(0); - EXPECT_TRUE(ais->Open()); - EXPECT_TRUE(aos->Open()); - ais->Start(&sink); - aos->Start(&source); + OpenAndStartAudioInputStreamOnAudioThread(&sink); + OpenAndStartAudioOutputStreamOnAudioThread(&source); VLOG(0) << ">> Speak into the microphone to record audio"; EXPECT_TRUE(event.TimedWait(TestTimeouts::action_max_timeout())); - aos->Stop(); - ais->Stop(); - aos->Close(); - ais->Close(); + StopAndCloseAudioOutputStreamOnAudioThread(); + StopAndCloseAudioInputStreamOnAudioThread(); } // Start audio in both directions while feeding captured data into a FIFO so @@ -776,18 +942,17 @@ TEST_P(AudioAndroidInputTest, // audio on Android. AudioParameters io_params(default_input_params.format(), default_input_params.channel_layout(), + ChannelLayoutToChannelCount( + default_input_params.channel_layout()), default_input_params.sample_rate(), default_input_params.bits_per_sample(), - default_input_params.sample_rate() / 100); + default_input_params.sample_rate() / 100, + default_input_params.effects()); VLOG(1) << io_params; // Create input and output streams using the common audio parameters. - AudioInputStream* ais = audio_manager()->MakeAudioInputStream( - io_params, AudioManagerBase::kDefaultDeviceId); - EXPECT_TRUE(ais); - AudioOutputStream* aos = audio_manager()->MakeAudioOutputStream( - io_params, std::string(), std::string()); - EXPECT_TRUE(aos); + MakeAudioInputStreamOnAudioThread(io_params); + MakeAudioOutputStreamOnAudioThread(io_params); FullDuplexAudioSinkSource full_duplex(io_params); @@ -795,20 +960,16 @@ TEST_P(AudioAndroidInputTest, // delay we should expect from the FIFO. If real-time delay measurements are // performed, the result should be reduced by this extra delay since it is // something that has been added by the test. - EXPECT_TRUE(ais->Open()); - EXPECT_TRUE(aos->Open()); - ais->Start(&full_duplex); - aos->Start(&full_duplex); + OpenAndStartAudioInputStreamOnAudioThread(&full_duplex); + OpenAndStartAudioOutputStreamOnAudioThread(&full_duplex); VLOG(1) << "HINT: an estimate of the extra FIFO delay will be updated " << "once per second during this test."; VLOG(0) << ">> Speak into the mic and listen to the audio in loopback..."; fflush(stdout); base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(20)); printf("\n"); - aos->Stop(); - ais->Stop(); - aos->Close(); - ais->Close(); + StopAndCloseAudioOutputStreamOnAudioThread(); + StopAndCloseAudioInputStreamOnAudioThread(); } INSTANTIATE_TEST_CASE_P(AudioAndroidInputTest, AudioAndroidInputTest, diff --git a/chromium/media/audio/android/audio_manager_android.cc b/chromium/media/audio/android/audio_manager_android.cc index 3464d89a30f..48f203ab74e 100644 --- a/chromium/media/audio/android/audio_manager_android.cc +++ b/chromium/media/audio/android/audio_manager_android.cc @@ -9,6 +9,7 @@ #include "base/android/jni_string.h" #include "base/android/scoped_java_ref.h" #include "base/logging.h" +#include "base/message_loop/message_loop.h" #include "base/strings/string_number_conversions.h" #include "jni/AudioManagerAndroid_jni.h" #include "media/audio/android/audio_record_input.h" @@ -37,9 +38,6 @@ static void AddDefaultDevice(AudioDeviceNames* device_names) { // Maximum number of output streams that can be open simultaneously. static const int kMaxOutputStreams = 10; -static const int kAudioModeNormal = 0x00000000; -static const int kAudioModeInCommunication = 0x00000003; - static const int kDefaultInputBufferSize = 1024; static const int kDefaultOutputBufferSize = 2048; @@ -48,19 +46,26 @@ AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) { } AudioManagerAndroid::AudioManagerAndroid(AudioLogFactory* audio_log_factory) - : AudioManagerBase(audio_log_factory) { + : AudioManagerBase(audio_log_factory), + communication_mode_is_on_(false) { SetMaxOutputStreamsAllowed(kMaxOutputStreams); - j_audio_manager_.Reset( - Java_AudioManagerAndroid_createAudioManagerAndroid( - base::android::AttachCurrentThread(), - base::android::GetApplicationContext(), - reinterpret_cast<intptr_t>(this))); - Init(); + // WARNING: This is executed on the UI loop, do not add any code here which + // loads libraries or attempts to call out into the OS. Instead add such code + // to the InitializeOnAudioThread() method below. + + // Task must be posted last to avoid races from handing out "this" to the + // audio thread. + GetTaskRunner()->PostTask(FROM_HERE, base::Bind( + &AudioManagerAndroid::InitializeOnAudioThread, + base::Unretained(this))); } AudioManagerAndroid::~AudioManagerAndroid() { - Close(); + // It's safe to post a task here since Shutdown() will wait for all tasks to + // complete before returning. + GetTaskRunner()->PostTask(FROM_HERE, base::Bind( + &AudioManagerAndroid::ShutdownOnAudioThread, base::Unretained(this))); Shutdown(); } @@ -74,13 +79,22 @@ bool AudioManagerAndroid::HasAudioInputDevices() { void AudioManagerAndroid::GetAudioInputDeviceNames( AudioDeviceNames* device_names) { + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); + // Always add default device parameters as first element. + DCHECK(device_names->empty()); AddDefaultDevice(device_names); + // Get list of available audio devices. JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jobjectArray> j_device_array = Java_AudioManagerAndroid_getAudioInputDeviceNames( env, j_audio_manager_.obj()); + if (j_device_array.is_null()) { + // Most probable reason for a NULL result here is that the process lacks + // MODIFY_AUDIO_SETTINGS or RECORD_AUDIO permissions. + return; + } jsize len = env->GetArrayLength(j_device_array.obj()); AudioDeviceName device; for (jsize i = 0; i < len; ++i) { @@ -104,76 +118,96 @@ void AudioManagerAndroid::GetAudioOutputDeviceNames( AudioParameters AudioManagerAndroid::GetInputStreamParameters( const std::string& device_id) { - JNIEnv* env = AttachCurrentThread(); + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); + // Use mono as preferred number of input channels on Android to save // resources. Using mono also avoids a driver issue seen on Samsung // Galaxy S3 and S4 devices. See http://crbug.com/256851 for details. + JNIEnv* env = AttachCurrentThread(); ChannelLayout channel_layout = CHANNEL_LAYOUT_MONO; int buffer_size = Java_AudioManagerAndroid_getMinInputFrameSize( env, GetNativeOutputSampleRate(), ChannelLayoutToChannelCount(channel_layout)); + buffer_size = buffer_size <= 0 ? kDefaultInputBufferSize : buffer_size; int effects = AudioParameters::NO_EFFECTS; effects |= Java_AudioManagerAndroid_shouldUseAcousticEchoCanceler(env) ? AudioParameters::ECHO_CANCELLER : AudioParameters::NO_EFFECTS; + + int user_buffer_size = GetUserBufferSize(); + if (user_buffer_size) + buffer_size = user_buffer_size; + AudioParameters params( AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, 0, - GetNativeOutputSampleRate(), 16, - buffer_size <= 0 ? kDefaultInputBufferSize : buffer_size, effects); + GetNativeOutputSampleRate(), 16, buffer_size, effects); return params; } AudioOutputStream* AudioManagerAndroid::MakeAudioOutputStream( const AudioParameters& params, - const std::string& device_id, - const std::string& input_device_id) { + const std::string& device_id) { + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); AudioOutputStream* stream = - AudioManagerBase::MakeAudioOutputStream(params, std::string(), - std::string()); - if (stream && output_stream_count() == 1) { - SetAudioMode(kAudioModeInCommunication); - } - - { - base::AutoLock lock(streams_lock_); - streams_.insert(static_cast<OpenSLESOutputStream*>(stream)); - } - + AudioManagerBase::MakeAudioOutputStream(params, std::string()); + streams_.insert(static_cast<OpenSLESOutputStream*>(stream)); return stream; } AudioInputStream* AudioManagerAndroid::MakeAudioInputStream( const AudioParameters& params, const std::string& device_id) { + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); + bool has_no_input_streams = HasNoAudioInputStreams(); AudioInputStream* stream = AudioManagerBase::MakeAudioInputStream(params, device_id); + + // The audio manager for Android creates streams intended for real-time + // VoIP sessions and therefore sets the audio mode to MODE_IN_COMMUNICATION. + // If a Bluetooth headset is used, the audio stream will use the SCO + // channel and therefore have a limited bandwidth (8kHz). + if (stream && has_no_input_streams) { + communication_mode_is_on_ = true; + SetCommunicationAudioModeOn(true); + } return stream; } void AudioManagerAndroid::ReleaseOutputStream(AudioOutputStream* stream) { + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); AudioManagerBase::ReleaseOutputStream(stream); - if (!output_stream_count()) { - SetAudioMode(kAudioModeNormal); - } - base::AutoLock lock(streams_lock_); streams_.erase(static_cast<OpenSLESOutputStream*>(stream)); } void AudioManagerAndroid::ReleaseInputStream(AudioInputStream* stream) { + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); + DCHECK(!j_audio_manager_.is_null()); AudioManagerBase::ReleaseInputStream(stream); + + // Restore the audio mode which was used before the first communication- + // mode stream was created. + if (HasNoAudioInputStreams()) { + communication_mode_is_on_ = false; + SetCommunicationAudioModeOn(false); + } } AudioOutputStream* AudioManagerAndroid::MakeLinearOutputStream( const AudioParameters& params) { DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); - return new OpenSLESOutputStream(this, params); + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); + return new OpenSLESOutputStream(this, params, SL_ANDROID_STREAM_MEDIA); } AudioOutputStream* AudioManagerAndroid::MakeLowLatencyOutputStream( const AudioParameters& params, - const std::string& device_id, - const std::string& input_device_id) { + const std::string& device_id) { DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); - return new OpenSLESOutputStream(this, params); + + // Set stream type which matches the current system-wide audio mode used by + // the Android audio manager. + const SLint32 stream_type = communication_mode_is_on_ ? + SL_ANDROID_STREAM_VOICE : SL_ANDROID_STREAM_MEDIA; + return new OpenSLESOutputStream(this, params, stream_type); } AudioInputStream* AudioManagerAndroid::MakeLinearInputStream( @@ -187,13 +221,18 @@ AudioInputStream* AudioManagerAndroid::MakeLinearInputStream( AudioInputStream* AudioManagerAndroid::MakeLowLatencyInputStream( const AudioParameters& params, const std::string& device_id) { + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); DLOG_IF(ERROR, device_id.empty()) << "Invalid device ID!"; - // Utilize the device ID to select the correct input device. + + // Use the device ID to select the correct input device. // Note that the input device is always associated with a certain output // device, i.e., this selection does also switch the output device. // All input and output streams will be affected by the device selection. - SetAudioDevice(device_id); + if (!SetAudioDevice(device_id)) { + LOG(ERROR) << "Unable to select audio device!"; + return NULL; + } if (params.effects() != AudioParameters::NO_EFFECTS) { // Platform effects can only be enabled through the AudioRecord path. @@ -211,22 +250,25 @@ AudioInputStream* AudioManagerAndroid::MakeLowLatencyInputStream( return new OpenSLESInputStream(this, params); } -int AudioManagerAndroid::GetOptimalOutputFrameSize(int sample_rate, - int channels) { - if (IsAudioLowLatencySupported()) { - return GetAudioLowLatencyOutputFrameSize(); - } else { - return std::max(kDefaultOutputBufferSize, - Java_AudioManagerAndroid_getMinOutputFrameSize( - base::android::AttachCurrentThread(), - sample_rate, channels)); - } +// static +bool AudioManagerAndroid::RegisterAudioManager(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +void AudioManagerAndroid::SetMute(JNIEnv* env, jobject obj, jboolean muted) { + GetTaskRunner()->PostTask( + FROM_HERE, + base::Bind( + &AudioManagerAndroid::DoSetMuteOnAudioThread, + base::Unretained(this), + muted)); } AudioParameters AudioManagerAndroid::GetPreferredOutputStreamParameters( const std::string& output_device_id, const AudioParameters& input_params) { // TODO(tommi): Support |output_device_id|. + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); DLOG_IF(ERROR, !output_device_id.empty()) << "Not implemented!"; ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; int sample_rate = GetNativeOutputSampleRate(); @@ -252,57 +294,55 @@ AudioParameters AudioManagerAndroid::GetPreferredOutputStreamParameters( sample_rate, bits_per_sample, buffer_size, AudioParameters::NO_EFFECTS); } -// static -bool AudioManagerAndroid::RegisterAudioManager(JNIEnv* env) { - return RegisterNativesImpl(env); +bool AudioManagerAndroid::HasNoAudioInputStreams() { + return input_stream_count() == 0; } -void AudioManagerAndroid::Init() { +void AudioManagerAndroid::InitializeOnAudioThread() { + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); + + // Create the Android audio manager on the audio thread. + DVLOG(2) << "Creating Java part of the audio manager"; + j_audio_manager_.Reset( + Java_AudioManagerAndroid_createAudioManagerAndroid( + base::android::AttachCurrentThread(), + base::android::GetApplicationContext(), + reinterpret_cast<intptr_t>(this))); + + // Prepare the list of audio devices and register receivers for device + // notifications. Java_AudioManagerAndroid_init( base::android::AttachCurrentThread(), j_audio_manager_.obj()); } -void AudioManagerAndroid::Close() { +void AudioManagerAndroid::ShutdownOnAudioThread() { + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); + DVLOG(2) << "Destroying Java part of the audio manager"; Java_AudioManagerAndroid_close( base::android::AttachCurrentThread(), j_audio_manager_.obj()); + j_audio_manager_.Reset(); } -void AudioManagerAndroid::SetMute(JNIEnv* env, jobject obj, jboolean muted) { - GetMessageLoop()->PostTask( - FROM_HERE, - base::Bind( - &AudioManagerAndroid::DoSetMuteOnAudioThread, - base::Unretained(this), - muted)); -} - -void AudioManagerAndroid::DoSetMuteOnAudioThread(bool muted) { - base::AutoLock lock(streams_lock_); - for (OutputStreams::iterator it = streams_.begin(); - it != streams_.end(); ++it) { - (*it)->SetMute(muted); - } -} - -void AudioManagerAndroid::SetAudioMode(int mode) { - Java_AudioManagerAndroid_setMode( +void AudioManagerAndroid::SetCommunicationAudioModeOn(bool on) { + Java_AudioManagerAndroid_setCommunicationAudioModeOn( base::android::AttachCurrentThread(), - j_audio_manager_.obj(), mode); + j_audio_manager_.obj(), on); } -void AudioManagerAndroid::SetAudioDevice(const std::string& device_id) { - JNIEnv* env = AttachCurrentThread(); +bool AudioManagerAndroid::SetAudioDevice(const std::string& device_id) { + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); // Send the unique device ID to the Java audio manager and make the // device switch. Provide an empty string to the Java audio manager // if the default device is selected. + JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jstring> j_device_id = ConvertUTF8ToJavaString( env, device_id == AudioManagerBase::kDefaultDeviceId ? std::string() : device_id); - Java_AudioManagerAndroid_setDevice( + return Java_AudioManagerAndroid_setDevice( env, j_audio_manager_.obj(), j_device_id.obj()); } @@ -324,4 +364,23 @@ int AudioManagerAndroid::GetAudioLowLatencyOutputFrameSize() { j_audio_manager_.obj()); } +int AudioManagerAndroid::GetOptimalOutputFrameSize(int sample_rate, + int channels) { + if (IsAudioLowLatencySupported()) + return GetAudioLowLatencyOutputFrameSize(); + + return std::max(kDefaultOutputBufferSize, + Java_AudioManagerAndroid_getMinOutputFrameSize( + base::android::AttachCurrentThread(), + sample_rate, channels)); +} + +void AudioManagerAndroid::DoSetMuteOnAudioThread(bool muted) { + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); + for (OutputStreams::iterator it = streams_.begin(); + it != streams_.end(); ++it) { + (*it)->SetMute(muted); + } +} + } // namespace media diff --git a/chromium/media/audio/android/audio_manager_android.h b/chromium/media/audio/android/audio_manager_android.h index 2900c0f8e29..ee5ad28e36e 100644 --- a/chromium/media/audio/android/audio_manager_android.h +++ b/chromium/media/audio/android/audio_manager_android.h @@ -10,6 +10,7 @@ #include "base/android/jni_android.h" #include "base/gtest_prod_util.h" #include "base/synchronization/lock.h" +#include "base/synchronization/waitable_event.h" #include "media/audio/audio_manager_base.h" namespace media { @@ -33,8 +34,7 @@ class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase { virtual AudioOutputStream* MakeAudioOutputStream( const AudioParameters& params, - const std::string& device_id, - const std::string& input_device_id) OVERRIDE; + const std::string& device_id) OVERRIDE; virtual AudioInputStream* MakeAudioInputStream( const AudioParameters& params, const std::string& device_id) OVERRIDE; @@ -46,8 +46,7 @@ class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase { const AudioParameters& params) OVERRIDE; virtual AudioOutputStream* MakeLowLatencyOutputStream( const AudioParameters& params, - const std::string& device_id, - const std::string& input_device_id) OVERRIDE; + const std::string& device_id) OVERRIDE; virtual AudioInputStream* MakeLinearInputStream( const AudioParameters& params, const std::string& device_id) OVERRIDE; @@ -67,10 +66,12 @@ class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase { const AudioParameters& input_params) OVERRIDE; private: - void Init(); - void Close(); - void SetAudioMode(int mode); - void SetAudioDevice(const std::string& device_id); + void InitializeOnAudioThread(); + void ShutdownOnAudioThread(); + + bool HasNoAudioInputStreams(); + void SetCommunicationAudioModeOn(bool on); + bool SetAudioDevice(const std::string& device_id); int GetNativeOutputSampleRate(); bool IsAudioLowLatencySupported(); int GetAudioLowLatencyOutputFrameSize(); @@ -78,18 +79,15 @@ class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase { void DoSetMuteOnAudioThread(bool muted); - // Allow the AudioAndroidTest to access private methods. - FRIEND_TEST_ALL_PREFIXES(AudioAndroidOutputTest, IsAudioLowLatencySupported); - // Java AudioManager instance. base::android::ScopedJavaGlobalRef<jobject> j_audio_manager_; typedef std::set<OpenSLESOutputStream*> OutputStreams; OutputStreams streams_; - // TODO(wjia): remove this lock once unit test modules are fixed to call - // AudioManager::MakeAudioOutputStream on the audio thread. For now, this - // lock is used to guard access to |streams_|. - base::Lock streams_lock_; + + // Enabled when first input stream is created and set to false when last + // input stream is destroyed. Also affects the stream type of output streams. + bool communication_mode_is_on_; DISALLOW_COPY_AND_ASSIGN(AudioManagerAndroid); }; diff --git a/chromium/media/audio/android/audio_record_input.cc b/chromium/media/audio/android/audio_record_input.cc index 15a0c3d3b7b..3f19588b4a6 100644 --- a/chromium/media/audio/android/audio_record_input.cc +++ b/chromium/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() { @@ -90,8 +97,7 @@ void AudioRecordInputStream::Stop() { base::android::AttachCurrentThread(), j_audio_record_.obj()); // The Java thread must have been stopped at this point, so we are free to - // set |callback_|. - callback_->OnClose(this); + // clear |callback_|. callback_ = NULL; } diff --git a/chromium/media/audio/android/audio_record_input.h b/chromium/media/audio/android/audio_record_input.h index 0a2578b1079..c240038360b 100644 --- a/chromium/media/audio/android/audio_record_input.h +++ b/chromium/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<media::AudioBus> audio_bus_; + int bytes_per_sample_; + DISALLOW_COPY_AND_ASSIGN(AudioRecordInputStream); }; diff --git a/chromium/media/audio/android/opensles_input.cc b/chromium/media/audio/android/opensles_input.cc index e51ba4f3a97..1ef3aaca5ef 100644 --- a/chromium/media/audio/android/opensles_input.cc +++ b/chromium/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<SLuint32>(params.channels()); @@ -132,6 +134,7 @@ void OpenSLESInputStream::Stop() { (*simple_buffer_queue_)->Clear(simple_buffer_queue_)); started_ = false; + callback_ = NULL; } void OpenSLESInputStream::Close() { @@ -141,15 +144,9 @@ void OpenSLESInputStream::Close() { // Stop the stream if it is still recording. Stop(); { + // TODO(henrika): Do we need to hold the lock here? base::AutoLock lock(lock_); - // TODO(henrika): we use |callback_| in Close() but |callback_| is set - // in Start(). Hence, it should be cleared in Stop() and not used here. - if (callback_) { - callback_->OnClose(this); - callback_ = NULL; - } - // Destroy the buffer queue recorder object and invalidate all associated // interfaces. recorder_object_.Reset(); @@ -300,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/chromium/media/audio/android/opensles_input.h b/chromium/media/audio/android/opensles_input.h index cb07d51f78b..288ab43425e 100644 --- a/chromium/media/audio/android/opensles_input.h +++ b/chromium/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<media::AudioBus> audio_bus_; + DISALLOW_COPY_AND_ASSIGN(OpenSLESInputStream); }; diff --git a/chromium/media/audio/android/opensles_output.cc b/chromium/media/audio/android/opensles_output.cc index b71680f0a7e..41c03c7867a 100644 --- a/chromium/media/audio/android/opensles_output.cc +++ b/chromium/media/audio/android/opensles_output.cc @@ -20,8 +20,10 @@ namespace media { OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager, - const AudioParameters& params) + const AudioParameters& params, + SLint32 stream_type) : audio_manager_(manager), + stream_type_(stream_type), callback_(NULL), player_(NULL), simple_buffer_queue_(NULL), @@ -30,7 +32,8 @@ OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager, started_(false), muted_(false), volume_(1.0) { - DVLOG(2) << "OpenSLESOutputStream::OpenSLESOutputStream()"; + DVLOG(2) << "OpenSLESOutputStream::OpenSLESOutputStream(" + << "stream_type=" << stream_type << ")"; format_.formatType = SL_DATAFORMAT_PCM; format_.numChannels = static_cast<SLuint32>(params.channels()); // Provides sampling rate in milliHertz to OpenSLES. @@ -248,11 +251,11 @@ bool OpenSLESOutputStream::CreatePlayer() { player_object_.Get(), SL_IID_ANDROIDCONFIGURATION, &player_config), false); - SLint32 stream_type = SL_ANDROID_STREAM_VOICE; + // Set configuration using the stream type provided at construction. LOG_ON_FAILURE_AND_RETURN( (*player_config)->SetConfiguration(player_config, SL_ANDROID_KEY_STREAM_TYPE, - &stream_type, + &stream_type_, sizeof(SLint32)), false); diff --git a/chromium/media/audio/android/opensles_output.h b/chromium/media/audio/android/opensles_output.h index 623b0193894..b0b678cea6e 100644 --- a/chromium/media/audio/android/opensles_output.h +++ b/chromium/media/audio/android/opensles_output.h @@ -28,7 +28,8 @@ class OpenSLESOutputStream : public AudioOutputStream { static const int kMaxNumOfBuffersInQueue = 2; OpenSLESOutputStream(AudioManagerAndroid* manager, - const AudioParameters& params); + const AudioParameters& params, + SLint32 stream_type); virtual ~OpenSLESOutputStream(); @@ -77,6 +78,10 @@ class OpenSLESOutputStream : public AudioOutputStream { AudioManagerAndroid* audio_manager_; + // Audio playback stream type. + // See SLES/OpenSLES_Android.h for details. + SLint32 stream_type_; + AudioSourceCallback* callback_; // Shared engine interfaces for the app. |