summaryrefslogtreecommitdiff
path: root/media/audio
diff options
context:
space:
mode:
authorTorne (Richard Coles) <torne@google.com>2013-09-19 22:36:51 +0100
committerTorne (Richard Coles) <torne@google.com>2013-09-19 22:36:51 +0100
commitd0247b1b59f9c528cb6df88b4f2b9afaf80d181e (patch)
tree5c397fadc190cc71bffe2ffad1efc27a5b95309d /media/audio
parentf7571f5f07547e2f3e0addf48d1f2a7ec3632957 (diff)
downloadchromium_org-d0247b1b59f9c528cb6df88b4f2b9afaf80d181e.tar.gz
Merge from Chromium at DEPS revision 224184
This commit was generated by merge_to_master.py. Change-Id: Ia3424df5abed9bea642c522b9e2358dceabd8423
Diffstat (limited to 'media/audio')
-rw-r--r--media/audio/android/audio_android_unittest.cc769
-rw-r--r--media/audio/android/audio_manager_android.cc20
-rw-r--r--media/audio/android/audio_manager_android.h19
-rw-r--r--media/audio/android/opensles_input.cc197
-rw-r--r--media/audio/android/opensles_input.h24
-rw-r--r--media/audio/android/opensles_output.cc240
-rw-r--r--media/audio/android/opensles_output.h31
-rw-r--r--media/audio/android/opensles_wrapper.cc109
-rw-r--r--media/audio/audio_input_device_unittest.cc199
-rw-r--r--media/audio/audio_manager_base.cc3
-rw-r--r--media/audio/audio_manager_base.h13
-rw-r--r--media/audio/audio_manager_unittest.cc350
-rw-r--r--media/audio/cras/audio_manager_cras.cc30
-rw-r--r--media/audio/cras/audio_manager_cras.h13
-rw-r--r--media/audio/cras/cras_input.cc5
-rw-r--r--media/audio/linux/audio_manager_linux.cc4
-rw-r--r--media/audio/linux/audio_manager_linux.h8
-rw-r--r--media/audio/mac/audio_manager_mac.h8
-rw-r--r--media/audio/mock_audio_manager.cc4
-rw-r--r--media/audio/pulse/audio_manager_pulse.h8
-rw-r--r--media/audio/shared_memory_util.cc2
-rw-r--r--media/audio/win/audio_low_latency_input_win.cc136
-rw-r--r--media/audio/win/audio_low_latency_input_win.h8
-rw-r--r--media/audio/win/audio_low_latency_input_win_unittest.cc169
-rw-r--r--media/audio/win/audio_manager_win.cc5
-rw-r--r--media/audio/win/audio_manager_win.h10
-rw-r--r--media/audio/win/device_enumeration_win.cc10
-rw-r--r--media/audio/win/wavein_input_win.h2
28 files changed, 1797 insertions, 599 deletions
diff --git a/media/audio/android/audio_android_unittest.cc b/media/audio/android/audio_android_unittest.cc
new file mode 100644
index 0000000000..a8e448f821
--- /dev/null
+++ b/media/audio/android/audio_android_unittest.cc
@@ -0,0 +1,769 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/path_service.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/test_timeouts.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "media/audio/android/audio_manager_android.h"
+#include "media/audio/audio_io.h"
+#include "media/audio/audio_manager_base.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/seekable_buffer.h"
+#include "media/base/test_data_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::AtLeast;
+using ::testing::DoAll;
+using ::testing::Invoke;
+using ::testing::NotNull;
+using ::testing::Return;
+
+namespace media {
+
+ACTION_P3(CheckCountAndPostQuitTask, count, limit, loop) {
+ if (++*count >= limit) {
+ loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
+ }
+}
+
+static const char kSpeechFile_16b_s_48k[] = "speech_16b_stereo_48kHz.raw";
+static const char kSpeechFile_16b_m_48k[] = "speech_16b_mono_48kHz.raw";
+static const char kSpeechFile_16b_s_44k[] = "speech_16b_stereo_44kHz.raw";
+static const char kSpeechFile_16b_m_44k[] = "speech_16b_mono_44kHz.raw";
+
+static const float kCallbackTestTimeMs = 2000.0;
+static const int kBitsPerSample = 16;
+static const int kBytesPerSample = kBitsPerSample / 8;
+
+// Converts AudioParameters::Format enumerator to readable string.
+static std::string FormatToString(AudioParameters::Format format) {
+ switch (format) {
+ case AudioParameters::AUDIO_PCM_LINEAR:
+ return std::string("AUDIO_PCM_LINEAR");
+ case AudioParameters::AUDIO_PCM_LOW_LATENCY:
+ return std::string("AUDIO_PCM_LOW_LATENCY");
+ case AudioParameters::AUDIO_FAKE:
+ return std::string("AUDIO_FAKE");
+ case AudioParameters::AUDIO_LAST_FORMAT:
+ return std::string("AUDIO_LAST_FORMAT");
+ default:
+ return std::string();
+ }
+}
+
+// Converts ChannelLayout enumerator to readable string. Does not include
+// multi-channel cases since these layouts are not supported on Android.
+static std::string LayoutToString(ChannelLayout channel_layout) {
+ switch (channel_layout) {
+ case CHANNEL_LAYOUT_NONE:
+ return std::string("CHANNEL_LAYOUT_NONE");
+ case CHANNEL_LAYOUT_MONO:
+ return std::string("CHANNEL_LAYOUT_MONO");
+ case CHANNEL_LAYOUT_STEREO:
+ return std::string("CHANNEL_LAYOUT_STEREO");
+ case CHANNEL_LAYOUT_UNSUPPORTED:
+ default:
+ return std::string("CHANNEL_LAYOUT_UNSUPPORTED");
+ }
+}
+
+static double ExpectedTimeBetweenCallbacks(AudioParameters params) {
+ return (base::TimeDelta::FromMicroseconds(
+ params.frames_per_buffer() * base::Time::kMicrosecondsPerSecond /
+ static_cast<double>(params.sample_rate()))).InMillisecondsF();
+}
+
+std::ostream& operator<<(std::ostream& os, const AudioParameters& params) {
+ using namespace std;
+ os << endl << "format: " << FormatToString(params.format()) << endl
+ << "channel layout: " << LayoutToString(params.channel_layout()) << endl
+ << "sample rate: " << params.sample_rate() << endl
+ << "bits per sample: " << params.bits_per_sample() << endl
+ << "frames per buffer: " << params.frames_per_buffer() << endl
+ << "channels: " << params.channels() << endl
+ << "bytes per buffer: " << params.GetBytesPerBuffer() << endl
+ << "bytes per second: " << params.GetBytesPerSecond() << endl
+ << "bytes per frame: " << params.GetBytesPerFrame() << endl
+ << "frame size in ms: " << ExpectedTimeBetweenCallbacks(params);
+ return os;
+}
+
+// Gmock implementation of AudioInputStream::AudioInputCallback.
+class MockAudioInputCallback : public AudioInputStream::AudioInputCallback {
+ public:
+ MOCK_METHOD5(OnData,
+ void(AudioInputStream* stream,
+ const uint8* src,
+ uint32 size,
+ 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 {
+ public:
+ explicit FileAudioSource(base::WaitableEvent* event, const std::string& name)
+ : event_(event), pos_(0) {
+ // Reads a test file from media/test/data directory and stores it in
+ // a DecoderBuffer.
+ file_ = ReadTestDataFile(name);
+
+ // Log the name of the file which is used as input for this test.
+ base::FilePath file_path = GetTestDataFilePath(name);
+ LOG(INFO) << "Reading from file: " << file_path.value().c_str();
+ }
+
+ virtual ~FileAudioSource() {}
+
+ // AudioOutputStream::AudioSourceCallback implementation.
+
+ // Use samples read from a data file and fill up the audio buffer
+ // provided to us in the callback.
+ virtual int OnMoreData(AudioBus* audio_bus,
+ AudioBuffersState buffers_state) OVERRIDE {
+ bool stop_playing = false;
+ int max_size =
+ audio_bus->frames() * audio_bus->channels() * kBytesPerSample;
+
+ // Adjust data size and prepare for end signal if file has ended.
+ if (pos_ + max_size > file_size()) {
+ stop_playing = true;
+ max_size = file_size() - pos_;
+ }
+
+ // File data is stored as interleaved 16-bit values. Copy data samples from
+ // the file and deinterleave to match the audio bus format.
+ // FromInterleaved() will zero out any unfilled frames when there is not
+ // sufficient data remaining in the file to fill up the complete frame.
+ int frames = max_size / (audio_bus->channels() * kBytesPerSample);
+ if (max_size) {
+ audio_bus->FromInterleaved(file_->data() + pos_, frames, kBytesPerSample);
+ pos_ += max_size;
+ }
+
+ // Set event to ensure that the test can stop when the file has ended.
+ if (stop_playing)
+ event_->Signal();
+
+ 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(); }
+
+ private:
+ base::WaitableEvent* event_;
+ int pos_;
+ scoped_refptr<DecoderBuffer> file_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileAudioSource);
+};
+
+// Implements AudioInputStream::AudioInputCallback and writes the recorded
+// audio data to a local output file. Note that this implementation should
+// only be used for manually invoked and evaluated tests, hence the created
+// file will not be destroyed after the test is done since the intention is
+// that it shall be available for off-line analysis.
+class FileAudioSink : public AudioInputStream::AudioInputCallback {
+ public:
+ explicit FileAudioSink(base::WaitableEvent* event,
+ const AudioParameters& params,
+ const std::string& file_name)
+ : event_(event), params_(params) {
+ // Allocate space for ~10 seconds of data.
+ const int kMaxBufferSize = 10 * params.GetBytesPerSecond();
+ buffer_.reset(new media::SeekableBuffer(0, kMaxBufferSize));
+
+ // Open up the binary file which will be written to in the destructor.
+ base::FilePath file_path;
+ EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &file_path));
+ file_path = file_path.AppendASCII(file_name.c_str());
+ binary_file_ = file_util::OpenFile(file_path, "wb");
+ DLOG_IF(ERROR, !binary_file_) << "Failed to open binary PCM data file.";
+ LOG(INFO) << "Writing to file: " << file_path.value().c_str();
+ }
+
+ virtual ~FileAudioSink() {
+ int bytes_written = 0;
+ while (bytes_written < buffer_->forward_capacity()) {
+ const uint8* chunk;
+ int chunk_size;
+
+ // Stop writing if no more data is available.
+ if (!buffer_->GetCurrentChunk(&chunk, &chunk_size))
+ break;
+
+ // Write recorded data chunk to the file and prepare for next chunk.
+ // TODO(henrika): use file_util:: instead.
+ fwrite(chunk, 1, chunk_size, binary_file_);
+ buffer_->Seek(chunk_size);
+ bytes_written += chunk_size;
+ }
+ file_util::CloseFile(binary_file_);
+ }
+
+ // AudioInputStream::AudioInputCallback implementation.
+ virtual void OnData(AudioInputStream* stream,
+ const uint8* src,
+ uint32 size,
+ uint32 hardware_delay_bytes,
+ double volume) OVERRIDE {
+ // 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))
+ event_->Signal();
+ }
+
+ virtual void OnClose(AudioInputStream* stream) OVERRIDE {}
+ virtual void OnError(AudioInputStream* stream) OVERRIDE {}
+
+ private:
+ base::WaitableEvent* event_;
+ AudioParameters params_;
+ scoped_ptr<media::SeekableBuffer> buffer_;
+ FILE* binary_file_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileAudioSink);
+};
+
+// Implements AudioInputCallback and AudioSourceCallback to support full
+// duplex audio where captured samples are played out in loopback after
+// reading from a temporary FIFO storage.
+class FullDuplexAudioSinkSource
+ : public AudioInputStream::AudioInputCallback,
+ public AudioOutputStream::AudioSourceCallback {
+ public:
+ explicit FullDuplexAudioSinkSource(const AudioParameters& params)
+ : params_(params),
+ previous_time_(base::TimeTicks::Now()),
+ started_(false) {
+ // Start with a reasonably small FIFO size. It will be increased
+ // dynamically during the test if required.
+ fifo_.reset(new media::SeekableBuffer(0, 2 * params.GetBytesPerBuffer()));
+ buffer_.reset(new uint8[params_.GetBytesPerBuffer()]);
+ }
+
+ virtual ~FullDuplexAudioSinkSource() {}
+
+ // AudioInputStream::AudioInputCallback implementation
+ virtual void OnData(AudioInputStream* stream,
+ const uint8* src,
+ uint32 size,
+ uint32 hardware_delay_bytes,
+ double volume) OVERRIDE {
+ const base::TimeTicks now_time = base::TimeTicks::Now();
+ const int diff = (now_time - previous_time_).InMilliseconds();
+
+ base::AutoLock lock(lock_);
+ if (diff > 1000) {
+ started_ = true;
+ previous_time_ = now_time;
+
+ // Log out the extra delay added by the FIFO. This is a best effort
+ // estimate. We might be +- 10ms off here.
+ int extra_fifo_delay =
+ static_cast<int>(BytesToMilliseconds(fifo_->forward_bytes() + size));
+ DVLOG(1) << extra_fifo_delay;
+ }
+
+ // We add an initial delay of ~1 second before loopback starts to ensure
+ // a stable callback sequence and to avoid initial bursts which might add
+ // to the extra FIFO delay.
+ if (!started_)
+ return;
+
+ // 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)) {
+ 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
+ virtual int OnMoreData(AudioBus* dest,
+ AudioBuffersState buffers_state) OVERRIDE {
+ const int size_in_bytes =
+ (params_.bits_per_sample() / 8) * dest->frames() * dest->channels();
+ EXPECT_EQ(size_in_bytes, params_.GetBytesPerBuffer());
+
+ base::AutoLock lock(lock_);
+
+ // We add an initial delay of ~1 second before loopback starts to ensure
+ // a stable callback sequences and to avoid initial bursts which might add
+ // to the extra FIFO delay.
+ if (!started_) {
+ dest->Zero();
+ return dest->frames();
+ }
+
+ // Fill up destination with zeros if the FIFO does not contain enough
+ // data to fulfill the request.
+ if (fifo_->forward_bytes() < size_in_bytes) {
+ dest->Zero();
+ } else {
+ fifo_->Read(buffer_.get(), size_in_bytes);
+ dest->FromInterleaved(
+ buffer_.get(), dest->frames(), params_.bits_per_sample() / 8);
+ }
+
+ return dest->frames();
+ }
+
+ virtual int OnMoreIOData(AudioBus* source,
+ AudioBus* dest,
+ AudioBuffersState buffers_state) OVERRIDE {
+ NOTREACHED();
+ return 0;
+ }
+
+ virtual void OnError(AudioOutputStream* stream) OVERRIDE {}
+
+ private:
+ // Converts from bytes to milliseconds given number of bytes and existing
+ // audio parameters.
+ double BytesToMilliseconds(int bytes) const {
+ const int frames = bytes / params_.GetBytesPerFrame();
+ return (base::TimeDelta::FromMicroseconds(
+ frames * base::Time::kMicrosecondsPerSecond /
+ static_cast<double>(params_.sample_rate()))).InMillisecondsF();
+ }
+
+ AudioParameters params_;
+ base::TimeTicks previous_time_;
+ base::Lock lock_;
+ scoped_ptr<media::SeekableBuffer> fifo_;
+ scoped_ptr<uint8[]> buffer_;
+ bool started_;
+
+ DISALLOW_COPY_AND_ASSIGN(FullDuplexAudioSinkSource);
+};
+
+// Test fixture class.
+class AudioAndroidTest : public testing::Test {
+ public:
+ AudioAndroidTest() {}
+
+ protected:
+ virtual void SetUp() {
+ audio_manager_.reset(AudioManager::Create());
+ loop_.reset(new base::MessageLoopForUI());
+ }
+
+ virtual void TearDown() {}
+
+ AudioManager* audio_manager() { return audio_manager_.get(); }
+ base::MessageLoopForUI* loop() { return loop_.get(); }
+
+ AudioParameters GetDefaultInputStreamParameters() {
+ return audio_manager()->GetInputStreamParameters(
+ AudioManagerBase::kDefaultDeviceId);
+ }
+
+ AudioParameters GetDefaultOutputStreamParameters() {
+ return audio_manager()->GetDefaultOutputStreamParameters();
+ }
+
+ double AverageTimeBetweenCallbacks(int num_callbacks) const {
+ return ((end_time_ - start_time_) / static_cast<double>(num_callbacks - 1))
+ .InMillisecondsF();
+ }
+
+ 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);
+
+ int count = 0;
+ MockAudioInputCallback sink;
+
+ EXPECT_CALL(sink,
+ OnData(stream, NotNull(), params.GetBytesPerBuffer(), _, _))
+ .Times(AtLeast(num_callbacks))
+ .WillRepeatedly(
+ CheckCountAndPostQuitTask(&count, num_callbacks, loop()));
+ EXPECT_CALL(sink, OnError(stream)).Times(0);
+ EXPECT_CALL(sink, OnClose(stream)).Times(1);
+
+ EXPECT_TRUE(stream->Open());
+ stream->Start(&sink);
+ start_time_ = base::TimeTicks::Now();
+ loop()->Run();
+ end_time_ = base::TimeTicks::Now();
+ stream->Stop();
+ stream->Close();
+
+ double average_time_between_callbacks_ms =
+ AverageTimeBetweenCallbacks(num_callbacks);
+ LOG(INFO) << "expected time between callbacks: "
+ << expected_time_between_callbacks_ms << " ms";
+ LOG(INFO) << "average time between callbacks: "
+ << average_time_between_callbacks_ms << " ms";
+ 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);
+ }
+
+ void StartOutputStreamCallbacks(const AudioParameters& params) {
+ double expected_time_between_callbacks_ms =
+ 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);
+
+ int count = 0;
+ MockAudioOutputCallback 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);
+
+ EXPECT_TRUE(stream->Open());
+ stream->Start(&source);
+ start_time_ = base::TimeTicks::Now();
+ loop()->Run();
+ end_time_ = base::TimeTicks::Now();
+ stream->Stop();
+ stream->Close();
+
+ double average_time_between_callbacks_ms =
+ AverageTimeBetweenCallbacks(num_callbacks);
+ LOG(INFO) << "expected time between callbacks: "
+ << expected_time_between_callbacks_ms << " ms";
+ LOG(INFO) << "average time between callbacks: "
+ << average_time_between_callbacks_ms << " ms";
+ 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);
+ }
+
+ scoped_ptr<base::MessageLoopForUI> loop_;
+ scoped_ptr<AudioManager> audio_manager_;
+ base::TimeTicks start_time_;
+ base::TimeTicks end_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioAndroidTest);
+};
+
+// Get the default audio input parameters and log the result.
+TEST_F(AudioAndroidTest, GetInputStreamParameters) {
+ AudioParameters params = GetDefaultInputStreamParameters();
+ EXPECT_TRUE(params.IsValid());
+ VLOG(1) << params;
+}
+
+// Get the default audio output parameters and log the result.
+TEST_F(AudioAndroidTest, GetDefaultOutputStreamParameters) {
+ AudioParameters params = GetDefaultOutputStreamParameters();
+ EXPECT_TRUE(params.IsValid());
+ VLOG(1) << params;
+}
+
+// Check if low-latency output is supported and log the result as output.
+TEST_F(AudioAndroidTest, IsAudioLowLatencySupported) {
+ AudioManagerAndroid* manager =
+ static_cast<AudioManagerAndroid*>(audio_manager());
+ bool low_latency = manager->IsAudioLowLatencySupported();
+ low_latency ? LOG(INFO) << "Low latency output is supported"
+ : LOG(INFO) << "Low latency output is *not* supported";
+}
+
+// Ensure that a default input stream can be created and closed.
+TEST_F(AudioAndroidTest, CreateAndCloseInputStream) {
+ AudioParameters params = GetDefaultInputStreamParameters();
+ AudioInputStream* ais = audio_manager()->MakeAudioInputStream(
+ params, AudioManagerBase::kDefaultDeviceId);
+ EXPECT_TRUE(ais);
+ ais->Close();
+}
+
+// Ensure that a default output stream can be created and closed.
+// TODO(henrika): should we also verify that this API changes the audio mode
+// to communication mode, and calls RegisterHeadsetReceiver, the first time
+// it is called?
+TEST_F(AudioAndroidTest, CreateAndCloseOutputStream) {
+ AudioParameters params = GetDefaultOutputStreamParameters();
+ AudioOutputStream* aos = audio_manager()->MakeAudioOutputStream(
+ params, std::string(), std::string());
+ EXPECT_TRUE(aos);
+ aos->Close();
+}
+
+// Ensure that a default input stream can be opened and closed.
+TEST_F(AudioAndroidTest, OpenAndCloseInputStream) {
+ AudioParameters params = GetDefaultInputStreamParameters();
+ AudioInputStream* ais = audio_manager()->MakeAudioInputStream(
+ params, AudioManagerBase::kDefaultDeviceId);
+ EXPECT_TRUE(ais);
+ EXPECT_TRUE(ais->Open());
+ ais->Close();
+}
+
+// Ensure that a default output stream can be opened and closed.
+TEST_F(AudioAndroidTest, OpenAndCloseOutputStream) {
+ AudioParameters params = GetDefaultOutputStreamParameters();
+ AudioOutputStream* aos = audio_manager()->MakeAudioOutputStream(
+ params, std::string(), std::string());
+ EXPECT_TRUE(aos);
+ EXPECT_TRUE(aos->Open());
+ aos->Close();
+}
+
+// Start input streaming using default input parameters and ensure that the
+// callback sequence is sane.
+TEST_F(AudioAndroidTest, StartInputStreamCallbacks) {
+ AudioParameters params = GetDefaultInputStreamParameters();
+ StartInputStreamCallbacks(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_F(AudioAndroidTest, StartInputStreamCallbacksNonDefaultParameters) {
+ AudioParameters native_params = GetDefaultInputStreamParameters();
+ AudioParameters params(native_params.format(),
+ native_params.channel_layout(),
+ native_params.sample_rate(),
+ native_params.bits_per_sample(),
+ native_params.sample_rate() / 100);
+ StartInputStreamCallbacks(params);
+}
+
+// Start output streaming using default output parameters and ensure that the
+// callback sequence is sane.
+TEST_F(AudioAndroidTest, StartOutputStreamCallbacks) {
+ AudioParameters params = GetDefaultOutputStreamParameters();
+ StartOutputStreamCallbacks(params);
+}
+
+// Start output streaming using non default output 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 and to open up the
+// device in mono.
+// TODO(henrika): possibly add support for more variations.
+TEST_F(AudioAndroidTest, StartOutputStreamCallbacksNonDefaultParameters) {
+ AudioParameters native_params = GetDefaultOutputStreamParameters();
+ AudioParameters params(native_params.format(),
+ CHANNEL_LAYOUT_MONO,
+ native_params.sample_rate(),
+ native_params.bits_per_sample(),
+ native_params.sample_rate() / 100);
+ StartOutputStreamCallbacks(params);
+}
+
+// Play out a PCM file segment in real time and allow the user to verify that
+// the rendered audio sounds OK.
+// NOTE: this test requires user interaction and is not designed to run as an
+// automatized test on bots.
+TEST_F(AudioAndroidTest, DISABLED_RunOutputStreamWithFileAsSource) {
+ AudioParameters params = GetDefaultOutputStreamParameters();
+ VLOG(1) << params;
+ AudioOutputStream* aos = audio_manager()->MakeAudioOutputStream(
+ params, std::string(), std::string());
+ EXPECT_TRUE(aos);
+
+ std::string file_name;
+ if (params.sample_rate() == 48000 && params.channels() == 2) {
+ file_name = kSpeechFile_16b_s_48k;
+ } else if (params.sample_rate() == 48000 && params.channels() == 1) {
+ file_name = kSpeechFile_16b_m_48k;
+ } else if (params.sample_rate() == 44100 && params.channels() == 2) {
+ file_name = kSpeechFile_16b_s_44k;
+ } else if (params.sample_rate() == 44100 && params.channels() == 1) {
+ file_name = kSpeechFile_16b_m_44k;
+ } else {
+ FAIL() << "This test supports 44.1kHz and 48kHz mono/stereo only.";
+ return;
+ }
+
+ base::WaitableEvent event(false, false);
+ FileAudioSource source(&event, file_name);
+
+ EXPECT_TRUE(aos->Open());
+ aos->SetVolume(1.0);
+ aos->Start(&source);
+ LOG(INFO) << ">> Verify that the file is played out correctly...";
+ EXPECT_TRUE(event.TimedWait(TestTimeouts::action_max_timeout()));
+ aos->Stop();
+ aos->Close();
+}
+
+// Start input streaming and run it for ten seconds while recording to a
+// local audio file.
+// NOTE: this test requires user interaction and is not designed to run as an
+// automatized test on bots.
+TEST_F(AudioAndroidTest, DISABLED_RunSimplexInputStreamWithFileAsSink) {
+ AudioParameters params = GetDefaultInputStreamParameters();
+ VLOG(1) << params;
+ AudioInputStream* ais = audio_manager()->MakeAudioInputStream(
+ params, AudioManagerBase::kDefaultDeviceId);
+ EXPECT_TRUE(ais);
+
+ std::string file_name = base::StringPrintf("out_simplex_%d_%d_%d.pcm",
+ params.sample_rate(),
+ params.frames_per_buffer(),
+ params.channels());
+
+ base::WaitableEvent event(false, false);
+ FileAudioSink sink(&event, params, file_name);
+
+ EXPECT_TRUE(ais->Open());
+ ais->Start(&sink);
+ LOG(INFO) << ">> Speak into the microphone to record audio...";
+ EXPECT_TRUE(event.TimedWait(TestTimeouts::action_max_timeout()));
+ ais->Stop();
+ ais->Close();
+}
+
+// Same test as RunSimplexInputStreamWithFileAsSink but this time output
+// streaming is active as well (reads zeros only).
+// NOTE: this test requires user interaction and is not designed to run as an
+// automatized test on bots.
+TEST_F(AudioAndroidTest, DISABLED_RunDuplexInputStreamWithFileAsSink) {
+ AudioParameters in_params = GetDefaultInputStreamParameters();
+ AudioInputStream* ais = audio_manager()->MakeAudioInputStream(
+ in_params, AudioManagerBase::kDefaultDeviceId);
+ EXPECT_TRUE(ais);
+
+ AudioParameters out_params =
+ audio_manager()->GetDefaultOutputStreamParameters();
+ AudioOutputStream* aos = audio_manager()->MakeAudioOutputStream(
+ out_params, std::string(), std::string());
+ EXPECT_TRUE(aos);
+
+ std::string file_name = base::StringPrintf("out_duplex_%d_%d_%d.pcm",
+ in_params.sample_rate(),
+ in_params.frames_per_buffer(),
+ in_params.channels());
+
+ base::WaitableEvent event(false, false);
+ FileAudioSink sink(&event, in_params, file_name);
+ MockAudioOutputCallback 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_TRUE(ais->Open());
+ EXPECT_TRUE(aos->Open());
+ ais->Start(&sink);
+ aos->Start(&source);
+ LOG(INFO) << ">> Speak into the microphone to record audio";
+ EXPECT_TRUE(event.TimedWait(TestTimeouts::action_max_timeout()));
+ aos->Stop();
+ ais->Stop();
+ aos->Close();
+ ais->Close();
+}
+
+// Start audio in both directions while feeding captured data into a FIFO so
+// it can be read directly (in loopback) by the render side. A small extra
+// delay will be added by the FIFO and an estimate of this delay will be
+// printed out during the test.
+// NOTE: this test requires user interaction and is not designed to run as an
+// automatized test on bots.
+TEST_F(AudioAndroidTest,
+ DISABLED_RunSymmetricInputAndOutputStreamsInFullDuplex) {
+ // Get native audio parameters for the input side.
+ AudioParameters default_input_params = GetDefaultInputStreamParameters();
+
+ // Modify the parameters so that both input and output can use the same
+ // parameters by selecting 10ms as buffer size. This will also ensure that
+ // the output stream will be a mono stream since mono is default for input
+ // audio on Android.
+ AudioParameters io_params(default_input_params.format(),
+ default_input_params.channel_layout(),
+ default_input_params.sample_rate(),
+ default_input_params.bits_per_sample(),
+ default_input_params.sample_rate() / 100);
+ 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);
+
+ FullDuplexAudioSinkSource full_duplex(io_params);
+
+ // Start a full duplex audio session and print out estimates of the extra
+ // 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);
+ VLOG(1) << "HINT: an estimate of the extra FIFO delay will be updated "
+ << "once per second during this test.";
+ LOG(INFO) << ">> 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();
+}
+
+} // namespace media
diff --git a/media/audio/android/audio_manager_android.cc b/media/audio/android/audio_manager_android.cc
index e6eed7fea4..04b226fa64 100644
--- a/media/audio/android/audio_manager_android.cc
+++ b/media/audio/android/audio_manager_android.cc
@@ -16,6 +16,13 @@
namespace media {
+static void AddDefaultDevice(AudioDeviceNames* device_names) {
+ DCHECK(device_names->empty());
+ device_names->push_front(
+ AudioDeviceName(AudioManagerBase::kDefaultDeviceName,
+ AudioManagerBase::kDefaultDeviceId));
+}
+
// Maximum number of output streams that can be open simultaneously.
static const int kMaxOutputStreams = 10;
@@ -51,10 +58,13 @@ bool AudioManagerAndroid::HasAudioInputDevices() {
}
void AudioManagerAndroid::GetAudioInputDeviceNames(
- media::AudioDeviceNames* device_names) {
- DCHECK(device_names->empty());
- device_names->push_front(
- media::AudioDeviceName(kDefaultDeviceName, kDefaultDeviceId));
+ AudioDeviceNames* device_names) {
+ AddDefaultDevice(device_names);
+}
+
+void AudioManagerAndroid::GetAudioOutputDeviceNames(
+ AudioDeviceNames* device_names) {
+ AddDefaultDevice(device_names);
}
AudioParameters AudioManagerAndroid::GetInputStreamParameters(
@@ -90,7 +100,7 @@ AudioOutputStream* AudioManagerAndroid::MakeAudioOutputStream(
AudioInputStream* AudioManagerAndroid::MakeAudioInputStream(
const AudioParameters& params, const std::string& device_id) {
AudioInputStream* stream =
- AudioManagerBase::MakeAudioInputStream(params, device_id);
+ AudioManagerBase::MakeAudioInputStream(params, device_id);
return stream;
}
diff --git a/media/audio/android/audio_manager_android.h b/media/audio/android/audio_manager_android.h
index ba5bed61e3..ed2b2c3ce9 100644
--- a/media/audio/android/audio_manager_android.h
+++ b/media/audio/android/audio_manager_android.h
@@ -6,6 +6,7 @@
#define MEDIA_AUDIO_ANDROID_AUDIO_MANAGER_ANDROID_H_
#include "base/android/jni_android.h"
+#include "base/gtest_prod_util.h"
#include "media/audio/audio_manager_base.h"
namespace media {
@@ -18,8 +19,10 @@ class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase {
// Implementation of AudioManager.
virtual bool HasAudioOutputDevices() OVERRIDE;
virtual bool HasAudioInputDevices() OVERRIDE;
- virtual void GetAudioInputDeviceNames(media::AudioDeviceNames* device_names)
- OVERRIDE;
+ virtual void GetAudioInputDeviceNames(
+ AudioDeviceNames* device_names) OVERRIDE;
+ virtual void GetAudioOutputDeviceNames(
+ AudioDeviceNames* device_names) OVERRIDE;
virtual AudioParameters GetInputStreamParameters(
const std::string& device_id) OVERRIDE;
@@ -28,7 +31,8 @@ class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase {
const std::string& device_id,
const std::string& input_device_id) OVERRIDE;
virtual AudioInputStream* MakeAudioInputStream(
- const AudioParameters& params, const std::string& device_id) OVERRIDE;
+ const AudioParameters& params,
+ const std::string& device_id) OVERRIDE;
virtual void ReleaseOutputStream(AudioOutputStream* stream) OVERRIDE;
virtual void ReleaseInputStream(AudioInputStream* stream) OVERRIDE;
@@ -40,9 +44,11 @@ class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase {
const std::string& device_id,
const std::string& input_device_id) OVERRIDE;
virtual AudioInputStream* MakeLinearInputStream(
- const AudioParameters& params, const std::string& device_id) OVERRIDE;
+ const AudioParameters& params,
+ const std::string& device_id) OVERRIDE;
virtual AudioInputStream* MakeLowLatencyInputStream(
- const AudioParameters& params, const std::string& device_id) OVERRIDE;
+ const AudioParameters& params,
+ const std::string& device_id) OVERRIDE;
static bool RegisterAudioManager(JNIEnv* env);
@@ -62,6 +68,9 @@ class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase {
int GetAudioLowLatencyOutputFrameSize();
int GetOptimalOutputFrameSize(int sample_rate, int channels);
+ // Allow the AudioAndroidTest to access private methods.
+ FRIEND_TEST_ALL_PREFIXES(AudioAndroidTest, IsAudioLowLatencySupported);
+
// Java AudioManager instance.
base::android::ScopedJavaGlobalRef<jobject> j_audio_manager_;
diff --git a/media/audio/android/opensles_input.cc b/media/audio/android/opensles_input.cc
index 15c3eac372..a0e4ce3b98 100644
--- a/media/audio/android/opensles_input.cc
+++ b/media/audio/android/opensles_input.cc
@@ -4,16 +4,17 @@
#include "media/audio/android/opensles_input.h"
+#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "media/audio/android/audio_manager_android.h"
-#define LOG_ON_FAILURE_AND_RETURN(op, ...) \
- do { \
- SLresult err = (op); \
- if (err != SL_RESULT_SUCCESS) { \
+#define LOG_ON_FAILURE_AND_RETURN(op, ...) \
+ do { \
+ SLresult err = (op); \
+ if (err != SL_RESULT_SUCCESS) { \
DLOG(ERROR) << #op << " failed: " << err; \
- return __VA_ARGS__; \
- } \
+ return __VA_ARGS__; \
+ } \
} while (0)
namespace media {
@@ -24,9 +25,10 @@ OpenSLESInputStream::OpenSLESInputStream(AudioManagerAndroid* audio_manager,
callback_(NULL),
recorder_(NULL),
simple_buffer_queue_(NULL),
- active_queue_(0),
+ active_buffer_index_(0),
buffer_size_bytes_(0),
started_(false) {
+ DVLOG(2) << "OpenSLESInputStream::OpenSLESInputStream()";
format_.formatType = SL_DATAFORMAT_PCM;
format_.numChannels = static_cast<SLuint32>(params.channels());
// Provides sampling rate in milliHertz to OpenSLES.
@@ -47,6 +49,8 @@ OpenSLESInputStream::OpenSLESInputStream(AudioManagerAndroid* audio_manager,
}
OpenSLESInputStream::~OpenSLESInputStream() {
+ DVLOG(2) << "OpenSLESInputStream::~OpenSLESInputStream()";
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!recorder_object_.Get());
DCHECK(!engine_object_.Get());
DCHECK(!recorder_);
@@ -55,6 +59,8 @@ OpenSLESInputStream::~OpenSLESInputStream() {
}
bool OpenSLESInputStream::Open() {
+ DVLOG(2) << "OpenSLESInputStream::Open()";
+ DCHECK(thread_checker_.CalledOnValidThread());
if (engine_object_.Get())
return false;
@@ -67,44 +73,59 @@ bool OpenSLESInputStream::Open() {
}
void OpenSLESInputStream::Start(AudioInputCallback* callback) {
+ DVLOG(2) << "OpenSLESInputStream::Start()";
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(callback);
DCHECK(recorder_);
DCHECK(simple_buffer_queue_);
if (started_)
return;
- // Enable the flags before streaming.
+ base::AutoLock lock(lock_);
+ DCHECK(callback_ == NULL || callback_ == callback);
callback_ = callback;
- active_queue_ = 0;
- started_ = true;
+ active_buffer_index_ = 0;
+ // Enqueues kMaxNumOfBuffersInQueue zero buffers to get the ball rolling.
+ // TODO(henrika): add support for Start/Stop/Start sequences when we are
+ // able to clear the buffer queue. There is currently a bug in the OpenSLES
+ // implementation which forces us to always call Stop() and Close() before
+ // calling Start() again.
SLresult err = SL_RESULT_UNKNOWN_ERROR;
- // Enqueues |kNumOfQueuesInBuffer| zero buffers to get the ball rolling.
- for (int i = 0; i < kNumOfQueuesInBuffer; ++i) {
+ for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i) {
err = (*simple_buffer_queue_)->Enqueue(
- simple_buffer_queue_,
- audio_data_[i],
- buffer_size_bytes_);
+ simple_buffer_queue_, audio_data_[i], buffer_size_bytes_);
if (SL_RESULT_SUCCESS != err) {
HandleError(err);
+ started_ = false;
return;
}
}
- // Start the recording by setting the state to |SL_RECORDSTATE_RECORDING|.
+ // Start the recording by setting the state to SL_RECORDSTATE_RECORDING.
+ // When the object is in the SL_RECORDSTATE_RECORDING state, adding buffers
+ // will implicitly start the filling process.
err = (*recorder_)->SetRecordState(recorder_, SL_RECORDSTATE_RECORDING);
- if (SL_RESULT_SUCCESS != err)
+ if (SL_RESULT_SUCCESS != err) {
HandleError(err);
+ started_ = false;
+ return;
+ }
+
+ started_ = true;
}
void OpenSLESInputStream::Stop() {
+ DVLOG(2) << "OpenSLESInputStream::Stop()";
+ DCHECK(thread_checker_.CalledOnValidThread());
if (!started_)
return;
- // Stop recording by setting the record state to |SL_RECORDSTATE_STOPPED|.
+ base::AutoLock lock(lock_);
+
+ // Stop recording by setting the record state to SL_RECORDSTATE_STOPPED.
LOG_ON_FAILURE_AND_RETURN(
- (*recorder_)->SetRecordState(recorder_,
- SL_RECORDSTATE_STOPPED));
+ (*recorder_)->SetRecordState(recorder_, SL_RECORDSTATE_STOPPED));
// Clear the buffer queue to get rid of old data when resuming recording.
LOG_ON_FAILURE_AND_RETURN(
@@ -114,17 +135,32 @@ void OpenSLESInputStream::Stop() {
}
void OpenSLESInputStream::Close() {
+ DVLOG(2) << "OpenSLESInputStream::Close()";
+ DCHECK(thread_checker_.CalledOnValidThread());
+
// Stop the stream if it is still recording.
Stop();
+ {
+ 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;
+ }
- // Explicitly free the player objects and invalidate their associated
- // interfaces. They have to be done in the correct order.
- recorder_object_.Reset();
- engine_object_.Reset();
- simple_buffer_queue_ = NULL;
- recorder_ = NULL;
+ // Destroy the buffer queue recorder object and invalidate all associated
+ // interfaces.
+ recorder_object_.Reset();
+ simple_buffer_queue_ = NULL;
+ recorder_ = NULL;
- ReleaseAudioBuffer();
+ // Destroy the engine object. We don't store any associated interface for
+ // this object.
+ engine_object_.Reset();
+ ReleaseAudioBuffer();
+ }
audio_manager_->ReleaseInputStream(this);
}
@@ -134,9 +170,7 @@ double OpenSLESInputStream::GetMaxVolume() {
return 0.0;
}
-void OpenSLESInputStream::SetVolume(double volume) {
- NOTIMPLEMENTED();
-}
+void OpenSLESInputStream::SetVolume(double volume) { NOTIMPLEMENTED(); }
double OpenSLESInputStream::GetVolume() {
NOTIMPLEMENTED();
@@ -153,54 +187,47 @@ bool OpenSLESInputStream::GetAutomaticGainControl() {
}
bool OpenSLESInputStream::CreateRecorder() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!engine_object_.Get());
+ DCHECK(!recorder_object_.Get());
+ DCHECK(!recorder_);
+ DCHECK(!simple_buffer_queue_);
+
// Initializes the engine object with specific option. After working with the
// object, we need to free the object and its resources.
SLEngineOption option[] = {
- { SL_ENGINEOPTION_THREADSAFE, static_cast<SLuint32>(SL_BOOLEAN_TRUE) }
- };
- LOG_ON_FAILURE_AND_RETURN(slCreateEngine(engine_object_.Receive(),
- 1,
- option,
- 0,
- NULL,
- NULL),
- false);
+ {SL_ENGINEOPTION_THREADSAFE, static_cast<SLuint32>(SL_BOOLEAN_TRUE)}};
+ LOG_ON_FAILURE_AND_RETURN(
+ slCreateEngine(engine_object_.Receive(), 1, option, 0, NULL, NULL),
+ false);
// Realize the SL engine object in synchronous mode.
- LOG_ON_FAILURE_AND_RETURN(engine_object_->Realize(engine_object_.Get(),
- SL_BOOLEAN_FALSE),
- false);
+ LOG_ON_FAILURE_AND_RETURN(
+ engine_object_->Realize(engine_object_.Get(), SL_BOOLEAN_FALSE), false);
// Get the SL engine interface which is implicit.
SLEngineItf engine;
- LOG_ON_FAILURE_AND_RETURN(engine_object_->GetInterface(engine_object_.Get(),
- SL_IID_ENGINE,
- &engine),
+ LOG_ON_FAILURE_AND_RETURN(engine_object_->GetInterface(
+ engine_object_.Get(), SL_IID_ENGINE, &engine),
false);
// Audio source configuration.
SLDataLocator_IODevice mic_locator = {
- SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
- SL_DEFAULTDEVICEID_AUDIOINPUT, NULL
- };
- SLDataSource audio_source = { &mic_locator, NULL };
+ SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
+ SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
+ SLDataSource audio_source = {&mic_locator, NULL};
// Audio sink configuration.
SLDataLocator_AndroidSimpleBufferQueue buffer_queue = {
- SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, // Locator type.
- static_cast<SLuint32>(kNumOfQueuesInBuffer) // Number of buffers.
- };
- SLDataSink audio_sink = { &buffer_queue, &format_ };
+ SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
+ static_cast<SLuint32>(kMaxNumOfBuffersInQueue)};
+ SLDataSink audio_sink = {&buffer_queue, &format_};
// Create an audio recorder.
- const SLInterfaceID interface_id[] = {
- SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
- SL_IID_ANDROIDCONFIGURATION
- };
- const SLboolean interface_required[] = {
- SL_BOOLEAN_TRUE,
- SL_BOOLEAN_TRUE
- };
+ const SLInterfaceID interface_id[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+ SL_IID_ANDROIDCONFIGURATION};
+ const SLboolean interface_required[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
+
// Create AudioRecorder and specify SL_IID_ANDROIDCONFIGURATION.
LOG_ON_FAILURE_AND_RETURN(
(*engine)->CreateAudioRecorder(engine,
@@ -219,24 +246,24 @@ bool OpenSLESInputStream::CreateRecorder() {
&recorder_config),
false);
+ // Uses the main microphone tuned for audio communications.
SLint32 stream_type = SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION;
LOG_ON_FAILURE_AND_RETURN(
(*recorder_config)->SetConfiguration(recorder_config,
SL_ANDROID_KEY_RECORDING_PRESET,
- &stream_type, sizeof(SLint32)),
+ &stream_type,
+ sizeof(SLint32)),
false);
// Realize the recorder object in synchronous mode.
LOG_ON_FAILURE_AND_RETURN(
- recorder_object_->Realize(recorder_object_.Get(),
- SL_BOOLEAN_FALSE),
+ recorder_object_->Realize(recorder_object_.Get(), SL_BOOLEAN_FALSE),
false);
// Get an implicit recorder interface.
LOG_ON_FAILURE_AND_RETURN(
- recorder_object_->GetInterface(recorder_object_.Get(),
- SL_IID_RECORD,
- &recorder_),
+ recorder_object_->GetInterface(
+ recorder_object_.Get(), SL_IID_RECORD, &recorder_),
false);
// Get the simple buffer queue interface.
@@ -249,61 +276,67 @@ bool OpenSLESInputStream::CreateRecorder() {
// Register the input callback for the simple buffer queue.
// This callback will be called when receiving new data from the device.
LOG_ON_FAILURE_AND_RETURN(
- (*simple_buffer_queue_)->RegisterCallback(simple_buffer_queue_,
- SimpleBufferQueueCallback,
- this),
+ (*simple_buffer_queue_)->RegisterCallback(
+ simple_buffer_queue_, SimpleBufferQueueCallback, this),
false);
return true;
}
void OpenSLESInputStream::SimpleBufferQueueCallback(
- SLAndroidSimpleBufferQueueItf buffer_queue, void* instance) {
+ SLAndroidSimpleBufferQueueItf buffer_queue,
+ void* instance) {
OpenSLESInputStream* stream =
reinterpret_cast<OpenSLESInputStream*>(instance);
stream->ReadBufferQueue();
}
void OpenSLESInputStream::ReadBufferQueue() {
+ base::AutoLock lock(lock_);
if (!started_)
return;
- // TODO(xians): Get an accurate delay estimation.
+ TRACE_EVENT0("audio", "OpenSLESOutputStream::ReadBufferQueue");
+
+ // TODO(henrika): Investigate if it is possible to get an accurate
+ // delay estimation.
callback_->OnData(this,
- audio_data_[active_queue_],
+ audio_data_[active_buffer_index_],
buffer_size_bytes_,
buffer_size_bytes_,
0.0);
// Done with this buffer. Send it to device for recording.
- SLresult err = (*simple_buffer_queue_)->Enqueue(
- simple_buffer_queue_,
- audio_data_[active_queue_],
- buffer_size_bytes_);
+ SLresult err =
+ (*simple_buffer_queue_)->Enqueue(simple_buffer_queue_,
+ audio_data_[active_buffer_index_],
+ buffer_size_bytes_);
if (SL_RESULT_SUCCESS != err)
HandleError(err);
- active_queue_ = (active_queue_ + 1) % kNumOfQueuesInBuffer;
+ active_buffer_index_ = (active_buffer_index_ + 1) % kMaxNumOfBuffersInQueue;
}
void OpenSLESInputStream::SetupAudioBuffer() {
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!audio_data_[0]);
- for (int i = 0; i < kNumOfQueuesInBuffer; ++i) {
+ for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i) {
audio_data_[i] = new uint8[buffer_size_bytes_];
}
}
void OpenSLESInputStream::ReleaseAudioBuffer() {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (audio_data_[0]) {
- for (int i = 0; i < kNumOfQueuesInBuffer; ++i) {
- delete [] audio_data_[i];
+ for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i) {
+ delete[] audio_data_[i];
audio_data_[i] = NULL;
}
}
}
void OpenSLESInputStream::HandleError(SLresult error) {
- DLOG(FATAL) << "OpenSLES Input error " << error;
+ DLOG(ERROR) << "OpenSLES Input error " << error;
if (callback_)
callback_->OnError(this);
}
diff --git a/media/audio/android/opensles_input.h b/media/audio/android/opensles_input.h
index 9743992fc6..e05831c671 100644
--- a/media/audio/android/opensles_input.h
+++ b/media/audio/android/opensles_input.h
@@ -9,18 +9,23 @@
#include <SLES/OpenSLES_Android.h>
#include "base/compiler_specific.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_checker.h"
+#include "media/audio/android/opensles_util.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_parameters.h"
-#include "media/audio/android/opensles_util.h"
namespace media {
class AudioManagerAndroid;
// Implements PCM audio input support for Android using the OpenSLES API.
+// This class is created and lives on the Audio Manager thread but recorded
+// audio buffers are given to us from an internal OpenSLES audio thread.
+// All public methods should be called on the Audio Manager thread.
class OpenSLESInputStream : public AudioInputStream {
public:
- static const int kNumOfQueuesInBuffer = 2;
+ static const int kMaxNumOfBuffersInQueue = 2;
OpenSLESInputStream(AudioManagerAndroid* manager,
const AudioParameters& params);
@@ -41,9 +46,12 @@ class OpenSLESInputStream : public AudioInputStream {
private:
bool CreateRecorder();
+ // Called from OpenSLES specific audio worker thread.
static void SimpleBufferQueueCallback(
- SLAndroidSimpleBufferQueueItf buffer_queue, void* instance);
+ SLAndroidSimpleBufferQueueItf buffer_queue,
+ void* instance);
+ // Called from OpenSLES specific audio worker thread.
void ReadBufferQueue();
// Called in Open();
@@ -56,6 +64,12 @@ class OpenSLESInputStream : public AudioInputStream {
// the attached AudioInputCallback::OnError().
void HandleError(SLresult error);
+ base::ThreadChecker thread_checker_;
+
+ // Protects |callback_|, |active_buffer_index_|, |audio_data_|,
+ // |buffer_size_bytes_| and |simple_buffer_queue_|.
+ base::Lock lock_;
+
AudioManagerAndroid* audio_manager_;
AudioInputCallback* callback_;
@@ -73,9 +87,9 @@ class OpenSLESInputStream : public AudioInputStream {
// Audio buffers that are allocated in the constructor based on
// info from audio parameters.
- uint8* audio_data_[kNumOfQueuesInBuffer];
+ uint8* audio_data_[kMaxNumOfBuffersInQueue];
- int active_queue_;
+ int active_buffer_index_;
int buffer_size_bytes_;
bool started_;
diff --git a/media/audio/android/opensles_output.cc b/media/audio/android/opensles_output.cc
index c6d455715d..5643f833c3 100644
--- a/media/audio/android/opensles_output.cc
+++ b/media/audio/android/opensles_output.cc
@@ -8,13 +8,13 @@
#include "base/logging.h"
#include "media/audio/android/audio_manager_android.h"
-#define LOG_ON_FAILURE_AND_RETURN(op, ...) \
- do { \
- SLresult err = (op); \
- if (err != SL_RESULT_SUCCESS) { \
+#define LOG_ON_FAILURE_AND_RETURN(op, ...) \
+ do { \
+ SLresult err = (op); \
+ if (err != SL_RESULT_SUCCESS) { \
DLOG(ERROR) << #op << " failed: " << err; \
- return __VA_ARGS__; \
- } \
+ return __VA_ARGS__; \
+ } \
} while (0)
namespace media {
@@ -25,10 +25,11 @@ OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager,
callback_(NULL),
player_(NULL),
simple_buffer_queue_(NULL),
- active_queue_(0),
+ active_buffer_index_(0),
buffer_size_bytes_(0),
started_(false),
volume_(1.0) {
+ DVLOG(2) << "OpenSLESOutputStream::OpenSLESOutputStream()";
format_.formatType = SL_DATAFORMAT_PCM;
format_.numChannels = static_cast<SLuint32>(params.channels());
// Provides sampling rate in milliHertz to OpenSLES.
@@ -50,6 +51,8 @@ OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager,
}
OpenSLESOutputStream::~OpenSLESOutputStream() {
+ DVLOG(2) << "OpenSLESOutputStream::~OpenSLESOutputStream()";
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!engine_object_.Get());
DCHECK(!player_object_.Get());
DCHECK(!output_mixer_.Get());
@@ -59,6 +62,8 @@ OpenSLESOutputStream::~OpenSLESOutputStream() {
}
bool OpenSLESOutputStream::Open() {
+ DVLOG(2) << "OpenSLESOutputStream::Open()";
+ DCHECK(thread_checker_.CalledOnValidThread());
if (engine_object_.Get())
return false;
@@ -66,37 +71,46 @@ bool OpenSLESOutputStream::Open() {
return false;
SetupAudioBuffer();
+ active_buffer_index_ = 0;
return true;
}
void OpenSLESOutputStream::Start(AudioSourceCallback* callback) {
+ DVLOG(2) << "OpenSLESOutputStream::Start()";
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(callback);
DCHECK(player_);
DCHECK(simple_buffer_queue_);
if (started_)
return;
- // Enable the flags before streaming.
+ base::AutoLock lock(lock_);
+ DCHECK(callback_ == NULL || callback_ == callback);
callback_ = callback;
- active_queue_ = 0;
- started_ = true;
// Avoid start-up glitches by filling up one buffer queue before starting
// the stream.
- FillBufferQueue();
+ FillBufferQueueNoLock();
- // Start streaming data by setting the play state to |SL_PLAYSTATE_PLAYING|.
+ // Start streaming data by setting the play state to SL_PLAYSTATE_PLAYING.
+ // For a player object, when the object is in the SL_PLAYSTATE_PLAYING
+ // state, adding buffers will implicitly start playback.
LOG_ON_FAILURE_AND_RETURN(
(*player_)->SetPlayState(player_, SL_PLAYSTATE_PLAYING));
+
+ started_ = true;
}
void OpenSLESOutputStream::Stop() {
+ DVLOG(2) << "OpenSLESOutputStream::Stop()";
+ DCHECK(thread_checker_.CalledOnValidThread());
if (!started_)
return;
- started_ = false;
- // Stop playing by setting the play state to |SL_PLAYSTATE_STOPPED|.
+ base::AutoLock lock(lock_);
+
+ // Stop playing by setting the play state to SL_PLAYSTATE_STOPPED.
LOG_ON_FAILURE_AND_RETURN(
(*player_)->SetPlayState(player_, SL_PLAYSTATE_STOPPED));
@@ -104,26 +118,48 @@ void OpenSLESOutputStream::Stop() {
// resuming playing.
LOG_ON_FAILURE_AND_RETURN(
(*simple_buffer_queue_)->Clear(simple_buffer_queue_));
+
+#ifndef NDEBUG
+ // Verify that the buffer queue is in fact cleared as it should.
+ SLAndroidSimpleBufferQueueState buffer_queue_state;
+ LOG_ON_FAILURE_AND_RETURN((*simple_buffer_queue_)->GetState(
+ simple_buffer_queue_, &buffer_queue_state));
+ DCHECK_EQ(0u, buffer_queue_state.count);
+ DCHECK_EQ(0u, buffer_queue_state.index);
+#endif
+
+ started_ = false;
}
void OpenSLESOutputStream::Close() {
+ DVLOG(2) << "OpenSLESOutputStream::Close()";
+ DCHECK(thread_checker_.CalledOnValidThread());
+
// Stop the stream if it is still playing.
Stop();
-
- // Explicitly free the player objects and invalidate their associated
- // interfaces. They have to be done in the correct order.
- player_object_.Reset();
- output_mixer_.Reset();
- engine_object_.Reset();
- simple_buffer_queue_ = NULL;
- player_ = NULL;
-
- ReleaseAudioBuffer();
+ {
+ // Destroy the buffer queue player object and invalidate all associated
+ // interfaces.
+ player_object_.Reset();
+ simple_buffer_queue_ = NULL;
+ player_ = NULL;
+
+ // Destroy the mixer object. We don't store any associated interface for
+ // this object.
+ output_mixer_.Reset();
+
+ // Destroy the engine object. We don't store any associated interface for
+ // this object.
+ engine_object_.Reset();
+ ReleaseAudioBuffer();
+ }
audio_manager_->ReleaseOutputStream(this);
}
void OpenSLESOutputStream::SetVolume(double volume) {
+ DVLOG(2) << "OpenSLESOutputStream::SetVolume(" << volume << ")";
+ DCHECK(thread_checker_.CalledOnValidThread());
float volume_float = static_cast<float>(volume);
if (volume_float < 0.0f || volume_float > 1.0f) {
return;
@@ -132,70 +168,61 @@ void OpenSLESOutputStream::SetVolume(double volume) {
}
void OpenSLESOutputStream::GetVolume(double* volume) {
+ DCHECK(thread_checker_.CalledOnValidThread());
*volume = static_cast<double>(volume_);
}
bool OpenSLESOutputStream::CreatePlayer() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!engine_object_.Get());
+ DCHECK(!player_object_.Get());
+ DCHECK(!output_mixer_.Get());
+ DCHECK(!player_);
+ DCHECK(!simple_buffer_queue_);
+
// Initializes the engine object with specific option. After working with the
// object, we need to free the object and its resources.
SLEngineOption option[] = {
- { SL_ENGINEOPTION_THREADSAFE, static_cast<SLuint32>(SL_BOOLEAN_TRUE) }
- };
+ {SL_ENGINEOPTION_THREADSAFE, static_cast<SLuint32>(SL_BOOLEAN_TRUE)}};
LOG_ON_FAILURE_AND_RETURN(
slCreateEngine(engine_object_.Receive(), 1, option, 0, NULL, NULL),
false);
// Realize the SL engine object in synchronous mode.
LOG_ON_FAILURE_AND_RETURN(
- engine_object_->Realize(engine_object_.Get(), SL_BOOLEAN_FALSE),
- false);
+ engine_object_->Realize(engine_object_.Get(), SL_BOOLEAN_FALSE), false);
// Get the SL engine interface which is implicit.
SLEngineItf engine;
- LOG_ON_FAILURE_AND_RETURN(
- engine_object_->GetInterface(engine_object_.Get(),
- SL_IID_ENGINE,
- &engine),
- false);
+ LOG_ON_FAILURE_AND_RETURN(engine_object_->GetInterface(
+ engine_object_.Get(), SL_IID_ENGINE, &engine),
+ false);
// Create ouput mixer object to be used by the player.
- LOG_ON_FAILURE_AND_RETURN(
- (*engine)->CreateOutputMix(engine,
- output_mixer_.Receive(),
- 0,
- NULL,
- NULL),
- false);
+ LOG_ON_FAILURE_AND_RETURN((*engine)->CreateOutputMix(
+ engine, output_mixer_.Receive(), 0, NULL, NULL),
+ false);
// Realizing the output mix object in synchronous mode.
LOG_ON_FAILURE_AND_RETURN(
- output_mixer_->Realize(output_mixer_.Get(), SL_BOOLEAN_FALSE),
- false);
+ output_mixer_->Realize(output_mixer_.Get(), SL_BOOLEAN_FALSE), false);
// Audio source configuration.
SLDataLocator_AndroidSimpleBufferQueue simple_buffer_queue = {
- SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
- static_cast<SLuint32>(kNumOfQueuesInBuffer)
- };
- SLDataSource audio_source = { &simple_buffer_queue, &format_ };
+ SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
+ static_cast<SLuint32>(kMaxNumOfBuffersInQueue)};
+ SLDataSource audio_source = {&simple_buffer_queue, &format_};
// Audio sink configuration.
- SLDataLocator_OutputMix locator_output_mix = {
- SL_DATALOCATOR_OUTPUTMIX, output_mixer_.Get()
- };
- SLDataSink audio_sink = { &locator_output_mix, NULL };
+ SLDataLocator_OutputMix locator_output_mix = {SL_DATALOCATOR_OUTPUTMIX,
+ output_mixer_.Get()};
+ SLDataSink audio_sink = {&locator_output_mix, NULL};
// Create an audio player.
- const SLInterfaceID interface_id[] = {
- SL_IID_BUFFERQUEUE,
- SL_IID_VOLUME,
- SL_IID_ANDROIDCONFIGURATION
- };
- const SLboolean interface_required[] = {
- SL_BOOLEAN_TRUE,
- SL_BOOLEAN_TRUE,
- SL_BOOLEAN_TRUE
- };
+ const SLInterfaceID interface_id[] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME,
+ SL_IID_ANDROIDCONFIGURATION};
+ const SLboolean interface_required[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE,
+ SL_BOOLEAN_TRUE};
LOG_ON_FAILURE_AND_RETURN(
(*engine)->CreateAudioPlayer(engine,
player_object_.Receive(),
@@ -209,22 +236,21 @@ bool OpenSLESOutputStream::CreatePlayer() {
// Create AudioPlayer and specify SL_IID_ANDROIDCONFIGURATION.
SLAndroidConfigurationItf player_config;
LOG_ON_FAILURE_AND_RETURN(
- player_object_->GetInterface(player_object_.Get(),
- SL_IID_ANDROIDCONFIGURATION,
- &player_config),
+ player_object_->GetInterface(
+ player_object_.Get(), SL_IID_ANDROIDCONFIGURATION, &player_config),
false);
SLint32 stream_type = SL_ANDROID_STREAM_VOICE;
LOG_ON_FAILURE_AND_RETURN(
(*player_config)->SetConfiguration(player_config,
SL_ANDROID_KEY_STREAM_TYPE,
- &stream_type, sizeof(SLint32)),
+ &stream_type,
+ sizeof(SLint32)),
false);
// Realize the player object in synchronous mode.
LOG_ON_FAILURE_AND_RETURN(
- player_object_->Realize(player_object_.Get(), SL_BOOLEAN_FALSE),
- false);
+ player_object_->Realize(player_object_.Get(), SL_BOOLEAN_FALSE), false);
// Get an implicit player interface.
LOG_ON_FAILURE_AND_RETURN(
@@ -233,72 +259,104 @@ bool OpenSLESOutputStream::CreatePlayer() {
// Get the simple buffer queue interface.
LOG_ON_FAILURE_AND_RETURN(
- player_object_->GetInterface(player_object_.Get(),
- SL_IID_BUFFERQUEUE,
- &simple_buffer_queue_),
+ player_object_->GetInterface(
+ player_object_.Get(), SL_IID_BUFFERQUEUE, &simple_buffer_queue_),
false);
// Register the input callback for the simple buffer queue.
// This callback will be called when the soundcard needs data.
LOG_ON_FAILURE_AND_RETURN(
- (*simple_buffer_queue_)->RegisterCallback(simple_buffer_queue_,
- SimpleBufferQueueCallback,
- this),
+ (*simple_buffer_queue_)->RegisterCallback(
+ simple_buffer_queue_, SimpleBufferQueueCallback, this),
false);
return true;
}
void OpenSLESOutputStream::SimpleBufferQueueCallback(
- SLAndroidSimpleBufferQueueItf buffer_queue, void* instance) {
+ SLAndroidSimpleBufferQueueItf buffer_queue,
+ void* instance) {
OpenSLESOutputStream* stream =
reinterpret_cast<OpenSLESOutputStream*>(instance);
stream->FillBufferQueue();
}
void OpenSLESOutputStream::FillBufferQueue() {
+ base::AutoLock lock(lock_);
if (!started_)
return;
TRACE_EVENT0("audio", "OpenSLESOutputStream::FillBufferQueue");
+
+ // Verify that we are in a playing state.
+ SLuint32 state;
+ SLresult err = (*player_)->GetPlayState(player_, &state);
+ if (SL_RESULT_SUCCESS != err) {
+ HandleError(err);
+ return;
+ }
+ if (state != SL_PLAYSTATE_PLAYING) {
+ DLOG(WARNING) << "Received callback in non-playing state";
+ return;
+ }
+
+ // Fill up one buffer in the queue by asking the registered source for
+ // data using the OnMoreData() callback.
+ FillBufferQueueNoLock();
+}
+
+void OpenSLESOutputStream::FillBufferQueueNoLock() {
+ // Ensure that the calling thread has acquired the lock since it is not
+ // done in this method.
+ lock_.AssertAcquired();
+
// Read data from the registered client source.
- // TODO(xians): Get an accurate delay estimation.
- uint32 hardware_delay = buffer_size_bytes_;
+ // TODO(henrika): Investigate if it is possible to get a more accurate
+ // delay estimation.
+ const uint32 hardware_delay = buffer_size_bytes_;
int frames_filled = callback_->OnMoreData(
audio_bus_.get(), AudioBuffersState(0, hardware_delay));
- if (frames_filled <= 0)
- return; // Audio source is shutting down, or halted on error.
- int num_filled_bytes =
+ if (frames_filled <= 0) {
+ // Audio source is shutting down, or halted on error.
+ return;
+ }
+
+ // Note: If the internal representation ever changes from 16-bit PCM to
+ // raw float, the data must be clipped and sanitized since it may come
+ // from an untrusted source such as NaCl.
+ audio_bus_->Scale(volume_);
+ audio_bus_->ToInterleaved(frames_filled,
+ format_.bitsPerSample / 8,
+ audio_data_[active_buffer_index_]);
+
+ const int num_filled_bytes =
frames_filled * audio_bus_->channels() * format_.bitsPerSample / 8;
DCHECK_LE(static_cast<size_t>(num_filled_bytes), buffer_size_bytes_);
- // Note: If this ever changes to output raw float the data must be clipped and
- // sanitized since it may come from an untrusted source such as NaCl.
- audio_bus_->Scale(volume_);
- audio_bus_->ToInterleaved(
- frames_filled, format_.bitsPerSample / 8, audio_data_[active_queue_]);
// Enqueue the buffer for playback.
- SLresult err = (*simple_buffer_queue_)->Enqueue(
- simple_buffer_queue_,
- audio_data_[active_queue_],
- num_filled_bytes);
+ SLresult err =
+ (*simple_buffer_queue_)->Enqueue(simple_buffer_queue_,
+ audio_data_[active_buffer_index_],
+ num_filled_bytes);
if (SL_RESULT_SUCCESS != err)
HandleError(err);
- active_queue_ = (active_queue_ + 1) % kNumOfQueuesInBuffer;
+ active_buffer_index_ = (active_buffer_index_ + 1) % kMaxNumOfBuffersInQueue;
}
void OpenSLESOutputStream::SetupAudioBuffer() {
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!audio_data_[0]);
- for (int i = 0; i < kNumOfQueuesInBuffer; ++i) {
+ for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i) {
audio_data_[i] = new uint8[buffer_size_bytes_];
}
}
void OpenSLESOutputStream::ReleaseAudioBuffer() {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (audio_data_[0]) {
- for (int i = 0; i < kNumOfQueuesInBuffer; ++i) {
- delete [] audio_data_[i];
+ for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i) {
+ delete[] audio_data_[i];
audio_data_[i] = NULL;
}
}
diff --git a/media/audio/android/opensles_output.h b/media/audio/android/opensles_output.h
index f505b5165c..7232d5da5f 100644
--- a/media/audio/android/opensles_output.h
+++ b/media/audio/android/opensles_output.h
@@ -9,6 +9,8 @@
#include <SLES/OpenSLES_Android.h>
#include "base/compiler_specific.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_checker.h"
#include "media/audio/android/opensles_util.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_parameters.h"
@@ -18,9 +20,12 @@ namespace media {
class AudioManagerAndroid;
// Implements PCM audio output support for Android using the OpenSLES API.
+// This class is created and lives on the Audio Manager thread but recorded
+// audio buffers are given to us from an internal OpenSLES audio thread.
+// All public methods should be called on the Audio Manager thread.
class OpenSLESOutputStream : public AudioOutputStream {
public:
- static const int kNumOfQueuesInBuffer = 2;
+ static const int kMaxNumOfBuffersInQueue = 2;
OpenSLESOutputStream(AudioManagerAndroid* manager,
const AudioParameters& params);
@@ -38,11 +43,18 @@ class OpenSLESOutputStream : public AudioOutputStream {
private:
bool CreatePlayer();
+ // Called from OpenSLES specific audio worker thread.
static void SimpleBufferQueueCallback(
- SLAndroidSimpleBufferQueueItf buffer_queue, void* instance);
+ SLAndroidSimpleBufferQueueItf buffer_queue,
+ void* instance);
+ // Fills up one buffer by asking the registered source for data.
+ // Called from OpenSLES specific audio worker thread.
void FillBufferQueue();
+ // Called from the audio manager thread.
+ void FillBufferQueueNoLock();
+
// Called in Open();
void SetupAudioBuffer();
@@ -53,6 +65,12 @@ class OpenSLESOutputStream : public AudioOutputStream {
// the attached AudioOutputCallback::OnError().
void HandleError(SLresult error);
+ base::ThreadChecker thread_checker_;
+
+ // Protects |callback_|, |active_buffer_index_|, |audio_data_|,
+ // |buffer_size_bytes_| and |simple_buffer_queue_|.
+ base::Lock lock_;
+
AudioManagerAndroid* audio_manager_;
AudioSourceCallback* callback_;
@@ -69,10 +87,11 @@ class OpenSLESOutputStream : public AudioOutputStream {
SLDataFormat_PCM format_;
- // Audio buffer arrays that are allocated in the constructor.
- uint8* audio_data_[kNumOfQueuesInBuffer];
+ // Audio buffers that are allocated in the constructor based on
+ // info from audio parameters.
+ uint8* audio_data_[kMaxNumOfBuffersInQueue];
- int active_queue_;
+ int active_buffer_index_;
size_t buffer_size_bytes_;
bool started_;
@@ -88,4 +107,4 @@ class OpenSLESOutputStream : public AudioOutputStream {
} // namespace media
-#endif // MEDIA_AUDIO_ANDROID_OPENSLES_INPUT_H_
+#endif // MEDIA_AUDIO_ANDROID_OPENSLES_OUTPUT_H_
diff --git a/media/audio/android/opensles_wrapper.cc b/media/audio/android/opensles_wrapper.cc
new file mode 100644
index 0000000000..b8f9ea45e4
--- /dev/null
+++ b/media/audio/android/opensles_wrapper.cc
@@ -0,0 +1,109 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The file defines the symbols from OpenSLES that android is using. It then
+// loads the library dynamically on first use.
+
+// The openSLES API is using constant as part of the API. This file will define
+// proxies for those constants and redefine those when the library is first
+// loaded. For this, it need to be able to change their content and so import
+// the headers without const. This is correct because OpenSLES.h is a C API.
+#define const
+#include <SLES/OpenSLES.h>
+#include <SLES/OpenSLES_Android.h>
+#undef const
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/native_library.h"
+
+// The constants used in chromium. SLInterfaceID is actually a pointer to
+// SLInterfaceID_. Those symbols are defined as extern symbols in the OpenSLES
+// headers. They will be initialized to their correct values when the library is
+// loaded.
+SLInterfaceID SL_IID_ENGINE = NULL;
+SLInterfaceID SL_IID_ANDROIDSIMPLEBUFFERQUEUE = NULL;
+SLInterfaceID SL_IID_ANDROIDCONFIGURATION = NULL;
+SLInterfaceID SL_IID_RECORD = NULL;
+SLInterfaceID SL_IID_BUFFERQUEUE = NULL;
+SLInterfaceID SL_IID_VOLUME = NULL;
+SLInterfaceID SL_IID_PLAY = NULL;
+
+namespace {
+
+// The name of the library to load.
+const char kOpenSLLibraryName[] = "libOpenSLES.so";
+
+// Loads the OpenSLES library, and initializes all the proxies.
+base::NativeLibrary IntializeLibraryHandle() {
+ base::NativeLibrary handle =
+ base::LoadNativeLibrary(base::FilePath(kOpenSLLibraryName), NULL);
+ DCHECK(handle) << "Unable to load " << kOpenSLLibraryName;
+
+ // Setup the proxy for each symbol.
+ // Attach the symbol name to the proxy address.
+ struct SymbolDefinition {
+ const char* name;
+ SLInterfaceID* sl_iid;
+ };
+
+ // The list of defined symbols.
+ const SymbolDefinition kSymbols[] = {
+ {"SL_IID_ENGINE", &SL_IID_ENGINE},
+ {"SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &SL_IID_ANDROIDSIMPLEBUFFERQUEUE},
+ {"SL_IID_ANDROIDCONFIGURATION", &SL_IID_ANDROIDCONFIGURATION},
+ {"SL_IID_RECORD", &SL_IID_RECORD},
+ {"SL_IID_BUFFERQUEUE", &SL_IID_BUFFERQUEUE},
+ {"SL_IID_VOLUME", &SL_IID_VOLUME},
+ {"SL_IID_PLAY", &SL_IID_PLAY}
+ };
+
+ for (size_t i = 0; i < sizeof(kSymbols) / sizeof(kSymbols[0]); ++i) {
+ memcpy(kSymbols[i].sl_iid,
+ base::GetFunctionPointerFromNativeLibrary(handle, kSymbols[i].name),
+ sizeof(SLInterfaceID));
+ DCHECK(*kSymbols[i].sl_iid) << "Unable to find symbol for "
+ << kSymbols[i].name;
+ }
+ return handle;
+}
+
+// Returns the handler to the shared library. The library itself will be lazily
+// loaded during the first call to this function.
+base::NativeLibrary LibraryHandle() {
+ // The handle is lazily initialized on the first call.
+ static base::NativeLibrary g_opensles_LibraryHandle =
+ IntializeLibraryHandle();
+ return g_opensles_LibraryHandle;
+}
+
+} // namespace
+
+// Redefine slCreateEngine symbol.
+SLresult slCreateEngine(SLObjectItf* engine,
+ SLuint32 num_options,
+ SLEngineOption* engine_options,
+ SLuint32 num_interfaces,
+ SLInterfaceID* interface_ids,
+ SLboolean* interfaces_required) {
+ typedef SLresult (*SlCreateEngineSignature)(SLObjectItf*,
+ SLuint32,
+ SLEngineOption*,
+ SLuint32,
+ SLInterfaceID*,
+ SLboolean*);
+ static SlCreateEngineSignature g_sl_create_engine_handle =
+ reinterpret_cast<SlCreateEngineSignature>(
+ base::GetFunctionPointerFromNativeLibrary(LibraryHandle(),
+ "slCreateEngine"));
+ DCHECK(g_sl_create_engine_handle)
+ << "Unable to find symbol for slCreateEngine";
+ return g_sl_create_engine_handle(engine,
+ num_options,
+ engine_options,
+ num_interfaces,
+ interface_ids,
+ interfaces_required);
+}
diff --git a/media/audio/audio_input_device_unittest.cc b/media/audio/audio_input_device_unittest.cc
deleted file mode 100644
index 61a97832f6..0000000000
--- a/media/audio/audio_input_device_unittest.cc
+++ /dev/null
@@ -1,199 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/environment.h"
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "media/audio/audio_manager.h"
-#include "media/audio/audio_manager_base.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#if defined(OS_WIN)
-#include "base/win/scoped_com_initializer.h"
-#include "media/audio/win/audio_manager_win.h"
-#include "media/audio/win/wavein_input_win.h"
-#endif
-
-namespace media {
-
-// Test fixture which allows us to override the default enumeration API on
-// Windows.
-class AudioInputDeviceTest
- : public ::testing::Test {
- protected:
- AudioInputDeviceTest()
- : audio_manager_(AudioManager::Create())
-#if defined(OS_WIN)
- , com_init_(base::win::ScopedCOMInitializer::kMTA)
-#endif
- {
- }
-
-#if defined(OS_WIN)
- bool SetMMDeviceEnumeration() {
- AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager_.get());
- // Windows Wave is used as default if Windows XP was detected =>
- // return false since MMDevice is not supported on XP.
- if (amw->enumeration_type() == AudioManagerWin::kWaveEnumeration)
- return false;
-
- amw->SetEnumerationType(AudioManagerWin::kMMDeviceEnumeration);
- return true;
- }
-
- void SetWaveEnumeration() {
- AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager_.get());
- amw->SetEnumerationType(AudioManagerWin::kWaveEnumeration);
- }
-
- std::string GetDeviceIdFromPCMWaveInAudioInputStream(
- const std::string& device_id) {
- AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager_.get());
- AudioParameters parameters(
- AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO,
- AudioParameters::kAudioCDSampleRate, 16,
- 1024);
- scoped_ptr<PCMWaveInAudioInputStream> stream(
- static_cast<PCMWaveInAudioInputStream*>(
- amw->CreatePCMWaveInAudioInputStream(parameters, device_id)));
- return stream.get() ? stream->device_id_ : std::string();
- }
-#endif
-
- // Helper method which verifies that the device list starts with a valid
- // default record followed by non-default device names.
- static void CheckDeviceNames(const AudioDeviceNames& device_names) {
- if (!device_names.empty()) {
- 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());
- EXPECT_NE(std::string(AudioManagerBase::kDefaultDeviceName),
- it->device_name);
- EXPECT_NE(std::string(AudioManagerBase::kDefaultDeviceId),
- it->unique_id);
- ++it;
- }
- } else {
- // 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";
- }
- }
-
- bool CanRunAudioTest() {
- return audio_manager_->HasAudioInputDevices();
- }
-
- scoped_ptr<AudioManager> audio_manager_;
-
-#if defined(OS_WIN)
- // The MMDevice API requires COM to be initialized on the current thread.
- base::win::ScopedCOMInitializer com_init_;
-#endif
-};
-
-// Test that devices can be enumerated.
-TEST_F(AudioInputDeviceTest, EnumerateDevices) {
- if (!CanRunAudioTest())
- return;
-
- AudioDeviceNames device_names;
- audio_manager_->GetAudioInputDeviceNames(&device_names);
- CheckDeviceNames(device_names);
-}
-
-// Run additional tests for Windows since enumeration can be done using
-// two different APIs. MMDevice is default for Vista and higher and Wave
-// is default for XP and lower.
-#if defined(OS_WIN)
-
-// Override default enumeration API and force usage of Windows MMDevice.
-// This test will only run on Windows Vista and higher.
-TEST_F(AudioInputDeviceTest, EnumerateDevicesWinMMDevice) {
- if (!CanRunAudioTest())
- return;
-
- AudioDeviceNames device_names;
- if (!SetMMDeviceEnumeration()) {
- // Usage of MMDevice will fail on XP and lower.
- LOG(WARNING) << "MM device enumeration is not supported.";
- return;
- }
- audio_manager_->GetAudioInputDeviceNames(&device_names);
- CheckDeviceNames(device_names);
-}
-
-// Override default enumeration API and force usage of Windows Wave.
-// This test will run on Windows XP, Windows Vista and Windows 7.
-TEST_F(AudioInputDeviceTest, EnumerateDevicesWinWave) {
- if (!CanRunAudioTest())
- return;
-
- AudioDeviceNames device_names;
- SetWaveEnumeration();
- audio_manager_->GetAudioInputDeviceNames(&device_names);
- CheckDeviceNames(device_names);
-}
-
-TEST_F(AudioInputDeviceTest, WinXPDeviceIdUnchanged) {
- if (!CanRunAudioTest())
- return;
-
- AudioDeviceNames xp_device_names;
- SetWaveEnumeration();
- audio_manager_->GetAudioInputDeviceNames(&xp_device_names);
- CheckDeviceNames(xp_device_names);
-
- // Device ID should remain unchanged, including the default device ID.
- for (AudioDeviceNames::iterator i = xp_device_names.begin();
- i != xp_device_names.end(); ++i) {
- EXPECT_EQ(i->unique_id,
- GetDeviceIdFromPCMWaveInAudioInputStream(i->unique_id));
- }
-}
-
-TEST_F(AudioInputDeviceTest, ConvertToWinXPInputDeviceId) {
- if (!CanRunAudioTest())
- return;
-
- if (!SetMMDeviceEnumeration()) {
- // Usage of MMDevice will fail on XP and lower.
- LOG(WARNING) << "MM device enumeration is not supported.";
- return;
- }
-
- AudioDeviceNames device_names;
- audio_manager_->GetAudioInputDeviceNames(&device_names);
- CheckDeviceNames(device_names);
-
- for (AudioDeviceNames::iterator i = device_names.begin();
- i != device_names.end(); ++i) {
- std::string converted_id =
- GetDeviceIdFromPCMWaveInAudioInputStream(i->unique_id);
- if (i == device_names.begin()) {
- // The first in the list is the default device ID, which should not be
- // changed when passed to PCMWaveInAudioInputStream.
- EXPECT_EQ(i->unique_id, converted_id);
- } else {
- // MMDevice-style device IDs should be converted to WaveIn-style device
- // IDs.
- EXPECT_NE(i->unique_id, converted_id);
- }
- }
-}
-
-#endif
-
-} // namespace media
diff --git a/media/audio/audio_manager_base.cc b/media/audio/audio_manager_base.cc
index 1477ce3c5a..5b1f4b3690 100644
--- a/media/audio/audio_manager_base.cc
+++ b/media/audio/audio_manager_base.cc
@@ -34,6 +34,7 @@ static const int kMaxInputChannels = 2;
const char AudioManagerBase::kDefaultDeviceName[] = "Default";
const char AudioManagerBase::kDefaultDeviceId[] = "default";
+const char AudioManagerBase::kLoopbackInputDeviceId[] = "loopback";
struct AudioManagerBase::DispatcherParams {
DispatcherParams(const AudioParameters& input,
@@ -310,8 +311,6 @@ void AudioManagerBase::GetAudioInputDeviceNames(
void AudioManagerBase::GetAudioOutputDeviceNames(
AudioDeviceNames* device_names) {
- // TODO(joi): Remove this and keep pure virtual once implemented everywhere.
- NOTREACHED() << "Don't use this yet, it's not ready on all platforms!";
}
void AudioManagerBase::ReleaseOutputStream(AudioOutputStream* stream) {
diff --git a/media/audio/audio_manager_base.h b/media/audio/audio_manager_base.h
index 15c1b24066..cdf7d3a76a 100644
--- a/media/audio/audio_manager_base.h
+++ b/media/audio/audio_manager_base.h
@@ -32,11 +32,24 @@ class AudioOutputDispatcher;
// AudioManagerBase provides AudioManager functions common for all platforms.
class MEDIA_EXPORT AudioManagerBase : public AudioManager {
public:
+ // TODO(sergeyu): The constants below belong to AudioManager interface, not
+ // to the base implementation.
+
// Name of the generic "default" device.
static const char kDefaultDeviceName[];
// Unique Id of the generic "default" device.
static const char kDefaultDeviceId[];
+ // Input device ID used to capture the default system playback stream. When
+ // this device ID is passed to MakeAudioInputStream() the returned
+ // AudioInputStream will be capturing audio currently being played on the
+ // default playback device. At the moment this feature is supported only on
+ // some platforms. AudioInputStream::Intialize() will return an error on
+ // platforms that don't support it. GetInputStreamParameters() must be used
+ // to get the parameters of the loopback device before creating a loopback
+ // stream, otherwise stream initialization may fail.
+ static const char kLoopbackInputDeviceId[];
+
virtual ~AudioManagerBase();
virtual scoped_refptr<base::MessageLoopProxy> GetMessageLoop() OVERRIDE;
diff --git a/media/audio/audio_manager_unittest.cc b/media/audio/audio_manager_unittest.cc
index 96300c9a83..4747c2e299 100644
--- a/media/audio/audio_manager_unittest.cc
+++ b/media/audio/audio_manager_unittest.cc
@@ -2,9 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/environment.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "media/audio/audio_manager.h"
+#include "media/audio/audio_manager_base.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_LINUX)
@@ -12,8 +14,10 @@
#endif // defined(OS_LINUX)
#if defined(OS_WIN)
+#include "base/win/scoped_com_initializer.h"
#include "media/audio/win/audio_manager_win.h"
-#endif // defined(OS_WIN)
+#include "media/audio/win/wavein_input_win.h"
+#endif
#if defined(USE_PULSEAUDIO)
#include "media/audio/pulse/audio_manager_pulse.h"
@@ -21,109 +25,305 @@
namespace media {
-void GetAudioOutputDeviceNamesImpl(AudioManager* audio_manager) {
- AudioDeviceNames device_names;
- audio_manager->GetAudioOutputDeviceNames(&device_names);
-
- VLOG(2) << "Got " << device_names.size() << " audio output devices.";
- for (AudioDeviceNames::iterator it = device_names.begin();
- it != device_names.end();
- ++it) {
- EXPECT_FALSE(it->unique_id.empty());
- EXPECT_FALSE(it->device_name.empty());
- VLOG(2) << "Device ID(" << it->unique_id << "), label: " << it->device_name;
+// Test fixture which allows us to override the default enumeration API on
+// Windows.
+class AudioManagerTest
+ : public ::testing::Test {
+ protected:
+ AudioManagerTest()
+ : audio_manager_(AudioManager::Create())
+#if defined(OS_WIN)
+ , com_init_(base::win::ScopedCOMInitializer::kMTA)
+#endif
+ {
}
-}
-// So that tests herein can be friends of AudioManagerWin.
-//
-// TODO(joi): Make this go away by unifying audio_manager_unittest.cc
-// and audio_input_device_unittest.cc
-class AudioManagerTest : public ::testing::Test {
- public:
- bool SetupForSecondTest(AudioManager* amw) {
#if defined(OS_WIN)
- AudioManagerWin* audio_manager_win = static_cast<AudioManagerWin*>(amw);
- if (audio_manager_win->enumeration_type() ==
- AudioManagerWin::kWaveEnumeration) {
- // This will be true only if running on Windows XP.
- VLOG(2) << "AudioManagerWin on WinXP; nothing more to test.";
+ bool SetMMDeviceEnumeration() {
+ AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager_.get());
+ // Windows Wave is used as default if Windows XP was detected =>
+ // return false since MMDevice is not supported on XP.
+ if (amw->enumeration_type() == AudioManagerWin::kWaveEnumeration)
+ return false;
+
+ amw->SetEnumerationType(AudioManagerWin::kMMDeviceEnumeration);
+ return true;
+ }
+
+ void SetWaveEnumeration() {
+ AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager_.get());
+ amw->SetEnumerationType(AudioManagerWin::kWaveEnumeration);
+ }
+
+ std::string GetDeviceIdFromPCMWaveInAudioInputStream(
+ const std::string& device_id) {
+ AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager_.get());
+ AudioParameters parameters(
+ AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO,
+ AudioParameters::kAudioCDSampleRate, 16,
+ 1024);
+ scoped_ptr<PCMWaveInAudioInputStream> stream(
+ static_cast<PCMWaveInAudioInputStream*>(
+ amw->CreatePCMWaveInAudioInputStream(parameters, device_id)));
+ return stream.get() ? stream->device_id_ : std::string();
+ }
+#endif
+
+ // Helper method which verifies that the device list starts with a valid
+ // default record 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()) {
+ 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;
+ }
} else {
- VLOG(2) << "Testing AudioManagerWin in fallback WinXP mode.";
- audio_manager_win->SetEnumerationType(AudioManagerWin::kWaveEnumeration);
- return true;
+ // 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";
}
-#endif // defined(OS_WIN)
- return false;
}
-};
-TEST_F(AudioManagerTest, GetAudioOutputDeviceNames) {
- // On Linux, we may be able to test both the Alsa and Pulseaudio
- // versions of the audio manager.
-#if defined(USE_PULSEAUDIO)
- {
- VLOG(2) << "Testing AudioManagerPulse.";
- scoped_ptr<AudioManager> pulse_audio_manager(AudioManagerPulse::Create());
- if (pulse_audio_manager.get())
- GetAudioOutputDeviceNamesImpl(pulse_audio_manager.get());
- else
- LOG(WARNING) << "No pulseaudio on this system.";
+ bool CanRunInputTest() {
+ return audio_manager_->HasAudioInputDevices();
}
-#endif // defined(USE_PULSEAUDIO)
-#if defined(USE_ALSA)
- {
- VLOG(2) << "Testing AudioManagerLinux.";
- scoped_ptr<AudioManager> alsa_audio_manager(new AudioManagerLinux());
- GetAudioOutputDeviceNamesImpl(alsa_audio_manager.get());
+
+ bool CanRunOutputTest() {
+ return audio_manager_->HasAudioOutputDevices();
}
-#endif // defined(USE_ALSA)
-#if defined(OS_MACOSX)
- VLOG(2) << "Testing platform-default AudioManager.";
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
- GetAudioOutputDeviceNamesImpl(audio_manager.get());
-#endif // defined(OS_MACOSX)
+ scoped_ptr<AudioManager> audio_manager_;
#if defined(OS_WIN)
- {
- // TODO(joi): Unify the tests in audio_input_device_unittest.cc
- // with the tests in this file, and reuse the Windows-specific
- // bits from that file.
- VLOG(2) << "Testing AudioManagerWin in its default mode.";
- scoped_ptr<AudioManager> audio_manager_win(AudioManager::Create());
- GetAudioOutputDeviceNamesImpl(audio_manager_win.get());
-
- if (SetupForSecondTest(audio_manager_win.get())) {
- GetAudioOutputDeviceNamesImpl(audio_manager_win.get());
+ // The MMDevice API requires COM to be initialized on the current thread.
+ base::win::ScopedCOMInitializer com_init_;
+#endif
+};
+
+// Test that devices can be enumerated.
+TEST_F(AudioManagerTest, EnumerateInputDevices) {
+ if (!CanRunInputTest())
+ return;
+
+ AudioDeviceNames device_names;
+ audio_manager_->GetAudioInputDeviceNames(&device_names);
+ CheckDeviceNames(device_names);
+}
+
+// Test that devices can be enumerated.
+TEST_F(AudioManagerTest, EnumerateOutputDevices) {
+ if (!CanRunOutputTest())
+ return;
+
+ AudioDeviceNames device_names;
+ audio_manager_->GetAudioOutputDeviceNames(&device_names);
+ CheckDeviceNames(device_names);
+}
+
+// Run additional tests for Windows since enumeration can be done using
+// two different APIs. MMDevice is default for Vista and higher and Wave
+// is default for XP and lower.
+#if defined(OS_WIN)
+
+// Override default enumeration API and force usage of Windows MMDevice.
+// This test will only run on Windows Vista and higher.
+TEST_F(AudioManagerTest, EnumerateInputDevicesWinMMDevice) {
+ if (!CanRunInputTest())
+ return;
+
+ AudioDeviceNames device_names;
+ if (!SetMMDeviceEnumeration()) {
+ // Usage of MMDevice will fail on XP and lower.
+ LOG(WARNING) << "MM device enumeration is not supported.";
+ return;
+ }
+ audio_manager_->GetAudioInputDeviceNames(&device_names);
+ CheckDeviceNames(device_names);
+}
+
+TEST_F(AudioManagerTest, EnumerateOutputDevicesWinMMDevice) {
+ if (!CanRunOutputTest())
+ return;
+
+ AudioDeviceNames device_names;
+ if (!SetMMDeviceEnumeration()) {
+ // Usage of MMDevice will fail on XP and lower.
+ LOG(WARNING) << "MM device enumeration is not supported.";
+ return;
+ }
+ audio_manager_->GetAudioOutputDeviceNames(&device_names);
+ CheckDeviceNames(device_names);
+}
+
+// Override default enumeration API and force usage of Windows Wave.
+// This test will run on Windows XP, Windows Vista and Windows 7.
+TEST_F(AudioManagerTest, EnumerateInputDevicesWinWave) {
+ if (!CanRunInputTest())
+ return;
+
+ AudioDeviceNames device_names;
+ SetWaveEnumeration();
+ audio_manager_->GetAudioInputDeviceNames(&device_names);
+ CheckDeviceNames(device_names);
+}
+
+TEST_F(AudioManagerTest, EnumerateOutputDevicesWinWave) {
+ if (!CanRunOutputTest())
+ return;
+
+ AudioDeviceNames device_names;
+ SetWaveEnumeration();
+ audio_manager_->GetAudioOutputDeviceNames(&device_names);
+ CheckDeviceNames(device_names);
+}
+
+TEST_F(AudioManagerTest, WinXPDeviceIdUnchanged) {
+ if (!CanRunInputTest())
+ return;
+
+ AudioDeviceNames xp_device_names;
+ SetWaveEnumeration();
+ audio_manager_->GetAudioInputDeviceNames(&xp_device_names);
+ CheckDeviceNames(xp_device_names);
+
+ // Device ID should remain unchanged, including the default device ID.
+ for (AudioDeviceNames::iterator i = xp_device_names.begin();
+ i != xp_device_names.end(); ++i) {
+ EXPECT_EQ(i->unique_id,
+ GetDeviceIdFromPCMWaveInAudioInputStream(i->unique_id));
+ }
+}
+
+TEST_F(AudioManagerTest, ConvertToWinXPInputDeviceId) {
+ if (!CanRunInputTest())
+ return;
+
+ if (!SetMMDeviceEnumeration()) {
+ // Usage of MMDevice will fail on XP and lower.
+ LOG(WARNING) << "MM device enumeration is not supported.";
+ return;
+ }
+
+ AudioDeviceNames device_names;
+ audio_manager_->GetAudioInputDeviceNames(&device_names);
+ CheckDeviceNames(device_names);
+
+ for (AudioDeviceNames::iterator i = device_names.begin();
+ i != device_names.end(); ++i) {
+ std::string converted_id =
+ GetDeviceIdFromPCMWaveInAudioInputStream(i->unique_id);
+ if (i == device_names.begin()) {
+ // The first in the list is the default device ID, which should not be
+ // changed when passed to PCMWaveInAudioInputStream.
+ EXPECT_EQ(i->unique_id, converted_id);
+ } else {
+ // MMDevice-style device IDs should be converted to WaveIn-style device
+ // IDs.
+ EXPECT_NE(i->unique_id, converted_id);
}
}
+}
+
#endif // defined(OS_WIN)
+
+#if defined(USE_PULSEAUDIO)
+// On Linux, there are two implementations available and both can
+// sometimes be tested on a single system. These tests specifically
+// test Pulseaudio.
+
+TEST_F(AudioManagerTest, EnumerateInputDevicesPulseaudio) {
+ if (!CanRunInputTest())
+ return;
+
+ audio_manager_.reset(AudioManagerPulse::Create());
+ if (audio_manager_.get()) {
+ AudioDeviceNames device_names;
+ audio_manager_->GetAudioInputDeviceNames(&device_names);
+ CheckDeviceNames(device_names);
+ } else {
+ LOG(WARNING) << "No pulseaudio on this system.";
+ }
}
+TEST_F(AudioManagerTest, EnumerateOutputDevicesPulseaudio) {
+ if (!CanRunOutputTest())
+ return;
+
+ audio_manager_.reset(AudioManagerPulse::Create());
+ if (audio_manager_.get()) {
+ AudioDeviceNames device_names;
+ audio_manager_->GetAudioOutputDeviceNames(&device_names);
+ CheckDeviceNames(device_names);
+ } else {
+ LOG(WARNING) << "No pulseaudio on this system.";
+ }
+}
+#endif // defined(USE_PULSEAUDIO)
+
+#if defined(USE_ALSA)
+// On Linux, there are two implementations available and both can
+// sometimes be tested on a single system. These tests specifically
+// test Alsa.
+
+TEST_F(AudioManagerTest, EnumerateInputDevicesAlsa) {
+ if (!CanRunInputTest())
+ return;
+
+ VLOG(2) << "Testing AudioManagerLinux.";
+ audio_manager_.reset(new AudioManagerLinux());
+ AudioDeviceNames device_names;
+ audio_manager_->GetAudioInputDeviceNames(&device_names);
+ CheckDeviceNames(device_names);
+}
+
+TEST_F(AudioManagerTest, EnumerateOutputDevicesAlsa) {
+ if (!CanRunOutputTest())
+ return;
+
+ VLOG(2) << "Testing AudioManagerLinux.";
+ audio_manager_.reset(new AudioManagerLinux());
+ AudioDeviceNames device_names;
+ audio_manager_->GetAudioOutputDeviceNames(&device_names);
+ CheckDeviceNames(device_names);
+}
+#endif // defined(USE_ALSA)
+
TEST_F(AudioManagerTest, GetDefaultOutputStreamParameters) {
#if defined(OS_WIN) || defined(OS_MACOSX)
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
- ASSERT_TRUE(audio_manager);
- if (!audio_manager->HasAudioOutputDevices())
+ if (!CanRunInputTest())
return;
- AudioParameters params = audio_manager->GetDefaultOutputStreamParameters();
+ AudioParameters params = audio_manager_->GetDefaultOutputStreamParameters();
EXPECT_TRUE(params.IsValid());
#endif // defined(OS_WIN) || defined(OS_MACOSX)
}
TEST_F(AudioManagerTest, GetAssociatedOutputDeviceID) {
#if defined(OS_WIN) || defined(OS_MACOSX)
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
- ASSERT_TRUE(audio_manager);
- if (!audio_manager->HasAudioOutputDevices() ||
- !audio_manager->HasAudioInputDevices()) {
+ if (!CanRunInputTest() || !CanRunOutputTest())
return;
- }
AudioDeviceNames device_names;
- audio_manager->GetAudioInputDeviceNames(&device_names);
+ audio_manager_->GetAudioInputDeviceNames(&device_names);
bool found_an_associated_device = false;
for (AudioDeviceNames::iterator it = device_names.begin();
it != device_names.end();
@@ -131,7 +331,7 @@ TEST_F(AudioManagerTest, GetAssociatedOutputDeviceID) {
EXPECT_FALSE(it->unique_id.empty());
EXPECT_FALSE(it->device_name.empty());
std::string output_device_id(
- audio_manager->GetAssociatedOutputDeviceID(it->unique_id));
+ audio_manager_->GetAssociatedOutputDeviceID(it->unique_id));
if (!output_device_id.empty()) {
VLOG(2) << it->unique_id << " matches with " << output_device_id;
found_an_associated_device = true;
diff --git a/media/audio/cras/audio_manager_cras.cc b/media/audio/cras/audio_manager_cras.cc
index 276487557e..14a0c4e86a 100644
--- a/media/audio/cras/audio_manager_cras.cc
+++ b/media/audio/cras/audio_manager_cras.cc
@@ -16,14 +16,21 @@
namespace media {
+static void AddDefaultDevice(AudioDeviceNames* device_names) {
+ DCHECK(device_names->empty());
+
+ // Cras will route audio from a proper physical device automatically.
+ device_names->push_back(
+ AudioDeviceName(AudioManagerBase::kDefaultDeviceName,
+ AudioManagerBase::kDefaultDeviceId));
+}
+
// Maximum number of output streams that can be open simultaneously.
static const int kMaxOutputStreams = 50;
// Default sample rate for input and output streams.
static const int kDefaultSampleRate = 48000;
-const char AudioManagerCras::kLoopbackDeviceId[] = "loopback";
-
bool AudioManagerCras::HasAudioOutputDevices() {
return true;
}
@@ -45,10 +52,13 @@ void AudioManagerCras::ShowAudioInputSettings() {
}
void AudioManagerCras::GetAudioInputDeviceNames(
- media::AudioDeviceNames* device_names) {
- DCHECK(device_names->empty());
- GetCrasAudioInputDevices(device_names);
- return;
+ AudioDeviceNames* device_names) {
+ AddDefaultDevice(device_names);
+}
+
+void AudioManagerCras::GetAudioOutputDeviceNames(
+ AudioDeviceNames* device_names) {
+ AddDefaultDevice(device_names);
}
AudioParameters AudioManagerCras::GetInputStreamParameters(
@@ -61,14 +71,6 @@ AudioParameters AudioManagerCras::GetInputStreamParameters(
kDefaultSampleRate, 16, kDefaultInputBufferSize);
}
-void AudioManagerCras::GetCrasAudioInputDevices(
- media::AudioDeviceNames* device_names) {
- // Cras will route audio from a proper physical device automatically.
- device_names->push_back(
- AudioDeviceName(AudioManagerBase::kDefaultDeviceName,
- AudioManagerBase::kDefaultDeviceId));
-}
-
AudioOutputStream* AudioManagerCras::MakeLinearOutputStream(
const AudioParameters& params) {
DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
diff --git a/media/audio/cras/audio_manager_cras.h b/media/audio/cras/audio_manager_cras.h
index 41e1876ac8..3b0ef530e0 100644
--- a/media/audio/cras/audio_manager_cras.h
+++ b/media/audio/cras/audio_manager_cras.h
@@ -15,18 +15,16 @@ namespace media {
class MEDIA_EXPORT AudioManagerCras : public AudioManagerBase {
public:
- // Unique ID of the "loopback" input device. This device captures post-mix,
- // pre-DSP system audio.
- static const char kLoopbackDeviceId[];
-
AudioManagerCras();
// AudioManager implementation.
virtual bool HasAudioOutputDevices() OVERRIDE;
virtual bool HasAudioInputDevices() OVERRIDE;
virtual void ShowAudioInputSettings() OVERRIDE;
- virtual void GetAudioInputDeviceNames(media::AudioDeviceNames* device_names)
- OVERRIDE;
+ virtual void GetAudioInputDeviceNames(
+ AudioDeviceNames* device_names) OVERRIDE;
+ virtual void GetAudioOutputDeviceNames(
+ AudioDeviceNames* device_names) OVERRIDE;
virtual AudioParameters GetInputStreamParameters(
const std::string& device_id) OVERRIDE;
@@ -50,9 +48,6 @@ class MEDIA_EXPORT AudioManagerCras : public AudioManagerBase {
const AudioParameters& input_params) OVERRIDE;
private:
- // Gets a list of available cras input devices.
- void GetCrasAudioInputDevices(media::AudioDeviceNames* device_names);
-
// Called by MakeLinearOutputStream and MakeLowLatencyOutputStream.
AudioOutputStream* MakeOutputStream(const AudioParameters& params);
diff --git a/media/audio/cras/cras_input.cc b/media/audio/cras/cras_input.cc
index 363dc68227..fd574dc86e 100644
--- a/media/audio/cras/cras_input.cc
+++ b/media/audio/cras/cras_input.cc
@@ -26,9 +26,8 @@ CrasInputStream::CrasInputStream(const AudioParameters& params,
params_(params),
started_(false),
stream_id_(0),
- stream_direction_(device_id == AudioManagerCras::kLoopbackDeviceId
- ? CRAS_STREAM_POST_MIX_PRE_DSP
- : CRAS_STREAM_INPUT) {
+ stream_direction_(device_id == AudioManagerBase::kLoopbackInputDeviceId ?
+ CRAS_STREAM_POST_MIX_PRE_DSP : CRAS_STREAM_INPUT) {
DCHECK(audio_manager_);
}
diff --git a/media/audio/linux/audio_manager_linux.cc b/media/audio/linux/audio_manager_linux.cc
index 7596c2fe25..708e4f2684 100644
--- a/media/audio/linux/audio_manager_linux.cc
+++ b/media/audio/linux/audio_manager_linux.cc
@@ -103,13 +103,13 @@ void AudioManagerLinux::ShowAudioInputSettings() {
}
void AudioManagerLinux::GetAudioInputDeviceNames(
- media::AudioDeviceNames* device_names) {
+ AudioDeviceNames* device_names) {
DCHECK(device_names->empty());
GetAlsaAudioDevices(kStreamCapture, device_names);
}
void AudioManagerLinux::GetAudioOutputDeviceNames(
- media::AudioDeviceNames* device_names) {
+ AudioDeviceNames* device_names) {
DCHECK(device_names->empty());
GetAlsaAudioDevices(kStreamPlayback, device_names);
}
diff --git a/media/audio/linux/audio_manager_linux.h b/media/audio/linux/audio_manager_linux.h
index 2258e81eb9..ab284dfdce 100644
--- a/media/audio/linux/audio_manager_linux.h
+++ b/media/audio/linux/audio_manager_linux.h
@@ -25,10 +25,10 @@ class MEDIA_EXPORT AudioManagerLinux : public AudioManagerBase {
virtual bool HasAudioOutputDevices() OVERRIDE;
virtual bool HasAudioInputDevices() OVERRIDE;
virtual void ShowAudioInputSettings() OVERRIDE;
- virtual void GetAudioInputDeviceNames(media::AudioDeviceNames* device_names)
- OVERRIDE;
- virtual void GetAudioOutputDeviceNames(media::AudioDeviceNames* device_names)
- OVERRIDE;
+ virtual void GetAudioInputDeviceNames(
+ AudioDeviceNames* device_names) OVERRIDE;
+ virtual void GetAudioOutputDeviceNames(
+ AudioDeviceNames* device_names) OVERRIDE;
virtual AudioParameters GetInputStreamParameters(
const std::string& device_id) OVERRIDE;
diff --git a/media/audio/mac/audio_manager_mac.h b/media/audio/mac/audio_manager_mac.h
index 9757315920..d162554b40 100644
--- a/media/audio/mac/audio_manager_mac.h
+++ b/media/audio/mac/audio_manager_mac.h
@@ -27,10 +27,10 @@ class MEDIA_EXPORT AudioManagerMac : public AudioManagerBase {
// Implementation of AudioManager.
virtual bool HasAudioOutputDevices() OVERRIDE;
virtual bool HasAudioInputDevices() OVERRIDE;
- virtual void GetAudioInputDeviceNames(media::AudioDeviceNames* device_names)
- OVERRIDE;
- virtual void GetAudioOutputDeviceNames(media::AudioDeviceNames* device_names)
- OVERRIDE;
+ virtual void GetAudioInputDeviceNames(
+ AudioDeviceNames* device_names) OVERRIDE;
+ virtual void GetAudioOutputDeviceNames(
+ AudioDeviceNames* device_names) OVERRIDE;
virtual AudioParameters GetInputStreamParameters(
const std::string& device_id) OVERRIDE;
virtual std::string GetAssociatedOutputDeviceID(
diff --git a/media/audio/mock_audio_manager.cc b/media/audio/mock_audio_manager.cc
index 2ab2b708da..a164332a64 100644
--- a/media/audio/mock_audio_manager.cc
+++ b/media/audio/mock_audio_manager.cc
@@ -33,11 +33,11 @@ void MockAudioManager::ShowAudioInputSettings() {
}
void MockAudioManager::GetAudioInputDeviceNames(
- media::AudioDeviceNames* device_names) {
+ AudioDeviceNames* device_names) {
}
void MockAudioManager::GetAudioOutputDeviceNames(
- media::AudioDeviceNames* device_names) {
+ AudioDeviceNames* device_names) {
}
media::AudioOutputStream* MockAudioManager::MakeAudioOutputStream(
diff --git a/media/audio/pulse/audio_manager_pulse.h b/media/audio/pulse/audio_manager_pulse.h
index 8fc4310cba..3639663992 100644
--- a/media/audio/pulse/audio_manager_pulse.h
+++ b/media/audio/pulse/audio_manager_pulse.h
@@ -25,10 +25,10 @@ class MEDIA_EXPORT AudioManagerPulse : public AudioManagerBase {
virtual bool HasAudioOutputDevices() OVERRIDE;
virtual bool HasAudioInputDevices() OVERRIDE;
virtual void ShowAudioInputSettings() OVERRIDE;
- virtual void GetAudioInputDeviceNames(media::AudioDeviceNames* device_names)
- OVERRIDE;
- virtual void GetAudioOutputDeviceNames(media::AudioDeviceNames* device_names)
- OVERRIDE;
+ virtual void GetAudioInputDeviceNames(
+ AudioDeviceNames* device_names) OVERRIDE;
+ virtual void GetAudioOutputDeviceNames(
+ AudioDeviceNames* device_names) OVERRIDE;
virtual AudioParameters GetInputStreamParameters(
const std::string& device_id) OVERRIDE;
diff --git a/media/audio/shared_memory_util.cc b/media/audio/shared_memory_util.cc
index b65df03e2e..523cdb9646 100644
--- a/media/audio/shared_memory_util.cc
+++ b/media/audio/shared_memory_util.cc
@@ -4,6 +4,8 @@
#include "media/audio/shared_memory_util.h"
+#include <algorithm>
+
#include "base/atomicops.h"
#include "base/logging.h"
diff --git a/media/audio/win/audio_low_latency_input_win.cc b/media/audio/win/audio_low_latency_input_win.cc
index e081943910..a174ea2ea0 100644
--- a/media/audio/win/audio_low_latency_input_win.cc
+++ b/media/audio/win/audio_low_latency_input_win.cc
@@ -103,9 +103,8 @@ bool WASAPIAudioInputStream::Open() {
// Verify that the selected audio endpoint supports the specified format
// set during construction.
- if (!DesiredFormatIsSupported()) {
+ if (!DesiredFormatIsSupported())
return false;
- }
// Initialize the audio stream between the client and the device using
// shared mode and a lowest possible glitch-free latency.
@@ -141,6 +140,9 @@ void WASAPIAudioInputStream::Start(AudioInputCallback* callback) {
HRESULT hr = audio_client_->Start();
DLOG_IF(ERROR, FAILED(hr)) << "Failed to start input streaming.";
+ if (SUCCEEDED(hr) && audio_render_client_for_loopback_)
+ hr = audio_render_client_for_loopback_->Start();
+
started_ = SUCCEEDED(hr);
}
@@ -276,6 +278,10 @@ HRESULT WASAPIAudioInputStream::GetMixFormat(const std::string& device_id,
// Retrieve the default capture audio endpoint.
hr = enumerator->GetDefaultAudioEndpoint(eCapture, eConsole,
endpoint_device.Receive());
+ } else if (device_id == AudioManagerBase::kLoopbackInputDeviceId) {
+ // Capture the default playback stream.
+ hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole,
+ endpoint_device.Receive());
} else {
// Retrieve a capture endpoint device that is specified by an endpoint
// device-identification string.
@@ -454,42 +460,44 @@ void WASAPIAudioInputStream::HandleError(HRESULT err) {
HRESULT WASAPIAudioInputStream::SetCaptureDevice() {
ScopedComPtr<IMMDeviceEnumerator> enumerator;
- HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
- NULL,
- CLSCTX_INPROC_SERVER,
- __uuidof(IMMDeviceEnumerator),
- enumerator.ReceiveVoid());
- if (SUCCEEDED(hr)) {
- // Retrieve the IMMDevice by using the specified role or the specified
- // unique endpoint device-identification string.
- // TODO(henrika): possibly add support for the eCommunications as well.
- if (device_id_ == AudioManagerBase::kDefaultDeviceId) {
- // Retrieve the default capture audio endpoint for the specified role.
- // Note that, in Windows Vista, the MMDevice API supports device roles
- // but the system-supplied user interface programs do not.
- hr = enumerator->GetDefaultAudioEndpoint(eCapture,
- eConsole,
- endpoint_device_.Receive());
- } else {
- // Retrieve a capture endpoint device that is specified by an endpoint
- // device-identification string.
- hr = enumerator->GetDevice(UTF8ToUTF16(device_id_).c_str(),
- endpoint_device_.Receive());
- }
+ HRESULT hr = enumerator.CreateInstance(__uuidof(MMDeviceEnumerator),
+ NULL, CLSCTX_INPROC_SERVER);
+ if (FAILED(hr))
+ return hr;
- if (FAILED(hr))
- return hr;
+ // Retrieve the IMMDevice by using the specified role or the specified
+ // unique endpoint device-identification string.
+ // TODO(henrika): possibly add support for the eCommunications as well.
+ if (device_id_ == AudioManagerBase::kDefaultDeviceId) {
+ // Retrieve the default capture audio endpoint for the specified role.
+ // Note that, in Windows Vista, the MMDevice API supports device roles
+ // but the system-supplied user interface programs do not.
+ hr = enumerator->GetDefaultAudioEndpoint(eCapture, eConsole,
+ endpoint_device_.Receive());
+ } else if (device_id_ == AudioManagerBase::kLoopbackInputDeviceId) {
+ // Capture the default playback stream.
+ hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole,
+ endpoint_device_.Receive());
+ } else {
+ // Retrieve a capture endpoint device that is specified by an endpoint
+ // device-identification string.
+ hr = enumerator->GetDevice(UTF8ToUTF16(device_id_).c_str(),
+ endpoint_device_.Receive());
+ }
- // Verify that the audio endpoint device is active, i.e., the audio
- // adapter that connects to the endpoint device is present and enabled.
- DWORD state = DEVICE_STATE_DISABLED;
- hr = endpoint_device_->GetState(&state);
- if (SUCCEEDED(hr)) {
- if (!(state & DEVICE_STATE_ACTIVE)) {
- DLOG(ERROR) << "Selected capture device is not active.";
- hr = E_ACCESSDENIED;
- }
- }
+ if (FAILED(hr))
+ return hr;
+
+ // Verify that the audio endpoint device is active, i.e., the audio
+ // adapter that connects to the endpoint device is present and enabled.
+ DWORD state = DEVICE_STATE_DISABLED;
+ hr = endpoint_device_->GetState(&state);
+ if (FAILED(hr))
+ return hr;
+
+ if (!(state & DEVICE_STATE_ACTIVE)) {
+ DLOG(ERROR) << "Selected capture device is not active.";
+ hr = E_ACCESSDENIED;
}
return hr;
@@ -565,16 +573,25 @@ bool WASAPIAudioInputStream::DesiredFormatIsSupported() {
}
HRESULT WASAPIAudioInputStream::InitializeAudioEngine() {
+ DWORD flags;
+ // Use event-driven mode only fo regular input devices. For loopback the
+ // EVENTCALLBACK flag is specified when intializing
+ // |audio_render_client_for_loopback_|.
+ if (device_id_ == AudioManagerBase::kLoopbackInputDeviceId) {
+ flags = AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_NOPERSIST;
+ } else {
+ flags =
+ AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST;
+ }
+
// Initialize the audio stream between the client and the device.
- // We connect indirectly through the audio engine by using shared mode
- // and WASAPI is initialized in an event driven mode.
+ // We connect indirectly through the audio engine by using shared mode.
// Note that, |hnsBufferDuration| is set of 0, which ensures that the
// buffer is never smaller than the minimum buffer size needed to ensure
// that glitches do not occur between the periodic processing passes.
// This setting should lead to lowest possible latency.
HRESULT hr = audio_client_->Initialize(AUDCLNT_SHAREMODE_SHARED,
- AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
- AUDCLNT_STREAMFLAGS_NOPERSIST,
+ flags,
0, // hnsBufferDuration
0,
&format_,
@@ -590,6 +607,7 @@ HRESULT WASAPIAudioInputStream::InitializeAudioEngine() {
hr = audio_client_->GetBufferSize(&endpoint_buffer_size_frames_);
if (FAILED(hr))
return hr;
+
DVLOG(1) << "endpoint buffer size: " << endpoint_buffer_size_frames_
<< " [frames]";
@@ -618,9 +636,41 @@ HRESULT WASAPIAudioInputStream::InitializeAudioEngine() {
}
#endif
- // Set the event handle that the audio engine will signal each time
- // a buffer becomes ready to be processed by the client.
- hr = audio_client_->SetEventHandle(audio_samples_ready_event_.Get());
+ // Set the event handle that the audio engine will signal each time a buffer
+ // becomes ready to be processed by the client.
+ //
+ // In loopback case the capture device doesn't receive any events, so we
+ // need to create a separate playback client to get notifications. According
+ // to MSDN:
+ //
+ // A pull-mode capture client does not receive any events when a stream is
+ // initialized with event-driven buffering and is loopback-enabled. To
+ // work around this, initialize a render stream in event-driven mode. Each
+ // time the client receives an event for the render stream, it must signal
+ // the capture client to run the capture thread that reads the next set of
+ // samples from the capture endpoint buffer.
+ //
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/dd316551(v=vs.85).aspx
+ if (device_id_ == AudioManagerBase::kLoopbackInputDeviceId) {
+ hr = endpoint_device_->Activate(
+ __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL,
+ audio_render_client_for_loopback_.ReceiveVoid());
+ if (FAILED(hr))
+ return hr;
+
+ hr = audio_render_client_for_loopback_->Initialize(
+ AUDCLNT_SHAREMODE_SHARED,
+ AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
+ 0, 0, &format_, NULL);
+ if (FAILED(hr))
+ return hr;
+
+ hr = audio_render_client_for_loopback_->SetEventHandle(
+ audio_samples_ready_event_.Get());
+ } else {
+ hr = audio_client_->SetEventHandle(audio_samples_ready_event_.Get());
+ }
+
if (FAILED(hr))
return hr;
diff --git a/media/audio/win/audio_low_latency_input_win.h b/media/audio/win/audio_low_latency_input_win.h
index 4f9c7fb6c8..99e1604925 100644
--- a/media/audio/win/audio_low_latency_input_win.h
+++ b/media/audio/win/audio_low_latency_input_win.h
@@ -184,6 +184,14 @@ class MEDIA_EXPORT WASAPIAudioInputStream
// an audio stream between an audio application and the audio engine.
base::win::ScopedComPtr<IAudioClient> audio_client_;
+ // Loopback IAudioClient doesn't support event-driven mode, so a separate
+ // IAudioClient is needed to receive notifications when data is available in
+ // the buffer. For loopback input |audio_client_| is used to receive data,
+ // while |audio_render_client_for_loopback_| is used to get notifications
+ // when a new buffer is ready. See comment in InitializeAudioEngine() for
+ // details.
+ base::win::ScopedComPtr<IAudioClient> audio_render_client_for_loopback_;
+
// The IAudioCaptureClient interface enables a client to read input data
// from a capture endpoint buffer.
base::win::ScopedComPtr<IAudioCaptureClient> audio_capture_client_;
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 40990ec13d..11fad25d3f 100644
--- a/media/audio/win/audio_low_latency_input_win_unittest.cc
+++ b/media/audio/win/audio_low_latency_input_win_unittest.cc
@@ -39,12 +39,53 @@ 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));
+ const uint8* src, uint32 size,
+ uint32 hardware_delay_bytes, double volume));
MOCK_METHOD1(OnClose, void(AudioInputStream* stream));
MOCK_METHOD1(OnError, void(AudioInputStream* stream));
};
+class FakeAudioInputCallback : public AudioInputStream::AudioInputCallback {
+ public:
+ FakeAudioInputCallback()
+ : closed_(false),
+ error_(false),
+ data_event_(false, false) {
+ }
+
+ const std::vector<uint8>& received_data() const { return received_data_; }
+ bool closed() const { return closed_; }
+ bool error() const { return error_; }
+
+ // Waits until OnData() is called on another thread.
+ void WaitForData() {
+ data_event_.Wait();
+ }
+
+ 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);
+ data_event_.Signal();
+ }
+
+ virtual void OnClose(AudioInputStream* stream) OVERRIDE {
+ closed_ = true;
+ }
+
+ virtual void OnError(AudioInputStream* stream) OVERRIDE {
+ error_ = true;
+ }
+
+ private:
+ std::vector<uint8> received_data_;
+ base::WaitableEvent data_event_;
+ bool closed_;
+ bool error_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeAudioInputCallback);
+};
+
// This audio sink implementation should be used for manual tests only since
// the recorded data is stored on a raw binary data file.
class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback {
@@ -188,6 +229,39 @@ static AudioInputStream* CreateDefaultAudioInputStream(
return ais;
}
+class ScopedAudioInputStream {
+ public:
+ explicit ScopedAudioInputStream(AudioInputStream* stream)
+ : stream_(stream) {}
+
+ ~ScopedAudioInputStream() {
+ if (stream_)
+ stream_->Close();
+ }
+
+ void Close() {
+ if (stream_)
+ stream_->Close();
+ stream_ = NULL;
+ }
+
+ AudioInputStream* operator->() {
+ return stream_;
+ }
+
+ AudioInputStream* get() const { return stream_; }
+
+ void Reset(AudioInputStream* new_stream) {
+ Close();
+ stream_ = new_stream;
+ }
+
+ private:
+ AudioInputStream* stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAudioInputStream);
+};
+
// Verify that we can retrieve the current hardware/mixing sample rate
// for all available input devices.
TEST(WinAudioInputTest, WASAPIAudioInputStreamHardwareSampleRate) {
@@ -217,8 +291,9 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamCreateAndClose) {
scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
if (!CanRunAudioTests(audio_manager.get()))
return;
- AudioInputStream* ais = CreateDefaultAudioInputStream(audio_manager.get());
- ais->Close();
+ ScopedAudioInputStream ais(
+ CreateDefaultAudioInputStream(audio_manager.get()));
+ ais.Close();
}
// Test Open(), Close() calling sequence.
@@ -226,9 +301,10 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenAndClose) {
scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
if (!CanRunAudioTests(audio_manager.get()))
return;
- AudioInputStream* ais = CreateDefaultAudioInputStream(audio_manager.get());
+ ScopedAudioInputStream ais(
+ CreateDefaultAudioInputStream(audio_manager.get()));
EXPECT_TRUE(ais->Open());
- ais->Close();
+ ais.Close();
}
// Test Open(), Start(), Close() calling sequence.
@@ -236,13 +312,14 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartAndClose) {
scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
if (!CanRunAudioTests(audio_manager.get()))
return;
- AudioInputStream* ais = CreateDefaultAudioInputStream(audio_manager.get());
+ ScopedAudioInputStream ais(
+ CreateDefaultAudioInputStream(audio_manager.get()));
EXPECT_TRUE(ais->Open());
MockAudioInputCallback sink;
ais->Start(&sink);
- EXPECT_CALL(sink, OnClose(ais))
+ EXPECT_CALL(sink, OnClose(ais.get()))
.Times(1);
- ais->Close();
+ ais.Close();
}
// Test Open(), Start(), Stop(), Close() calling sequence.
@@ -250,14 +327,15 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartStopAndClose) {
scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
if (!CanRunAudioTests(audio_manager.get()))
return;
- AudioInputStream* ais = CreateDefaultAudioInputStream(audio_manager.get());
+ ScopedAudioInputStream ais(
+ CreateDefaultAudioInputStream(audio_manager.get()));
EXPECT_TRUE(ais->Open());
MockAudioInputCallback sink;
ais->Start(&sink);
ais->Stop();
- EXPECT_CALL(sink, OnClose(ais))
+ EXPECT_CALL(sink, OnClose(ais.get()))
.Times(1);
- ais->Close();
+ ais.Close();
}
// Test some additional calling sequences.
@@ -265,8 +343,10 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamMiscCallingSequences) {
scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
if (!CanRunAudioTests(audio_manager.get()))
return;
- AudioInputStream* ais = CreateDefaultAudioInputStream(audio_manager.get());
- WASAPIAudioInputStream* wais = static_cast<WASAPIAudioInputStream*>(ais);
+ ScopedAudioInputStream ais(
+ CreateDefaultAudioInputStream(audio_manager.get()));
+ WASAPIAudioInputStream* wais =
+ static_cast<WASAPIAudioInputStream*>(ais.get());
// Open(), Open() should fail the second time.
EXPECT_TRUE(ais->Open());
@@ -286,9 +366,9 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamMiscCallingSequences) {
ais->Stop();
EXPECT_FALSE(wais->started());
- EXPECT_CALL(sink, OnClose(ais))
+ EXPECT_CALL(sink, OnClose(ais.get()))
.Times(1);
- ais->Close();
+ ais.Close();
}
TEST(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) {
@@ -304,7 +384,7 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) {
// Create default WASAPI input stream which records in stereo using
// the shared mixing rate. The default buffer size is 10ms.
AudioInputStreamWrapper aisw(audio_manager.get());
- AudioInputStream* ais = aisw.Create();
+ ScopedAudioInputStream ais(aisw.Create());
EXPECT_TRUE(ais->Open());
MockAudioInputCallback sink;
@@ -317,7 +397,7 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) {
// All should contain valid packets of the same size and a valid delay
// estimate.
EXPECT_CALL(sink, OnData(
- ais, NotNull(), bytes_per_packet, Gt(bytes_per_packet), _))
+ ais.get(), NotNull(), bytes_per_packet, Gt(bytes_per_packet), _))
.Times(AtLeast(10))
.WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
ais->Start(&sink);
@@ -327,49 +407,78 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) {
// Store current packet size (to be used in the subsequent tests).
int samples_per_packet_10ms = aisw.samples_per_packet();
- EXPECT_CALL(sink, OnClose(ais))
+ EXPECT_CALL(sink, OnClose(ais.get()))
.Times(1);
- ais->Close();
+ ais.Close();
// 20 ms packet size.
count = 0;
- ais = aisw.Create(2 * samples_per_packet_10ms);
+ ais.Reset(aisw.Create(2 * samples_per_packet_10ms));
EXPECT_TRUE(ais->Open());
bytes_per_packet = aisw.channels() * aisw.samples_per_packet() *
(aisw.bits_per_sample() / 8);
EXPECT_CALL(sink, OnData(
- ais, NotNull(), bytes_per_packet, Gt(bytes_per_packet), _))
+ ais.get(), NotNull(), bytes_per_packet, Gt(bytes_per_packet), _))
.Times(AtLeast(10))
.WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
ais->Start(&sink);
loop.Run();
ais->Stop();
- EXPECT_CALL(sink, OnClose(ais))
+ EXPECT_CALL(sink, OnClose(ais.get()))
.Times(1);
- ais->Close();
+ ais.Close();
// 5 ms packet size.
count = 0;
- ais = aisw.Create(samples_per_packet_10ms / 2);
+ ais.Reset(aisw.Create(samples_per_packet_10ms / 2));
EXPECT_TRUE(ais->Open());
bytes_per_packet = aisw.channels() * aisw.samples_per_packet() *
(aisw.bits_per_sample() / 8);
EXPECT_CALL(sink, OnData(
- ais, NotNull(), bytes_per_packet, Gt(bytes_per_packet), _))
+ ais.get(), NotNull(), bytes_per_packet, Gt(bytes_per_packet), _))
.Times(AtLeast(10))
.WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
ais->Start(&sink);
loop.Run();
ais->Stop();
- EXPECT_CALL(sink, OnClose(ais))
+ EXPECT_CALL(sink, OnClose(ais.get()))
.Times(1);
- ais->Close();
+ ais.Close();
+}
+
+// Test that we can capture loopback stream.
+TEST(WinAudioInputTest, WASAPIAudioInputStreamLoopback) {
+ scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ if (!audio_manager->HasAudioOutputDevices() || !CoreAudioUtil::IsSupported())
+ return;
+
+ AudioParameters params = audio_manager->GetInputStreamParameters(
+ AudioManagerBase::kLoopbackInputDeviceId);
+
+ AudioParameters output_params =
+ audio_manager->GetOutputStreamParameters(std::string());
+ EXPECT_EQ(params.sample_rate(), output_params.sample_rate());
+ EXPECT_EQ(params.channel_layout(), output_params.channel_layout());
+
+ ScopedAudioInputStream stream(audio_manager->MakeAudioInputStream(
+ params, AudioManagerBase::kLoopbackInputDeviceId));
+ ASSERT_TRUE(stream->Open());
+ FakeAudioInputCallback sink;
+ stream->Start(&sink);
+ ASSERT_FALSE(sink.error());
+
+ sink.WaitForData();
+ stream.Close();
+
+ EXPECT_FALSE(sink.received_data().empty());
+ EXPECT_TRUE(sink.closed());
+ EXPECT_FALSE(sink.error());
}
// This test is intended for manual tests and should only be enabled
@@ -389,7 +498,7 @@ TEST(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) {
const char* file_name = "out_stereo_10sec.pcm";
AudioInputStreamWrapper aisw(audio_manager.get());
- AudioInputStream* ais = aisw.Create();
+ ScopedAudioInputStream ais(aisw.Create());
EXPECT_TRUE(ais->Open());
LOG(INFO) << ">> Sample rate: " << aisw.sample_rate() << " [Hz]";
@@ -399,7 +508,7 @@ TEST(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) {
base::PlatformThread::Sleep(TestTimeouts::action_timeout());
ais->Stop();
LOG(INFO) << ">> Recording has stopped.";
- ais->Close();
+ ais.Close();
}
} // namespace media
diff --git a/media/audio/win/audio_manager_win.cc b/media/audio/win/audio_manager_win.cc
index e3a95883b2..0352e6677d 100644
--- a/media/audio/win/audio_manager_win.cc
+++ b/media/audio/win/audio_manager_win.cc
@@ -299,6 +299,11 @@ AudioParameters AudioManagerWin::GetInputStreamParameters(
std::string AudioManagerWin::GetAssociatedOutputDeviceID(
const std::string& input_device_id) {
+ if (!CoreAudioUtil::IsSupported()) {
+ NOTIMPLEMENTED()
+ << "GetAssociatedOutputDeviceID is not supported on this OS";
+ return std::string();
+ }
return CoreAudioUtil::GetMatchingOutputDeviceID(input_device_id);
}
diff --git a/media/audio/win/audio_manager_win.h b/media/audio/win/audio_manager_win.h
index b3e8de9286..86e22badc5 100644
--- a/media/audio/win/audio_manager_win.h
+++ b/media/audio/win/audio_manager_win.h
@@ -25,10 +25,10 @@ class MEDIA_EXPORT AudioManagerWin : public AudioManagerBase {
virtual bool HasAudioInputDevices() OVERRIDE;
virtual string16 GetAudioInputDeviceModel() OVERRIDE;
virtual void ShowAudioInputSettings() OVERRIDE;
- virtual void GetAudioInputDeviceNames(AudioDeviceNames* device_names)
- OVERRIDE;
- virtual void GetAudioOutputDeviceNames(AudioDeviceNames* device_names)
- OVERRIDE;
+ virtual void GetAudioInputDeviceNames(
+ AudioDeviceNames* device_names) OVERRIDE;
+ virtual void GetAudioOutputDeviceNames(
+ AudioDeviceNames* device_names) OVERRIDE;
virtual AudioParameters GetInputStreamParameters(
const std::string& device_id) OVERRIDE;
virtual std::string GetAssociatedOutputDeviceID(
@@ -62,9 +62,7 @@ class MEDIA_EXPORT AudioManagerWin : public AudioManagerBase {
};
// Allow unit test to modify the utilized enumeration API.
- // TODO(joi): Collapse these tests into one.
friend class AudioManagerTest;
- friend class AudioInputDeviceTest;
EnumerationType enumeration_type_;
EnumerationType enumeration_type() { return enumeration_type_; }
diff --git a/media/audio/win/device_enumeration_win.cc b/media/audio/win/device_enumeration_win.cc
index 50d0b7aa0e..aa66afb12b 100644
--- a/media/audio/win/device_enumeration_win.cc
+++ b/media/audio/win/device_enumeration_win.cc
@@ -25,10 +25,8 @@ using base::win::ScopedCoMem;
namespace media {
-namespace {
-
-bool GetDeviceNamesWinImpl(EDataFlow data_flow,
- AudioDeviceNames* device_names) {
+static bool GetDeviceNamesWinImpl(EDataFlow data_flow,
+ AudioDeviceNames* device_names) {
// It is assumed that this method is called from a COM thread, i.e.,
// CoInitializeEx() is not called here again to avoid STA/MTA conflicts.
ScopedComPtr<IMMDeviceEnumerator> enumerator;
@@ -103,7 +101,7 @@ bool GetDeviceNamesWinImpl(EDataFlow data_flow,
template <UINT (__stdcall *NumDevsFunc)(),
typename CAPSSTRUCT,
MMRESULT (__stdcall *DevCapsFunc)(UINT_PTR, CAPSSTRUCT*, UINT)>
-bool GetDeviceNamesWinXPImpl(AudioDeviceNames* device_names) {
+static bool GetDeviceNamesWinXPImpl(AudioDeviceNames* device_names) {
// Retrieve the number of active waveform input devices.
UINT number_of_active_devices = NumDevsFunc();
if (number_of_active_devices == 0)
@@ -138,8 +136,6 @@ bool GetDeviceNamesWinXPImpl(AudioDeviceNames* device_names) {
return true;
}
-} // namespace
-
bool GetInputDeviceNamesWin(AudioDeviceNames* device_names) {
return GetDeviceNamesWinImpl(eCapture, device_names);
}
diff --git a/media/audio/win/wavein_input_win.h b/media/audio/win/wavein_input_win.h
index 4b830e3480..df5ce4d129 100644
--- a/media/audio/win/wavein_input_win.h
+++ b/media/audio/win/wavein_input_win.h
@@ -56,7 +56,7 @@ class PCMWaveInAudioInputStream : public AudioInputStream {
};
// Allow unit tests to query the device ID.
- friend class AudioInputDeviceTest;
+ friend class AudioManagerTest;
// Windows calls us back with the recorded audio data here. See msdn
// documentation for 'waveInProc' for details about the parameters.