diff options
author | Andras Becsi <andras.becsi@digia.com> | 2013-12-11 21:33:03 +0100 |
---|---|---|
committer | Andras Becsi <andras.becsi@digia.com> | 2013-12-13 12:34:07 +0100 |
commit | f2a33ff9cbc6d19943f1c7fbddd1f23d23975577 (patch) | |
tree | 0586a32aa390ade8557dfd6b4897f43a07449578 /chromium/media/audio | |
parent | 5362912cdb5eea702b68ebe23702468d17c3017a (diff) | |
download | qtwebengine-chromium-f2a33ff9cbc6d19943f1c7fbddd1f23d23975577.tar.gz |
Update Chromium to branch 1650 (31.0.1650.63)
Change-Id: I57d8c832eaec1eb2364e0a8e7352a6dd354db99f
Reviewed-by: Jocelyn Turcotte <jocelyn.turcotte@digia.com>
Diffstat (limited to 'chromium/media/audio')
82 files changed, 2956 insertions, 2483 deletions
diff --git a/chromium/media/audio/android/audio_android_unittest.cc b/chromium/media/audio/android/audio_android_unittest.cc new file mode 100644 index 00000000000..a8e448f821f --- /dev/null +++ b/chromium/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/chromium/media/audio/android/audio_manager_android.cc b/chromium/media/audio/android/audio_manager_android.cc index 164344aba0b..04b226fa64f 100644 --- a/chromium/media/audio/android/audio_manager_android.cc +++ b/chromium/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( @@ -74,9 +84,12 @@ AudioParameters AudioManagerAndroid::GetInputStreamParameters( } AudioOutputStream* AudioManagerAndroid::MakeAudioOutputStream( - const AudioParameters& params, const std::string& input_device_id) { + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id) { AudioOutputStream* stream = - AudioManagerBase::MakeAudioOutputStream(params, std::string()); + AudioManagerBase::MakeAudioOutputStream(params, std::string(), + std::string()); if (stream && output_stream_count() == 1) { SetAudioMode(kAudioModeInCommunication); RegisterHeadsetReceiver(); @@ -87,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; } @@ -104,13 +117,16 @@ void AudioManagerAndroid::ReleaseInputStream(AudioInputStream* stream) { } AudioOutputStream* AudioManagerAndroid::MakeLinearOutputStream( - const AudioParameters& params) { + const AudioParameters& params) { DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); return new OpenSLESOutputStream(this, params); } AudioOutputStream* AudioManagerAndroid::MakeLowLatencyOutputStream( - const AudioParameters& params, const std::string& input_device_id) { + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id) { + DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); return new OpenSLESOutputStream(this, params); } @@ -140,7 +156,10 @@ int AudioManagerAndroid::GetOptimalOutputFrameSize(int sample_rate, } AudioParameters AudioManagerAndroid::GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) { + // TODO(tommi): Support |output_device_id|. + DLOG_IF(ERROR, !output_device_id.empty()) << "Not implemented!"; ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; int sample_rate = GetNativeOutputSampleRate(); int buffer_size = GetOptimalOutputFrameSize(sample_rate, 2); diff --git a/chromium/media/audio/android/audio_manager_android.h b/chromium/media/audio/android/audio_manager_android.h index fa1c3736a35..ed2b2c3ce91 100644 --- a/chromium/media/audio/android/audio_manager_android.h +++ b/chromium/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,16 +19,20 @@ 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; virtual AudioOutputStream* MakeAudioOutputStream( const AudioParameters& params, + 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; @@ -36,11 +41,14 @@ class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase { const AudioParameters& params) OVERRIDE; virtual AudioOutputStream* MakeLowLatencyOutputStream( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) OVERRIDE; 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); @@ -48,6 +56,7 @@ class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase { virtual ~AudioManagerAndroid(); virtual AudioParameters GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) OVERRIDE; private: @@ -59,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/chromium/media/audio/android/opensles_input.cc b/chromium/media/audio/android/opensles_input.cc index 15c3eac3726..a0e4ce3b987 100644 --- a/chromium/media/audio/android/opensles_input.cc +++ b/chromium/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/chromium/media/audio/android/opensles_input.h b/chromium/media/audio/android/opensles_input.h index 9743992fc65..e05831c6712 100644 --- a/chromium/media/audio/android/opensles_input.h +++ b/chromium/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/chromium/media/audio/android/opensles_output.cc b/chromium/media/audio/android/opensles_output.cc index c6d455715d9..5643f833c3d 100644 --- a/chromium/media/audio/android/opensles_output.cc +++ b/chromium/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/chromium/media/audio/android/opensles_output.h b/chromium/media/audio/android/opensles_output.h index f505b5165cd..7232d5da5f7 100644 --- a/chromium/media/audio/android/opensles_output.h +++ b/chromium/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/chromium/media/audio/android/opensles_wrapper.cc b/chromium/media/audio/android/opensles_wrapper.cc new file mode 100644 index 00000000000..b8f9ea45e4d --- /dev/null +++ b/chromium/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/chromium/media/audio/async_socket_io_handler.h b/chromium/media/audio/async_socket_io_handler.h deleted file mode 100644 index cc7185eb243..00000000000 --- a/chromium/media/audio/async_socket_io_handler.h +++ /dev/null @@ -1,113 +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. - -#ifndef MEDIA_AUDIO_ASYNC_SOCKET_IO_HANDLER_H_ -#define MEDIA_AUDIO_ASYNC_SOCKET_IO_HANDLER_H_ - -#include "base/message_loop/message_loop.h" -#include "base/sync_socket.h" -#include "base/threading/non_thread_safe.h" -#include "media/base/media_export.h" - -namespace media { - -// The message loop callback interface is different based on platforms. -#if defined(OS_WIN) -typedef base::MessageLoopForIO::IOHandler MessageLoopIOHandler; -#elif defined(OS_POSIX) -typedef base::MessageLoopForIO::Watcher MessageLoopIOHandler; -#endif - -// Extends the CancelableSyncSocket class to allow reading from a socket -// asynchronously on a TYPE_IO message loop thread. This makes it easy to share -// a thread that uses a message loop (e.g. for IPC and other things) and not -// require a separate thread to read from the socket. -// -// Example usage (also see the unit tests): -// -// class SocketReader { -// public: -// SocketReader(base::CancelableSyncSocket* socket) -// : socket_(socket), buffer_() { -// io_handler.Initialize(socket_->handle(), -// base::Bind(&SocketReader::OnDataAvailable, -// base::Unretained(this)); -// } -// -// void AsyncRead() { -// CHECK(io_handler.Read(&buffer_[0], sizeof(buffer_))); -// } -// -// private: -// void OnDataAvailable(int bytes_read) { -// if (ProcessData(&buffer_[0], bytes_read)) { -// // Issue another read. -// CHECK(io_handler.Read(&buffer_[0], sizeof(buffer_))); -// } -// } -// -// media::AsyncSocketIoHandler io_handler; -// base::CancelableSyncSocket* socket_; -// char buffer_[kBufferSize]; -// }; -// -class MEDIA_EXPORT AsyncSocketIoHandler - : public NON_EXPORTED_BASE(base::NonThreadSafe), - public NON_EXPORTED_BASE(MessageLoopIOHandler) { - public: - AsyncSocketIoHandler(); - virtual ~AsyncSocketIoHandler(); - - // Type definition for the callback. The parameter tells how many - // bytes were read and is 0 if an error occurred. - typedef base::Callback<void(int)> ReadCompleteCallback; - - // Initializes the AsyncSocketIoHandler by hooking it up to the current - // thread's message loop (must be TYPE_IO), to do async reads from the socket - // on the current thread. The |callback| will be invoked whenever a Read() - // has completed. - bool Initialize(base::SyncSocket::Handle socket, - const ReadCompleteCallback& callback); - - // Attempts to read from the socket. The return value will be |false| - // if an error occurred and |true| if data was read or a pending read - // was issued. Regardless of async or sync operation, the - // ReadCompleteCallback (see above) will be called when data is available. - bool Read(char* buffer, int buffer_len); - - private: -#if defined(OS_WIN) - // Implementation of IOHandler on Windows. - virtual void OnIOCompleted(base::MessageLoopForIO::IOContext* context, - DWORD bytes_transfered, - DWORD error) OVERRIDE; -#elif defined(OS_POSIX) - // Implementation of base::MessageLoopForIO::Watcher. - virtual void OnFileCanWriteWithoutBlocking(int socket) OVERRIDE {} - virtual void OnFileCanReadWithoutBlocking(int socket) OVERRIDE; - - void EnsureWatchingSocket(); -#endif - - base::SyncSocket::Handle socket_; -#if defined(OS_WIN) - base::MessageLoopForIO::IOContext* context_; - bool is_pending_; -#elif defined(OS_POSIX) - base::MessageLoopForIO::FileDescriptorWatcher socket_watcher_; - // |pending_buffer_| and |pending_buffer_len_| are valid only between - // Read() and OnFileCanReadWithoutBlocking(). - char* pending_buffer_; - int pending_buffer_len_; - // |true| iff the message loop is watching the socket for IO events. - bool is_watching_; -#endif - ReadCompleteCallback read_complete_; - - DISALLOW_COPY_AND_ASSIGN(AsyncSocketIoHandler); -}; - -} // namespace media. - -#endif // MEDIA_AUDIO_ASYNC_SOCKET_IO_HANDLER_H_ diff --git a/chromium/media/audio/async_socket_io_handler_posix.cc b/chromium/media/audio/async_socket_io_handler_posix.cc deleted file mode 100644 index be8f3708cb7..00000000000 --- a/chromium/media/audio/async_socket_io_handler_posix.cc +++ /dev/null @@ -1,98 +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 "media/audio/async_socket_io_handler.h" - -#include <fcntl.h> - -#include "base/posix/eintr_wrapper.h" - -namespace media { - -AsyncSocketIoHandler::AsyncSocketIoHandler() - : socket_(base::SyncSocket::kInvalidHandle), - pending_buffer_(NULL), - pending_buffer_len_(0), - is_watching_(false) { -} - -AsyncSocketIoHandler::~AsyncSocketIoHandler() { - DCHECK(CalledOnValidThread()); -} - -void AsyncSocketIoHandler::OnFileCanReadWithoutBlocking(int socket) { - DCHECK(CalledOnValidThread()); - DCHECK_EQ(socket, socket_); - DCHECK(!read_complete_.is_null()); - - if (pending_buffer_) { - int bytes_read = HANDLE_EINTR(read(socket_, pending_buffer_, - pending_buffer_len_)); - DCHECK_GE(bytes_read, 0); - pending_buffer_ = NULL; - pending_buffer_len_ = 0; - read_complete_.Run(bytes_read > 0 ? bytes_read : 0); - } else { - // We're getting notifications that we can read from the socket while - // we're not waiting for data. In order to not starve the message loop, - // let's stop watching the fd and restart the watch when Read() is called. - is_watching_ = false; - socket_watcher_.StopWatchingFileDescriptor(); - } -} - -bool AsyncSocketIoHandler::Read(char* buffer, int buffer_len) { - DCHECK(CalledOnValidThread()); - DCHECK(!read_complete_.is_null()); - DCHECK(!pending_buffer_); - - EnsureWatchingSocket(); - - int bytes_read = HANDLE_EINTR(read(socket_, buffer, buffer_len)); - if (bytes_read < 0) { - if (errno == EAGAIN) { - pending_buffer_ = buffer; - pending_buffer_len_ = buffer_len; - } else { - NOTREACHED() << "read(): " << errno; - return false; - } - } else { - read_complete_.Run(bytes_read); - } - return true; -} - -bool AsyncSocketIoHandler::Initialize(base::SyncSocket::Handle socket, - const ReadCompleteCallback& callback) { - DCHECK_EQ(socket_, base::SyncSocket::kInvalidHandle); - - DetachFromThread(); - - socket_ = socket; - read_complete_ = callback; - - // SyncSocket is blocking by default, so let's convert it to non-blocking. - int value = fcntl(socket, F_GETFL); - if (!(value & O_NONBLOCK)) { - // Set the socket to be non-blocking so we can do async reads. - if (fcntl(socket, F_SETFL, O_NONBLOCK) == -1) { - NOTREACHED(); - return false; - } - } - - return true; -} - -void AsyncSocketIoHandler::EnsureWatchingSocket() { - DCHECK(CalledOnValidThread()); - if (!is_watching_ && socket_ != base::SyncSocket::kInvalidHandle) { - is_watching_ = base::MessageLoopForIO::current()->WatchFileDescriptor( - socket_, true, base::MessageLoopForIO::WATCH_READ, - &socket_watcher_, this); - } -} - -} // namespace media. diff --git a/chromium/media/audio/async_socket_io_handler_unittest.cc b/chromium/media/audio/async_socket_io_handler_unittest.cc deleted file mode 100644 index ae971464dbc..00000000000 --- a/chromium/media/audio/async_socket_io_handler_unittest.cc +++ /dev/null @@ -1,168 +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 "media/audio/async_socket_io_handler.h" - -#include "base/bind.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { -const char kAsyncSocketIoTestString[] = "Hello, AsyncSocketIoHandler"; -const size_t kAsyncSocketIoTestStringLength = - arraysize(kAsyncSocketIoTestString); - -class TestSocketReader { - public: - // Set |number_of_reads_before_quit| to >0 when you expect a specific number - // of Read operations to complete. Once that number is reached, the current - // message loop will be Quit(). Set |number_of_reads_before_quit| to -1 if - // callbacks should not be counted. - TestSocketReader(base::CancelableSyncSocket* socket, - int number_of_reads_before_quit, - bool issue_reads_from_callback, - bool expect_eof) - : socket_(socket), buffer_(), - number_of_reads_before_quit_(number_of_reads_before_quit), - callbacks_received_(0), - issue_reads_from_callback_(issue_reads_from_callback), - expect_eof_(expect_eof) { - io_handler.Initialize(socket_->handle(), - base::Bind(&TestSocketReader::OnRead, - base::Unretained(this))); - } - ~TestSocketReader() {} - - bool IssueRead() { - return io_handler.Read(&buffer_[0], sizeof(buffer_)); - } - - const char* buffer() const { return &buffer_[0]; } - - int callbacks_received() const { return callbacks_received_; } - - private: - void OnRead(int bytes_read) { - if (!expect_eof_) { - EXPECT_GT(bytes_read, 0); - } else { - EXPECT_GE(bytes_read, 0); - } - ++callbacks_received_; - if (number_of_reads_before_quit_ == callbacks_received_) { - base::MessageLoop::current()->Quit(); - } else if (issue_reads_from_callback_) { - IssueRead(); - } - } - - media::AsyncSocketIoHandler io_handler; - base::CancelableSyncSocket* socket_; // Ownership lies outside the class. - char buffer_[kAsyncSocketIoTestStringLength]; - int number_of_reads_before_quit_; - int callbacks_received_; - bool issue_reads_from_callback_; - bool expect_eof_; -}; - -// Workaround to be able to use a base::Closure for sending data. -// Send() returns int but a closure must return void. -void SendData(base::CancelableSyncSocket* socket, - const void* buffer, - size_t length) { - socket->Send(buffer, length); -} - -} // end namespace. - -// Tests doing a pending read from a socket and use an IO handler to get -// notified of data. -TEST(AsyncSocketIoHandlerTest, AsynchronousReadWithMessageLoop) { - base::MessageLoopForIO loop; - - base::CancelableSyncSocket pair[2]; - ASSERT_TRUE(base::CancelableSyncSocket::CreatePair(&pair[0], &pair[1])); - - TestSocketReader reader(&pair[0], 1, false, false); - EXPECT_TRUE(reader.IssueRead()); - - pair[1].Send(kAsyncSocketIoTestString, kAsyncSocketIoTestStringLength); - base::MessageLoop::current()->Run(); - EXPECT_EQ(strcmp(reader.buffer(), kAsyncSocketIoTestString), 0); - EXPECT_EQ(1, reader.callbacks_received()); -} - -// Tests doing a read from a socket when we know that there is data in the -// socket. Here we want to make sure that any async 'can read' notifications -// won't trip us off and that the synchronous case works as well. -TEST(AsyncSocketIoHandlerTest, SynchronousReadWithMessageLoop) { - base::MessageLoopForIO loop; - - base::CancelableSyncSocket pair[2]; - ASSERT_TRUE(base::CancelableSyncSocket::CreatePair(&pair[0], &pair[1])); - - TestSocketReader reader(&pair[0], -1, false, false); - - pair[1].Send(kAsyncSocketIoTestString, kAsyncSocketIoTestStringLength); - base::MessageLoop::current()->PostDelayedTask(FROM_HERE, - base::MessageLoop::QuitClosure(), - base::TimeDelta::FromMilliseconds(100)); - base::MessageLoop::current()->Run(); - - EXPECT_TRUE(reader.IssueRead()); - EXPECT_EQ(strcmp(reader.buffer(), kAsyncSocketIoTestString), 0); - // We've now verified that the read happened synchronously, but it's not - // guaranteed that the callback has been issued since the callback will be - // called asynchronously even though the read may have been done. - // So we call RunUntilIdle() to allow any event notifications or APC's on - // Windows, to execute before checking the count of how many callbacks we've - // received. - base::MessageLoop::current()->RunUntilIdle(); - EXPECT_EQ(1, reader.callbacks_received()); -} - -// Calls Read() from within a callback to test that simple read "loops" work. -TEST(AsyncSocketIoHandlerTest, ReadFromCallback) { - base::MessageLoopForIO loop; - - base::CancelableSyncSocket pair[2]; - ASSERT_TRUE(base::CancelableSyncSocket::CreatePair(&pair[0], &pair[1])); - - const int kReadOperationCount = 10; - TestSocketReader reader(&pair[0], kReadOperationCount, true, false); - EXPECT_TRUE(reader.IssueRead()); - - // Issue sends on an interval to satisfy the Read() requirements. - int64 milliseconds = 0; - for (int i = 0; i < kReadOperationCount; ++i) { - base::MessageLoop::current()->PostDelayedTask(FROM_HERE, - base::Bind(&SendData, &pair[1], kAsyncSocketIoTestString, - kAsyncSocketIoTestStringLength), - base::TimeDelta::FromMilliseconds(milliseconds)); - milliseconds += 10; - } - - base::MessageLoop::current()->PostDelayedTask(FROM_HERE, - base::MessageLoop::QuitClosure(), - base::TimeDelta::FromMilliseconds(100 + milliseconds)); - - base::MessageLoop::current()->Run(); - EXPECT_EQ(kReadOperationCount, reader.callbacks_received()); -} - -// Calls Read() then close other end, check that a correct callback is received. -TEST(AsyncSocketIoHandlerTest, ReadThenClose) { - base::MessageLoopForIO loop; - - base::CancelableSyncSocket pair[2]; - ASSERT_TRUE(base::CancelableSyncSocket::CreatePair(&pair[0], &pair[1])); - - const int kReadOperationCount = 1; - TestSocketReader reader(&pair[0], kReadOperationCount, false, true); - EXPECT_TRUE(reader.IssueRead()); - - pair[1].Close(); - - base::MessageLoop::current()->Run(); - EXPECT_EQ(kReadOperationCount, reader.callbacks_received()); -} diff --git a/chromium/media/audio/async_socket_io_handler_win.cc b/chromium/media/audio/async_socket_io_handler_win.cc deleted file mode 100644 index ea6bd4ad0d5..00000000000 --- a/chromium/media/audio/async_socket_io_handler_win.cc +++ /dev/null @@ -1,77 +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 "media/audio/async_socket_io_handler.h" - -namespace media { - -AsyncSocketIoHandler::AsyncSocketIoHandler() - : socket_(base::SyncSocket::kInvalidHandle), - context_(NULL), - is_pending_(false) {} - -AsyncSocketIoHandler::~AsyncSocketIoHandler() { - // We need to be deleted on the correct thread to avoid racing with the - // message loop thread. - DCHECK(CalledOnValidThread()); - - if (context_) { - if (is_pending_) { - // Make the context be deleted by the message pump when done. - context_->handler = NULL; - } else { - delete context_; - } - } -} - -// Implementation of IOHandler on Windows. -void AsyncSocketIoHandler::OnIOCompleted( - base::MessageLoopForIO::IOContext* context, - DWORD bytes_transfered, - DWORD error) { - DCHECK(CalledOnValidThread()); - DCHECK_EQ(context_, context); - DCHECK(!read_complete_.is_null()); - is_pending_ = false; - read_complete_.Run(error == ERROR_SUCCESS ? bytes_transfered : 0); -} - -bool AsyncSocketIoHandler::Read(char* buffer, int buffer_len) { - DCHECK(CalledOnValidThread()); - DCHECK(!read_complete_.is_null()); - DCHECK(!is_pending_); - DCHECK_NE(socket_, base::SyncSocket::kInvalidHandle); - - DWORD bytes_read = 0; - BOOL ok = ::ReadFile(socket_, buffer, buffer_len, &bytes_read, - &context_->overlapped); - // The completion port will be signaled regardless of completing the read - // straight away or asynchronously (ERROR_IO_PENDING). OnIOCompleted() will - // be called regardless and we don't need to explicitly run the callback - // in the case where ok is FALSE and GLE==ERROR_IO_PENDING. - is_pending_ = !ok && (GetLastError() == ERROR_IO_PENDING); - return ok || is_pending_; -} - -bool AsyncSocketIoHandler::Initialize(base::SyncSocket::Handle socket, - const ReadCompleteCallback& callback) { - DCHECK(!context_); - DCHECK_EQ(socket_, base::SyncSocket::kInvalidHandle); - - DetachFromThread(); - - socket_ = socket; - read_complete_ = callback; - - base::MessageLoopForIO::current()->RegisterIOHandler(socket, this); - - context_ = new base::MessageLoopForIO::IOContext(); - context_->handler = this; - memset(&context_->overlapped, 0, sizeof(context_->overlapped)); - - return true; -} - -} // namespace media. diff --git a/chromium/media/audio/audio_input_controller.cc b/chromium/media/audio/audio_input_controller.cc index 31e137e2b17..ef94d1274d6 100644 --- a/chromium/media/audio/audio_input_controller.cc +++ b/chromium/media/audio/audio_input_controller.cc @@ -8,6 +8,7 @@ #include "base/threading/thread_restrictions.h" #include "media/base/limits.h" #include "media/base/scoped_histogram_timer.h" +#include "media/base/user_input_monitor.h" namespace { const int kMaxInputChannels = 2; @@ -18,16 +19,10 @@ const int kMaxInputChannels = 2; // breakage (very hard to repro bugs!) on other platforms: See // http://crbug.com/226327 and http://crbug.com/230972. const int kTimerResetIntervalSeconds = 1; -#if defined(OS_IOS) -// The first callback on iOS is received after the current background -// audio has faded away. -const int kTimerInitialIntervalSeconds = 4; -#else // We have received reports that the timer can be too trigger happy on some // Mac devices and the initial timer interval has therefore been increased // from 1 second to 5 seconds. const int kTimerInitialIntervalSeconds = 5; -#endif // defined(OS_IOS) } namespace media { @@ -36,14 +31,17 @@ namespace media { AudioInputController::Factory* AudioInputController::factory_ = NULL; AudioInputController::AudioInputController(EventHandler* handler, - SyncWriter* sync_writer) + SyncWriter* sync_writer, + UserInputMonitor* user_input_monitor) : creator_loop_(base::MessageLoopProxy::current()), handler_(handler), stream_(NULL), data_is_active_(false), state_(kEmpty), sync_writer_(sync_writer), - max_volume_(0.0) { + max_volume_(0.0), + user_input_monitor_(user_input_monitor), + prev_key_down_count_(0) { DCHECK(creator_loop_.get()); } @@ -56,17 +54,19 @@ scoped_refptr<AudioInputController> AudioInputController::Create( AudioManager* audio_manager, EventHandler* event_handler, const AudioParameters& params, - const std::string& device_id) { + const std::string& device_id, + UserInputMonitor* user_input_monitor) { DCHECK(audio_manager); if (!params.IsValid() || (params.channels() > kMaxInputChannels)) return NULL; - if (factory_) - return factory_->Create(audio_manager, event_handler, params); - - scoped_refptr<AudioInputController> controller(new AudioInputController( - event_handler, NULL)); + if (factory_) { + return factory_->Create( + audio_manager, event_handler, params, user_input_monitor); + } + scoped_refptr<AudioInputController> controller( + new AudioInputController(event_handler, NULL, user_input_monitor)); controller->message_loop_ = audio_manager->GetMessageLoop(); @@ -87,7 +87,8 @@ scoped_refptr<AudioInputController> AudioInputController::CreateLowLatency( EventHandler* event_handler, const AudioParameters& params, const std::string& device_id, - SyncWriter* sync_writer) { + SyncWriter* sync_writer, + UserInputMonitor* user_input_monitor) { DCHECK(audio_manager); DCHECK(sync_writer); @@ -96,8 +97,8 @@ scoped_refptr<AudioInputController> AudioInputController::CreateLowLatency( // Create the AudioInputController object and ensure that it runs on // the audio-manager thread. - scoped_refptr<AudioInputController> controller(new AudioInputController( - event_handler, sync_writer)); + scoped_refptr<AudioInputController> controller( + new AudioInputController(event_handler, sync_writer, user_input_monitor)); controller->message_loop_ = audio_manager->GetMessageLoop(); // Create and open a new audio input stream from the existing @@ -116,14 +117,15 @@ scoped_refptr<AudioInputController> AudioInputController::CreateForStream( const scoped_refptr<base::MessageLoopProxy>& message_loop, EventHandler* event_handler, AudioInputStream* stream, - SyncWriter* sync_writer) { + SyncWriter* sync_writer, + UserInputMonitor* user_input_monitor) { DCHECK(sync_writer); DCHECK(stream); // Create the AudioInputController object and ensure that it runs on // the audio-manager thread. - scoped_refptr<AudioInputController> controller(new AudioInputController( - event_handler, sync_writer)); + scoped_refptr<AudioInputController> controller( + new AudioInputController(event_handler, sync_writer, user_input_monitor)); controller->message_loop_ = message_loop; // TODO(miu): See TODO at top of file. Until that's resolved, we need to @@ -171,7 +173,7 @@ void AudioInputController::DoCreate(AudioManager* audio_manager, SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioInputController.CreateTime"); // TODO(miu): See TODO at top of file. Until that's resolved, assume all // platform audio input requires the |no_data_timer_| be used to auto-detect - // errors. In reality, probably only Windows and IOS need to be treated as + // errors. In reality, probably only Windows needs to be treated as // unreliable here. DoCreateForStream(audio_manager->MakeAudioInputStream(params, device_id), true); @@ -211,6 +213,11 @@ void AudioInputController::DoCreateForStream( state_ = kCreated; handler_->OnCreated(this); + + if (user_input_monitor_) { + user_input_monitor_->EnableKeyPressMonitoring(); + prev_key_down_count_ = user_input_monitor_->GetKeyPressCount(); + } } void AudioInputController::DoRecord() { @@ -251,6 +258,9 @@ void AudioInputController::DoClose() { } state_ = kClosed; + + if (user_input_monitor_) + user_input_monitor_->DisableKeyPressMonitoring(); } } @@ -317,8 +327,10 @@ void AudioInputController::DoCheckForNoData() { base::Unretained(this))); } -void AudioInputController::OnData(AudioInputStream* stream, const uint8* data, - uint32 size, uint32 hardware_delay_bytes, +void AudioInputController::OnData(AudioInputStream* stream, + const uint8* data, + uint32 size, + uint32 hardware_delay_bytes, double volume) { { base::AutoLock auto_lock(lock_); @@ -326,13 +338,21 @@ void AudioInputController::OnData(AudioInputStream* stream, const uint8* data, return; } + bool key_pressed = false; + if (user_input_monitor_) { + size_t current_count = user_input_monitor_->GetKeyPressCount(); + key_pressed = current_count != prev_key_down_count_; + prev_key_down_count_ = current_count; + DVLOG_IF(6, key_pressed) << "Detected keypress."; + } + // Mark data as active to ensure that the periodic calls to // DoCheckForNoData() does not report an error to the event handler. SetDataIsActive(true); // Use SyncSocket if we are in a low-latency mode. if (LowLatencyMode()) { - sync_writer_->Write(data, size, volume); + sync_writer_->Write(data, size, volume, key_pressed); sync_writer_->UpdateRecordedBytes(hardware_delay_bytes); return; } @@ -354,7 +374,7 @@ void AudioInputController::OnError(AudioInputStream* stream) { } void AudioInputController::DoStopCloseAndClearStream( - base::WaitableEvent *done) { + base::WaitableEvent* done) { DCHECK(message_loop_->BelongsToCurrentThread()); // Allow calling unconditionally and bail if we don't have a stream to close. diff --git a/chromium/media/audio/audio_input_controller.h b/chromium/media/audio/audio_input_controller.h index 586d47703a1..6b40459ded6 100644 --- a/chromium/media/audio/audio_input_controller.h +++ b/chromium/media/audio/audio_input_controller.h @@ -72,6 +72,8 @@ // namespace media { +class UserInputMonitor; + class MEDIA_EXPORT AudioInputController : public base::RefCountedThreadSafe<AudioInputController>, public AudioInputStream::AudioInputCallback { @@ -102,7 +104,10 @@ class MEDIA_EXPORT AudioInputController // Write certain amount of data from |data|. This method returns // number of written bytes. - virtual uint32 Write(const void* data, uint32 size, double volume) = 0; + virtual uint32 Write(const void* data, + uint32 size, + double volume, + bool key_pressed) = 0; // Close this synchronous writer. virtual void Close() = 0; @@ -110,11 +115,15 @@ class MEDIA_EXPORT AudioInputController // AudioInputController::Create() can use the currently registered Factory // to create the AudioInputController. Factory is intended for testing only. + // |user_input_monitor| is used for typing detection and can be NULL. class Factory { public: - virtual AudioInputController* Create(AudioManager* audio_manager, - EventHandler* event_handler, - AudioParameters params) = 0; + virtual AudioInputController* Create( + AudioManager* audio_manager, + EventHandler* event_handler, + AudioParameters params, + UserInputMonitor* user_input_monitor) = 0; + protected: virtual ~Factory() {} }; @@ -123,11 +132,13 @@ class MEDIA_EXPORT AudioInputController // The audio device will be created on the audio thread, and when that is // done, the event handler will receive an OnCreated() call from that same // thread. |device_id| is the unique ID of the audio device to be opened. + // |user_input_monitor| is used for typing detection and can be NULL. static scoped_refptr<AudioInputController> Create( AudioManager* audio_manager, EventHandler* event_handler, const AudioParameters& params, - const std::string& device_id); + const std::string& device_id, + UserInputMonitor* user_input_monitor); // Sets the factory used by the static method Create(). AudioInputController // does not take ownership of |factory|. A value of NULL results in an @@ -138,25 +149,28 @@ class MEDIA_EXPORT AudioInputController // Factory method for creating an AudioInputController for low-latency mode. // The audio device will be created on the audio thread, and when that is // done, the event handler will receive an OnCreated() call from that same - // thread. + // thread. |user_input_monitor| is used for typing detection and can be NULL. static scoped_refptr<AudioInputController> CreateLowLatency( AudioManager* audio_manager, EventHandler* event_handler, const AudioParameters& params, const std::string& device_id, // External synchronous writer for audio controller. - SyncWriter* sync_writer); + SyncWriter* sync_writer, + UserInputMonitor* user_input_monitor); // Factory method for creating an AudioInputController for low-latency mode, // taking ownership of |stream|. The stream will be opened on the audio // thread, and when that is done, the event handler will receive an - // OnCreated() call from that same thread. + // OnCreated() call from that same thread. |user_input_monitor| is used for + // typing detection and can be NULL. static scoped_refptr<AudioInputController> CreateForStream( const scoped_refptr<base::MessageLoopProxy>& message_loop, EventHandler* event_handler, AudioInputStream* stream, // External synchronous writer for audio controller. - SyncWriter* sync_writer); + SyncWriter* sync_writer, + UserInputMonitor* user_input_monitor); // Starts recording using the created audio input stream. // This method is called on the creator thread. @@ -201,7 +215,9 @@ class MEDIA_EXPORT AudioInputController kError }; - AudioInputController(EventHandler* handler, SyncWriter* sync_writer); + AudioInputController(EventHandler* handler, + SyncWriter* sync_writer, + UserInputMonitor* user_input_monitor); virtual ~AudioInputController(); // Methods called on the audio thread (owned by the AudioManager). @@ -266,6 +282,10 @@ class MEDIA_EXPORT AudioInputController double max_volume_; + UserInputMonitor* user_input_monitor_; + + size_t prev_key_down_count_; + DISALLOW_COPY_AND_ASSIGN(AudioInputController); }; diff --git a/chromium/media/audio/audio_input_controller_unittest.cc b/chromium/media/audio/audio_input_controller_unittest.cc index b96ef3ad016..6388cbf975b 100644 --- a/chromium/media/audio/audio_input_controller_unittest.cc +++ b/chromium/media/audio/audio_input_controller_unittest.cc @@ -83,9 +83,13 @@ TEST_F(AudioInputControllerTest, CreateAndClose) { scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout, kSampleRate, kBitsPerSample, kSamplesPerPacket); + scoped_refptr<AudioInputController> controller = - AudioInputController::Create(audio_manager.get(), &event_handler, params, - AudioManagerBase::kDefaultDeviceId); + AudioInputController::Create(audio_manager.get(), + &event_handler, + params, + AudioManagerBase::kDefaultDeviceId, + NULL); ASSERT_TRUE(controller.get()); // Wait for OnCreated() to fire. @@ -120,8 +124,11 @@ TEST_F(AudioInputControllerTest, RecordAndClose) { // Creating the AudioInputController should render an OnCreated() call. scoped_refptr<AudioInputController> controller = - AudioInputController::Create(audio_manager.get(), &event_handler, params, - AudioManagerBase::kDefaultDeviceId); + AudioInputController::Create(audio_manager.get(), + &event_handler, + params, + AudioManagerBase::kDefaultDeviceId, + NULL); ASSERT_TRUE(controller.get()); // Start recording and trigger one OnRecording() call. @@ -167,8 +174,11 @@ TEST_F(AudioInputControllerTest, RecordAndError) { // Creating the AudioInputController should render an OnCreated() call. scoped_refptr<AudioInputController> controller = - AudioInputController::Create(audio_manager.get(), &event_handler, params, - AudioManagerBase::kDefaultDeviceId); + AudioInputController::Create(audio_manager.get(), + &event_handler, + params, + AudioManagerBase::kDefaultDeviceId, + NULL); ASSERT_TRUE(controller.get()); // Start recording and trigger one OnRecording() call. @@ -196,11 +206,17 @@ TEST_F(AudioInputControllerTest, SamplesPerPacketTooLarge) { .Times(Exactly(0)); scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); - AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout, - kSampleRate, kBitsPerSample, kSamplesPerPacket * 1000); + AudioParameters params(AudioParameters::AUDIO_FAKE, + kChannelLayout, + kSampleRate, + kBitsPerSample, + kSamplesPerPacket * 1000); scoped_refptr<AudioInputController> controller = - AudioInputController::Create(audio_manager.get(), &event_handler, params, - AudioManagerBase::kDefaultDeviceId); + AudioInputController::Create(audio_manager.get(), + &event_handler, + params, + AudioManagerBase::kDefaultDeviceId, + NULL); ASSERT_FALSE(controller.get()); } @@ -216,11 +232,17 @@ TEST_F(AudioInputControllerTest, CloseTwice) { .Times(Exactly(1)); scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); - AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout, - kSampleRate, kBitsPerSample, kSamplesPerPacket); + AudioParameters params(AudioParameters::AUDIO_FAKE, + kChannelLayout, + kSampleRate, + kBitsPerSample, + kSamplesPerPacket); scoped_refptr<AudioInputController> controller = - AudioInputController::Create(audio_manager.get(), &event_handler, params, - AudioManagerBase::kDefaultDeviceId); + AudioInputController::Create(audio_manager.get(), + &event_handler, + params, + AudioManagerBase::kDefaultDeviceId, + NULL); ASSERT_TRUE(controller.get()); controller->Record(); diff --git a/chromium/media/audio/audio_input_device.cc b/chromium/media/audio/audio_input_device.cc index 87fd57143cd..d7685840ecf 100644 --- a/chromium/media/audio/audio_input_device.cc +++ b/chromium/media/audio/audio_input_device.cc @@ -291,9 +291,12 @@ void AudioInputDevice::AudioThreadCallback::Process(int pending_data) { uint8* ptr = static_cast<uint8*>(shared_memory_.memory()); ptr += current_segment_id_ * segment_length_; AudioInputBuffer* buffer = reinterpret_cast<AudioInputBuffer*>(ptr); - DCHECK_EQ(buffer->params.size, + // Usually this will be equal but in the case of low sample rate (e.g. 8kHz, + // the buffer may be bigger (on mac at least)). + DCHECK_GE(buffer->params.size, segment_length_ - sizeof(AudioInputBufferParameters)); double volume = buffer->params.volume; + bool key_pressed = buffer->params.key_pressed; int audio_delay_milliseconds = pending_data / bytes_per_ms_; int16* memory = reinterpret_cast<int16*>(&buffer->audio[0]); @@ -308,8 +311,8 @@ void AudioInputDevice::AudioThreadCallback::Process(int pending_data) { // Deliver captured data to the client in floating point format // and update the audio-delay measurement. - capture_callback_->Capture(audio_bus_.get(), - audio_delay_milliseconds, volume); + capture_callback_->Capture( + audio_bus_.get(), audio_delay_milliseconds, volume, key_pressed); } } // namespace media diff --git a/chromium/media/audio/audio_low_latency_input_output_unittest.cc b/chromium/media/audio/audio_low_latency_input_output_unittest.cc index 33729c45a04..a616761294d 100644 --- a/chromium/media/audio/audio_low_latency_input_output_unittest.cc +++ b/chromium/media/audio/audio_low_latency_input_output_unittest.cc @@ -308,7 +308,8 @@ class AudioOutputStreamTraits { static StreamType* CreateStream(AudioManager* audio_manager, const AudioParameters& params) { - return audio_manager->MakeAudioOutputStream(params, std::string()); + return audio_manager->MakeAudioOutputStream(params, std::string(), + std::string()); } }; diff --git a/chromium/media/audio/audio_manager.h b/chromium/media/audio/audio_manager.h index cc5b95c8197..891d2a26589 100644 --- a/chromium/media/audio/audio_manager.h +++ b/chromium/media/audio/audio_manager.h @@ -58,11 +58,16 @@ class MEDIA_EXPORT AudioManager { // threads to avoid blocking the rest of the application. virtual void ShowAudioInputSettings() = 0; - // Appends a list of available input devices. It is not guaranteed that - // all the devices in the list support all formats and sample rates for + // Appends a list of available input devices to |device_names|, + // which must initially be empty. It is not guaranteed that all the + // devices in the list support all formats and sample rates for // recording. virtual void GetAudioInputDeviceNames(AudioDeviceNames* device_names) = 0; + // Appends a list of available output devices to |device_names|, + // which must initially be empty. + virtual void GetAudioOutputDeviceNames(AudioDeviceNames* device_names) = 0; + // Factory for all the supported stream formats. |params| defines parameters // of the audio stream to be created. // @@ -71,6 +76,14 @@ class MEDIA_EXPORT AudioManager { // or three buffers are created, one will be locked for playback and one will // be ready to be filled in the call to AudioSourceCallback::OnMoreData(). // + // To create a stream for the default output device, pass an empty string + // for |device_id|, otherwise the specified audio device will be opened. + // + // The |input_device_id| is used for low-latency unified streams + // (input+output) only and then only if the audio parameters specify a >0 + // input channel count. In other cases this id is ignored and should be + // empty. + // // Returns NULL if the combination of the parameters is not supported, or if // we have reached some other platform specific limit. // @@ -82,14 +95,18 @@ class MEDIA_EXPORT AudioManager { // // Do not free the returned AudioOutputStream. It is owned by AudioManager. virtual AudioOutputStream* MakeAudioOutputStream( - const AudioParameters& params, const std::string& input_device_id) = 0; + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id) = 0; // Creates new audio output proxy. A proxy implements // AudioOutputStream interface, but unlike regular output stream // created with MakeAudioOutputStream() it opens device only when a // sound is actually playing. virtual AudioOutputStream* MakeAudioOutputStreamProxy( - const AudioParameters& params, const std::string& input_device_id) = 0; + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id) = 0; // Factory to create audio recording streams. // |channels| can be 1 or 2. @@ -130,14 +147,28 @@ class MEDIA_EXPORT AudioManager { // streams. It is a convenience interface to // AudioManagerBase::GetPreferredOutputStreamParameters and each AudioManager // does not need their own implementation to this interface. + // TODO(tommi): Remove this method and use GetOutputStreamParameteres instead. virtual AudioParameters GetDefaultOutputStreamParameters() = 0; + // Returns the output hardware audio parameters for a specific output device. + virtual AudioParameters GetOutputStreamParameters( + const std::string& device_id) = 0; + // Returns the input hardware audio parameters of the specific device // for opening input streams. Each AudioManager needs to implement their own // version of this interface. virtual AudioParameters GetInputStreamParameters( const std::string& device_id) = 0; + // Returns the device id of an output device that belongs to the same hardware + // as the specified input device. + // If the hardware has only an input device (e.g. a webcam), the return value + // will be empty (which the caller can then interpret to be the default output + // device). Implementations that don't yet support this feature, must return + // an empty string. + virtual std::string GetAssociatedOutputDeviceID( + const std::string& input_device_id) = 0; + protected: AudioManager(); diff --git a/chromium/media/audio/audio_manager_base.cc b/chromium/media/audio/audio_manager_base.cc index db77f004e38..5b1f4b3690a 100644 --- a/chromium/media/audio/audio_manager_base.cc +++ b/chromium/media/audio/audio_manager_base.cc @@ -34,19 +34,23 @@ 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, const AudioParameters& output, - const std::string& device_id) + const std::string& output_device_id, + const std::string& input_device_id) : input_params(input), output_params(output), - input_device_id(device_id) {} + input_device_id(input_device_id), + output_device_id(output_device_id) {} ~DispatcherParams() {} const AudioParameters input_params; const AudioParameters output_params; const std::string input_device_id; + const std::string output_device_id; scoped_refptr<AudioOutputDispatcher> dispatcher; private: @@ -65,6 +69,7 @@ class AudioManagerBase::CompareByParams { // of the existing dispatcher are the same as the request dispatcher. return (dispatcher_->input_params == dispatcher_in->input_params && dispatcher_->output_params == dispatcher_in->output_params && + dispatcher_->output_device_id == dispatcher_in->output_device_id && (!dispatcher_->input_params.input_channels() || dispatcher_->input_device_id == dispatcher_in->input_device_id)); } @@ -134,6 +139,7 @@ scoped_refptr<base::MessageLoopProxy> AudioManagerBase::GetWorkerLoop() { AudioOutputStream* AudioManagerBase::MakeAudioOutputStream( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) { // TODO(miu): Fix ~50 call points across several unit test modules to call // this method on the audio thread, then uncomment the following: @@ -159,10 +165,12 @@ AudioOutputStream* AudioManagerBase::MakeAudioOutputStream( AudioOutputStream* stream; switch (params.format()) { case AudioParameters::AUDIO_PCM_LINEAR: + DCHECK(device_id.empty()) + << "AUDIO_PCM_LINEAR supports only the default device."; stream = MakeLinearOutputStream(params); break; case AudioParameters::AUDIO_PCM_LOW_LATENCY: - stream = MakeLowLatencyOutputStream(params, input_device_id); + stream = MakeLowLatencyOutputStream(params, device_id, input_device_id); break; case AudioParameters::AUDIO_FAKE: stream = FakeAudioOutputStream::MakeFakeStream(this, params); @@ -180,7 +188,8 @@ AudioOutputStream* AudioManagerBase::MakeAudioOutputStream( } AudioInputStream* AudioManagerBase::MakeAudioInputStream( - const AudioParameters& params, const std::string& device_id) { + const AudioParameters& params, + const std::string& device_id) { // TODO(miu): Fix ~20 call points across several unit test modules to call // this method on the audio thread, then uncomment the following: // DCHECK(message_loop_->BelongsToCurrentThread()); @@ -222,19 +231,26 @@ AudioInputStream* AudioManagerBase::MakeAudioInputStream( } AudioOutputStream* AudioManagerBase::MakeAudioOutputStreamProxy( - const AudioParameters& params, const std::string& input_device_id) { -#if defined(OS_IOS) - // IOS implements audio input only. - NOTIMPLEMENTED(); - return NULL; -#else + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id) { DCHECK(message_loop_->BelongsToCurrentThread()); + // If the caller supplied an empty device id to select the default device, + // we fetch the actual device id of the default device so that the lookup + // will find the correct device regardless of whether it was opened as + // "default" or via the specific id. + // NOTE: Implementations that don't yet support opening non-default output + // devices may return an empty string from GetDefaultOutputDeviceID(). + std::string output_device_id = device_id.empty() ? + GetDefaultOutputDeviceID() : device_id; + // If we're not using AudioOutputResampler our output parameters are the same // as our input parameters. AudioParameters output_params = params; if (params.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY) { - output_params = GetPreferredOutputStreamParameters(params); + output_params = + GetPreferredOutputStreamParameters(output_device_id, params); // Ensure we only pass on valid output parameters. if (!output_params.IsValid()) { @@ -257,7 +273,8 @@ AudioOutputStream* AudioManagerBase::MakeAudioOutputStreamProxy( } DispatcherParams* dispatcher_params = - new DispatcherParams(params, output_params, input_device_id); + new DispatcherParams(params, output_params, output_device_id, + input_device_id); AudioOutputDispatchers::iterator it = std::find_if(output_dispatchers_.begin(), output_dispatchers_.end(), @@ -272,23 +289,28 @@ AudioOutputStream* AudioManagerBase::MakeAudioOutputStreamProxy( scoped_refptr<AudioOutputDispatcher> dispatcher; if (output_params.format() != AudioParameters::AUDIO_FAKE) { dispatcher = new AudioOutputResampler(this, params, output_params, - input_device_id, kCloseDelay); + output_device_id, input_device_id, + kCloseDelay); } else { dispatcher = new AudioOutputDispatcherImpl(this, output_params, + output_device_id, input_device_id, kCloseDelay); } dispatcher_params->dispatcher = dispatcher; output_dispatchers_.push_back(dispatcher_params); return new AudioOutputProxy(dispatcher.get()); -#endif // defined(OS_IOS) } void AudioManagerBase::ShowAudioInputSettings() { } void AudioManagerBase::GetAudioInputDeviceNames( - media::AudioDeviceNames* device_names) { + AudioDeviceNames* device_names) { +} + +void AudioManagerBase::GetAudioOutputDeviceNames( + AudioDeviceNames* device_names) { } void AudioManagerBase::ReleaseOutputStream(AudioOutputStream* stream) { @@ -333,10 +355,6 @@ void AudioManagerBase::Shutdown() { } void AudioManagerBase::ShutdownOnAudioThread() { -// IOS implements audio input only. -#if defined(OS_IOS) - return; -#else // This should always be running on the audio thread, but since we've cleared // the audio_thread_ member pointer when we get here, we can't verify exactly // what thread we're running on. The method is not public though and only @@ -357,7 +375,6 @@ void AudioManagerBase::ShutdownOnAudioThread() { } output_dispatchers_.clear(); -#endif // defined(OS_IOS) } void AudioManagerBase::AddOutputDeviceChangeListener( @@ -379,7 +396,14 @@ void AudioManagerBase::NotifyAllOutputDeviceChangeListeners() { } AudioParameters AudioManagerBase::GetDefaultOutputStreamParameters() { - return GetPreferredOutputStreamParameters(AudioParameters()); + return GetPreferredOutputStreamParameters(GetDefaultOutputDeviceID(), + AudioParameters()); +} + +AudioParameters AudioManagerBase::GetOutputStreamParameters( + const std::string& device_id) { + return GetPreferredOutputStreamParameters(device_id, + AudioParameters()); } AudioParameters AudioManagerBase::GetInputStreamParameters( @@ -388,4 +412,15 @@ AudioParameters AudioManagerBase::GetInputStreamParameters( return AudioParameters(); } +std::string AudioManagerBase::GetAssociatedOutputDeviceID( + const std::string& input_device_id) { + NOTIMPLEMENTED(); + return ""; +} + +std::string AudioManagerBase::GetDefaultOutputDeviceID() { + NOTIMPLEMENTED(); + return ""; +} + } // namespace media diff --git a/chromium/media/audio/audio_manager_base.h b/chromium/media/audio/audio_manager_base.h index 8b34d9fcf94..cdf7d3a76ae 100644 --- a/chromium/media/audio/audio_manager_base.h +++ b/chromium/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; @@ -47,10 +60,14 @@ class MEDIA_EXPORT AudioManagerBase : public AudioManager { virtual void ShowAudioInputSettings() OVERRIDE; virtual void GetAudioInputDeviceNames( - media::AudioDeviceNames* device_names) OVERRIDE; + AudioDeviceNames* device_names) OVERRIDE; + + virtual void GetAudioOutputDeviceNames( + AudioDeviceNames* device_names) OVERRIDE; virtual AudioOutputStream* MakeAudioOutputStream( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) OVERRIDE; virtual AudioInputStream* MakeAudioInputStream( @@ -58,6 +75,7 @@ class MEDIA_EXPORT AudioManagerBase : public AudioManager { virtual AudioOutputStream* MakeAudioOutputStreamProxy( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) OVERRIDE; // Called internally by the audio stream when it has been closed. @@ -72,7 +90,9 @@ class MEDIA_EXPORT AudioManagerBase : public AudioManager { // Creates the output stream for the |AUDIO_PCM_LOW_LATENCY| format. // |input_device_id| is used by unified IO to open the correct input device. virtual AudioOutputStream* MakeLowLatencyOutputStream( - const AudioParameters& params, const std::string& input_device_id) = 0; + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id) = 0; // Creates the input stream for the |AUDIO_PCM_LINEAR| format. The legacy // name is also from |AUDIO_PCM_LINEAR|. @@ -90,9 +110,15 @@ class MEDIA_EXPORT AudioManagerBase : public AudioManager { AudioDeviceListener* listener) OVERRIDE; virtual AudioParameters GetDefaultOutputStreamParameters() OVERRIDE; + virtual AudioParameters GetOutputStreamParameters( + const std::string& device_id) OVERRIDE; + virtual AudioParameters GetInputStreamParameters( const std::string& device_id) OVERRIDE; + virtual std::string GetAssociatedOutputDeviceID( + const std::string& input_device_id) OVERRIDE; + protected: AudioManagerBase(); @@ -115,9 +141,16 @@ class MEDIA_EXPORT AudioManagerBase : public AudioManager { // will decide if they should return the values from |input_params| or the // default hardware values. If the |input_params| is invalid, it will return // the default hardware audio parameters. + // If |output_device_id| is empty, the implementation must treat that as + // a request for the default output device. virtual AudioParameters GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) = 0; + // Returns the ID of the default audio output device. + // Implementations that don't yet support this should return an empty string. + virtual std::string GetDefaultOutputDeviceID(); + // Get number of input or output streams. int input_stream_count() { return num_input_streams_; } int output_stream_count() { return num_output_streams_; } diff --git a/chromium/media/audio/audio_input_device_unittest.cc b/chromium/media/audio/audio_manager_unittest.cc index dc211a48a93..4747c2e2996 100644 --- a/chromium/media/audio/audio_input_device_unittest.cc +++ b/chromium/media/audio/audio_manager_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// 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. @@ -9,20 +9,28 @@ #include "media/audio/audio_manager_base.h" #include "testing/gtest/include/gtest/gtest.h" +#if defined(OS_LINUX) +#include "media/audio/linux/audio_manager_linux.h" +#endif // defined(OS_LINUX) + #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 +#if defined(USE_PULSEAUDIO) +#include "media/audio/pulse/audio_manager_pulse.h" +#endif // defined(USE_PULSEAUDIO) + namespace media { // Test fixture which allows us to override the default enumeration API on // Windows. -class AudioInputDeviceTest +class AudioManagerTest : public ::testing::Test { protected: - AudioInputDeviceTest() + AudioManagerTest() : audio_manager_(AudioManager::Create()) #if defined(OS_WIN) , com_init_(base::win::ScopedCOMInitializer::kMTA) @@ -64,6 +72,7 @@ class AudioInputDeviceTest // 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(); @@ -78,6 +87,8 @@ class AudioInputDeviceTest 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), @@ -92,10 +103,14 @@ class AudioInputDeviceTest } } - bool CanRunAudioTest() { + bool CanRunInputTest() { return audio_manager_->HasAudioInputDevices(); } + bool CanRunOutputTest() { + return audio_manager_->HasAudioOutputDevices(); + } + scoped_ptr<AudioManager> audio_manager_; #if defined(OS_WIN) @@ -105,8 +120,8 @@ class AudioInputDeviceTest }; // Test that devices can be enumerated. -TEST_F(AudioInputDeviceTest, EnumerateDevices) { - if (!CanRunAudioTest()) +TEST_F(AudioManagerTest, EnumerateInputDevices) { + if (!CanRunInputTest()) return; AudioDeviceNames device_names; @@ -114,6 +129,16 @@ TEST_F(AudioInputDeviceTest, EnumerateDevices) { 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. @@ -121,8 +146,8 @@ TEST_F(AudioInputDeviceTest, EnumerateDevices) { // 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()) +TEST_F(AudioManagerTest, EnumerateInputDevicesWinMMDevice) { + if (!CanRunInputTest()) return; AudioDeviceNames device_names; @@ -135,10 +160,24 @@ TEST_F(AudioInputDeviceTest, EnumerateDevicesWinMMDevice) { 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(AudioInputDeviceTest, EnumerateDevicesWinWave) { - if (!CanRunAudioTest()) +TEST_F(AudioManagerTest, EnumerateInputDevicesWinWave) { + if (!CanRunInputTest()) return; AudioDeviceNames device_names; @@ -147,8 +186,18 @@ TEST_F(AudioInputDeviceTest, EnumerateDevicesWinWave) { CheckDeviceNames(device_names); } -TEST_F(AudioInputDeviceTest, WinXPDeviceIdUnchanged) { - if (!CanRunAudioTest()) +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; @@ -164,8 +213,8 @@ TEST_F(AudioInputDeviceTest, WinXPDeviceIdUnchanged) { } } -TEST_F(AudioInputDeviceTest, ConvertToWinXPDeviceId) { - if (!CanRunAudioTest()) +TEST_F(AudioManagerTest, ConvertToWinXPInputDeviceId) { + if (!CanRunInputTest()) return; if (!SetMMDeviceEnumeration()) { @@ -194,6 +243,103 @@ TEST_F(AudioInputDeviceTest, ConvertToWinXPDeviceId) { } } -#endif +#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) + if (!CanRunInputTest()) + return; + + 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) + if (!CanRunInputTest() || !CanRunOutputTest()) + return; + + AudioDeviceNames device_names; + audio_manager_->GetAudioInputDeviceNames(&device_names); + bool found_an_associated_device = false; + for (AudioDeviceNames::iterator it = device_names.begin(); + it != device_names.end(); + ++it) { + EXPECT_FALSE(it->unique_id.empty()); + EXPECT_FALSE(it->device_name.empty()); + std::string output_device_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; + } + } + + EXPECT_TRUE(found_an_associated_device); +#endif // defined(OS_WIN) || defined(OS_MACOSX) +} } // namespace media diff --git a/chromium/media/audio/audio_output_controller.cc b/chromium/media/audio/audio_output_controller.cc index f7f4cf8240b..649612cd4f6 100644 --- a/chromium/media/audio/audio_output_controller.cc +++ b/chromium/media/audio/audio_output_controller.cc @@ -20,26 +20,31 @@ using base::TimeDelta; namespace media { +#if defined(AUDIO_POWER_MONITORING) // Time constant for AudioPowerMonitor. See AudioPowerMonitor ctor comments for // semantics. This value was arbitrarily chosen, but seems to work well. static const int kPowerMeasurementTimeConstantMillis = 10; // Desired frequency of calls to EventHandler::OnPowerMeasured() for reporting // power levels in the audio signal. -static const int kPowerMeasurementsPerSecond = 30; +static const int kPowerMeasurementsPerSecond = 4; +#endif // Polling-related constants. const int AudioOutputController::kPollNumAttempts = 3; const int AudioOutputController::kPollPauseInMilliseconds = 3; -AudioOutputController::AudioOutputController(AudioManager* audio_manager, - EventHandler* handler, - const AudioParameters& params, - const std::string& input_device_id, - SyncReader* sync_reader) +AudioOutputController::AudioOutputController( + AudioManager* audio_manager, + EventHandler* handler, + const AudioParameters& params, + const std::string& output_device_id, + const std::string& input_device_id, + SyncReader* sync_reader) : audio_manager_(audio_manager), params_(params), handler_(handler), + output_device_id_(output_device_id), input_device_id_(input_device_id), stream_(NULL), diverting_to_stream_(NULL), @@ -48,10 +53,12 @@ AudioOutputController::AudioOutputController(AudioManager* audio_manager, num_allowed_io_(0), sync_reader_(sync_reader), message_loop_(audio_manager->GetMessageLoop()), - number_polling_attempts_left_(0), +#if defined(AUDIO_POWER_MONITORING) power_monitor_( params.sample_rate(), - TimeDelta::FromMilliseconds(kPowerMeasurementTimeConstantMillis)) { + TimeDelta::FromMilliseconds(kPowerMeasurementTimeConstantMillis)), +#endif + number_polling_attempts_left_(0) { DCHECK(audio_manager); DCHECK(handler_); DCHECK(sync_reader_); @@ -67,6 +74,7 @@ scoped_refptr<AudioOutputController> AudioOutputController::Create( AudioManager* audio_manager, EventHandler* event_handler, const AudioParameters& params, + const std::string& output_device_id, const std::string& input_device_id, SyncReader* sync_reader) { DCHECK(audio_manager); @@ -76,7 +84,8 @@ scoped_refptr<AudioOutputController> AudioOutputController::Create( return NULL; scoped_refptr<AudioOutputController> controller(new AudioOutputController( - audio_manager, event_handler, params, input_device_id, sync_reader)); + audio_manager, event_handler, params, output_device_id, input_device_id, + sync_reader)); controller->message_loop_->PostTask(FROM_HERE, base::Bind( &AudioOutputController::DoCreate, controller, false)); return controller; @@ -114,8 +123,10 @@ void AudioOutputController::DoCreate(bool is_for_device_change) { DoStopCloseAndClearStream(); // Calls RemoveOutputDeviceChangeListener(). DCHECK_EQ(kEmpty, state_); - stream_ = diverting_to_stream_ ? diverting_to_stream_ : - audio_manager_->MakeAudioOutputStreamProxy(params_, input_device_id_); + stream_ = diverting_to_stream_ ? + diverting_to_stream_ : + audio_manager_->MakeAudioOutputStreamProxy(params_, output_device_id_, + input_device_id_); if (!stream_) { state_ = kError; handler_->OnError(); @@ -158,6 +169,7 @@ void AudioOutputController::DoPlay() { state_ = kPlaying; +#if defined(AUDIO_POWER_MONITORING) power_monitor_.Reset(); power_poll_callback_.Reset( base::Bind(&AudioOutputController::ReportPowerMeasurementPeriodically, @@ -165,6 +177,7 @@ void AudioOutputController::DoPlay() { // Run the callback to send an initial notification that we're starting in // silence, and to schedule periodic callbacks. power_poll_callback_.callback().Run(); +#endif // We start the AudioOutputStream lazily. AllowEntryToOnMoreIOData(); @@ -173,6 +186,7 @@ void AudioOutputController::DoPlay() { handler_->OnPlaying(); } +#if defined(AUDIO_POWER_MONITORING) void AudioOutputController::ReportPowerMeasurementPeriodically() { DCHECK(message_loop_->BelongsToCurrentThread()); const std::pair<float, bool>& reading = @@ -182,6 +196,7 @@ void AudioOutputController::ReportPowerMeasurementPeriodically() { FROM_HERE, power_poll_callback_.callback(), TimeDelta::FromSeconds(1) / kPowerMeasurementsPerSecond); } +#endif void AudioOutputController::StopStream() { DCHECK(message_loop_->BelongsToCurrentThread()); @@ -190,7 +205,9 @@ void AudioOutputController::StopStream() { stream_->Stop(); DisallowEntryToOnMoreIOData(); +#if defined(AUDIO_POWER_MONITORING) power_poll_callback_.Cancel(); +#endif state_ = kPaused; } @@ -208,8 +225,10 @@ void AudioOutputController::DoPause() { // Send a special pause mark to the low-latency audio thread. sync_reader_->UpdatePendingBytes(kPauseMark); +#if defined(AUDIO_POWER_MONITORING) // Paused means silence follows. handler_->OnPowerMeasured(AudioPowerMonitor::zero_power(), false); +#endif handler_->OnPaused(); } @@ -283,7 +302,9 @@ int AudioOutputController::OnMoreIOData(AudioBus* source, sync_reader_->UpdatePendingBytes( buffers_state.total_bytes() + frames * params_.GetBytesPerFrame()); +#if defined(AUDIO_POWER_MONITORING) power_monitor_.Scan(*dest, frames); +#endif AllowEntryToOnMoreIOData(); return frames; diff --git a/chromium/media/audio/audio_output_controller.h b/chromium/media/audio/audio_output_controller.h index 38a2c03f590..615c6a5e6c6 100644 --- a/chromium/media/audio/audio_output_controller.h +++ b/chromium/media/audio/audio_output_controller.h @@ -52,6 +52,11 @@ namespace media { +// Only do power monitoring for non-mobile platforms that need it for the UI. +#if !defined(OS_ANDROID) && !defined(OS_IOS) +#define AUDIO_POWER_MONITORING +#endif + class MEDIA_EXPORT AudioOutputController : public base::RefCountedThreadSafe<AudioOutputController>, public AudioOutputStream::AudioSourceCallback, @@ -101,10 +106,14 @@ class MEDIA_EXPORT AudioOutputController // thread, and if this is successful, the |event_handler| will receive an // OnCreated() call from the same audio manager thread. |audio_manager| must // outlive AudioOutputController. + // The |output_device_id| can be either empty (default device) or specify a + // specific hardware device for audio output. The |input_device_id| is + // used only for unified audio when opening up input and output at the same + // time (controlled by |params.input_channel_count()|). static scoped_refptr<AudioOutputController> Create( AudioManager* audio_manager, EventHandler* event_handler, - const AudioParameters& params, const std::string& input_device_id, - SyncReader* sync_reader); + const AudioParameters& params, const std::string& output_device_id, + const std::string& input_device_id, SyncReader* sync_reader); // Methods to control playback of the stream. @@ -166,6 +175,7 @@ class MEDIA_EXPORT AudioOutputController AudioOutputController(AudioManager* audio_manager, EventHandler* handler, const AudioParameters& params, + const std::string& output_device_id, const std::string& input_device_id, SyncReader* sync_reader); @@ -198,8 +208,12 @@ class MEDIA_EXPORT AudioOutputController const AudioParameters params_; EventHandler* const handler_; + // Specifies the device id of the output device to open or empty for the + // default output device. + const std::string output_device_id_; + // Used by the unified IO to open the correct input device. - std::string input_device_id_; + const std::string input_device_id_; AudioOutputStream* stream_; @@ -227,15 +241,17 @@ class MEDIA_EXPORT AudioOutputController // The message loop of audio manager thread that this object runs on. const scoped_refptr<base::MessageLoopProxy> message_loop_; - // When starting stream we wait for data to become available. - // Number of times left. - int number_polling_attempts_left_; - +#if defined(AUDIO_POWER_MONITORING) // Scans audio samples from OnMoreIOData() as input to compute power levels. AudioPowerMonitor power_monitor_; // Periodic callback to report power levels during playback. base::CancelableClosure power_poll_callback_; +#endif + + // When starting stream we wait for data to become available. + // Number of times left. + int number_polling_attempts_left_; DISALLOW_COPY_AND_ASSIGN(AudioOutputController); }; diff --git a/chromium/media/audio/audio_output_controller_unittest.cc b/chromium/media/audio/audio_output_controller_unittest.cc index 128cc07716f..a7118e17a30 100644 --- a/chromium/media/audio/audio_output_controller_unittest.cc +++ b/chromium/media/audio/audio_output_controller_unittest.cc @@ -29,8 +29,6 @@ static const int kSampleRate = AudioParameters::kAudioCDSampleRate; static const int kBitsPerSample = 16; static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO; static const int kSamplesPerPacket = kSampleRate / 100; -static const int kHardwareBufferSize = kSamplesPerPacket * - ChannelLayoutToChannelCount(kChannelLayout) * kBitsPerSample / 8; static const double kTestVolume = 0.25; class MockAudioOutputControllerEventHandler @@ -122,7 +120,7 @@ class AudioOutputControllerTest : public testing::Test { controller_ = AudioOutputController::Create( audio_manager_.get(), &mock_event_handler_, params_, std::string(), - &mock_sync_reader_); + std::string(), &mock_sync_reader_); if (controller_.get()) controller_->SetVolume(kTestVolume); @@ -134,8 +132,10 @@ class AudioOutputControllerTest : public testing::Test { // OnPowerMeasured() calls. EXPECT_CALL(mock_event_handler_, OnPlaying()) .WillOnce(SignalEvent(&play_event_)); +#if defined(AUDIO_POWER_MONITORING) EXPECT_CALL(mock_event_handler_, OnPowerMeasured(_, false)) .Times(AtLeast(1)); +#endif // During playback, the mock pretends to provide audio data rendered and // sent from the render process. diff --git a/chromium/media/audio/audio_output_dispatcher.cc b/chromium/media/audio/audio_output_dispatcher.cc index 06206d7be7f..a151c449f02 100644 --- a/chromium/media/audio/audio_output_dispatcher.cc +++ b/chromium/media/audio/audio_output_dispatcher.cc @@ -11,10 +11,12 @@ namespace media { AudioOutputDispatcher::AudioOutputDispatcher( AudioManager* audio_manager, const AudioParameters& params, + const std::string& output_device_id, const std::string& input_device_id) : audio_manager_(audio_manager), message_loop_(base::MessageLoop::current()), params_(params), + output_device_id_(output_device_id), input_device_id_(input_device_id) { // We expect to be instantiated on the audio thread. Otherwise the // message_loop_ member will point to the wrong message loop! diff --git a/chromium/media/audio/audio_output_dispatcher.h b/chromium/media/audio/audio_output_dispatcher.h index a79fd94477f..30266ed6a9a 100644 --- a/chromium/media/audio/audio_output_dispatcher.h +++ b/chromium/media/audio/audio_output_dispatcher.h @@ -38,6 +38,7 @@ class MEDIA_EXPORT AudioOutputDispatcher public: AudioOutputDispatcher(AudioManager* audio_manager, const AudioParameters& params, + const std::string& output_device_id, const std::string& input_device_id); // Called by AudioOutputProxy to open the stream. @@ -79,6 +80,7 @@ class MEDIA_EXPORT AudioOutputDispatcher AudioManager* audio_manager_; base::MessageLoop* message_loop_; AudioParameters params_; + const std::string output_device_id_; const std::string input_device_id_; private: diff --git a/chromium/media/audio/audio_output_dispatcher_impl.cc b/chromium/media/audio/audio_output_dispatcher_impl.cc index 1df8e7ddd5b..bcdcd65146e 100644 --- a/chromium/media/audio/audio_output_dispatcher_impl.cc +++ b/chromium/media/audio/audio_output_dispatcher_impl.cc @@ -19,9 +19,11 @@ namespace media { AudioOutputDispatcherImpl::AudioOutputDispatcherImpl( AudioManager* audio_manager, const AudioParameters& params, + const std::string& output_device_id, const std::string& input_device_id, const base::TimeDelta& close_delay) - : AudioOutputDispatcher(audio_manager, params, input_device_id), + : AudioOutputDispatcher(audio_manager, params, output_device_id, + input_device_id), pause_delay_(base::TimeDelta::FromMicroseconds( 2 * params.frames_per_buffer() * base::Time::kMicrosecondsPerSecond / static_cast<float>(params.sample_rate()))), @@ -168,7 +170,7 @@ void AudioOutputDispatcherImpl::Shutdown() { bool AudioOutputDispatcherImpl::CreateAndOpenStream() { DCHECK_EQ(base::MessageLoop::current(), message_loop_); AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream( - params_, input_device_id_); + params_, output_device_id_, input_device_id_); if (!stream) return false; diff --git a/chromium/media/audio/audio_output_dispatcher_impl.h b/chromium/media/audio/audio_output_dispatcher_impl.h index 06fe3ebeaf1..b59f835f9b0 100644 --- a/chromium/media/audio/audio_output_dispatcher_impl.h +++ b/chromium/media/audio/audio_output_dispatcher_impl.h @@ -35,6 +35,7 @@ class MEDIA_EXPORT AudioOutputDispatcherImpl : public AudioOutputDispatcher { // the audio device is closed. AudioOutputDispatcherImpl(AudioManager* audio_manager, const AudioParameters& params, + const std::string& output_device_id, const std::string& input_device_id, const base::TimeDelta& close_delay); diff --git a/chromium/media/audio/audio_output_proxy_unittest.cc b/chromium/media/audio/audio_output_proxy_unittest.cc index de95b0661ec..1806ce66131 100644 --- a/chromium/media/audio/audio_output_proxy_unittest.cc +++ b/chromium/media/audio/audio_output_proxy_unittest.cc @@ -95,10 +95,14 @@ class MockAudioManager : public AudioManagerBase { MOCK_METHOD0(HasAudioOutputDevices, bool()); MOCK_METHOD0(HasAudioInputDevices, bool()); MOCK_METHOD0(GetAudioInputDeviceModel, string16()); - MOCK_METHOD2(MakeAudioOutputStream, AudioOutputStream*( - const AudioParameters& params, const std::string& input_device_id)); - MOCK_METHOD2(MakeAudioOutputStreamProxy, AudioOutputStream*( - const AudioParameters& params, const std::string& input_device_id)); + MOCK_METHOD3(MakeAudioOutputStream, AudioOutputStream*( + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id)); + MOCK_METHOD3(MakeAudioOutputStreamProxy, AudioOutputStream*( + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id)); MOCK_METHOD2(MakeAudioInputStream, AudioInputStream*( const AudioParameters& params, const std::string& device_id)); MOCK_METHOD0(ShowAudioInputSettings, void()); @@ -108,14 +112,15 @@ class MockAudioManager : public AudioManagerBase { MOCK_METHOD1(MakeLinearOutputStream, AudioOutputStream*( const AudioParameters& params)); - MOCK_METHOD2(MakeLowLatencyOutputStream, AudioOutputStream*( - const AudioParameters& params, const std::string& input_device_id)); + MOCK_METHOD3(MakeLowLatencyOutputStream, AudioOutputStream*( + const AudioParameters& params, const std::string& device_id, + const std::string& input_device_id)); MOCK_METHOD2(MakeLinearInputStream, AudioInputStream*( const AudioParameters& params, const std::string& device_id)); MOCK_METHOD2(MakeLowLatencyInputStream, AudioInputStream*( const AudioParameters& params, const std::string& device_id)); - MOCK_METHOD1(GetPreferredOutputStreamParameters, AudioParameters( - const AudioParameters& params)); + MOCK_METHOD2(GetPreferredOutputStreamParameters, AudioParameters( + const std::string& device_id, const AudioParameters& params)); }; class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback { @@ -161,6 +166,7 @@ class AudioOutputProxyTest : public testing::Test { dispatcher_impl_ = new AudioOutputDispatcherImpl(&manager(), params_, std::string(), + std::string(), close_delay); // Necessary to know how long the dispatcher will wait before posting @@ -186,7 +192,7 @@ class AudioOutputProxyTest : public testing::Test { void OpenAndClose(AudioOutputDispatcher* dispatcher) { MockAudioOutputStream stream(&manager_, params_); - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .WillOnce(Return(&stream)); EXPECT_CALL(stream, Open()) .WillOnce(Return(true)); @@ -203,7 +209,7 @@ class AudioOutputProxyTest : public testing::Test { void StartAndStop(AudioOutputDispatcher* dispatcher) { MockAudioOutputStream stream(&manager_, params_); - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .WillOnce(Return(&stream)); EXPECT_CALL(stream, Open()) .WillOnce(Return(true)); @@ -229,7 +235,7 @@ class AudioOutputProxyTest : public testing::Test { void CloseAfterStop(AudioOutputDispatcher* dispatcher) { MockAudioOutputStream stream(&manager_, params_); - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .WillOnce(Return(&stream)); EXPECT_CALL(stream, Open()) .WillOnce(Return(true)); @@ -261,7 +267,7 @@ class AudioOutputProxyTest : public testing::Test { void TwoStreams(AudioOutputDispatcher* dispatcher) { MockAudioOutputStream stream(&manager_, params_); - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .WillOnce(Return(&stream)); EXPECT_CALL(stream, Open()) .WillOnce(Return(true)); @@ -283,7 +289,7 @@ class AudioOutputProxyTest : public testing::Test { void OpenFailed(AudioOutputDispatcher* dispatcher) { MockAudioOutputStream stream(&manager_, params_); - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .WillOnce(Return(&stream)); EXPECT_CALL(stream, Open()) .WillOnce(Return(false)); @@ -301,7 +307,7 @@ class AudioOutputProxyTest : public testing::Test { void CreateAndWait(AudioOutputDispatcher* dispatcher) { MockAudioOutputStream stream(&manager_, params_); - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .WillOnce(Return(&stream)); EXPECT_CALL(stream, Open()) .WillOnce(Return(true)); @@ -328,7 +334,7 @@ class AudioOutputProxyTest : public testing::Test { MockAudioOutputStream stream1(&manager_, params_); MockAudioOutputStream stream2(&manager_, params_); - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .WillOnce(Return(&stream1)) .WillOnce(Return(&stream2)); @@ -366,7 +372,7 @@ class AudioOutputProxyTest : public testing::Test { MockAudioOutputStream stream1(&manager_, params_); MockAudioOutputStream stream2(&manager_, params_); - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .WillOnce(Return(&stream1)) .WillOnce(Return(&stream2)); @@ -406,7 +412,7 @@ class AudioOutputProxyTest : public testing::Test { void StartFailed(AudioOutputDispatcher* dispatcher) { MockAudioOutputStream stream(&manager_, params_); - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .WillOnce(Return(&stream)); EXPECT_CALL(stream, Open()) .WillOnce(Return(true)); @@ -425,7 +431,7 @@ class AudioOutputProxyTest : public testing::Test { Mock::VerifyAndClear(&stream); // |stream| is closed at this point. Start() should reopen it again. - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .Times(2) .WillRepeatedly(Return(reinterpret_cast<AudioOutputStream*>(NULL))); @@ -467,7 +473,8 @@ class AudioOutputResamplerTest : public AudioOutputProxyTest { AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, 16000, 16, 1024); resampler_ = new AudioOutputResampler( - &manager(), params_, resampler_params_, std::string(), close_delay); + &manager(), params_, resampler_params_, std::string(), std::string(), + close_delay); } virtual void OnStart() OVERRIDE { @@ -568,7 +575,7 @@ TEST_F(AudioOutputResamplerTest, StartFailed) { StartFailed(resampler_.get()); } // ensure AudioOutputResampler falls back to the high latency path. TEST_F(AudioOutputResamplerTest, LowLatencyCreateFailedFallback) { MockAudioOutputStream stream(&manager_, params_); - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .Times(2) .WillOnce(Return(static_cast<AudioOutputStream*>(NULL))) .WillRepeatedly(Return(&stream)); @@ -588,7 +595,7 @@ TEST_F(AudioOutputResamplerTest, LowLatencyCreateFailedFallback) { TEST_F(AudioOutputResamplerTest, LowLatencyOpenFailedFallback) { MockAudioOutputStream failed_stream(&manager_, params_); MockAudioOutputStream okay_stream(&manager_, params_); - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .Times(2) .WillOnce(Return(&failed_stream)) .WillRepeatedly(Return(&okay_stream)); @@ -619,7 +626,7 @@ TEST_F(AudioOutputResamplerTest, HighLatencyFallbackFailed) { #else static const int kFallbackCount = 1; #endif - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .Times(kFallbackCount) .WillRepeatedly(Return(static_cast<AudioOutputStream*>(NULL))); @@ -630,7 +637,7 @@ TEST_F(AudioOutputResamplerTest, HighLatencyFallbackFailed) { testing::Property(&AudioParameters::sample_rate, params_.sample_rate()), testing::Property( &AudioParameters::frames_per_buffer, params_.frames_per_buffer())), - _)) + _, _)) .Times(1) .WillOnce(Return(&okay_stream)); EXPECT_CALL(okay_stream, Open()) @@ -655,7 +662,7 @@ TEST_F(AudioOutputResamplerTest, AllFallbackFailed) { #else static const int kFallbackCount = 2; #endif - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .Times(kFallbackCount) .WillRepeatedly(Return(static_cast<AudioOutputStream*>(NULL))); @@ -673,7 +680,7 @@ TEST_F(AudioOutputResamplerTest, LowLatencyOpenEventuallyFails) { MockAudioOutputStream stream3(&manager_, params_); // Setup the mock such that all three streams are successfully created. - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .WillOnce(Return(&stream1)) .WillOnce(Return(&stream2)) .WillOnce(Return(&stream3)) diff --git a/chromium/media/audio/audio_output_resampler.cc b/chromium/media/audio/audio_output_resampler.cc index 6db0e2fb2fe..da424ec1246 100644 --- a/chromium/media/audio/audio_output_resampler.cc +++ b/chromium/media/audio/audio_output_resampler.cc @@ -147,12 +147,13 @@ static AudioParameters SetupFallbackParams( AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager, const AudioParameters& input_params, const AudioParameters& output_params, + const std::string& output_device_id, const std::string& input_device_id, const base::TimeDelta& close_delay) - : AudioOutputDispatcher(audio_manager, input_params, input_device_id), + : AudioOutputDispatcher(audio_manager, input_params, output_device_id, + input_device_id), close_delay_(close_delay), output_params_(output_params), - input_device_id_(input_device_id), streams_opened_(false) { DCHECK(input_params.IsValid()); DCHECK(output_params.IsValid()); @@ -172,7 +173,8 @@ void AudioOutputResampler::Initialize() { DCHECK(!streams_opened_); DCHECK(callbacks_.empty()); dispatcher_ = new AudioOutputDispatcherImpl( - audio_manager_, output_params_, input_device_id_, close_delay_); + audio_manager_, output_params_, output_device_id_, input_device_id_, + close_delay_); } bool AudioOutputResampler::OpenStream() { diff --git a/chromium/media/audio/audio_output_resampler.h b/chromium/media/audio/audio_output_resampler.h index df9e4320b55..f9a75ac38f5 100644 --- a/chromium/media/audio/audio_output_resampler.h +++ b/chromium/media/audio/audio_output_resampler.h @@ -40,6 +40,7 @@ class MEDIA_EXPORT AudioOutputResampler : public AudioOutputDispatcher { AudioOutputResampler(AudioManager* audio_manager, const AudioParameters& input_params, const AudioParameters& output_params, + const std::string& output_device_id, const std::string& input_device_id, const base::TimeDelta& close_delay); @@ -74,9 +75,6 @@ class MEDIA_EXPORT AudioOutputResampler : public AudioOutputDispatcher { // AudioParameters used to setup the output stream. AudioParameters output_params_; - // Device ID to be used by the unified IO to open the correct input device. - const std::string input_device_id_; - // Whether any streams have been opened through |dispatcher_|, if so we can't // fallback on future OpenStream() failures. bool streams_opened_; diff --git a/chromium/media/audio/audio_parameters.h b/chromium/media/audio/audio_parameters.h index 2817cd2c5a6..bc629a7db00 100644 --- a/chromium/media/audio/audio_parameters.h +++ b/chromium/media/audio/audio_parameters.h @@ -14,6 +14,7 @@ namespace media { struct MEDIA_EXPORT AudioInputBufferParameters { double volume; uint32 size; + bool key_pressed; }; // Use a struct-in-struct approach to ensure that we can calculate the required diff --git a/chromium/media/audio/clockless_audio_sink.cc b/chromium/media/audio/clockless_audio_sink.cc new file mode 100644 index 00000000000..ff809d0541d --- /dev/null +++ b/chromium/media/audio/clockless_audio_sink.cc @@ -0,0 +1,107 @@ +// 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 "media/audio/clockless_audio_sink.h" + +#include "base/threading/simple_thread.h" +#include "base/time/time.h" +#include "media/base/audio_renderer_sink.h" + +namespace media { + +// Internal to ClocklessAudioSink. Class is used to call Render() on a seperate +// thread, running as fast as it can read the data. +class ClocklessAudioSinkThread : public base::DelegateSimpleThread::Delegate { + public: + explicit ClocklessAudioSinkThread(const AudioParameters& params, + AudioRendererSink::RenderCallback* callback) + : callback_(callback), + audio_bus_(AudioBus::Create(params)), + stop_event_(new base::WaitableEvent(false, false)) {} + + void Start() { + stop_event_->Reset(); + thread_.reset(new base::DelegateSimpleThread(this, "ClocklessAudioSink")); + thread_->Start(); + } + + // Generate a signal to stop calling Render(). + base::TimeDelta Stop() { + stop_event_->Signal(); + thread_->Join(); + return playback_time_; + } + + private: + // Call Render() repeatedly, keeping track of the rendering time. + virtual void Run() OVERRIDE { + base::TimeTicks start; + while (!stop_event_->IsSignaled()) { + int frames_received = callback_->Render(audio_bus_.get(), 0); + if (frames_received <= 0) { + // No data received, so let other threads run to provide data. + base::PlatformThread::YieldCurrentThread(); + } else if (start.is_null()) { + // First time we processed some audio, so record the starting time. + start = base::TimeTicks::HighResNow(); + } else { + // Keep track of the last time data was rendered. + playback_time_ = base::TimeTicks::HighResNow() - start; + } + } + } + + AudioRendererSink::RenderCallback* callback_; + scoped_ptr<AudioBus> audio_bus_; + scoped_ptr<base::WaitableEvent> stop_event_; + scoped_ptr<base::DelegateSimpleThread> thread_; + base::TimeDelta playback_time_; +}; + +ClocklessAudioSink::ClocklessAudioSink() + : initialized_(false), + playing_(false) {} + +ClocklessAudioSink::~ClocklessAudioSink() {} + +void ClocklessAudioSink::Initialize(const AudioParameters& params, + RenderCallback* callback) { + DCHECK(!initialized_); + thread_.reset(new ClocklessAudioSinkThread(params, callback)); + initialized_ = true; +} + +void ClocklessAudioSink::Start() { + DCHECK(!playing_); +} + +void ClocklessAudioSink::Stop() { + DCHECK(initialized_); + + if (!playing_) + return; + + playback_time_ = thread_->Stop(); +} + +void ClocklessAudioSink::Play() { + DCHECK(initialized_); + + if (playing_) + return; + + playing_ = true; + thread_->Start(); +} + +void ClocklessAudioSink::Pause() { + Stop(); +} + +bool ClocklessAudioSink::SetVolume(double volume) { + // Audio is always muted. + return volume == 0.0; +} + +} // namespace media diff --git a/chromium/media/audio/clockless_audio_sink.h b/chromium/media/audio/clockless_audio_sink.h new file mode 100644 index 00000000000..9e73b1a8817 --- /dev/null +++ b/chromium/media/audio/clockless_audio_sink.h @@ -0,0 +1,55 @@ +// 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. + +#ifndef MEDIA_AUDIO_CLOCKLESS_AUDIO_SINK_H_ +#define MEDIA_AUDIO_CLOCKLESS_AUDIO_SINK_H_ + +#include "base/memory/scoped_ptr.h" +#include "base/time/time.h" +#include "media/base/audio_renderer_sink.h" + +namespace base { +class MessageLoopProxy; +} + +namespace media { +class AudioBus; +class ClocklessAudioSinkThread; + +// Implementation of an AudioRendererSink that consumes the audio as fast as +// possible. This class does not support multiple Play()/Pause() events. +class MEDIA_EXPORT ClocklessAudioSink + : NON_EXPORTED_BASE(public AudioRendererSink) { + public: + ClocklessAudioSink(); + + // AudioRendererSink implementation. + virtual void Initialize(const AudioParameters& params, + RenderCallback* callback) OVERRIDE; + virtual void Start() OVERRIDE; + virtual void Stop() OVERRIDE; + virtual void Pause() OVERRIDE; + virtual void Play() OVERRIDE; + virtual bool SetVolume(double volume) OVERRIDE; + + // Returns the time taken to consume all the audio. + base::TimeDelta render_time() { return playback_time_; } + + protected: + virtual ~ClocklessAudioSink(); + + private: + scoped_ptr<ClocklessAudioSinkThread> thread_; + bool initialized_; + bool playing_; + + // Time taken in last set of Render() calls. + base::TimeDelta playback_time_; + + DISALLOW_COPY_AND_ASSIGN(ClocklessAudioSink); +}; + +} // namespace media + +#endif // MEDIA_AUDIO_CLOCKLESS_AUDIO_SINK_H_ diff --git a/chromium/media/audio/cras/audio_manager_cras.cc b/chromium/media/audio/cras/audio_manager_cras.cc index 165d642922c..14a0c4e86ac 100644 --- a/chromium/media/audio/cras/audio_manager_cras.cc +++ b/chromium/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()); @@ -76,7 +78,10 @@ AudioOutputStream* AudioManagerCras::MakeLinearOutputStream( } AudioOutputStream* AudioManagerCras::MakeLowLatencyOutputStream( - const AudioParameters& params, const std::string& input_device_id) { + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id) { + DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); // TODO(dgreid): Open the correct input device for unified IO. return MakeOutputStream(params); @@ -95,7 +100,10 @@ AudioInputStream* AudioManagerCras::MakeLowLatencyInputStream( } AudioParameters AudioManagerCras::GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) { + // TODO(tommi): Support |output_device_id|. + DLOG_IF(ERROR, !output_device_id.empty()) << "Not implemented!"; static const int kDefaultOutputBufferSize = 512; ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; diff --git a/chromium/media/audio/cras/audio_manager_cras.h b/chromium/media/audio/cras/audio_manager_cras.h index fdc5b02688a..3b0ef530e07 100644 --- a/chromium/media/audio/cras/audio_manager_cras.h +++ b/chromium/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; @@ -35,6 +33,7 @@ class MEDIA_EXPORT AudioManagerCras : public AudioManagerBase { const AudioParameters& params) OVERRIDE; virtual AudioOutputStream* MakeLowLatencyOutputStream( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) OVERRIDE; virtual AudioInputStream* MakeLinearInputStream( const AudioParameters& params, const std::string& device_id) OVERRIDE; @@ -45,12 +44,10 @@ class MEDIA_EXPORT AudioManagerCras : public AudioManagerBase { virtual ~AudioManagerCras(); virtual AudioParameters GetPreferredOutputStreamParameters( + const std::string& output_device_id, 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/chromium/media/audio/cras/cras_input.cc b/chromium/media/audio/cras/cras_input.cc index a82fe283f7a..fd574dc86e5 100644 --- a/chromium/media/audio/cras/cras_input.cc +++ b/chromium/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_); } @@ -114,7 +113,6 @@ void CrasInputStream::Start(AudioInputCallback* callback) { StartAgc(); callback_ = callback; - LOG(ERROR) << "Input Start"; // Prepare |audio_format| and |stream_params| for the stream we // will create. diff --git a/chromium/media/audio/cras/cras_unified.cc b/chromium/media/audio/cras/cras_unified.cc index c1c3ee9228f..a7741864b31 100644 --- a/chromium/media/audio/cras/cras_unified.cc +++ b/chromium/media/audio/cras/cras_unified.cc @@ -168,7 +168,6 @@ void CrasUnifiedStream::Start(AudioSourceCallback* callback) { if (is_playing_) return; - LOG(ERROR) << "Unified Start"; // Prepare |audio_format| and |stream_params| for the stream we // will create. cras_audio_format* audio_format = cras_audio_format_create( diff --git a/chromium/media/audio/cross_process_notification.cc b/chromium/media/audio/cross_process_notification.cc deleted file mode 100644 index 1806f777da3..00000000000 --- a/chromium/media/audio/cross_process_notification.cc +++ /dev/null @@ -1,30 +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 "media/audio/cross_process_notification.h" - -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" - -CrossProcessNotification::CrossProcessNotification() {} - -CrossProcessNotification::WaitForMultiple::WaitForMultiple( - const Notifications* notifications) { - Reset(notifications); -} - -int CrossProcessNotification::WaitForMultiple::Wait() { - DCHECK(CalledOnValidThread()); - int ret = WaitMultiple(*notifications_, wait_offset_); - wait_offset_ = (ret + 1) % notifications_->size(); - return ret; -} - -void CrossProcessNotification::WaitForMultiple::Reset( - const Notifications* notifications) { - DCHECK(CalledOnValidThread()); - wait_offset_ = 0; - notifications_ = notifications; - DCHECK(!notifications_->empty()); -} diff --git a/chromium/media/audio/cross_process_notification.h b/chromium/media/audio/cross_process_notification.h deleted file mode 100644 index 16f2fc07fcf..00000000000 --- a/chromium/media/audio/cross_process_notification.h +++ /dev/null @@ -1,172 +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. - -#ifndef MEDIA_AUDIO_CROSS_PROCESS_NOTIFICATION_H_ -#define MEDIA_AUDIO_CROSS_PROCESS_NOTIFICATION_H_ - -#include <vector> - -#include "base/basictypes.h" -#include "base/process/process.h" -#include "base/threading/non_thread_safe.h" -#include "media/base/media_export.h" - -#if defined(OS_WIN) -#include "base/win/scoped_handle.h" -#else -#include "base/file_descriptor_posix.h" -#include "base/sync_socket.h" -#endif - -// A mechanism to synchronize access to a shared resource between two parties -// when the usage pattern resembles that of two players playing a game of chess. -// Each end has an instance of CrossProcessNotification and calls Signal() when -// it has finished using the shared resource. -// Before accessing the resource, it must call Wait() in order to know when the -// other end has called Signal(). -// -// Here's some pseudo code for how this class can be used: -// -// This method is used by both processes as it's a general way to use the -// shared resource and then grant the privilege to the other process: -// -// void WriteToSharedMemory(CrossProcessNotification* notification, -// SharedMemory* mem, -// const char my_char) { -// notification->Wait(); // Wait for the other process to yield access. -// reinterpret_cast<char*>(mem->memory())[0] = my_char; -// notification->Signal(); // Grant the other process access. -// } -// -// Process A: -// -// class A { -// public: -// void Initialize(base::ProcessHandle process_b) { -// mem_.CreateNamed("foo", false, 1024); -// -// CrossProcessNotification other; -// CHECK(CrossProcessNotification::InitializePair(¬ification_, &other)); -// CrossProcessNotification::IPCHandle handle_1, handle_2; -// CHECK(other.ShareToProcess(process_b, &handle_1, &handle_2)); -// // This could be implemented by using some IPC mechanism -// // such as MessageLoop. -// SendToProcessB(mem_, handle_1, handle_2); -// // Allow process B the first chance to write to the memory: -// notification_.Signal(); -// // Once B is done, we'll write 'A' to the shared memory. -// WriteToSharedMemory(¬ification_, &mem_, 'A'); -// } -// -// CrossProcessNotification notification_; -// SharedMemory mem_; -// }; -// -// Process B: -// -// class B { -// public: -// // Called when we receive the IPC message from A. -// void Initialize(SharedMemoryHandle mem, -// CrossProcessNotification::IPCHandle handle_1, -// CrossProcessNotification::IPCHandle handle_2) { -// mem_.reset(new SharedMemory(mem, false)); -// notification_.reset(new CrossProcessNotification(handle_1, handle_2)); -// WriteToSharedMemory(¬ification_, &mem_, 'B'); -// } -// -// CrossProcessNotification notification_; -// scoped_ptr<SharedMemory> mem_; -// }; -// -class MEDIA_EXPORT CrossProcessNotification { - public: -#if defined(OS_WIN) - typedef HANDLE IPCHandle; -#else - typedef base::FileDescriptor IPCHandle; -#endif - - typedef std::vector<CrossProcessNotification*> Notifications; - - // Default ctor. Initializes a NULL notification. User must call - // InitializePair() to initialize the instance along with a connected one. - CrossProcessNotification(); - - // Ctor for the user that does not call InitializePair but instead receives - // handles from the one that did. These handles come from a call to - // ShareToProcess. - CrossProcessNotification(IPCHandle handle_1, IPCHandle handle_2); - ~CrossProcessNotification(); - - // Raises a signal that the shared resource now can be accessed by the other - // party. - // NOTE: Calling Signal() more than once without calling Wait() in between - // is not a supported scenario and will result in undefined behavior (and - // different depending on platform). - void Signal(); - - // Waits for the other party to finish using the shared resource. - // NOTE: As with Signal(), you must not call Wait() more than once without - // calling Signal() in between. - void Wait(); - - bool IsValid() const; - - // Copies the internal handles to the output parameters, |handle_1| and - // |handle_2|. The operation can fail, so the caller must be prepared to - // handle that case. - bool ShareToProcess(base::ProcessHandle process, IPCHandle* handle_1, - IPCHandle* handle_2); - - // Initializes a pair of CrossProcessNotification instances. Note that this - // can fail (e.g. due to EMFILE on Linux). - static bool InitializePair(CrossProcessNotification* a, - CrossProcessNotification* b); - - // Use an instance of this class when you have to repeatedly wait for multiple - // notifications on the same thread. The class will store information about - // which notification was last signaled and try to distribute the signals so - // that all notifications get a chance to be processed in times of high load - // and a busy one won't starve the others. - // TODO(tommi): Support a way to abort the wait. - class MEDIA_EXPORT WaitForMultiple : - public NON_EXPORTED_BASE(base::NonThreadSafe) { - public: - // Caller must make sure that the lifetime of the array is greater than - // that of the WaitForMultiple instance. - explicit WaitForMultiple(const Notifications* notifications); - - // Waits for any of the notifications to be signaled. Returns the 0 based - // index of a signaled notification. - int Wait(); - - // Call when the array changes. This should be called on the same thread - // as Wait() is called on and the array must never change while a Wait() - // is in progress. - void Reset(const Notifications* notifications); - - private: - const Notifications* notifications_; - size_t wait_offset_; - }; - - private: - // Only called by the WaitForMultiple class. See documentation - // for WaitForMultiple and comments inside WaitMultiple for details. - static int WaitMultiple(const Notifications& notifications, - size_t wait_offset); - -#if defined(OS_WIN) - base::win::ScopedHandle mine_; - base::win::ScopedHandle other_; -#else - typedef base::CancelableSyncSocket SocketClass; - SocketClass socket_; -#endif - - DISALLOW_COPY_AND_ASSIGN(CrossProcessNotification); -}; - -#endif // MEDIA_AUDIO_CROSS_PROCESS_NOTIFICATION_H_ diff --git a/chromium/media/audio/cross_process_notification_posix.cc b/chromium/media/audio/cross_process_notification_posix.cc deleted file mode 100644 index d5683495ef9..00000000000 --- a/chromium/media/audio/cross_process_notification_posix.cc +++ /dev/null @@ -1,114 +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 "media/audio/cross_process_notification.h" - -#include <errno.h> -#include <sys/poll.h> - -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/file_descriptor_posix.h" - -CrossProcessNotification::~CrossProcessNotification() {} - -CrossProcessNotification::CrossProcessNotification(IPCHandle handle_1, - IPCHandle handle_2) - : socket_(handle_1.fd) { - DCHECK_NE(handle_1.fd, -1); - DCHECK_EQ(handle_2.fd, -1); - DCHECK(IsValid()); -} - -void CrossProcessNotification::Signal() { - DCHECK(IsValid()); - char signal = 1; - size_t bytes = socket_.Send(&signal, sizeof(signal)); - DCHECK_EQ(bytes, 1U) << "errno: " << errno; -} - -void CrossProcessNotification::Wait() { - DCHECK(IsValid()); - char signal = 0; - size_t bytes = socket_.Receive(&signal, sizeof(signal)); - DCHECK_EQ(bytes, 1U) << "errno: " << errno; - DCHECK_EQ(signal, 1); -} - -bool CrossProcessNotification::IsValid() const { - return socket_.handle() != SocketClass::kInvalidHandle; -} - -bool CrossProcessNotification::ShareToProcess(base::ProcessHandle process, - IPCHandle* handle_1, - IPCHandle* handle_2) { - DCHECK(IsValid()); - handle_1->fd = socket_.handle(); - handle_1->auto_close = false; - handle_2->fd = -1; - return true; -} - -// static -bool CrossProcessNotification::InitializePair(CrossProcessNotification* a, - CrossProcessNotification* b) { - DCHECK(!a->IsValid()); - DCHECK(!b->IsValid()); - - bool ok = SocketClass::CreatePair(&a->socket_, &b->socket_); - - DLOG_IF(WARNING, !ok) << "failed to create socket: " << errno; - DCHECK(!ok || a->IsValid()); - DCHECK(!ok || b->IsValid()); - return ok; -} - -// static -int CrossProcessNotification::WaitMultiple(const Notifications& notifications, - size_t wait_offset) { - DCHECK_LT(wait_offset, notifications.size()); - - for (size_t i = 0; i < notifications.size(); ++i) { - DCHECK(notifications[i]->IsValid()); - } - - // Below, we always check the |revents| of the first socket in the array - // and return the index of that socket if set. This can cause sockets - // that come later in the array to starve when the first sockets are - // very busy. So to avoid the starving problem, we use the |wait_offset| - // variable to split up the array so that the last socket to be signaled - // becomes the last socket in the array and all the other sockets will have - // priority the next time WaitMultiple is called. - scoped_ptr<struct pollfd[]> sockets(new struct pollfd[notifications.size()]); - memset(&sockets[0], 0, notifications.size() * sizeof(sockets[0])); - size_t index = 0; - for (size_t i = wait_offset; i < notifications.size(); ++i) { - struct pollfd& fd = sockets[index++]; - fd.events = POLLIN; - fd.fd = notifications[i]->socket_.handle(); - } - - for (size_t i = 0; i < wait_offset; ++i) { - struct pollfd& fd = sockets[index++]; - fd.events = POLLIN; - fd.fd = notifications[i]->socket_.handle(); - } - DCHECK_EQ(index, notifications.size()); - - int err = poll(&sockets[0], notifications.size(), -1); - if (err != -1) { - for (size_t i = 0; i < notifications.size(); ++i) { - if (sockets[i].revents) { - size_t ret = (i + wait_offset) % notifications.size(); - DCHECK_EQ(sockets[i].fd, notifications[ret]->socket_.handle()); - notifications[ret]->Wait(); - return ret; - } - } - } - // Either poll() failed or we failed to find a single socket that was - // signaled. Either way continuing will result in undefined behavior. - LOG(FATAL) << "poll() failed: " << errno; - return -1; -} diff --git a/chromium/media/audio/cross_process_notification_unittest.cc b/chromium/media/audio/cross_process_notification_unittest.cc deleted file mode 100644 index a27219496cb..00000000000 --- a/chromium/media/audio/cross_process_notification_unittest.cc +++ /dev/null @@ -1,462 +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/compiler_specific.h" -#include "base/logging.h" -#include "base/memory/shared_memory.h" -#include "base/process/kill.h" -#include "base/stl_util.h" -#include "base/test/multiprocess_test.h" -#include "base/threading/platform_thread.h" -#include "media/audio/cross_process_notification.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/multiprocess_func_list.h" - -#include <utility> // NOLINT - -namespace { - -// Initializes (ctor) and deletes (dtor) two vectors of pairs of -// CrossProcessNotification instances. -class NotificationsOwner { - public: - // Attempts to create up to |number_of_pairs| number of pairs. Call size() - // after construction to find out how many pairs were actually created. - explicit NotificationsOwner(size_t number_of_pairs) { - CreateMultiplePairs(number_of_pairs); - } - ~NotificationsOwner() { - STLDeleteElements(&a_); - STLDeleteElements(&b_); - } - - size_t size() const { - DCHECK_EQ(a_.size(), b_.size()); - return a_.size(); - } - - const CrossProcessNotification::Notifications& a() { return a_; } - const CrossProcessNotification::Notifications& b() { return b_; } - - private: - void CreateMultiplePairs(size_t count) { - a_.resize(count); - b_.resize(count); - size_t i = 0; - for (; i < count; ++i) { - a_[i] = new CrossProcessNotification(); - b_[i] = new CrossProcessNotification(); - if (!CrossProcessNotification::InitializePair(a_[i], b_[i])) { - LOG(WARNING) << "InitializePair failed at " << i; - delete a_[i]; - delete b_[i]; - break; - } - } - a_.resize(i); - b_.resize(i); - } - - CrossProcessNotification::Notifications a_; - CrossProcessNotification::Notifications b_; -}; - -// A simple thread that we'll run two instances of. Both threads get a pointer -// to the same |shared_data| and use a CrossProcessNotification to control when -// each thread can read/write. -class SingleNotifierWorker : public base::PlatformThread::Delegate { - public: - SingleNotifierWorker(size_t* shared_data, size_t repeats, - CrossProcessNotification* notifier) - : shared_data_(shared_data), repeats_(repeats), - notifier_(notifier) { - } - virtual ~SingleNotifierWorker() {} - - // base::PlatformThread::Delegate: - virtual void ThreadMain() OVERRIDE { - for (size_t i = 0; i < repeats_; ++i) { - notifier_->Wait(); - ++(*shared_data_); - notifier_->Signal(); - } - } - - private: - size_t* shared_data_; - size_t repeats_; - CrossProcessNotification* notifier_; - DISALLOW_COPY_AND_ASSIGN(SingleNotifierWorker); -}; - -// Similar to SingleNotifierWorker, except each instance of this class will -// have >1 instances of CrossProcessNotification to Wait/Signal and an equal -// amount of |shared_data| that the notifiers control access to. -class MultiNotifierWorker : public base::PlatformThread::Delegate { - public: - MultiNotifierWorker(size_t* shared_data, size_t repeats, - const CrossProcessNotification::Notifications* notifiers) - : shared_data_(shared_data), repeats_(repeats), - notifiers_(notifiers) { - } - virtual ~MultiNotifierWorker() {} - - // base::PlatformThread::Delegate: - virtual void ThreadMain() OVERRIDE { - CrossProcessNotification::WaitForMultiple waiter(notifiers_); - for (size_t i = 0; i < repeats_; ++i) { - int signaled = waiter.Wait(); - ++shared_data_[signaled]; - (*notifiers_)[signaled]->Signal(); - } - } - - private: - size_t* shared_data_; - size_t repeats_; - const CrossProcessNotification::Notifications* notifiers_; - DISALLOW_COPY_AND_ASSIGN(MultiNotifierWorker); -}; - -// A fixed array of bool flags. Each flag uses 1 bit. Use sizeof(FlagArray) -// to determine how much memory you need. The number of flags will therefore -// be sizeof(FlagArray) * 8. -// We use 'struct' to signify that this structures represents compiler -// independent structured data. I.e. you must be able to map this class -// to a piece of shared memory of size sizeof(FlagArray) and be able to -// use the class. No vtables etc. -// TODO(tommi): Move this to its own header when we start using it for signaling -// audio devices. As is, it's just here for perf comparison against the -// "multiple notifiers" approach. -struct FlagArray { - public: - FlagArray() : flags_() {} - - bool is_set(size_t index) const { - return (flags_[index >> 5] & (1 << (index & 31))); - } - - void set(size_t index) { - flags_[index >> 5] |= (1U << (static_cast<uint32>(index) & 31)); - } - - void clear(size_t index) { - flags_[index >> 5] &= ~(1U << (static_cast<uint32>(index) & 31)); - } - - // Returns the number of flags that can be set/checked. - size_t size() const { return sizeof(flags_) * 8; } - - private: - // 256 * 32 = 8192 flags in 1KB. - uint32 flags_[256]; - DISALLOW_COPY_AND_ASSIGN(FlagArray); -}; - -class MultiNotifierWorkerFlagArray : public base::PlatformThread::Delegate { - public: - MultiNotifierWorkerFlagArray(size_t count, FlagArray* signals, - size_t* shared_data, size_t repeats, - CrossProcessNotification* notifier) - : count_(count), signals_(signals), shared_data_(shared_data), - repeats_(repeats), notifier_(notifier) { - } - virtual ~MultiNotifierWorkerFlagArray() {} - - // base::PlatformThread::Delegate: - virtual void ThreadMain() OVERRIDE { - for (size_t i = 0; i < repeats_; ++i) { - notifier_->Wait(); - for (size_t s = 0; s < count_; ++s) { - if (signals_->is_set(s)) { - ++shared_data_[s]; - // We don't clear the flag here but simply leave it signaled because - // we want the other thread to also increment this variable. - } - } - notifier_->Signal(); - } - } - - private: - size_t count_; - FlagArray* signals_; - size_t* shared_data_; - size_t repeats_; - CrossProcessNotification* notifier_; - DISALLOW_COPY_AND_ASSIGN(MultiNotifierWorkerFlagArray); -}; - -} // end namespace - -TEST(CrossProcessNotification, FlagArray) { - FlagArray flags; - EXPECT_GT(flags.size(), 1000U); - for (size_t i = 0; i < flags.size(); ++i) { - EXPECT_FALSE(flags.is_set(i)); - flags.set(i); - EXPECT_TRUE(flags.is_set(i)); - flags.clear(i); - EXPECT_FALSE(flags.is_set(i)); - } -} - -// Initializes two notifiers, signals the each one and make sure the others -// wait is satisfied. -TEST(CrossProcessNotification, Basic) { - CrossProcessNotification a, b; - ASSERT_TRUE(CrossProcessNotification::InitializePair(&a, &b)); - EXPECT_TRUE(a.IsValid()); - EXPECT_TRUE(b.IsValid()); - - a.Signal(); - b.Wait(); - - b.Signal(); - a.Wait(); -} - -// Spins two worker threads, each with their own CrossProcessNotification -// that they use to read and write from a shared memory buffer. -// Disabled as it trips of the TSAN bot (false positive since TSAN doesn't -// recognize sockets as being a synchronization primitive). -TEST(CrossProcessNotification, DISABLED_TwoThreads) { - CrossProcessNotification a, b; - ASSERT_TRUE(CrossProcessNotification::InitializePair(&a, &b)); - - size_t data = 0; - const size_t kRepeats = 10000; - SingleNotifierWorker worker1(&data, kRepeats, &a); - SingleNotifierWorker worker2(&data, kRepeats, &b); - base::PlatformThreadHandle thread1, thread2; - base::PlatformThread::Create(0, &worker1, &thread1); - base::PlatformThread::Create(0, &worker2, &thread2); - - // Start the first thread. They should ping pong a few times and take turns - // incrementing the shared variable and never step on each other's toes. - a.Signal(); - - base::PlatformThread::Join(thread1); - base::PlatformThread::Join(thread2); - - EXPECT_EQ(kRepeats * 2, data); -} - -// Uses a pair of threads to access up to 1000 pieces of synchronized shared -// data. On regular dev machines, the number of notifiers should be 1000, but on -// mac and linux bots, the number will be smaller due to the RLIMIT_NOFILE -// limit. Specifically, linux will have this limit at 1024 which means for this -// test that the max number of notifiers will be in the range 500-512. On Mac -// the limit is 256, so |count| will be ~120. Oh, and raising the limit via -// setrlimit() won't work. -// DISABLED since the distribution won't be accurate when run on valgrind. -TEST(CrossProcessNotification, DISABLED_ThousandNotifiersTwoThreads) { - const size_t kCount = 1000; - NotificationsOwner pairs(kCount); - size_t data[kCount] = {0}; - // We use a multiple of the count so that the division in the check below - // will be nice and round. - size_t repeats = pairs.size() * 1; - - MultiNotifierWorker worker_1(&data[0], repeats, &pairs.a()); - MultiNotifierWorker worker_2(&data[0], repeats, &pairs.b()); - base::PlatformThreadHandle thread_1, thread_2; - base::PlatformThread::Create(0, &worker_1, &thread_1); - base::PlatformThread::Create(0, &worker_2, &thread_2); - - for (size_t i = 0; i < pairs.size(); ++i) - pairs.a()[i]->Signal(); - - base::PlatformThread::Join(thread_1); - base::PlatformThread::Join(thread_2); - - size_t expected_total = pairs.size() * 2; - size_t total = 0; - for (size_t i = 0; i < pairs.size(); ++i) { - // The CrossProcessNotification::WaitForMultiple class should have ensured - // that all notifiers had the same quality of service. - EXPECT_EQ(expected_total / pairs.size(), data[i]); - total += data[i]; - } - EXPECT_EQ(expected_total, total); -} - -// Functionally equivalent (as far as the shared data goes) to the -// ThousandNotifiersTwoThreads test but uses a single pair of notifiers + -// FlagArray for the 1000 signals. This approach is significantly faster. -// Disabled as it trips of the TSAN bot - "Possible data race during write of -// size 4" (the flag array). -TEST(CrossProcessNotification, DISABLED_TwoNotifiersTwoThreads1000Signals) { - CrossProcessNotification a, b; - ASSERT_TRUE(CrossProcessNotification::InitializePair(&a, &b)); - - const size_t kCount = 1000; - FlagArray signals; - ASSERT_GE(signals.size(), kCount); - size_t data[kCount] = {0}; - - // Since this algorithm checks all events each time the notifier is - // signaled, |repeat| doesn't mean the same thing here as it does in - // ThousandNotifiersTwoThreads. 1 repeat here is the same as kCount - // repeats in ThousandNotifiersTwoThreads. - size_t repeats = 1; - MultiNotifierWorkerFlagArray worker1(kCount, &signals, &data[0], repeats, &a); - MultiNotifierWorkerFlagArray worker2(kCount, &signals, &data[0], repeats, &b); - base::PlatformThreadHandle thread1, thread2; - base::PlatformThread::Create(0, &worker1, &thread1); - base::PlatformThread::Create(0, &worker2, &thread2); - - for (size_t i = 0; i < kCount; ++i) - signals.set(i); - a.Signal(); - - base::PlatformThread::Join(thread1); - base::PlatformThread::Join(thread2); - - size_t expected_total = kCount * 2; - size_t total = 0; - for (size_t i = 0; i < kCount; ++i) { - // Since for each signal, we process all signaled events, the shared data - // variables should all be equal. - EXPECT_EQ(expected_total / kCount, data[i]); - total += data[i]; - } - EXPECT_EQ(expected_total, total); -} - -// Test the maximum number of notifiers without spinning further wait -// threads on Windows. This test assumes we can always create 64 pairs and -// bails if we can't. -TEST(CrossProcessNotification, MultipleWaits64) { - const size_t kCount = 64; - NotificationsOwner pairs(kCount); - ASSERT_TRUE(pairs.size() == kCount); - - CrossProcessNotification::WaitForMultiple waiter(&pairs.b()); - for (size_t i = 0; i < kCount; ++i) { - pairs.a()[i]->Signal(); - int index = waiter.Wait(); - EXPECT_EQ(i, static_cast<size_t>(index)); - } -} - -// Tests waiting for more notifiers than the OS supports on one thread. -// The test will create at most 1000 pairs, but on mac/linux bots the actual -// number will be lower. See comment about the RLIMIT_NOFILE limit above for -// more details. -// DISABLED since the distribution won't be accurate when run on valgrind. -TEST(CrossProcessNotification, DISABLED_MultipleWaits1000) { - // A 1000 notifiers requires 16 threads on Windows, including the current - // one, to perform the wait operation. - const size_t kCount = 1000; - NotificationsOwner pairs(kCount); - - for (size_t i = 0; i < pairs.size(); ++i) { - pairs.a()[i]->Signal(); - // To disable the load distribution algorithm and force the extra worker - // thread(s) to catch the signaled event, we define the |waiter| inside - // the loop. - CrossProcessNotification::WaitForMultiple waiter(&pairs.b()); - int index = waiter.Wait(); - EXPECT_EQ(i, static_cast<size_t>(index)); - } -} - -class CrossProcessNotificationMultiProcessTest : public base::MultiProcessTest { -}; - -namespace { - -// A very crude IPC mechanism that we use to set up the spawned child process -// and the parent process. -struct CrudeIpc { - uint8 ready; - CrossProcessNotification::IPCHandle handle_1; - CrossProcessNotification::IPCHandle handle_2; -}; - -#if defined(OS_POSIX) -const int kPosixChildSharedMem = 30; -#else -const char kSharedMemName[] = "CrossProcessNotificationMultiProcessTest"; -#endif - -const size_t kSharedMemSize = 1024; - -} // namespace - -// The main routine of the child process. Waits for the parent process -// to copy handles over to the child and then uses a CrossProcessNotification to -// wait and signal to the parent process. -MULTIPROCESS_TEST_MAIN(CrossProcessNotificationChildMain) { -#if defined(OS_POSIX) - base::SharedMemory mem( - base::SharedMemoryHandle(kPosixChildSharedMem, true /* auto close */), - false); -#else - base::SharedMemory mem; - CHECK(mem.CreateNamed(kSharedMemName, true, kSharedMemSize)); -#endif - - CHECK(mem.Map(kSharedMemSize)); - CrudeIpc* ipc = reinterpret_cast<CrudeIpc*>(mem.memory()); - - while (!ipc->ready) - base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10)); - - CrossProcessNotification notifier(ipc->handle_1, ipc->handle_2); - notifier.Wait(); - notifier.Signal(); - - return 0; -} - -// Spawns a new process and hands a CrossProcessNotification instance to the -// new process. Once that's done, it waits for the child process to signal -// it's end and quits. -TEST_F(CrossProcessNotificationMultiProcessTest, Basic) { - CrossProcessNotification a, b; - ASSERT_TRUE(CrossProcessNotification::InitializePair(&a, &b)); - EXPECT_TRUE(a.IsValid()); - EXPECT_TRUE(b.IsValid()); - - base::SharedMemory mem; - -#if defined(OS_POSIX) - ASSERT_TRUE(mem.CreateAndMapAnonymous(kSharedMemSize)); -#else - mem.Delete(kSharedMemName); // In case a previous run was unsuccessful. - ASSERT_TRUE(mem.CreateNamed(kSharedMemName, false, kSharedMemSize)); - ASSERT_TRUE(mem.Map(kSharedMemSize)); -#endif - - CrudeIpc* ipc = reinterpret_cast<CrudeIpc*>(mem.memory()); - ipc->ready = false; - -#if defined(OS_POSIX) - const int kPosixChildSocket = 20; - EXPECT_TRUE(b.ShareToProcess( - base::kNullProcessHandle, &ipc->handle_1, &ipc->handle_2)); - base::FileHandleMappingVector fd_mapping_vec; - fd_mapping_vec.push_back(std::make_pair(ipc->handle_1.fd, kPosixChildSocket)); - fd_mapping_vec.push_back( - std::make_pair(mem.handle().fd, kPosixChildSharedMem)); - ipc->handle_1.fd = kPosixChildSocket; - base::ProcessHandle process = SpawnChild("CrossProcessNotificationChildMain", - fd_mapping_vec, false); -#else - base::ProcessHandle process = SpawnChild("CrossProcessNotificationChildMain", - false); - EXPECT_TRUE(b.ShareToProcess(process, &ipc->handle_1, &ipc->handle_2)); -#endif - - ipc->ready = true; - - a.Signal(); - a.Wait(); - - int exit_code = -1; - base::WaitForExitCode(process, &exit_code); - EXPECT_EQ(0, exit_code); -} diff --git a/chromium/media/audio/cross_process_notification_win.cc b/chromium/media/audio/cross_process_notification_win.cc deleted file mode 100644 index 53bf0f4525e..00000000000 --- a/chromium/media/audio/cross_process_notification_win.cc +++ /dev/null @@ -1,270 +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 "media/audio/cross_process_notification.h" - -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/threading/platform_thread.h" -#include "base/win/scoped_handle.h" - -CrossProcessNotification::~CrossProcessNotification() {} - -CrossProcessNotification::CrossProcessNotification(IPCHandle handle_1, - IPCHandle handle_2) - : mine_(handle_1), other_(handle_2) { - DCHECK(IsValid()); -} - -void CrossProcessNotification::Signal() { - DCHECK(IsValid()); - DCHECK_EQ(::WaitForSingleObject(mine_, 0), static_cast<DWORD>(WAIT_TIMEOUT)) - << "Are you calling Signal() without calling Wait() first?"; - BOOL ok = ::SetEvent(mine_); - CHECK(ok); -} - -void CrossProcessNotification::Wait() { - DCHECK(IsValid()); - DWORD wait = ::WaitForSingleObject(other_, INFINITE); - DCHECK_EQ(wait, WAIT_OBJECT_0); - BOOL ok = ::ResetEvent(other_); - CHECK(ok); -} - -bool CrossProcessNotification::IsValid() const { - return mine_.IsValid() && other_.IsValid(); -} - -bool CrossProcessNotification::ShareToProcess(base::ProcessHandle process, - IPCHandle* handle_1, - IPCHandle* handle_2) { - DCHECK(IsValid()); - HANDLE our_process = ::GetCurrentProcess(); - if (!::DuplicateHandle(our_process, mine_, process, handle_1, 0, FALSE, - DUPLICATE_SAME_ACCESS)) { - return false; - } - - if (!::DuplicateHandle(our_process, other_, process, handle_2, 0, FALSE, - DUPLICATE_SAME_ACCESS)) { - // In case we're sharing to ourselves, we can close the handle, but - // if the target process is a different process, we do nothing. - if (process == our_process) - ::CloseHandle(*handle_1); - *handle_1 = NULL; - return false; - } - - return true; -} - -// static -bool CrossProcessNotification::InitializePair(CrossProcessNotification* a, - CrossProcessNotification* b) { - DCHECK(!a->IsValid()); - DCHECK(!b->IsValid()); - - bool success = false; - - // Create two manually resettable events and give each party a handle - // to both events. - HANDLE event_a = ::CreateEvent(NULL, TRUE, FALSE, NULL); - HANDLE event_b = ::CreateEvent(NULL, TRUE, FALSE, NULL); - if (event_a && event_b) { - a->mine_.Set(event_a); - a->other_.Set(event_b); - success = a->ShareToProcess(GetCurrentProcess(), &event_a, &event_b); - if (success) { - b->mine_.Set(event_b); - b->other_.Set(event_a); - } else { - a->mine_.Close(); - a->other_.Close(); - } - } else { - if (event_a) - ::CloseHandle(event_a); - if (event_b) - ::CloseHandle(event_b); - } - - DCHECK(!success || a->IsValid()); - DCHECK(!success || b->IsValid()); - - return success; -} - -namespace { -class ExtraWaitThread : public base::PlatformThread::Delegate { - public: - ExtraWaitThread(HANDLE stop, HANDLE* events, size_t count, - int* signaled_event) - : stop_(stop), events_(events), count_(count), - signaled_event_(signaled_event) { - *signaled_event_ = -1; - } - virtual ~ExtraWaitThread() {} - - virtual void ThreadMain() OVERRIDE { - // Store the |stop_| event as the first event. - HANDLE events[MAXIMUM_WAIT_OBJECTS] = { stop_ }; - HANDLE next_thread = NULL; - DWORD event_count = MAXIMUM_WAIT_OBJECTS; - int thread_signaled_event = -1; - scoped_ptr<ExtraWaitThread> extra_wait_thread; - if (count_ > (MAXIMUM_WAIT_OBJECTS - 1)) { - std::copy(&events_[0], &events_[MAXIMUM_WAIT_OBJECTS - 2], &events[1]); - - extra_wait_thread.reset(new ExtraWaitThread(stop_, - &events_[MAXIMUM_WAIT_OBJECTS - 2], - count_ - (MAXIMUM_WAIT_OBJECTS - 2), - &thread_signaled_event)); - base::PlatformThreadHandle handle; - base::PlatformThread::Create(0, extra_wait_thread.get(), &handle); - next_thread = handle.platform_handle(); - - event_count = MAXIMUM_WAIT_OBJECTS; - events[MAXIMUM_WAIT_OBJECTS - 1] = next_thread; - } else { - std::copy(&events_[0], &events_[count_], &events[1]); - event_count = count_ + 1; - } - - DWORD wait = ::WaitForMultipleObjects(event_count, &events[0], FALSE, - INFINITE); - if (wait >= WAIT_OBJECT_0 && wait < (WAIT_OBJECT_0 + event_count)) { - wait -= WAIT_OBJECT_0; - if (wait == 0) { - // The stop event was signaled. Check if it was signaled by a - // sub thread. In case our sub thread had to spin another thread (and - // so on), we must wait for ours to exit before we can check the - // propagated event offset. - if (next_thread) { - base::PlatformThread::Join(base::PlatformThreadHandle(next_thread)); - next_thread = NULL; - } - if (thread_signaled_event != -1) - *signaled_event_ = thread_signaled_event + (MAXIMUM_WAIT_OBJECTS - 2); - } else if (events[wait] == next_thread) { - NOTREACHED(); - } else { - *signaled_event_ = static_cast<int>(wait); - SetEvent(stop_); - } - } else { - NOTREACHED(); - } - - if (next_thread) - base::PlatformThread::Join(base::PlatformThreadHandle(next_thread)); - } - - private: - HANDLE stop_; - HANDLE* events_; - size_t count_; - int* signaled_event_; - DISALLOW_COPY_AND_ASSIGN(ExtraWaitThread); -}; -} // end namespace - -// static -int CrossProcessNotification::WaitMultiple(const Notifications& notifications, - size_t wait_offset) { - DCHECK_LT(wait_offset, notifications.size()); - - for (size_t i = 0; i < notifications.size(); ++i) { - DCHECK(notifications[i]->IsValid()); - } - - // TODO(tommi): Should we wait in an alertable state so that we can be - // canceled via an APC? - scoped_ptr<HANDLE[]> handles(new HANDLE[notifications.size()]); - - // Because of the way WaitForMultipleObjects works, we do a little trick here. - // When multiple events are signaled, WaitForMultipleObjects will return the - // index of the first signaled item (lowest). This means that if we always - // pass the array the same way to WaitForMultipleObjects, the objects that - // come first, have higher priority. In times of heavy load, this will cause - // elements at the back to become DOS-ed. - // So, we store the location of the item that was last signaled. Then we split - // up the array and move everything higher than the last signaled index to the - // front and the rest to the back (meaning that the last signaled item will - // become the last element in the list). - // Assuming equally busy events, this approach distributes the priority - // evenly. - - size_t index = 0; - for (size_t i = wait_offset; i < notifications.size(); ++i) - handles[index++] = notifications[i]->other_; - - for (size_t i = 0; i < wait_offset; ++i) - handles[index++] = notifications[i]->other_; - DCHECK_EQ(index, notifications.size()); - - DWORD wait = WAIT_FAILED; - bool wait_failed = false; - if (notifications.size() <= MAXIMUM_WAIT_OBJECTS) { - wait = ::WaitForMultipleObjects(notifications.size(), &handles[0], FALSE, - INFINITE); - wait_failed = wait < WAIT_OBJECT_0 || - wait >= (WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS); - } else { - // Used to stop the other wait threads when an event has been signaled. - base::win::ScopedHandle stop(::CreateEvent(NULL, TRUE, FALSE, NULL)); - - // Create the first thread and pass a pointer to all handles >63 - // to the thread + 'stop'. Then implement the thread so that it checks - // if the number of handles is > 63. If so, spawns a new thread and - // passes >62 handles to that thread and waits for the 62 handles + stop + - // next thread. etc etc. - - // Create a list of threads so that each thread waits on at most 62 events - // including one event for when a child thread signals completion and one - // event for when all of the threads must be stopped (due to some event - // being signaled). - - int thread_signaled_event = -1; - ExtraWaitThread wait_thread(stop, &handles[MAXIMUM_WAIT_OBJECTS - 1], - notifications.size() - (MAXIMUM_WAIT_OBJECTS - 1), - &thread_signaled_event); - base::PlatformThreadHandle thread; - base::PlatformThread::Create(0, &wait_thread, &thread); - HANDLE events[MAXIMUM_WAIT_OBJECTS]; - std::copy(&handles[0], &handles[MAXIMUM_WAIT_OBJECTS - 1], &events[0]); - events[MAXIMUM_WAIT_OBJECTS - 1] = thread.platform_handle(); - wait = ::WaitForMultipleObjects(MAXIMUM_WAIT_OBJECTS, &events[0], FALSE, - INFINITE); - wait_failed = wait < WAIT_OBJECT_0 || - wait >= (WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS); - if (wait == WAIT_OBJECT_0 + (MAXIMUM_WAIT_OBJECTS - 1)) { - if (thread_signaled_event < 0) { - wait_failed = true; - NOTREACHED(); - } else { - wait = WAIT_OBJECT_0 + (MAXIMUM_WAIT_OBJECTS - 2) + - thread_signaled_event; - } - } else { - ::SetEvent(stop); - } - base::PlatformThread::Join(thread); - } - - int ret = -1; - if (!wait_failed) { - // Subtract to be politically correct (WAIT_OBJECT_0 is actually 0). - wait -= WAIT_OBJECT_0; - BOOL ok = ::ResetEvent(handles[wait]); - CHECK(ok); - ret = (wait + wait_offset) % notifications.size(); - DCHECK_EQ(handles[wait], notifications[ret]->other_.Get()); - } else { - NOTREACHED(); - } - - CHECK_NE(ret, -1); - return ret; -} diff --git a/chromium/media/audio/ios/audio_manager_ios.h b/chromium/media/audio/ios/audio_manager_ios.h deleted file mode 100644 index 19751502fd2..00000000000 --- a/chromium/media/audio/ios/audio_manager_ios.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 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. - -#ifndef MEDIA_AUDIO_IOS_AUDIO_MANAGER_IOS_H_ -#define MEDIA_AUDIO_IOS_AUDIO_MANAGER_IOS_H_ - -#include "base/basictypes.h" -#include "media/audio/audio_manager_base.h" - -namespace media { - -class PCMQueueInAudioInputStream; - -// iOS implementation of the AudioManager singleton. Supports only audio input. -class MEDIA_EXPORT AudioManagerIOS : public AudioManagerBase { - public: - AudioManagerIOS(); - - // Implementation of AudioManager. - virtual bool HasAudioOutputDevices() OVERRIDE; - virtual bool HasAudioInputDevices() OVERRIDE; - virtual AudioOutputStream* MakeAudioOutputStream( - const AudioParameters& params, - const std::string& input_device_id) OVERRIDE; - virtual AudioInputStream* MakeAudioInputStream( - const AudioParameters& params, const std::string& device_id) OVERRIDE; - virtual AudioParameters GetInputStreamParameters( - const std::string& device_id) OVERRIDE; - - // Implementation of AudioManagerBase. - virtual AudioOutputStream* MakeLinearOutputStream( - const AudioParameters& params) OVERRIDE; - virtual AudioOutputStream* MakeLowLatencyOutputStream( - const AudioParameters& params, - const std::string& input_device_id) OVERRIDE; - virtual AudioInputStream* MakeLinearInputStream( - const AudioParameters& params, const std::string& device_id) OVERRIDE; - virtual AudioInputStream* MakeLowLatencyInputStream( - const AudioParameters& params, const std::string& device_id) OVERRIDE; - virtual void ReleaseOutputStream(AudioOutputStream* stream) OVERRIDE; - virtual void ReleaseInputStream(AudioInputStream* stream) OVERRIDE; - - protected: - virtual ~AudioManagerIOS(); - - virtual AudioParameters GetPreferredOutputStreamParameters( - const AudioParameters& input_params) OVERRIDE; - - private: - DISALLOW_COPY_AND_ASSIGN(AudioManagerIOS); -}; - -} // namespace media - -#endif // MEDIA_AUDIO_IOS_AUDIO_MANAGER_IOS_H_ diff --git a/chromium/media/audio/ios/audio_manager_ios.mm b/chromium/media/audio/ios/audio_manager_ios.mm deleted file mode 100644 index 49479302efc..00000000000 --- a/chromium/media/audio/ios/audio_manager_ios.mm +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 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 "media/audio/ios/audio_manager_ios.h" - -#import <AudioToolbox/AudioToolbox.h> -#import <AVFoundation/AVFoundation.h> - -#include "base/sys_info.h" -#include "media/audio/audio_parameters.h" -#include "media/audio/fake_audio_input_stream.h" -#include "media/audio/ios/audio_session_util_ios.h" -#include "media/audio/mac/audio_input_mac.h" -#include "media/base/channel_layout.h" -#include "media/base/limits.h" - -namespace media { - -enum { kMaxInputChannels = 2 }; - -AudioManagerIOS::AudioManagerIOS() { -} - -AudioManagerIOS::~AudioManagerIOS() { - Shutdown(); -} - -bool AudioManagerIOS::HasAudioOutputDevices() { - return false; -} - -bool AudioManagerIOS::HasAudioInputDevices() { - if (!InitAudioSessionIOS()) - return false; - // Note that the |kAudioSessionProperty_AudioInputAvailable| property is a - // 32-bit integer, not a boolean. - UInt32 property_size; - OSStatus error = - AudioSessionGetPropertySize(kAudioSessionProperty_AudioInputAvailable, - &property_size); - if (error != kAudioSessionNoError) - return false; - UInt32 audio_input_is_available = false; - DCHECK(property_size == sizeof(audio_input_is_available)); - error = AudioSessionGetProperty(kAudioSessionProperty_AudioInputAvailable, - &property_size, - &audio_input_is_available); - return error == kAudioSessionNoError ? audio_input_is_available : false; -} - -AudioParameters AudioManagerIOS::GetInputStreamParameters( - const std::string& device_id) { - // TODO(xians): figure out the right input sample rate and buffer size to - // achieve the best audio performance for iOS devices. - // TODO(xians): query the native channel layout for the specific device. - static const int kDefaultSampleRate = 48000; - static const int kDefaultBufferSize = 2048; - return AudioParameters( - AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, - kDefaultSampleRate, 16, kDefaultBufferSize); -} - -AudioOutputStream* AudioManagerIOS::MakeAudioOutputStream( - const AudioParameters& params, const std::string& input_device_id) { - NOTIMPLEMENTED(); // Only input is supported on iOS. - return NULL; -} - -AudioInputStream* AudioManagerIOS::MakeAudioInputStream( - const AudioParameters& params, const std::string& device_id) { - // Current line of iOS devices has only one audio input. - // Ignore the device_id (unittest uses a test value in it). - if (!params.IsValid() || (params.channels() > kMaxInputChannels)) - return NULL; - - if (params.format() == AudioParameters::AUDIO_FAKE) - return FakeAudioInputStream::MakeFakeStream(this, params); - else if (params.format() == AudioParameters::AUDIO_PCM_LINEAR) - return new PCMQueueInAudioInputStream(this, params); - return NULL; -} - -AudioOutputStream* AudioManagerIOS::MakeLinearOutputStream( - const AudioParameters& params) { - NOTIMPLEMENTED(); // Only input is supported on iOS. - return NULL; -} - -AudioOutputStream* AudioManagerIOS::MakeLowLatencyOutputStream( - const AudioParameters& params, const std::string& input_device_id) { - NOTIMPLEMENTED(); // Only input is supported on iOS. - return NULL; -} - -AudioInputStream* AudioManagerIOS::MakeLinearInputStream( - const AudioParameters& params, const std::string& device_id) { - return MakeAudioInputStream(params, device_id); -} - -AudioInputStream* AudioManagerIOS::MakeLowLatencyInputStream( - const AudioParameters& params, const std::string& device_id) { - NOTIMPLEMENTED(); // Only linear audio input is supported on iOS. - return MakeAudioInputStream(params, device_id); -} - - -AudioParameters AudioManagerIOS::GetPreferredOutputStreamParameters( - const AudioParameters& input_params) { - // TODO(xians): handle the case when input_params is valid. - // TODO(xians): figure out the right output sample rate and sample rate to - // achieve the best audio performance for iOS devices. - // TODO(xians): add support to --audio-buffer-size flag. - static const int kDefaultSampleRate = 48000; - static const int kDefaultBufferSize = 2048; - if (input_params.IsValid()) { - NOTREACHED(); - } - - return AudioParameters( - AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, - kDefaultSampleRate, 16, kDefaultBufferSize); -} - -// Called by the stream when it has been released by calling Close(). -void AudioManagerIOS::ReleaseOutputStream(AudioOutputStream* stream) { - NOTIMPLEMENTED(); // Only input is supported on iOS. -} - -// Called by the stream when it has been released by calling Close(). -void AudioManagerIOS::ReleaseInputStream(AudioInputStream* stream) { - delete stream; -} - -// static -AudioManager* CreateAudioManager() { - return new AudioManagerIOS(); -} - -} // namespace media diff --git a/chromium/media/audio/ios/audio_manager_ios_unittest.cc b/chromium/media/audio/ios/audio_manager_ios_unittest.cc deleted file mode 100644 index 30ebc04f204..00000000000 --- a/chromium/media/audio/ios/audio_manager_ios_unittest.cc +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 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/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "media/audio/audio_io.h" -#include "media/audio/audio_manager.h" -#include "testing/gtest/include/gtest/gtest.h" - -using namespace media; - -// Test that input is supported and output is not. -TEST(IOSAudioTest, AudioSupport) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); - ASSERT_TRUE(NULL != audio_manager.get()); - ASSERT_FALSE(audio_manager->HasAudioOutputDevices()); - ASSERT_TRUE(audio_manager->HasAudioInputDevices()); -} - -// Test that input stream can be opened and closed. -TEST(IOSAudioTest, InputStreamOpenAndClose) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); - ASSERT_TRUE(NULL != audio_manager.get()); - if (!audio_manager->HasAudioInputDevices()) - return; - AudioInputStream* ias = audio_manager->MakeAudioInputStream( - AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO, - 8000, 16, 1024), - std::string("test_device")); - ASSERT_TRUE(NULL != ias); - EXPECT_TRUE(ias->Open()); - ias->Close(); -} diff --git a/chromium/media/audio/ios/audio_session_util_ios.h b/chromium/media/audio/ios/audio_session_util_ios.h deleted file mode 100644 index 175db91fae0..00000000000 --- a/chromium/media/audio/ios/audio_session_util_ios.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 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. - -#ifndef MEDIA_AUDIO_IOS_AUDIO_SESSION_UTIL_IOS_H_ -#define MEDIA_AUDIO_IOS_AUDIO_SESSION_UTIL_IOS_H_ - -namespace media { - -// Initializes and configures the audio session, returning a bool indicating -// whether initialization was successful. Can be called multiple times. -// Safe to call from any thread. -bool InitAudioSessionIOS(); - -} // namespace media - -#endif // MEDIA_AUDIO_IOS_AUDIO_SESSION_UTIL_IOS_H_ diff --git a/chromium/media/audio/ios/audio_session_util_ios.mm b/chromium/media/audio/ios/audio_session_util_ios.mm deleted file mode 100644 index a4071a04cc1..00000000000 --- a/chromium/media/audio/ios/audio_session_util_ios.mm +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 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 "media/audio/ios/audio_session_util_ios.h" - -#include <AVFoundation/AVFoundation.h> - -#include "base/logging.h" - -namespace media { - -bool InitAudioSessionIOS() { - static bool kSessionInitialized = false; - static dispatch_once_t once = 0; - dispatch_once(&once, ^{ - OSStatus error = AudioSessionInitialize(NULL, NULL, NULL, NULL); - if (error != kAudioSessionNoError) - DLOG(ERROR) << "AudioSessionInitialize OSStatus error: " << error; - BOOL result = [[AVAudioSession sharedInstance] - setCategory:AVAudioSessionCategoryPlayAndRecord - error:nil]; - if (!result) - DLOG(ERROR) << "AVAudioSession setCategory failed"; - UInt32 allowMixing = true; - AudioSessionSetProperty( - kAudioSessionProperty_OverrideCategoryMixWithOthers, - sizeof(allowMixing), &allowMixing); - UInt32 defaultToSpeaker = true; - AudioSessionSetProperty( - kAudioSessionProperty_OverrideCategoryDefaultToSpeaker, - sizeof(defaultToSpeaker), - &defaultToSpeaker); - // Speech input cannot be used if either of these two conditions fail. - kSessionInitialized = (error == kAudioSessionNoError) && result; - }); - return kSessionInitialized; -} - -} // namespace media diff --git a/chromium/media/audio/linux/alsa_output_unittest.cc b/chromium/media/audio/linux/alsa_output_unittest.cc index 32456360f47..82fbab94c19 100644 --- a/chromium/media/audio/linux/alsa_output_unittest.cc +++ b/chromium/media/audio/linux/alsa_output_unittest.cc @@ -83,8 +83,10 @@ class MockAudioManagerLinux : public AudioManagerLinux { MOCK_METHOD0(HasAudioInputDevices, bool()); MOCK_METHOD1(MakeLinearOutputStream, AudioOutputStream*( const AudioParameters& params)); - MOCK_METHOD2(MakeLowLatencyOutputStream, AudioOutputStream*( - const AudioParameters& params, const std::string& input_device_id)); + MOCK_METHOD3(MakeLowLatencyOutputStream, AudioOutputStream*( + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id)); MOCK_METHOD2(MakeLowLatencyInputStream, AudioInputStream*( const AudioParameters& params, const std::string& device_id)); diff --git a/chromium/media/audio/linux/audio_manager_linux.cc b/chromium/media/audio/linux/audio_manager_linux.cc index 38253e2e651..708e4f26840 100644 --- a/chromium/media/audio/linux/audio_manager_linux.cc +++ b/chromium/media/audio/linux/audio_manager_linux.cc @@ -42,9 +42,9 @@ static const int kDefaultSampleRate = 48000; // hence surround devices are not stored in the list. static const char* kInvalidAudioInputDevices[] = { "default", + "dmix", "null", "pulse", - "dmix", "surround", }; @@ -103,9 +103,15 @@ void AudioManagerLinux::ShowAudioInputSettings() { } void AudioManagerLinux::GetAudioInputDeviceNames( - media::AudioDeviceNames* device_names) { + AudioDeviceNames* device_names) { + DCHECK(device_names->empty()); + GetAlsaAudioDevices(kStreamCapture, device_names); +} + +void AudioManagerLinux::GetAudioOutputDeviceNames( + AudioDeviceNames* device_names) { DCHECK(device_names->empty()); - GetAlsaAudioInputDevices(device_names); + GetAlsaAudioDevices(kStreamPlayback, device_names); } AudioParameters AudioManagerLinux::GetInputStreamParameters( @@ -117,7 +123,8 @@ AudioParameters AudioManagerLinux::GetInputStreamParameters( kDefaultSampleRate, 16, kDefaultInputBufferSize); } -void AudioManagerLinux::GetAlsaAudioInputDevices( +void AudioManagerLinux::GetAlsaAudioDevices( + StreamType type, media::AudioDeviceNames* device_names) { // Constants specified by the ALSA API for device hints. static const char kPcmInterfaceName[] = "pcm"; @@ -128,37 +135,40 @@ void AudioManagerLinux::GetAlsaAudioInputDevices( void** hints = NULL; int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints); if (!error) { - GetAlsaDevicesInfo(hints, device_names); + GetAlsaDevicesInfo(type, hints, device_names); // Destroy the hints now that we're done with it. wrapper_->DeviceNameFreeHint(hints); } else { - DLOG(WARNING) << "GetAudioInputDevices: unable to get device hints: " + DLOG(WARNING) << "GetAlsaAudioDevices: unable to get device hints: " << wrapper_->StrError(error); } } } void AudioManagerLinux::GetAlsaDevicesInfo( - void** hints, media::AudioDeviceNames* device_names) { + AudioManagerLinux::StreamType type, + void** hints, + media::AudioDeviceNames* device_names) { static const char kIoHintName[] = "IOID"; static const char kNameHintName[] = "NAME"; static const char kDescriptionHintName[] = "DESC"; - static const char kOutputDevice[] = "Output"; + + const char* unwanted_device_type = UnwantedDeviceTypeWhenEnumerating(type); for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) { - // Only examine devices that are input capable. Valid values are + // Only examine devices of the right type. Valid values are // "Input", "Output", and NULL which means both input and output. scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter, kIoHintName)); - if (io != NULL && strcmp(kOutputDevice, io.get()) == 0) + if (io != NULL && strcmp(unwanted_device_type, io.get()) == 0) continue; - // Found an input device, prepend the default device since we always want - // it to be on the top of the list for all platforms. And there is no - // duplicate counting here since it is only done if the list is still empty. - // Note, pulse has exclusively opened the default device, so we must open - // the device via the "default" moniker. + // Found a device, prepend the default device since we always want + // it to be on the top of the list for all platforms. And there is + // no duplicate counting here since it is only done if the list is + // still empty. Note, pulse has exclusively opened the default + // device, so we must open the device via the "default" moniker. if (device_names->empty()) { device_names->push_front(media::AudioDeviceName( AudioManagerBase::kDefaultDeviceName, @@ -170,7 +180,7 @@ void AudioManagerLinux::GetAlsaDevicesInfo( wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName)); // Find out if the device is available. - if (IsAlsaDeviceAvailable(unique_device_name.get())) { + if (IsAlsaDeviceAvailable(type, unique_device_name.get())) { // Get the description for the device. scoped_ptr_malloc<char> desc(wrapper_->DeviceNameGetHint( *hint_iter, kDescriptionHintName)); @@ -196,25 +206,46 @@ void AudioManagerLinux::GetAlsaDevicesInfo( } } -bool AudioManagerLinux::IsAlsaDeviceAvailable(const char* device_name) { +// static +bool AudioManagerLinux::IsAlsaDeviceAvailable( + AudioManagerLinux::StreamType type, + const char* device_name) { if (!device_name) return false; - // Check if the device is in the list of invalid devices. - for (size_t i = 0; i < arraysize(kInvalidAudioInputDevices); ++i) { - if (strncmp(kInvalidAudioInputDevices[i], device_name, - strlen(kInvalidAudioInputDevices[i])) == 0) - return false; + // We do prefix matches on the device name to see whether to include + // it or not. + if (type == kStreamCapture) { + // Check if the device is in the list of invalid devices. + for (size_t i = 0; i < arraysize(kInvalidAudioInputDevices); ++i) { + if (strncmp(kInvalidAudioInputDevices[i], device_name, + strlen(kInvalidAudioInputDevices[i])) == 0) + return false; + } + return true; + } else { + DCHECK_EQ(kStreamPlayback, type); + // We prefer the device type that maps straight to hardware but + // goes through software conversion if needed (e.g. incompatible + // sample rate). + // TODO(joi): Should we prefer "hw" instead? + static const char kDeviceTypeDesired[] = "plughw"; + return strncmp(kDeviceTypeDesired, + device_name, + arraysize(kDeviceTypeDesired) - 1) == 0; } +} - return true; +// static +const char* AudioManagerLinux::UnwantedDeviceTypeWhenEnumerating( + AudioManagerLinux::StreamType wanted_type) { + return wanted_type == kStreamPlayback ? "Input" : "Output"; } -bool AudioManagerLinux::HasAnyAlsaAudioDevice(StreamType stream) { +bool AudioManagerLinux::HasAnyAlsaAudioDevice( + AudioManagerLinux::StreamType stream) { static const char kPcmInterfaceName[] = "pcm"; static const char kIoHintName[] = "IOID"; - const char* kNotWantedDevice = - (stream == kStreamPlayback ? "Input" : "Output"); void** hints = NULL; bool has_device = false; int card = -1; @@ -230,7 +261,8 @@ bool AudioManagerLinux::HasAnyAlsaAudioDevice(StreamType stream) { // "Input", "Output", and NULL which means both input and output. scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter, kIoHintName)); - if (io != NULL && strcmp(kNotWantedDevice, io.get()) == 0) + const char* unwanted_type = UnwantedDeviceTypeWhenEnumerating(stream); + if (io != NULL && strcmp(unwanted_type, io.get()) == 0) continue; // Wrong type, skip the device. // Found an input device. @@ -258,7 +290,9 @@ AudioOutputStream* AudioManagerLinux::MakeLinearOutputStream( AudioOutputStream* AudioManagerLinux::MakeLowLatencyOutputStream( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) { + DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); // TODO(xians): Use input_device_id for unified IO. return MakeOutputStream(params); @@ -277,7 +311,10 @@ AudioInputStream* AudioManagerLinux::MakeLowLatencyInputStream( } AudioParameters AudioManagerLinux::GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) { + // TODO(tommi): Support |output_device_id|. + DLOG_IF(ERROR, !output_device_id.empty()) << "Not implemented!"; static const int kDefaultOutputBufferSize = 2048; ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; int sample_rate = kDefaultSampleRate; diff --git a/chromium/media/audio/linux/audio_manager_linux.h b/chromium/media/audio/linux/audio_manager_linux.h index 28abaa116e7..ab284dfdce9 100644 --- a/chromium/media/audio/linux/audio_manager_linux.h +++ b/chromium/media/audio/linux/audio_manager_linux.h @@ -25,8 +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 GetAudioInputDeviceNames( + AudioDeviceNames* device_names) OVERRIDE; + virtual void GetAudioOutputDeviceNames( + AudioDeviceNames* device_names) OVERRIDE; virtual AudioParameters GetInputStreamParameters( const std::string& device_id) OVERRIDE; @@ -35,6 +37,7 @@ class MEDIA_EXPORT AudioManagerLinux : public AudioManagerBase { const AudioParameters& params) OVERRIDE; virtual AudioOutputStream* MakeLowLatencyOutputStream( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) OVERRIDE; virtual AudioInputStream* MakeLinearInputStream( const AudioParameters& params, const std::string& device_id) OVERRIDE; @@ -45,6 +48,7 @@ class MEDIA_EXPORT AudioManagerLinux : public AudioManagerBase { virtual ~AudioManagerLinux(); virtual AudioParameters GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) OVERRIDE; private: @@ -53,14 +57,22 @@ class MEDIA_EXPORT AudioManagerLinux : public AudioManagerBase { kStreamCapture, }; - // Gets a list of available ALSA input devices. - void GetAlsaAudioInputDevices(media::AudioDeviceNames* device_names); + // Gets a list of available ALSA devices. + void GetAlsaAudioDevices(StreamType type, + media::AudioDeviceNames* device_names); - // Gets the ALSA devices' names and ids. - void GetAlsaDevicesInfo(void** hint, media::AudioDeviceNames* device_names); + // Gets the ALSA devices' names and ids that support streams of the + // given type. + void GetAlsaDevicesInfo(StreamType type, + void** hint, + media::AudioDeviceNames* device_names); // Checks if the specific ALSA device is available. - bool IsAlsaDeviceAvailable(const char* device_name); + static bool IsAlsaDeviceAvailable(StreamType type, + const char* device_name); + + static const char* UnwantedDeviceTypeWhenEnumerating( + StreamType wanted_type); // Returns true if a device is present for the given stream type. bool HasAnyAlsaAudioDevice(StreamType stream); diff --git a/chromium/media/audio/mac/audio_auhal_mac_unittest.cc b/chromium/media/audio/mac/audio_auhal_mac_unittest.cc index b4cf8c64cc6..9b699ff10f8 100644 --- a/chromium/media/audio/mac/audio_auhal_mac_unittest.cc +++ b/chromium/media/audio/mac/audio_auhal_mac_unittest.cc @@ -101,7 +101,7 @@ class AudioOutputStreamWrapper { samples_per_packet_); AudioOutputStream* aos = audio_man_->MakeAudioOutputStream(params, - std::string()); + std::string(), std::string()); EXPECT_TRUE(aos); return aos; } diff --git a/chromium/media/audio/mac/audio_input_mac.cc b/chromium/media/audio/mac/audio_input_mac.cc index 06af6d11c12..7930567fd9c 100644 --- a/chromium/media/audio/mac/audio_input_mac.cc +++ b/chromium/media/audio/mac/audio_input_mac.cc @@ -4,15 +4,14 @@ #include "media/audio/mac/audio_input_mac.h" +#include <CoreServices/CoreServices.h> + #include "base/basictypes.h" #include "base/logging.h" #include "base/mac/mac_logging.h" #include "media/audio/audio_manager_base.h" #include "media/audio/audio_util.h" -#if !defined(OS_IOS) -#include <CoreServices/CoreServices.h> -#endif namespace media { diff --git a/chromium/media/audio/mac/audio_low_latency_input_mac.cc b/chromium/media/audio/mac/audio_low_latency_input_mac.cc index 17a87b0a7dc..d97f453ca99 100644 --- a/chromium/media/audio/mac/audio_low_latency_input_mac.cc +++ b/chromium/media/audio/mac/audio_low_latency_input_mac.cc @@ -35,7 +35,9 @@ static std::ostream& operator<<(std::ostream& os, // for more details and background regarding this implementation. AUAudioInputStream::AUAudioInputStream( - AudioManagerMac* manager, const AudioParameters& params, + AudioManagerMac* manager, + const AudioParameters& input_params, + const AudioParameters& output_params, AudioDeviceID audio_device_id) : manager_(manager), sink_(NULL), @@ -48,15 +50,15 @@ AUAudioInputStream::AUAudioInputStream( DCHECK(manager_); // Set up the desired (output) format specified by the client. - format_.mSampleRate = params.sample_rate(); + format_.mSampleRate = input_params.sample_rate(); format_.mFormatID = kAudioFormatLinearPCM; format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger; - format_.mBitsPerChannel = params.bits_per_sample(); - format_.mChannelsPerFrame = params.channels(); + format_.mBitsPerChannel = input_params.bits_per_sample(); + format_.mChannelsPerFrame = input_params.channels(); format_.mFramesPerPacket = 1; // uncompressed audio format_.mBytesPerPacket = (format_.mBitsPerChannel * - params.channels()) / 8; + input_params.channels()) / 8; format_.mBytesPerFrame = format_.mBytesPerPacket; format_.mReserved = 0; @@ -68,10 +70,7 @@ AUAudioInputStream::AUAudioInputStream( // Note that we use the same native buffer size as for the output side here // since the AUHAL implementation requires that both capture and render side // use the same buffer size. See http://crbug.com/154352 for more details. - // TODO(xians): Get the audio parameters from the right device. - const AudioParameters parameters = - manager_->GetInputStreamParameters(AudioManagerBase::kDefaultDeviceId); - number_of_frames_ = parameters.frames_per_buffer(); + number_of_frames_ = output_params.frames_per_buffer(); DVLOG(1) << "Size of data buffer in frames : " << number_of_frames_; // Derive size (in bytes) of the buffers that we will render to. @@ -85,7 +84,7 @@ AUAudioInputStream::AUAudioInputStream( audio_buffer_list_.mNumberBuffers = 1; AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers; - audio_buffer->mNumberChannels = params.channels(); + audio_buffer->mNumberChannels = input_params.channels(); audio_buffer->mDataByteSize = data_byte_size; audio_buffer->mData = audio_data_buffer_.get(); @@ -93,9 +92,16 @@ AUAudioInputStream::AUAudioInputStream( // until a requested size is ready to be sent to the client. // It is not possible to ask for less than |kAudioFramesPerCallback| number of // audio frames. - const size_t requested_size_frames = - params.GetBytesPerBuffer() / format_.mBytesPerPacket; - DCHECK_GE(requested_size_frames, number_of_frames_); + size_t requested_size_frames = + input_params.GetBytesPerBuffer() / format_.mBytesPerPacket; + if (requested_size_frames < number_of_frames_) { + // For devices that only support a low sample rate like 8kHz, we adjust the + // buffer size to match number_of_frames_. The value of number_of_frames_ + // in this case has not been calculated based on hardware settings but + // rather our hardcoded defaults (see ChooseBufferSize). + requested_size_frames = number_of_frames_; + } + requested_size_bytes_ = requested_size_frames * format_.mBytesPerFrame; DVLOG(1) << "Requested buffer size in bytes : " << requested_size_bytes_; DLOG_IF(INFO, requested_size_frames > number_of_frames_) << "FIFO is used"; diff --git a/chromium/media/audio/mac/audio_low_latency_input_mac.h b/chromium/media/audio/mac/audio_low_latency_input_mac.h index 736bf082f5b..04592d2cecf 100644 --- a/chromium/media/audio/mac/audio_low_latency_input_mac.h +++ b/chromium/media/audio/mac/audio_low_latency_input_mac.h @@ -57,7 +57,8 @@ class AUAudioInputStream : public AgcAudioStream<AudioInputStream> { // The ctor takes all the usual parameters, plus |manager| which is the // the audio manager who is creating this object. AUAudioInputStream(AudioManagerMac* manager, - const AudioParameters& params, + const AudioParameters& input_params, + const AudioParameters& output_params, AudioDeviceID audio_device_id); // The dtor is typically called by the AudioManager only and it is usually // triggered by calling AudioInputStream::Close(). diff --git a/chromium/media/audio/mac/audio_manager_mac.cc b/chromium/media/audio/mac/audio_manager_mac.cc index c0c18ee2cce..8e4b969854e 100644 --- a/chromium/media/audio/mac/audio_manager_mac.cc +++ b/chromium/media/audio/mac/audio_manager_mac.cc @@ -81,11 +81,10 @@ bool AudioManagerMac::HasUnifiedDefaultIO() { return input_id == output_id; } +// Retrieves information on audio devices, and prepends the default +// device to the list if the list is non-empty. static void GetAudioDeviceInfo(bool is_input, media::AudioDeviceNames* device_names) { - DCHECK(device_names); - device_names->clear(); - // Query the number of total devices. AudioObjectPropertyAddress property_address = { kAudioHardwarePropertyDevices, @@ -176,6 +175,16 @@ static void GetAudioDeviceInfo(bool is_input, if (name) CFRelease(name); } + + if (!device_names->empty()) { + // Prepend the default device to the list since we always want it to be + // on the top of the list for all platforms. There is no duplicate + // counting here since the default device has been abstracted out before. + media::AudioDeviceName name; + name.device_name = AudioManagerBase::kDefaultDeviceName; + name.unique_id = AudioManagerBase::kDefaultDeviceId; + device_names->push_front(name); + } } static AudioDeviceID GetAudioDeviceIdByUId(bool is_input, @@ -189,7 +198,7 @@ static AudioDeviceID GetAudioDeviceIdByUId(bool is_input, UInt32 device_size = sizeof(audio_device_id); OSStatus result = -1; - if (device_id == AudioManagerBase::kDefaultDeviceId) { + if (device_id == AudioManagerBase::kDefaultDeviceId || device_id.empty()) { // Default Device. property_address.mSelector = is_input ? kAudioHardwarePropertyDefaultInputDevice : @@ -263,7 +272,7 @@ bool AudioManagerMac::HasAudioInputDevices() { return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice); } -// TODO(crogers): There are several places on the OSX specific code which +// TODO(xians): There are several places on the OSX specific code which // could benefit from these helper functions. bool AudioManagerMac::GetDefaultInputDevice( AudioDeviceID* device) { @@ -397,16 +406,14 @@ int AudioManagerMac::HardwareSampleRate() { void AudioManagerMac::GetAudioInputDeviceNames( media::AudioDeviceNames* device_names) { + DCHECK(device_names->empty()); GetAudioDeviceInfo(true, device_names); - if (!device_names->empty()) { - // Prepend the default device to the list since we always want it to be - // on the top of the list for all platforms. There is no duplicate - // counting here since the default device has been abstracted out before. - media::AudioDeviceName name; - name.device_name = AudioManagerBase::kDefaultDeviceName; - name.unique_id = AudioManagerBase::kDefaultDeviceId; - device_names->push_front(name); - } +} + +void AudioManagerMac::GetAudioOutputDeviceNames( + media::AudioDeviceNames* device_names) { + DCHECK(device_names->empty()); + GetAudioDeviceInfo(false, device_names); } AudioParameters AudioManagerMac::GetInputStreamParameters( @@ -443,21 +450,86 @@ AudioParameters AudioManagerMac::GetInputStreamParameters( sample_rate, 16, buffer_size); } +std::string AudioManagerMac::GetAssociatedOutputDeviceID( + const std::string& input_device_id) { + AudioDeviceID device = GetAudioDeviceIdByUId(true, input_device_id); + if (device == kAudioObjectUnknown) + return std::string(); + + UInt32 size = 0; + AudioObjectPropertyAddress pa = { + kAudioDevicePropertyRelatedDevices, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size); + if (result || !size) + return std::string(); + + int device_count = size / sizeof(AudioDeviceID); + scoped_ptr_malloc<AudioDeviceID> + devices(reinterpret_cast<AudioDeviceID*>(malloc(size))); + result = AudioObjectGetPropertyData( + device, &pa, 0, NULL, &size, devices.get()); + if (result) + return std::string(); + + for (int i = 0; i < device_count; ++i) { + // Get the number of output channels of the device. + pa.mSelector = kAudioDevicePropertyStreams; + size = 0; + result = AudioObjectGetPropertyDataSize(devices.get()[i], + &pa, + 0, + NULL, + &size); + if (result || !size) + continue; // Skip if there aren't any output channels. + + // Get device UID. + CFStringRef uid = NULL; + size = sizeof(uid); + pa.mSelector = kAudioDevicePropertyDeviceUID; + result = AudioObjectGetPropertyData(devices.get()[i], + &pa, + 0, + NULL, + &size, + &uid); + if (result || !uid) + continue; + + std::string ret(base::SysCFStringRefToUTF8(uid)); + CFRelease(uid); + return ret; + } + + // No matching device found. + return std::string(); +} + AudioOutputStream* AudioManagerMac::MakeLinearOutputStream( const AudioParameters& params) { - return MakeLowLatencyOutputStream(params, std::string()); + return MakeLowLatencyOutputStream(params, std::string(), std::string()); } AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream( - const AudioParameters& params, const std::string& input_device_id) { + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id) { // Handle basic output with no input channels. if (params.input_channels() == 0) { - AudioDeviceID device = kAudioObjectUnknown; - GetDefaultOutputDevice(&device); + AudioDeviceID device = GetAudioDeviceIdByUId(false, device_id); + if (device == kAudioObjectUnknown) { + DLOG(ERROR) << "Failed to open output device: " << device_id; + return NULL; + } return new AUHALStream(this, params, device); } - // TODO(crogers): support more than stereo input. + DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; + + // TODO(xians): support more than stereo input. if (params.input_channels() != 2) { // WebAudio is currently hard-coded to 2 channels so we should not // see this case. @@ -494,7 +566,7 @@ AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream( // different and arbitrary combinations of input and output devices // even running at different sample-rates. // kAudioDeviceUnknown translates to "use default" here. - // TODO(crogers): consider tracking UMA stats on AUHALStream + // TODO(xians): consider tracking UMA stats on AUHALStream // versus AudioSynchronizedStream. AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, input_device_id); if (audio_device_id == kAudioObjectUnknown) @@ -506,6 +578,33 @@ AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream( kAudioDeviceUnknown); } +std::string AudioManagerMac::GetDefaultOutputDeviceID() { + AudioDeviceID device_id = kAudioObjectUnknown; + if (!GetDefaultOutputDevice(&device_id)) + return std::string(); + + const AudioObjectPropertyAddress property_address = { + kAudioDevicePropertyDeviceUID, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + CFStringRef device_uid = NULL; + UInt32 size = sizeof(device_uid); + OSStatus status = AudioObjectGetPropertyData(device_id, + &property_address, + 0, + NULL, + &size, + &device_uid); + if (status != kAudioHardwareNoError || !device_uid) + return std::string(); + + std::string ret(base::SysCFStringRefToUTF8(device_uid)); + CFRelease(device_uid); + + return ret; +} + AudioInputStream* AudioManagerMac::MakeLinearInputStream( const AudioParameters& params, const std::string& device_id) { DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); @@ -515,27 +614,47 @@ AudioInputStream* AudioManagerMac::MakeLinearInputStream( AudioInputStream* AudioManagerMac::MakeLowLatencyInputStream( const AudioParameters& params, const std::string& device_id) { DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); - // Gets the AudioDeviceID that refers to the AudioOutputDevice with the device + // Gets the AudioDeviceID that refers to the AudioInputDevice with the device // unique id. This AudioDeviceID is used to set the device for Audio Unit. AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, device_id); AudioInputStream* stream = NULL; - if (audio_device_id != kAudioObjectUnknown) - stream = new AUAudioInputStream(this, params, audio_device_id); + if (audio_device_id != kAudioObjectUnknown) { + // AUAudioInputStream needs to be fed the preferred audio output parameters + // of the matching device so that the buffer size of both input and output + // can be matched. See constructor of AUAudioInputStream for more. + const std::string associated_output_device( + GetAssociatedOutputDeviceID(device_id)); + const AudioParameters output_params = + GetPreferredOutputStreamParameters( + associated_output_device.empty() ? + AudioManagerBase::kDefaultDeviceId : associated_output_device, + params); + stream = new AUAudioInputStream(this, params, output_params, + audio_device_id); + } return stream; } AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) { + AudioDeviceID device = GetAudioDeviceIdByUId(false, output_device_id); + if (device == kAudioObjectUnknown) { + DLOG(ERROR) << "Invalid output device " << output_device_id; + return AudioParameters(); + } + int hardware_channels = 2; - if (!GetDefaultOutputChannels(&hardware_channels)) { + if (!GetDeviceChannels(device, kAudioDevicePropertyScopeOutput, + &hardware_channels)) { // Fallback to stereo. hardware_channels = 2; } ChannelLayout channel_layout = GuessChannelLayout(hardware_channels); - const int hardware_sample_rate = AUAudioOutputStream::HardwareSampleRate(); + const int hardware_sample_rate = HardwareSampleRateForDevice(device); const int buffer_size = ChooseBufferSize(hardware_sample_rate); int input_channels = 0; @@ -543,7 +662,7 @@ AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters( input_channels = input_params.input_channels(); if (input_channels > 0) { - // TODO(crogers): given the limitations of the AudioOutputStream + // TODO(xians): given the limitations of the AudioOutputStream // back-ends used with synchronized I/O, we hard-code to stereo. // Specifically, this is a limitation of AudioSynchronizedStream which // can be removed as part of the work to consolidate these back-ends. diff --git a/chromium/media/audio/mac/audio_manager_mac.h b/chromium/media/audio/mac/audio_manager_mac.h index cd3cc2e94b5..d162554b405 100644 --- a/chromium/media/audio/mac/audio_manager_mac.h +++ b/chromium/media/audio/mac/audio_manager_mac.h @@ -27,21 +27,27 @@ 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 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( + const std::string& input_device_id) OVERRIDE; // Implementation of AudioManagerBase. virtual AudioOutputStream* MakeLinearOutputStream( const AudioParameters& params) OVERRIDE; virtual AudioOutputStream* MakeLowLatencyOutputStream( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) OVERRIDE; virtual AudioInputStream* MakeLinearInputStream( const AudioParameters& params, const std::string& device_id) OVERRIDE; virtual AudioInputStream* MakeLowLatencyInputStream( const AudioParameters& params, const std::string& device_id) OVERRIDE; + virtual std::string GetDefaultOutputDeviceID() OVERRIDE; static bool GetDefaultInputDevice(AudioDeviceID* device); static bool GetDefaultOutputDevice(AudioDeviceID* device); @@ -64,6 +70,7 @@ class MEDIA_EXPORT AudioManagerMac : public AudioManagerBase { virtual ~AudioManagerMac(); virtual AudioParameters GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) OVERRIDE; private: diff --git a/chromium/media/audio/mock_audio_manager.cc b/chromium/media/audio/mock_audio_manager.cc index 60898bd61b8..a164332a64a 100644 --- a/chromium/media/audio/mock_audio_manager.cc +++ b/chromium/media/audio/mock_audio_manager.cc @@ -33,18 +33,24 @@ void MockAudioManager::ShowAudioInputSettings() { } void MockAudioManager::GetAudioInputDeviceNames( - media::AudioDeviceNames* device_names) { + AudioDeviceNames* device_names) { +} + +void MockAudioManager::GetAudioOutputDeviceNames( + AudioDeviceNames* device_names) { } media::AudioOutputStream* MockAudioManager::MakeAudioOutputStream( - const media::AudioParameters& params, - const std::string& input_device_id) { + const media::AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id) { NOTREACHED(); return NULL; } media::AudioOutputStream* MockAudioManager::MakeAudioOutputStreamProxy( const media::AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) { NOTREACHED(); return NULL; @@ -77,9 +83,19 @@ AudioParameters MockAudioManager::GetDefaultOutputStreamParameters() { return AudioParameters(); } +AudioParameters MockAudioManager::GetOutputStreamParameters( + const std::string& device_id) { + return AudioParameters(); +} + AudioParameters MockAudioManager::GetInputStreamParameters( const std::string& device_id) { return AudioParameters(); } +std::string MockAudioManager::GetAssociatedOutputDeviceID( + const std::string& input_device_id) { + return std::string(); +} + } // namespace media. diff --git a/chromium/media/audio/mock_audio_manager.h b/chromium/media/audio/mock_audio_manager.h index eee84b1643f..7bc30f578e7 100644 --- a/chromium/media/audio/mock_audio_manager.h +++ b/chromium/media/audio/mock_audio_manager.h @@ -34,12 +34,17 @@ class MockAudioManager : public media::AudioManager { virtual void GetAudioInputDeviceNames( media::AudioDeviceNames* device_names) OVERRIDE; + virtual void GetAudioOutputDeviceNames( + media::AudioDeviceNames* device_names) OVERRIDE; + virtual media::AudioOutputStream* MakeAudioOutputStream( const media::AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) OVERRIDE; virtual media::AudioOutputStream* MakeAudioOutputStreamProxy( const media::AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) OVERRIDE; virtual media::AudioInputStream* MakeAudioInputStream( @@ -55,8 +60,12 @@ class MockAudioManager : public media::AudioManager { AudioDeviceListener* listener) OVERRIDE; virtual AudioParameters GetDefaultOutputStreamParameters() OVERRIDE; + virtual AudioParameters GetOutputStreamParameters( + const std::string& device_id) OVERRIDE; virtual AudioParameters GetInputStreamParameters( const std::string& device_id) OVERRIDE; + virtual std::string GetAssociatedOutputDeviceID( + const std::string& input_device_id) OVERRIDE; private: virtual ~MockAudioManager(); diff --git a/chromium/media/audio/openbsd/audio_manager_openbsd.cc b/chromium/media/audio/openbsd/audio_manager_openbsd.cc index 4005aeb98f0..a97ea8f625e 100644 --- a/chromium/media/audio/openbsd/audio_manager_openbsd.cc +++ b/chromium/media/audio/openbsd/audio_manager_openbsd.cc @@ -92,7 +92,9 @@ AudioOutputStream* AudioManagerOpenBSD::MakeLinearOutputStream( AudioOutputStream* AudioManagerOpenBSD::MakeLowLatencyOutputStream( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) { + DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format); return MakeOutputStream(params); } @@ -112,7 +114,10 @@ AudioInputStream* AudioManagerOpenBSD::MakeLowLatencyInputStream( } AudioParameters AudioManagerOpenBSD::GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) { + // TODO(tommi): Support |output_device_id|. + DLOG_IF(ERROR, !output_device_id.empty()) << "Not implemented!"; static const int kDefaultOutputBufferSize = 512; ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; diff --git a/chromium/media/audio/openbsd/audio_manager_openbsd.h b/chromium/media/audio/openbsd/audio_manager_openbsd.h index a1adcb6c86c..e4bb3948d28 100644 --- a/chromium/media/audio/openbsd/audio_manager_openbsd.h +++ b/chromium/media/audio/openbsd/audio_manager_openbsd.h @@ -27,6 +27,7 @@ class MEDIA_EXPORT AudioManagerOpenBSD : public AudioManagerBase { const AudioParameters& params) OVERRIDE; virtual AudioOutputStream* MakeLowLatencyOutputStream( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) OVERRIDE; virtual AudioInputStream* MakeLinearInputStream( const AudioParameters& params, const std::string& device_id) OVERRIDE; @@ -37,6 +38,7 @@ class MEDIA_EXPORT AudioManagerOpenBSD : public AudioManagerBase { virtual ~AudioManagerOpenBSD(); virtual AudioParameters GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) OVERRIDE; private: diff --git a/chromium/media/audio/pulse/audio_manager_pulse.cc b/chromium/media/audio/pulse/audio_manager_pulse.cc index dcdd3282228..5c09f149057 100644 --- a/chromium/media/audio/pulse/audio_manager_pulse.cc +++ b/chromium/media/audio/pulse/audio_manager_pulse.cc @@ -66,19 +66,13 @@ AudioManagerPulse::~AudioManagerPulse() { // Implementation of AudioManager. bool AudioManagerPulse::HasAudioOutputDevices() { - DCHECK(input_mainloop_); - DCHECK(input_context_); - media::AudioDeviceNames devices; - AutoPulseLock auto_lock(input_mainloop_); - devices_ = &devices; - pa_operation* operation = pa_context_get_sink_info_list( - input_context_, OutputDevicesInfoCallback, this); - WaitForOperationCompletion(input_mainloop_, operation); + AudioDeviceNames devices; + GetAudioOutputDeviceNames(&devices); return !devices.empty(); } bool AudioManagerPulse::HasAudioInputDevices() { - media::AudioDeviceNames devices; + AudioDeviceNames devices; GetAudioInputDeviceNames(&devices); return !devices.empty(); } @@ -87,18 +81,24 @@ void AudioManagerPulse::ShowAudioInputSettings() { AudioManagerLinux::ShowLinuxAudioInputSettings(); } -void AudioManagerPulse::GetAudioInputDeviceNames( - media::AudioDeviceNames* device_names) { +void AudioManagerPulse::GetAudioDeviceNames( + bool input, media::AudioDeviceNames* device_names) { DCHECK(device_names->empty()); DCHECK(input_mainloop_); DCHECK(input_context_); AutoPulseLock auto_lock(input_mainloop_); devices_ = device_names; - pa_operation* operation = pa_context_get_source_info_list( + pa_operation* operation = NULL; + if (input) { + operation = pa_context_get_source_info_list( input_context_, InputDevicesInfoCallback, this); + } else { + operation = pa_context_get_sink_info_list( + input_context_, OutputDevicesInfoCallback, this); + } WaitForOperationCompletion(input_mainloop_, operation); - // Append the default device on the top of the list if the list is not empty. + // Prepend the default device if the list is not empty. if (!device_names->empty()) { device_names->push_front( AudioDeviceName(AudioManagerBase::kDefaultDeviceName, @@ -106,6 +106,16 @@ void AudioManagerPulse::GetAudioInputDeviceNames( } } +void AudioManagerPulse::GetAudioInputDeviceNames( + AudioDeviceNames* device_names) { + GetAudioDeviceNames(true, device_names); +} + +void AudioManagerPulse::GetAudioOutputDeviceNames( + AudioDeviceNames* device_names) { + GetAudioDeviceNames(false, device_names); +} + AudioParameters AudioManagerPulse::GetInputStreamParameters( const std::string& device_id) { static const int kDefaultInputBufferSize = 1024; @@ -123,7 +133,10 @@ AudioOutputStream* AudioManagerPulse::MakeLinearOutputStream( } AudioOutputStream* AudioManagerPulse::MakeLowLatencyOutputStream( - const AudioParameters& params, const std::string& input_device_id) { + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id) { + DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); return MakeOutputStream(params, input_device_id); } @@ -141,7 +154,10 @@ AudioInputStream* AudioManagerPulse::MakeLowLatencyInputStream( } AudioParameters AudioManagerPulse::GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) { + // TODO(tommi): Support |output_device_id|. + DLOG_IF(ERROR, !output_device_id.empty()) << "Not implemented!"; static const int kDefaultOutputBufferSize = 512; ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; @@ -286,8 +302,8 @@ void AudioManagerPulse::InputDevicesInfoCallback(pa_context* context, // Exclude the output devices. if (info->monitor_of_sink == PA_INVALID_INDEX) { - manager->devices_->push_back(media::AudioDeviceName(info->description, - info->name)); + manager->devices_->push_back(AudioDeviceName(info->description, + info->name)); } } @@ -302,8 +318,8 @@ void AudioManagerPulse::OutputDevicesInfoCallback(pa_context* context, return; } - manager->devices_->push_back(media::AudioDeviceName(info->description, - info->name)); + manager->devices_->push_back(AudioDeviceName(info->description, + info->name)); } void AudioManagerPulse::SampleRateInfoCallback(pa_context* context, diff --git a/chromium/media/audio/pulse/audio_manager_pulse.h b/chromium/media/audio/pulse/audio_manager_pulse.h index 6dfebaeff39..36396639929 100644 --- a/chromium/media/audio/pulse/audio_manager_pulse.h +++ b/chromium/media/audio/pulse/audio_manager_pulse.h @@ -25,8 +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 GetAudioInputDeviceNames( + AudioDeviceNames* device_names) OVERRIDE; + virtual void GetAudioOutputDeviceNames( + AudioDeviceNames* device_names) OVERRIDE; virtual AudioParameters GetInputStreamParameters( const std::string& device_id) OVERRIDE; @@ -35,6 +37,7 @@ class MEDIA_EXPORT AudioManagerPulse : public AudioManagerBase { const AudioParameters& params) OVERRIDE; virtual AudioOutputStream* MakeLowLatencyOutputStream( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) OVERRIDE; virtual AudioInputStream* MakeLinearInputStream( const AudioParameters& params, const std::string& device_id) OVERRIDE; @@ -43,12 +46,15 @@ class MEDIA_EXPORT AudioManagerPulse : public AudioManagerBase { protected: virtual AudioParameters GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) OVERRIDE; private: bool Init(); void DestroyPulse(); + void GetAudioDeviceNames(bool input, media::AudioDeviceNames* device_names); + // Callback to get the devices' info like names, used by GetInputDevices(). static void InputDevicesInfoCallback(pa_context* context, const pa_source_info* info, diff --git a/chromium/media/audio/shared_memory_util.cc b/chromium/media/audio/shared_memory_util.cc index b65df03e2e1..523cdb9646c 100644 --- a/chromium/media/audio/shared_memory_util.cc +++ b/chromium/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/chromium/media/audio/test_audio_input_controller_factory.cc b/chromium/media/audio/test_audio_input_controller_factory.cc index 64bfb9f060d..d146231a25d 100644 --- a/chromium/media/audio/test_audio_input_controller_factory.cc +++ b/chromium/media/audio/test_audio_input_controller_factory.cc @@ -12,8 +12,9 @@ TestAudioInputController::TestAudioInputController( AudioManager* audio_manager, const AudioParameters& audio_parameters, EventHandler* event_handler, - SyncWriter* sync_writer) - : AudioInputController(event_handler, sync_writer), + SyncWriter* sync_writer, + UserInputMonitor* user_input_monitor) + : AudioInputController(event_handler, sync_writer, user_input_monitor), audio_parameters_(audio_parameters), factory_(factory), event_handler_(event_handler) { @@ -48,10 +49,11 @@ TestAudioInputControllerFactory::~TestAudioInputControllerFactory() { AudioInputController* TestAudioInputControllerFactory::Create( AudioManager* audio_manager, AudioInputController::EventHandler* event_handler, - AudioParameters params) { + AudioParameters params, + UserInputMonitor* user_input_monitor) { DCHECK(!controller_); // Only one test instance managed at a time. - controller_ = new TestAudioInputController(this, audio_manager, params, - event_handler, NULL); + controller_ = new TestAudioInputController( + this, audio_manager, params, event_handler, NULL, user_input_monitor); return controller_; } diff --git a/chromium/media/audio/test_audio_input_controller_factory.h b/chromium/media/audio/test_audio_input_controller_factory.h index 0a179473c1c..4968c013d97 100644 --- a/chromium/media/audio/test_audio_input_controller_factory.h +++ b/chromium/media/audio/test_audio_input_controller_factory.h @@ -10,6 +10,7 @@ namespace media { +class UserInputMonitor; class TestAudioInputControllerFactory; // TestAudioInputController and TestAudioInputControllerFactory are used for @@ -56,7 +57,8 @@ class TestAudioInputController : public AudioInputController { AudioManager* audio_manager, const AudioParameters& audio_parameters, EventHandler* event_handler, - SyncWriter* sync_writer); + SyncWriter* sync_writer, + UserInputMonitor* user_input_monitor); // Returns the event handler installed on the AudioInputController. EventHandler* event_handler() const { return event_handler_; } @@ -94,7 +96,8 @@ class TestAudioInputControllerFactory : public AudioInputController::Factory { virtual AudioInputController* Create( AudioManager* audio_manager, AudioInputController::EventHandler* event_handler, - AudioParameters params) OVERRIDE; + AudioParameters params, + UserInputMonitor* user_input_monitor) OVERRIDE; void SetDelegateForTests(TestAudioInputControllerDelegate* delegate); diff --git a/chromium/media/audio/win/audio_low_latency_input_win.cc b/chromium/media/audio/win/audio_low_latency_input_win.cc index e0819439109..a174ea2ea0d 100644 --- a/chromium/media/audio/win/audio_low_latency_input_win.cc +++ b/chromium/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/chromium/media/audio/win/audio_low_latency_input_win.h b/chromium/media/audio/win/audio_low_latency_input_win.h index 4f9c7fb6c88..99e1604925a 100644 --- a/chromium/media/audio/win/audio_low_latency_input_win.h +++ b/chromium/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/chromium/media/audio/win/audio_low_latency_input_win_unittest.cc b/chromium/media/audio/win/audio_low_latency_input_win_unittest.cc index 40990ec13d4..11fad25d3fe 100644 --- a/chromium/media/audio/win/audio_low_latency_input_win_unittest.cc +++ b/chromium/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/chromium/media/audio/win/audio_low_latency_output_win.cc b/chromium/media/audio/win/audio_low_latency_output_win.cc index b2098b02094..c889c03ef2c 100644 --- a/chromium/media/audio/win/audio_low_latency_output_win.cc +++ b/chromium/media/audio/win/audio_low_latency_output_win.cc @@ -111,14 +111,26 @@ ChannelLayout WASAPIAudioOutputStream::HardwareChannelLayout() { } // static -int WASAPIAudioOutputStream::HardwareSampleRate() { +int WASAPIAudioOutputStream::HardwareSampleRate(const std::string& device_id) { WAVEFORMATPCMEX format; - return SUCCEEDED(CoreAudioUtil::GetDefaultSharedModeMixFormat( - eRender, eConsole, &format)) ? - static_cast<int>(format.Format.nSamplesPerSec) : 0; + ScopedComPtr<IAudioClient> client; + if (device_id.empty()) { + client = CoreAudioUtil::CreateDefaultClient(eRender, eConsole); + } else { + ScopedComPtr<IMMDevice> device(CoreAudioUtil::CreateDevice(device_id)); + if (!device) + return 0; + client = CoreAudioUtil::CreateClient(device); + } + + if (!client || FAILED(CoreAudioUtil::GetSharedModeMixFormat(client, &format))) + return 0; + + return static_cast<int>(format.Format.nSamplesPerSec); } WASAPIAudioOutputStream::WASAPIAudioOutputStream(AudioManagerWin* manager, + const std::string& device_id, const AudioParameters& params, ERole device_role) : creating_thread_id_(base::PlatformThread::CurrentId()), @@ -127,6 +139,7 @@ WASAPIAudioOutputStream::WASAPIAudioOutputStream(AudioManagerWin* manager, audio_parameters_are_valid_(false), volume_(1.0), endpoint_buffer_size_frames_(0), + device_id_(device_id), device_role_(device_role), share_mode_(GetShareMode()), num_written_frames_(0), @@ -142,12 +155,16 @@ WASAPIAudioOutputStream::WASAPIAudioOutputStream(AudioManagerWin* manager, // channel count are excluded) to the preferred (native) audio parameters. // Open() will fail if this is not the case. AudioParameters preferred_params; - HRESULT hr = CoreAudioUtil::GetPreferredAudioParameters( - eRender, device_role, &preferred_params); + HRESULT hr = device_id_.empty() ? + CoreAudioUtil::GetPreferredAudioParameters(eRender, device_role, + &preferred_params) : + CoreAudioUtil::GetPreferredAudioParameters(device_id_, + &preferred_params); audio_parameters_are_valid_ = SUCCEEDED(hr) && CompareAudioParametersNoBitDepthOrChannels(params, preferred_params); LOG_IF(WARNING, !audio_parameters_are_valid_) - << "Input and preferred parameters are not identical."; + << "Input and preferred parameters are not identical. " + << "Device id: " << device_id_; } // Load the Avrt DLL if not already loaded. Required to support MMCSS. @@ -203,7 +220,6 @@ bool WASAPIAudioOutputStream::Open() { if (opened_) return true; - // Audio parameters must be identical to the preferred set of parameters // if shared mode (default) is utilized. if (share_mode_ == AUDCLNT_SHAREMODE_SHARED) { @@ -214,8 +230,16 @@ bool WASAPIAudioOutputStream::Open() { } // Create an IAudioClient interface for the default rendering IMMDevice. - ScopedComPtr<IAudioClient> audio_client = - CoreAudioUtil::CreateDefaultClient(eRender, device_role_); + ScopedComPtr<IAudioClient> audio_client; + if (device_id_.empty()) { + audio_client = CoreAudioUtil::CreateDefaultClient(eRender, device_role_); + } else { + ScopedComPtr<IMMDevice> device(CoreAudioUtil::CreateDevice(device_id_)); + DLOG_IF(ERROR, !device) << "Failed to open device: " << device_id_; + if (device) + audio_client = CoreAudioUtil::CreateClient(device); + } + if (!audio_client) return false; diff --git a/chromium/media/audio/win/audio_low_latency_output_win.h b/chromium/media/audio/win/audio_low_latency_output_win.h index b0e990bb1a4..7884d8840f7 100644 --- a/chromium/media/audio/win/audio_low_latency_output_win.h +++ b/chromium/media/audio/win/audio_low_latency_output_win.h @@ -122,6 +122,7 @@ class MEDIA_EXPORT WASAPIAudioOutputStream : // The ctor takes all the usual parameters, plus |manager| which is the // the audio manager who is creating this object. WASAPIAudioOutputStream(AudioManagerWin* manager, + const std::string& device_id, const AudioParameters& params, ERole device_role); @@ -149,8 +150,9 @@ class MEDIA_EXPORT WASAPIAudioOutputStream : static ChannelLayout HardwareChannelLayout(); // Retrieves the sample rate the audio engine uses for its internal - // processing/mixing of shared-mode streams for the default endpoint device. - static int HardwareSampleRate(); + // processing/mixing of shared-mode streams. To fetch the settings for the + // default device, pass an empty string as the |device_id|. + static int HardwareSampleRate(const std::string& device_id); // Returns AUDCLNT_SHAREMODE_EXCLUSIVE if --enable-exclusive-mode is used // as command-line flag and AUDCLNT_SHAREMODE_SHARED otherwise (default). @@ -219,6 +221,9 @@ class MEDIA_EXPORT WASAPIAudioOutputStream : // Length of the audio endpoint buffer. uint32 endpoint_buffer_size_frames_; + // The target device id or an empty string for the default device. + const std::string device_id_; + // Defines the role that the system has assigned to an audio endpoint device. ERole device_role_; diff --git a/chromium/media/audio/win/audio_low_latency_output_win_unittest.cc b/chromium/media/audio/win/audio_low_latency_output_win_unittest.cc index 8c3e366c0cc..1f78facf91d 100644 --- a/chromium/media/audio/win/audio_low_latency_output_win_unittest.cc +++ b/chromium/media/audio/win/audio_low_latency_output_win_unittest.cc @@ -234,7 +234,7 @@ class AudioOutputStreamWrapper { AudioOutputStream* aos = audio_man_->MakeAudioOutputStream( AudioParameters(format_, channel_layout_, sample_rate_, bits_per_sample_, samples_per_packet_), - std::string()); + std::string(), std::string()); EXPECT_TRUE(aos); return aos; } @@ -268,7 +268,7 @@ TEST(WASAPIAudioOutputStreamTest, HardwareSampleRate) { // Default device intended for games, system notification sounds, // and voice commands. int fs = static_cast<int>( - WASAPIAudioOutputStream::HardwareSampleRate()); + WASAPIAudioOutputStream::HardwareSampleRate(std::string())); EXPECT_GE(fs, 0); } diff --git a/chromium/media/audio/win/audio_manager_win.cc b/chromium/media/audio/win/audio_manager_win.cc index a753e554cb4..0352e6677d2 100644 --- a/chromium/media/audio/win/audio_manager_win.cc +++ b/chromium/media/audio/win/audio_manager_win.cc @@ -240,27 +240,44 @@ void AudioManagerWin::ShowAudioInputSettings() { base::LaunchProcess(command_line, base::LaunchOptions(), NULL); } -void AudioManagerWin::GetAudioInputDeviceNames( - media::AudioDeviceNames* device_names) { +void AudioManagerWin::GetAudioDeviceNamesImpl( + bool input, + AudioDeviceNames* device_names) { + DCHECK(device_names->empty()); DCHECK(enumeration_type() != kUninitializedEnumeration); // Enumerate all active audio-endpoint capture devices. if (enumeration_type() == kWaveEnumeration) { // Utilize the Wave API for Windows XP. - media::GetInputDeviceNamesWinXP(device_names); + if (input) + GetInputDeviceNamesWinXP(device_names); + else + GetOutputDeviceNamesWinXP(device_names); } else { // Utilize the MMDevice API (part of Core Audio) for Vista and higher. - media::GetInputDeviceNamesWin(device_names); + if (input) + GetInputDeviceNamesWin(device_names); + else + GetOutputDeviceNamesWin(device_names); } // Always add default device parameters as first element. if (!device_names->empty()) { - media::AudioDeviceName name; + AudioDeviceName name; name.device_name = AudioManagerBase::kDefaultDeviceName; name.unique_id = AudioManagerBase::kDefaultDeviceId; device_names->push_front(name); } } +void AudioManagerWin::GetAudioInputDeviceNames(AudioDeviceNames* device_names) { + GetAudioDeviceNamesImpl(true, device_names); +} + +void AudioManagerWin::GetAudioOutputDeviceNames( + AudioDeviceNames* device_names) { + GetAudioDeviceNamesImpl(false, device_names); +} + AudioParameters AudioManagerWin::GetInputStreamParameters( const std::string& device_id) { int sample_rate = 48000; @@ -280,6 +297,16 @@ AudioParameters AudioManagerWin::GetInputStreamParameters( sample_rate, 16, kFallbackBufferSize); } +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); +} + // Factory for the implementations of AudioOutputStream for AUDIO_PCM_LINEAR // mode. // - PCMWaveOutAudioOutputStream: Based on the waveOut API. @@ -291,7 +318,7 @@ AudioOutputStream* AudioManagerWin::MakeLinearOutputStream( return new PCMWaveOutAudioOutputStream(this, params, - media::NumberOfWaveOutBuffers(), + NumberOfWaveOutBuffers(), WAVE_MAPPER); } @@ -301,25 +328,31 @@ AudioOutputStream* AudioManagerWin::MakeLinearOutputStream( // - PCMWaveOutAudioOutputStream: Based on the waveOut API. // - WASAPIAudioOutputStream: Based on Core Audio (WASAPI) API. AudioOutputStream* AudioManagerWin::MakeLowLatencyOutputStream( - const AudioParameters& params, const std::string& input_device_id) { + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id) { DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); if (params.channels() > kWinMaxChannels) return NULL; if (!CoreAudioUtil::IsSupported()) { // Fall back to Windows Wave implementation on Windows XP or lower. + DLOG_IF(ERROR, !device_id.empty()) + << "Opening by device id not supported by PCMWaveOutAudioOutputStream"; DVLOG(1) << "Using WaveOut since WASAPI requires at least Vista."; return new PCMWaveOutAudioOutputStream( - this, params, media::NumberOfWaveOutBuffers(), WAVE_MAPPER); + this, params, NumberOfWaveOutBuffers(), WAVE_MAPPER); } - // TODO(crogers): support more than stereo input. + // TODO(rtoy): support more than stereo input. if (params.input_channels() > 0) { DVLOG(1) << "WASAPIUnifiedStream is created."; + DLOG_IF(ERROR, !device_id.empty()) + << "Opening by device id not supported by WASAPIUnifiedStream"; return new WASAPIUnifiedStream(this, params, input_device_id); } - return new WASAPIAudioOutputStream(this, params, eConsole); + return new WASAPIAudioOutputStream(this, device_id, params, eConsole); } // Factory for the implementations of AudioInputStream for AUDIO_PCM_LINEAR @@ -347,55 +380,68 @@ AudioInputStream* AudioManagerWin::MakeLowLatencyInputStream( return stream; } +std::string AudioManagerWin::GetDefaultOutputDeviceID() { + if (!CoreAudioUtil::IsSupported()) + return std::string(); + return CoreAudioUtil::GetDefaultOutputDeviceID(); +} + AudioParameters AudioManagerWin::GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) { + const bool core_audio_supported = CoreAudioUtil::IsSupported(); + DLOG_IF(ERROR, !core_audio_supported && !output_device_id.empty()) + << "CoreAudio is required to open non-default devices."; + const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; int sample_rate = 48000; int buffer_size = kFallbackBufferSize; int bits_per_sample = 16; int input_channels = 0; - bool use_input_params = !CoreAudioUtil::IsSupported(); - if (cmd_line->HasSwitch(switches::kEnableExclusiveAudio)) { - // TODO(crogers): tune these values for best possible WebAudio performance. - // WebRTC works well at 48kHz and a buffer size of 480 samples will be used - // for this case. Note that exclusive mode is experimental. - // This sample rate will be combined with a buffer size of 256 samples, - // which corresponds to an output delay of ~5.33ms. - sample_rate = 48000; - buffer_size = 256; - if (input_params.IsValid()) - channel_layout = input_params.channel_layout(); - } else if (!use_input_params) { - // Hardware sample-rate on Windows can be configured, so we must query. - // TODO(henrika): improve possibility to specify an audio endpoint. - // Use the default device (same as for Wave) for now to be compatible. - int hw_sample_rate = WASAPIAudioOutputStream::HardwareSampleRate(); - - AudioParameters params; - HRESULT hr = CoreAudioUtil::GetPreferredAudioParameters(eRender, eConsole, - ¶ms); - int hw_buffer_size = - FAILED(hr) ? kFallbackBufferSize : params.frames_per_buffer(); - channel_layout = WASAPIAudioOutputStream::HardwareChannelLayout(); - - // TODO(henrika): Figure out the right thing to do here. - if (hw_sample_rate && hw_buffer_size) { - sample_rate = hw_sample_rate; - buffer_size = hw_buffer_size; + bool use_input_params = !core_audio_supported; + if (core_audio_supported) { + if (cmd_line->HasSwitch(switches::kEnableExclusiveAudio)) { + // TODO(rtoy): tune these values for best possible WebAudio + // performance. WebRTC works well at 48kHz and a buffer size of 480 + // samples will be used for this case. Note that exclusive mode is + // experimental. This sample rate will be combined with a buffer size of + // 256 samples, which corresponds to an output delay of ~5.33ms. + sample_rate = 48000; + buffer_size = 256; + if (input_params.IsValid()) + channel_layout = input_params.channel_layout(); } else { - use_input_params = true; + AudioParameters params; + HRESULT hr = CoreAudioUtil::GetPreferredAudioParameters( + output_device_id.empty() ? + GetDefaultOutputDeviceID() : output_device_id, + ¶ms); + if (SUCCEEDED(hr)) { + bits_per_sample = params.bits_per_sample(); + buffer_size = params.frames_per_buffer(); + channel_layout = params.channel_layout(); + sample_rate = params.sample_rate(); + } else { + use_input_params = true; + } } } if (input_params.IsValid()) { - if (cmd_line->HasSwitch(switches::kTrySupportedChannelLayouts) && - CoreAudioUtil::IsSupported()) { + if (core_audio_supported && + cmd_line->HasSwitch(switches::kTrySupportedChannelLayouts)) { // Check if it is possible to open up at the specified input channel // layout but avoid checking if the specified layout is the same as the // hardware (preferred) layout. We do this extra check to avoid the // CoreAudioUtil::IsChannelLayoutSupported() overhead in most cases. if (input_params.channel_layout() != channel_layout) { + // TODO(henrika): Use |output_device_id| here. + // Internally, IsChannelLayoutSupported does many of the operations + // that have already been done such as opening up a client and fetching + // the WAVEFORMATPCMEX format. Ideally we should only do that once and + // do it for the requested device. Then here, we can check the layout + // from the data we already hold. if (CoreAudioUtil::IsChannelLayoutSupported( eRender, eConsole, input_params.channel_layout())) { // Open up using the same channel layout as the source if it is @@ -413,10 +459,10 @@ AudioParameters AudioManagerWin::GetPreferredOutputStreamParameters( // equal to the input values, AudioOutputResampler will skip resampling // and bit per sample differences (since the input parameters will match // the output parameters). - sample_rate = input_params.sample_rate(); bits_per_sample = input_params.bits_per_sample(); - channel_layout = input_params.channel_layout(); buffer_size = input_params.frames_per_buffer(); + channel_layout = input_params.channel_layout(); + sample_rate = input_params.sample_rate(); } } @@ -435,7 +481,7 @@ AudioInputStream* AudioManagerWin::CreatePCMWaveInAudioInputStream( std::string xp_device_id = device_id; if (device_id != AudioManagerBase::kDefaultDeviceId && enumeration_type_ == kMMDeviceEnumeration) { - xp_device_id = media::ConvertToWinXPDeviceId(device_id); + xp_device_id = ConvertToWinXPInputDeviceId(device_id); if (xp_device_id.empty()) { DLOG(ERROR) << "Cannot find a waveIn device which matches the device ID " << device_id; diff --git a/chromium/media/audio/win/audio_manager_win.h b/chromium/media/audio/win/audio_manager_win.h index 65cc73bbd6e..86e22badc5f 100644 --- a/chromium/media/audio/win/audio_manager_win.h +++ b/chromium/media/audio/win/audio_manager_win.h @@ -25,26 +25,33 @@ class MEDIA_EXPORT AudioManagerWin : public AudioManagerBase { virtual bool HasAudioInputDevices() OVERRIDE; virtual string16 GetAudioInputDeviceModel() 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; + virtual std::string GetAssociatedOutputDeviceID( + const std::string& input_device_id) OVERRIDE; // Implementation of AudioManagerBase. virtual AudioOutputStream* MakeLinearOutputStream( const AudioParameters& params) OVERRIDE; virtual AudioOutputStream* MakeLowLatencyOutputStream( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) OVERRIDE; virtual AudioInputStream* MakeLinearInputStream( const AudioParameters& params, const std::string& device_id) OVERRIDE; virtual AudioInputStream* MakeLowLatencyInputStream( const AudioParameters& params, const std::string& device_id) OVERRIDE; + virtual std::string GetDefaultOutputDeviceID() OVERRIDE; protected: virtual ~AudioManagerWin(); virtual AudioParameters GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) OVERRIDE; private: @@ -55,7 +62,7 @@ class MEDIA_EXPORT AudioManagerWin : public AudioManagerBase { }; // Allow unit test to modify the utilized enumeration API. - friend class AudioInputDeviceTest; + friend class AudioManagerTest; EnumerationType enumeration_type_; EnumerationType enumeration_type() { return enumeration_type_; } @@ -76,6 +83,8 @@ class MEDIA_EXPORT AudioManagerWin : public AudioManagerBase { void CreateDeviceListener(); void DestroyDeviceListener(); + void GetAudioDeviceNamesImpl(bool input, AudioDeviceNames* device_names); + // Listen for output device changes. scoped_ptr<AudioDeviceListenerWin> output_device_listener_; diff --git a/chromium/media/audio/win/audio_output_win_unittest.cc b/chromium/media/audio/win/audio_output_win_unittest.cc index 4e13d84f3d6..7ce146b0ab4 100644 --- a/chromium/media/audio/win/audio_output_win_unittest.cc +++ b/chromium/media/audio/win/audio_output_win_unittest.cc @@ -185,7 +185,7 @@ TEST(WinAudioTest, PCMWaveStreamGetAndClose) { AudioOutputStream* oas = audio_man->MakeAudioOutputStream( AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO, 8000, 16, 256), - std::string()); + std::string(), std::string()); ASSERT_TRUE(NULL != oas); oas->Close(); } @@ -201,29 +201,29 @@ TEST(WinAudioTest, SanityOnMakeParams) { AudioParameters::Format fmt = AudioParameters::AUDIO_PCM_LINEAR; EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( AudioParameters(fmt, CHANNEL_LAYOUT_UNSUPPORTED, 8000, 16, 256), - std::string())); + std::string(), std::string())); EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( AudioParameters(fmt, CHANNEL_LAYOUT_MONO, 1024 * 1024, 16, 256), - std::string())); + std::string(), std::string())); EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( AudioParameters(fmt, CHANNEL_LAYOUT_STEREO, 8000, 80, 256), - std::string())); + std::string(), std::string())); EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( AudioParameters(fmt, CHANNEL_LAYOUT_UNSUPPORTED, 8000, 16, 256), - std::string())); + std::string(), std::string())); EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( AudioParameters(fmt, CHANNEL_LAYOUT_STEREO, -8000, 16, 256), - std::string())); + std::string(), std::string())); EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( AudioParameters(fmt, CHANNEL_LAYOUT_MONO, 8000, 16, -100), - std::string())); + std::string(), std::string())); EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( AudioParameters(fmt, CHANNEL_LAYOUT_MONO, 8000, 16, 0), - std::string())); + std::string(), std::string())); EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( AudioParameters(fmt, CHANNEL_LAYOUT_MONO, 8000, 16, media::limits::kMaxSamplesPerPacket + 1), - std::string())); + std::string(), std::string())); } // Test that it can be opened and closed. @@ -237,7 +237,7 @@ TEST(WinAudioTest, PCMWaveStreamOpenAndClose) { AudioOutputStream* oas = audio_man->MakeAudioOutputStream( AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO, 8000, 16, 256), - std::string()); + std::string(), std::string()); ASSERT_TRUE(NULL != oas); EXPECT_TRUE(oas->Open()); oas->Close(); @@ -254,7 +254,7 @@ TEST(WinAudioTest, PCMWaveStreamOpenLimit) { AudioOutputStream* oas = audio_man->MakeAudioOutputStream( AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO, 8000, 16, 1024 * 1024 * 1024), - std::string()); + std::string(), std::string()); EXPECT_TRUE(NULL == oas); if (oas) oas->Close(); @@ -273,7 +273,7 @@ TEST(WinAudioTest, PCMWaveSlowSource) { AudioOutputStream* oas = audio_man->MakeAudioOutputStream( AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, 16000, 16, 256), - std::string()); + std::string(), std::string()); ASSERT_TRUE(NULL != oas); TestSourceLaggy test_laggy(2, 90); EXPECT_TRUE(oas->Open()); @@ -302,7 +302,7 @@ TEST(WinAudioTest, PCMWaveStreamPlaySlowLoop) { AudioOutputStream* oas = audio_man->MakeAudioOutputStream( AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, AudioParameters::kAudioCDSampleRate, 16, samples_100_ms), - std::string()); + std::string(), std::string()); ASSERT_TRUE(NULL != oas); SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate); @@ -333,7 +333,7 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzTone44Kss) { AudioOutputStream* oas = audio_man->MakeAudioOutputStream( AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, AudioParameters::kAudioCDSampleRate, 16, samples_100_ms), - std::string()); + std::string(), std::string()); ASSERT_TRUE(NULL != oas); SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate); @@ -362,7 +362,7 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzTone22Kss) { AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, AudioParameters::kAudioCDSampleRate / 2, 16, samples_100_ms), - std::string()); + std::string(), std::string()); ASSERT_TRUE(NULL != oas); SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate/2); @@ -402,7 +402,7 @@ TEST(WinAudioTest, PushSourceFile16KHz) { AudioOutputStream* oas = audio_man->MakeAudioOutputStream( AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, kSampleRate, 16, kSamples100ms), - std::string()); + std::string(), std::string()); ASSERT_TRUE(NULL != oas); EXPECT_TRUE(oas->Open()); @@ -439,7 +439,7 @@ TEST(WinAudioTest, PCMWaveStreamPlayTwice200HzTone44Kss) { AudioOutputStream* oas = audio_man->MakeAudioOutputStream( AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, AudioParameters::kAudioCDSampleRate, 16, samples_100_ms), - std::string()); + std::string(), std::string()); ASSERT_TRUE(NULL != oas); SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate); @@ -486,7 +486,7 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzToneLowLatency) { AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_MONO, sample_rate, 16, n * samples_10_ms), - std::string()); + std::string(), std::string()); ASSERT_TRUE(NULL != oas); SineWaveAudioSource source(1, 200, sample_rate); @@ -520,7 +520,7 @@ TEST(WinAudioTest, PCMWaveStreamPendingBytes) { AudioOutputStream* oas = audio_man->MakeAudioOutputStream( AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, AudioParameters::kAudioCDSampleRate, 16, samples_100_ms), - std::string()); + std::string(), std::string()); ASSERT_TRUE(NULL != oas); NiceMock<MockAudioSource> source; @@ -680,7 +680,7 @@ TEST(WinAudioTest, SyncSocketBasic) { AudioOutputStream* oas = audio_man->MakeAudioOutputStream(params, - std::string()); + std::string(), std::string()); ASSERT_TRUE(NULL != oas); ASSERT_TRUE(oas->Open()); diff --git a/chromium/media/audio/win/audio_unified_win_unittest.cc b/chromium/media/audio/win/audio_unified_win_unittest.cc index cfd17aea14f..011c36348b5 100644 --- a/chromium/media/audio/win/audio_unified_win_unittest.cc +++ b/chromium/media/audio/win/audio_unified_win_unittest.cc @@ -196,13 +196,13 @@ class AudioUnifiedStreamWrapper { // Creates an AudioOutputStream object using default parameters. WASAPIUnifiedStream* Create() { - return static_cast<WASAPIUnifiedStream*> (CreateOutputStream()); + return static_cast<WASAPIUnifiedStream*>(CreateOutputStream()); } // Creates an AudioOutputStream object using default parameters but a // specified input device. WASAPIUnifiedStream* Create(const std::string device_id) { - return static_cast<WASAPIUnifiedStream*> (CreateOutputStream(device_id)); + return static_cast<WASAPIUnifiedStream*>(CreateOutputStream(device_id)); } AudioParameters::Format format() const { return params_.format(); } @@ -223,20 +223,21 @@ class AudioUnifiedStreamWrapper { CoreAudioUtil::CreateDefaultDevice(eCapture, eConsole); AudioDeviceName name; EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDeviceName(audio_device, &name))); - const std::string& device_id = name.unique_id; - EXPECT_TRUE(CoreAudioUtil::DeviceIsDefault(eCapture, eConsole, device_id)); + const std::string& input_device_id = name.unique_id; + EXPECT_TRUE(CoreAudioUtil::DeviceIsDefault(eCapture, eConsole, + input_device_id)); // Create the unified audio I/O stream using the default input device. AudioOutputStream* aos = audio_man_->MakeAudioOutputStream(params_, - device_id); + "", input_device_id); EXPECT_TRUE(aos); return aos; } - AudioOutputStream* CreateOutputStream(const std::string& device_id) { + AudioOutputStream* CreateOutputStream(const std::string& input_device_id) { // Create the unified audio I/O stream using the specified input device. AudioOutputStream* aos = audio_man_->MakeAudioOutputStream(params_, - device_id); + "", input_device_id); EXPECT_TRUE(aos); return aos; } diff --git a/chromium/media/audio/win/core_audio_util_win.cc b/chromium/media/audio/win/core_audio_util_win.cc index 392184b7a01..4adfdda090a 100644 --- a/chromium/media/audio/win/core_audio_util_win.cc +++ b/chromium/media/audio/win/core_audio_util_win.cc @@ -4,8 +4,9 @@ #include "media/audio/win/core_audio_util_win.h" -#include <Audioclient.h> -#include <Functiondiscoverykeys_devpkey.h> +#include <audioclient.h> +#include <devicetopology.h> +#include <functiondiscoverykeys_devpkey.h> #include "base/command_line.h" #include "base/logging.h" @@ -122,7 +123,7 @@ static std::ostream& operator<<(std::ostream& os, return os; } -bool LoadAudiosesDll() { +static bool LoadAudiosesDll() { static const wchar_t* const kAudiosesDLL = L"%WINDIR%\\system32\\audioses.dll"; @@ -131,7 +132,7 @@ bool LoadAudiosesDll() { return (LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) != NULL); } -bool CanCreateDeviceEnumerator() { +static bool CanCreateDeviceEnumerator() { ScopedComPtr<IMMDeviceEnumerator> device_enumerator; HRESULT hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER); @@ -143,6 +144,14 @@ bool CanCreateDeviceEnumerator() { return SUCCEEDED(hr); } +static std::string GetDeviceID(IMMDevice* device) { + ScopedCoMem<WCHAR> device_id_com; + std::string device_id; + if (SUCCEEDED(device->GetId(&device_id_com))) + WideToUTF8(device_id_com, wcslen(device_id_com), &device_id); + return device_id; +} + bool CoreAudioUtil::IsSupported() { // It is possible to force usage of WaveXxx APIs by using a command line flag. const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); @@ -262,6 +271,12 @@ ScopedComPtr<IMMDevice> CoreAudioUtil::CreateDefaultDevice(EDataFlow data_flow, return endpoint_device; } +std::string CoreAudioUtil::GetDefaultOutputDeviceID() { + DCHECK(IsSupported()); + ScopedComPtr<IMMDevice> device(CreateDefaultDevice(eRender, eConsole)); + return device ? GetDeviceID(device) : std::string(); +} + ScopedComPtr<IMMDevice> CoreAudioUtil::CreateDevice( const std::string& device_id) { DCHECK(IsSupported()); @@ -288,17 +303,14 @@ HRESULT CoreAudioUtil::GetDeviceName(IMMDevice* device, AudioDeviceName* name) { // Retrieve unique name of endpoint device. // Example: "{0.0.1.00000000}.{8db6020f-18e3-4f25-b6f5-7726c9122574}". AudioDeviceName device_name; - ScopedCoMem<WCHAR> endpoint_device_id; - HRESULT hr = device->GetId(&endpoint_device_id); - if (FAILED(hr)) - return hr; - WideToUTF8(endpoint_device_id, wcslen(endpoint_device_id), - &device_name.unique_id); + device_name.unique_id = GetDeviceID(device); + if (device_name.unique_id.empty()) + return E_FAIL; // Retrieve user-friendly name of endpoint device. // Example: "Microphone (Realtek High Definition Audio)". ScopedComPtr<IPropertyStore> properties; - hr = device->OpenPropertyStore(STGM_READ, properties.Receive()); + HRESULT hr = device->OpenPropertyStore(STGM_READ, properties.Receive()); if (FAILED(hr)) return hr; base::win::ScopedPropVariant friendly_name; @@ -317,6 +329,88 @@ HRESULT CoreAudioUtil::GetDeviceName(IMMDevice* device, AudioDeviceName* name) { return hr; } +std::string CoreAudioUtil::GetAudioControllerID(IMMDevice* device, + IMMDeviceEnumerator* enumerator) { + DCHECK(IsSupported()); + + // Fetching the controller device id could be as simple as fetching the value + // of the "{B3F8FA53-0004-438E-9003-51A46E139BFC},2" property in the property + // store of the |device|, but that key isn't defined in any header and + // according to MS should not be relied upon. + // So, instead, we go deeper, look at the device topology and fetch the + // PKEY_Device_InstanceId of the associated physical audio device. + ScopedComPtr<IDeviceTopology> topology; + ScopedComPtr<IConnector> connector; + ScopedCoMem<WCHAR> filter_id; + if (FAILED(device->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, + topology.ReceiveVoid()) || + // For our purposes checking the first connected device should be enough + // and if there are cases where there are more than one device connected + // we're not sure how to handle that anyway. So we pass 0. + FAILED(topology->GetConnector(0, connector.Receive())) || + FAILED(connector->GetDeviceIdConnectedTo(&filter_id)))) { + DLOG(ERROR) << "Failed to get the device identifier of the audio device"; + return std::string(); + } + + // Now look at the properties of the connected device node and fetch the + // instance id (PKEY_Device_InstanceId) of the device node that uniquely + // identifies the controller. + ScopedComPtr<IMMDevice> device_node; + ScopedComPtr<IPropertyStore> properties; + base::win::ScopedPropVariant instance_id; + if (FAILED(enumerator->GetDevice(filter_id, device_node.Receive())) || + FAILED(device_node->OpenPropertyStore(STGM_READ, properties.Receive())) || + FAILED(properties->GetValue(PKEY_Device_InstanceId, + instance_id.Receive())) || + instance_id.get().vt != VT_LPWSTR) { + DLOG(ERROR) << "Failed to get instance id of the audio device node"; + return std::string(); + } + + std::string controller_id; + WideToUTF8(instance_id.get().pwszVal, + wcslen(instance_id.get().pwszVal), + &controller_id); + + return controller_id; +} + +std::string CoreAudioUtil::GetMatchingOutputDeviceID( + const std::string& input_device_id) { + ScopedComPtr<IMMDevice> input_device(CreateDevice(input_device_id)); + if (!input_device) + return std::string(); + + // See if we can get id of the associated controller. + ScopedComPtr<IMMDeviceEnumerator> enumerator(CreateDeviceEnumerator()); + std::string controller_id(GetAudioControllerID(input_device, enumerator)); + if (controller_id.empty()) + return std::string(); + + // Now enumerate the available (and active) output devices and see if any of + // them is associated with the same controller. + ScopedComPtr<IMMDeviceCollection> collection; + enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, + collection.Receive()); + if (!collection) + return std::string(); + + UINT count = 0; + collection->GetCount(&count); + ScopedComPtr<IMMDevice> output_device; + for (UINT i = 0; i < count; ++i) { + collection->Item(i, output_device.Receive()); + std::string output_controller_id(CoreAudioUtil::GetAudioControllerID( + output_device, enumerator)); + if (output_controller_id == controller_id) + break; + output_device = NULL; + } + + return output_device ? GetDeviceID(output_device) : std::string(); +} + std::string CoreAudioUtil::GetFriendlyName(const std::string& device_id) { DCHECK(IsSupported()); ScopedComPtr<IMMDevice> audio_device = CreateDevice(device_id); @@ -339,16 +433,8 @@ bool CoreAudioUtil::DeviceIsDefault(EDataFlow flow, if (!device) return false; - ScopedCoMem<WCHAR> default_device_id; - HRESULT hr = device->GetId(&default_device_id); - if (FAILED(hr)) - return false; - - std::string str_default; - WideToUTF8(default_device_id, wcslen(default_device_id), &str_default); - if (device_id.compare(str_default) != 0) - return false; - return true; + std::string str_default(GetDeviceID(device)); + return device_id.compare(str_default) == 0; } EDataFlow CoreAudioUtil::GetDataFlow(IMMDevice* device) { diff --git a/chromium/media/audio/win/core_audio_util_win.h b/chromium/media/audio/win/core_audio_util_win.h index 3b2734570d0..cdf6dfb11df 100644 --- a/chromium/media/audio/win/core_audio_util_win.h +++ b/chromium/media/audio/win/core_audio_util_win.h @@ -59,6 +59,10 @@ class MEDIA_EXPORT CoreAudioUtil { static ScopedComPtr<IMMDevice> CreateDefaultDevice( EDataFlow data_flow, ERole role); + // Returns the device id of the default output device or an empty string + // if no such device exists or if the default device has been disabled. + static std::string GetDefaultOutputDeviceID(); + // Creates an endpoint device that is specified by a unique endpoint device- // identification string. static ScopedComPtr<IMMDevice> CreateDevice(const std::string& device_id); @@ -68,6 +72,24 @@ class MEDIA_EXPORT CoreAudioUtil { // "Microphone (Realtek High Definition Audio)". static HRESULT GetDeviceName(IMMDevice* device, AudioDeviceName* name); + // Returns the device ID/path of the controller (a.k.a. physical device that + // |device| is connected to. This ID will be the same for all devices from + // the same controller so it is useful for doing things like determining + // whether a set of output and input devices belong to the same controller. + // The device enumerator is required as well as the device itself since + // looking at the device topology is required and we need to open up + // associated devices to determine the controller id. + // If the ID could not be determined for some reason, an empty string is + // returned. + static std::string GetAudioControllerID(IMMDevice* device, + IMMDeviceEnumerator* enumerator); + + // Accepts an id of an input device and finds a matching output device id. + // If the associated hardware does not have an audio output device (e.g. + // a webcam with a mic), an empty string is returned. + static std::string GetMatchingOutputDeviceID( + const std::string& input_device_id); + // Gets the user-friendly name of the endpoint device which is represented // by a unique id in |device_id|. static std::string GetFriendlyName(const std::string& device_id); diff --git a/chromium/media/audio/win/core_audio_util_win_unittest.cc b/chromium/media/audio/win/core_audio_util_win_unittest.cc index 6d3e1fcf093..abef8682020 100644 --- a/chromium/media/audio/win/core_audio_util_win_unittest.cc +++ b/chromium/media/audio/win/core_audio_util_win_unittest.cc @@ -3,7 +3,9 @@ // found in the LICENSE file. #include "base/memory/scoped_ptr.h" +#include "base/strings/utf_string_conversions.h" #include "base/synchronization/waitable_event.h" +#include "base/win/scoped_co_mem.h" #include "base/win/scoped_com_initializer.h" #include "base/win/scoped_handle.h" #include "media/audio/win/core_audio_util_win.h" @@ -140,6 +142,33 @@ TEST_F(CoreAudioUtilWinTest, GetDefaultDeviceName) { } } +TEST_F(CoreAudioUtilWinTest, GetAudioControllerID) { + if (!CanRunAudioTest()) + return; + + ScopedComPtr<IMMDeviceEnumerator> enumerator( + CoreAudioUtil::CreateDeviceEnumerator()); + ASSERT_TRUE(enumerator); + + // Enumerate all active input and output devices and fetch the ID of + // the associated device. + EDataFlow flows[] = { eRender , eCapture }; + for (int i = 0; i < arraysize(flows); ++i) { + ScopedComPtr<IMMDeviceCollection> collection; + ASSERT_TRUE(SUCCEEDED(enumerator->EnumAudioEndpoints(flows[i], + DEVICE_STATE_ACTIVE, collection.Receive()))); + UINT count = 0; + collection->GetCount(&count); + for (UINT j = 0; j < count; ++j) { + ScopedComPtr<IMMDevice> device; + collection->Item(j, device.Receive()); + std::string controller_id(CoreAudioUtil::GetAudioControllerID( + device, enumerator)); + EXPECT_FALSE(controller_id.empty()); + } + } +} + TEST_F(CoreAudioUtilWinTest, GetFriendlyName) { if (!CanRunAudioTest()) return; @@ -448,6 +477,46 @@ TEST_F(CoreAudioUtilWinTest, FillRenderEndpointBufferWithSilence) { EXPECT_EQ(num_queued_frames, endpoint_buffer_size); } -// +// This test can only succeed on a machine that has audio hardware +// that has both input and output devices. Currently this is the case +// with our test bots and the CanRunAudioTest() method should make sure +// that the test won't run in unsupported environments, but be warned. +TEST_F(CoreAudioUtilWinTest, GetMatchingOutputDeviceID) { + if (!CanRunAudioTest()) + return; + + bool found_a_pair = false; + + ScopedComPtr<IMMDeviceEnumerator> enumerator( + CoreAudioUtil::CreateDeviceEnumerator()); + ASSERT_TRUE(enumerator); + + // Enumerate all active input and output devices and fetch the ID of + // the associated device. + ScopedComPtr<IMMDeviceCollection> collection; + ASSERT_TRUE(SUCCEEDED(enumerator->EnumAudioEndpoints(eCapture, + DEVICE_STATE_ACTIVE, collection.Receive()))); + UINT count = 0; + collection->GetCount(&count); + for (UINT i = 0; i < count && !found_a_pair; ++i) { + ScopedComPtr<IMMDevice> device; + collection->Item(i, device.Receive()); + base::win::ScopedCoMem<WCHAR> wide_id; + device->GetId(&wide_id); + std::string id; + WideToUTF8(wide_id, wcslen(wide_id), &id); + found_a_pair = !CoreAudioUtil::GetMatchingOutputDeviceID(id).empty(); + } + + EXPECT_TRUE(found_a_pair); +} + +TEST_F(CoreAudioUtilWinTest, GetDefaultOutputDeviceID) { + if (!CanRunAudioTest()) + return; + + std::string default_device_id(CoreAudioUtil::GetDefaultOutputDeviceID()); + EXPECT_FALSE(default_device_id.empty()); +} } // namespace media diff --git a/chromium/media/audio/win/device_enumeration_win.cc b/chromium/media/audio/win/device_enumeration_win.cc index 36ed2913ffe..aa66afb12b1 100644 --- a/chromium/media/audio/win/device_enumeration_win.cc +++ b/chromium/media/audio/win/device_enumeration_win.cc @@ -8,13 +8,13 @@ #include "media/audio/win/audio_manager_win.h" +#include "base/basictypes.h" #include "base/logging.h" #include "base/strings/utf_string_conversions.h" #include "base/win/scoped_co_mem.h" #include "base/win/scoped_comptr.h" #include "base/win/scoped_propvariant.h" -using media::AudioDeviceNames; using base::win::ScopedComPtr; using base::win::ScopedCoMem; @@ -25,7 +25,8 @@ using base::win::ScopedCoMem; namespace media { -bool GetInputDeviceNamesWin(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; @@ -37,24 +38,24 @@ bool GetInputDeviceNamesWin(AudioDeviceNames* device_names) { return false; } - // Generate a collection of active audio capture endpoint devices. + // Generate a collection of active audio endpoint devices. // This method will succeed even if all devices are disabled. ScopedComPtr<IMMDeviceCollection> collection; - hr = enumerator->EnumAudioEndpoints(eCapture, + hr = enumerator->EnumAudioEndpoints(data_flow, DEVICE_STATE_ACTIVE, collection.Receive()); if (FAILED(hr)) return false; - // Retrieve the number of active capture devices. + // Retrieve the number of active devices. UINT number_of_active_devices = 0; collection->GetCount(&number_of_active_devices); if (number_of_active_devices == 0) return true; - media::AudioDeviceName device; + AudioDeviceName device; - // Loop over all active capture devices and add friendly name and + // Loop over all active devices and add friendly name and // unique ID to the |device_names| list. for (UINT i = 0; i < number_of_active_devices; ++i) { // Retrieve unique name of endpoint device. @@ -92,14 +93,22 @@ bool GetInputDeviceNamesWin(AudioDeviceNames* device_names) { return true; } -bool GetInputDeviceNamesWinXP(AudioDeviceNames* device_names) { +// The waveform API is weird in that it has completely separate but +// almost identical functions and structs for input devices vs. output +// devices. We deal with this by implementing the logic as a templated +// function that takes the functions and struct type to use as +// template parameters. +template <UINT (__stdcall *NumDevsFunc)(), + typename CAPSSTRUCT, + MMRESULT (__stdcall *DevCapsFunc)(UINT_PTR, CAPSSTRUCT*, UINT)> +static bool GetDeviceNamesWinXPImpl(AudioDeviceNames* device_names) { // Retrieve the number of active waveform input devices. - UINT number_of_active_devices = waveInGetNumDevs(); + UINT number_of_active_devices = NumDevsFunc(); if (number_of_active_devices == 0) return true; - media::AudioDeviceName device; - WAVEINCAPS capabilities; + AudioDeviceName device; + CAPSSTRUCT capabilities; MMRESULT err = MMSYSERR_NOERROR; // Loop over all active capture devices and add friendly name and @@ -108,7 +117,7 @@ bool GetInputDeviceNamesWinXP(AudioDeviceNames* device_names) { // there is no safe method to retrieve a unique device name on XP. for (UINT i = 0; i < number_of_active_devices; ++i) { // Retrieve the capabilities of the specified waveform-audio input device. - err = waveInGetDevCaps(i, &capabilities, sizeof(capabilities)); + err = DevCapsFunc(i, &capabilities, sizeof(capabilities)); if (err != MMSYSERR_NOERROR) continue; @@ -118,7 +127,7 @@ bool GetInputDeviceNamesWinXP(AudioDeviceNames* device_names) { device.device_name = WideToUTF8(capabilities.szPname); // Store the "unique" name (we use same as friendly name on Windows XP). - device.unique_id = WideToUTF8(capabilities.szPname); + device.unique_id = device.device_name; // Add combination of user-friendly and unique name to the output list. device_names->push_back(device); @@ -127,7 +136,25 @@ bool GetInputDeviceNamesWinXP(AudioDeviceNames* device_names) { return true; } -std::string ConvertToWinXPDeviceId(const std::string& device_id) { +bool GetInputDeviceNamesWin(AudioDeviceNames* device_names) { + return GetDeviceNamesWinImpl(eCapture, device_names); +} + +bool GetOutputDeviceNamesWin(AudioDeviceNames* device_names) { + return GetDeviceNamesWinImpl(eRender, device_names); +} + +bool GetInputDeviceNamesWinXP(AudioDeviceNames* device_names) { + return GetDeviceNamesWinXPImpl< + waveInGetNumDevs, WAVEINCAPSW, waveInGetDevCapsW>(device_names); +} + +bool GetOutputDeviceNamesWinXP(AudioDeviceNames* device_names) { + return GetDeviceNamesWinXPImpl< + waveOutGetNumDevs, WAVEOUTCAPSW, waveOutGetDevCapsW>(device_names); +} + +std::string ConvertToWinXPInputDeviceId(const std::string& device_id) { UINT number_of_active_devices = waveInGetNumDevs(); MMRESULT result = MMSYSERR_NOERROR; diff --git a/chromium/media/audio/win/device_enumeration_win.h b/chromium/media/audio/win/device_enumeration_win.h index 3d44670a6d3..e61a331842a 100644 --- a/chromium/media/audio/win/device_enumeration_win.h +++ b/chromium/media/audio/win/device_enumeration_win.h @@ -11,28 +11,32 @@ namespace media { -// Returns a list of audio input device structures (name and unique device ID) -// using the MMDevice API which is supported on Windows Vista and higher. +// Returns a list of audio input or output device structures (name and +// unique device ID) using the MMDevice API which is supported on +// Windows Vista and higher. // Example record in the output list: // - device_name: "Microphone (Realtek High Definition Audio)". // - unique_id: "{0.0.1.00000000}.{8db6020f-18e3-4f25-b6f5-7726c9122574}" // This method must be called from a COM thread using MTA. bool GetInputDeviceNamesWin(media::AudioDeviceNames* device_names); +bool GetOutputDeviceNamesWin(media::AudioDeviceNames* device_names); -// Returns a list of audio input device structures (name and unique device ID) -// using the WaveIn API which is supported on Windows XP and higher. +// Returns a list of audio input or output device structures (name and +// unique device ID) using the WaveIn API which is supported on +// Windows XP and higher. // Example record in the output list: // - device_name: "Microphone (Realtek High Defini". // - unique_id: "Microphone (Realtek High Defini" (same as friendly name). bool GetInputDeviceNamesWinXP(media::AudioDeviceNames* device_names); +bool GetOutputDeviceNamesWinXP(media::AudioDeviceNames* device_names); -// Converts a device ID generated by |GetInputDeviceNamesWin()| to the +// Converts an input device ID generated by |GetInputDeviceNamesWin()| to the // corresponding ID by |GetInputDeviceNamesWinXP()|. Returns an empty string on // failure. // Example input and output: // - input ID: "{0.0.1.00000000}.{8db6020f-18e3-4f25-b6f5-7726c9122574}" // - output ID: "Microphone (Realtek High Defini" -std::string ConvertToWinXPDeviceId(const std::string& device_id); +std::string ConvertToWinXPInputDeviceId(const std::string& device_id); } // namespace media diff --git a/chromium/media/audio/win/wavein_input_win.h b/chromium/media/audio/win/wavein_input_win.h index 4b830e34805..df5ce4d129b 100644 --- a/chromium/media/audio/win/wavein_input_win.h +++ b/chromium/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. |