diff options
author | Andras Becsi <andras.becsi@digia.com> | 2014-03-18 13:16:26 +0100 |
---|---|---|
committer | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2014-03-20 15:55:39 +0100 |
commit | 3f0f86b0caed75241fa71c95a5d73bc0164348c5 (patch) | |
tree | 92b9fb00f2e9e90b0be2262093876d4f43b6cd13 /chromium/media/audio | |
parent | e90d7c4b152c56919d963987e2503f9909a666d2 (diff) | |
download | qtwebengine-chromium-3f0f86b0caed75241fa71c95a5d73bc0164348c5.tar.gz |
Update to new stable branch 1750
This also includes an updated ninja and chromium dependencies
needed on Windows.
Change-Id: Icd597d80ed3fa4425933c9f1334c3c2e31291c42
Reviewed-by: Zoltan Arvai <zarvai@inf.u-szeged.hu>
Reviewed-by: Zeno Albisser <zeno.albisser@digia.com>
Diffstat (limited to 'chromium/media/audio')
108 files changed, 3548 insertions, 1852 deletions
diff --git a/chromium/media/audio/linux/alsa_input.cc b/chromium/media/audio/alsa/alsa_input.cc index 929cbe79063..9dcbf2b8662 100644 --- a/chromium/media/audio/linux/alsa_input.cc +++ b/chromium/media/audio/alsa/alsa_input.cc @@ -1,19 +1,19 @@ -// 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. -#include "media/audio/linux/alsa_input.h" +#include "media/audio/alsa/alsa_input.h" #include "base/basictypes.h" #include "base/bind.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/time/time.h" +#include "media/audio/alsa/alsa_output.h" +#include "media/audio/alsa/alsa_util.h" +#include "media/audio/alsa/alsa_wrapper.h" +#include "media/audio/alsa/audio_manager_alsa.h" #include "media/audio/audio_manager.h" -#include "media/audio/linux/alsa_output.h" -#include "media/audio/linux/alsa_util.h" -#include "media/audio/linux/alsa_wrapper.h" -#include "media/audio/linux/audio_manager_linux.h" namespace media { @@ -24,7 +24,7 @@ static const char kDefaultDevice2[] = "plug:default"; const char AlsaPcmInputStream::kAutoSelectDevice[] = ""; -AlsaPcmInputStream::AlsaPcmInputStream(AudioManagerLinux* audio_manager, +AlsaPcmInputStream::AlsaPcmInputStream(AudioManagerBase* audio_manager, const std::string& device_name, const AudioParameters& params, AlsaWrapper* wrapper) diff --git a/chromium/media/audio/linux/alsa_input.h b/chromium/media/audio/alsa/alsa_input.h index 888e4780ac0..6e9aad9056e 100644 --- a/chromium/media/audio/linux/alsa_input.h +++ b/chromium/media/audio/alsa/alsa_input.h @@ -1,9 +1,9 @@ -// 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. -#ifndef MEDIA_AUDIO_LINUX_ALSA_INPUT_H_ -#define MEDIA_AUDIO_LINUX_ALSA_INPUT_H_ +#ifndef MEDIA_AUDIO_ALSA_ALSA_INPUT_H_ +#define MEDIA_AUDIO_ALSA_ALSA_INPUT_H_ #include <alsa/asoundlib.h> @@ -20,7 +20,7 @@ namespace media { class AlsaWrapper; -class AudioManagerLinux; +class AudioManagerBase; // Provides an input stream for audio capture based on the ALSA PCM interface. // This object is not thread safe and all methods should be invoked in the @@ -34,7 +34,7 @@ class AlsaPcmInputStream : public AgcAudioStream<AudioInputStream> { // Create a PCM Output stream for the ALSA device identified by // |device_name|. If unsure of what to use for |device_name|, use // |kAutoSelectDevice|. - AlsaPcmInputStream(AudioManagerLinux* audio_manager, + AlsaPcmInputStream(AudioManagerBase* audio_manager, const std::string& device_name, const AudioParameters& params, AlsaWrapper* wrapper); @@ -69,7 +69,7 @@ class AlsaPcmInputStream : public AgcAudioStream<AudioInputStream> { // want circular references. Additionally, stream objects live on the audio // thread, which is owned by the audio manager and we don't want to addref // the manager from that thread. - AudioManagerLinux* audio_manager_; + AudioManagerBase* audio_manager_; std::string device_name_; AudioParameters params_; int bytes_per_buffer_; @@ -89,4 +89,4 @@ class AlsaPcmInputStream : public AgcAudioStream<AudioInputStream> { } // namespace media -#endif // MEDIA_AUDIO_LINUX_ALSA_INPUT_H_ +#endif // MEDIA_AUDIO_ALSA_ALSA_INPUT_H_ diff --git a/chromium/media/audio/linux/alsa_output.cc b/chromium/media/audio/alsa/alsa_output.cc index fa838354b5a..eccf8ee28a8 100644 --- a/chromium/media/audio/linux/alsa_output.cc +++ b/chromium/media/audio/alsa/alsa_output.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. // @@ -32,7 +32,7 @@ // view, it will seem that the device has just clogged and stopped requesting // data. -#include "media/audio/linux/alsa_output.h" +#include "media/audio/alsa/alsa_output.h" #include <algorithm> @@ -42,9 +42,9 @@ #include "base/message_loop/message_loop.h" #include "base/stl_util.h" #include "base/time/time.h" -#include "media/audio/linux/alsa_util.h" -#include "media/audio/linux/alsa_wrapper.h" -#include "media/audio/linux/audio_manager_linux.h" +#include "media/audio/alsa/alsa_util.h" +#include "media/audio/alsa/alsa_wrapper.h" +#include "media/audio/alsa/audio_manager_alsa.h" #include "media/base/channel_mixer.h" #include "media/base/data_buffer.h" #include "media/base/seekable_buffer.h" @@ -134,7 +134,7 @@ const uint32 AlsaPcmOutputStream::kMinLatencyMicros = 40 * 1000; AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name, const AudioParameters& params, AlsaWrapper* wrapper, - AudioManagerLinux* manager) + AudioManagerBase* manager) : requested_device_name_(device_name), pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample())), channels_(params.channels()), diff --git a/chromium/media/audio/linux/alsa_output.h b/chromium/media/audio/alsa/alsa_output.h index 841615d9d3f..65a23f75124 100644 --- a/chromium/media/audio/linux/alsa_output.h +++ b/chromium/media/audio/alsa/alsa_output.h @@ -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. // @@ -18,8 +18,8 @@ // the audio thread. When modifying the code in this class, please read the // threading assumptions at the top of the implementation. -#ifndef MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_ -#define MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_ +#ifndef MEDIA_AUDIO_ALSA_ALSA_OUTPUT_H_ +#define MEDIA_AUDIO_ALSA_ALSA_OUTPUT_H_ #include <alsa/asoundlib.h> @@ -40,7 +40,7 @@ class MessageLoop; namespace media { class AlsaWrapper; -class AudioManagerLinux; +class AudioManagerBase; class ChannelMixer; class SeekableBuffer; @@ -70,7 +70,7 @@ class MEDIA_EXPORT AlsaPcmOutputStream : public AudioOutputStream { AlsaPcmOutputStream(const std::string& device_name, const AudioParameters& params, AlsaWrapper* wrapper, - AudioManagerLinux* manager); + AudioManagerBase* manager); virtual ~AlsaPcmOutputStream(); @@ -187,7 +187,7 @@ class MEDIA_EXPORT AlsaPcmOutputStream : public AudioOutputStream { AlsaWrapper* wrapper_; // Audio manager that created us. Used to report that we've been closed. - AudioManagerLinux* manager_; + AudioManagerBase* manager_; // Message loop to use for polling. The object is owned by the AudioManager. // We hold a reference to the audio thread message loop since @@ -225,4 +225,4 @@ MEDIA_EXPORT std::ostream& operator<<(std::ostream& os, }; // namespace media -#endif // MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_ +#endif // MEDIA_AUDIO_ALSA_ALSA_OUTPUT_H_ diff --git a/chromium/media/audio/linux/alsa_output_unittest.cc b/chromium/media/audio/alsa/alsa_output_unittest.cc index 82fbab94c19..99ae8b02e0a 100644 --- a/chromium/media/audio/linux/alsa_output_unittest.cc +++ b/chromium/media/audio/alsa/alsa_output_unittest.cc @@ -1,12 +1,13 @@ -// 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. #include "base/message_loop/message_loop.h" #include "base/strings/stringprintf.h" -#include "media/audio/linux/alsa_output.h" -#include "media/audio/linux/alsa_wrapper.h" -#include "media/audio/linux/audio_manager_linux.h" +#include "media/audio/alsa/alsa_output.h" +#include "media/audio/alsa/alsa_wrapper.h" +#include "media/audio/alsa/audio_manager_alsa.h" +#include "media/audio/fake_audio_log_factory.h" #include "media/base/data_buffer.h" #include "media/base/seekable_buffer.h" #include "testing/gmock/include/gmock/gmock.h" @@ -76,8 +77,9 @@ class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback { MOCK_METHOD1(OnError, void(AudioOutputStream* stream)); }; -class MockAudioManagerLinux : public AudioManagerLinux { +class MockAudioManagerAlsa : public AudioManagerAlsa { public: + MockAudioManagerAlsa() : AudioManagerAlsa(&fake_audio_log_factory_) {} MOCK_METHOD0(Init, void()); MOCK_METHOD0(HasAudioOutputDevices, bool()); MOCK_METHOD0(HasAudioInputDevices, bool()); @@ -104,12 +106,15 @@ class MockAudioManagerLinux : public AudioManagerLinux { virtual scoped_refptr<base::MessageLoopProxy> GetMessageLoop() OVERRIDE { return base::MessageLoop::current()->message_loop_proxy(); } + + private: + FakeAudioLogFactory fake_audio_log_factory_; }; class AlsaPcmOutputStreamTest : public testing::Test { protected: AlsaPcmOutputStreamTest() { - mock_manager_.reset(new StrictMock<MockAudioManagerLinux>()); + mock_manager_.reset(new StrictMock<MockAudioManagerAlsa>()); } virtual ~AlsaPcmOutputStreamTest() { @@ -171,7 +176,7 @@ class AlsaPcmOutputStreamTest : public testing::Test { static void* kFakeHints[]; StrictMock<MockAlsaWrapper> mock_alsa_wrapper_; - scoped_ptr<StrictMock<MockAudioManagerLinux> > mock_manager_; + scoped_ptr<StrictMock<MockAudioManagerAlsa> > mock_manager_; base::MessageLoop message_loop_; scoped_refptr<media::DataBuffer> packet_; diff --git a/chromium/media/audio/linux/alsa_util.cc b/chromium/media/audio/alsa/alsa_util.cc index 176ef697741..f26cbd30f2f 100644 --- a/chromium/media/audio/linux/alsa_util.cc +++ b/chromium/media/audio/alsa/alsa_util.cc @@ -1,13 +1,13 @@ -// 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. -#include "media/audio/linux/alsa_util.h" +#include "media/audio/alsa/alsa_util.h" #include <string> #include "base/logging.h" -#include "media/audio/linux/alsa_wrapper.h" +#include "media/audio/alsa/alsa_wrapper.h" namespace alsa_util { diff --git a/chromium/media/audio/linux/alsa_util.h b/chromium/media/audio/alsa/alsa_util.h index 53cf80af84c..a23ab317dd7 100644 --- a/chromium/media/audio/linux/alsa_util.h +++ b/chromium/media/audio/alsa/alsa_util.h @@ -1,9 +1,9 @@ -// 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. -#ifndef MEDIA_AUDIO_LINUX_ALSA_UTIL_H_ -#define MEDIA_AUDIO_LINUX_ALSA_UTIL_H_ +#ifndef MEDIA_AUDIO_ALSA_ALSA_UTIL_H_ +#define MEDIA_AUDIO_ALSA_ALSA_UTIL_H_ #include <alsa/asoundlib.h> #include <string> @@ -44,4 +44,4 @@ snd_mixer_elem_t* LoadCaptureMixerElement(media::AlsaWrapper* wrapper, } // namespace alsa_util -#endif // MEDIA_AUDIO_LINUX_ALSA_UTIL_H_ +#endif // MEDIA_AUDIO_ALSA_ALSA_UTIL_H_ diff --git a/chromium/media/audio/linux/alsa_wrapper.cc b/chromium/media/audio/alsa/alsa_wrapper.cc index c1ce359b303..969f3c499f4 100644 --- a/chromium/media/audio/linux/alsa_wrapper.cc +++ b/chromium/media/audio/alsa/alsa_wrapper.cc @@ -1,8 +1,8 @@ -// 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. -#include "media/audio/linux/alsa_wrapper.h" +#include "media/audio/alsa/alsa_wrapper.h" #include <alsa/asoundlib.h> diff --git a/chromium/media/audio/linux/alsa_wrapper.h b/chromium/media/audio/alsa/alsa_wrapper.h index 30d94635dd5..4b3c295e582 100644 --- a/chromium/media/audio/linux/alsa_wrapper.h +++ b/chromium/media/audio/alsa/alsa_wrapper.h @@ -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. // @@ -6,6 +6,9 @@ // we want to use. It's purpose is to allow injection of a mock so that the // higher level code is testable. +#ifndef MEDIA_AUDIO_ALSA_ALSA_WRAPPER_H_ +#define MEDIA_AUDIO_ALSA_ALSA_WRAPPER_H_ + #include <alsa/asoundlib.h> #include "base/basictypes.h" @@ -79,3 +82,5 @@ class MEDIA_EXPORT AlsaWrapper { }; } // namespace media + +#endif // MEDIA_AUDIO_ALSA_ALSA_WRAPPER_H_ diff --git a/chromium/media/audio/alsa/audio_manager_alsa.cc b/chromium/media/audio/alsa/audio_manager_alsa.cc new file mode 100644 index 00000000000..ac61a5fa974 --- /dev/null +++ b/chromium/media/audio/alsa/audio_manager_alsa.cc @@ -0,0 +1,362 @@ +// 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/alsa/audio_manager_alsa.h" + +#include "base/command_line.h" +#include "base/environment.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/metrics/histogram.h" +#include "base/nix/xdg_util.h" +#include "base/process/launch.h" +#include "base/stl_util.h" +#include "media/audio/audio_output_dispatcher.h" +#include "media/audio/audio_parameters.h" +#if defined(USE_CRAS) +#include "media/audio/cras/audio_manager_cras.h" +#endif +#include "media/audio/alsa/alsa_input.h" +#include "media/audio/alsa/alsa_output.h" +#include "media/audio/alsa/alsa_wrapper.h" +#if defined(USE_PULSEAUDIO) +#include "media/audio/pulse/audio_manager_pulse.h" +#endif +#include "media/base/channel_layout.h" +#include "media/base/limits.h" +#include "media/base/media_switches.h" + +namespace media { + +// 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; + +// Since "default", "pulse" and "dmix" devices are virtual devices mapped to +// real devices, we remove them from the list to avoiding duplicate counting. +// In addition, note that we support no more than 2 channels for recording, +// hence surround devices are not stored in the list. +static const char* kInvalidAudioInputDevices[] = { + "default", + "dmix", + "null", + "pulse", + "surround", +}; + +// static +void AudioManagerAlsa::ShowLinuxAudioInputSettings() { + scoped_ptr<base::Environment> env(base::Environment::Create()); + CommandLine command_line(CommandLine::NO_PROGRAM); + switch (base::nix::GetDesktopEnvironment(env.get())) { + case base::nix::DESKTOP_ENVIRONMENT_GNOME: + command_line.SetProgram(base::FilePath("gnome-volume-control")); + break; + case base::nix::DESKTOP_ENVIRONMENT_KDE3: + case base::nix::DESKTOP_ENVIRONMENT_KDE4: + command_line.SetProgram(base::FilePath("kmix")); + break; + case base::nix::DESKTOP_ENVIRONMENT_UNITY: + command_line.SetProgram(base::FilePath("gnome-control-center")); + command_line.AppendArg("sound"); + command_line.AppendArg("input"); + break; + default: + LOG(ERROR) << "Failed to show audio input settings: we don't know " + << "what command to use for your desktop environment."; + return; + } + base::LaunchProcess(command_line, base::LaunchOptions(), NULL); +} + +// Implementation of AudioManager. +bool AudioManagerAlsa::HasAudioOutputDevices() { + return HasAnyAlsaAudioDevice(kStreamPlayback); +} + +bool AudioManagerAlsa::HasAudioInputDevices() { + return HasAnyAlsaAudioDevice(kStreamCapture); +} + +AudioManagerAlsa::AudioManagerAlsa(AudioLogFactory* audio_log_factory) + : AudioManagerBase(audio_log_factory), + wrapper_(new AlsaWrapper()) { + SetMaxOutputStreamsAllowed(kMaxOutputStreams); +} + +AudioManagerAlsa::~AudioManagerAlsa() { + Shutdown(); +} + +void AudioManagerAlsa::ShowAudioInputSettings() { + ShowLinuxAudioInputSettings(); +} + +void AudioManagerAlsa::GetAudioInputDeviceNames( + AudioDeviceNames* device_names) { + DCHECK(device_names->empty()); + GetAlsaAudioDevices(kStreamCapture, device_names); +} + +void AudioManagerAlsa::GetAudioOutputDeviceNames( + AudioDeviceNames* device_names) { + DCHECK(device_names->empty()); + GetAlsaAudioDevices(kStreamPlayback, device_names); +} + +AudioParameters AudioManagerAlsa::GetInputStreamParameters( + const std::string& device_id) { + static const int kDefaultInputBufferSize = 1024; + + return AudioParameters( + AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, + kDefaultSampleRate, 16, kDefaultInputBufferSize); +} + +void AudioManagerAlsa::GetAlsaAudioDevices( + StreamType type, + media::AudioDeviceNames* device_names) { + // Constants specified by the ALSA API for device hints. + static const char kPcmInterfaceName[] = "pcm"; + int card = -1; + + // Loop through the sound cards to get ALSA device hints. + while (!wrapper_->CardNext(&card) && card >= 0) { + void** hints = NULL; + int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints); + if (!error) { + GetAlsaDevicesInfo(type, hints, device_names); + + // Destroy the hints now that we're done with it. + wrapper_->DeviceNameFreeHint(hints); + } else { + DLOG(WARNING) << "GetAlsaAudioDevices: unable to get device hints: " + << wrapper_->StrError(error); + } + } +} + +void AudioManagerAlsa::GetAlsaDevicesInfo( + AudioManagerAlsa::StreamType type, + void** hints, + media::AudioDeviceNames* device_names) { + static const char kIoHintName[] = "IOID"; + static const char kNameHintName[] = "NAME"; + static const char kDescriptionHintName[] = "DESC"; + + const char* unwanted_device_type = UnwantedDeviceTypeWhenEnumerating(type); + + for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) { + // 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(unwanted_device_type, io.get()) == 0) + continue; + + // 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, + AudioManagerBase::kDefaultDeviceId)); + } + + // Get the unique device name for the device. + scoped_ptr_malloc<char> unique_device_name( + wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName)); + + // Find out if the device is available. + if (IsAlsaDeviceAvailable(type, unique_device_name.get())) { + // Get the description for the device. + scoped_ptr_malloc<char> desc(wrapper_->DeviceNameGetHint( + *hint_iter, kDescriptionHintName)); + + media::AudioDeviceName name; + name.unique_id = unique_device_name.get(); + if (desc) { + // Use the more user friendly description as name. + // Replace '\n' with '-'. + char* pret = strchr(desc.get(), '\n'); + if (pret) + *pret = '-'; + name.device_name = desc.get(); + } else { + // Virtual devices don't necessarily have descriptions. + // Use their names instead. + name.device_name = unique_device_name.get(); + } + + // Store the device information. + device_names->push_back(name); + } + } +} + +// static +bool AudioManagerAlsa::IsAlsaDeviceAvailable( + AudioManagerAlsa::StreamType type, + const char* device_name) { + if (!device_name) + 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; + } +} + +// static +const char* AudioManagerAlsa::UnwantedDeviceTypeWhenEnumerating( + AudioManagerAlsa::StreamType wanted_type) { + return wanted_type == kStreamPlayback ? "Input" : "Output"; +} + +bool AudioManagerAlsa::HasAnyAlsaAudioDevice( + AudioManagerAlsa::StreamType stream) { + static const char kPcmInterfaceName[] = "pcm"; + static const char kIoHintName[] = "IOID"; + void** hints = NULL; + bool has_device = false; + int card = -1; + + // Loop through the sound cards. + // Don't use snd_device_name_hint(-1,..) since there is a access violation + // inside this ALSA API with libasound.so.2.0.0. + while (!wrapper_->CardNext(&card) && (card >= 0) && !has_device) { + int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints); + if (!error) { + for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) { + // Only examine devices that are |stream| capable. Valid values are + // "Input", "Output", and NULL which means both input and output. + scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter, + kIoHintName)); + 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. + has_device = true; + break; + } + + // Destroy the hints now that we're done with it. + wrapper_->DeviceNameFreeHint(hints); + hints = NULL; + } else { + DLOG(WARNING) << "HasAnyAudioDevice: unable to get device hints: " + << wrapper_->StrError(error); + } + } + + return has_device; +} + +AudioOutputStream* AudioManagerAlsa::MakeLinearOutputStream( + const AudioParameters& params) { + DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); + return MakeOutputStream(params); +} + +AudioOutputStream* AudioManagerAlsa::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); +} + +AudioInputStream* AudioManagerAlsa::MakeLinearInputStream( + const AudioParameters& params, const std::string& device_id) { + DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); + return MakeInputStream(params, device_id); +} + +AudioInputStream* AudioManagerAlsa::MakeLowLatencyInputStream( + const AudioParameters& params, const std::string& device_id) { + DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); + return MakeInputStream(params, device_id); +} + +AudioParameters AudioManagerAlsa::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; + int buffer_size = kDefaultOutputBufferSize; + int bits_per_sample = 16; + int input_channels = 0; + if (input_params.IsValid()) { + // Some clients, such as WebRTC, have a more limited use case and work + // acceptably with a smaller buffer size. The check below allows clients + // which want to try a smaller buffer size on Linux to do so. + // TODO(dalecurtis): This should include bits per channel and channel layout + // eventually. + sample_rate = input_params.sample_rate(); + bits_per_sample = input_params.bits_per_sample(); + channel_layout = input_params.channel_layout(); + input_channels = input_params.input_channels(); + buffer_size = std::min(input_params.frames_per_buffer(), buffer_size); + } + + int user_buffer_size = GetUserBufferSize(); + if (user_buffer_size) + buffer_size = user_buffer_size; + + return AudioParameters( + AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels, + sample_rate, bits_per_sample, buffer_size, AudioParameters::NO_EFFECTS); +} + +AudioOutputStream* AudioManagerAlsa::MakeOutputStream( + const AudioParameters& params) { + std::string device_name = AlsaPcmOutputStream::kAutoSelectDevice; + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kAlsaOutputDevice)) { + device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kAlsaOutputDevice); + } + return new AlsaPcmOutputStream(device_name, params, wrapper_.get(), this); +} + +AudioInputStream* AudioManagerAlsa::MakeInputStream( + const AudioParameters& params, const std::string& device_id) { + std::string device_name = (device_id == AudioManagerBase::kDefaultDeviceId) ? + AlsaPcmInputStream::kAutoSelectDevice : device_id; + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAlsaInputDevice)) { + device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kAlsaInputDevice); + } + + return new AlsaPcmInputStream(this, device_name, params, wrapper_.get()); +} + +} // namespace media diff --git a/chromium/media/audio/linux/audio_manager_linux.h b/chromium/media/audio/alsa/audio_manager_alsa.h index ab284dfdce9..155089f06bc 100644 --- a/chromium/media/audio/linux/audio_manager_linux.h +++ b/chromium/media/audio/alsa/audio_manager_alsa.h @@ -1,9 +1,9 @@ -// 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. -#ifndef MEDIA_AUDIO_LINUX_AUDIO_MANAGER_LINUX_H_ -#define MEDIA_AUDIO_LINUX_AUDIO_MANAGER_LINUX_H_ +#ifndef MEDIA_AUDIO_ALSA_AUDIO_MANAGER_ALSA_H_ +#define MEDIA_AUDIO_ALSA_AUDIO_MANAGER_ALSA_H_ #include <string> #include "base/compiler_specific.h" @@ -15,9 +15,9 @@ namespace media { class AlsaWrapper; -class MEDIA_EXPORT AudioManagerLinux : public AudioManagerBase { +class MEDIA_EXPORT AudioManagerAlsa : public AudioManagerBase { public: - AudioManagerLinux(); + AudioManagerAlsa(AudioLogFactory* audio_log_factory); static void ShowLinuxAudioInputSettings(); @@ -45,7 +45,7 @@ class MEDIA_EXPORT AudioManagerLinux : public AudioManagerBase { const AudioParameters& params, const std::string& device_id) OVERRIDE; protected: - virtual ~AudioManagerLinux(); + virtual ~AudioManagerAlsa(); virtual AudioParameters GetPreferredOutputStreamParameters( const std::string& output_device_id, @@ -86,9 +86,9 @@ class MEDIA_EXPORT AudioManagerLinux : public AudioManagerBase { scoped_ptr<AlsaWrapper> wrapper_; - DISALLOW_COPY_AND_ASSIGN(AudioManagerLinux); + DISALLOW_COPY_AND_ASSIGN(AudioManagerAlsa); }; } // namespace media -#endif // MEDIA_AUDIO_LINUX_AUDIO_MANAGER_LINUX_H_ +#endif // MEDIA_AUDIO_ALSA_AUDIO_MANAGER_ALSA_H_ diff --git a/chromium/media/audio/android/audio_android_unittest.cc b/chromium/media/audio/android/audio_android_unittest.cc index a8e448f821f..e7913265269 100644 --- a/chromium/media/audio/android/audio_android_unittest.cc +++ b/chromium/media/audio/android/audio_android_unittest.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/android/build_info.h" #include "base/basictypes.h" #include "base/file_util.h" #include "base/memory/scoped_ptr.h" @@ -95,7 +96,9 @@ std::ostream& operator<<(std::ostream& os, const AudioParameters& params) { << "bytes per buffer: " << params.GetBytesPerBuffer() << endl << "bytes per second: " << params.GetBytesPerSecond() << endl << "bytes per frame: " << params.GetBytesPerFrame() << endl - << "frame size in ms: " << ExpectedTimeBetweenCallbacks(params); + << "chunk size in ms: " << ExpectedTimeBetweenCallbacks(params) << endl + << "echo_canceller: " + << (params.effects() & AudioParameters::ECHO_CANCELLER); return os; } @@ -142,7 +145,7 @@ class FileAudioSource : public AudioOutputStream::AudioSourceCallback { // 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(); + VLOG(0) << "Reading from file: " << file_path.value().c_str(); } virtual ~FileAudioSource() {} @@ -218,9 +221,9 @@ class FileAudioSink : public AudioInputStream::AudioInputCallback { 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"); + binary_file_ = base::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(); + VLOG(0) << "Writing to file: " << file_path.value().c_str(); } virtual ~FileAudioSink() { @@ -239,7 +242,7 @@ class FileAudioSink : public AudioInputStream::AudioInputCallback { buffer_->Seek(chunk_size); bytes_written += chunk_size; } - file_util::CloseFile(binary_file_); + base::CloseFile(binary_file_); } // AudioInputStream::AudioInputCallback implementation. @@ -383,14 +386,14 @@ class FullDuplexAudioSinkSource DISALLOW_COPY_AND_ASSIGN(FullDuplexAudioSinkSource); }; -// Test fixture class. -class AudioAndroidTest : public testing::Test { +// Test fixture class for tests which only exercise the output path. +class AudioAndroidOutputTest : public testing::Test { public: - AudioAndroidTest() {} + AudioAndroidOutputTest() {} protected: virtual void SetUp() { - audio_manager_.reset(AudioManager::Create()); + audio_manager_.reset(AudioManager::CreateForTesting()); loop_.reset(new base::MessageLoopForUI()); } @@ -399,11 +402,6 @@ class AudioAndroidTest : public testing::Test { 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(); } @@ -413,28 +411,28 @@ class AudioAndroidTest : public testing::Test { .InMillisecondsF(); } - void StartInputStreamCallbacks(const AudioParameters& params) { + void StartOutputStreamCallbacks(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); + AudioOutputStream* stream = audio_manager()->MakeAudioOutputStream( + params, std::string(), std::string()); EXPECT_TRUE(stream); int count = 0; - MockAudioInputCallback sink; + MockAudioOutputCallback source; - EXPECT_CALL(sink, - OnData(stream, NotNull(), params.GetBytesPerBuffer(), _, _)) + EXPECT_CALL(source, OnMoreData(NotNull(), _)) .Times(AtLeast(num_callbacks)) .WillRepeatedly( - CheckCountAndPostQuitTask(&count, num_callbacks, loop())); - EXPECT_CALL(sink, OnError(stream)).Times(0); - EXPECT_CALL(sink, OnClose(stream)).Times(1); + 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(&sink); + stream->Start(&source); start_time_ = base::TimeTicks::Now(); loop()->Run(); end_time_ = base::TimeTicks::Now(); @@ -443,38 +441,83 @@ class AudioAndroidTest : public testing::Test { 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"; + VLOG(0) << "expected time between callbacks: " + << expected_time_between_callbacks_ms << " ms"; + VLOG(0) << "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) { + scoped_ptr<base::MessageLoopForUI> loop_; + scoped_ptr<AudioManager> audio_manager_; + base::TimeTicks start_time_; + base::TimeTicks end_time_; + + private: + DISALLOW_COPY_AND_ASSIGN(AudioAndroidOutputTest); +}; + +// AudioRecordInputStream should only be created on Jelly Bean and higher. This +// ensures we only test against the AudioRecord path when that is satisfied. +std::vector<bool> RunAudioRecordInputPathTests() { + std::vector<bool> tests; + tests.push_back(false); + if (base::android::BuildInfo::GetInstance()->sdk_int() >= 16) + tests.push_back(true); + return tests; +} + +// Test fixture class for tests which exercise the input path, or both input and +// output paths. It is value-parameterized to test against both the Java +// AudioRecord (when true) and native OpenSLES (when false) input paths. +class AudioAndroidInputTest : public AudioAndroidOutputTest, + public testing::WithParamInterface<bool> { + public: + AudioAndroidInputTest() {} + + protected: + AudioParameters GetInputStreamParameters() { + AudioParameters input_params = audio_manager()->GetInputStreamParameters( + AudioManagerBase::kDefaultDeviceId); + // Override the platform effects setting to use the AudioRecord or OpenSLES + // path as requested. + int effects = GetParam() ? AudioParameters::ECHO_CANCELLER : + AudioParameters::NO_EFFECTS; + AudioParameters params(input_params.format(), + input_params.channel_layout(), + input_params.input_channels(), + input_params.sample_rate(), + input_params.bits_per_sample(), + input_params.frames_per_buffer(), + effects); + return params; + } + + void StartInputStreamCallbacks(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()); + AudioInputStream* stream = audio_manager()->MakeAudioInputStream( + params, AudioManagerBase::kDefaultDeviceId); EXPECT_TRUE(stream); int count = 0; - MockAudioOutputCallback source; + MockAudioInputCallback sink; - EXPECT_CALL(source, OnMoreData(NotNull(), _)) + EXPECT_CALL(sink, + OnData(stream, NotNull(), params.GetBytesPerBuffer(), _, _)) .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); + 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(&source); + stream->Start(&sink); start_time_ = base::TimeTicks::Now(); loop()->Run(); end_time_ = base::TimeTicks::Now(); @@ -483,50 +526,50 @@ class AudioAndroidTest : public testing::Test { 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"; + VLOG(0) << "expected time between callbacks: " + << expected_time_between_callbacks_ms << " ms"; + VLOG(0) << "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); + private: + DISALLOW_COPY_AND_ASSIGN(AudioAndroidInputTest); }; // Get the default audio input parameters and log the result. -TEST_F(AudioAndroidTest, GetInputStreamParameters) { - AudioParameters params = GetDefaultInputStreamParameters(); +TEST_P(AudioAndroidInputTest, GetDefaultInputStreamParameters) { + // We don't go through AudioAndroidInputTest::GetInputStreamParameters() here + // so that we can log the real (non-overridden) values of the effects. + AudioParameters params = audio_manager()->GetInputStreamParameters( + AudioManagerBase::kDefaultDeviceId); EXPECT_TRUE(params.IsValid()); VLOG(1) << params; } // Get the default audio output parameters and log the result. -TEST_F(AudioAndroidTest, GetDefaultOutputStreamParameters) { +TEST_F(AudioAndroidOutputTest, 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) { +TEST_F(AudioAndroidOutputTest, 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"; + low_latency ? VLOG(0) << "Low latency output is supported" + : VLOG(0) << "Low latency output is *not* supported"; } // Ensure that a default input stream can be created and closed. -TEST_F(AudioAndroidTest, CreateAndCloseInputStream) { - AudioParameters params = GetDefaultInputStreamParameters(); +TEST_P(AudioAndroidInputTest, CreateAndCloseInputStream) { + AudioParameters params = GetInputStreamParameters(); AudioInputStream* ais = audio_manager()->MakeAudioInputStream( params, AudioManagerBase::kDefaultDeviceId); EXPECT_TRUE(ais); @@ -537,7 +580,7 @@ TEST_F(AudioAndroidTest, CreateAndCloseInputStream) { // 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) { +TEST_F(AudioAndroidOutputTest, CreateAndCloseOutputStream) { AudioParameters params = GetDefaultOutputStreamParameters(); AudioOutputStream* aos = audio_manager()->MakeAudioOutputStream( params, std::string(), std::string()); @@ -546,8 +589,8 @@ TEST_F(AudioAndroidTest, CreateAndCloseOutputStream) { } // Ensure that a default input stream can be opened and closed. -TEST_F(AudioAndroidTest, OpenAndCloseInputStream) { - AudioParameters params = GetDefaultInputStreamParameters(); +TEST_P(AudioAndroidInputTest, OpenAndCloseInputStream) { + AudioParameters params = GetInputStreamParameters(); AudioInputStream* ais = audio_manager()->MakeAudioInputStream( params, AudioManagerBase::kDefaultDeviceId); EXPECT_TRUE(ais); @@ -556,7 +599,7 @@ TEST_F(AudioAndroidTest, OpenAndCloseInputStream) { } // Ensure that a default output stream can be opened and closed. -TEST_F(AudioAndroidTest, OpenAndCloseOutputStream) { +TEST_F(AudioAndroidOutputTest, OpenAndCloseOutputStream) { AudioParameters params = GetDefaultOutputStreamParameters(); AudioOutputStream* aos = audio_manager()->MakeAudioOutputStream( params, std::string(), std::string()); @@ -567,8 +610,8 @@ TEST_F(AudioAndroidTest, OpenAndCloseOutputStream) { // Start input streaming using default input parameters and ensure that the // callback sequence is sane. -TEST_F(AudioAndroidTest, StartInputStreamCallbacks) { - AudioParameters params = GetDefaultInputStreamParameters(); +TEST_P(AudioAndroidInputTest, StartInputStreamCallbacks) { + AudioParameters params = GetInputStreamParameters(); StartInputStreamCallbacks(params); } @@ -576,19 +619,21 @@ TEST_F(AudioAndroidTest, StartInputStreamCallbacks) { // 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(); +TEST_P(AudioAndroidInputTest, StartInputStreamCallbacksNonDefaultParameters) { + AudioParameters native_params = GetInputStreamParameters(); AudioParameters params(native_params.format(), native_params.channel_layout(), + native_params.input_channels(), native_params.sample_rate(), native_params.bits_per_sample(), - native_params.sample_rate() / 100); + native_params.sample_rate() / 100, + native_params.effects()); StartInputStreamCallbacks(params); } // Start output streaming using default output parameters and ensure that the // callback sequence is sane. -TEST_F(AudioAndroidTest, StartOutputStreamCallbacks) { +TEST_F(AudioAndroidOutputTest, StartOutputStreamCallbacks) { AudioParameters params = GetDefaultOutputStreamParameters(); StartOutputStreamCallbacks(params); } @@ -598,7 +643,7 @@ TEST_F(AudioAndroidTest, StartOutputStreamCallbacks) { // select a 10ms buffer size instead of the default size and to open up the // device in mono. // TODO(henrika): possibly add support for more variations. -TEST_F(AudioAndroidTest, StartOutputStreamCallbacksNonDefaultParameters) { +TEST_F(AudioAndroidOutputTest, StartOutputStreamCallbacksNonDefaultParameters) { AudioParameters native_params = GetDefaultOutputStreamParameters(); AudioParameters params(native_params.format(), CHANNEL_LAYOUT_MONO, @@ -612,7 +657,7 @@ TEST_F(AudioAndroidTest, StartOutputStreamCallbacksNonDefaultParameters) { // 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) { +TEST_F(AudioAndroidOutputTest, DISABLED_RunOutputStreamWithFileAsSource) { AudioParameters params = GetDefaultOutputStreamParameters(); VLOG(1) << params; AudioOutputStream* aos = audio_manager()->MakeAudioOutputStream( @@ -639,7 +684,7 @@ TEST_F(AudioAndroidTest, DISABLED_RunOutputStreamWithFileAsSource) { EXPECT_TRUE(aos->Open()); aos->SetVolume(1.0); aos->Start(&source); - LOG(INFO) << ">> Verify that the file is played out correctly..."; + VLOG(0) << ">> Verify that the file is played out correctly..."; EXPECT_TRUE(event.TimedWait(TestTimeouts::action_max_timeout())); aos->Stop(); aos->Close(); @@ -649,8 +694,8 @@ TEST_F(AudioAndroidTest, DISABLED_RunOutputStreamWithFileAsSource) { // 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(); +TEST_P(AudioAndroidInputTest, DISABLED_RunSimplexInputStreamWithFileAsSink) { + AudioParameters params = GetInputStreamParameters(); VLOG(1) << params; AudioInputStream* ais = audio_manager()->MakeAudioInputStream( params, AudioManagerBase::kDefaultDeviceId); @@ -666,7 +711,7 @@ TEST_F(AudioAndroidTest, DISABLED_RunSimplexInputStreamWithFileAsSink) { EXPECT_TRUE(ais->Open()); ais->Start(&sink); - LOG(INFO) << ">> Speak into the microphone to record audio..."; + VLOG(0) << ">> Speak into the microphone to record audio..."; EXPECT_TRUE(event.TimedWait(TestTimeouts::action_max_timeout())); ais->Stop(); ais->Close(); @@ -676,8 +721,8 @@ TEST_F(AudioAndroidTest, DISABLED_RunSimplexInputStreamWithFileAsSink) { // 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(); +TEST_P(AudioAndroidInputTest, DISABLED_RunDuplexInputStreamWithFileAsSink) { + AudioParameters in_params = GetInputStreamParameters(); AudioInputStream* ais = audio_manager()->MakeAudioInputStream( in_params, AudioManagerBase::kDefaultDeviceId); EXPECT_TRUE(ais); @@ -706,7 +751,7 @@ TEST_F(AudioAndroidTest, DISABLED_RunDuplexInputStreamWithFileAsSink) { EXPECT_TRUE(aos->Open()); ais->Start(&sink); aos->Start(&source); - LOG(INFO) << ">> Speak into the microphone to record audio"; + VLOG(0) << ">> Speak into the microphone to record audio"; EXPECT_TRUE(event.TimedWait(TestTimeouts::action_max_timeout())); aos->Stop(); ais->Stop(); @@ -720,10 +765,10 @@ TEST_F(AudioAndroidTest, DISABLED_RunDuplexInputStreamWithFileAsSink) { // 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, +TEST_P(AudioAndroidInputTest, DISABLED_RunSymmetricInputAndOutputStreamsInFullDuplex) { // Get native audio parameters for the input side. - AudioParameters default_input_params = GetDefaultInputStreamParameters(); + AudioParameters default_input_params = GetInputStreamParameters(); // 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 @@ -756,7 +801,7 @@ TEST_F(AudioAndroidTest, 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..."; + VLOG(0) << ">> Speak into the mic and listen to the audio in loopback..."; fflush(stdout); base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(20)); printf("\n"); @@ -766,4 +811,7 @@ TEST_F(AudioAndroidTest, ais->Close(); } +INSTANTIATE_TEST_CASE_P(AudioAndroidInputTest, AudioAndroidInputTest, + testing::ValuesIn(RunAudioRecordInputPathTests())); + } // namespace media diff --git a/chromium/media/audio/android/audio_manager_android.cc b/chromium/media/audio/android/audio_manager_android.cc index 04b226fa64f..3464d89a30f 100644 --- a/chromium/media/audio/android/audio_manager_android.cc +++ b/chromium/media/audio/android/audio_manager_android.cc @@ -4,16 +4,27 @@ #include "media/audio/android/audio_manager_android.h" +#include "base/android/build_info.h" +#include "base/android/jni_array.h" +#include "base/android/jni_string.h" +#include "base/android/scoped_java_ref.h" #include "base/logging.h" +#include "base/strings/string_number_conversions.h" #include "jni/AudioManagerAndroid_jni.h" +#include "media/audio/android/audio_record_input.h" #include "media/audio/android/opensles_input.h" #include "media/audio/android/opensles_output.h" #include "media/audio/audio_manager.h" #include "media/audio/audio_parameters.h" -#include "media/audio/audio_util.h" #include "media/audio/fake_audio_input_stream.h" #include "media/base/channel_layout.h" +using base::android::AppendJavaStringArrayToStringVector; +using base::android::AttachCurrentThread; +using base::android::ConvertJavaStringToUTF8; +using base::android::ConvertUTF8ToJavaString; +using base::android::ScopedJavaLocalRef; + namespace media { static void AddDefaultDevice(AudioDeviceNames* device_names) { @@ -32,20 +43,24 @@ static const int kAudioModeInCommunication = 0x00000003; static const int kDefaultInputBufferSize = 1024; static const int kDefaultOutputBufferSize = 2048; -AudioManager* CreateAudioManager() { - return new AudioManagerAndroid(); +AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) { + return new AudioManagerAndroid(audio_log_factory); } -AudioManagerAndroid::AudioManagerAndroid() { +AudioManagerAndroid::AudioManagerAndroid(AudioLogFactory* audio_log_factory) + : AudioManagerBase(audio_log_factory) { SetMaxOutputStreamsAllowed(kMaxOutputStreams); j_audio_manager_.Reset( Java_AudioManagerAndroid_createAudioManagerAndroid( base::android::AttachCurrentThread(), - base::android::GetApplicationContext())); + base::android::GetApplicationContext(), + reinterpret_cast<intptr_t>(this))); + Init(); } AudioManagerAndroid::~AudioManagerAndroid() { + Close(); Shutdown(); } @@ -59,28 +74,52 @@ bool AudioManagerAndroid::HasAudioInputDevices() { void AudioManagerAndroid::GetAudioInputDeviceNames( AudioDeviceNames* device_names) { + // Always add default device parameters as first element. AddDefaultDevice(device_names); + + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobjectArray> j_device_array = + Java_AudioManagerAndroid_getAudioInputDeviceNames( + env, j_audio_manager_.obj()); + jsize len = env->GetArrayLength(j_device_array.obj()); + AudioDeviceName device; + for (jsize i = 0; i < len; ++i) { + ScopedJavaLocalRef<jobject> j_device( + env, env->GetObjectArrayElement(j_device_array.obj(), i)); + ScopedJavaLocalRef<jstring> j_device_name = + Java_AudioDeviceName_name(env, j_device.obj()); + ConvertJavaStringToUTF8(env, j_device_name.obj(), &device.device_name); + ScopedJavaLocalRef<jstring> j_device_id = + Java_AudioDeviceName_id(env, j_device.obj()); + ConvertJavaStringToUTF8(env, j_device_id.obj(), &device.unique_id); + device_names->push_back(device); + } } void AudioManagerAndroid::GetAudioOutputDeviceNames( AudioDeviceNames* device_names) { + // TODO(henrika): enumerate using GetAudioInputDeviceNames(). AddDefaultDevice(device_names); } AudioParameters AudioManagerAndroid::GetInputStreamParameters( const std::string& device_id) { + JNIEnv* env = AttachCurrentThread(); // Use mono as preferred number of input channels on Android to save // resources. Using mono also avoids a driver issue seen on Samsung // Galaxy S3 and S4 devices. See http://crbug.com/256851 for details. ChannelLayout channel_layout = CHANNEL_LAYOUT_MONO; int buffer_size = Java_AudioManagerAndroid_getMinInputFrameSize( - base::android::AttachCurrentThread(), GetNativeOutputSampleRate(), + env, GetNativeOutputSampleRate(), ChannelLayoutToChannelCount(channel_layout)); - - return AudioParameters( - AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, + int effects = AudioParameters::NO_EFFECTS; + effects |= Java_AudioManagerAndroid_shouldUseAcousticEchoCanceler(env) ? + AudioParameters::ECHO_CANCELLER : AudioParameters::NO_EFFECTS; + AudioParameters params( + AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, 0, GetNativeOutputSampleRate(), 16, - buffer_size <= 0 ? kDefaultInputBufferSize : buffer_size); + buffer_size <= 0 ? kDefaultInputBufferSize : buffer_size, effects); + return params; } AudioOutputStream* AudioManagerAndroid::MakeAudioOutputStream( @@ -92,8 +131,13 @@ AudioOutputStream* AudioManagerAndroid::MakeAudioOutputStream( std::string()); if (stream && output_stream_count() == 1) { SetAudioMode(kAudioModeInCommunication); - RegisterHeadsetReceiver(); } + + { + base::AutoLock lock(streams_lock_); + streams_.insert(static_cast<OpenSLESOutputStream*>(stream)); + } + return stream; } @@ -107,9 +151,10 @@ AudioInputStream* AudioManagerAndroid::MakeAudioInputStream( void AudioManagerAndroid::ReleaseOutputStream(AudioOutputStream* stream) { AudioManagerBase::ReleaseOutputStream(stream); if (!output_stream_count()) { - UnregisterHeadsetReceiver(); SetAudioMode(kAudioModeNormal); } + base::AutoLock lock(streams_lock_); + streams_.erase(static_cast<OpenSLESOutputStream*>(stream)); } void AudioManagerAndroid::ReleaseInputStream(AudioInputStream* stream) { @@ -133,6 +178,9 @@ AudioOutputStream* AudioManagerAndroid::MakeLowLatencyOutputStream( AudioInputStream* AudioManagerAndroid::MakeLinearInputStream( const AudioParameters& params, const std::string& device_id) { + // TODO(henrika): add support for device selection if/when any client + // needs it. + DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); return new OpenSLESInputStream(this, params); } @@ -140,6 +188,26 @@ AudioInputStream* AudioManagerAndroid::MakeLinearInputStream( AudioInputStream* AudioManagerAndroid::MakeLowLatencyInputStream( const AudioParameters& params, const std::string& device_id) { DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); + DLOG_IF(ERROR, device_id.empty()) << "Invalid device ID!"; + // Utilize the device ID to select the correct input device. + // Note that the input device is always associated with a certain output + // device, i.e., this selection does also switch the output device. + // All input and output streams will be affected by the device selection. + SetAudioDevice(device_id); + + if (params.effects() != AudioParameters::NO_EFFECTS) { + // Platform effects can only be enabled through the AudioRecord path. + // An effect should only have been requested here if recommended by + // AudioManagerAndroid.shouldUse<Effect>. + // + // Creating this class requires Jelly Bean, which is already guaranteed by + // shouldUse<Effect>. Only DCHECK on that condition to allow tests to use + // the effect settings as a way to select the input path. + DCHECK_GE(base::android::BuildInfo::GetInstance()->sdk_int(), 16); + DVLOG(1) << "Creating AudioRecordInputStream"; + return new AudioRecordInputStream(this, params); + } + DVLOG(1) << "Creating OpenSLESInputStream"; return new OpenSLESInputStream(this, params); } @@ -181,7 +249,7 @@ AudioParameters AudioManagerAndroid::GetPreferredOutputStreamParameters( return AudioParameters( AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels, - sample_rate, bits_per_sample, buffer_size); + sample_rate, bits_per_sample, buffer_size, AudioParameters::NO_EFFECTS); } // static @@ -189,22 +257,53 @@ bool AudioManagerAndroid::RegisterAudioManager(JNIEnv* env) { return RegisterNativesImpl(env); } -void AudioManagerAndroid::SetAudioMode(int mode) { - Java_AudioManagerAndroid_setMode( +void AudioManagerAndroid::Init() { + Java_AudioManagerAndroid_init( base::android::AttachCurrentThread(), - j_audio_manager_.obj(), mode); + j_audio_manager_.obj()); } -void AudioManagerAndroid::RegisterHeadsetReceiver() { - Java_AudioManagerAndroid_registerHeadsetReceiver( +void AudioManagerAndroid::Close() { + Java_AudioManagerAndroid_close( base::android::AttachCurrentThread(), j_audio_manager_.obj()); } -void AudioManagerAndroid::UnregisterHeadsetReceiver() { - Java_AudioManagerAndroid_unregisterHeadsetReceiver( +void AudioManagerAndroid::SetMute(JNIEnv* env, jobject obj, jboolean muted) { + GetMessageLoop()->PostTask( + FROM_HERE, + base::Bind( + &AudioManagerAndroid::DoSetMuteOnAudioThread, + base::Unretained(this), + muted)); +} + +void AudioManagerAndroid::DoSetMuteOnAudioThread(bool muted) { + base::AutoLock lock(streams_lock_); + for (OutputStreams::iterator it = streams_.begin(); + it != streams_.end(); ++it) { + (*it)->SetMute(muted); + } +} + +void AudioManagerAndroid::SetAudioMode(int mode) { + Java_AudioManagerAndroid_setMode( base::android::AttachCurrentThread(), - j_audio_manager_.obj()); + j_audio_manager_.obj(), mode); +} + +void AudioManagerAndroid::SetAudioDevice(const std::string& device_id) { + JNIEnv* env = AttachCurrentThread(); + + // Send the unique device ID to the Java audio manager and make the + // device switch. Provide an empty string to the Java audio manager + // if the default device is selected. + ScopedJavaLocalRef<jstring> j_device_id = ConvertUTF8ToJavaString( + env, + device_id == AudioManagerBase::kDefaultDeviceId ? + std::string() : device_id); + Java_AudioManagerAndroid_setDevice( + env, j_audio_manager_.obj(), j_device_id.obj()); } int AudioManagerAndroid::GetNativeOutputSampleRate() { diff --git a/chromium/media/audio/android/audio_manager_android.h b/chromium/media/audio/android/audio_manager_android.h index ed2b2c3ce91..2900c0f8e29 100644 --- a/chromium/media/audio/android/audio_manager_android.h +++ b/chromium/media/audio/android/audio_manager_android.h @@ -5,16 +5,21 @@ #ifndef MEDIA_AUDIO_ANDROID_AUDIO_MANAGER_ANDROID_H_ #define MEDIA_AUDIO_ANDROID_AUDIO_MANAGER_ANDROID_H_ +#include <set> + #include "base/android/jni_android.h" #include "base/gtest_prod_util.h" +#include "base/synchronization/lock.h" #include "media/audio/audio_manager_base.h" namespace media { +class OpenSLESOutputStream; + // Android implemention of AudioManager. class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase { public: - AudioManagerAndroid(); + AudioManagerAndroid(AudioLogFactory* audio_log_factory); // Implementation of AudioManager. virtual bool HasAudioOutputDevices() OVERRIDE; @@ -52,6 +57,8 @@ class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase { static bool RegisterAudioManager(JNIEnv* env); + void SetMute(JNIEnv* env, jobject obj, jboolean muted); + protected: virtual ~AudioManagerAndroid(); @@ -60,20 +67,30 @@ class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase { const AudioParameters& input_params) OVERRIDE; private: + void Init(); + void Close(); void SetAudioMode(int mode); - void RegisterHeadsetReceiver(); - void UnregisterHeadsetReceiver(); + void SetAudioDevice(const std::string& device_id); int GetNativeOutputSampleRate(); bool IsAudioLowLatencySupported(); int GetAudioLowLatencyOutputFrameSize(); int GetOptimalOutputFrameSize(int sample_rate, int channels); + void DoSetMuteOnAudioThread(bool muted); + // Allow the AudioAndroidTest to access private methods. - FRIEND_TEST_ALL_PREFIXES(AudioAndroidTest, IsAudioLowLatencySupported); + FRIEND_TEST_ALL_PREFIXES(AudioAndroidOutputTest, IsAudioLowLatencySupported); // Java AudioManager instance. base::android::ScopedJavaGlobalRef<jobject> j_audio_manager_; + typedef std::set<OpenSLESOutputStream*> OutputStreams; + OutputStreams streams_; + // TODO(wjia): remove this lock once unit test modules are fixed to call + // AudioManager::MakeAudioOutputStream on the audio thread. For now, this + // lock is used to guard access to |streams_|. + base::Lock streams_lock_; + DISALLOW_COPY_AND_ASSIGN(AudioManagerAndroid); }; diff --git a/chromium/media/audio/android/audio_record_input.cc b/chromium/media/audio/android/audio_record_input.cc new file mode 100644 index 00000000000..15a0c3d3b7b --- /dev/null +++ b/chromium/media/audio/android/audio_record_input.cc @@ -0,0 +1,131 @@ +// 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/android/audio_record_input.h" + +#include "base/logging.h" +#include "jni/AudioRecordInput_jni.h" +#include "media/audio/android/audio_manager_android.h" + +namespace media { + +AudioRecordInputStream::AudioRecordInputStream( + AudioManagerAndroid* audio_manager, const AudioParameters& params) + : audio_manager_(audio_manager), + callback_(NULL), + direct_buffer_address_(NULL) { + DVLOG(2) << __PRETTY_FUNCTION__; + DCHECK(params.IsValid()); + j_audio_record_.Reset( + Java_AudioRecordInput_createAudioRecordInput( + base::android::AttachCurrentThread(), + reinterpret_cast<intptr_t>(this), + params.sample_rate(), + params.channels(), + params.bits_per_sample(), + params.GetBytesPerBuffer(), + params.effects() & AudioParameters::ECHO_CANCELLER)); +} + +AudioRecordInputStream::~AudioRecordInputStream() { + DVLOG(2) << __PRETTY_FUNCTION__; + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void AudioRecordInputStream::CacheDirectBufferAddress(JNIEnv* env, jobject obj, + jobject byte_buffer) { + DCHECK(thread_checker_.CalledOnValidThread()); + direct_buffer_address_ = static_cast<uint8*>( + env->GetDirectBufferAddress(byte_buffer)); +} + +// static +bool AudioRecordInputStream::RegisterAudioRecordInput(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +void AudioRecordInputStream::OnData(JNIEnv* env, jobject obj, jint size, + jint hardware_delay_bytes) { + DCHECK(direct_buffer_address_); + // Passing zero as the volume parameter indicates there is no access to a + // hardware volume slider. + callback_->OnData(this, direct_buffer_address_, size, hardware_delay_bytes, + 0.0); +} + +bool AudioRecordInputStream::Open() { + DVLOG(2) << __PRETTY_FUNCTION__; + DCHECK(thread_checker_.CalledOnValidThread()); + return Java_AudioRecordInput_open( + base::android::AttachCurrentThread(), j_audio_record_.obj()); +} + +void AudioRecordInputStream::Start(AudioInputCallback* callback) { + DVLOG(2) << __PRETTY_FUNCTION__; + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(callback); + + if (callback_) { + // Start() was already called. + DCHECK_EQ(callback_, callback); + return; + } + // The Java thread has not yet started, so we are free to set |callback_|. + callback_ = callback; + + Java_AudioRecordInput_start( + base::android::AttachCurrentThread(), j_audio_record_.obj()); +} + +void AudioRecordInputStream::Stop() { + DVLOG(2) << __PRETTY_FUNCTION__; + DCHECK(thread_checker_.CalledOnValidThread()); + if (!callback_) { + // Start() was never called, or Stop() was already called. + return; + } + + Java_AudioRecordInput_stop( + base::android::AttachCurrentThread(), j_audio_record_.obj()); + + // The Java thread must have been stopped at this point, so we are free to + // set |callback_|. + callback_->OnClose(this); + callback_ = NULL; +} + +void AudioRecordInputStream::Close() { + DVLOG(2) << __PRETTY_FUNCTION__; + DCHECK(thread_checker_.CalledOnValidThread()); + Stop(); + DCHECK(!callback_); + Java_AudioRecordInput_close( + base::android::AttachCurrentThread(), j_audio_record_.obj()); + audio_manager_->ReleaseInputStream(this); +} + +double AudioRecordInputStream::GetMaxVolume() { + NOTIMPLEMENTED(); + return 0.0; +} + +void AudioRecordInputStream::SetVolume(double volume) { + NOTIMPLEMENTED(); +} + +double AudioRecordInputStream::GetVolume() { + NOTIMPLEMENTED(); + return 0.0; +} + +void AudioRecordInputStream::SetAutomaticGainControl(bool enabled) { + NOTIMPLEMENTED(); +} + +bool AudioRecordInputStream::GetAutomaticGainControl() { + NOTIMPLEMENTED(); + return false; +} + +} // namespace media diff --git a/chromium/media/audio/android/audio_record_input.h b/chromium/media/audio/android/audio_record_input.h new file mode 100644 index 00000000000..0a2578b1079 --- /dev/null +++ b/chromium/media/audio/android/audio_record_input.h @@ -0,0 +1,72 @@ +// 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_ANDROID_AUDIO_RECORD_INPUT_H_ +#define MEDIA_AUDIO_ANDROID_AUDIO_RECORD_INPUT_H_ + +#include "base/android/jni_android.h" +#include "base/threading/thread_checker.h" +#include "media/audio/audio_io.h" +#include "media/audio/audio_parameters.h" + +namespace media { + +class AudioManagerAndroid; + +// Implements PCM audio input support for Android using the Java AudioRecord +// interface. Most of the work is done by its Java counterpart in +// AudioRecordInput.java. This class is created and lives on the Audio Manager +// thread but recorded audio buffers are delivered on a thread managed by +// the Java class. +// +// The Java class makes use of AudioEffect features which are first available +// in Jelly Bean. It should not be instantiated running against earlier SDKs. +class MEDIA_EXPORT AudioRecordInputStream : public AudioInputStream { + public: + AudioRecordInputStream(AudioManagerAndroid* manager, + const AudioParameters& params); + + virtual ~AudioRecordInputStream(); + + // Implementation of AudioInputStream. + virtual bool Open() OVERRIDE; + virtual void Start(AudioInputCallback* callback) OVERRIDE; + virtual void Stop() OVERRIDE; + virtual void Close() OVERRIDE; + virtual double GetMaxVolume() OVERRIDE; + virtual void SetVolume(double volume) OVERRIDE; + virtual double GetVolume() OVERRIDE; + virtual void SetAutomaticGainControl(bool enabled) OVERRIDE; + virtual bool GetAutomaticGainControl() OVERRIDE; + + static bool RegisterAudioRecordInput(JNIEnv* env); + + // Called from Java when data is available. + void OnData(JNIEnv* env, jobject obj, jint size, jint hardware_delay_bytes); + + // Called from Java so that we can cache the address of the Java-managed + // |byte_buffer| in |direct_buffer_address_|. + void CacheDirectBufferAddress(JNIEnv* env, jobject obj, jobject byte_buffer); + + private: + base::ThreadChecker thread_checker_; + AudioManagerAndroid* audio_manager_; + + // Java AudioRecordInput instance. + base::android::ScopedJavaGlobalRef<jobject> j_audio_record_; + + // This is the only member accessed by both the Audio Manager and Java + // threads. Explanations for why we do not require explicit synchronization + // are given in the implementation. + AudioInputCallback* callback_; + + // Owned by j_audio_record_. + uint8* direct_buffer_address_; + + DISALLOW_COPY_AND_ASSIGN(AudioRecordInputStream); +}; + +} // namespace media + +#endif // MEDIA_AUDIO_ANDROID_AUDIO_RECORD_INPUT_H_ diff --git a/chromium/media/audio/android/opensles_input.cc b/chromium/media/audio/android/opensles_input.cc index a0e4ce3b987..e51ba4f3a97 100644 --- a/chromium/media/audio/android/opensles_input.cc +++ b/chromium/media/audio/android/opensles_input.cc @@ -28,7 +28,7 @@ OpenSLESInputStream::OpenSLESInputStream(AudioManagerAndroid* audio_manager, active_buffer_index_(0), buffer_size_bytes_(0), started_(false) { - DVLOG(2) << "OpenSLESInputStream::OpenSLESInputStream()"; + DVLOG(2) << __PRETTY_FUNCTION__; format_.formatType = SL_DATAFORMAT_PCM; format_.numChannels = static_cast<SLuint32>(params.channels()); // Provides sampling rate in milliHertz to OpenSLES. @@ -49,7 +49,7 @@ OpenSLESInputStream::OpenSLESInputStream(AudioManagerAndroid* audio_manager, } OpenSLESInputStream::~OpenSLESInputStream() { - DVLOG(2) << "OpenSLESInputStream::~OpenSLESInputStream()"; + DVLOG(2) << __PRETTY_FUNCTION__; DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(!recorder_object_.Get()); DCHECK(!engine_object_.Get()); @@ -59,7 +59,7 @@ OpenSLESInputStream::~OpenSLESInputStream() { } bool OpenSLESInputStream::Open() { - DVLOG(2) << "OpenSLESInputStream::Open()"; + DVLOG(2) << __PRETTY_FUNCTION__; DCHECK(thread_checker_.CalledOnValidThread()); if (engine_object_.Get()) return false; @@ -73,7 +73,7 @@ bool OpenSLESInputStream::Open() { } void OpenSLESInputStream::Start(AudioInputCallback* callback) { - DVLOG(2) << "OpenSLESInputStream::Start()"; + DVLOG(2) << __PRETTY_FUNCTION__; DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(callback); DCHECK(recorder_); @@ -116,7 +116,7 @@ void OpenSLESInputStream::Start(AudioInputCallback* callback) { } void OpenSLESInputStream::Stop() { - DVLOG(2) << "OpenSLESInputStream::Stop()"; + DVLOG(2) << __PRETTY_FUNCTION__; DCHECK(thread_checker_.CalledOnValidThread()); if (!started_) return; @@ -135,7 +135,7 @@ void OpenSLESInputStream::Stop() { } void OpenSLESInputStream::Close() { - DVLOG(2) << "OpenSLESInputStream::Close()"; + DVLOG(2) << __PRETTY_FUNCTION__; DCHECK(thread_checker_.CalledOnValidThread()); // Stop the stream if it is still recording. @@ -170,7 +170,9 @@ double OpenSLESInputStream::GetMaxVolume() { return 0.0; } -void OpenSLESInputStream::SetVolume(double volume) { NOTIMPLEMENTED(); } +void OpenSLESInputStream::SetVolume(double volume) { + NOTIMPLEMENTED(); +} double OpenSLESInputStream::GetVolume() { NOTIMPLEMENTED(); diff --git a/chromium/media/audio/android/opensles_input.h b/chromium/media/audio/android/opensles_input.h index e05831c6712..cb07d51f78b 100644 --- a/chromium/media/audio/android/opensles_input.h +++ b/chromium/media/audio/android/opensles_input.h @@ -21,8 +21,8 @@ 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. +// audio buffers are delivered on an internal OpenSLES audio thread. All public +// methods should be called on the Audio Manager thread. class OpenSLESInputStream : public AudioInputStream { public: static const int kMaxNumOfBuffersInQueue = 2; diff --git a/chromium/media/audio/android/opensles_output.cc b/chromium/media/audio/android/opensles_output.cc index 5643f833c3d..b71680f0a7e 100644 --- a/chromium/media/audio/android/opensles_output.cc +++ b/chromium/media/audio/android/opensles_output.cc @@ -28,6 +28,7 @@ OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager, active_buffer_index_(0), buffer_size_bytes_(0), started_(false), + muted_(false), volume_(1.0) { DVLOG(2) << "OpenSLESOutputStream::OpenSLESOutputStream()"; format_.formatType = SL_DATAFORMAT_PCM; @@ -128,6 +129,7 @@ void OpenSLESOutputStream::Stop() { DCHECK_EQ(0u, buffer_queue_state.index); #endif + callback_ = NULL; started_ = false; } @@ -172,6 +174,12 @@ void OpenSLESOutputStream::GetVolume(double* volume) { *volume = static_cast<double>(volume_); } +void OpenSLESOutputStream::SetMute(bool muted) { + DVLOG(2) << "OpenSLESOutputStream::SetMute(" << muted << ")"; + DCHECK(thread_checker_.CalledOnValidThread()); + muted_ = muted; +} + bool OpenSLESOutputStream::CreatePlayer() { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(!engine_object_.Get()); @@ -324,7 +332,7 @@ void OpenSLESOutputStream::FillBufferQueueNoLock() { // 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_->Scale(muted_ ? 0.0f : volume_); audio_bus_->ToInterleaved(frames_filled, format_.bitsPerSample / 8, audio_data_[active_buffer_index_]); diff --git a/chromium/media/audio/android/opensles_output.h b/chromium/media/audio/android/opensles_output.h index 7232d5da5f7..623b0193894 100644 --- a/chromium/media/audio/android/opensles_output.h +++ b/chromium/media/audio/android/opensles_output.h @@ -40,6 +40,10 @@ class OpenSLESOutputStream : public AudioOutputStream { virtual void SetVolume(double volume) OVERRIDE; virtual void GetVolume(double* volume) OVERRIDE; + // Set the value of |muted_|. It does not affect |volume_| which can be + // got by calling GetVolume(). See comments for |muted_| below. + void SetMute(bool muted); + private: bool CreatePlayer(); @@ -96,6 +100,12 @@ class OpenSLESOutputStream : public AudioOutputStream { bool started_; + // Volume control coming from hardware. It overrides |volume_| when it's + // true. Otherwise, use |volume_| for scaling. + // This is needed because platform voice volume never goes to zero in + // COMMUNICATION mode on Android. + bool muted_; + // Volume level from 0 to 1. float volume_; diff --git a/chromium/media/audio/audio_device_thread.cc b/chromium/media/audio/audio_device_thread.cc index d5c1bbcebdc..daf908556d8 100644 --- a/chromium/media/audio/audio_device_thread.cc +++ b/chromium/media/audio/audio_device_thread.cc @@ -12,7 +12,6 @@ #include "base/message_loop/message_loop.h" #include "base/threading/platform_thread.h" #include "base/threading/thread_restrictions.h" -#include "media/audio/audio_util.h" #include "media/base/audio_bus.h" using base::PlatformThread; @@ -29,7 +28,8 @@ class AudioDeviceThread::Thread public: Thread(AudioDeviceThread::Callback* callback, base::SyncSocket::Handle socket, - const char* thread_name); + const char* thread_name, + bool synchronized_buffers); void Start(); @@ -55,6 +55,7 @@ class AudioDeviceThread::Thread base::CancelableSyncSocket socket_; base::Lock callback_lock_; const char* thread_name_; + const bool synchronized_buffers_; DISALLOW_COPY_AND_ASSIGN(Thread); }; @@ -68,10 +69,12 @@ AudioDeviceThread::~AudioDeviceThread() { DCHECK(!thread_.get()); } void AudioDeviceThread::Start(AudioDeviceThread::Callback* callback, base::SyncSocket::Handle socket, - const char* thread_name) { + const char* thread_name, + bool synchronized_buffers) { base::AutoLock auto_lock(thread_lock_); - CHECK(thread_.get() == NULL); - thread_ = new AudioDeviceThread::Thread(callback, socket, thread_name); + CHECK(!thread_); + thread_ = new AudioDeviceThread::Thread( + callback, socket, thread_name, synchronized_buffers); thread_->Start(); } @@ -85,17 +88,19 @@ void AudioDeviceThread::Stop(base::MessageLoop* loop_for_join) { bool AudioDeviceThread::IsStopped() { base::AutoLock auto_lock(thread_lock_); - return thread_.get() == NULL; + return !thread_; } // AudioDeviceThread::Thread implementation AudioDeviceThread::Thread::Thread(AudioDeviceThread::Callback* callback, base::SyncSocket::Handle socket, - const char* thread_name) + const char* thread_name, + bool synchronized_buffers) : thread_(), callback_(callback), socket_(socket), - thread_name_(thread_name) { + thread_name_(thread_name), + synchronized_buffers_(synchronized_buffers) { } AudioDeviceThread::Thread::~Thread() { @@ -157,6 +162,7 @@ void AudioDeviceThread::Thread::ThreadMain() { } void AudioDeviceThread::Thread::Run() { + uint32 buffer_index = 0; while (true) { int pending_data = 0; size_t bytes_read = socket_.Receive(&pending_data, sizeof(pending_data)); @@ -165,9 +171,21 @@ void AudioDeviceThread::Thread::Run() { break; } - base::AutoLock auto_lock(callback_lock_); - if (callback_) - callback_->Process(pending_data); + { + base::AutoLock auto_lock(callback_lock_); + if (callback_) + callback_->Process(pending_data); + } + + // Let the other end know which buffer we just filled. The buffer index is + // used to ensure the other end is getting the buffer it expects. For more + // details on how this works see AudioSyncReader::WaitUntilDataIsReady(). + if (synchronized_buffers_) { + ++buffer_index; + size_t bytes_sent = socket_.Send(&buffer_index, sizeof(buffer_index)); + if (bytes_sent != sizeof(buffer_index)) + break; + } } } diff --git a/chromium/media/audio/audio_device_thread.h b/chromium/media/audio/audio_device_thread.h index 976f88359ba..7a1a6ed8c4c 100644 --- a/chromium/media/audio/audio_device_thread.h +++ b/chromium/media/audio/audio_device_thread.h @@ -12,7 +12,6 @@ #include "base/sync_socket.h" #include "base/synchronization/lock.h" #include "media/audio/audio_parameters.h" -#include "media/audio/shared_memory_util.h" #include "media/base/media_export.h" namespace base { @@ -74,10 +73,13 @@ class MEDIA_EXPORT AudioDeviceThread { AudioDeviceThread(); ~AudioDeviceThread(); - // Starts the audio thread. The thread must not already be running. + // Starts the audio thread. The thread must not already be running. If + // |sychronized_buffers| is set, the browser expects to be notified via the + // |socket| every time AudioDeviceThread::Process() completes. void Start(AudioDeviceThread::Callback* callback, base::SyncSocket::Handle socket, - const char* thread_name); + const char* thread_name, + bool synchronized_buffers); // This tells the audio thread to stop and clean up the data. // The method can stop the thread synchronously or asynchronously. diff --git a/chromium/media/audio/audio_input_controller_unittest.cc b/chromium/media/audio/audio_input_controller_unittest.cc index 6388cbf975b..a7bb600aaf4 100644 --- a/chromium/media/audio/audio_input_controller_unittest.cc +++ b/chromium/media/audio/audio_input_controller_unittest.cc @@ -80,7 +80,7 @@ TEST_F(AudioInputControllerTest, CreateAndClose) { EXPECT_CALL(event_handler, OnCreated(NotNull())) .WillOnce(QuitMessageLoop(&message_loop_)); - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout, kSampleRate, kBitsPerSample, kSamplesPerPacket); @@ -118,7 +118,7 @@ TEST_F(AudioInputControllerTest, RecordAndClose) { .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, message_loop_.message_loop_proxy())); - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout, kSampleRate, kBitsPerSample, kSamplesPerPacket); @@ -168,7 +168,7 @@ TEST_F(AudioInputControllerTest, RecordAndError) { .Times(Exactly(1)) .WillOnce(QuitMessageLoop(&message_loop_)); - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout, kSampleRate, kBitsPerSample, kSamplesPerPacket); @@ -205,7 +205,7 @@ TEST_F(AudioInputControllerTest, SamplesPerPacketTooLarge) { EXPECT_CALL(event_handler, OnCreated(NotNull())) .Times(Exactly(0)); - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout, kSampleRate, @@ -231,7 +231,7 @@ TEST_F(AudioInputControllerTest, CloseTwice) { EXPECT_CALL(event_handler, OnRecording(NotNull())) .Times(Exactly(1)); - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout, kSampleRate, diff --git a/chromium/media/audio/audio_input_device.cc b/chromium/media/audio/audio_input_device.cc index d7685840ecf..d1a6ab89f9f 100644 --- a/chromium/media/audio/audio_input_device.cc +++ b/chromium/media/audio/audio_input_device.cc @@ -138,10 +138,10 @@ void AudioInputDevice::OnStreamCreated( return; DCHECK(audio_thread_.IsStopped()); - audio_callback_.reset( - new AudioInputDevice::AudioThreadCallback( - audio_parameters_, handle, length, total_segments, callback_)); - audio_thread_.Start(audio_callback_.get(), socket_handle, "AudioInputDevice"); + audio_callback_.reset(new AudioInputDevice::AudioThreadCallback( + audio_parameters_, handle, length, total_segments, callback_)); + audio_thread_.Start( + audio_callback_.get(), socket_handle, "AudioInputDevice", false); state_ = RECORDING; ipc_->RecordStream(); diff --git a/chromium/media/audio/audio_input_unittest.cc b/chromium/media/audio/audio_input_unittest.cc index 8adb746ee86..838cab3867a 100644 --- a/chromium/media/audio/audio_input_unittest.cc +++ b/chromium/media/audio/audio_input_unittest.cc @@ -77,7 +77,7 @@ static AudioInputStream* CreateTestAudioInputStream(AudioManager* audio_man) { // Test that AudioInputStream rejects out of range parameters. TEST(AudioInputTest, SanityOnMakeParams) { - scoped_ptr<AudioManager> audio_man(AudioManager::Create()); + scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_man.get())) return; @@ -111,7 +111,7 @@ TEST(AudioInputTest, SanityOnMakeParams) { // Test create and close of an AudioInputStream without recording audio. TEST(AudioInputTest, CreateAndClose) { - scoped_ptr<AudioManager> audio_man(AudioManager::Create()); + scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_man.get())) return; AudioInputStream* ais = CreateTestAudioInputStream(audio_man.get()); @@ -126,7 +126,7 @@ TEST(AudioInputTest, CreateAndClose) { #endif // Test create, open and close of an AudioInputStream without recording audio. TEST(AudioInputTest, MAYBE_OpenAndClose) { - scoped_ptr<AudioManager> audio_man(AudioManager::Create()); + scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_man.get())) return; AudioInputStream* ais = CreateTestAudioInputStream(audio_man.get()); @@ -142,7 +142,7 @@ TEST(AudioInputTest, MAYBE_OpenAndClose) { #endif // Test create, open, stop and close of an AudioInputStream without recording. TEST(AudioInputTest, MAYBE_OpenStopAndClose) { - scoped_ptr<AudioManager> audio_man(AudioManager::Create()); + scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_man.get())) return; AudioInputStream* ais = CreateTestAudioInputStream(audio_man.get()); @@ -159,7 +159,7 @@ TEST(AudioInputTest, MAYBE_OpenStopAndClose) { #endif // Test a normal recording sequence using an AudioInputStream. TEST(AudioInputTest, MAYBE_Record) { - scoped_ptr<AudioManager> audio_man(AudioManager::Create()); + scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_man.get())) return; base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); diff --git a/chromium/media/audio/audio_input_volume_unittest.cc b/chromium/media/audio/audio_input_volume_unittest.cc index 570c045570e..e89d106f7ed 100644 --- a/chromium/media/audio/audio_input_volume_unittest.cc +++ b/chromium/media/audio/audio_input_volume_unittest.cc @@ -8,7 +8,6 @@ #include "base/memory/scoped_ptr.h" #include "media/audio/audio_io.h" #include "media/audio/audio_manager_base.h" -#include "media/audio/audio_util.h" #include "testing/gtest/include/gtest/gtest.h" #if defined(OS_WIN) @@ -39,7 +38,7 @@ double GetVolumeAfterSetVolumeOnLinux(AudioInputStream* ais, class AudioInputVolumeTest : public ::testing::Test { protected: AudioInputVolumeTest() - : audio_manager_(AudioManager::Create()) + : audio_manager_(AudioManager::CreateForTesting()) #if defined(OS_WIN) , com_init_(base::win::ScopedCOMInitializer::kMTA) #endif diff --git a/chromium/media/audio/audio_logging.h b/chromium/media/audio/audio_logging.h new file mode 100644 index 00000000000..1d8366bad75 --- /dev/null +++ b/chromium/media/audio/audio_logging.h @@ -0,0 +1,84 @@ +// 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_AUDIO_LOGGING_H_ +#define MEDIA_AUDIO_AUDIO_LOGGING_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" + +namespace media { +class AudioParameters; + +// AudioLog logs state information about an active audio component. Each method +// takes a |component_id| along with method specific information. Its methods +// are safe to call from any thread. +class AudioLog { + public: + virtual ~AudioLog() {} + + // Called when an audio component is created. |params| are the parameters of + // the created stream. |input_device_id| and |output_device_id| are the + // respective device ids for input and output. Either one or both may be + // specified. + virtual void OnCreated(int component_id, + const media::AudioParameters& params, + const std::string& input_device_id, + const std::string& output_device_id) = 0; + + // Called when an audio component is started, generally this is synonymous + // with "playing." + virtual void OnStarted(int component_id) = 0; + + // Called when an audio component is stopped, generally this is synonymous + // with "paused." + virtual void OnStopped(int component_id) = 0; + + // Called when an audio component is closed, generally this is synonymous + // with "deleted." + virtual void OnClosed(int component_id) = 0; + + // Called when an audio component encounters an error. + virtual void OnError(int component_id) = 0; + + // Called when an audio component changes volume. |volume| is the new volume. + virtual void OnSetVolume(int component_id, double volume) = 0; +}; + +// AudioLogFactory dispenses AudioLog instances to owning classes for tracking +// AudioComponent behavior. All AudioComponents have the concept of an owning +// class: +// +// - AudioInputRendererHost for AudioInputController +// - AudioRendererHost for AudioOutputController +// - AudioOutputDispatcherImpl for AudioOutputStream +// +// Each of these owning classes may own multiple instances of each component, as +// such each AudioLog supports logging for multiple instances. +class AudioLogFactory { + public: + enum AudioComponent { + // Input controllers have a 1:1 mapping with streams, so there's no need to + // track both controllers and streams. + AUDIO_INPUT_CONTROLLER, + // Output controllers may or may not be backed by an active stream, so we + // need to track both controllers and streams. + AUDIO_OUTPUT_CONTROLLER, + AUDIO_OUTPUT_STREAM, + AUDIO_COMPONENT_MAX + }; + + // Create a new AudioLog object for tracking the behavior for one or more + // instances of the given component. Each instance of an "owning" class must + // create its own AudioLog. + virtual scoped_ptr<AudioLog> CreateAudioLog(AudioComponent component) = 0; + + protected: + virtual ~AudioLogFactory() {} +}; + +} // namespace media + +#endif // MEDIA_AUDIO_AUDIO_LOGGING_H_ 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 a616761294d..c0cfa6937cf 100644 --- a/chromium/media/audio/audio_low_latency_input_output_unittest.cc +++ b/chromium/media/audio/audio_low_latency_input_output_unittest.cc @@ -14,12 +14,13 @@ #include "build/build_config.h" #include "media/audio/audio_io.h" #include "media/audio/audio_manager_base.h" +#include "media/audio/fake_audio_log_factory.h" #include "media/base/seekable_buffer.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#if defined(OS_LINUX) || defined(OS_OPENBSD) -#include "media/audio/linux/audio_manager_linux.h" +#if defined(USE_ALSA) +#include "media/audio/alsa/audio_manager_alsa.h" #elif defined(OS_MACOSX) #include "media/audio/mac/audio_manager_mac.h" #elif defined(OS_WIN) @@ -27,18 +28,22 @@ #include "media/audio/win/core_audio_util_win.h" #elif defined(OS_ANDROID) #include "media/audio/android/audio_manager_android.h" +#else +#include "media/audio/fake_audio_manager.h" #endif namespace media { -#if defined(OS_LINUX) || defined(OS_OPENBSD) -typedef AudioManagerLinux AudioManagerAnyPlatform; +#if defined(USE_ALSA) +typedef AudioManagerAlsa AudioManagerAnyPlatform; #elif defined(OS_MACOSX) typedef AudioManagerMac AudioManagerAnyPlatform; #elif defined(OS_WIN) typedef AudioManagerWin AudioManagerAnyPlatform; #elif defined(OS_ANDROID) typedef AudioManagerAndroid AudioManagerAnyPlatform; +#else +typedef FakeAudioManager AudioManagerAnyPlatform; #endif // Limits the number of delay measurements we can store in an array and @@ -80,7 +85,7 @@ struct AudioDelayState { // the main thread instead of the audio thread. class MockAudioManager : public AudioManagerAnyPlatform { public: - MockAudioManager() {} + MockAudioManager() : AudioManagerAnyPlatform(&fake_audio_log_factory_) {} virtual ~MockAudioManager() {} virtual scoped_refptr<base::MessageLoopProxy> GetMessageLoop() OVERRIDE { @@ -88,6 +93,7 @@ class MockAudioManager : public AudioManagerAnyPlatform { } private: + FakeAudioLogFactory fake_audio_log_factory_; DISALLOW_COPY_AND_ASSIGN(MockAudioManager); }; @@ -156,9 +162,9 @@ class FullDuplexAudioSinkSource EXPECT_TRUE(PathService::Get(base::DIR_EXE, &file_name)); file_name = file_name.AppendASCII(kDelayValuesFileName); - FILE* text_file = file_util::OpenFile(file_name, "wt"); + FILE* text_file = base::OpenFile(file_name, "wt"); DLOG_IF(ERROR, !text_file) << "Failed to open log file."; - LOG(INFO) << ">> Output file " << file_name.value() << " has been created."; + VLOG(0) << ">> Output file " << file_name.value() << " has been created."; // Write the array which contains time-stamps, buffer size and // audio delays values to a text file. @@ -174,7 +180,7 @@ class FullDuplexAudioSinkSource ++elements_written; } - file_util::CloseFile(text_file); + base::CloseFile(text_file); } // AudioInputStream::AudioInputCallback. @@ -421,10 +427,10 @@ TEST_F(AudioLowLatencyInputOutputTest, DISABLED_FullDuplexDelayMeasurement) { FullDuplexAudioSinkSource full_duplex( aisw.sample_rate(), aisw.samples_per_packet(), aisw.channels()); - LOG(INFO) << ">> You should now be able to hear yourself in loopback..."; - DLOG(INFO) << " sample_rate : " << aisw.sample_rate(); - DLOG(INFO) << " samples_per_packet: " << aisw.samples_per_packet(); - DLOG(INFO) << " channels : " << aisw.channels(); + VLOG(0) << ">> You should now be able to hear yourself in loopback..."; + DVLOG(0) << " sample_rate : " << aisw.sample_rate(); + DVLOG(0) << " samples_per_packet: " << aisw.samples_per_packet(); + DVLOG(0) << " channels : " << aisw.channels(); ais->Start(&full_duplex); aos->Start(&full_duplex); diff --git a/chromium/media/audio/audio_manager.cc b/chromium/media/audio/audio_manager.cc index 3f49a45ad87..03eeb171d70 100644 --- a/chromium/media/audio/audio_manager.cc +++ b/chromium/media/audio/audio_manager.cc @@ -4,12 +4,12 @@ #include "media/audio/audio_manager.h" -#include "base/at_exit.h" -#include "base/atomicops.h" #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/lazy_instance.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" +#include "media/audio/fake_audio_log_factory.h" namespace media { namespace { @@ -17,24 +17,30 @@ AudioManager* g_last_created = NULL; } // Forward declaration of the platform specific AudioManager factory function. -AudioManager* CreateAudioManager(); +AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory); -AudioManager::AudioManager() { -} +AudioManager::AudioManager() {} AudioManager::~AudioManager() { - CHECK(g_last_created == NULL || g_last_created == this); + CHECK(!g_last_created || g_last_created == this); g_last_created = NULL; } // static -AudioManager* AudioManager::Create() { - CHECK(g_last_created == NULL); - g_last_created = CreateAudioManager(); +AudioManager* AudioManager::Create(AudioLogFactory* audio_log_factory) { + CHECK(!g_last_created); + g_last_created = CreateAudioManager(audio_log_factory); return g_last_created; } // static +AudioManager* AudioManager::CreateForTesting() { + static base::LazyInstance<FakeAudioLogFactory>::Leaky fake_log_factory = + LAZY_INSTANCE_INITIALIZER; + return Create(fake_log_factory.Pointer()); +} + +// static AudioManager* AudioManager::Get() { return g_last_created; } diff --git a/chromium/media/audio/audio_manager.h b/chromium/media/audio/audio_manager.h index 891d2a26589..0ca468ed4dd 100644 --- a/chromium/media/audio/audio_manager.h +++ b/chromium/media/audio/audio_manager.h @@ -11,6 +11,7 @@ #include "base/memory/ref_counted.h" #include "base/strings/string16.h" #include "media/audio/audio_device_name.h" +#include "media/audio/audio_logging.h" #include "media/audio/audio_parameters.h" namespace base { @@ -23,16 +24,19 @@ namespace media { class AudioInputStream; class AudioOutputStream; -// Manages all audio resources. In particular it owns the AudioOutputStream -// objects. Provides some convenience functions that avoid the need to provide -// iterators over the existing streams. +// Manages all audio resources. Provides some convenience functions that avoid +// the need to provide iterators over the existing streams. class MEDIA_EXPORT AudioManager { - public: - virtual ~AudioManager(); + public: + virtual ~AudioManager(); - // Use to construct the audio manager. - // NOTE: There should only be one instance. - static AudioManager* Create(); + // Construct the audio manager; only one instance is allowed. The manager + // will forward CreateAudioLog() calls to the provided AudioLogFactory; as + // such |audio_log_factory| must outlive the AudioManager. + static AudioManager* Create(AudioLogFactory* audio_log_factory); + + // Similar to Create() except uses a FakeAudioLogFactory for testing. + static AudioManager* CreateForTesting(); // Returns the pointer to the last created instance, or NULL if not yet // created. This is a utility method for the code outside of media directory, @@ -50,7 +54,7 @@ class MEDIA_EXPORT AudioManager { // Returns a human readable string for the model/make of the active audio // input device for this computer. - virtual string16 GetAudioInputDeviceModel() = 0; + virtual base::string16 GetAudioInputDeviceModel() = 0; // Opens the platform default audio input settings UI. // Note: This could invoke an external application/preferences pane, so @@ -62,10 +66,16 @@ class MEDIA_EXPORT AudioManager { // which must initially be empty. It is not guaranteed that all the // devices in the list support all formats and sample rates for // recording. + // + // Not threadsafe; in production this should only be called from the + // Audio IO thread (see GetMessageLoop). virtual void GetAudioInputDeviceNames(AudioDeviceNames* device_names) = 0; // Appends a list of available output devices to |device_names|, // which must initially be empty. + // + // Not threadsafe; in production this should only be called from the + // Audio IO thread (see GetMessageLoop). virtual void GetAudioOutputDeviceNames(AudioDeviceNames* device_names) = 0; // Factory for all the supported stream formats. |params| defines parameters @@ -169,6 +179,16 @@ class MEDIA_EXPORT AudioManager { virtual std::string GetAssociatedOutputDeviceID( const std::string& input_device_id) = 0; + // Create a new AudioLog object for tracking the behavior for one or more + // instances of the given component. See AudioLogFactory for more details. + virtual scoped_ptr<AudioLog> CreateAudioLog( + AudioLogFactory::AudioComponent component) = 0; + + // Called when a component has detected a OS level audio wedge. Shuts down + // all active audio streams and then restarts them transparently. See + // http://crbug.com/160920 + virtual void FixWedgedAudio() = 0; + protected: AudioManager(); diff --git a/chromium/media/audio/audio_manager_base.cc b/chromium/media/audio/audio_manager_base.cc index 5b1f4b3690a..f7b590ae724 100644 --- a/chromium/media/audio/audio_manager_base.cc +++ b/chromium/media/audio/audio_manager_base.cc @@ -8,12 +8,11 @@ #include "base/bind_helpers.h" #include "base/command_line.h" #include "base/message_loop/message_loop_proxy.h" -#include "base/threading/thread.h" +#include "base/strings/string_number_conversions.h" #include "build/build_config.h" #include "media/audio/audio_output_dispatcher_impl.h" #include "media/audio/audio_output_proxy.h" #include "media/audio/audio_output_resampler.h" -#include "media/audio/audio_util.h" #include "media/audio/fake_audio_input_stream.h" #include "media/audio/fake_audio_output_stream.h" #include "media/base/media_switches.h" @@ -78,7 +77,7 @@ class AudioManagerBase::CompareByParams { const DispatcherParams* dispatcher_; }; -AudioManagerBase::AudioManagerBase() +AudioManagerBase::AudioManagerBase(AudioLogFactory* audio_log_factory) : max_num_output_streams_(kDefaultMaxOutputStreams), max_num_input_streams_(kDefaultMaxInputStreams), num_output_streams_(0), @@ -87,9 +86,10 @@ AudioManagerBase::AudioManagerBase() // block the UI thread when swapping devices. output_listeners_( ObserverList<AudioDeviceListener>::NOTIFY_EXISTING_ONLY), - audio_thread_(new base::Thread("AudioThread")) { + audio_thread_("AudioThread"), + audio_log_factory_(audio_log_factory) { #if defined(OS_WIN) - audio_thread_->init_com_with_mta(true); + audio_thread_.init_com_with_mta(true); #elif defined(OS_MACOSX) // CoreAudio calls must occur on the main thread of the process, which in our // case is sadly the browser UI thread. Failure to execute calls on the right @@ -104,8 +104,8 @@ AudioManagerBase::AudioManagerBase() } #endif - CHECK(audio_thread_->Start()); - message_loop_ = audio_thread_->message_loop_proxy(); + CHECK(audio_thread_.Start()); + message_loop_ = audio_thread_.message_loop_proxy(); } AudioManagerBase::~AudioManagerBase() { @@ -114,15 +114,15 @@ AudioManagerBase::~AudioManagerBase() { // stopping the thread, resulting an unexpected behavior. // This way we make sure activities of the audio streams are all stopped // before we destroy them. - CHECK(!audio_thread_.get()); + CHECK(!audio_thread_.IsRunning()); // All the output streams should have been deleted. DCHECK_EQ(0, num_output_streams_); // All the input streams should have been deleted. DCHECK_EQ(0, num_input_streams_); } -string16 AudioManagerBase::GetAudioInputDeviceModel() { - return string16(); +base::string16 AudioManagerBase::GetAudioInputDeviceModel() { + return base::string16(); } scoped_refptr<base::MessageLoopProxy> AudioManagerBase::GetMessageLoop() { @@ -131,10 +131,10 @@ scoped_refptr<base::MessageLoopProxy> AudioManagerBase::GetMessageLoop() { scoped_refptr<base::MessageLoopProxy> AudioManagerBase::GetWorkerLoop() { // Lazily start the worker thread. - if (!audio_thread_->IsRunning()) - CHECK(audio_thread_->Start()); + if (!audio_thread_.IsRunning()) + CHECK(audio_thread_.Start()); - return audio_thread_->message_loop_proxy(); + return audio_thread_.message_loop_proxy(); } AudioOutputStream* AudioManagerBase::MakeAudioOutputStream( @@ -330,17 +330,6 @@ void AudioManagerBase::ReleaseInputStream(AudioInputStream* stream) { } void AudioManagerBase::Shutdown() { - // To avoid running into deadlocks while we stop the thread, shut it down - // via a local variable while not holding the audio thread lock. - scoped_ptr<base::Thread> audio_thread; - { - base::AutoLock lock(audio_thread_lock_); - audio_thread_.swap(audio_thread); - } - - if (!audio_thread) - return; - // Only true when we're sharing the UI message loop with the browser. The UI // loop is no longer running at this time and browser destruction is imminent. if (message_loop_->BelongsToCurrentThread()) { @@ -351,27 +340,24 @@ void AudioManagerBase::Shutdown() { } // Stop() will wait for any posted messages to be processed first. - audio_thread->Stop(); + audio_thread_.Stop(); } void AudioManagerBase::ShutdownOnAudioThread() { - // 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 - // called from one place, so we'll leave it at that. + DCHECK(message_loop_->BelongsToCurrentThread()); + AudioOutputDispatchers::iterator it = output_dispatchers_.begin(); for (; it != output_dispatchers_.end(); ++it) { scoped_refptr<AudioOutputDispatcher>& dispatcher = (*it)->dispatcher; - if (dispatcher.get()) { - dispatcher->Shutdown(); - // All AudioOutputProxies must have been freed before Shutdown is called. - // If they still exist, things will go bad. They have direct pointers to - // both physical audio stream objects that belong to the dispatcher as - // well as the message loop of the audio thread that will soon go away. - // So, better crash now than later. - DCHECK(dispatcher->HasOneRef()) << "AudioOutputProxies are still alive"; - dispatcher = NULL; - } + dispatcher->Shutdown(); + + // All AudioOutputProxies must have been freed before Shutdown is called. + // If they still exist, things will go bad. They have direct pointers to + // both physical audio stream objects that belong to the dispatcher as + // well as the message loop of the audio thread that will soon go away. + // So, better crash now than later. + DCHECK(dispatcher->HasOneRef()) << "AudioOutputProxies are still alive"; + dispatcher = NULL; } output_dispatchers_.clear(); @@ -419,8 +405,51 @@ std::string AudioManagerBase::GetAssociatedOutputDeviceID( } std::string AudioManagerBase::GetDefaultOutputDeviceID() { - NOTIMPLEMENTED(); return ""; } +int AudioManagerBase::GetUserBufferSize() { + const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); + int buffer_size = 0; + std::string buffer_size_str(cmd_line->GetSwitchValueASCII( + switches::kAudioBufferSize)); + if (base::StringToInt(buffer_size_str, &buffer_size) && buffer_size > 0) + return buffer_size; + + return 0; +} + +scoped_ptr<AudioLog> AudioManagerBase::CreateAudioLog( + AudioLogFactory::AudioComponent component) { + return audio_log_factory_->CreateAudioLog(component); +} + +void AudioManagerBase::FixWedgedAudio() { + DCHECK(message_loop_->BelongsToCurrentThread()); +#if defined(OS_MACOSX) + // Through trial and error, we've found that one way to restore audio after a + // hang is to close all outstanding audio streams. Once all streams have been + // closed, new streams appear to work correctly. + // + // In Chrome terms, this means we need to ask all AudioOutputDispatchers to + // close all Open()'d streams. Once all streams across all dispatchers have + // been closed, we ask for all previously Start()'d streams to be recreated + // using the same AudioSourceCallback they had before. + // + // Since this operation takes place on the audio thread we can be sure that no + // other state-changing stream operations will take place while the fix is in + // progress. + // + // See http://crbug.com/160920 for additional details. + for (AudioOutputDispatchers::iterator it = output_dispatchers_.begin(); + it != output_dispatchers_.end(); ++it) { + (*it)->dispatcher->CloseStreamsForWedgeFix(); + } + for (AudioOutputDispatchers::iterator it = output_dispatchers_.begin(); + it != output_dispatchers_.end(); ++it) { + (*it)->dispatcher->RestartStreamsForWedgeFix(); + } +#endif +} + } // namespace media diff --git a/chromium/media/audio/audio_manager_base.h b/chromium/media/audio/audio_manager_base.h index cdf7d3a76ae..09b021a0d2b 100644 --- a/chromium/media/audio/audio_manager_base.h +++ b/chromium/media/audio/audio_manager_base.h @@ -12,7 +12,7 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" #include "base/observer_list.h" -#include "base/synchronization/lock.h" +#include "base/threading/thread.h" #include "media/audio/audio_manager.h" #include "media/audio/audio_output_dispatcher.h" @@ -21,10 +21,6 @@ #include "base/win/scoped_com_initializer.h" #endif -namespace base { -class Thread; -} - namespace media { class AudioOutputDispatcher; @@ -55,7 +51,7 @@ class MEDIA_EXPORT AudioManagerBase : public AudioManager { virtual scoped_refptr<base::MessageLoopProxy> GetMessageLoop() OVERRIDE; virtual scoped_refptr<base::MessageLoopProxy> GetWorkerLoop() OVERRIDE; - virtual string16 GetAudioInputDeviceModel() OVERRIDE; + virtual base::string16 GetAudioInputDeviceModel() OVERRIDE; virtual void ShowAudioInputSettings() OVERRIDE; @@ -119,9 +115,13 @@ class MEDIA_EXPORT AudioManagerBase : public AudioManager { virtual std::string GetAssociatedOutputDeviceID( const std::string& input_device_id) OVERRIDE; - protected: - AudioManagerBase(); + virtual scoped_ptr<AudioLog> CreateAudioLog( + AudioLogFactory::AudioComponent component) OVERRIDE; + + virtual void FixWedgedAudio() OVERRIDE; + protected: + AudioManagerBase(AudioLogFactory* audio_log_factory); // Shuts down the audio thread and releases all the audio output dispatchers // on the audio thread. All audio streams should be freed before Shutdown() @@ -136,6 +136,10 @@ class MEDIA_EXPORT AudioManagerBase : public AudioManager { // thread. void NotifyAllOutputDeviceChangeListeners(); + // Returns user buffer size as specified on the command line or 0 if no buffer + // size has been specified. + int GetUserBufferSize(); + // Returns the preferred hardware audio output parameters for opening output // streams. If the users inject a valid |input_params|, each AudioManager // will decide if they should return the values from |input_params| or the @@ -181,8 +185,7 @@ class MEDIA_EXPORT AudioManagerBase : public AudioManager { ObserverList<AudioDeviceListener> output_listeners_; // Thread used to interact with audio streams created by this audio manager. - scoped_ptr<base::Thread> audio_thread_; - mutable base::Lock audio_thread_lock_; + base::Thread audio_thread_; // The message loop of the audio thread this object runs on. Used for internal // tasks which run on the audio thread even after Shutdown() has been started @@ -193,6 +196,9 @@ class MEDIA_EXPORT AudioManagerBase : public AudioManager { // from the audio thread (no locking). AudioOutputDispatchers output_dispatchers_; + // Proxy for creating AudioLog objects. + AudioLogFactory* const audio_log_factory_; + DISALLOW_COPY_AND_ASSIGN(AudioManagerBase); }; diff --git a/chromium/media/audio/audio_manager_unittest.cc b/chromium/media/audio/audio_manager_unittest.cc index 4747c2e2996..8c6cc10b423 100644 --- a/chromium/media/audio/audio_manager_unittest.cc +++ b/chromium/media/audio/audio_manager_unittest.cc @@ -7,11 +7,12 @@ #include "base/memory/scoped_ptr.h" #include "media/audio/audio_manager.h" #include "media/audio/audio_manager_base.h" +#include "media/audio/fake_audio_log_factory.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(USE_ALSA) +#include "media/audio/alsa/audio_manager_alsa.h" +#endif // defined(USE_ALSA) #if defined(OS_WIN) #include "base/win/scoped_com_initializer.h" @@ -31,7 +32,7 @@ class AudioManagerTest : public ::testing::Test { protected: AudioManagerTest() - : audio_manager_(AudioManager::Create()) + : audio_manager_(AudioManager::CreateForTesting()) #if defined(OS_WIN) , com_init_(base::win::ScopedCOMInitializer::kMTA) #endif @@ -111,6 +112,17 @@ class AudioManagerTest return audio_manager_->HasAudioOutputDevices(); } +#if defined(USE_ALSA) || defined(USE_PULSEAUDIO) + template <class T> + void CreateAudioManagerForTesting() { + // Only one AudioManager may exist at a time, so destroy the one we're + // currently holding before creating a new one. + audio_manager_.reset(); + audio_manager_.reset(T::Create(&fake_audio_log_factory_)); + } +#endif + + FakeAudioLogFactory fake_audio_log_factory_; scoped_ptr<AudioManager> audio_manager_; #if defined(OS_WIN) @@ -254,7 +266,7 @@ TEST_F(AudioManagerTest, EnumerateInputDevicesPulseaudio) { if (!CanRunInputTest()) return; - audio_manager_.reset(AudioManagerPulse::Create()); + CreateAudioManagerForTesting<AudioManagerPulse>(); if (audio_manager_.get()) { AudioDeviceNames device_names; audio_manager_->GetAudioInputDeviceNames(&device_names); @@ -268,7 +280,7 @@ TEST_F(AudioManagerTest, EnumerateOutputDevicesPulseaudio) { if (!CanRunOutputTest()) return; - audio_manager_.reset(AudioManagerPulse::Create()); + CreateAudioManagerForTesting<AudioManagerPulse>(); if (audio_manager_.get()) { AudioDeviceNames device_names; audio_manager_->GetAudioOutputDeviceNames(&device_names); @@ -288,8 +300,8 @@ TEST_F(AudioManagerTest, EnumerateInputDevicesAlsa) { if (!CanRunInputTest()) return; - VLOG(2) << "Testing AudioManagerLinux."; - audio_manager_.reset(new AudioManagerLinux()); + VLOG(2) << "Testing AudioManagerAlsa."; + CreateAudioManagerForTesting<AudioManagerAlsa>(); AudioDeviceNames device_names; audio_manager_->GetAudioInputDeviceNames(&device_names); CheckDeviceNames(device_names); @@ -299,8 +311,8 @@ TEST_F(AudioManagerTest, EnumerateOutputDevicesAlsa) { if (!CanRunOutputTest()) return; - VLOG(2) << "Testing AudioManagerLinux."; - audio_manager_.reset(new AudioManagerLinux()); + VLOG(2) << "Testing AudioManagerAlsa."; + CreateAudioManagerForTesting<AudioManagerAlsa>(); AudioDeviceNames device_names; audio_manager_->GetAudioOutputDeviceNames(&device_names); CheckDeviceNames(device_names); diff --git a/chromium/media/audio/audio_output_controller.cc b/chromium/media/audio/audio_output_controller.cc index 649612cd4f6..92f9f25de53 100644 --- a/chromium/media/audio/audio_output_controller.cc +++ b/chromium/media/audio/audio_output_controller.cc @@ -8,11 +8,10 @@ #include "base/debug/trace_event.h" #include "base/message_loop/message_loop.h" #include "base/metrics/histogram.h" +#include "base/task_runner_util.h" #include "base/threading/platform_thread.h" #include "base/time/time.h" #include "build/build_config.h" -#include "media/audio/audio_util.h" -#include "media/audio/shared_memory_util.h" #include "media/base/scoped_histogram_timer.h" using base::Time; @@ -58,7 +57,7 @@ AudioOutputController::AudioOutputController( params.sample_rate(), TimeDelta::FromMilliseconds(kPowerMeasurementTimeConstantMillis)), #endif - number_polling_attempts_left_(0) { + on_more_io_data_called_(0) { DCHECK(audio_manager); DCHECK(handler_); DCHECK(sync_reader_); @@ -112,9 +111,28 @@ void AudioOutputController::SetVolume(double volume) { &AudioOutputController::DoSetVolume, this, volume)); } +void AudioOutputController::GetOutputDeviceId( + base::Callback<void(const std::string&)> callback) const { + base::PostTaskAndReplyWithResult( + message_loop_.get(), + FROM_HERE, + base::Bind(&AudioOutputController::DoGetOutputDeviceId, this), + callback); +} + +void AudioOutputController::SwitchOutputDevice( + const std::string& output_device_id, const base::Closure& callback) { + message_loop_->PostTaskAndReply( + FROM_HERE, + base::Bind(&AudioOutputController::DoSwitchOutputDevice, this, + output_device_id), + callback); +} + void AudioOutputController::DoCreate(bool is_for_device_change) { DCHECK(message_loop_->BelongsToCurrentThread()); SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CreateTime"); + TRACE_EVENT0("audio", "AudioOutputController::DoCreate"); // Close() can be called before DoCreate() is executed. if (state_ == kClosed) @@ -159,6 +177,7 @@ void AudioOutputController::DoCreate(bool is_for_device_change) { void AudioOutputController::DoPlay() { DCHECK(message_loop_->BelongsToCurrentThread()); SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PlayTime"); + TRACE_EVENT0("audio", "AudioOutputController::DoPlay"); // We can start from created or paused state. if (state_ != kCreated && state_ != kPaused) @@ -179,10 +198,26 @@ void AudioOutputController::DoPlay() { power_poll_callback_.callback().Run(); #endif - // We start the AudioOutputStream lazily. + on_more_io_data_called_ = 0; AllowEntryToOnMoreIOData(); stream_->Start(this); + // For UMA tracking purposes, start the wedge detection timer. This allows us + // to record statistics about the number of wedged playbacks in the field. + // + // WedgeCheck() will look to see if |on_more_io_data_called_| is true after + // the timeout expires. Care must be taken to ensure the wedge check delay is + // large enough that the value isn't queried while OnMoreDataIO() is setting + // it. + // + // Timer self-manages its lifetime and WedgeCheck() will only record the UMA + // statistic if state is still kPlaying. Additional Start() calls will + // invalidate the previous timer. + wedge_timer_.reset(new base::OneShotTimer<AudioOutputController>()); + wedge_timer_->Start( + FROM_HERE, TimeDelta::FromSeconds(5), this, + &AudioOutputController::WedgeCheck); + handler_->OnPlaying(); } @@ -202,6 +237,7 @@ void AudioOutputController::StopStream() { DCHECK(message_loop_->BelongsToCurrentThread()); if (state_ == kPlaying) { + wedge_timer_.reset(); stream_->Stop(); DisallowEntryToOnMoreIOData(); @@ -216,14 +252,17 @@ void AudioOutputController::StopStream() { void AudioOutputController::DoPause() { DCHECK(message_loop_->BelongsToCurrentThread()); SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PauseTime"); + TRACE_EVENT0("audio", "AudioOutputController::DoPause"); StopStream(); if (state_ != kPaused) return; - // Send a special pause mark to the low-latency audio thread. - sync_reader_->UpdatePendingBytes(kPauseMark); + // Let the renderer know we've stopped. Necessary to let PPAPI clients know + // audio has been shutdown. TODO(dalecurtis): This stinks. PPAPI should have + // a better way to know when it should exit PPB_Audio_Shared::Run(). + sync_reader_->UpdatePendingBytes(-1); #if defined(AUDIO_POWER_MONITORING) // Paused means silence follows. @@ -236,6 +275,7 @@ void AudioOutputController::DoPause() { void AudioOutputController::DoClose() { DCHECK(message_loop_->BelongsToCurrentThread()); SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CloseTime"); + TRACE_EVENT0("audio", "AudioOutputController::DoClose"); if (state_ != kClosed) { DoStopCloseAndClearStream(); @@ -262,6 +302,31 @@ void AudioOutputController::DoSetVolume(double volume) { } } +std::string AudioOutputController::DoGetOutputDeviceId() const { + DCHECK(message_loop_->BelongsToCurrentThread()); + return output_device_id_; +} + +void AudioOutputController::DoSwitchOutputDevice( + const std::string& output_device_id) { + DCHECK(message_loop_->BelongsToCurrentThread()); + + if (state_ == kClosed) + return; + + if (output_device_id == output_device_id_) + return; + + output_device_id_ = output_device_id; + + // If output is currently diverted, we must not call OnDeviceChange + // since it would break the diverted setup. Once diversion is + // finished using StopDiverting() the output will switch to the new + // device ID. + if (stream_ != diverting_to_stream_) + OnDeviceChange(); +} + void AudioOutputController::DoReportError() { DCHECK(message_loop_->BelongsToCurrentThread()); if (state_ != kClosed) @@ -279,26 +344,16 @@ int AudioOutputController::OnMoreIOData(AudioBus* source, DisallowEntryToOnMoreIOData(); TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData"); - // The OS level audio APIs on Linux and Windows all have problems requesting - // data on a fixed interval. Sometimes they will issue calls back to back - // which can cause glitching, so wait until the renderer is ready. - // - // We also need to wait when diverting since the virtual stream will call this - // multiple times without waiting. - // - // NEVER wait on OSX unless a virtual stream is connected, otherwise we can - // end up hanging the entire OS. - // - // See many bugs for context behind this decision: http://crbug.com/170498, - // http://crbug.com/171651, http://crbug.com/174985, and more. -#if defined(OS_WIN) || defined(OS_LINUX) - const bool kShouldBlock = true; -#else - const bool kShouldBlock = diverting_to_stream_ != NULL; -#endif + // Indicate that we haven't wedged (at least not indefinitely, WedgeCheck() + // may have already fired if OnMoreIOData() took an abnormal amount of time). + // Since this thread is the only writer of |on_more_io_data_called_| once the + // thread starts, its safe to compare and then increment. + if (base::AtomicRefCountIsZero(&on_more_io_data_called_)) + base::AtomicRefCountInc(&on_more_io_data_called_); - const int frames = sync_reader_->Read(kShouldBlock, source, dest); - DCHECK_LE(0, frames); + sync_reader_->Read(source, dest); + + const int frames = dest->frames(); sync_reader_->UpdatePendingBytes( buffers_state.total_bytes() + frames * params_.GetBytesPerFrame()); @@ -339,6 +394,7 @@ void AudioOutputController::DoStopCloseAndClearStream() { void AudioOutputController::OnDeviceChange() { DCHECK(message_loop_->BelongsToCurrentThread()); SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.DeviceChangeTime"); + TRACE_EVENT0("audio", "AudioOutputController::OnDeviceChange"); // TODO(dalecurtis): Notify the renderer side that a device change has // occurred. Currently querying the hardware information here will lead to @@ -417,4 +473,21 @@ void AudioOutputController::DisallowEntryToOnMoreIOData() { DCHECK(is_zero); } +void AudioOutputController::WedgeCheck() { + DCHECK(message_loop_->BelongsToCurrentThread()); + + // If we should be playing and we haven't, that's a wedge. + if (state_ == kPlaying) { + const bool playback_success = + base::AtomicRefCountIsOne(&on_more_io_data_called_); + + UMA_HISTOGRAM_BOOLEAN( + "Media.AudioOutputControllerPlaybackStartupSuccess", playback_success); + + // Let the AudioManager try and fix it. + if (!playback_success) + audio_manager_->FixWedgedAudio(); + } +} + } // namespace media diff --git a/chromium/media/audio/audio_output_controller.h b/chromium/media/audio/audio_output_controller.h index 615c6a5e6c6..d16ce9e79b6 100644 --- a/chromium/media/audio/audio_output_controller.h +++ b/chromium/media/audio/audio_output_controller.h @@ -9,6 +9,7 @@ #include "base/callback.h" #include "base/cancelable_callback.h" #include "base/memory/ref_counted.h" +#include "base/timer/timer.h" #include "media/audio/audio_io.h" #include "media/audio/audio_manager.h" #include "media/audio/audio_power_monitor.h" @@ -91,11 +92,10 @@ class MEDIA_EXPORT AudioOutputController // prepare more data and perform synchronization. virtual void UpdatePendingBytes(uint32 bytes) = 0; - // Attempt to completely fill |dest|, return the actual number of frames - // that could be read. |source| may optionally be provided for input data. - // If |block| is specified, the Read() will block until data is available - // or a timeout is reached. - virtual int Read(bool block, const AudioBus* source, AudioBus* dest) = 0; + // Attempts to completely fill |dest|, zeroing |dest| if the request can not + // be fulfilled (due to timeout). |source| may optionally be provided for + // input data. + virtual void Read(const AudioBus* source, AudioBus* dest) = 0; // Close this synchronous reader. virtual void Close() = 0; @@ -135,6 +135,23 @@ class MEDIA_EXPORT AudioOutputController // Sets the volume of the audio output stream. void SetVolume(double volume); + // Calls |callback| (on the caller's thread) with the current output + // device ID. + void GetOutputDeviceId( + base::Callback<void(const std::string&)> callback) const; + + // Changes which output device to use. If desired, you can provide a + // callback that will be notified (on the thread you called from) + // when the function has completed execution. + // + // Changing the output device causes the controller to go through + // the same state transition back to the current state as a call to + // OnDeviceChange (unless it is currently diverting, see + // Start/StopDiverting below, in which case the state transition + // will happen when StopDiverting is called). + void SwitchOutputDevice(const std::string& output_device_id, + const base::Closure& callback); + // AudioSourceCallback implementation. virtual int OnMoreData(AudioBus* dest, AudioBuffersState buffers_state) OVERRIDE; @@ -185,6 +202,8 @@ class MEDIA_EXPORT AudioOutputController void DoPause(); void DoClose(); void DoSetVolume(double volume); + std::string DoGetOutputDeviceId() const; + void DoSwitchOutputDevice(const std::string& output_device_id); void DoReportError(); void DoStartDiverting(AudioOutputStream* to_stream); void DoStopDiverting(); @@ -204,13 +223,16 @@ class MEDIA_EXPORT AudioOutputController void AllowEntryToOnMoreIOData(); void DisallowEntryToOnMoreIOData(); + // Checks if a stream was started successfully but never calls OnMoreIOData(). + void WedgeCheck(); + AudioManager* const audio_manager_; 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_; + std::string output_device_id_; // Used by the unified IO to open the correct input device. const std::string input_device_id_; @@ -249,9 +271,9 @@ class MEDIA_EXPORT AudioOutputController 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_; + // Flags when we've asked for a stream to start but it never did. + base::AtomicRefCount on_more_io_data_called_; + scoped_ptr<base::OneShotTimer<AudioOutputController> > wedge_timer_; 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 a7118e17a30..457265ec970 100644 --- a/chromium/media/audio/audio_output_controller_unittest.cc +++ b/chromium/media/audio/audio_output_controller_unittest.cc @@ -10,6 +10,7 @@ #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/synchronization/waitable_event.h" +#include "media/audio/audio_manager_base.h" #include "media/audio/audio_output_controller.h" #include "media/audio/audio_parameters.h" #include "media/base/audio_bus.h" @@ -53,7 +54,7 @@ class MockAudioOutputControllerSyncReader MockAudioOutputControllerSyncReader() {} MOCK_METHOD1(UpdatePendingBytes, void(uint32 bytes)); - MOCK_METHOD3(Read, int(bool block, const AudioBus* source, AudioBus* dest)); + MOCK_METHOD2(Read, void(const AudioBus* source, AudioBus* dest)); MOCK_METHOD0(Close, void()); private: @@ -83,16 +84,16 @@ ACTION_P(SignalEvent, event) { static const float kBufferNonZeroData = 1.0f; ACTION(PopulateBuffer) { - arg2->Zero(); + arg1->Zero(); // Note: To confirm the buffer will be populated in these tests, it's // sufficient that only the first float in channel 0 is set to the value. - arg2->channel(0)[0] = kBufferNonZeroData; + arg1->channel(0)[0] = kBufferNonZeroData; } class AudioOutputControllerTest : public testing::Test { public: AudioOutputControllerTest() - : audio_manager_(AudioManager::Create()), + : audio_manager_(AudioManager::CreateForTesting()), create_event_(false, false), play_event_(false, false), read_event_(false, false), @@ -141,10 +142,9 @@ class AudioOutputControllerTest : public testing::Test { // sent from the render process. EXPECT_CALL(mock_sync_reader_, UpdatePendingBytes(_)) .Times(AtLeast(1)); - EXPECT_CALL(mock_sync_reader_, Read(_, _, _)) + EXPECT_CALL(mock_sync_reader_, Read(_, _)) .WillRepeatedly(DoAll(PopulateBuffer(), - SignalEvent(&read_event_), - Return(params_.frames_per_buffer()))); + SignalEvent(&read_event_))); controller_->Play(); } @@ -216,6 +216,19 @@ class AudioOutputControllerTest : public testing::Test { controller_->StopDiverting(); } + void SwitchDevice(bool diverting) { + if (!diverting) { + // Expect the current stream to close and a new stream to start + // playing if not diverting. When diverting, nothing happens + // until diverting is stopped. + EXPECT_CALL(mock_event_handler_, OnPlaying()) + .WillOnce(SignalEvent(&play_event_)); + } + + controller_->SwitchOutputDevice(AudioManagerBase::kDefaultDeviceName, + base::Bind(&base::DoNothing)); + } + void Close() { EXPECT_CALL(mock_sync_reader_, Close()); @@ -314,6 +327,18 @@ TEST_F(AudioOutputControllerTest, PlayDeviceChangeClose) { Close(); } +TEST_F(AudioOutputControllerTest, PlaySwitchDeviceClose) { + Create(kSamplesPerPacket); + WaitForCreate(); + Play(); + WaitForPlay(); + WaitForReads(); + SwitchDevice(false); + WaitForPlay(); + WaitForReads(); + Close(); +} + TEST_F(AudioOutputControllerTest, PlayDivertRevertClose) { Create(kSamplesPerPacket); WaitForCreate(); @@ -329,6 +354,22 @@ TEST_F(AudioOutputControllerTest, PlayDivertRevertClose) { Close(); } +TEST_F(AudioOutputControllerTest, PlayDivertSwitchDeviceRevertClose) { + Create(kSamplesPerPacket); + WaitForCreate(); + Play(); + WaitForPlay(); + WaitForReads(); + DivertWhilePlaying(); + WaitForPlay(); + SwitchDevice(true); + ReadDivertedAudioData(); + RevertWhilePlaying(); + WaitForPlay(); + WaitForReads(); + Close(); +} + TEST_F(AudioOutputControllerTest, PlayDivertRevertDivertRevertClose) { Create(kSamplesPerPacket); WaitForCreate(); diff --git a/chromium/media/audio/audio_output_device.cc b/chromium/media/audio/audio_output_device.cc index 0c406cab0d6..1f9efc185bd 100644 --- a/chromium/media/audio/audio_output_device.cc +++ b/chromium/media/audio/audio_output_device.cc @@ -10,8 +10,6 @@ #include "base/threading/thread_restrictions.h" #include "base/time/time.h" #include "media/audio/audio_output_controller.h" -#include "media/audio/audio_util.h" -#include "media/audio/shared_memory_util.h" #include "media/base/limits.h" namespace media { @@ -245,8 +243,8 @@ void AudioOutputDevice::OnStreamCreated( DCHECK(audio_thread_.IsStopped()); audio_callback_.reset(new AudioOutputDevice::AudioThreadCallback( audio_parameters_, handle, length, callback_)); - audio_thread_.Start(audio_callback_.get(), socket_handle, - "AudioOutputDevice"); + audio_thread_.Start( + audio_callback_.get(), socket_handle, "AudioOutputDevice", true); state_ = PAUSED; // We handle the case where Play() and/or Pause() may have been called @@ -273,26 +271,21 @@ AudioOutputDevice::AudioThreadCallback::AudioThreadCallback( base::SharedMemoryHandle memory, int memory_length, AudioRendererSink::RenderCallback* render_callback) - : AudioDeviceThread::Callback(audio_parameters, - memory, - memory_length, - 1), - render_callback_(render_callback) { -} + : AudioDeviceThread::Callback(audio_parameters, memory, memory_length, 1), + render_callback_(render_callback) {} AudioOutputDevice::AudioThreadCallback::~AudioThreadCallback() { } void AudioOutputDevice::AudioThreadCallback::MapSharedMemory() { CHECK_EQ(total_segments_, 1); - CHECK(shared_memory_.Map(TotalSharedMemorySizeInBytes(memory_length_))); + CHECK(shared_memory_.Map(memory_length_)); // Calculate output and input memory size. int output_memory_size = AudioBus::CalculateMemorySize(audio_parameters_); int input_channels = audio_parameters_.input_channels(); int frames = audio_parameters_.frames_per_buffer(); - int input_memory_size = - AudioBus::CalculateMemorySize(input_channels, frames); + int input_memory_size = AudioBus::CalculateMemorySize(input_channels, frames); int io_size = output_memory_size + input_memory_size; @@ -305,21 +298,17 @@ void AudioOutputDevice::AudioThreadCallback::MapSharedMemory() { // The input data is after the output data. char* input_data = static_cast<char*>(shared_memory_.memory()) + output_memory_size; - input_bus_ = - AudioBus::WrapMemory(input_channels, frames, input_data); + input_bus_ = AudioBus::WrapMemory(input_channels, frames, input_data); } } // Called whenever we receive notifications about pending data. void AudioOutputDevice::AudioThreadCallback::Process(int pending_data) { - if (pending_data == kPauseMark) { - memset(shared_memory_.memory(), 0, memory_length_); - SetActualDataSizeInBytes(&shared_memory_, memory_length_, 0); + // Negative |pending_data| indicates the browser side stream has stopped. + if (pending_data < 0) return; - } - // Convert the number of pending bytes in the render buffer - // into milliseconds. + // Convert the number of pending bytes in the render buffer into milliseconds. int audio_delay_milliseconds = pending_data / bytes_per_ms_; TRACE_EVENT0("audio", "AudioOutputDevice::FireRenderCallback"); @@ -328,25 +317,12 @@ void AudioOutputDevice::AudioThreadCallback::Process(int pending_data) { // |output_bus_| is wrapping the shared memory the Render() call is writing // directly into the shared memory. int input_channels = audio_parameters_.input_channels(); - size_t num_frames = audio_parameters_.frames_per_buffer(); - - if (input_bus_.get() && input_channels > 0) { - render_callback_->RenderIO(input_bus_.get(), - output_bus_.get(), - audio_delay_milliseconds); + if (input_bus_ && input_channels > 0) { + render_callback_->RenderIO( + input_bus_.get(), output_bus_.get(), audio_delay_milliseconds); } else { - num_frames = render_callback_->Render(output_bus_.get(), - audio_delay_milliseconds); + render_callback_->Render(output_bus_.get(), audio_delay_milliseconds); } - - // Let the host know we are done. - // TODO(dalecurtis): Technically this is not always correct. Due to channel - // padding for alignment, there may be more data available than this. We're - // relying on AudioSyncReader::Read() to parse this with that in mind. Rename - // these methods to Set/GetActualFrameCount(). - SetActualDataSizeInBytes( - &shared_memory_, memory_length_, - num_frames * sizeof(*output_bus_->channel(0)) * output_bus_->channels()); } } // namespace media. diff --git a/chromium/media/audio/audio_output_device_unittest.cc b/chromium/media/audio/audio_output_device_unittest.cc index 96da77d7404..7aca2627745 100644 --- a/chromium/media/audio/audio_output_device_unittest.cc +++ b/chromium/media/audio/audio_output_device_unittest.cc @@ -12,7 +12,6 @@ #include "base/test/test_timeouts.h" #include "media/audio/audio_output_device.h" #include "media/audio/sample_rates.h" -#include "media/audio/shared_memory_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock_mutant.h" #include "testing/gtest/include/gtest/gtest.h" @@ -124,8 +123,6 @@ class AudioOutputDeviceTest DISALLOW_COPY_AND_ASSIGN(AudioOutputDeviceTest); }; -static const int kStreamId = 123; - int AudioOutputDeviceTest::CalculateMemorySize() { // Calculate output and input memory size. int output_memory_size = @@ -135,13 +132,7 @@ int AudioOutputDeviceTest::CalculateMemorySize() { int input_memory_size = AudioBus::CalculateMemorySize(input_channels_, frames); - int io_buffer_size = output_memory_size + input_memory_size; - - // This is where it gets a bit hacky. The shared memory contract between - // AudioOutputDevice and its browser side counter part includes a bit more - // than just the audio data, so we must call TotalSharedMemorySizeInBytes() - // to get the actual size needed to fit the audio data plus the extra data. - return TotalSharedMemorySizeInBytes(io_buffer_size); + return output_memory_size + input_memory_size; } AudioOutputDeviceTest::AudioOutputDeviceTest() @@ -195,7 +186,7 @@ void AudioOutputDeviceTest::CreateStream() { &duplicated_memory_handle)); audio_device_->OnStreamCreated(duplicated_memory_handle, audio_device_socket, - PacketSizeInBytes(kMemorySize)); + kMemorySize); io_loop_.RunUntilIdle(); } diff --git a/chromium/media/audio/audio_output_dispatcher.cc b/chromium/media/audio/audio_output_dispatcher.cc index a151c449f02..89912c07dce 100644 --- a/chromium/media/audio/audio_output_dispatcher.cc +++ b/chromium/media/audio/audio_output_dispatcher.cc @@ -4,7 +4,7 @@ #include "media/audio/audio_output_dispatcher.h" -#include "base/message_loop/message_loop.h" +#include "base/message_loop/message_loop_proxy.h" namespace media { @@ -14,7 +14,7 @@ AudioOutputDispatcher::AudioOutputDispatcher( const std::string& output_device_id, const std::string& input_device_id) : audio_manager_(audio_manager), - message_loop_(base::MessageLoop::current()), + message_loop_(audio_manager->GetMessageLoop()), params_(params), output_device_id_(output_device_id), input_device_id_(input_device_id) { @@ -24,7 +24,7 @@ AudioOutputDispatcher::AudioOutputDispatcher( } AudioOutputDispatcher::~AudioOutputDispatcher() { - DCHECK_EQ(base::MessageLoop::current(), message_loop_); + DCHECK(message_loop_->BelongsToCurrentThread()); } } // namespace media diff --git a/chromium/media/audio/audio_output_dispatcher.h b/chromium/media/audio/audio_output_dispatcher.h index 30266ed6a9a..d707aff14b6 100644 --- a/chromium/media/audio/audio_output_dispatcher.h +++ b/chromium/media/audio/audio_output_dispatcher.h @@ -26,7 +26,7 @@ #include "media/audio/audio_parameters.h" namespace base { -class MessageLoop; +class MessageLoopProxy; } namespace media { @@ -66,21 +66,26 @@ class MEDIA_EXPORT AudioOutputDispatcher // Called on the audio thread when the AudioManager is shutting down. virtual void Shutdown() = 0; + // Called by the AudioManager to restart streams when a wedge is detected. A + // wedge means the OS failed to request any audio after StartStream(). When a + // wedge is detected all streams across all dispatchers must be closed. After + // all streams are closed, streams are restarted. See http://crbug.com/160920 + virtual void CloseStreamsForWedgeFix() = 0; + virtual void RestartStreamsForWedgeFix() = 0; + // Accessor to the input device id used by unified IO. const std::string& input_device_id() const { return input_device_id_; } protected: friend class base::RefCountedThreadSafe<AudioOutputDispatcher>; - friend class AudioOutputProxyTest; - virtual ~AudioOutputDispatcher(); // A no-reference-held pointer (we don't want circular references) back to the // AudioManager that owns this object. AudioManager* audio_manager_; - base::MessageLoop* message_loop_; - AudioParameters params_; - const std::string output_device_id_; + const scoped_refptr<base::MessageLoopProxy> message_loop_; + const AudioParameters params_; + 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 bcdcd65146e..5118bef71e9 100644 --- a/chromium/media/audio/audio_output_dispatcher_impl.cc +++ b/chromium/media/audio/audio_output_dispatcher_impl.cc @@ -12,7 +12,6 @@ #include "base/time/time.h" #include "media/audio/audio_io.h" #include "media/audio/audio_output_proxy.h" -#include "media/audio/audio_util.h" namespace media { @@ -22,36 +21,33 @@ AudioOutputDispatcherImpl::AudioOutputDispatcherImpl( const std::string& output_device_id, const std::string& input_device_id, const base::TimeDelta& close_delay) - : 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()))), - paused_proxies_(0), - weak_this_(this), + : AudioOutputDispatcher(audio_manager, + params, + output_device_id, + input_device_id), + idle_proxies_(0), close_timer_(FROM_HERE, close_delay, this, - &AudioOutputDispatcherImpl::ClosePendingStreams) { -} + &AudioOutputDispatcherImpl::CloseAllIdleStreams), + audio_log_( + audio_manager->CreateAudioLog(AudioLogFactory::AUDIO_OUTPUT_STREAM)), + audio_stream_id_(0) {} AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() { + DCHECK_EQ(idle_proxies_, 0u); DCHECK(proxy_to_physical_map_.empty()); DCHECK(idle_streams_.empty()); - DCHECK(pausing_streams_.empty()); } bool AudioOutputDispatcherImpl::OpenStream() { - DCHECK_EQ(base::MessageLoop::current(), message_loop_); - - paused_proxies_++; + DCHECK(message_loop_->BelongsToCurrentThread()); // Ensure that there is at least one open stream. - if (idle_streams_.empty() && !CreateAndOpenStream()) { - paused_proxies_--; + if (idle_streams_.empty() && !CreateAndOpenStream()) return false; - } + ++idle_proxies_; close_timer_.Reset(); return true; } @@ -59,34 +55,34 @@ bool AudioOutputDispatcherImpl::OpenStream() { bool AudioOutputDispatcherImpl::StartStream( AudioOutputStream::AudioSourceCallback* callback, AudioOutputProxy* stream_proxy) { - DCHECK_EQ(base::MessageLoop::current(), message_loop_); + DCHECK(message_loop_->BelongsToCurrentThread()); + DCHECK(proxy_to_physical_map_.find(stream_proxy) == + proxy_to_physical_map_.end()); if (idle_streams_.empty() && !CreateAndOpenStream()) return false; AudioOutputStream* physical_stream = idle_streams_.back(); - DCHECK(physical_stream); idle_streams_.pop_back(); - DCHECK_GT(paused_proxies_, 0u); - --paused_proxies_; - - close_timer_.Reset(); - - // Schedule task to allocate streams for other proxies if we need to. - message_loop_->PostTask(FROM_HERE, base::Bind( - &AudioOutputDispatcherImpl::OpenTask, weak_this_.GetWeakPtr())); + DCHECK_GT(idle_proxies_, 0u); + --idle_proxies_; double volume = 0; stream_proxy->GetVolume(&volume); physical_stream->SetVolume(volume); + const int stream_id = audio_stream_ids_[physical_stream]; + audio_log_->OnSetVolume(stream_id, volume); physical_stream->Start(callback); + audio_log_->OnStarted(stream_id); proxy_to_physical_map_[stream_proxy] = physical_stream; + + close_timer_.Reset(); return true; } void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) { - DCHECK_EQ(base::MessageLoop::current(), message_loop_); + DCHECK(message_loop_->BelongsToCurrentThread()); AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); DCHECK(it != proxy_to_physical_map_.end()); @@ -94,81 +90,46 @@ void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) { proxy_to_physical_map_.erase(it); physical_stream->Stop(); + audio_log_->OnStopped(audio_stream_ids_[physical_stream]); + ++idle_proxies_; + idle_streams_.push_back(physical_stream); - ++paused_proxies_; - - pausing_streams_.push_front(physical_stream); - - // Don't recycle stream until two buffers worth of time has elapsed. - message_loop_->PostDelayedTask( - FROM_HERE, - base::Bind(&AudioOutputDispatcherImpl::StopStreamTask, - weak_this_.GetWeakPtr()), - pause_delay_); + close_timer_.Reset(); } void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy* stream_proxy, double volume) { - DCHECK_EQ(base::MessageLoop::current(), message_loop_); + DCHECK(message_loop_->BelongsToCurrentThread()); AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); if (it != proxy_to_physical_map_.end()) { AudioOutputStream* physical_stream = it->second; physical_stream->SetVolume(volume); + audio_log_->OnSetVolume(audio_stream_ids_[physical_stream], volume); } } -void AudioOutputDispatcherImpl::StopStreamTask() { - DCHECK_EQ(base::MessageLoop::current(), message_loop_); - - if (pausing_streams_.empty()) - return; - - AudioOutputStream* stream = pausing_streams_.back(); - pausing_streams_.pop_back(); - idle_streams_.push_back(stream); - close_timer_.Reset(); -} - void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) { - DCHECK_EQ(base::MessageLoop::current(), message_loop_); + DCHECK(message_loop_->BelongsToCurrentThread()); - while (!pausing_streams_.empty()) { - idle_streams_.push_back(pausing_streams_.back()); - pausing_streams_.pop_back(); - } - - DCHECK_GT(paused_proxies_, 0u); - paused_proxies_--; + DCHECK_GT(idle_proxies_, 0u); + --idle_proxies_; - while (idle_streams_.size() > paused_proxies_) { - idle_streams_.back()->Close(); - idle_streams_.pop_back(); - } + // Leave at least a single stream running until the close timer fires to help + // cycle time when streams are opened and closed repeatedly. + CloseIdleStreams(std::max(idle_proxies_, static_cast<size_t>(1))); + close_timer_.Reset(); } void AudioOutputDispatcherImpl::Shutdown() { - DCHECK_EQ(base::MessageLoop::current(), message_loop_); - - // Cancel any pending tasks to close paused streams or create new ones. - weak_this_.InvalidateWeakPtrs(); + DCHECK(message_loop_->BelongsToCurrentThread()); - // No AudioOutputProxy objects should hold a reference to us when we get - // to this stage. - DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference"; - - AudioOutputStreamList::iterator it = idle_streams_.begin(); - for (; it != idle_streams_.end(); ++it) - (*it)->Close(); - idle_streams_.clear(); - - it = pausing_streams_.begin(); - for (; it != pausing_streams_.end(); ++it) - (*it)->Close(); - pausing_streams_.clear(); + // Close all idle streams immediately. The |close_timer_| will handle + // invalidating any outstanding tasks upon its destruction. + CloseAllIdleStreams(); } bool AudioOutputDispatcherImpl::CreateAndOpenStream() { - DCHECK_EQ(base::MessageLoop::current(), message_loop_); + DCHECK(message_loop_->BelongsToCurrentThread()); AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream( params_, output_device_id_, input_device_id_); if (!stream) @@ -178,29 +139,48 @@ bool AudioOutputDispatcherImpl::CreateAndOpenStream() { stream->Close(); return false; } + + const int stream_id = audio_stream_id_++; + audio_stream_ids_[stream] = stream_id; + audio_log_->OnCreated( + stream_id, params_, input_device_id_, output_device_id_); + idle_streams_.push_back(stream); return true; } -void AudioOutputDispatcherImpl::OpenTask() { - DCHECK_EQ(base::MessageLoop::current(), message_loop_); - // Make sure that we have at least one stream allocated if there - // are paused streams. - if (paused_proxies_ > 0 && idle_streams_.empty() && - pausing_streams_.empty()) { - CreateAndOpenStream(); +void AudioOutputDispatcherImpl::CloseAllIdleStreams() { + DCHECK(message_loop_->BelongsToCurrentThread()); + CloseIdleStreams(0); +} + +void AudioOutputDispatcherImpl::CloseIdleStreams(size_t keep_alive) { + DCHECK(message_loop_->BelongsToCurrentThread()); + if (idle_streams_.size() <= keep_alive) + return; + for (size_t i = keep_alive; i < idle_streams_.size(); ++i) { + AudioOutputStream* stream = idle_streams_[i]; + stream->Close(); + + AudioStreamIDMap::iterator it = audio_stream_ids_.find(stream); + DCHECK(it != audio_stream_ids_.end()); + audio_log_->OnClosed(it->second); + audio_stream_ids_.erase(it); } + idle_streams_.erase(idle_streams_.begin() + keep_alive, idle_streams_.end()); +} - close_timer_.Reset(); +void AudioOutputDispatcherImpl::CloseStreamsForWedgeFix() { + DCHECK(message_loop_->BelongsToCurrentThread()); + CloseAllIdleStreams(); } -// This method is called by |close_timer_|. -void AudioOutputDispatcherImpl::ClosePendingStreams() { - DCHECK_EQ(base::MessageLoop::current(), message_loop_); - while (!idle_streams_.empty()) { - idle_streams_.back()->Close(); - idle_streams_.pop_back(); - } +void AudioOutputDispatcherImpl::RestartStreamsForWedgeFix() { + DCHECK(message_loop_->BelongsToCurrentThread()); + + // Should only be called when the dispatcher is used with fake streams which + // don't need to be shutdown or restarted. + CHECK_EQ(params_.format(), AudioParameters::AUDIO_FAKE); } } // namespace media diff --git a/chromium/media/audio/audio_output_dispatcher_impl.h b/chromium/media/audio/audio_output_dispatcher_impl.h index b59f835f9b0..037e11466f1 100644 --- a/chromium/media/audio/audio_output_dispatcher_impl.h +++ b/chromium/media/audio/audio_output_dispatcher_impl.h @@ -13,14 +13,14 @@ #ifndef MEDIA_AUDIO_AUDIO_OUTPUT_DISPATCHER_IMPL_H_ #define MEDIA_AUDIO_AUDIO_OUTPUT_DISPATCHER_IMPL_H_ -#include <list> #include <map> +#include <vector> #include "base/basictypes.h" #include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" #include "base/timer/timer.h" #include "media/audio/audio_io.h" +#include "media/audio/audio_logging.h" #include "media/audio/audio_manager.h" #include "media/audio/audio_output_dispatcher.h" #include "media/audio/audio_parameters.h" @@ -31,8 +31,8 @@ class AudioOutputProxy; class MEDIA_EXPORT AudioOutputDispatcherImpl : public AudioOutputDispatcher { public: - // |close_delay_ms| specifies delay after the stream is paused until - // the audio device is closed. + // |close_delay| specifies delay after the stream is idle until the audio + // device is closed. AudioOutputDispatcherImpl(AudioManager* audio_manager, const AudioParameters& params, const std::string& output_device_id, @@ -48,52 +48,53 @@ class MEDIA_EXPORT AudioOutputDispatcherImpl : public AudioOutputDispatcher { virtual bool StartStream(AudioOutputStream::AudioSourceCallback* callback, AudioOutputProxy* stream_proxy) OVERRIDE; - // Holds the physical stream temporarily in |pausing_streams_| and then - // |stream| is added to the pool of pending streams (i.e. |idle_streams_|). + // Stops the stream assigned to the specified proxy and moves it into + // |idle_streams_| for reuse by other proxies. virtual void StopStream(AudioOutputProxy* stream_proxy) OVERRIDE; virtual void StreamVolumeSet(AudioOutputProxy* stream_proxy, double volume) OVERRIDE; + // Closes |idle_streams_| until the number of |idle_streams_| is equal to the + // |idle_proxies_| count. If there are no |idle_proxies_| a single stream is + // kept alive until |close_timer_| fires. virtual void CloseStream(AudioOutputProxy* stream_proxy) OVERRIDE; virtual void Shutdown() OVERRIDE; + virtual void CloseStreamsForWedgeFix() OVERRIDE; + virtual void RestartStreamsForWedgeFix() OVERRIDE; + private: - typedef std::map<AudioOutputProxy*, AudioOutputStream*> AudioStreamMap; friend class base::RefCountedThreadSafe<AudioOutputDispatcherImpl>; virtual ~AudioOutputDispatcherImpl(); - friend class AudioOutputProxyTest; - // Creates a new physical output stream, opens it and pushes to // |idle_streams_|. Returns false if the stream couldn't be created or // opened. bool CreateAndOpenStream(); - // A task scheduled by StartStream(). Opens a new stream and puts - // it in |idle_streams_|. - void OpenTask(); - - // Before a stream is reused, it should sit idle for a bit. This task is - // called once that time has elapsed. - void StopStreamTask(); + // Closes all |idle_streams_|. + void CloseAllIdleStreams(); + // Similar to CloseAllIdleStreams(), but keeps |keep_alive| streams alive. + void CloseIdleStreams(size_t keep_alive); - // Called by |close_timer_|. Closes all pending streams. - void ClosePendingStreams(); + size_t idle_proxies_; + std::vector<AudioOutputStream*> idle_streams_; - base::TimeDelta pause_delay_; - size_t paused_proxies_; - typedef std::list<AudioOutputStream*> AudioOutputStreamList; - AudioOutputStreamList idle_streams_; - AudioOutputStreamList pausing_streams_; - - // Used to post delayed tasks to ourselves that we cancel inside Shutdown(). - base::WeakPtrFactory<AudioOutputDispatcherImpl> weak_this_; + // When streams are stopped they're added to |idle_streams_|, if no stream is + // reused before |close_delay_| elapses |close_timer_| will run + // CloseIdleStreams(). base::DelayTimer<AudioOutputDispatcherImpl> close_timer_; + typedef std::map<AudioOutputProxy*, AudioOutputStream*> AudioStreamMap; AudioStreamMap proxy_to_physical_map_; + scoped_ptr<AudioLog> audio_log_; + typedef std::map<AudioOutputStream*, int> AudioStreamIDMap; + AudioStreamIDMap audio_stream_ids_; + int audio_stream_id_; + DISALLOW_COPY_AND_ASSIGN(AudioOutputDispatcherImpl); }; diff --git a/chromium/media/audio/audio_output_proxy_unittest.cc b/chromium/media/audio/audio_output_proxy_unittest.cc index 1806ce66131..cea098820aa 100644 --- a/chromium/media/audio/audio_output_proxy_unittest.cc +++ b/chromium/media/audio/audio_output_proxy_unittest.cc @@ -6,11 +6,13 @@ #include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop_proxy.h" +#include "base/run_loop.h" #include "media/audio/audio_manager.h" #include "media/audio/audio_manager_base.h" #include "media/audio/audio_output_dispatcher_impl.h" #include "media/audio/audio_output_proxy.h" #include "media/audio/audio_output_resampler.h" +#include "media/audio/fake_audio_log_factory.h" #include "media/audio/fake_audio_output_stream.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -36,10 +38,7 @@ using media::FakeAudioOutputStream; namespace { -static const int kTestCloseDelayMs = 100; - -// Used in the test where we don't want a stream to be closed unexpectedly. -static const int kTestBigCloseDelaySeconds = 1000; +static const int kTestCloseDelayMs = 10; // Delay between callbacks to AudioSourceCallback::OnMoreData. static const int kOnMoreDataCallbackDelayMs = 10; @@ -87,14 +86,14 @@ class MockAudioOutputStream : public AudioOutputStream { class MockAudioManager : public AudioManagerBase { public: - MockAudioManager() {} + MockAudioManager() : AudioManagerBase(&fake_audio_log_factory_) {} virtual ~MockAudioManager() { Shutdown(); } MOCK_METHOD0(HasAudioOutputDevices, bool()); MOCK_METHOD0(HasAudioInputDevices, bool()); - MOCK_METHOD0(GetAudioInputDeviceModel, string16()); + MOCK_METHOD0(GetAudioInputDeviceModel, base::string16()); MOCK_METHOD3(MakeAudioOutputStream, AudioOutputStream*( const AudioParameters& params, const std::string& device_id, @@ -107,6 +106,7 @@ class MockAudioManager : public AudioManagerBase { const AudioParameters& params, const std::string& device_id)); MOCK_METHOD0(ShowAudioInputSettings, void()); MOCK_METHOD0(GetMessageLoop, scoped_refptr<base::MessageLoopProxy>()); + MOCK_METHOD0(GetWorkerLoop, scoped_refptr<base::MessageLoopProxy>()); MOCK_METHOD1(GetAudioInputDeviceNames, void( media::AudioDeviceNames* device_name)); @@ -121,6 +121,9 @@ class MockAudioManager : public AudioManagerBase { const AudioParameters& params, const std::string& device_id)); MOCK_METHOD2(GetPreferredOutputStreamParameters, AudioParameters( const std::string& device_id, const AudioParameters& params)); + + private: + media::FakeAudioLogFactory fake_audio_log_factory_; }; class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback { @@ -145,33 +148,28 @@ class AudioOutputProxyTest : public testing::Test { virtual void SetUp() { EXPECT_CALL(manager_, GetMessageLoop()) .WillRepeatedly(Return(message_loop_.message_loop_proxy())); + EXPECT_CALL(manager_, GetWorkerLoop()) + .WillRepeatedly(Return(message_loop_.message_loop_proxy())); + // Use a low sample rate and large buffer size when testing otherwise the + // FakeAudioOutputStream will keep the message loop busy indefinitely; i.e., + // RunUntilIdle() will never terminate. + params_ = AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, + CHANNEL_LAYOUT_STEREO, 8000, 16, 2048); InitDispatcher(base::TimeDelta::FromMilliseconds(kTestCloseDelayMs)); } virtual void TearDown() { - // All paused proxies should have been closed at this point. - EXPECT_EQ(0u, dispatcher_impl_->paused_proxies_); - // This is necessary to free all proxy objects that have been // closed by the test. message_loop_.RunUntilIdle(); } virtual void InitDispatcher(base::TimeDelta close_delay) { - // Use a low sample rate and large buffer size when testing otherwise the - // FakeAudioOutputStream will keep the message loop busy indefinitely; i.e., - // RunUntilIdle() will never terminate. - params_ = AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, - CHANNEL_LAYOUT_STEREO, 8000, 16, 2048); dispatcher_impl_ = new AudioOutputDispatcherImpl(&manager(), params_, std::string(), std::string(), close_delay); - - // Necessary to know how long the dispatcher will wait before posting - // StopStreamTask. - pause_delay_ = dispatcher_impl_->pause_delay_; } virtual void OnStart() {} @@ -180,15 +178,24 @@ class AudioOutputProxyTest : public testing::Test { return manager_; } - // Wait for the close timer to fire. - void WaitForCloseTimer(const int timer_delay_ms) { - message_loop_.RunUntilIdle(); // OpenTask() may reset the timer. - base::PlatformThread::Sleep( - base::TimeDelta::FromMilliseconds(timer_delay_ms) * 2); - message_loop_.RunUntilIdle(); + void WaitForCloseTimer(MockAudioOutputStream* stream) { + base::RunLoop run_loop; + EXPECT_CALL(*stream, Close()) + .WillOnce(testing::InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); + run_loop.Run(); } - // Methods that do actual tests. + void CloseAndWaitForCloseTimer(AudioOutputProxy* proxy, + MockAudioOutputStream* stream) { + // Close the stream and verify it doesn't happen immediately. + proxy->Close(); + Mock::VerifyAndClear(stream); + + // Wait for the actual close event to come from the close timer. + WaitForCloseTimer(stream); + } + + // Basic Open() and Close() test. void OpenAndClose(AudioOutputDispatcher* dispatcher) { MockAudioOutputStream stream(&manager_, params_); @@ -196,16 +203,13 @@ class AudioOutputProxyTest : public testing::Test { .WillOnce(Return(&stream)); EXPECT_CALL(stream, Open()) .WillOnce(Return(true)); - EXPECT_CALL(stream, Close()) - .Times(1); AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher); EXPECT_TRUE(proxy->Open()); - proxy->Close(); - WaitForCloseTimer(kTestCloseDelayMs); + CloseAndWaitForCloseTimer(proxy, &stream); } - // Create a stream, and then calls Start() and Stop(). + // Creates a stream, and then calls Start() and Stop(). void StartAndStop(AudioOutputDispatcher* dispatcher) { MockAudioOutputStream stream(&manager_, params_); @@ -215,8 +219,6 @@ class AudioOutputProxyTest : public testing::Test { .WillOnce(Return(true)); EXPECT_CALL(stream, SetVolume(_)) .Times(1); - EXPECT_CALL(stream, Close()) - .Times(1); AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher); EXPECT_TRUE(proxy->Open()); @@ -225,13 +227,12 @@ class AudioOutputProxyTest : public testing::Test { OnStart(); proxy->Stop(); - proxy->Close(); - WaitForCloseTimer(kTestCloseDelayMs); + CloseAndWaitForCloseTimer(proxy, &stream); EXPECT_TRUE(stream.stop_called()); EXPECT_TRUE(stream.start_called()); } - // Verify that the stream is closed after Stop is called. + // Verify that the stream is closed after Stop() is called. void CloseAfterStop(AudioOutputDispatcher* dispatcher) { MockAudioOutputStream stream(&manager_, params_); @@ -241,8 +242,6 @@ class AudioOutputProxyTest : public testing::Test { .WillOnce(Return(true)); EXPECT_CALL(stream, SetVolume(_)) .Times(1); - EXPECT_CALL(stream, Close()) - .Times(1); AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher); EXPECT_TRUE(proxy->Open()); @@ -251,19 +250,14 @@ class AudioOutputProxyTest : public testing::Test { OnStart(); proxy->Stop(); - // Wait for StopStream() to post StopStreamTask(). - base::PlatformThread::Sleep(pause_delay_ * 2); - WaitForCloseTimer(kTestCloseDelayMs); - - // Verify expectation before calling Close(). - Mock::VerifyAndClear(&stream); - + // Wait for the close timer to fire after StopStream(). + WaitForCloseTimer(&stream); proxy->Close(); EXPECT_TRUE(stream.stop_called()); EXPECT_TRUE(stream.start_called()); } - // Create two streams, but don't start them. Only one device must be open. + // Create two streams, but don't start them. Only one device must be opened. void TwoStreams(AudioOutputDispatcher* dispatcher) { MockAudioOutputStream stream(&manager_, params_); @@ -271,16 +265,13 @@ class AudioOutputProxyTest : public testing::Test { .WillOnce(Return(&stream)); EXPECT_CALL(stream, Open()) .WillOnce(Return(true)); - EXPECT_CALL(stream, Close()) - .Times(1); AudioOutputProxy* proxy1 = new AudioOutputProxy(dispatcher); AudioOutputProxy* proxy2 = new AudioOutputProxy(dispatcher); EXPECT_TRUE(proxy1->Open()); EXPECT_TRUE(proxy2->Open()); proxy1->Close(); - proxy2->Close(); - WaitForCloseTimer(kTestCloseDelayMs); + CloseAndWaitForCloseTimer(proxy2, &stream); EXPECT_FALSE(stream.stop_called()); EXPECT_FALSE(stream.start_called()); } @@ -299,7 +290,6 @@ class AudioOutputProxyTest : public testing::Test { AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher); EXPECT_FALSE(proxy->Open()); proxy->Close(); - WaitForCloseTimer(kTestCloseDelayMs); EXPECT_FALSE(stream.stop_called()); EXPECT_FALSE(stream.start_called()); } @@ -311,61 +301,45 @@ class AudioOutputProxyTest : public testing::Test { .WillOnce(Return(&stream)); EXPECT_CALL(stream, Open()) .WillOnce(Return(true)); - EXPECT_CALL(stream, Close()) - .Times(1); AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher); EXPECT_TRUE(proxy->Open()); - // Simulate a delay. - base::PlatformThread::Sleep( - base::TimeDelta::FromMilliseconds(kTestCloseDelayMs) * 2); - message_loop_.RunUntilIdle(); - - // Verify expectation before calling Close(). - Mock::VerifyAndClear(&stream); - + WaitForCloseTimer(&stream); proxy->Close(); EXPECT_FALSE(stream.stop_called()); EXPECT_FALSE(stream.start_called()); } - void TwoStreams_OnePlaying(AudioOutputDispatcher* dispatcher) { - MockAudioOutputStream stream1(&manager_, params_); - MockAudioOutputStream stream2(&manager_, params_); + void OneStream_TwoPlays(AudioOutputDispatcher* dispatcher) { + MockAudioOutputStream stream(&manager_, params_); EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) - .WillOnce(Return(&stream1)) - .WillOnce(Return(&stream2)); - - EXPECT_CALL(stream1, Open()) - .WillOnce(Return(true)); - EXPECT_CALL(stream1, SetVolume(_)) - .Times(1); - EXPECT_CALL(stream1, Close()) - .Times(1); + .WillOnce(Return(&stream)); - EXPECT_CALL(stream2, Open()) + EXPECT_CALL(stream, Open()) .WillOnce(Return(true)); - EXPECT_CALL(stream2, Close()) - .Times(1); + EXPECT_CALL(stream, SetVolume(_)) + .Times(2); AudioOutputProxy* proxy1 = new AudioOutputProxy(dispatcher); - AudioOutputProxy* proxy2 = new AudioOutputProxy(dispatcher); EXPECT_TRUE(proxy1->Open()); - EXPECT_TRUE(proxy2->Open()); proxy1->Start(&callback_); - message_loop_.RunUntilIdle(); OnStart(); proxy1->Stop(); + // The stream should now be idle and get reused by |proxy2|. + AudioOutputProxy* proxy2 = new AudioOutputProxy(dispatcher); + EXPECT_TRUE(proxy2->Open()); + proxy2->Start(&callback_); + OnStart(); + proxy2->Stop(); + proxy1->Close(); - proxy2->Close(); - EXPECT_TRUE(stream1.stop_called()); - EXPECT_TRUE(stream1.start_called()); - EXPECT_FALSE(stream2.stop_called()); - EXPECT_FALSE(stream2.start_called()); + CloseAndWaitForCloseTimer(proxy2, &stream); + EXPECT_TRUE(stream.stop_called()); + EXPECT_TRUE(stream.start_called()); } void TwoStreams_BothPlaying(AudioOutputDispatcher* dispatcher) { @@ -380,15 +354,11 @@ class AudioOutputProxyTest : public testing::Test { .WillOnce(Return(true)); EXPECT_CALL(stream1, SetVolume(_)) .Times(1); - EXPECT_CALL(stream1, Close()) - .Times(1); EXPECT_CALL(stream2, Open()) .WillOnce(Return(true)); EXPECT_CALL(stream2, SetVolume(_)) .Times(1); - EXPECT_CALL(stream2, Close()) - .Times(1); AudioOutputProxy* proxy1 = new AudioOutputProxy(dispatcher); AudioOutputProxy* proxy2 = new AudioOutputProxy(dispatcher); @@ -399,10 +369,11 @@ class AudioOutputProxyTest : public testing::Test { proxy2->Start(&callback_); OnStart(); proxy1->Stop(); + CloseAndWaitForCloseTimer(proxy1, &stream1); + proxy2->Stop(); + CloseAndWaitForCloseTimer(proxy2, &stream2); - proxy1->Close(); - proxy2->Close(); EXPECT_TRUE(stream1.stop_called()); EXPECT_TRUE(stream1.start_called()); EXPECT_TRUE(stream2.stop_called()); @@ -416,19 +387,11 @@ class AudioOutputProxyTest : public testing::Test { .WillOnce(Return(&stream)); EXPECT_CALL(stream, Open()) .WillOnce(Return(true)); - EXPECT_CALL(stream, Close()) - .Times(1); AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher); EXPECT_TRUE(proxy->Open()); - // Simulate a delay. - base::PlatformThread::Sleep( - base::TimeDelta::FromMilliseconds(kTestCloseDelayMs) * 2); - message_loop_.RunUntilIdle(); - - // Verify expectation before calling Close(). - Mock::VerifyAndClear(&stream); + WaitForCloseTimer(&stream); // |stream| is closed at this point. Start() should reopen it again. EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) @@ -452,7 +415,6 @@ class AudioOutputProxyTest : public testing::Test { base::MessageLoop message_loop_; scoped_refptr<AudioOutputDispatcherImpl> dispatcher_impl_; - base::TimeDelta pause_delay_; MockAudioManager manager_; MockAudioSourceCallback callback_; AudioParameters params_; @@ -465,7 +427,6 @@ class AudioOutputResamplerTest : public AudioOutputProxyTest { } virtual void InitDispatcher(base::TimeDelta close_delay) OVERRIDE { - AudioOutputProxyTest::InitDispatcher(close_delay); // Use a low sample rate and large buffer size when testing otherwise the // FakeAudioOutputStream will keep the message loop busy indefinitely; i.e., // RunUntilIdle() will never terminate. @@ -478,10 +439,13 @@ class AudioOutputResamplerTest : public AudioOutputProxyTest { } virtual void OnStart() OVERRIDE { - // Let start run for a bit. - message_loop_.RunUntilIdle(); - base::PlatformThread::Sleep( + // Let Start() run for a bit. + base::RunLoop run_loop; + message_loop_.PostDelayedTask( + FROM_HERE, + run_loop.QuitClosure(), base::TimeDelta::FromMilliseconds(kStartRunTimeMs)); + run_loop.Run(); } protected: @@ -490,86 +454,82 @@ class AudioOutputResamplerTest : public AudioOutputProxyTest { }; TEST_F(AudioOutputProxyTest, CreateAndClose) { - AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher_impl_.get()); + AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher_impl_); proxy->Close(); } TEST_F(AudioOutputResamplerTest, CreateAndClose) { - AudioOutputProxy* proxy = new AudioOutputProxy(resampler_.get()); + AudioOutputProxy* proxy = new AudioOutputProxy(resampler_); proxy->Close(); } TEST_F(AudioOutputProxyTest, OpenAndClose) { - OpenAndClose(dispatcher_impl_.get()); + OpenAndClose(dispatcher_impl_); } TEST_F(AudioOutputResamplerTest, OpenAndClose) { - OpenAndClose(resampler_.get()); + OpenAndClose(resampler_); } // Create a stream, and verify that it is closed after kTestCloseDelayMs. // if it doesn't start playing. TEST_F(AudioOutputProxyTest, CreateAndWait) { - CreateAndWait(dispatcher_impl_.get()); + CreateAndWait(dispatcher_impl_); } // Create a stream, and verify that it is closed after kTestCloseDelayMs. // if it doesn't start playing. TEST_F(AudioOutputResamplerTest, CreateAndWait) { - CreateAndWait(resampler_.get()); + CreateAndWait(resampler_); } TEST_F(AudioOutputProxyTest, StartAndStop) { - StartAndStop(dispatcher_impl_.get()); + StartAndStop(dispatcher_impl_); } TEST_F(AudioOutputResamplerTest, StartAndStop) { - StartAndStop(resampler_.get()); + StartAndStop(resampler_); } TEST_F(AudioOutputProxyTest, CloseAfterStop) { - CloseAfterStop(dispatcher_impl_.get()); + CloseAfterStop(dispatcher_impl_); } TEST_F(AudioOutputResamplerTest, CloseAfterStop) { - CloseAfterStop(resampler_.get()); + CloseAfterStop(resampler_); } -TEST_F(AudioOutputProxyTest, TwoStreams) { TwoStreams(dispatcher_impl_.get()); } +TEST_F(AudioOutputProxyTest, TwoStreams) { TwoStreams(dispatcher_impl_); } -TEST_F(AudioOutputResamplerTest, TwoStreams) { TwoStreams(resampler_.get()); } +TEST_F(AudioOutputResamplerTest, TwoStreams) { TwoStreams(resampler_); } // Two streams: verify that second stream is allocated when the first // starts playing. -TEST_F(AudioOutputProxyTest, TwoStreams_OnePlaying) { - InitDispatcher(base::TimeDelta::FromSeconds(kTestBigCloseDelaySeconds)); - TwoStreams_OnePlaying(dispatcher_impl_.get()); +TEST_F(AudioOutputProxyTest, OneStream_TwoPlays) { + OneStream_TwoPlays(dispatcher_impl_); } -TEST_F(AudioOutputResamplerTest, TwoStreams_OnePlaying) { - InitDispatcher(base::TimeDelta::FromSeconds(kTestBigCloseDelaySeconds)); - TwoStreams_OnePlaying(resampler_.get()); +TEST_F(AudioOutputResamplerTest, OneStream_TwoPlays) { + OneStream_TwoPlays(resampler_); } // Two streams, both are playing. Dispatcher should not open a third stream. TEST_F(AudioOutputProxyTest, TwoStreams_BothPlaying) { - InitDispatcher(base::TimeDelta::FromSeconds(kTestBigCloseDelaySeconds)); - TwoStreams_BothPlaying(dispatcher_impl_.get()); + TwoStreams_BothPlaying(dispatcher_impl_); } TEST_F(AudioOutputResamplerTest, TwoStreams_BothPlaying) { - InitDispatcher(base::TimeDelta::FromSeconds(kTestBigCloseDelaySeconds)); - TwoStreams_BothPlaying(resampler_.get()); + TwoStreams_BothPlaying(resampler_); } -TEST_F(AudioOutputProxyTest, OpenFailed) { OpenFailed(dispatcher_impl_.get()); } +TEST_F(AudioOutputProxyTest, OpenFailed) { OpenFailed(dispatcher_impl_); } // Start() method failed. TEST_F(AudioOutputProxyTest, StartFailed) { - StartFailed(dispatcher_impl_.get()); + StartFailed(dispatcher_impl_); } -TEST_F(AudioOutputResamplerTest, StartFailed) { StartFailed(resampler_.get()); } +TEST_F(AudioOutputResamplerTest, StartFailed) { StartFailed(resampler_); } // Simulate AudioOutputStream::Create() failure with a low latency stream and // ensure AudioOutputResampler falls back to the high latency path. @@ -581,13 +541,10 @@ TEST_F(AudioOutputResamplerTest, LowLatencyCreateFailedFallback) { .WillRepeatedly(Return(&stream)); EXPECT_CALL(stream, Open()) .WillOnce(Return(true)); - EXPECT_CALL(stream, Close()) - .Times(1); - AudioOutputProxy* proxy = new AudioOutputProxy(resampler_.get()); + AudioOutputProxy* proxy = new AudioOutputProxy(resampler_); EXPECT_TRUE(proxy->Open()); - proxy->Close(); - WaitForCloseTimer(kTestCloseDelayMs); + CloseAndWaitForCloseTimer(proxy, &stream); } // Simulate AudioOutputStream::Open() failure with a low latency stream and @@ -605,13 +562,10 @@ TEST_F(AudioOutputResamplerTest, LowLatencyOpenFailedFallback) { .Times(1); EXPECT_CALL(okay_stream, Open()) .WillOnce(Return(true)); - EXPECT_CALL(okay_stream, Close()) - .Times(1); - AudioOutputProxy* proxy = new AudioOutputProxy(resampler_.get()); + AudioOutputProxy* proxy = new AudioOutputProxy(resampler_); EXPECT_TRUE(proxy->Open()); - proxy->Close(); - WaitForCloseTimer(kTestCloseDelayMs); + CloseAndWaitForCloseTimer(proxy, &okay_stream); } // Simulate failures to open both the low latency and the fallback high latency @@ -642,13 +596,10 @@ TEST_F(AudioOutputResamplerTest, HighLatencyFallbackFailed) { .WillOnce(Return(&okay_stream)); EXPECT_CALL(okay_stream, Open()) .WillOnce(Return(true)); - EXPECT_CALL(okay_stream, Close()) - .Times(1); - AudioOutputProxy* proxy = new AudioOutputProxy(resampler_.get()); + AudioOutputProxy* proxy = new AudioOutputProxy(resampler_); EXPECT_TRUE(proxy->Open()); - proxy->Close(); - WaitForCloseTimer(kTestCloseDelayMs); + CloseAndWaitForCloseTimer(proxy, &okay_stream); } // Simulate failures to open both the low latency, the fallback high latency @@ -666,10 +617,9 @@ TEST_F(AudioOutputResamplerTest, AllFallbackFailed) { .Times(kFallbackCount) .WillRepeatedly(Return(static_cast<AudioOutputStream*>(NULL))); - AudioOutputProxy* proxy = new AudioOutputProxy(resampler_.get()); + AudioOutputProxy* proxy = new AudioOutputProxy(resampler_); EXPECT_FALSE(proxy->Open()); proxy->Close(); - WaitForCloseTimer(kTestCloseDelayMs); } // Simulate an eventual OpenStream() failure; i.e. successful OpenStream() calls @@ -677,72 +627,126 @@ TEST_F(AudioOutputResamplerTest, AllFallbackFailed) { TEST_F(AudioOutputResamplerTest, LowLatencyOpenEventuallyFails) { MockAudioOutputStream stream1(&manager_, params_); MockAudioOutputStream stream2(&manager_, params_); - MockAudioOutputStream stream3(&manager_, params_); // Setup the mock such that all three streams are successfully created. EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .WillOnce(Return(&stream1)) .WillOnce(Return(&stream2)) - .WillOnce(Return(&stream3)) .WillRepeatedly(Return(static_cast<AudioOutputStream*>(NULL))); // Stream1 should be able to successfully open and start. EXPECT_CALL(stream1, Open()) .WillOnce(Return(true)); - EXPECT_CALL(stream1, Close()) - .Times(1); EXPECT_CALL(stream1, SetVolume(_)) .Times(1); // Stream2 should also be able to successfully open and start. EXPECT_CALL(stream2, Open()) .WillOnce(Return(true)); - EXPECT_CALL(stream2, Close()) - .Times(1); EXPECT_CALL(stream2, SetVolume(_)) .Times(1); - // Stream3 should fail on Open() (yet still be closed since - // MakeAudioOutputStream returned a valid AudioOutputStream object). - EXPECT_CALL(stream3, Open()) - .WillOnce(Return(false)); - EXPECT_CALL(stream3, Close()) - .Times(1); - // Open and start the first proxy and stream. - AudioOutputProxy* proxy1 = new AudioOutputProxy(resampler_.get()); + AudioOutputProxy* proxy1 = new AudioOutputProxy(resampler_); EXPECT_TRUE(proxy1->Open()); proxy1->Start(&callback_); OnStart(); // Open and start the second proxy and stream. - AudioOutputProxy* proxy2 = new AudioOutputProxy(resampler_.get()); + AudioOutputProxy* proxy2 = new AudioOutputProxy(resampler_); EXPECT_TRUE(proxy2->Open()); proxy2->Start(&callback_); OnStart(); // Attempt to open the third stream which should fail. - AudioOutputProxy* proxy3 = new AudioOutputProxy(resampler_.get()); + AudioOutputProxy* proxy3 = new AudioOutputProxy(resampler_); EXPECT_FALSE(proxy3->Open()); + proxy3->Close(); // Perform the required Stop()/Close() shutdown dance for each proxy. Under // the hood each proxy should correctly call CloseStream() if OpenStream() // succeeded or not. + proxy2->Stop(); + CloseAndWaitForCloseTimer(proxy2, &stream2); + + proxy1->Stop(); + CloseAndWaitForCloseTimer(proxy1, &stream1); + + EXPECT_TRUE(stream1.stop_called()); + EXPECT_TRUE(stream1.start_called()); + EXPECT_TRUE(stream2.stop_called()); + EXPECT_TRUE(stream2.start_called()); +} + +// Ensures the methods used to fix audio output wedges are working correctly. +TEST_F(AudioOutputResamplerTest, WedgeFix) { + MockAudioOutputStream stream1(&manager_, params_); + MockAudioOutputStream stream2(&manager_, params_); + MockAudioOutputStream stream3(&manager_, params_); + + // Setup the mock such that all three streams are successfully created. + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) + .WillOnce(Return(&stream1)) + .WillOnce(Return(&stream2)) + .WillOnce(Return(&stream3)); + + // Stream1 should be able to successfully open and start. + EXPECT_CALL(stream1, Open()) + .WillOnce(Return(true)); + EXPECT_CALL(stream1, SetVolume(_)); + EXPECT_CALL(stream2, Open()) + .WillOnce(Return(true)); + EXPECT_CALL(stream2, SetVolume(_)); + + // Open and start the first proxy and stream. + AudioOutputProxy* proxy1 = new AudioOutputProxy(resampler_.get()); + EXPECT_TRUE(proxy1->Open()); + proxy1->Start(&callback_); + OnStart(); + + // Open, but do not start the second proxy. + AudioOutputProxy* proxy2 = new AudioOutputProxy(resampler_.get()); + EXPECT_TRUE(proxy2->Open()); + + // Open, start and then stop the third proxy. + AudioOutputProxy* proxy3 = new AudioOutputProxy(resampler_.get()); + EXPECT_TRUE(proxy3->Open()); + proxy3->Start(&callback_); + OnStart(); proxy3->Stop(); + + // Wait for stream to timeout and shutdown. + WaitForCloseTimer(&stream2); + + EXPECT_CALL(stream1, Close()); + resampler_->CloseStreamsForWedgeFix(); + + // Don't pump the MessageLoop between CloseStreamsForWedgeFix() and + // RestartStreamsForWedgeFix() to simulate intended usage. The OnStart() call + // will take care of necessary work. + + // Stream3 should take Stream1's place after RestartStreamsForWedgeFix(). No + // additional streams should be opened for proxy2 and proxy3. + EXPECT_CALL(stream3, Open()) + .WillOnce(Return(true)); + EXPECT_CALL(stream3, SetVolume(_)); + + resampler_->RestartStreamsForWedgeFix(); + OnStart(); + + // Perform the required Stop()/Close() shutdown dance for each proxy. proxy3->Close(); - proxy2->Stop(); proxy2->Close(); proxy1->Stop(); - proxy1->Close(); + CloseAndWaitForCloseTimer(proxy1, &stream3); // Wait for all of the messages to fly and then verify stream behavior. - WaitForCloseTimer(kTestCloseDelayMs); EXPECT_TRUE(stream1.stop_called()); EXPECT_TRUE(stream1.start_called()); EXPECT_TRUE(stream2.stop_called()); EXPECT_TRUE(stream2.start_called()); - EXPECT_FALSE(stream3.stop_called()); - EXPECT_FALSE(stream3.start_called()); + EXPECT_TRUE(stream3.stop_called()); + EXPECT_TRUE(stream3.start_called()); } } // namespace media diff --git a/chromium/media/audio/audio_output_resampler.cc b/chromium/media/audio/audio_output_resampler.cc index da424ec1246..c53f3e089ce 100644 --- a/chromium/media/audio/audio_output_resampler.cc +++ b/chromium/media/audio/audio_output_resampler.cc @@ -14,7 +14,6 @@ #include "media/audio/audio_io.h" #include "media/audio/audio_output_dispatcher_impl.h" #include "media/audio/audio_output_proxy.h" -#include "media/audio/audio_util.h" #include "media/audio/sample_rates.h" #include "media/base/audio_converter.h" #include "media/base/limits.h" @@ -44,6 +43,8 @@ class OnMoreDataConverter // Clears |source_callback_| and flushes the resampler. void Stop(); + bool started() { return source_callback_ != NULL; } + private: // AudioConverter::InputCallback implementation. virtual double ProvideInput(AudioBus* audio_bus, @@ -51,15 +52,11 @@ class OnMoreDataConverter // Ratio of input bytes to output bytes used to correct playback delay with // regard to buffering and resampling. - double io_ratio_; + const double io_ratio_; - // Source callback and associated lock. - base::Lock source_lock_; + // Source callback. AudioOutputStream::AudioSourceCallback* source_callback_; - // |source| passed to OnMoreIOData() which should be passed downstream. - AudioBus* source_bus_; - // Last AudioBuffersState object received via OnMoreData(), used to correct // playback delay by ProvideInput() and passed on to |source_callback_|. AudioBuffersState current_buffers_state_; @@ -121,28 +118,27 @@ static void RecordFallbackStats(const AudioParameters& output_params) { } } +// Converts low latency based |output_params| into high latency appropriate +// output parameters in error situations. +void AudioOutputResampler::SetupFallbackParams() { // Only Windows has a high latency output driver that is not the same as the low // latency path. #if defined(OS_WIN) -// Converts low latency based |output_params| into high latency appropriate -// output parameters in error situations. -static AudioParameters SetupFallbackParams( - const AudioParameters& input_params, const AudioParameters& output_params) { // Choose AudioParameters appropriate for opening the device in high latency // mode. |kMinLowLatencyFrameSize| is arbitrarily based on Pepper Flash's // MAXIMUM frame size for low latency. static const int kMinLowLatencyFrameSize = 2048; - int frames_per_buffer = std::min( - std::max(input_params.frames_per_buffer(), kMinLowLatencyFrameSize), - static_cast<int>( - GetHighLatencyOutputBufferSize(input_params.sample_rate()))); - - return AudioParameters( - AudioParameters::AUDIO_PCM_LINEAR, input_params.channel_layout(), - input_params.sample_rate(), input_params.bits_per_sample(), + const int frames_per_buffer = + std::max(params_.frames_per_buffer(), kMinLowLatencyFrameSize); + + output_params_ = AudioParameters( + AudioParameters::AUDIO_PCM_LINEAR, params_.channel_layout(), + params_.sample_rate(), params_.bits_per_sample(), frames_per_buffer); -} + output_device_id_ = ""; + Initialize(); #endif +} AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager, const AudioParameters& input_params, @@ -178,7 +174,7 @@ void AudioOutputResampler::Initialize() { } bool AudioOutputResampler::OpenStream() { - DCHECK_EQ(base::MessageLoop::current(), message_loop_); + DCHECK(message_loop_->BelongsToCurrentThread()); if (dispatcher_->OpenStream()) { // Only record the UMA statistic if we didn't fallback during construction @@ -210,8 +206,7 @@ bool AudioOutputResampler::OpenStream() { DLOG(ERROR) << "Unable to open audio device in low latency mode. Falling " << "back to high latency audio output."; - output_params_ = SetupFallbackParams(params_, output_params_); - Initialize(); + SetupFallbackParams(); if (dispatcher_->OpenStream()) { streams_opened_ = true; return true; @@ -238,7 +233,7 @@ bool AudioOutputResampler::OpenStream() { bool AudioOutputResampler::StartStream( AudioOutputStream::AudioSourceCallback* callback, AudioOutputProxy* stream_proxy) { - DCHECK_EQ(base::MessageLoop::current(), message_loop_); + DCHECK(message_loop_->BelongsToCurrentThread()); OnMoreDataConverter* resampler_callback = NULL; CallbackMap::iterator it = callbacks_.find(stream_proxy); @@ -258,12 +253,12 @@ bool AudioOutputResampler::StartStream( void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy* stream_proxy, double volume) { - DCHECK_EQ(base::MessageLoop::current(), message_loop_); + DCHECK(message_loop_->BelongsToCurrentThread()); dispatcher_->StreamVolumeSet(stream_proxy, volume); } void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) { - DCHECK_EQ(base::MessageLoop::current(), message_loop_); + DCHECK(message_loop_->BelongsToCurrentThread()); dispatcher_->StopStream(stream_proxy); // Now that StopStream() has completed the underlying physical stream should @@ -275,7 +270,7 @@ void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) { } void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) { - DCHECK_EQ(base::MessageLoop::current(), message_loop_); + DCHECK(message_loop_->BelongsToCurrentThread()); dispatcher_->CloseStream(stream_proxy); // We assume that StopStream() is always called prior to CloseStream(), so @@ -288,7 +283,7 @@ void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) { } void AudioOutputResampler::Shutdown() { - DCHECK_EQ(base::MessageLoop::current(), message_loop_); + DCHECK(message_loop_->BelongsToCurrentThread()); // No AudioOutputProxy objects should hold a reference to us when we get // to this stage. @@ -298,16 +293,44 @@ void AudioOutputResampler::Shutdown() { DCHECK(callbacks_.empty()); } +void AudioOutputResampler::CloseStreamsForWedgeFix() { + DCHECK(message_loop_->BelongsToCurrentThread()); + + // Stop and close all active streams. Once all streams across all dispatchers + // have been closed the AudioManager will call RestartStreamsForWedgeFix(). + for (CallbackMap::iterator it = callbacks_.begin(); it != callbacks_.end(); + ++it) { + if (it->second->started()) + dispatcher_->StopStream(it->first); + dispatcher_->CloseStream(it->first); + } + + // Close all idle streams as well. + dispatcher_->CloseStreamsForWedgeFix(); +} + +void AudioOutputResampler::RestartStreamsForWedgeFix() { + DCHECK(message_loop_->BelongsToCurrentThread()); + // By opening all streams first and then starting them one by one we ensure + // the dispatcher only opens streams for those which will actually be used. + for (CallbackMap::iterator it = callbacks_.begin(); it != callbacks_.end(); + ++it) { + dispatcher_->OpenStream(); + } + for (CallbackMap::iterator it = callbacks_.begin(); it != callbacks_.end(); + ++it) { + if (it->second->started()) + dispatcher_->StartStream(it->second, it->first); + } +} + OnMoreDataConverter::OnMoreDataConverter(const AudioParameters& input_params, const AudioParameters& output_params) - : source_callback_(NULL), - source_bus_(NULL), + : io_ratio_(static_cast<double>(input_params.GetBytesPerSecond()) / + output_params.GetBytesPerSecond()), + source_callback_(NULL), input_bytes_per_second_(input_params.GetBytesPerSecond()), - audio_converter_(input_params, output_params, false) { - io_ratio_ = - static_cast<double>(input_params.GetBytesPerSecond()) / - output_params.GetBytesPerSecond(); -} + audio_converter_(input_params, output_params, false) {} OnMoreDataConverter::~OnMoreDataConverter() { // Ensure Stop() has been called so we don't end up with an AudioOutputStream @@ -317,7 +340,6 @@ OnMoreDataConverter::~OnMoreDataConverter() { void OnMoreDataConverter::Start( AudioOutputStream::AudioSourceCallback* callback) { - base::AutoLock auto_lock(source_lock_); CHECK(!source_callback_); source_callback_ = callback; @@ -328,7 +350,6 @@ void OnMoreDataConverter::Start( } void OnMoreDataConverter::Stop() { - base::AutoLock auto_lock(source_lock_); CHECK(source_callback_); source_callback_ = NULL; audio_converter_.RemoveInput(this); @@ -342,26 +363,20 @@ int OnMoreDataConverter::OnMoreData(AudioBus* dest, int OnMoreDataConverter::OnMoreIOData(AudioBus* source, AudioBus* dest, AudioBuffersState buffers_state) { - base::AutoLock auto_lock(source_lock_); - // While we waited for |source_lock_| the callback might have been cleared. - if (!source_callback_) { - dest->Zero(); - return dest->frames(); - } + // Note: The input portion of OnMoreIOData() is not supported when a converter + // has been injected. Downstream clients prefer silence to potentially split + // apart input data. - source_bus_ = source; current_buffers_state_ = buffers_state; audio_converter_.Convert(dest); - // Always return the full number of frames requested, ProvideInput_Locked() + // Always return the full number of frames requested, ProvideInput() // will pad with silence if it wasn't able to acquire enough data. return dest->frames(); } double OnMoreDataConverter::ProvideInput(AudioBus* dest, base::TimeDelta buffer_delay) { - source_lock_.AssertAcquired(); - // Adjust playback delay to include |buffer_delay|. // TODO(dalecurtis): Stop passing bytes around, it doesn't make sense since // AudioBus is just float data. Use TimeDelta instead. @@ -371,27 +386,18 @@ double OnMoreDataConverter::ProvideInput(AudioBus* dest, buffer_delay.InSecondsF() * input_bytes_per_second_); // Retrieve data from the original callback. - int frames = source_callback_->OnMoreIOData( - source_bus_, dest, new_buffers_state); - - // |source_bus_| should only be provided once. - // TODO(dalecurtis, crogers): This is not a complete fix. If ProvideInput() - // is called multiple times, we need to do something more clever here. - source_bus_ = NULL; + const int frames = source_callback_->OnMoreIOData( + NULL, dest, new_buffers_state); // Zero any unfilled frames if anything was filled, otherwise we'll just // return a volume of zero and let AudioConverter drop the output. if (frames > 0 && frames < dest->frames()) dest->ZeroFramesPartial(frames, dest->frames() - frames); - - // TODO(dalecurtis): Return the correct volume here. return frames > 0 ? 1 : 0; } void OnMoreDataConverter::OnError(AudioOutputStream* stream) { - base::AutoLock auto_lock(source_lock_); - if (source_callback_) - source_callback_->OnError(stream); + source_callback_->OnError(stream); } } // namespace media diff --git a/chromium/media/audio/audio_output_resampler.h b/chromium/media/audio/audio_output_resampler.h index f9a75ac38f5..a8fca232470 100644 --- a/chromium/media/audio/audio_output_resampler.h +++ b/chromium/media/audio/audio_output_resampler.h @@ -53,11 +53,17 @@ class MEDIA_EXPORT AudioOutputResampler : public AudioOutputDispatcher { double volume) OVERRIDE; virtual void CloseStream(AudioOutputProxy* stream_proxy) OVERRIDE; virtual void Shutdown() OVERRIDE; + virtual void CloseStreamsForWedgeFix() OVERRIDE; + virtual void RestartStreamsForWedgeFix() OVERRIDE; private: friend class base::RefCountedThreadSafe<AudioOutputResampler>; virtual ~AudioOutputResampler(); + // Converts low latency based output parameters into high latency + // appropriate output parameters in error situations. + void SetupFallbackParams(); + // Used to initialize and reinitialize |dispatcher_|. void Initialize(); diff --git a/chromium/media/audio/audio_parameters.cc b/chromium/media/audio/audio_parameters.cc index 5e77c60cb94..fff815610fe 100644 --- a/chromium/media/audio/audio_parameters.cc +++ b/chromium/media/audio/audio_parameters.cc @@ -16,7 +16,8 @@ AudioParameters::AudioParameters() bits_per_sample_(0), frames_per_buffer_(0), channels_(0), - input_channels_(0) { + input_channels_(0), + effects_(NO_EFFECTS) { } AudioParameters::AudioParameters(Format format, ChannelLayout channel_layout, @@ -28,20 +29,38 @@ AudioParameters::AudioParameters(Format format, ChannelLayout channel_layout, bits_per_sample_(bits_per_sample), frames_per_buffer_(frames_per_buffer), channels_(ChannelLayoutToChannelCount(channel_layout)), - input_channels_(0) { + input_channels_(0), + effects_(NO_EFFECTS) { } AudioParameters::AudioParameters(Format format, ChannelLayout channel_layout, int input_channels, int sample_rate, int bits_per_sample, - int frames_per_buffer) + int frames_per_buffer, int effects) : format_(format), channel_layout_(channel_layout), sample_rate_(sample_rate), bits_per_sample_(bits_per_sample), frames_per_buffer_(frames_per_buffer), channels_(ChannelLayoutToChannelCount(channel_layout)), - input_channels_(input_channels) { + input_channels_(input_channels), + effects_(effects) { +} + +AudioParameters::AudioParameters(Format format, ChannelLayout channel_layout, + int channels, int input_channels, + int sample_rate, int bits_per_sample, + int frames_per_buffer, int effects) + : format_(format), + channel_layout_(channel_layout), + sample_rate_(sample_rate), + bits_per_sample_(bits_per_sample), + frames_per_buffer_(frames_per_buffer), + channels_(channels), + input_channels_(input_channels), + effects_(effects) { + if (channel_layout != CHANNEL_LAYOUT_DISCRETE) + DCHECK_EQ(channels, ChannelLayoutToChannelCount(channel_layout)); } void AudioParameters::Reset(Format format, ChannelLayout channel_layout, @@ -89,9 +108,10 @@ int AudioParameters::GetBytesPerFrame() const { return channels_ * bits_per_sample_ / 8; } -void AudioParameters::SetDiscreteChannels(int channels) { - channel_layout_ = CHANNEL_LAYOUT_DISCRETE; - channels_ = channels; +base::TimeDelta AudioParameters::GetBufferDuration() const { + return base::TimeDelta::FromMicroseconds( + frames_per_buffer_ * base::Time::kMicrosecondsPerSecond / + static_cast<float>(sample_rate_)); } } // namespace media diff --git a/chromium/media/audio/audio_parameters.h b/chromium/media/audio/audio_parameters.h index bc629a7db00..62ff4fd48f1 100644 --- a/chromium/media/audio/audio_parameters.h +++ b/chromium/media/audio/audio_parameters.h @@ -6,6 +6,7 @@ #define MEDIA_AUDIO_AUDIO_PARAMETERS_H_ #include "base/basictypes.h" +#include "base/time/time.h" #include "media/base/channel_layout.h" #include "media/base/media_export.h" @@ -43,6 +44,13 @@ class MEDIA_EXPORT AudioParameters { kAudioCDSampleRate = 44100, }; + // Bitmasks to determine whether certain platform (typically hardware) audio + // effects should be enabled. + enum PlatformEffectsMask { + NO_EFFECTS = 0x0, + ECHO_CANCELLER = 0x1 + }; + AudioParameters(); AudioParameters(Format format, ChannelLayout channel_layout, int sample_rate, int bits_per_sample, @@ -50,7 +58,12 @@ class MEDIA_EXPORT AudioParameters { AudioParameters(Format format, ChannelLayout channel_layout, int input_channels, int sample_rate, int bits_per_sample, - int frames_per_buffer); + int frames_per_buffer, int effects); + AudioParameters(Format format, ChannelLayout channel_layout, + int channels, int input_channels, + int sample_rate, int bits_per_sample, + int frames_per_buffer, int effects); + void Reset(Format format, ChannelLayout channel_layout, int channels, int input_channels, int sample_rate, int bits_per_sample, @@ -69,6 +82,10 @@ class MEDIA_EXPORT AudioParameters { // Returns the number of bytes representing a frame of audio. int GetBytesPerFrame() const; + // Returns the duration of this buffer as calculated from frames_per_buffer() + // and sample_rate(). + base::TimeDelta GetBufferDuration() const; + Format format() const { return format_; } ChannelLayout channel_layout() const { return channel_layout_; } int sample_rate() const { return sample_rate_; } @@ -76,9 +93,7 @@ class MEDIA_EXPORT AudioParameters { int frames_per_buffer() const { return frames_per_buffer_; } int channels() const { return channels_; } int input_channels() const { return input_channels_; } - - // Set to CHANNEL_LAYOUT_DISCRETE with given number of channels. - void SetDiscreteChannels(int channels); + int effects() const { return effects_; } // Comparison with other AudioParams. bool operator==(const AudioParameters& other) const { @@ -88,10 +103,13 @@ class MEDIA_EXPORT AudioParameters { channels_ == other.channels() && input_channels_ == other.input_channels() && bits_per_sample_ == other.bits_per_sample() && - frames_per_buffer_ == other.frames_per_buffer(); + frames_per_buffer_ == other.frames_per_buffer() && + effects_ == other.effects(); } private: + // These members are mutable to support entire struct assignment. They should + // not be mutated individually. Format format_; // Format of the stream. ChannelLayout channel_layout_; // Order of surround sound channels. int sample_rate_; // Sampling frequency/rate. @@ -103,6 +121,7 @@ class MEDIA_EXPORT AudioParameters { int input_channels_; // Optional number of input channels. // Normally 0, but can be set to specify // synchronized I/O. + int effects_; // Bitmask using PlatformEffectsMask. }; // Comparison is useful when AudioParameters is used with std structures. diff --git a/chromium/media/audio/audio_power_monitor.cc b/chromium/media/audio/audio_power_monitor.cc index d8b9436060e..6536f464b9c 100644 --- a/chromium/media/audio/audio_power_monitor.cc +++ b/chromium/media/audio/audio_power_monitor.cc @@ -11,6 +11,7 @@ #include "base/logging.h" #include "base/time/time.h" #include "media/base/audio_bus.h" +#include "media/base/vector_math.h" namespace media { @@ -36,30 +37,19 @@ void AudioPowerMonitor::Scan(const AudioBus& buffer, int num_frames) { return; // Calculate a new average power by applying a first-order low-pass filter - // over the audio samples in |buffer|. - // - // TODO(miu): Implement optimized SSE/NEON to more efficiently compute the - // results (in media/base/vector_math) in soon-upcoming change. + // (a.k.a. an exponentially-weighted moving average) over the audio samples in + // each channel in |buffer|. float sum_power = 0.0f; for (int i = 0; i < num_channels; ++i) { - float average_power_this_channel = average_power_; - bool clipped = false; - const float* p = buffer.channel(i); - const float* const end_of_samples = p + num_frames; - for (; p < end_of_samples; ++p) { - const float sample = *p; - const float sample_squared = sample * sample; - clipped |= (sample_squared > 1.0f); - average_power_this_channel += - (sample_squared - average_power_this_channel) * sample_weight_; - } + const std::pair<float, float> ewma_and_max = vector_math::EWMAAndMaxPower( + average_power_, buffer.channel(i), num_frames, sample_weight_); // If data in audio buffer is garbage, ignore its effect on the result. - if (base::IsNaN(average_power_this_channel)) { - average_power_this_channel = average_power_; - clipped = false; + if (!base::IsFinite(ewma_and_max.first)) { + sum_power += average_power_; + } else { + sum_power += ewma_and_max.first; + has_clipped_ |= (ewma_and_max.second > 1.0f); } - sum_power += average_power_this_channel; - has_clipped_ |= clipped; } // Update accumulated results, with clamping for sanity. diff --git a/chromium/media/audio/audio_util.cc b/chromium/media/audio/audio_util.cc deleted file mode 100644 index 42c6c9109fd..00000000000 --- a/chromium/media/audio/audio_util.cc +++ /dev/null @@ -1,99 +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. - -// Software adjust volume of samples, allows each audio stream its own -// volume without impacting master volume for chrome and other applications. - -// Implemented as templates to allow 8, 16 and 32 bit implementations. -// 8 bit is unsigned and biased by 128. - -// TODO(vrk): This file has been running pretty wild and free, and it's likely -// that a lot of the functions can be simplified and made more elegant. Revisit -// after other audio cleanup is done. (crbug.com/120319) - -#include "media/audio/audio_util.h" - -#include "base/command_line.h" -#include "base/strings/string_number_conversions.h" -#include "base/time/time.h" -#include "media/base/media_switches.h" - -#if defined(OS_WIN) -#include "base/win/windows_version.h" -#endif - -namespace media { - -// Returns user buffer size as specified on the command line or 0 if no buffer -// size has been specified. -int GetUserBufferSize() { - const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); - int buffer_size = 0; - std::string buffer_size_str(cmd_line->GetSwitchValueASCII( - switches::kAudioBufferSize)); - if (base::StringToInt(buffer_size_str, &buffer_size) && buffer_size > 0) - return buffer_size; - - return 0; -} - -// Computes a buffer size based on the given |sample_rate|. Must be used in -// conjunction with AUDIO_PCM_LINEAR. -size_t GetHighLatencyOutputBufferSize(int sample_rate) { - int user_buffer_size = GetUserBufferSize(); - if (user_buffer_size) - return user_buffer_size; - - // TODO(vrk/crogers): The buffer sizes that this function computes is probably - // overly conservative. However, reducing the buffer size to 2048-8192 bytes - // caused crbug.com/108396. This computation should be revisited while making - // sure crbug.com/108396 doesn't happen again. - - // The minimum number of samples in a hardware packet. - // This value is selected so that we can handle down to 5khz sample rate. - static const size_t kMinSamplesPerHardwarePacket = 1024; - - // The maximum number of samples in a hardware packet. - // This value is selected so that we can handle up to 192khz sample rate. - static const size_t kMaxSamplesPerHardwarePacket = 64 * 1024; - - // This constant governs the hardware audio buffer size, this value should be - // chosen carefully. - // This value is selected so that we have 8192 samples for 48khz streams. - static const size_t kMillisecondsPerHardwarePacket = 170; - - // Select the number of samples that can provide at least - // |kMillisecondsPerHardwarePacket| worth of audio data. - size_t samples = kMinSamplesPerHardwarePacket; - while (samples <= kMaxSamplesPerHardwarePacket && - samples * base::Time::kMillisecondsPerSecond < - sample_rate * kMillisecondsPerHardwarePacket) { - samples *= 2; - } - return samples; -} - -#if defined(OS_WIN) - -int NumberOfWaveOutBuffers() { - // Use the user provided buffer count if provided. - int buffers = 0; - std::string buffers_str(CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kWaveOutBuffers)); - if (base::StringToInt(buffers_str, &buffers) && buffers > 0) { - return buffers; - } - - // Use 4 buffers for Vista, 3 for everyone else: - // - The entire Windows audio stack was rewritten for Windows Vista and wave - // out performance was degraded compared to XP. - // - The regression was fixed in Windows 7 and most configurations will work - // with 2, but some (e.g., some Sound Blasters) still need 3. - // - Some XP configurations (even multi-processor ones) also need 3. - return (base::win::GetVersion() == base::win::VERSION_VISTA) ? 4 : 3; -} - -#endif - -} // namespace media diff --git a/chromium/media/audio/audio_util.h b/chromium/media/audio/audio_util.h deleted file mode 100644 index a11c327aa47..00000000000 --- a/chromium/media/audio/audio_util.h +++ /dev/null @@ -1,31 +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_AUDIO_UTIL_H_ -#define MEDIA_AUDIO_AUDIO_UTIL_H_ - -#include "base/basictypes.h" -#include "build/build_config.h" -#include "media/base/media_export.h" - -namespace media { - -// Returns user buffer size as specified on the command line or 0 if no buffer -// size has been specified. -MEDIA_EXPORT int GetUserBufferSize(); - -// Computes a buffer size based on the given |sample_rate|. Must be used in -// conjunction with AUDIO_PCM_LINEAR. -MEDIA_EXPORT size_t GetHighLatencyOutputBufferSize(int sample_rate); - -#if defined(OS_WIN) - -// Returns number of buffers to be used by wave out. -MEDIA_EXPORT int NumberOfWaveOutBuffers(); - -#endif // defined(OS_WIN) - -} // namespace media - -#endif // MEDIA_AUDIO_AUDIO_UTIL_H_ diff --git a/chromium/media/audio/cras/audio_manager_cras.cc b/chromium/media/audio/cras/audio_manager_cras.cc index 14a0c4e86ac..876d6ce8136 100644 --- a/chromium/media/audio/cras/audio_manager_cras.cc +++ b/chromium/media/audio/cras/audio_manager_cras.cc @@ -9,7 +9,6 @@ #include "base/logging.h" #include "base/nix/xdg_util.h" #include "base/stl_util.h" -#include "media/audio/audio_util.h" #include "media/audio/cras/cras_input.h" #include "media/audio/cras/cras_unified.h" #include "media/base/channel_layout.h" @@ -39,7 +38,8 @@ bool AudioManagerCras::HasAudioInputDevices() { return true; } -AudioManagerCras::AudioManagerCras() { +AudioManagerCras::AudioManagerCras(AudioLogFactory* audio_log_factory) + : AudioManagerBase(audio_log_factory) { SetMaxOutputStreamsAllowed(kMaxOutputStreams); } @@ -125,7 +125,7 @@ AudioParameters AudioManagerCras::GetPreferredOutputStreamParameters( return AudioParameters( AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels, - sample_rate, bits_per_sample, buffer_size); + sample_rate, bits_per_sample, buffer_size, AudioParameters::NO_EFFECTS); } AudioOutputStream* AudioManagerCras::MakeOutputStream( diff --git a/chromium/media/audio/cras/audio_manager_cras.h b/chromium/media/audio/cras/audio_manager_cras.h index 3b0ef530e07..589374ae0b9 100644 --- a/chromium/media/audio/cras/audio_manager_cras.h +++ b/chromium/media/audio/cras/audio_manager_cras.h @@ -15,7 +15,7 @@ namespace media { class MEDIA_EXPORT AudioManagerCras : public AudioManagerBase { public: - AudioManagerCras(); + AudioManagerCras(AudioLogFactory* audio_log_factory); // AudioManager implementation. virtual bool HasAudioOutputDevices() OVERRIDE; diff --git a/chromium/media/audio/cras/cras_input.cc b/chromium/media/audio/cras/cras_input.cc index fd574dc86e5..c41f3645efd 100644 --- a/chromium/media/audio/cras/cras_input.cc +++ b/chromium/media/audio/cras/cras_input.cc @@ -10,9 +10,9 @@ #include "base/bind.h" #include "base/logging.h" #include "base/time/time.h" +#include "media/audio/alsa/alsa_util.h" #include "media/audio/audio_manager.h" #include "media/audio/cras/audio_manager_cras.h" -#include "media/audio/linux/alsa_util.h" namespace media { diff --git a/chromium/media/audio/cras/cras_unified.cc b/chromium/media/audio/cras/cras_unified.cc index a7741864b31..c85cf59dd5f 100644 --- a/chromium/media/audio/cras/cras_unified.cc +++ b/chromium/media/audio/cras/cras_unified.cc @@ -8,9 +8,8 @@ #include "base/command_line.h" #include "base/logging.h" -#include "media/audio/audio_util.h" +#include "media/audio/alsa/alsa_util.h" #include "media/audio/cras/audio_manager_cras.h" -#include "media/audio/linux/alsa_util.h" namespace media { @@ -162,6 +161,23 @@ void CrasUnifiedStream::Close() { void CrasUnifiedStream::Start(AudioSourceCallback* callback) { CHECK(callback); + + // Channel map to CRAS_CHANNEL, values in the same order of + // corresponding source in Chromium defined Channels. + static const int kChannelMap[] = { + CRAS_CH_FL, + CRAS_CH_FR, + CRAS_CH_FC, + CRAS_CH_LFE, + CRAS_CH_RL, + CRAS_CH_RR, + CRAS_CH_FLC, + CRAS_CH_FRC, + CRAS_CH_RC, + CRAS_CH_SL, + CRAS_CH_SR + }; + source_callback_ = callback; // Only start if we can enter the playing state. @@ -180,6 +196,22 @@ void CrasUnifiedStream::Start(AudioSourceCallback* callback) { return; } + // Initialize channel layout to all -1 to indicate that none of + // the channels is set in the layout. + int8 layout[CRAS_CH_MAX] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + + // Converts to CRAS defined channels. ChannelOrder will return -1 + // for channels that does not present in params_.channel_layout(). + for (size_t i = 0; i < arraysize(kChannelMap); ++i) + layout[kChannelMap[i]] = ChannelOrder(params_.channel_layout(), + static_cast<Channels>(i)); + + if (cras_audio_format_set_channel_layout(audio_format, layout)) { + LOG(WARNING) << "Error setting channel layout."; + callback->OnError(this); + return; + } + cras_stream_params* stream_params = cras_client_unified_params_create( stream_direction_, params_.frames_per_buffer(), diff --git a/chromium/media/audio/fake_audio_log_factory.cc b/chromium/media/audio/fake_audio_log_factory.cc new file mode 100644 index 00000000000..6f752e559fd --- /dev/null +++ b/chromium/media/audio/fake_audio_log_factory.cc @@ -0,0 +1,32 @@ +// 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/fake_audio_log_factory.h" + +namespace media { + +class FakeAudioLogImpl : public AudioLog { + public: + FakeAudioLogImpl() {} + virtual ~FakeAudioLogImpl() {} + virtual void OnCreated(int component_id, + const media::AudioParameters& params, + const std::string& input_device_id, + const std::string& output_device_id) OVERRIDE {} + virtual void OnStarted(int component_id) OVERRIDE {} + virtual void OnStopped(int component_id) OVERRIDE {} + virtual void OnClosed(int component_id) OVERRIDE {} + virtual void OnError(int component_id) OVERRIDE {} + virtual void OnSetVolume(int component_id, double volume) OVERRIDE {} +}; + +FakeAudioLogFactory::FakeAudioLogFactory() {} +FakeAudioLogFactory::~FakeAudioLogFactory() {} + +scoped_ptr<AudioLog> FakeAudioLogFactory::CreateAudioLog( + AudioComponent component) { + return scoped_ptr<AudioLog>(new FakeAudioLogImpl()); +} + +} // namespace media diff --git a/chromium/media/audio/fake_audio_log_factory.h b/chromium/media/audio/fake_audio_log_factory.h new file mode 100644 index 00000000000..30e39e63ff0 --- /dev/null +++ b/chromium/media/audio/fake_audio_log_factory.h @@ -0,0 +1,29 @@ +// 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_FAKE_AUDIO_LOG_FACTORY_H_ +#define MEDIA_AUDIO_FAKE_AUDIO_LOG_FACTORY_H_ + +#include "base/compiler_specific.h" +#include "media/audio/audio_logging.h" +#include "media/base/media_export.h" + +namespace media { + +// Creates stub AudioLog instances, for testing, which do nothing. +class MEDIA_EXPORT FakeAudioLogFactory + : NON_EXPORTED_BASE(public AudioLogFactory) { + public: + FakeAudioLogFactory(); + virtual ~FakeAudioLogFactory(); + virtual scoped_ptr<AudioLog> CreateAudioLog( + AudioComponent component) OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(FakeAudioLogFactory); +}; + +} // namespace media + +#endif // MEDIA_AUDIO_FAKE_AUDIO_LOG_FACTORY_H_ diff --git a/chromium/media/audio/fake_audio_manager.cc b/chromium/media/audio/fake_audio_manager.cc new file mode 100644 index 00000000000..bfe9a0a7ff3 --- /dev/null +++ b/chromium/media/audio/fake_audio_manager.cc @@ -0,0 +1,83 @@ +// 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/fake_audio_manager.h" + +namespace media { + +namespace { + +const int kDefaultInputBufferSize = 1024; +const int kDefaultSampleRate = 48000; + +} // namespace + +FakeAudioManager::FakeAudioManager(AudioLogFactory* audio_log_factory) + : AudioManagerBase(audio_log_factory) {} + +FakeAudioManager::~FakeAudioManager() { + Shutdown(); +} + +// Implementation of AudioManager. +bool FakeAudioManager::HasAudioOutputDevices() { return false; } + +bool FakeAudioManager::HasAudioInputDevices() { return false; } + +// Implementation of AudioManagerBase. +AudioOutputStream* FakeAudioManager::MakeLinearOutputStream( + const AudioParameters& params) { + return FakeAudioOutputStream::MakeFakeStream(this, params); +} + +AudioOutputStream* FakeAudioManager::MakeLowLatencyOutputStream( + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id) { + return FakeAudioOutputStream::MakeFakeStream(this, params); +} + +AudioInputStream* FakeAudioManager::MakeLinearInputStream( + const AudioParameters& params, + const std::string& device_id) { + return FakeAudioInputStream::MakeFakeStream(this, params); +} + +AudioInputStream* FakeAudioManager::MakeLowLatencyInputStream( + const AudioParameters& params, + const std::string& device_id) { + return FakeAudioInputStream::MakeFakeStream(this, params); +} + +AudioParameters FakeAudioManager::GetPreferredOutputStreamParameters( + const std::string& output_device_id, + const AudioParameters& input_params) { + static const int kDefaultOutputBufferSize = 2048; + static const int kDefaultSampleRate = 48000; + ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; + int sample_rate = kDefaultSampleRate; + int buffer_size = kDefaultOutputBufferSize; + int bits_per_sample = 16; + int input_channels = 0; + if (input_params.IsValid()) { + sample_rate = input_params.sample_rate(); + bits_per_sample = input_params.bits_per_sample(); + channel_layout = input_params.channel_layout(); + input_channels = input_params.input_channels(); + buffer_size = std::min(input_params.frames_per_buffer(), buffer_size); + } + + return AudioParameters( + AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels, + sample_rate, bits_per_sample, buffer_size, AudioParameters::NO_EFFECTS); +} + +AudioParameters FakeAudioManager::GetInputStreamParameters( + const std::string& device_id) { + return AudioParameters( + AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, + kDefaultSampleRate, 16, kDefaultInputBufferSize); +} + +} // namespace media diff --git a/chromium/media/audio/fake_audio_manager.h b/chromium/media/audio/fake_audio_manager.h new file mode 100644 index 00000000000..b5c45201ed1 --- /dev/null +++ b/chromium/media/audio/fake_audio_manager.h @@ -0,0 +1,53 @@ +// 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_FAKE_AUDIO_MANAGER_H_ +#define MEDIA_AUDIO_FAKE_AUDIO_MANAGER_H_ + +#include <string> +#include "base/compiler_specific.h" +#include "media/audio/audio_manager_base.h" +#include "media/audio/fake_audio_input_stream.h" +#include "media/audio/fake_audio_output_stream.h" + +namespace media { + +class MEDIA_EXPORT FakeAudioManager : public AudioManagerBase { + public: + FakeAudioManager(AudioLogFactory* audio_log_factory); + + // Implementation of AudioManager. + virtual bool HasAudioOutputDevices() OVERRIDE; + virtual bool HasAudioInputDevices() 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 AudioParameters GetInputStreamParameters( + const std::string& device_id) OVERRIDE; + + protected: + virtual ~FakeAudioManager(); + + virtual AudioParameters GetPreferredOutputStreamParameters( + const std::string& output_device_id, + const AudioParameters& input_params) OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(FakeAudioManager); +}; + +} // namespace media + +#endif // MEDIA_AUDIO_FAKE_AUDIO_MANAGER_H_ diff --git a/chromium/media/audio/fake_audio_output_stream.cc b/chromium/media/audio/fake_audio_output_stream.cc index b21a054f13b..fb460ab6805 100644 --- a/chromium/media/audio/fake_audio_output_stream.cc +++ b/chromium/media/audio/fake_audio_output_stream.cc @@ -22,7 +22,7 @@ FakeAudioOutputStream::FakeAudioOutputStream(AudioManagerBase* manager, const AudioParameters& params) : audio_manager_(manager), callback_(NULL), - fake_consumer_(manager->GetMessageLoop(), params) { + fake_consumer_(manager->GetWorkerLoop(), params) { } FakeAudioOutputStream::~FakeAudioOutputStream() { @@ -60,7 +60,7 @@ void FakeAudioOutputStream::GetVolume(double* volume) { }; void FakeAudioOutputStream::CallOnMoreData(AudioBus* audio_bus) { - DCHECK(audio_manager_->GetMessageLoop()->BelongsToCurrentThread()); + DCHECK(audio_manager_->GetWorkerLoop()->BelongsToCurrentThread()); callback_->OnMoreData(audio_bus, AudioBuffersState()); } diff --git a/chromium/media/audio/linux/audio_manager_linux.cc b/chromium/media/audio/linux/audio_manager_linux.cc index 708e4f26840..eaeb2f332b9 100644 --- a/chromium/media/audio/linux/audio_manager_linux.cc +++ b/chromium/media/audio/linux/audio_manager_linux.cc @@ -2,52 +2,23 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "media/audio/linux/audio_manager_linux.h" - #include "base/command_line.h" -#include "base/environment.h" -#include "base/files/file_path.h" -#include "base/logging.h" #include "base/metrics/histogram.h" -#include "base/nix/xdg_util.h" -#include "base/process/launch.h" -#include "base/stl_util.h" -#include "media/audio/audio_output_dispatcher.h" -#include "media/audio/audio_parameters.h" -#include "media/audio/audio_util.h" +#if defined(USE_ALSA) +#include "media/audio/alsa/audio_manager_alsa.h" +#else +#include "media/audio/fake_audio_manager.h" +#endif #if defined(USE_CRAS) #include "media/audio/cras/audio_manager_cras.h" #endif -#include "media/audio/linux/alsa_input.h" -#include "media/audio/linux/alsa_output.h" -#include "media/audio/linux/alsa_wrapper.h" #if defined(USE_PULSEAUDIO) #include "media/audio/pulse/audio_manager_pulse.h" #endif -#include "media/base/channel_layout.h" -#include "media/base/limits.h" #include "media/base/media_switches.h" namespace media { -// 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; - -// Since "default", "pulse" and "dmix" devices are virtual devices mapped to -// real devices, we remove them from the list to avoiding duplicate counting. -// In addition, note that we support no more than 2 channels for recording, -// hence surround devices are not stored in the list. -static const char* kInvalidAudioInputDevices[] = { - "default", - "dmix", - "null", - "pulse", - "surround", -}; - enum LinuxAudioIO { kPulse, kAlsa, @@ -55,335 +26,28 @@ enum LinuxAudioIO { kAudioIOMax // Must always be last! }; -// static -void AudioManagerLinux::ShowLinuxAudioInputSettings() { - scoped_ptr<base::Environment> env(base::Environment::Create()); - CommandLine command_line(CommandLine::NO_PROGRAM); - switch (base::nix::GetDesktopEnvironment(env.get())) { - case base::nix::DESKTOP_ENVIRONMENT_GNOME: - command_line.SetProgram(base::FilePath("gnome-volume-control")); - break; - case base::nix::DESKTOP_ENVIRONMENT_KDE3: - case base::nix::DESKTOP_ENVIRONMENT_KDE4: - command_line.SetProgram(base::FilePath("kmix")); - break; - case base::nix::DESKTOP_ENVIRONMENT_UNITY: - command_line.SetProgram(base::FilePath("gnome-control-center")); - command_line.AppendArg("sound"); - command_line.AppendArg("input"); - break; - default: - LOG(ERROR) << "Failed to show audio input settings: we don't know " - << "what command to use for your desktop environment."; - return; - } - base::LaunchProcess(command_line, base::LaunchOptions(), NULL); -} - -// Implementation of AudioManager. -bool AudioManagerLinux::HasAudioOutputDevices() { - return HasAnyAlsaAudioDevice(kStreamPlayback); -} - -bool AudioManagerLinux::HasAudioInputDevices() { - return HasAnyAlsaAudioDevice(kStreamCapture); -} - -AudioManagerLinux::AudioManagerLinux() - : wrapper_(new AlsaWrapper()) { - SetMaxOutputStreamsAllowed(kMaxOutputStreams); -} - -AudioManagerLinux::~AudioManagerLinux() { - Shutdown(); -} - -void AudioManagerLinux::ShowAudioInputSettings() { - ShowLinuxAudioInputSettings(); -} - -void AudioManagerLinux::GetAudioInputDeviceNames( - AudioDeviceNames* device_names) { - DCHECK(device_names->empty()); - GetAlsaAudioDevices(kStreamCapture, device_names); -} - -void AudioManagerLinux::GetAudioOutputDeviceNames( - AudioDeviceNames* device_names) { - DCHECK(device_names->empty()); - GetAlsaAudioDevices(kStreamPlayback, device_names); -} - -AudioParameters AudioManagerLinux::GetInputStreamParameters( - const std::string& device_id) { - static const int kDefaultInputBufferSize = 1024; - - return AudioParameters( - AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, - kDefaultSampleRate, 16, kDefaultInputBufferSize); -} - -void AudioManagerLinux::GetAlsaAudioDevices( - StreamType type, - media::AudioDeviceNames* device_names) { - // Constants specified by the ALSA API for device hints. - static const char kPcmInterfaceName[] = "pcm"; - int card = -1; - - // Loop through the sound cards to get ALSA device hints. - while (!wrapper_->CardNext(&card) && card >= 0) { - void** hints = NULL; - int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints); - if (!error) { - GetAlsaDevicesInfo(type, hints, device_names); - - // Destroy the hints now that we're done with it. - wrapper_->DeviceNameFreeHint(hints); - } else { - DLOG(WARNING) << "GetAlsaAudioDevices: unable to get device hints: " - << wrapper_->StrError(error); - } - } -} - -void AudioManagerLinux::GetAlsaDevicesInfo( - AudioManagerLinux::StreamType type, - void** hints, - media::AudioDeviceNames* device_names) { - static const char kIoHintName[] = "IOID"; - static const char kNameHintName[] = "NAME"; - static const char kDescriptionHintName[] = "DESC"; - - const char* unwanted_device_type = UnwantedDeviceTypeWhenEnumerating(type); - - for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) { - // 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(unwanted_device_type, io.get()) == 0) - continue; - - // 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, - AudioManagerBase::kDefaultDeviceId)); - } - - // Get the unique device name for the device. - scoped_ptr_malloc<char> unique_device_name( - wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName)); - - // Find out if the device is available. - if (IsAlsaDeviceAvailable(type, unique_device_name.get())) { - // Get the description for the device. - scoped_ptr_malloc<char> desc(wrapper_->DeviceNameGetHint( - *hint_iter, kDescriptionHintName)); - - media::AudioDeviceName name; - name.unique_id = unique_device_name.get(); - if (desc) { - // Use the more user friendly description as name. - // Replace '\n' with '-'. - char* pret = strchr(desc.get(), '\n'); - if (pret) - *pret = '-'; - name.device_name = desc.get(); - } else { - // Virtual devices don't necessarily have descriptions. - // Use their names instead. - name.device_name = unique_device_name.get(); - } - - // Store the device information. - device_names->push_back(name); - } - } -} - -// static -bool AudioManagerLinux::IsAlsaDeviceAvailable( - AudioManagerLinux::StreamType type, - const char* device_name) { - if (!device_name) - 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; - } -} - -// static -const char* AudioManagerLinux::UnwantedDeviceTypeWhenEnumerating( - AudioManagerLinux::StreamType wanted_type) { - return wanted_type == kStreamPlayback ? "Input" : "Output"; -} - -bool AudioManagerLinux::HasAnyAlsaAudioDevice( - AudioManagerLinux::StreamType stream) { - static const char kPcmInterfaceName[] = "pcm"; - static const char kIoHintName[] = "IOID"; - void** hints = NULL; - bool has_device = false; - int card = -1; - - // Loop through the sound cards. - // Don't use snd_device_name_hint(-1,..) since there is a access violation - // inside this ALSA API with libasound.so.2.0.0. - while (!wrapper_->CardNext(&card) && (card >= 0) && !has_device) { - int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints); - if (!error) { - for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) { - // Only examine devices that are |stream| capable. Valid values are - // "Input", "Output", and NULL which means both input and output. - scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter, - kIoHintName)); - 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. - has_device = true; - break; - } - - // Destroy the hints now that we're done with it. - wrapper_->DeviceNameFreeHint(hints); - hints = NULL; - } else { - DLOG(WARNING) << "HasAnyAudioDevice: unable to get device hints: " - << wrapper_->StrError(error); - } - } - - return has_device; -} - -AudioOutputStream* AudioManagerLinux::MakeLinearOutputStream( - const AudioParameters& params) { - DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); - return MakeOutputStream(params); -} - -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); -} - -AudioInputStream* AudioManagerLinux::MakeLinearInputStream( - const AudioParameters& params, const std::string& device_id) { - DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); - return MakeInputStream(params, device_id); -} - -AudioInputStream* AudioManagerLinux::MakeLowLatencyInputStream( - const AudioParameters& params, const std::string& device_id) { - DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); - return MakeInputStream(params, device_id); -} - -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; - int buffer_size = kDefaultOutputBufferSize; - int bits_per_sample = 16; - int input_channels = 0; - if (input_params.IsValid()) { - // Some clients, such as WebRTC, have a more limited use case and work - // acceptably with a smaller buffer size. The check below allows clients - // which want to try a smaller buffer size on Linux to do so. - // TODO(dalecurtis): This should include bits per channel and channel layout - // eventually. - sample_rate = input_params.sample_rate(); - bits_per_sample = input_params.bits_per_sample(); - channel_layout = input_params.channel_layout(); - input_channels = input_params.input_channels(); - buffer_size = std::min(input_params.frames_per_buffer(), buffer_size); - } - - int user_buffer_size = GetUserBufferSize(); - if (user_buffer_size) - buffer_size = user_buffer_size; - - return AudioParameters( - AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels, - sample_rate, bits_per_sample, buffer_size); -} - -AudioOutputStream* AudioManagerLinux::MakeOutputStream( - const AudioParameters& params) { - std::string device_name = AlsaPcmOutputStream::kAutoSelectDevice; - if (CommandLine::ForCurrentProcess()->HasSwitch( - switches::kAlsaOutputDevice)) { - device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kAlsaOutputDevice); - } - return new AlsaPcmOutputStream(device_name, params, wrapper_.get(), this); -} - -AudioInputStream* AudioManagerLinux::MakeInputStream( - const AudioParameters& params, const std::string& device_id) { - std::string device_name = (device_id == AudioManagerBase::kDefaultDeviceId) ? - AlsaPcmInputStream::kAutoSelectDevice : device_id; - if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAlsaInputDevice)) { - device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kAlsaInputDevice); - } - - return new AlsaPcmInputStream(this, device_name, params, wrapper_.get()); -} - -AudioManager* CreateAudioManager() { +AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) { #if defined(USE_CRAS) if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseCras)) { UMA_HISTOGRAM_ENUMERATION("Media.LinuxAudioIO", kCras, kAudioIOMax); - return new AudioManagerCras(); + return new AudioManagerCras(audio_log_factory); } #endif #if defined(USE_PULSEAUDIO) - AudioManager* manager = AudioManagerPulse::Create(); + AudioManager* manager = AudioManagerPulse::Create(audio_log_factory); if (manager) { UMA_HISTOGRAM_ENUMERATION("Media.LinuxAudioIO", kPulse, kAudioIOMax); return manager; } #endif +#if defined(USE_ALSA) UMA_HISTOGRAM_ENUMERATION("Media.LinuxAudioIO", kAlsa, kAudioIOMax); - return new AudioManagerLinux(); + return new AudioManagerAlsa(audio_log_factory); +#else + return new FakeAudioManager(audio_log_factory); +#endif } } // namespace media diff --git a/chromium/media/audio/mac/audio_auhal_mac.cc b/chromium/media/audio/mac/audio_auhal_mac.cc index 051b709c31d..9fcd46a6a95 100644 --- a/chromium/media/audio/mac/audio_auhal_mac.cc +++ b/chromium/media/audio/mac/audio_auhal_mac.cc @@ -7,29 +7,14 @@ #include <CoreServices/CoreServices.h> #include "base/basictypes.h" -#include "base/command_line.h" #include "base/debug/trace_event.h" #include "base/logging.h" #include "base/mac/mac_logging.h" -#include "media/audio/audio_util.h" #include "media/audio/mac/audio_manager_mac.h" -#include "media/base/media_switches.h" +#include "media/base/audio_pull_fifo.h" namespace media { -static std::ostream& operator<<(std::ostream& os, - const AudioStreamBasicDescription& format) { - os << "sample rate : " << format.mSampleRate << std::endl - << "format ID : " << format.mFormatID << std::endl - << "format flags : " << format.mFormatFlags << std::endl - << "bytes per packet : " << format.mBytesPerPacket << std::endl - << "frames per packet : " << format.mFramesPerPacket << std::endl - << "bytes per frame : " << format.mBytesPerFrame << std::endl - << "channels per frame: " << format.mChannelsPerFrame << std::endl - << "bits per channel : " << format.mBitsPerChannel; - return os; -} - static void ZeroBufferList(AudioBufferList* buffer_list) { for (size_t i = 0; i < buffer_list->mNumberBuffers; ++i) { memset(buffer_list->mBuffers[i].mData, @@ -45,7 +30,7 @@ static void WrapBufferList(AudioBufferList* buffer_list, DCHECK(bus); const int channels = bus->channels(); const int buffer_list_channels = buffer_list->mNumberBuffers; - DCHECK_EQ(channels, buffer_list_channels); + CHECK_EQ(channels, buffer_list_channels); // Copy pointers from AudioBufferList. for (int i = 0; i < channels; ++i) { @@ -72,8 +57,8 @@ AUHALStream::AUHALStream( volume_(1), hardware_latency_frames_(0), stopped_(false), - notified_for_possible_device_change_(false), - input_buffer_list_(NULL) { + input_buffer_list_(NULL), + current_hardware_pending_bytes_(0) { // We must have a manager. DCHECK(manager_); @@ -143,8 +128,12 @@ void AUHALStream::Close() { } if (audio_unit_) { - AudioUnitUninitialize(audio_unit_); - AudioComponentInstanceDispose(audio_unit_); + OSStatus result = AudioUnitUninitialize(audio_unit_); + OSSTATUS_DLOG_IF(ERROR, result != noErr, result) + << "AudioUnitUninitialize() failed."; + result = AudioComponentInstanceDispose(audio_unit_); + OSSTATUS_DLOG_IF(ERROR, result != noErr, result) + << "AudioComponentInstanceDispose() failed."; } // Inform the audio manager that we have been closed. This will cause our @@ -160,20 +149,30 @@ void AUHALStream::Start(AudioSourceCallback* callback) { } stopped_ = false; - notified_for_possible_device_change_ = false; + audio_fifo_.reset(); { base::AutoLock auto_lock(source_lock_); source_ = callback; } - AudioOutputUnitStart(audio_unit_); + OSStatus result = AudioOutputUnitStart(audio_unit_); + if (result == noErr) + return; + + Stop(); + OSSTATUS_DLOG(ERROR, result) << "AudioOutputUnitStart() failed."; + callback->OnError(this); } void AUHALStream::Stop() { if (stopped_) return; - AudioOutputUnitStop(audio_unit_); + OSStatus result = AudioOutputUnitStop(audio_unit_); + OSSTATUS_DLOG_IF(ERROR, result != noErr, result) + << "AudioOutputUnitStop() failed."; + if (result != noErr) + source_->OnError(this); base::AutoLock auto_lock(source_lock_); source_ = NULL; @@ -200,73 +199,69 @@ OSStatus AUHALStream::Render( AudioBufferList* io_data) { TRACE_EVENT0("audio", "AUHALStream::Render"); + // If the stream parameters change for any reason, we need to insert a FIFO + // since the OnMoreData() pipeline can't handle frame size changes. Generally + // this is a temporary situation which can occur after a device change has + // occurred but the AudioManager hasn't received the notification yet. if (number_of_frames != number_of_frames_) { - // This can happen if we've suddenly changed sample-rates. - // The stream should be stopping very soon. - // - // Unfortunately AUAudioInputStream and AUHALStream share the frame - // size set by kAudioDevicePropertyBufferFrameSize above on a per process - // basis. What this means is that the |number_of_frames| value may be - // larger or smaller than the value set during ConfigureAUHAL(). - // In this case either audio input or audio output will be broken, - // so just output silence. - ZeroBufferList(io_data); - - // In case we missed a device notification, notify the AudioManager that the - // device has changed. HandleDeviceChanges() will check to make sure the - // device has actually changed before taking any action. - if (!notified_for_possible_device_change_) { - notified_for_possible_device_change_ = true; - manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind( - &AudioManagerMac::HandleDeviceChanges, base::Unretained(manager_))); + // Create a FIFO on the fly to handle any discrepancies in callback rates. + if (!audio_fifo_) { + VLOG(1) << "Audio frame size change detected; adding FIFO to compensate."; + audio_fifo_.reset(new AudioPullFifo( + output_channels_, + number_of_frames_, + base::Bind(&AUHALStream::ProvideInput, base::Unretained(this)))); } - return noErr; - } - - if (input_channels_ > 0 && input_buffer_list_) { - // Get the input data. |input_buffer_list_| is wrapped - // to point to the data allocated in |input_bus_|. - OSStatus result = AudioUnitRender( - audio_unit_, - flags, - output_time_stamp, - 1, - number_of_frames, - input_buffer_list_); - if (result != noErr) - ZeroBufferList(input_buffer_list_); + // Synchronous IO is not supported in this state. + if (input_channels_ > 0) + input_bus_->Zero(); + } else { + if (input_channels_ > 0 && input_buffer_list_) { + // Get the input data. |input_buffer_list_| is wrapped + // to point to the data allocated in |input_bus_|. + OSStatus result = AudioUnitRender(audio_unit_, + flags, + output_time_stamp, + 1, + number_of_frames, + input_buffer_list_); + if (result != noErr) + ZeroBufferList(input_buffer_list_); + } } // Make |output_bus_| wrap the output AudioBufferList. WrapBufferList(io_data, output_bus_.get(), number_of_frames); // Update the playout latency. - double playout_latency_frames = GetPlayoutLatency(output_time_stamp); + const double playout_latency_frames = GetPlayoutLatency(output_time_stamp); + current_hardware_pending_bytes_ = static_cast<uint32>( + (playout_latency_frames + 0.5) * params_.GetBytesPerFrame()); - uint32 hardware_pending_bytes = static_cast<uint32> - ((playout_latency_frames + 0.5) * output_format_.mBytesPerFrame); + if (audio_fifo_) + audio_fifo_->Consume(output_bus_.get(), output_bus_->frames()); + else + ProvideInput(0, output_bus_.get()); - { - // Render() shouldn't be called except between AudioOutputUnitStart() and - // AudioOutputUnitStop() calls, but crash reports have shown otherwise: - // http://crbug.com/178765. We use |source_lock_| to prevent races and - // crashes in Render() when |source_| is cleared. - base::AutoLock auto_lock(source_lock_); - if (!source_) { - ZeroBufferList(io_data); - return noErr; - } + return noErr; +} - // Supply the input data and render the output data. - source_->OnMoreIOData( - input_bus_.get(), - output_bus_.get(), - AudioBuffersState(0, hardware_pending_bytes)); - output_bus_->Scale(volume_); +void AUHALStream::ProvideInput(int frame_delay, AudioBus* dest) { + base::AutoLock auto_lock(source_lock_); + if (!source_) { + dest->Zero(); + return; } - return noErr; + // Supply the input data and render the output data. + source_->OnMoreIOData( + input_bus_.get(), + dest, + AudioBuffersState(0, + current_hardware_pending_bytes_ + + frame_delay * params_.GetBytesPerFrame())); + dest->Scale(volume_); } // AUHAL callback. @@ -453,7 +448,7 @@ bool AUHALStream::ConfigureAUHAL() { OSStatus result = AudioComponentInstanceNew(comp, &audio_unit_); if (result != noErr) { - OSSTATUS_DLOG(WARNING, result) << "AudioComponentInstanceNew() failed."; + OSSTATUS_DLOG(ERROR, result) << "AudioComponentInstanceNew() failed."; return false; } @@ -511,7 +506,7 @@ bool AUHALStream::ConfigureAUHAL() { &buffer_size, sizeof(buffer_size)); if (result != noErr) { - OSSTATUS_DLOG(WARNING, result) + OSSTATUS_DLOG(ERROR, result) << "AudioUnitSetProperty(kAudioDevicePropertyBufferFrameSize) failed."; return false; } @@ -532,7 +527,7 @@ bool AUHALStream::ConfigureAUHAL() { result = AudioUnitInitialize(audio_unit_); if (result != noErr) { - OSSTATUS_DLOG(WARNING, result) << "AudioUnitInitialize() failed."; + OSSTATUS_DLOG(ERROR, result) << "AudioUnitInitialize() failed."; return false; } diff --git a/chromium/media/audio/mac/audio_auhal_mac.h b/chromium/media/audio/mac/audio_auhal_mac.h index 66feb8d0d11..b488b73c0d1 100644 --- a/chromium/media/audio/mac/audio_auhal_mac.h +++ b/chromium/media/audio/mac/audio_auhal_mac.h @@ -28,6 +28,7 @@ namespace media { class AudioManagerMac; +class AudioPullFifo; // Implementation of AudioOuputStream for Mac OS X using the // AUHAL Audio Unit present in OS 10.4 and later. @@ -83,6 +84,9 @@ class AUHALStream : public AudioOutputStream { UInt32 number_of_frames, AudioBufferList* io_data); + // Called by either |audio_fifo_| or Render() to provide audio data. + void ProvideInput(int frame_delay, AudioBus* dest); + // Helper method to enable input and output. bool EnableIO(bool enable, UInt32 scope); @@ -108,15 +112,15 @@ class AUHALStream : public AudioOutputStream { double GetPlayoutLatency(const AudioTimeStamp* output_time_stamp); // Our creator, the audio manager needs to be notified when we close. - AudioManagerMac* manager_; + AudioManagerMac* const manager_; - AudioParameters params_; + const AudioParameters params_; // For convenience - same as in params_. - int input_channels_; - int output_channels_; + const int input_channels_; + const int output_channels_; // Buffer-size. - size_t number_of_frames_; + const size_t number_of_frames_; // Pointer to the object that will provide the audio samples. AudioSourceCallback* source_; @@ -131,7 +135,7 @@ class AUHALStream : public AudioOutputStream { // The audio device to use with the AUHAL. // We can potentially handle both input and output with this device. - AudioDeviceID device_; + const AudioDeviceID device_; // The AUHAL Audio Unit which talks to |device_|. AudioUnit audio_unit_; @@ -145,10 +149,6 @@ class AUHALStream : public AudioOutputStream { // The flag used to stop the streaming. bool stopped_; - // The flag used to indicate if the AudioManager has been notified of a - // potential device change. Reset to false during Start(). - bool notified_for_possible_device_change_; - // The input AudioUnit renders its data here. scoped_ptr<uint8[]> input_buffer_list_storage_; AudioBufferList* input_buffer_list_; @@ -159,6 +159,13 @@ class AUHALStream : public AudioOutputStream { // Container for retrieving data from AudioSourceCallback::OnMoreIOData(). scoped_ptr<AudioBus> output_bus_; + // Dynamically allocated FIFO used when CoreAudio asks for unexpected frame + // sizes. + scoped_ptr<AudioPullFifo> audio_fifo_; + + // Current buffer delay. Set by Render(). + uint32 current_hardware_pending_bytes_; + DISALLOW_COPY_AND_ASSIGN(AUHALStream); }; diff --git a/chromium/media/audio/mac/audio_auhal_mac_unittest.cc b/chromium/media/audio/mac/audio_auhal_mac_unittest.cc index 9b699ff10f8..d709554dfaf 100644 --- a/chromium/media/audio/mac/audio_auhal_mac_unittest.cc +++ b/chromium/media/audio/mac/audio_auhal_mac_unittest.cc @@ -42,7 +42,7 @@ class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback { class AudioOutputStreamWrapper { public: explicit AudioOutputStreamWrapper() - : audio_man_(AudioManager::Create()), + : audio_man_(AudioManager::CreateForTesting()), format_(AudioParameters::AUDIO_PCM_LOW_LATENCY), bits_per_sample_(kBitsPerSample) { AudioParameters preferred_params = diff --git a/chromium/media/audio/mac/audio_input_mac.cc b/chromium/media/audio/mac/audio_input_mac.cc index 7930567fd9c..4aee1179cfa 100644 --- a/chromium/media/audio/mac/audio_input_mac.cc +++ b/chromium/media/audio/mac/audio_input_mac.cc @@ -10,7 +10,6 @@ #include "base/logging.h" #include "base/mac/mac_logging.h" #include "media/audio/audio_manager_base.h" -#include "media/audio/audio_util.h" 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 d97f453ca99..dbc75bfea31 100644 --- a/chromium/media/audio/mac/audio_low_latency_input_mac.cc +++ b/chromium/media/audio/mac/audio_low_latency_input_mac.cc @@ -9,14 +9,11 @@ #include "base/basictypes.h" #include "base/logging.h" #include "base/mac/mac_logging.h" -#include "media/audio/audio_util.h" #include "media/audio/mac/audio_manager_mac.h" #include "media/base/data_buffer.h" namespace media { -static const int kMinIntervalBetweenVolumeUpdatesMs = 1000; - static std::ostream& operator<<(std::ostream& os, const AudioStreamBasicDescription& format) { os << "sample rate : " << format.mSampleRate << std::endl @@ -104,7 +101,7 @@ AUAudioInputStream::AUAudioInputStream( 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"; + DVLOG_IF(0, requested_size_frames > number_of_frames_) << "FIFO is used"; const int number_of_bytes = number_of_frames_ * format_.mBytesPerFrame; fifo_delay_bytes_ = requested_size_bytes_ - number_of_bytes; diff --git a/chromium/media/audio/mac/audio_low_latency_input_mac_unittest.cc b/chromium/media/audio/mac/audio_low_latency_input_mac_unittest.cc index 9b5985117d7..9360befe575 100644 --- a/chromium/media/audio/mac/audio_low_latency_input_mac_unittest.cc +++ b/chromium/media/audio/mac/audio_low_latency_input_mac_unittest.cc @@ -95,7 +95,7 @@ class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback { class MacAudioInputTest : public testing::Test { protected: - MacAudioInputTest() : audio_manager_(AudioManager::Create()) {} + MacAudioInputTest() : audio_manager_(AudioManager::CreateForTesting()) {} virtual ~MacAudioInputTest() {} // Convenience method which ensures that we are not running on the build diff --git a/chromium/media/audio/mac/audio_manager_mac.cc b/chromium/media/audio/mac/audio_manager_mac.cc index 8e4b969854e..be7dddd5bb6 100644 --- a/chromium/media/audio/mac/audio_manager_mac.cc +++ b/chromium/media/audio/mac/audio_manager_mac.cc @@ -13,7 +13,6 @@ #include "base/mac/scoped_cftyperef.h" #include "base/strings/sys_string_conversions.h" #include "media/audio/audio_parameters.h" -#include "media/audio/audio_util.h" #include "media/audio/mac/audio_auhal_mac.h" #include "media/audio/mac/audio_input_mac.h" #include "media/audio/mac/audio_low_latency_input_mac.h" @@ -36,23 +35,6 @@ static const int kDefaultLowLatencyBufferSize = 128; // Default sample-rate on most Apple hardware. static const int kFallbackSampleRate = 44100; -static int ChooseBufferSize(int output_sample_rate) { - int buffer_size = kDefaultLowLatencyBufferSize; - const int user_buffer_size = GetUserBufferSize(); - if (user_buffer_size) { - buffer_size = user_buffer_size; - } else if (output_sample_rate > 48000) { - // The default buffer size is too small for higher sample rates and may lead - // to glitching. Adjust upwards by multiples of the default size. - if (output_sample_rate <= 96000) - buffer_size = 2 * kDefaultLowLatencyBufferSize; - else if (output_sample_rate <= 192000) - buffer_size = 4 * kDefaultLowLatencyBufferSize; - } - - return buffer_size; -} - static bool HasAudioHardware(AudioObjectPropertySelector selector) { AudioDeviceID output_device_id = kAudioObjectUnknown; const AudioObjectPropertyAddress property_address = { @@ -238,8 +220,9 @@ static AudioDeviceID GetAudioDeviceIdByUId(bool is_input, return audio_device_id; } -AudioManagerMac::AudioManagerMac() - : current_sample_rate_(0) { +AudioManagerMac::AudioManagerMac(AudioLogFactory* audio_log_factory) + : AudioManagerBase(audio_log_factory), + current_sample_rate_(0) { current_output_device_ = kAudioDeviceUnknown; SetMaxOutputStreamsAllowed(kMaxOutputStreams); @@ -474,6 +457,7 @@ std::string AudioManagerMac::GetAssociatedOutputDeviceID( if (result) return std::string(); + std::vector<std::string> associated_devices; for (int i = 0; i < device_count; ++i) { // Get the number of output channels of the device. pa.mSelector = kAudioDevicePropertyStreams; @@ -501,10 +485,30 @@ std::string AudioManagerMac::GetAssociatedOutputDeviceID( std::string ret(base::SysCFStringRefToUTF8(uid)); CFRelease(uid); - return ret; + associated_devices.push_back(ret); } // No matching device found. + if (associated_devices.empty()) + return std::string(); + + // Return the device if there is only one associated device. + if (associated_devices.size() == 1) + return associated_devices[0]; + + // When there are multiple associated devices, we currently do not have a way + // to detect if a device (e.g. a digital output device) is actually connected + // to an endpoint, so we cannot randomly pick a device. + // We pick the device iff the associated device is the default output device. + const std::string default_device = GetDefaultOutputDeviceID(); + for (std::vector<std::string>::const_iterator iter = + associated_devices.begin(); + iter != associated_devices.end(); ++iter) { + if (default_device == *iter) + return *iter; + } + + // Failed to figure out which is the matching device, return an emtpy string. return std::string(); } @@ -542,7 +546,7 @@ AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream( // For I/O, the simplest case is when the default input and output // devices are the same. GetDefaultOutputDevice(&device); - LOG(INFO) << "UNIFIED: default input and output devices are identical"; + VLOG(0) << "UNIFIED: default input and output devices are identical"; } else { // Some audio hardware is presented as separate input and output devices // even though they are really the same physical hardware and @@ -555,7 +559,7 @@ AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream( // so we get the lowest latency and use fewer threads. device = aggregate_device_manager_.GetDefaultAggregateDevice(); if (device != kAudioObjectUnknown) - LOG(INFO) << "Using AGGREGATE audio device"; + VLOG(0) << "Using AGGREGATE audio device"; } if (device != kAudioObjectUnknown && @@ -670,16 +674,20 @@ AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters( } } + if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) + channel_layout = CHANNEL_LAYOUT_DISCRETE; + else + hardware_channels = ChannelLayoutToChannelCount(channel_layout); + AudioParameters params( AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, + hardware_channels, input_channels, hardware_sample_rate, 16, - buffer_size); - - if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) - params.SetDiscreteChannels(hardware_channels); + buffer_size, + AudioParameters::NO_EFFECTS); return params; } @@ -722,8 +730,25 @@ void AudioManagerMac::HandleDeviceChanges() { NotifyAllOutputDeviceChangeListeners(); } -AudioManager* CreateAudioManager() { - return new AudioManagerMac(); +int AudioManagerMac::ChooseBufferSize(int output_sample_rate) { + int buffer_size = kDefaultLowLatencyBufferSize; + const int user_buffer_size = GetUserBufferSize(); + if (user_buffer_size) { + buffer_size = user_buffer_size; + } else if (output_sample_rate > 48000) { + // The default buffer size is too small for higher sample rates and may lead + // to glitching. Adjust upwards by multiples of the default size. + if (output_sample_rate <= 96000) + buffer_size = 2 * kDefaultLowLatencyBufferSize; + else if (output_sample_rate <= 192000) + buffer_size = 4 * kDefaultLowLatencyBufferSize; + } + + return buffer_size; +} + +AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) { + return new AudioManagerMac(audio_log_factory); } } // namespace media diff --git a/chromium/media/audio/mac/audio_manager_mac.h b/chromium/media/audio/mac/audio_manager_mac.h index d162554b405..fb521c940de 100644 --- a/chromium/media/audio/mac/audio_manager_mac.h +++ b/chromium/media/audio/mac/audio_manager_mac.h @@ -22,7 +22,7 @@ namespace media { // the AudioManager class. class MEDIA_EXPORT AudioManagerMac : public AudioManagerBase { public: - AudioManagerMac(); + AudioManagerMac(AudioLogFactory* audio_log_factory); // Implementation of AudioManager. virtual bool HasAudioOutputDevices() OVERRIDE; @@ -62,10 +62,6 @@ class MEDIA_EXPORT AudioManagerMac : public AudioManagerBase { static int HardwareSampleRateForDevice(AudioDeviceID device_id); static int HardwareSampleRate(); - // Notify streams of a device change if the default output device or its - // sample rate has changed, otherwise does nothing. - void HandleDeviceChanges(); - protected: virtual ~AudioManagerMac(); @@ -80,6 +76,12 @@ class MEDIA_EXPORT AudioManagerMac : public AudioManagerBase { void CreateDeviceListener(); void DestroyDeviceListener(); + int ChooseBufferSize(int output_sample_rate); + + // Notify streams of a device change if the default output device or its + // sample rate has changed, otherwise does nothing. + void HandleDeviceChanges(); + scoped_ptr<AudioDeviceListenerMac> output_device_listener_; // Track the output sample-rate and the default output device diff --git a/chromium/media/audio/mac/audio_synchronized_mac.cc b/chromium/media/audio/mac/audio_synchronized_mac.cc index a2484ca67fe..a9bc88e2bd3 100644 --- a/chromium/media/audio/mac/audio_synchronized_mac.cc +++ b/chromium/media/audio/mac/audio_synchronized_mac.cc @@ -11,7 +11,6 @@ #include "base/debug/trace_event.h" #include "base/logging.h" #include "base/mac/mac_logging.h" -#include "media/audio/audio_util.h" #include "media/audio/mac/audio_manager_mac.h" #include "media/base/channel_mixer.h" diff --git a/chromium/media/audio/mac/audio_unified_mac.cc b/chromium/media/audio/mac/audio_unified_mac.cc index 67ec2fe6f3e..d1dc007e6a8 100644 --- a/chromium/media/audio/mac/audio_unified_mac.cc +++ b/chromium/media/audio/mac/audio_unified_mac.cc @@ -9,7 +9,6 @@ #include "base/basictypes.h" #include "base/logging.h" #include "base/mac/mac_logging.h" -#include "media/audio/audio_util.h" #include "media/audio/mac/audio_manager_mac.h" namespace media { diff --git a/chromium/media/audio/mock_audio_manager.cc b/chromium/media/audio/mock_audio_manager.cc index a164332a64a..f2074d65357 100644 --- a/chromium/media/audio/mock_audio_manager.cc +++ b/chromium/media/audio/mock_audio_manager.cc @@ -25,8 +25,8 @@ bool MockAudioManager::HasAudioInputDevices() { return true; } -string16 MockAudioManager::GetAudioInputDeviceModel() { - return string16(); +base::string16 MockAudioManager::GetAudioInputDeviceModel() { + return base::string16(); } void MockAudioManager::ShowAudioInputSettings() { @@ -34,6 +34,11 @@ void MockAudioManager::ShowAudioInputSettings() { void MockAudioManager::GetAudioInputDeviceNames( AudioDeviceNames* device_names) { + DCHECK(device_names->empty()); + device_names->push_back(media::AudioDeviceName("fake_device_name_1", + "fake_device_id_1")); + device_names->push_back(media::AudioDeviceName("fake_device_name_2", + "fake_device_id_2")); } void MockAudioManager::GetAudioOutputDeviceNames( @@ -98,4 +103,11 @@ std::string MockAudioManager::GetAssociatedOutputDeviceID( return std::string(); } +scoped_ptr<AudioLog> MockAudioManager::CreateAudioLog( + AudioLogFactory::AudioComponent component) { + return scoped_ptr<AudioLog>(); +} + +void MockAudioManager::FixWedgedAudio() {} + } // namespace media. diff --git a/chromium/media/audio/mock_audio_manager.h b/chromium/media/audio/mock_audio_manager.h index 7bc30f578e7..2d71fe8493f 100644 --- a/chromium/media/audio/mock_audio_manager.h +++ b/chromium/media/audio/mock_audio_manager.h @@ -27,7 +27,7 @@ class MockAudioManager : public media::AudioManager { virtual bool HasAudioInputDevices() OVERRIDE; - virtual string16 GetAudioInputDeviceModel() OVERRIDE; + virtual base::string16 GetAudioInputDeviceModel() OVERRIDE; virtual void ShowAudioInputSettings() OVERRIDE; @@ -67,9 +67,15 @@ class MockAudioManager : public media::AudioManager { virtual std::string GetAssociatedOutputDeviceID( const std::string& input_device_id) OVERRIDE; - private: + virtual scoped_ptr<AudioLog> CreateAudioLog( + AudioLogFactory::AudioComponent component) OVERRIDE; + + virtual void FixWedgedAudio() OVERRIDE; + + protected: virtual ~MockAudioManager(); + private: scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; DISALLOW_COPY_AND_ASSIGN(MockAudioManager); diff --git a/chromium/media/audio/openbsd/audio_manager_openbsd.cc b/chromium/media/audio/openbsd/audio_manager_openbsd.cc index a97ea8f625e..b378b02d0cd 100644 --- a/chromium/media/audio/openbsd/audio_manager_openbsd.cc +++ b/chromium/media/audio/openbsd/audio_manager_openbsd.cc @@ -11,7 +11,6 @@ #include "base/stl_util.h" #include "media/audio/audio_output_dispatcher.h" #include "media/audio/audio_parameters.h" -#include "media/audio/audio_util.h" #include "media/audio/pulse/pulse_output.h" #include "media/audio/pulse/pulse_stubs.h" #include "media/base/channel_layout.h" @@ -65,8 +64,9 @@ AudioParameters AudioManagerOpenBSD::GetInputStreamParameters( kDefaultSampleRate, 16, kDefaultInputBufferSize); } -AudioManagerOpenBSD::AudioManagerOpenBSD() - : pulse_library_is_initialized_(false) { +AudioManagerOpenBSD::AudioManagerOpenBSD(AudioLogFactory* audio_log_factory) + : AudioManagerBase(audio_log_factory), + pulse_library_is_initialized_(false) { SetMaxOutputStreamsAllowed(kMaxOutputStreams); StubPathMap paths; @@ -139,7 +139,7 @@ AudioParameters AudioManagerOpenBSD::GetPreferredOutputStreamParameters( return AudioParameters( AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels, - sample_rate, bits_per_sample, buffer_size); + sample_rate, bits_per_sample, buffer_size, AudioParameters::NO_EFFECTS); } AudioOutputStream* AudioManagerOpenBSD::MakeOutputStream( @@ -152,8 +152,8 @@ AudioOutputStream* AudioManagerOpenBSD::MakeOutputStream( // TODO(xians): Merge AudioManagerOpenBSD with AudioManagerPulse; // static -AudioManager* CreateAudioManager() { - return new AudioManagerOpenBSD(); +AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) { + return new AudioManagerOpenBSD(audio_log_factory); } } // namespace media diff --git a/chromium/media/audio/openbsd/audio_manager_openbsd.h b/chromium/media/audio/openbsd/audio_manager_openbsd.h index e4bb3948d28..113f5915ae1 100644 --- a/chromium/media/audio/openbsd/audio_manager_openbsd.h +++ b/chromium/media/audio/openbsd/audio_manager_openbsd.h @@ -14,7 +14,7 @@ namespace media { class MEDIA_EXPORT AudioManagerOpenBSD : public AudioManagerBase { public: - AudioManagerOpenBSD(); + AudioManagerOpenBSD(AudioLogFactory* audio_log_factory); // Implementation of AudioManager. virtual bool HasAudioOutputDevices() OVERRIDE; diff --git a/chromium/media/audio/pulse/audio_manager_pulse.cc b/chromium/media/audio/pulse/audio_manager_pulse.cc index 5c09f149057..d369d135bef 100644 --- a/chromium/media/audio/pulse/audio_manager_pulse.cc +++ b/chromium/media/audio/pulse/audio_manager_pulse.cc @@ -10,9 +10,8 @@ #include "base/logging.h" #include "base/nix/xdg_util.h" #include "base/stl_util.h" +#include "media/audio/alsa/audio_manager_alsa.h" #include "media/audio/audio_parameters.h" -#include "media/audio/audio_util.h" -#include "media/audio/linux/audio_manager_linux.h" #include "media/audio/pulse/pulse_input.h" #include "media/audio/pulse/pulse_output.h" #include "media/audio/pulse/pulse_unified.h" @@ -39,8 +38,8 @@ static const base::FilePath::CharType kPulseLib[] = FILE_PATH_LITERAL("libpulse.so.0"); // static -AudioManager* AudioManagerPulse::Create() { - scoped_ptr<AudioManagerPulse> ret(new AudioManagerPulse()); +AudioManager* AudioManagerPulse::Create(AudioLogFactory* audio_log_factory) { + scoped_ptr<AudioManagerPulse> ret(new AudioManagerPulse(audio_log_factory)); if (ret->Init()) return ret.release(); @@ -48,8 +47,9 @@ AudioManager* AudioManagerPulse::Create() { return NULL; } -AudioManagerPulse::AudioManagerPulse() - : input_mainloop_(NULL), +AudioManagerPulse::AudioManagerPulse(AudioLogFactory* audio_log_factory) + : AudioManagerBase(audio_log_factory), + input_mainloop_(NULL), input_context_(NULL), devices_(NULL), native_input_sample_rate_(0) { @@ -78,7 +78,7 @@ bool AudioManagerPulse::HasAudioInputDevices() { } void AudioManagerPulse::ShowAudioInputSettings() { - AudioManagerLinux::ShowLinuxAudioInputSettings(); + AudioManagerAlsa::ShowLinuxAudioInputSettings(); } void AudioManagerPulse::GetAudioDeviceNames( @@ -181,7 +181,7 @@ AudioParameters AudioManagerPulse::GetPreferredOutputStreamParameters( return AudioParameters( AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels, - sample_rate, bits_per_sample, buffer_size); + sample_rate, bits_per_sample, buffer_size, AudioParameters::NO_EFFECTS); } AudioOutputStream* AudioManagerPulse::MakeOutputStream( diff --git a/chromium/media/audio/pulse/audio_manager_pulse.h b/chromium/media/audio/pulse/audio_manager_pulse.h index 36396639929..45fb8cb56fa 100644 --- a/chromium/media/audio/pulse/audio_manager_pulse.h +++ b/chromium/media/audio/pulse/audio_manager_pulse.h @@ -16,10 +16,10 @@ namespace media { class MEDIA_EXPORT AudioManagerPulse : public AudioManagerBase { public: - AudioManagerPulse(); + AudioManagerPulse(AudioLogFactory* audio_log_factory); virtual ~AudioManagerPulse(); - static AudioManager* Create(); + static AudioManager* Create(AudioLogFactory* audio_log_factory); // Implementation of AudioManager. virtual bool HasAudioOutputDevices() OVERRIDE; diff --git a/chromium/media/audio/shared_memory_util.cc b/chromium/media/audio/shared_memory_util.cc deleted file mode 100644 index 523cdb9646c..00000000000 --- a/chromium/media/audio/shared_memory_util.cc +++ /dev/null @@ -1,72 +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/shared_memory_util.h" - -#include <algorithm> - -#include "base/atomicops.h" -#include "base/logging.h" - -using base::subtle::Atomic32; - -static const uint32 kUnknownDataSize = static_cast<uint32>(-1); - -namespace media { - -uint32 TotalSharedMemorySizeInBytes(uint32 packet_size) { - // Need to reserve extra 4 bytes for size of data. - return packet_size + sizeof(Atomic32); -} - -uint32 PacketSizeInBytes(uint32 shared_memory_created_size) { - return shared_memory_created_size - sizeof(Atomic32); -} - -uint32 GetActualDataSizeInBytes(base::SharedMemory* shared_memory, - uint32 packet_size) { - char* ptr = static_cast<char*>(shared_memory->memory()) + packet_size; - DCHECK_EQ(0u, reinterpret_cast<size_t>(ptr) & 3); - - // Actual data size stored at the end of the buffer. - uint32 actual_data_size = - base::subtle::Acquire_Load(reinterpret_cast<volatile Atomic32*>(ptr)); - return std::min(actual_data_size, packet_size); -} - -void SetActualDataSizeInBytes(void* shared_memory_ptr, - uint32 packet_size, - uint32 actual_data_size) { - char* ptr = static_cast<char*>(shared_memory_ptr) + packet_size; - DCHECK_EQ(0u, reinterpret_cast<size_t>(ptr) & 3); - - // Set actual data size at the end of the buffer. - base::subtle::Release_Store(reinterpret_cast<volatile Atomic32*>(ptr), - actual_data_size); -} - -void SetActualDataSizeInBytes(base::SharedMemory* shared_memory, - uint32 packet_size, - uint32 actual_data_size) { - SetActualDataSizeInBytes(shared_memory->memory(), - packet_size, actual_data_size); -} - -void SetUnknownDataSize(base::SharedMemory* shared_memory, - uint32 packet_size) { - SetActualDataSizeInBytes(shared_memory, packet_size, kUnknownDataSize); -} - -bool IsUnknownDataSize(base::SharedMemory* shared_memory, - uint32 packet_size) { - char* ptr = static_cast<char*>(shared_memory->memory()) + packet_size; - DCHECK_EQ(0u, reinterpret_cast<size_t>(ptr) & 3); - - // Actual data size stored at the end of the buffer. - uint32 actual_data_size = - base::subtle::Acquire_Load(reinterpret_cast<volatile Atomic32*>(ptr)); - return actual_data_size == kUnknownDataSize; -} - -} // namespace media diff --git a/chromium/media/audio/shared_memory_util.h b/chromium/media/audio/shared_memory_util.h deleted file mode 100644 index 9186d5c9529..00000000000 --- a/chromium/media/audio/shared_memory_util.h +++ /dev/null @@ -1,39 +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_SHARED_MEMORY_UTIL_H_ -#define MEDIA_AUDIO_SHARED_MEMORY_UTIL_H_ - -#include "base/basictypes.h" -#include "base/memory/shared_memory.h" -#include "media/base/media_export.h" - -namespace media { - -// Value sent by the controller to the renderer in low-latency mode -// indicating that the stream is paused. -enum { kPauseMark = -1 }; - -// Functions that handle data buffer passed between processes in the shared -// memory. Called on both IPC sides. These are necessary because the shared -// memory has a layout: the last word in the block is the data size in bytes. - -MEDIA_EXPORT uint32 TotalSharedMemorySizeInBytes(uint32 packet_size); -MEDIA_EXPORT uint32 PacketSizeInBytes(uint32 shared_memory_created_size); -MEDIA_EXPORT uint32 GetActualDataSizeInBytes(base::SharedMemory* shared_memory, - uint32 packet_size); -MEDIA_EXPORT void SetActualDataSizeInBytes(base::SharedMemory* shared_memory, - uint32 packet_size, - uint32 actual_data_size); -MEDIA_EXPORT void SetActualDataSizeInBytes(void* shared_memory_ptr, - uint32 packet_size, - uint32 actual_data_size); -MEDIA_EXPORT void SetUnknownDataSize(base::SharedMemory* shared_memory, - uint32 packet_size); -MEDIA_EXPORT bool IsUnknownDataSize(base::SharedMemory* shared_memory, - uint32 packet_size); - -} // namespace media - -#endif // MEDIA_AUDIO_SHARED_MEMORY_UTIL_H_ diff --git a/chromium/media/audio/simple_sources.cc b/chromium/media/audio/simple_sources.cc index 7aa74d6e5f1..275413a232c 100644 --- a/chromium/media/audio/simple_sources.cc +++ b/chromium/media/audio/simple_sources.cc @@ -10,7 +10,6 @@ #include <algorithm> #include "base/logging.h" -#include "media/audio/audio_util.h" namespace media { diff --git a/chromium/media/audio/sounds/audio_stream_handler.cc b/chromium/media/audio/sounds/audio_stream_handler.cc new file mode 100644 index 00000000000..08608ac4187 --- /dev/null +++ b/chromium/media/audio/sounds/audio_stream_handler.cc @@ -0,0 +1,188 @@ +// 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/sounds/audio_stream_handler.h" + +#include <string> + +#include "base/logging.h" +#include "base/message_loop/message_loop_proxy.h" +#include "media/audio/audio_manager.h" +#include "media/audio/audio_manager_base.h" +#include "media/base/channel_layout.h" + +namespace media { + +namespace { + +// Volume percent. +const double kOutputVolumePercent = 0.8; + +// The number of frames each OnMoreData() call will request. +const int kDefaultFrameCount = 1024; + +AudioStreamHandler::TestObserver* g_observer_for_testing = NULL; +AudioOutputStream::AudioSourceCallback* g_audio_source_for_testing = NULL; + +} // namespace + +class AudioStreamHandler::AudioStreamContainer + : public AudioOutputStream::AudioSourceCallback { + public: + AudioStreamContainer(const WavAudioHandler& wav_audio, + const AudioParameters& params) + : stream_(NULL), + wav_audio_(wav_audio), + params_(params), + cursor_(0) { + } + + virtual ~AudioStreamContainer() { + DCHECK(AudioManager::Get()->GetMessageLoop()->BelongsToCurrentThread()); + } + + void Play() { + DCHECK(AudioManager::Get()->GetMessageLoop()->BelongsToCurrentThread()); + + if (!stream_) { + stream_ = AudioManager::Get()->MakeAudioOutputStreamProxy(params_, + std::string(), + std::string()); + if (!stream_ || !stream_->Open()) { + LOG(ERROR) << "Failed to open an output stream."; + return; + } + stream_->SetVolume(kOutputVolumePercent); + } else { + // TODO (ygorshenin@): implement smart stream rewind. + stream_->Stop(); + } + + cursor_ = 0; + if (g_audio_source_for_testing) + stream_->Start(g_audio_source_for_testing); + else + stream_->Start(this); + + if (g_observer_for_testing) + g_observer_for_testing->OnPlay(); + } + + void Stop() { + DCHECK(AudioManager::Get()->GetMessageLoop()->BelongsToCurrentThread()); + if (!stream_) + return; + stream_->Stop(); + stream_->Close(); + stream_ = NULL; + + if (g_observer_for_testing) + g_observer_for_testing->OnStop(cursor_); + } + + private: + // AudioOutputStream::AudioSourceCallback overrides: + // Following methods could be called from *ANY* thread. + virtual int OnMoreData(AudioBus* dest, + AudioBuffersState /* state */) OVERRIDE { + size_t bytes_written = 0; + if (wav_audio_.AtEnd(cursor_) || + !wav_audio_.CopyTo(dest, cursor_, &bytes_written)) { + AudioManager::Get()->GetMessageLoop()->PostTask( + FROM_HERE, + base::Bind(&AudioStreamContainer::Stop, base::Unretained(this))); + return 0; + } + cursor_ += bytes_written; + + return dest->frames(); + } + + virtual int OnMoreIOData(AudioBus* /* source */, + AudioBus* dest, + AudioBuffersState state) OVERRIDE { + return OnMoreData(dest, state); + } + + virtual void OnError(AudioOutputStream* /* stream */) OVERRIDE { + LOG(ERROR) << "Error during system sound reproduction."; + } + + AudioOutputStream* stream_; + + const WavAudioHandler wav_audio_; + const AudioParameters params_; + + size_t cursor_; + + DISALLOW_COPY_AND_ASSIGN(AudioStreamContainer); +}; + +AudioStreamHandler::AudioStreamHandler(const base::StringPiece& wav_data) + : wav_audio_(wav_data), + initialized_(false) { + AudioManager* manager = AudioManager::Get(); + if (!manager) { + LOG(ERROR) << "Can't get access to audio manager."; + return; + } + AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY, + GuessChannelLayout(wav_audio_.num_channels()), + wav_audio_.sample_rate(), + wav_audio_.bits_per_sample(), + kDefaultFrameCount); + if (!params.IsValid()) { + LOG(ERROR) << "Audio params are invalid."; + return; + } + stream_.reset(new AudioStreamContainer(wav_audio_, params)); + initialized_ = true; +} + +AudioStreamHandler::~AudioStreamHandler() { + DCHECK(CalledOnValidThread()); + AudioManager::Get()->GetMessageLoop()->PostTask( + FROM_HERE, + base::Bind(&AudioStreamContainer::Stop, base::Unretained(stream_.get()))); + AudioManager::Get()->GetMessageLoop()->DeleteSoon(FROM_HERE, + stream_.release()); +} + +bool AudioStreamHandler::IsInitialized() const { + DCHECK(CalledOnValidThread()); + return initialized_; +} + +bool AudioStreamHandler::Play() { + DCHECK(CalledOnValidThread()); + + if (!IsInitialized()) + return false; + + AudioManager::Get()->GetMessageLoop()->PostTask( + FROM_HERE, + base::Bind(base::IgnoreResult(&AudioStreamContainer::Play), + base::Unretained(stream_.get()))); + return true; +} + +void AudioStreamHandler::Stop() { + DCHECK(CalledOnValidThread()); + AudioManager::Get()->GetMessageLoop()->PostTask( + FROM_HERE, + base::Bind(&AudioStreamContainer::Stop, base::Unretained(stream_.get()))); +} + +// static +void AudioStreamHandler::SetObserverForTesting(TestObserver* observer) { + g_observer_for_testing = observer; +} + +// static +void AudioStreamHandler::SetAudioSourceForTesting( + AudioOutputStream::AudioSourceCallback* source) { + g_audio_source_for_testing = source; +} + +} // namespace media diff --git a/chromium/media/audio/sounds/audio_stream_handler.h b/chromium/media/audio/sounds/audio_stream_handler.h new file mode 100644 index 00000000000..7c63a24f034 --- /dev/null +++ b/chromium/media/audio/sounds/audio_stream_handler.h @@ -0,0 +1,76 @@ +// 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_SOUNDS_AUDIO_STREAM_HANDLER_H_ +#define MEDIA_AUDIO_SOUNDS_AUDIO_STREAM_HANDLER_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/strings/string_piece.h" +#include "base/threading/non_thread_safe.h" +#include "media/audio/audio_io.h" +#include "media/audio/audio_parameters.h" +#include "media/audio/sounds/wav_audio_handler.h" +#include "media/base/media_export.h" + +namespace media { + +class AudioManager; + +// This class sends a sound to the audio manager. +class MEDIA_EXPORT AudioStreamHandler : public base::NonThreadSafe { + public: + class TestObserver { + public: + virtual ~TestObserver() {} + + // Following methods will be called only from the audio thread. + + // Called when AudioOutputStreamProxy::Start() was successfully called. + virtual void OnPlay() = 0; + + // Called when AudioOutputStreamProxy::Stop() was successfully called. + virtual void OnStop(size_t cursor) = 0; + }; + + // C-tor for AudioStreamHandler. |wav_data| should be a raw + // uncompressed WAVE data which will be sent to the audio manager. + explicit AudioStreamHandler(const base::StringPiece& wav_data); + virtual ~AudioStreamHandler(); + + // Returns true iff AudioStreamHandler is correctly initialized; + bool IsInitialized() const; + + // Stops any previous playback if it's still not completed and + // starts new playback. Volume level will be set according to + // current settings and won't be changed during playback. Returns + // true iff new playback was successfully started. + bool Play(); + + // Stops current playback. + void Stop(); + + const WavAudioHandler& wav_audio_handler() const { return wav_audio_; } + + private: + friend class AudioStreamHandlerTest; + friend class SoundsManagerTest; + + class AudioStreamContainer; + + static void SetObserverForTesting(TestObserver* observer); + static void SetAudioSourceForTesting( + AudioOutputStream::AudioSourceCallback* source); + + WavAudioHandler wav_audio_; + scoped_ptr<AudioStreamContainer> stream_; + + bool initialized_; + + DISALLOW_COPY_AND_ASSIGN(AudioStreamHandler); +}; + +} // namespace media + +#endif // MEDIA_AUDIO_SOUNDS_AUDIO_STREAM_HANDLER_H_ diff --git a/chromium/media/audio/sounds/audio_stream_handler_unittest.cc b/chromium/media/audio/sounds/audio_stream_handler_unittest.cc new file mode 100644 index 00000000000..50bc301c38a --- /dev/null +++ b/chromium/media/audio/sounds/audio_stream_handler_unittest.cc @@ -0,0 +1,108 @@ +// 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/bind.h" +#include "base/bind_helpers.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "media/audio/audio_io.h" +#include "media/audio/audio_manager.h" +#include "media/audio/simple_sources.h" +#include "media/audio/sounds/audio_stream_handler.h" +#include "media/audio/sounds/test_data.h" +#include "media/base/channel_layout.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { + +class AudioStreamHandlerTest : public testing::Test { + public: + AudioStreamHandlerTest() {} + virtual ~AudioStreamHandlerTest() {} + + virtual void SetUp() OVERRIDE { + audio_manager_.reset(AudioManager::CreateForTesting()); + + base::StringPiece data(kTestAudioData, arraysize(kTestAudioData)); + audio_stream_handler_.reset(new AudioStreamHandler(data)); + } + + virtual void TearDown() OVERRIDE { + audio_stream_handler_.reset(); + audio_manager_.reset(); + } + + AudioStreamHandler* audio_stream_handler() { + return audio_stream_handler_.get(); + } + + void SetObserverForTesting(AudioStreamHandler::TestObserver* observer) { + AudioStreamHandler::SetObserverForTesting(observer); + } + + void SetAudioSourceForTesting( + AudioOutputStream::AudioSourceCallback* source) { + AudioStreamHandler::SetAudioSourceForTesting(source); + } + + private: + scoped_ptr<AudioManager> audio_manager_; + scoped_ptr<AudioStreamHandler> audio_stream_handler_; + + base::MessageLoop message_loop_; +}; + +TEST_F(AudioStreamHandlerTest, Play) { + base::RunLoop run_loop; + TestObserver observer(run_loop.QuitClosure()); + + SetObserverForTesting(&observer); + + ASSERT_TRUE(audio_stream_handler()->IsInitialized()); + ASSERT_TRUE(audio_stream_handler()->Play()); + + run_loop.Run(); + + SetObserverForTesting(NULL); + + ASSERT_EQ(1, observer.num_play_requests()); + ASSERT_EQ(1, observer.num_stop_requests()); + ASSERT_EQ(4, observer.cursor()); +} + +TEST_F(AudioStreamHandlerTest, Rewind) { + base::RunLoop run_loop; + TestObserver observer(run_loop.QuitClosure()); + SineWaveAudioSource source(CHANNEL_LAYOUT_STEREO, 200.0, 8000); + + SetObserverForTesting(&observer); + SetAudioSourceForTesting(&source); + + ASSERT_TRUE(audio_stream_handler()->IsInitialized()); + + ASSERT_TRUE(audio_stream_handler()->Play()); + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(base::IgnoreResult(&AudioStreamHandler::Play), + base::Unretained(audio_stream_handler())), + base::TimeDelta::FromSeconds(3)); + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&AudioStreamHandler::Stop, + base::Unretained(audio_stream_handler())), + base::TimeDelta::FromSeconds(6)); + + run_loop.Run(); + + SetObserverForTesting(NULL); + SetAudioSourceForTesting(NULL); + + ASSERT_EQ(2, observer.num_play_requests()); + ASSERT_EQ(1, observer.num_stop_requests()); +} + +} // namespace media diff --git a/chromium/media/audio/sounds/sounds_manager.cc b/chromium/media/audio/sounds/sounds_manager.cc new file mode 100644 index 00000000000..e93dc6588dd --- /dev/null +++ b/chromium/media/audio/sounds/sounds_manager.cc @@ -0,0 +1,150 @@ +// 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/sounds/sounds_manager.h" + +#include "base/command_line.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/memory/linked_ptr.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop/message_loop_proxy.h" +#include "media/audio/audio_manager.h" +#include "media/audio/sounds/audio_stream_handler.h" +#include "media/base/media_switches.h" + +namespace media { + +namespace { + +SoundsManager* g_instance = NULL; + +// SoundsManagerImpl --------------------------------------------------- + +class SoundsManagerImpl : public SoundsManager { + public: + SoundsManagerImpl(); + virtual ~SoundsManagerImpl(); + + // SoundsManager implementation: + virtual bool Initialize(SoundKey key, + const base::StringPiece& data) OVERRIDE; + virtual bool Play(SoundKey key) OVERRIDE; + virtual base::TimeDelta GetDuration(SoundKey key) OVERRIDE; + + private: + base::hash_map<SoundKey, linked_ptr<AudioStreamHandler> > handlers_; + scoped_refptr<base::MessageLoopProxy> message_loop_; + + DISALLOW_COPY_AND_ASSIGN(SoundsManagerImpl); +}; + +SoundsManagerImpl::SoundsManagerImpl() + : message_loop_(AudioManager::Get()->GetMessageLoop()) {} + +SoundsManagerImpl::~SoundsManagerImpl() { DCHECK(CalledOnValidThread()); } + +bool SoundsManagerImpl::Initialize(SoundKey key, + const base::StringPiece& data) { + if (handlers_.find(key) != handlers_.end() && handlers_[key]->IsInitialized()) + return true; + linked_ptr<AudioStreamHandler> handler(new AudioStreamHandler(data)); + if (!handler->IsInitialized()) { + LOG(WARNING) << "Can't initialize AudioStreamHandler for key=" << key; + return false; + } + handlers_[key] = handler; + return true; +} + +bool SoundsManagerImpl::Play(SoundKey key) { + DCHECK(CalledOnValidThread()); + if (handlers_.find(key) == handlers_.end() || + !handlers_[key]->IsInitialized()) { + return false; + } + return handlers_[key]->Play(); +} + +base::TimeDelta SoundsManagerImpl::GetDuration(SoundKey key) { + DCHECK(CalledOnValidThread()); + if (handlers_.find(key) == handlers_.end() || + !handlers_[key]->IsInitialized()) { + return base::TimeDelta(); + } + const WavAudioHandler& wav_audio = handlers_[key]->wav_audio_handler(); + const int64 size = wav_audio.size(); + const int64 rate = wav_audio.byte_rate(); + return base::TimeDelta::FromMicroseconds(size * 1000000 / rate); +} + +// SoundsManagerStub --------------------------------------------------- + +class SoundsManagerStub : public SoundsManager { + public: + SoundsManagerStub(); + virtual ~SoundsManagerStub(); + + // SoundsManager implementation: + virtual bool Initialize(SoundKey key, + const base::StringPiece& data) OVERRIDE; + virtual bool Play(SoundKey key) OVERRIDE; + virtual base::TimeDelta GetDuration(SoundKey key) OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(SoundsManagerStub); +}; + +SoundsManagerStub::SoundsManagerStub() {} + +SoundsManagerStub::~SoundsManagerStub() { DCHECK(CalledOnValidThread()); } + +bool SoundsManagerStub::Initialize(SoundKey /* key */, + const base::StringPiece& /* data */) { + DCHECK(CalledOnValidThread()); + return false; +} + +bool SoundsManagerStub::Play(SoundKey /* key */) { + DCHECK(CalledOnValidThread()); + return false; +} + +base::TimeDelta SoundsManagerStub::GetDuration(SoundKey /* key */) { + DCHECK(CalledOnValidThread()); + return base::TimeDelta(); +} + +} // namespace + +SoundsManager::SoundsManager() {} + +SoundsManager::~SoundsManager() { DCHECK(CalledOnValidThread()); } + +// static +void SoundsManager::Create() { + CHECK(!g_instance) << "SoundsManager::Create() is called twice"; + const bool enabled = !CommandLine::ForCurrentProcess()->HasSwitch( + ::switches::kDisableSystemSoundsManager); + if (enabled) + g_instance = new SoundsManagerImpl(); + else + g_instance = new SoundsManagerStub(); +} + +// static +void SoundsManager::Shutdown() { + CHECK(g_instance) << "SoundsManager::Shutdown() is called " + << "without previous call to Create()"; + delete g_instance; + g_instance = NULL; +} + +// static +SoundsManager* SoundsManager::Get() { + CHECK(g_instance) << "SoundsManager::Get() is called before Create()"; + return g_instance; +} + +} // namespace media diff --git a/chromium/media/audio/sounds/sounds_manager.h b/chromium/media/audio/sounds/sounds_manager.h new file mode 100644 index 00000000000..7ff6aafffdc --- /dev/null +++ b/chromium/media/audio/sounds/sounds_manager.h @@ -0,0 +1,56 @@ +// 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_SOUNDS_SOUNDS_MANAGER_H_ +#define MEDIA_AUDIO_SOUNDS_SOUNDS_MANAGER_H_ + +#include "base/basictypes.h" +#include "base/containers/hash_tables.h" +#include "base/strings/string_piece.h" +#include "base/threading/non_thread_safe.h" +#include "base/time/time.h" +#include "media/base/media_export.h" + +namespace media { + +// This class is used for reproduction of system sounds. All methods +// should be accessed from the Audio thread. +class MEDIA_EXPORT SoundsManager : public base::NonThreadSafe { + public: + typedef int SoundKey; + + // Creates a singleton instance of the SoundsManager. + static void Create(); + + // Removes a singleton instance of the SoundsManager. + static void Shutdown(); + + // Returns a pointer to a singleton instance of the SoundsManager. + static SoundsManager* Get(); + + // Initializes SoundsManager with the wav data for the system + // sounds. Returns true if SoundsManager was successfully + // initialized. + virtual bool Initialize(SoundKey key, const base::StringPiece& data) = 0; + + // Plays sound identified by |key|, returns false if SoundsManager + // was not properly initialized. + virtual bool Play(SoundKey key) = 0; + + // Returns duration of the sound identified by |key|. If SoundsManager + // was not properly initialized or |key| was not registered, this + // method returns an empty value. + virtual base::TimeDelta GetDuration(SoundKey key) = 0; + + protected: + SoundsManager(); + virtual ~SoundsManager(); + + private: + DISALLOW_COPY_AND_ASSIGN(SoundsManager); +}; + +} // namespace media + +#endif // MEDIA_AUDIO_SOUNDS_SOUNDS_MANAGER_H_ diff --git a/chromium/media/audio/sounds/sounds_manager_unittest.cc b/chromium/media/audio/sounds/sounds_manager_unittest.cc new file mode 100644 index 00000000000..5aa3694e838 --- /dev/null +++ b/chromium/media/audio/sounds/sounds_manager_unittest.cc @@ -0,0 +1,69 @@ +// 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 <vector> + +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/strings/string_piece.h" +#include "media/audio/audio_manager.h" +#include "media/audio/sounds/audio_stream_handler.h" +#include "media/audio/sounds/sounds_manager.h" +#include "media/audio/sounds/test_data.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { + +class SoundsManagerTest : public testing::Test { + public: + SoundsManagerTest() {} + virtual ~SoundsManagerTest() {} + + virtual void SetUp() OVERRIDE { + audio_manager_.reset(AudioManager::CreateForTesting()); + SoundsManager::Create(); + } + + virtual void TearDown() OVERRIDE { + SoundsManager::Shutdown(); + audio_manager_.reset(); + } + + void SetObserverForTesting(AudioStreamHandler::TestObserver* observer) { + AudioStreamHandler::SetObserverForTesting(observer); + } + + private: + scoped_ptr<AudioManager> audio_manager_; + + base::MessageLoop message_loop_; +}; + +TEST_F(SoundsManagerTest, Play) { + ASSERT_TRUE(SoundsManager::Get()); + + base::RunLoop run_loop; + TestObserver observer(run_loop.QuitClosure()); + + SetObserverForTesting(&observer); + + ASSERT_TRUE(SoundsManager::Get()->Initialize( + kTestAudioKey, + base::StringPiece(kTestAudioData, arraysize(kTestAudioData)))); + ASSERT_EQ(41, + SoundsManager::Get()->GetDuration(kTestAudioKey).InMicroseconds()); + ASSERT_TRUE(SoundsManager::Get()->Play(kTestAudioKey)); + run_loop.Run(); + + ASSERT_EQ(1, observer.num_play_requests()); + ASSERT_EQ(1, observer.num_stop_requests()); + ASSERT_EQ(4, observer.cursor()); + + SetObserverForTesting(NULL); +} + +} // namespace media diff --git a/chromium/media/audio/sounds/test_data.cc b/chromium/media/audio/sounds/test_data.cc new file mode 100644 index 00000000000..dc667c9996d --- /dev/null +++ b/chromium/media/audio/sounds/test_data.cc @@ -0,0 +1,34 @@ +// 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/sounds/test_data.h" + +#include "base/logging.h" +#include "base/message_loop/message_loop.h" + +namespace media { + +TestObserver::TestObserver(const base::Closure& quit) + : loop_(base::MessageLoop::current()), + quit_(quit), + num_play_requests_(0), + num_stop_requests_(0), + cursor_(0) { + DCHECK(loop_); +} + +TestObserver::~TestObserver() { +} + +void TestObserver::OnPlay() { + ++num_play_requests_; +} + +void TestObserver::OnStop(size_t cursor) { + ++num_stop_requests_; + cursor_ = cursor; + loop_->PostTask(FROM_HERE, quit_); +} + +} // namespace media diff --git a/chromium/media/audio/sounds/test_data.h b/chromium/media/audio/sounds/test_data.h new file mode 100644 index 00000000000..d7fb11ddb6b --- /dev/null +++ b/chromium/media/audio/sounds/test_data.h @@ -0,0 +1,51 @@ +// 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_SOUNDS_TEST_UTILS_H_ +#define MEDIA_AUDIO_SOUNDS_TEST_UTILS_H_ + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "media/audio/sounds/audio_stream_handler.h" + +namespace base { +class MessageLoop; +} + +namespace media { + +const int kTestAudioKey = 1000; + +const char kTestAudioData[] = "RIFF\x26\x00\x00\x00WAVEfmt \x10\x00\x00\x00" + "\x01\x00\x02\x00\x80\xbb\x00\x00\x00\x77\x01\x00\x02\x00\x10\x00" + "data\x04\x00\x00\x00\x01\x00\x01\x00"; + +class TestObserver : public AudioStreamHandler::TestObserver { + public: + TestObserver(const base::Closure& quit); + virtual ~TestObserver(); + + // AudioStreamHandler::TestObserver implementation: + virtual void OnPlay() OVERRIDE; + virtual void OnStop(size_t cursor) OVERRIDE; + + int num_play_requests() const { return num_play_requests_; } + int num_stop_requests() const { return num_stop_requests_; } + int cursor() const { return cursor_; } + + private: + base::MessageLoop* loop_; + base::Closure quit_; + + int num_play_requests_; + int num_stop_requests_; + int cursor_; + + DISALLOW_COPY_AND_ASSIGN(TestObserver); +}; + +} // namespace media + +#endif // MEDIA_AUDIO_SOUNDS_TEST_UTILS_H_ diff --git a/chromium/media/audio/sounds/wav_audio_handler.cc b/chromium/media/audio/sounds/wav_audio_handler.cc new file mode 100644 index 00000000000..20eab8be437 --- /dev/null +++ b/chromium/media/audio/sounds/wav_audio_handler.cc @@ -0,0 +1,141 @@ +// 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/sounds/wav_audio_handler.h" + +#include <algorithm> +#include <cstring> + +#include "base/logging.h" +#include "base/sys_byteorder.h" +#include "media/base/audio_bus.h" + +namespace { + +const char kChunkId[] = "RIFF"; +const char kFormat[] = "WAVE"; +const char kSubchunk1Id[] = "fmt "; +const char kSubchunk2Id[] = "data"; + +// The size of the header of a wav file. The header consists of 'RIFF', 4 bytes +// of total data length, and 'WAVE'. +const size_t kWavFileHeaderSize = 12; + +// The size of a chunk header in wav file format. A chunk header consists of a +// tag ('fmt ' or 'data') and 4 bytes of chunk length. +const size_t kChunkHeaderSize = 8; + +// The minimum size of 'fmt' chunk. +const size_t kFmtChunkMinimumSize = 16; + +// The offsets of 'fmt' fields. +const size_t kAudioFormatOffset = 0; +const size_t kChannelOffset = 2; +const size_t kSampleRateOffset = 4; +const size_t kByteRateOffset = 8; +const size_t kBitsPerSampleOffset = 14; + +// Some constants for audio format. +const int kAudioFormatPCM = 1; + +// Reads an integer from |data| with |offset|. +template<typename T> T ReadInt(const base::StringPiece& data, size_t offset) { + CHECK_LE(offset + sizeof(T), data.size()); + T result; + memcpy(&result, data.data() + offset, sizeof(T)); +#if !defined(ARCH_CPU_LITTLE_ENDIAN) + result = base::ByteSwap(result); +#endif + return result; +} + +} // namespace + +namespace media { + +WavAudioHandler::WavAudioHandler(const base::StringPiece& wav_data) + : num_channels_(0), + sample_rate_(0), + byte_rate_(0), + bits_per_sample_(0) { + CHECK_LE(kWavFileHeaderSize, wav_data.size()) << "wav data is too small"; + CHECK(wav_data.starts_with(kChunkId) && + memcmp(wav_data.data() + 8, kFormat, 4) == 0) + << "incorrect wav header"; + + uint32 total_length = std::min(ReadInt<uint32>(wav_data, 4), + static_cast<uint32>(wav_data.size())); + uint32 offset = kWavFileHeaderSize; + while (offset < total_length) { + const int length = ParseSubChunk(wav_data.substr(offset)); + CHECK_LE(0, length) << "can't parse wav sub-chunk"; + offset += length; + } +} + +WavAudioHandler::~WavAudioHandler() { +} + +bool WavAudioHandler::AtEnd(size_t cursor) const { + return data_.size() <= cursor; +} + +bool WavAudioHandler::CopyTo(AudioBus* bus, + size_t cursor, + size_t* bytes_written) const { + if (!bus) + return false; + if (bus->channels() != num_channels_) { + LOG(ERROR) << "Number of channel mismatch."; + return false; + } + if (AtEnd(cursor)) { + bus->Zero(); + return true; + } + const int remaining_frames = (data_.size() - cursor) / bytes_per_frame_; + const int frames = std::min(bus->frames(), remaining_frames); + bus->FromInterleaved(data_.data() + cursor, frames, bytes_per_sample_); + *bytes_written = frames * bytes_per_frame_; + bus->ZeroFramesPartial(frames, bus->frames() - frames); + return true; +} + +int WavAudioHandler::ParseSubChunk(const base::StringPiece& data) { + if (data.size() < kChunkHeaderSize) + return data.size(); + uint32 chunk_length = ReadInt<uint32>(data, 4); + if (data.starts_with(kSubchunk1Id)) { + if (!ParseFmtChunk(data.substr(kChunkHeaderSize, chunk_length))) + return -1; + } else if (data.starts_with(kSubchunk2Id)) { + if (!ParseDataChunk(data.substr(kChunkHeaderSize, chunk_length))) + return -1; + } else { + LOG(ERROR) << "Unknown data chunk: " << data.substr(0, 4) << "."; + } + return chunk_length + kChunkHeaderSize; +} + +bool WavAudioHandler::ParseFmtChunk(const base::StringPiece& data) { + if (data.size() < kFmtChunkMinimumSize) { + LOG(ERROR) << "Data size " << data.size() << " is too short."; + return false; + } + DCHECK_EQ(ReadInt<uint16>(data, kAudioFormatOffset), kAudioFormatPCM); + num_channels_ = ReadInt<uint16>(data, kChannelOffset); + sample_rate_ = ReadInt<uint32>(data, kSampleRateOffset); + byte_rate_ = ReadInt<uint32>(data, kByteRateOffset); + bits_per_sample_ = ReadInt<uint16>(data, kBitsPerSampleOffset); + bytes_per_sample_ = bits_per_sample_ >> 3; + bytes_per_frame_ = num_channels_ * bytes_per_sample_; + return true; +} + +bool WavAudioHandler::ParseDataChunk(const base::StringPiece& data) { + data_ = data; + return true; +} + +} // namespace media diff --git a/chromium/media/audio/sounds/wav_audio_handler.h b/chromium/media/audio/sounds/wav_audio_handler.h new file mode 100644 index 00000000000..a2c3e023650 --- /dev/null +++ b/chromium/media/audio/sounds/wav_audio_handler.h @@ -0,0 +1,59 @@ +// 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_SOUNDS_WAV_AUDIO_HANDLER_H_ +#define MEDIA_AUDIO_SOUNDS_WAV_AUDIO_HANDLER_H_ + +#include "base/strings/string_piece.h" +#include "media/base/media_export.h" + +namespace media { + +class AudioBus; + +// This class provides the input from wav file format. See +// https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ +class MEDIA_EXPORT WavAudioHandler { + public: + explicit WavAudioHandler(const base::StringPiece& wav_data); + virtual ~WavAudioHandler(); + + // Returns true when cursor points to the end of the track. + bool AtEnd(size_t cursor) const; + + // Copies the audio data to |bus| starting from the |cursor| and in + // the case of success stores the number of written bytes in + // |bytes_written|. |bytes_written| should not be NULL. + bool CopyTo(AudioBus* bus, size_t cursor, size_t* bytes_written) const; + + int size() const { return data_.size(); } + uint16 num_channels() const { return num_channels_; } + uint32 sample_rate() const { return sample_rate_; } + uint32 byte_rate() const { return byte_rate_; } + uint16 bits_per_sample() const { return bits_per_sample_; } + + private: + // Parses a chunk of wav format data. Returns the length of the chunk. + int ParseSubChunk(const base::StringPiece& data); + + // Parses the 'fmt' section chunk and stores |params_|. + bool ParseFmtChunk(const base::StringPiece& data); + + // Parses the 'data' section chunk and stores |data_|. + bool ParseDataChunk(const base::StringPiece& data); + + // Data part of the |wav_data_|. + base::StringPiece data_; + + uint16 num_channels_; + uint32 sample_rate_; + uint32 byte_rate_; + uint16 bits_per_sample_; + int bytes_per_sample_; + int bytes_per_frame_; +}; + +} // namespace media + +#endif // MEDIA_AUDIO_SOUNDS_WAV_AUDIO_HANDLER_H_ diff --git a/chromium/media/audio/sounds/wav_audio_handler_unittest.cc b/chromium/media/audio/sounds/wav_audio_handler_unittest.cc new file mode 100644 index 00000000000..a7f8728be35 --- /dev/null +++ b/chromium/media/audio/sounds/wav_audio_handler_unittest.cc @@ -0,0 +1,33 @@ +// 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/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_piece.h" +#include "media/audio/sounds/test_data.h" +#include "media/audio/sounds/wav_audio_handler.h" +#include "media/base/audio_bus.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { + +TEST(WavAudioHandlerTest, SampleDataTest) { + WavAudioHandler handler(base::StringPiece(kTestAudioData, + arraysize(kTestAudioData))); + ASSERT_EQ(static_cast<uint16>(2), handler.num_channels()); + ASSERT_EQ(static_cast<uint16>(16), handler.bits_per_sample()); + ASSERT_EQ(static_cast<uint32>(48000), handler.sample_rate()); + ASSERT_EQ(static_cast<uint32>(96000), handler.byte_rate()); + + ASSERT_EQ(4, handler.size()); + scoped_ptr<AudioBus> bus = AudioBus::Create( + handler.num_channels(), + handler.size() / handler.num_channels()); + size_t bytes_written; + ASSERT_TRUE(handler.CopyTo(bus.get(), 0, &bytes_written)); + ASSERT_EQ(static_cast<size_t>(handler.size()), bytes_written); +} + +} // namespace media diff --git a/chromium/media/audio/test_audio_input_controller_factory.cc b/chromium/media/audio/test_audio_input_controller_factory.cc index d146231a25d..3aeb7773366 100644 --- a/chromium/media/audio/test_audio_input_controller_factory.cc +++ b/chromium/media/audio/test_audio_input_controller_factory.cc @@ -57,11 +57,6 @@ AudioInputController* TestAudioInputControllerFactory::Create( return controller_; } -void TestAudioInputControllerFactory::SetDelegateForTests( - TestAudioInputControllerDelegate* delegate) { - delegate_ = delegate; -} - void TestAudioInputControllerFactory::OnTestAudioInputControllerDestroyed( TestAudioInputController* controller) { DCHECK_EQ(controller_, controller); diff --git a/chromium/media/audio/test_audio_input_controller_factory.h b/chromium/media/audio/test_audio_input_controller_factory.h index 4968c013d97..d49302280f2 100644 --- a/chromium/media/audio/test_audio_input_controller_factory.h +++ b/chromium/media/audio/test_audio_input_controller_factory.h @@ -69,6 +69,10 @@ class TestAudioInputController : public AudioInputController { // Ensure that the closure is run on the audio-manager thread. virtual void Close(const base::Closure& closed_task) OVERRIDE; + const AudioParameters& audio_parameters() const { + return audio_parameters_; + } + protected: virtual ~TestAudioInputController(); @@ -99,7 +103,9 @@ class TestAudioInputControllerFactory : public AudioInputController::Factory { AudioParameters params, UserInputMonitor* user_input_monitor) OVERRIDE; - void SetDelegateForTests(TestAudioInputControllerDelegate* delegate); + void set_delegate(TestAudioInputControllerDelegate* delegate) { + delegate_ = delegate; + } TestAudioInputController* controller() const { return controller_; } diff --git a/chromium/media/audio/win/audio_device_listener_win.cc b/chromium/media/audio/win/audio_device_listener_win.cc index 8734cf2b78f..adbc9a82e4d 100644 --- a/chromium/media/audio/win/audio_device_listener_win.cc +++ b/chromium/media/audio/win/audio_device_listener_win.cc @@ -11,7 +11,6 @@ #include "base/system_monitor/system_monitor.h" #include "base/win/scoped_co_mem.h" #include "base/win/windows_version.h" -#include "media/audio/audio_util.h" #include "media/audio/win/core_audio_util_win.h" using base::win::ScopedCoMem; 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 a174ea2ea0d..b16ef130a9f 100644 --- a/chromium/media/audio/win/audio_low_latency_input_win.cc +++ b/chromium/media/audio/win/audio_low_latency_input_win.cc @@ -7,7 +7,6 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/strings/utf_string_conversions.h" -#include "media/audio/audio_util.h" #include "media/audio/win/audio_manager_win.h" #include "media/audio/win/avrt_wrapper_win.h" 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 11fad25d3fe..54bd3f71b26 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 @@ -100,10 +100,9 @@ class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback { base::FilePath file_path; EXPECT_TRUE(PathService::Get(base::DIR_EXE, &file_path)); file_path = file_path.AppendASCII(file_name); - binary_file_ = file_util::OpenFile(file_path, "wb"); + binary_file_ = base::OpenFile(file_path, "wb"); DLOG_IF(ERROR, !binary_file_) << "Failed to open binary PCM data file."; - LOG(INFO) << ">> Output file: " << file_path.value() - << " has been created."; + VLOG(0) << ">> Output file: " << file_path.value() << " has been created."; } virtual ~WriteToFileAudioSink() { @@ -121,7 +120,7 @@ class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback { buffer_.Seek(chunk_size); bytes_written += chunk_size; } - file_util::CloseFile(binary_file_); + base::CloseFile(binary_file_); } // AudioInputStream::AudioInputCallback implementation. @@ -265,7 +264,7 @@ class ScopedAudioInputStream { // Verify that we can retrieve the current hardware/mixing sample rate // for all available input devices. TEST(WinAudioInputTest, WASAPIAudioInputStreamHardwareSampleRate) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_manager.get())) return; @@ -288,7 +287,7 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamHardwareSampleRate) { // Test Create(), Close() calling sequence. TEST(WinAudioInputTest, WASAPIAudioInputStreamCreateAndClose) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_manager.get())) return; ScopedAudioInputStream ais( @@ -298,7 +297,7 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamCreateAndClose) { // Test Open(), Close() calling sequence. TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenAndClose) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_manager.get())) return; ScopedAudioInputStream ais( @@ -309,7 +308,7 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenAndClose) { // Test Open(), Start(), Close() calling sequence. TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartAndClose) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_manager.get())) return; ScopedAudioInputStream ais( @@ -324,7 +323,7 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartAndClose) { // Test Open(), Start(), Stop(), Close() calling sequence. TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartStopAndClose) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_manager.get())) return; ScopedAudioInputStream ais( @@ -340,7 +339,7 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartStopAndClose) { // Test some additional calling sequences. TEST(WinAudioInputTest, WASAPIAudioInputStreamMiscCallingSequences) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_manager.get())) return; ScopedAudioInputStream ais( @@ -372,7 +371,7 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamMiscCallingSequences) { } TEST(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_manager.get())) return; @@ -454,7 +453,7 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) { // Test that we can capture loopback stream. TEST(WinAudioInputTest, WASAPIAudioInputStreamLoopback) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!audio_manager->HasAudioOutputDevices() || !CoreAudioUtil::IsSupported()) return; @@ -488,7 +487,7 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamLoopback) { // with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS // environment variable to a value greater than 0. TEST(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_manager.get())) return; @@ -501,13 +500,13 @@ TEST(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) { ScopedAudioInputStream ais(aisw.Create()); EXPECT_TRUE(ais->Open()); - LOG(INFO) << ">> Sample rate: " << aisw.sample_rate() << " [Hz]"; + VLOG(0) << ">> Sample rate: " << aisw.sample_rate() << " [Hz]"; WriteToFileAudioSink file_sink(file_name); - LOG(INFO) << ">> Speak into the default microphone while recording."; + VLOG(0) << ">> Speak into the default microphone while recording."; ais->Start(&file_sink); base::PlatformThread::Sleep(TestTimeouts::action_timeout()); ais->Stop(); - LOG(INFO) << ">> Recording has stopped."; + VLOG(0) << ">> Recording has stopped."; ais.Close(); } 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 c889c03ef2c..a10e67a46cb 100644 --- a/chromium/media/audio/win/audio_low_latency_output_win.cc +++ b/chromium/media/audio/win/audio_low_latency_output_win.cc @@ -25,23 +25,6 @@ using base::win::ScopedCoMem; namespace media { -typedef uint32 ChannelConfig; - -// Retrieves an integer mask which corresponds to the channel layout the -// audio engine uses for its internal processing/mixing of shared-mode -// streams. This mask indicates which channels are present in the multi- -// channel stream. The least significant bit corresponds with the Front Left -// speaker, the next least significant bit corresponds to the Front Right -// speaker, and so on, continuing in the order defined in KsMedia.h. -// See http://msdn.microsoft.com/en-us/library/windows/hardware/ff537083(v=vs.85).aspx -// for more details. -static ChannelConfig GetChannelConfig() { - WAVEFORMATPCMEX format; - return SUCCEEDED(CoreAudioUtil::GetDefaultSharedModeMixFormat( - eRender, eConsole, &format)) ? - static_cast<int>(format.dwChannelMask) : 0; -} - // Compare two sets of audio parameters and return true if they are equal. // Note that bits_per_sample() is excluded from this comparison since Core // Audio can deal with most bit depths. As an example, if the native/mixing @@ -55,40 +38,6 @@ static bool CompareAudioParametersNoBitDepthOrChannels( a.frames_per_buffer() == b.frames_per_buffer()); } -// Converts Microsoft's channel configuration to ChannelLayout. -// This mapping is not perfect but the best we can do given the current -// ChannelLayout enumerator and the Windows-specific speaker configurations -// defined in ksmedia.h. Don't assume that the channel ordering in -// ChannelLayout is exactly the same as the Windows specific configuration. -// As an example: KSAUDIO_SPEAKER_7POINT1_SURROUND is mapped to -// CHANNEL_LAYOUT_7_1 but the positions of Back L, Back R and Side L, Side R -// speakers are different in these two definitions. -static ChannelLayout ChannelConfigToChannelLayout(ChannelConfig config) { - switch (config) { - case KSAUDIO_SPEAKER_DIRECTOUT: - return CHANNEL_LAYOUT_NONE; - case KSAUDIO_SPEAKER_MONO: - return CHANNEL_LAYOUT_MONO; - case KSAUDIO_SPEAKER_STEREO: - return CHANNEL_LAYOUT_STEREO; - case KSAUDIO_SPEAKER_QUAD: - return CHANNEL_LAYOUT_QUAD; - case KSAUDIO_SPEAKER_SURROUND: - return CHANNEL_LAYOUT_4_0; - case KSAUDIO_SPEAKER_5POINT1: - return CHANNEL_LAYOUT_5_1_BACK; - case KSAUDIO_SPEAKER_5POINT1_SURROUND: - return CHANNEL_LAYOUT_5_1; - case KSAUDIO_SPEAKER_7POINT1: - return CHANNEL_LAYOUT_7_1_WIDE; - case KSAUDIO_SPEAKER_7POINT1_SURROUND: - return CHANNEL_LAYOUT_7_1; - default: - VLOG(1) << "Unsupported channel layout: " << config; - return CHANNEL_LAYOUT_UNSUPPORTED; - } -} - // static AUDCLNT_SHAREMODE WASAPIAudioOutputStream::GetShareMode() { const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); @@ -98,19 +47,6 @@ AUDCLNT_SHAREMODE WASAPIAudioOutputStream::GetShareMode() { } // static -int WASAPIAudioOutputStream::HardwareChannelCount() { - WAVEFORMATPCMEX format; - return SUCCEEDED(CoreAudioUtil::GetDefaultSharedModeMixFormat( - eRender, eConsole, &format)) ? - static_cast<int>(format.Format.nChannels) : 0; -} - -// static -ChannelLayout WASAPIAudioOutputStream::HardwareChannelLayout() { - return ChannelConfigToChannelLayout(GetChannelConfig()); -} - -// static int WASAPIAudioOutputStream::HardwareSampleRate(const std::string& device_id) { WAVEFORMATPCMEX format; ScopedComPtr<IAudioClient> client; @@ -135,9 +71,12 @@ WASAPIAudioOutputStream::WASAPIAudioOutputStream(AudioManagerWin* manager, ERole device_role) : creating_thread_id_(base::PlatformThread::CurrentId()), manager_(manager), + format_(), opened_(false), audio_parameters_are_valid_(false), volume_(1.0), + packet_size_frames_(0), + packet_size_bytes_(0), endpoint_buffer_size_frames_(0), device_id_(device_id), device_role_(device_role), @@ -187,18 +126,18 @@ WASAPIAudioOutputStream::WASAPIAudioOutputStream(AudioManagerWin* manager, // Add the parts which are unique to WAVE_FORMAT_EXTENSIBLE. format_.Samples.wValidBitsPerSample = params.bits_per_sample(); - format_.dwChannelMask = GetChannelConfig(); + format_.dwChannelMask = CoreAudioUtil::GetChannelConfig(device_id, eRender); format_.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; // Store size (in different units) of audio packets which we expect to // get from the audio endpoint device in each render event. packet_size_frames_ = params.frames_per_buffer(); packet_size_bytes_ = params.GetBytesPerBuffer(); - packet_size_ms_ = (1000.0 * packet_size_frames_) / params.sample_rate(); VLOG(1) << "Number of bytes per audio frame : " << format->nBlockAlign; VLOG(1) << "Number of audio frames per packet: " << packet_size_frames_; VLOG(1) << "Number of bytes per packet : " << packet_size_bytes_; - VLOG(1) << "Number of milliseconds per packet: " << packet_size_ms_; + VLOG(1) << "Number of milliseconds per packet: " + << params.GetBufferDuration().InMillisecondsF(); // All events are auto-reset events and non-signaled initially. @@ -298,6 +237,13 @@ bool WASAPIAudioOutputStream::Open() { audio_client_ = audio_client; audio_render_client_ = audio_render_client; + hr = audio_client_->GetService(__uuidof(IAudioClock), + audio_clock_.ReceiveVoid()); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to get IAudioClock service."; + return false; + } + opened_ = true; return true; } @@ -315,6 +261,17 @@ void WASAPIAudioOutputStream::Start(AudioSourceCallback* callback) { source_ = callback; + // Ensure that the endpoint buffer is prepared with silence. + if (share_mode_ == AUDCLNT_SHAREMODE_SHARED) { + if (!CoreAudioUtil::FillRenderEndpointBufferWithSilence( + audio_client_, audio_render_client_)) { + LOG(ERROR) << "Failed to prepare endpoint buffers with silence."; + callback->OnError(this); + return; + } + } + num_written_frames_ = endpoint_buffer_size_frames_; + // Create and start the thread that will drive the rendering by waiting for // render events. render_thread_.reset( @@ -322,26 +279,18 @@ void WASAPIAudioOutputStream::Start(AudioSourceCallback* callback) { render_thread_->Start(); if (!render_thread_->HasBeenStarted()) { LOG(ERROR) << "Failed to start WASAPI render thread."; + StopThread(); + callback->OnError(this); return; } - // Ensure that the endpoint buffer is prepared with silence. - if (share_mode_ == AUDCLNT_SHAREMODE_SHARED) { - if (!CoreAudioUtil::FillRenderEndpointBufferWithSilence( - audio_client_, audio_render_client_)) { - LOG(WARNING) << "Failed to prepare endpoint buffers with silence."; - return; - } - } - num_written_frames_ = endpoint_buffer_size_frames_; - // Start streaming data between the endpoint buffer and the audio engine. HRESULT hr = audio_client_->Start(); if (FAILED(hr)) { - SetEvent(stop_render_event_.Get()); - render_thread_->Join(); - render_thread_.reset(); - HandleError(hr); + LOG_GETLASTERROR(ERROR) + << "Failed to start output streaming: " << std::hex << hr; + StopThread(); + callback->OnError(this); } } @@ -354,27 +303,21 @@ void WASAPIAudioOutputStream::Stop() { // Stop output audio streaming. HRESULT hr = audio_client_->Stop(); if (FAILED(hr)) { - LOG_IF(ERROR, hr != AUDCLNT_E_NOT_INITIALIZED) + LOG_GETLASTERROR(ERROR) << "Failed to stop output streaming: " << std::hex << hr; + source_->OnError(this); } - // Wait until the thread completes and perform cleanup. - SetEvent(stop_render_event_.Get()); - render_thread_->Join(); - render_thread_.reset(); - - // Ensure that we don't quit the main thread loop immediately next - // time Start() is called. - ResetEvent(stop_render_event_.Get()); - - // Clear source callback, it'll be set again on the next Start() call. - source_ = NULL; + // Make a local copy of |source_| since StopThread() will clear it. + AudioSourceCallback* callback = source_; + StopThread(); // Flush all pending data and reset the audio clock stream position to 0. hr = audio_client_->Reset(); if (FAILED(hr)) { - LOG_IF(ERROR, hr != AUDCLNT_E_NOT_INITIALIZED) + LOG_GETLASTERROR(ERROR) << "Failed to reset streaming: " << std::hex << hr; + callback->OnError(this); } // Extra safety check to ensure that the buffers are cleared. @@ -443,17 +386,9 @@ void WASAPIAudioOutputStream::Run() { audio_samples_render_event_ }; UINT64 device_frequency = 0; - // The IAudioClock interface enables us to monitor a stream's data - // rate and the current position in the stream. Allocate it before we - // start spinning. - ScopedComPtr<IAudioClock> audio_clock; - hr = audio_client_->GetService(__uuidof(IAudioClock), - audio_clock.ReceiveVoid()); - if (SUCCEEDED(hr)) { - // The device frequency is the frequency generated by the hardware clock in - // the audio device. The GetFrequency() method reports a constant frequency. - hr = audio_clock->GetFrequency(&device_frequency); - } + // The device frequency is the frequency generated by the hardware clock in + // the audio device. The GetFrequency() method reports a constant frequency. + hr = audio_clock_->GetFrequency(&device_frequency); error = FAILED(hr); PLOG_IF(ERROR, error) << "Failed to acquire IAudioClock interface: " << std::hex << hr; @@ -474,7 +409,7 @@ void WASAPIAudioOutputStream::Run() { break; case WAIT_OBJECT_0 + 1: // |audio_samples_render_event_| has been set. - RenderAudioFromSource(audio_clock, device_frequency); + error = !RenderAudioFromSource(device_frequency); break; default: error = true; @@ -496,8 +431,7 @@ void WASAPIAudioOutputStream::Run() { } } -void WASAPIAudioOutputStream::RenderAudioFromSource( - IAudioClock* audio_clock, UINT64 device_frequency) { +bool WASAPIAudioOutputStream::RenderAudioFromSource(UINT64 device_frequency) { TRACE_EVENT0("audio", "RenderAudioFromSource"); HRESULT hr = S_FALSE; @@ -518,7 +452,7 @@ void WASAPIAudioOutputStream::RenderAudioFromSource( if (FAILED(hr)) { DLOG(ERROR) << "Failed to retrieve amount of available space: " << std::hex << hr; - return; + return false; } } else { // While the stream is running, the system alternately sends one @@ -536,7 +470,7 @@ void WASAPIAudioOutputStream::RenderAudioFromSource( // Check if there is enough available space to fit the packet size // specified by the client. if (num_available_frames < packet_size_frames_) - return; + return true; DLOG_IF(ERROR, num_available_frames % packet_size_frames_ != 0) << "Non-perfect timing detected (num_available_frames=" @@ -559,7 +493,7 @@ void WASAPIAudioOutputStream::RenderAudioFromSource( if (FAILED(hr)) { DLOG(ERROR) << "Failed to use rendering audio buffer: " << std::hex << hr; - return; + return false; } // Derive the audio delay which corresponds to the delay between @@ -569,7 +503,7 @@ void WASAPIAudioOutputStream::RenderAudioFromSource( // unit at the render side. UINT64 position = 0; int audio_delay_bytes = 0; - hr = audio_clock->GetPosition(&position, NULL); + hr = audio_clock_->GetPosition(&position, NULL); if (SUCCEEDED(hr)) { // Stream position of the sample that is currently playing // through the speaker. @@ -617,14 +551,8 @@ void WASAPIAudioOutputStream::RenderAudioFromSource( num_written_frames_ += packet_size_frames_; } -} -void WASAPIAudioOutputStream::HandleError(HRESULT err) { - CHECK((started() && GetCurrentThreadId() == render_thread_->tid()) || - (!started() && GetCurrentThreadId() == creating_thread_id_)); - NOTREACHED() << "Error code: " << std::hex << err; - if (source_) - source_->OnError(this); + return true; } HRESULT WASAPIAudioOutputStream::ExclusiveModeInitialization( @@ -706,4 +634,22 @@ HRESULT WASAPIAudioOutputStream::ExclusiveModeInitialization( return hr; } +void WASAPIAudioOutputStream::StopThread() { + if (render_thread_ ) { + if (render_thread_->HasBeenStarted()) { + // Wait until the thread completes and perform cleanup. + SetEvent(stop_render_event_.Get()); + render_thread_->Join(); + } + + render_thread_.reset(); + + // Ensure that we don't quit the main thread loop immediately next + // time Start() is called. + ResetEvent(stop_render_event_.Get()); + } + + source_ = NULL; +} + } // namespace media 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 7884d8840f7..2baf6f1ac9a 100644 --- a/chromium/media/audio/win/audio_low_latency_output_win.h +++ b/chromium/media/audio/win/audio_low_latency_output_win.h @@ -138,17 +138,6 @@ class MEDIA_EXPORT WASAPIAudioOutputStream : virtual void SetVolume(double volume) OVERRIDE; virtual void GetVolume(double* volume) OVERRIDE; - // Retrieves the number of channels the audio engine uses for its internal - // processing/mixing of shared-mode streams for the default endpoint device. - static int HardwareChannelCount(); - - // Retrieves the channel layout the audio engine uses for its internal - // processing/mixing of shared-mode streams for the default endpoint device. - // Note that we convert an internal channel layout mask (see ChannelMask()) - // into a Chrome-specific channel layout enumerator in this method, hence - // the match might not be perfect. - static ChannelLayout HardwareChannelLayout(); - // Retrieves the sample rate the audio engine uses for its internal // processing/mixing of shared-mode streams. To fetch the settings for the // default device, pass an empty string as the |device_id|. @@ -168,10 +157,7 @@ class MEDIA_EXPORT WASAPIAudioOutputStream : // Checks available amount of space in the endpoint buffer and reads // data from the client to fill up the buffer without causing audio // glitches. - void RenderAudioFromSource(IAudioClock* audio_clock, UINT64 device_frequency); - - // Issues the OnError() callback to the |sink_|. - void HandleError(HRESULT err); + bool RenderAudioFromSource(UINT64 device_frequency); // Called when the device will be opened in exclusive mode and use the // application specified format. @@ -181,6 +167,11 @@ class MEDIA_EXPORT WASAPIAudioOutputStream : HANDLE event_handle, uint32* endpoint_buffer_size); + // If |render_thread_| is valid, sets |stop_render_event_| and blocks until + // the thread has stopped. |stop_render_event_| is reset after the call. + // |source_| is set to NULL. + void StopThread(); + // Contains the thread ID of the creating thread. base::PlatformThreadId creating_thread_id_; @@ -215,9 +206,6 @@ class MEDIA_EXPORT WASAPIAudioOutputStream : // Size in bytes of each audio packet. size_t packet_size_bytes_; - // Size in milliseconds of each audio packet. - float packet_size_ms_; - // Length of the audio endpoint buffer. uint32 endpoint_buffer_size_frames_; @@ -238,9 +226,6 @@ class MEDIA_EXPORT WASAPIAudioOutputStream : // Pointer to the client that will deliver audio samples to be played out. AudioSourceCallback* source_; - // An IMMDeviceEnumerator interface which represents a device enumerator. - base::win::ScopedComPtr<IMMDeviceEnumerator> device_enumerator_; - // An IAudioClient interface which enables a client to create and initialize // an audio stream between an audio application and the audio engine. base::win::ScopedComPtr<IAudioClient> audio_client_; @@ -259,6 +244,8 @@ class MEDIA_EXPORT WASAPIAudioOutputStream : // Container for retrieving data from AudioSourceCallback::OnMoreData(). scoped_ptr<AudioBus> audio_bus_; + base::win::ScopedComPtr<IAudioClock> audio_clock_; + DISALLOW_COPY_AND_ASSIGN(WASAPIAudioOutputStream); }; 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 1f78facf91d..5fda4b14509 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 @@ -16,7 +16,6 @@ #include "base/win/scoped_com_initializer.h" #include "media/audio/audio_io.h" #include "media/audio/audio_manager.h" -#include "media/audio/audio_util.h" #include "media/audio/win/audio_low_latency_output_win.h" #include "media/audio/win/core_audio_util_win.h" #include "media/base/decoder_buffer.h" @@ -98,7 +97,7 @@ class ReadFromFileAudioSource : public AudioOutputStream::AudioSourceCallback { file_name = file_name.AppendASCII(kDeltaTimeMsFileName); EXPECT_TRUE(!text_file_); - text_file_ = file_util::OpenFile(file_name, "wt"); + text_file_ = base::OpenFile(file_name, "wt"); DLOG_IF(ERROR, !text_file_) << "Failed to open log file."; // Write the array which contains delta times to a text file. @@ -108,7 +107,7 @@ class ReadFromFileAudioSource : public AudioOutputStream::AudioSourceCallback { ++elements_written; } - file_util::CloseFile(text_file_); + base::CloseFile(text_file_); } // AudioOutputStream::AudioSourceCallback implementation. @@ -261,7 +260,7 @@ static AudioOutputStream* CreateDefaultAudioOutputStream( TEST(WASAPIAudioOutputStreamTest, HardwareSampleRate) { // Skip this test in exclusive mode since the resulting rate is only utilized // for shared mode streams. - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_manager.get()) || ExclusiveModeIsEnabled()) return; @@ -274,7 +273,7 @@ TEST(WASAPIAudioOutputStreamTest, HardwareSampleRate) { // Test Create(), Close() calling sequence. TEST(WASAPIAudioOutputStreamTest, CreateAndClose) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_manager.get())) return; AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager.get()); @@ -283,7 +282,7 @@ TEST(WASAPIAudioOutputStreamTest, CreateAndClose) { // Test Open(), Close() calling sequence. TEST(WASAPIAudioOutputStreamTest, OpenAndClose) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_manager.get())) return; AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager.get()); @@ -293,7 +292,7 @@ TEST(WASAPIAudioOutputStreamTest, OpenAndClose) { // Test Open(), Start(), Close() calling sequence. TEST(WASAPIAudioOutputStreamTest, OpenStartAndClose) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_manager.get())) return; AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager.get()); @@ -307,7 +306,7 @@ TEST(WASAPIAudioOutputStreamTest, OpenStartAndClose) { // Test Open(), Start(), Stop(), Close() calling sequence. TEST(WASAPIAudioOutputStreamTest, OpenStartStopAndClose) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_manager.get())) return; AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager.get()); @@ -322,7 +321,7 @@ TEST(WASAPIAudioOutputStreamTest, OpenStartStopAndClose) { // Test SetVolume(), GetVolume() TEST(WASAPIAudioOutputStreamTest, Volume) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_manager.get())) return; AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager.get()); @@ -359,7 +358,7 @@ TEST(WASAPIAudioOutputStreamTest, Volume) { // Test some additional calling sequences. TEST(WASAPIAudioOutputStreamTest, MiscCallingSequences) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_manager.get())) return; @@ -399,7 +398,7 @@ TEST(WASAPIAudioOutputStreamTest, MiscCallingSequences) { // Use preferred packet size and verify that rendering starts. TEST(WASAPIAudioOutputStreamTest, ValidPacketSize) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_manager.get())) return; @@ -435,7 +434,7 @@ TEST(WASAPIAudioOutputStreamTest, ValidPacketSize) { // Use a non-preferred packet size and verify that Open() fails. TEST(WASAPIAudioOutputStreamTest, InvalidPacketSize) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_manager.get())) return; @@ -462,7 +461,7 @@ TEST(WASAPIAudioOutputStreamTest, InvalidPacketSize) { // environment variable to a value greater than 0. // The test files are approximately 20 seconds long. TEST(WASAPIAudioOutputStreamTest, DISABLED_ReadFromStereoFile) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_manager.get())) return; @@ -484,13 +483,13 @@ TEST(WASAPIAudioOutputStreamTest, DISABLED_ReadFromStereoFile) { } ReadFromFileAudioSource file_source(file_name); - LOG(INFO) << "File name : " << file_name.c_str(); - LOG(INFO) << "Sample rate : " << aosw.sample_rate(); - LOG(INFO) << "Bits per sample: " << aosw.bits_per_sample(); - LOG(INFO) << "#channels : " << aosw.channels(); - LOG(INFO) << "File size : " << file_source.file_size(); - LOG(INFO) << "#file segments : " << kNumFileSegments; - LOG(INFO) << ">> Listen to the stereo file while playing..."; + VLOG(0) << "File name : " << file_name.c_str(); + VLOG(0) << "Sample rate : " << aosw.sample_rate(); + VLOG(0) << "Bits per sample: " << aosw.bits_per_sample(); + VLOG(0) << "#channels : " << aosw.channels(); + VLOG(0) << "File size : " << file_source.file_size(); + VLOG(0) << "#file segments : " << kNumFileSegments; + VLOG(0) << ">> Listen to the stereo file while playing..."; for (int i = 0; i < kNumFileSegments; i++) { // Each segment will start with a short (~20ms) block of zeros, hence @@ -503,7 +502,7 @@ TEST(WASAPIAudioOutputStreamTest, DISABLED_ReadFromStereoFile) { aos->Stop(); } - LOG(INFO) << ">> Stereo file playout has stopped."; + VLOG(0) << ">> Stereo file playout has stopped."; aos->Close(); } @@ -515,7 +514,7 @@ TEST(WASAPIAudioOutputStreamTest, ExclusiveModeBufferSizesAt48kHz) { if (!ExclusiveModeIsEnabled()) return; - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_manager.get())) return; @@ -566,7 +565,7 @@ TEST(WASAPIAudioOutputStreamTest, ExclusiveModeBufferSizesAt44kHz) { if (!ExclusiveModeIsEnabled()) return; - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_manager.get())) return; @@ -624,7 +623,7 @@ TEST(WASAPIAudioOutputStreamTest, ExclusiveModeMinBufferSizeAt48kHz) { if (!ExclusiveModeIsEnabled()) return; - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_manager.get())) return; @@ -665,7 +664,7 @@ TEST(WASAPIAudioOutputStreamTest, ExclusiveModeMinBufferSizeAt44kHz) { if (!ExclusiveModeIsEnabled()) return; - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunAudioTests(audio_manager.get())) return; diff --git a/chromium/media/audio/win/audio_manager_win.cc b/chromium/media/audio/win/audio_manager_win.cc index 0352e6677d2..242813a8c65 100644 --- a/chromium/media/audio/win/audio_manager_win.cc +++ b/chromium/media/audio/win/audio_manager_win.cc @@ -20,8 +20,8 @@ #include "base/process/launch.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" +#include "base/win/windows_version.h" #include "media/audio/audio_parameters.h" -#include "media/audio/audio_util.h" #include "media/audio/win/audio_device_listener_win.h" #include "media/audio/win/audio_low_latency_input_win.h" #include "media/audio/win/audio_low_latency_output_win.h" @@ -73,8 +73,8 @@ static int GetVersionPartAsInt(DWORDLONG num) { // Returns a string containing the given device's description and installed // driver version. -static string16 GetDeviceAndDriverInfo(HDEVINFO device_info, - SP_DEVINFO_DATA* device_data) { +static base::string16 GetDeviceAndDriverInfo(HDEVINFO device_info, + SP_DEVINFO_DATA* device_data) { // Save the old install params setting and set a flag for the // SetupDiBuildDriverInfoList below to return only the installed drivers. SP_DEVINSTALL_PARAMS old_device_install_params; @@ -88,13 +88,13 @@ static string16 GetDeviceAndDriverInfo(HDEVINFO device_info, SP_DRVINFO_DATA driver_data; driver_data.cbSize = sizeof(driver_data); - string16 device_and_driver_info; + base::string16 device_and_driver_info; if (SetupDiBuildDriverInfoList(device_info, device_data, SPDIT_COMPATDRIVER)) { if (SetupDiEnumDriverInfo(device_info, device_data, SPDIT_COMPATDRIVER, 0, &driver_data)) { DWORDLONG version = driver_data.DriverVersion; - device_and_driver_info = string16(driver_data.Description) + L" v" + + device_and_driver_info = base::string16(driver_data.Description) + L" v" + base::IntToString16(GetVersionPartAsInt((version >> 48))) + L"." + base::IntToString16(GetVersionPartAsInt((version >> 32))) + L"." + base::IntToString16(GetVersionPartAsInt((version >> 16))) + L"." + @@ -109,7 +109,26 @@ static string16 GetDeviceAndDriverInfo(HDEVINFO device_info, return device_and_driver_info; } -AudioManagerWin::AudioManagerWin() { +static int NumberOfWaveOutBuffers() { + // Use the user provided buffer count if provided. + int buffers = 0; + std::string buffers_str(CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kWaveOutBuffers)); + if (base::StringToInt(buffers_str, &buffers) && buffers > 0) { + return buffers; + } + + // Use 4 buffers for Vista, 3 for everyone else: + // - The entire Windows audio stack was rewritten for Windows Vista and wave + // out performance was degraded compared to XP. + // - The regression was fixed in Windows 7 and most configurations will work + // with 2, but some (e.g., some Sound Blasters) still need 3. + // - Some XP configurations (even multi-processor ones) also need 3. + return (base::win::GetVersion() == base::win::VERSION_VISTA) ? 4 : 3; +} + +AudioManagerWin::AudioManagerWin(AudioLogFactory* audio_log_factory) + : AudioManagerBase(audio_log_factory) { if (!CoreAudioUtil::IsSupported()) { // Use the Wave API for device enumeration if XP or lower. enumeration_type_ = kWaveEnumeration; @@ -157,7 +176,7 @@ void AudioManagerWin::DestroyDeviceListener() { output_device_listener_.reset(); } -string16 AudioManagerWin::GetAudioInputDeviceModel() { +base::string16 AudioManagerWin::GetAudioInputDeviceModel() { // Get the default audio capture device and its device interface name. DWORD device_id = 0; waveInMessage(reinterpret_cast<HWAVEIN>(WAVE_MAPPER), @@ -167,13 +186,13 @@ string16 AudioManagerWin::GetAudioInputDeviceModel() { waveInMessage(reinterpret_cast<HWAVEIN>(device_id), DRV_QUERYDEVICEINTERFACESIZE, reinterpret_cast<DWORD_PTR>(&device_interface_name_size), 0); - size_t bytes_in_char16 = sizeof(string16::value_type); + size_t bytes_in_char16 = sizeof(base::string16::value_type); DCHECK_EQ(0u, device_interface_name_size % bytes_in_char16); if (device_interface_name_size <= bytes_in_char16) - return string16(); // No audio capture device. + return base::string16(); // No audio capture device. - string16 device_interface_name; - string16::value_type* name_ptr = WriteInto(&device_interface_name, + base::string16 device_interface_name; + base::string16::value_type* name_ptr = WriteInto(&device_interface_name, device_interface_name_size / bytes_in_char16); waveInMessage(reinterpret_cast<HWAVEIN>(device_id), DRV_QUERYDEVICEINTERFACE, @@ -185,7 +204,7 @@ string16 AudioManagerWin::GetAudioInputDeviceModel() { HDEVINFO device_info = SetupDiGetClassDevs( &AM_KSCATEGORY_AUDIO, 0, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); if (device_info == INVALID_HANDLE_VALUE) - return string16(); + return base::string16(); DWORD interface_index = 0; SP_DEVICE_INTERFACE_DATA interface_data; @@ -210,7 +229,7 @@ string16 AudioManagerWin::GetAudioInputDeviceModel() { interface_detail, interface_detail_size, NULL, &device_data)) - return string16(); + return base::string16(); bool device_found = (device_interface_name == interface_detail->DevicePath); @@ -218,7 +237,7 @@ string16 AudioManagerWin::GetAudioInputDeviceModel() { return GetDeviceAndDriverInfo(device_info, &device_data); } - return string16(); + return base::string16(); } void AudioManagerWin::ShowAudioInputSettings() { @@ -337,7 +356,8 @@ AudioOutputStream* AudioManagerWin::MakeLowLatencyOutputStream( if (!CoreAudioUtil::IsSupported()) { // Fall back to Windows Wave implementation on Windows XP or lower. - DLOG_IF(ERROR, !device_id.empty()) + DLOG_IF(ERROR, !device_id.empty() && + device_id != AudioManagerBase::kDefaultDeviceId) << "Opening by device id not supported by PCMWaveOutAudioOutputStream"; DVLOG(1) << "Using WaveOut since WASAPI requires at least Vista."; return new PCMWaveOutAudioOutputStream( @@ -347,12 +367,19 @@ AudioOutputStream* AudioManagerWin::MakeLowLatencyOutputStream( // TODO(rtoy): support more than stereo input. if (params.input_channels() > 0) { DVLOG(1) << "WASAPIUnifiedStream is created."; - DLOG_IF(ERROR, !device_id.empty()) + DLOG_IF(ERROR, !device_id.empty() && + device_id != AudioManagerBase::kDefaultDeviceId) << "Opening by device id not supported by WASAPIUnifiedStream"; return new WASAPIUnifiedStream(this, params, input_device_id); } - return new WASAPIAudioOutputStream(this, device_id, params, eConsole); + // Pass an empty string to indicate that we want the default device + // since we consistently only check for an empty string in + // WASAPIAudioOutputStream. + return new WASAPIAudioOutputStream(this, + device_id == AudioManagerBase::kDefaultDeviceId ? + std::string() : device_id, + params, eConsole); } // Factory for the implementations of AudioInputStream for AUDIO_PCM_LINEAR @@ -429,21 +456,25 @@ AudioParameters AudioManagerWin::GetPreferredOutputStreamParameters( } if (input_params.IsValid()) { + // If the user has enabled checking supported channel layouts or we don't + // have a valid channel layout yet, try to use the input layout. See bugs + // http://crbug.com/259165 and http://crbug.com/311906 for more details. if (core_audio_supported && - cmd_line->HasSwitch(switches::kTrySupportedChannelLayouts)) { + (cmd_line->HasSwitch(switches::kTrySupportedChannelLayouts) || + channel_layout == CHANNEL_LAYOUT_UNSUPPORTED)) { // 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. + // TODO(henrika): 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. Then here, we can check the layout from the data we + // already hold. if (CoreAudioUtil::IsChannelLayoutSupported( - eRender, eConsole, input_params.channel_layout())) { + output_device_id, eRender, eConsole, + input_params.channel_layout())) { // Open up using the same channel layout as the source if it is // supported by the hardware. channel_layout = input_params.channel_layout(); @@ -472,7 +503,7 @@ AudioParameters AudioManagerWin::GetPreferredOutputStreamParameters( return AudioParameters( AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels, - sample_rate, bits_per_sample, buffer_size); + sample_rate, bits_per_sample, buffer_size, AudioParameters::NO_EFFECTS); } AudioInputStream* AudioManagerWin::CreatePCMWaveInAudioInputStream( @@ -494,8 +525,8 @@ AudioInputStream* AudioManagerWin::CreatePCMWaveInAudioInputStream( } /// static -AudioManager* CreateAudioManager() { - return new AudioManagerWin(); +AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) { + return new AudioManagerWin(audio_log_factory); } } // namespace media diff --git a/chromium/media/audio/win/audio_manager_win.h b/chromium/media/audio/win/audio_manager_win.h index 86e22badc5f..01044da40a0 100644 --- a/chromium/media/audio/win/audio_manager_win.h +++ b/chromium/media/audio/win/audio_manager_win.h @@ -18,12 +18,12 @@ class AudioDeviceListenerWin; // the AudioManager class. class MEDIA_EXPORT AudioManagerWin : public AudioManagerBase { public: - AudioManagerWin(); + AudioManagerWin(AudioLogFactory* audio_log_factory); // Implementation of AudioManager. virtual bool HasAudioOutputDevices() OVERRIDE; virtual bool HasAudioInputDevices() OVERRIDE; - virtual string16 GetAudioInputDeviceModel() OVERRIDE; + virtual base::string16 GetAudioInputDeviceModel() OVERRIDE; virtual void ShowAudioInputSettings() OVERRIDE; virtual void GetAudioInputDeviceNames( AudioDeviceNames* device_names) OVERRIDE; diff --git a/chromium/media/audio/win/audio_output_win_unittest.cc b/chromium/media/audio/win/audio_output_win_unittest.cc index 7ce146b0ab4..2b8036d52a2 100644 --- a/chromium/media/audio/win/audio_output_win_unittest.cc +++ b/chromium/media/audio/win/audio_output_win_unittest.cc @@ -14,7 +14,6 @@ #include "base/win/windows_version.h" #include "media/base/limits.h" #include "media/audio/audio_io.h" -#include "media/audio/audio_util.h" #include "media/audio/audio_manager.h" #include "media/audio/simple_sources.h" #include "testing/gmock/include/gmock/gmock.h" @@ -176,7 +175,7 @@ class ReadOnlyMappedFile { // Test that can it be created and closed. TEST(WinAudioTest, PCMWaveStreamGetAndClose) { - scoped_ptr<AudioManager> audio_man(AudioManager::Create()); + scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting()); if (!audio_man->HasAudioOutputDevices()) { LOG(WARNING) << "No output device detected."; return; @@ -192,7 +191,7 @@ TEST(WinAudioTest, PCMWaveStreamGetAndClose) { // Test that can it be cannot be created with invalid parameters. TEST(WinAudioTest, SanityOnMakeParams) { - scoped_ptr<AudioManager> audio_man(AudioManager::Create()); + scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting()); if (!audio_man->HasAudioOutputDevices()) { LOG(WARNING) << "No output device detected."; return; @@ -228,7 +227,7 @@ TEST(WinAudioTest, SanityOnMakeParams) { // Test that it can be opened and closed. TEST(WinAudioTest, PCMWaveStreamOpenAndClose) { - scoped_ptr<AudioManager> audio_man(AudioManager::Create()); + scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting()); if (!audio_man->HasAudioOutputDevices()) { LOG(WARNING) << "No output device detected."; return; @@ -245,7 +244,7 @@ TEST(WinAudioTest, PCMWaveStreamOpenAndClose) { // Test that it has a maximum packet size. TEST(WinAudioTest, PCMWaveStreamOpenLimit) { - scoped_ptr<AudioManager> audio_man(AudioManager::Create()); + scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting()); if (!audio_man->HasAudioOutputDevices()) { LOG(WARNING) << "No output device detected."; return; @@ -264,7 +263,7 @@ TEST(WinAudioTest, PCMWaveStreamOpenLimit) { // time. The actual EXPECT_GT are mostly meaningless and the real test is that // the test completes in reasonable time. TEST(WinAudioTest, PCMWaveSlowSource) { - scoped_ptr<AudioManager> audio_man(AudioManager::Create()); + scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting()); if (!audio_man->HasAudioOutputDevices()) { LOG(WARNING) << "No output device detected."; return; @@ -292,7 +291,7 @@ TEST(WinAudioTest, PCMWaveSlowSource) { // gets paused. This test is best when run over RDP with audio enabled. See // bug 19276 for more details. TEST(WinAudioTest, PCMWaveStreamPlaySlowLoop) { - scoped_ptr<AudioManager> audio_man(AudioManager::Create()); + scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting()); if (!audio_man->HasAudioOutputDevices()) { LOG(WARNING) << "No output device detected."; return; @@ -323,7 +322,7 @@ TEST(WinAudioTest, PCMWaveStreamPlaySlowLoop) { // device at 44.1K s/sec. Parameters have been chosen carefully so you should // not hear pops or noises while the sound is playing. TEST(WinAudioTest, PCMWaveStreamPlay200HzTone44Kss) { - scoped_ptr<AudioManager> audio_man(AudioManager::Create()); + scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting()); if (!audio_man->HasAudioOutputDevices()) { LOG(WARNING) << "No output device detected."; return; @@ -351,7 +350,7 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzTone44Kss) { // not hear pops or noises while the sound is playing. The audio also should // sound with a lower volume than PCMWaveStreamPlay200HzTone44Kss. TEST(WinAudioTest, PCMWaveStreamPlay200HzTone22Kss) { - scoped_ptr<AudioManager> audio_man(AudioManager::Create()); + scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting()); if (!audio_man->HasAudioOutputDevices()) { LOG(WARNING) << "No output device detected."; return; @@ -386,7 +385,7 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzTone22Kss) { // try hard to generate situation where the two threads are accessing the // object roughly at the same time. TEST(WinAudioTest, PushSourceFile16KHz) { - scoped_ptr<AudioManager> audio_man(AudioManager::Create()); + scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting()); if (!audio_man->HasAudioOutputDevices()) { LOG(WARNING) << "No output device detected."; return; @@ -429,7 +428,7 @@ TEST(WinAudioTest, PushSourceFile16KHz) { // stopped. You will here two .5 seconds wave signal separated by 0.5 seconds // of silence. TEST(WinAudioTest, PCMWaveStreamPlayTwice200HzTone44Kss) { - scoped_ptr<AudioManager> audio_man(AudioManager::Create()); + scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting()); if (!audio_man->HasAudioOutputDevices()) { LOG(WARNING) << "No output device detected."; return; @@ -466,7 +465,7 @@ TEST(WinAudioTest, PCMWaveStreamPlayTwice200HzTone44Kss) { // higher and Wave is used for XP and lower. It is possible to utilize a // smaller buffer size for WASAPI than for Wave. TEST(WinAudioTest, PCMWaveStreamPlay200HzToneLowLatency) { - scoped_ptr<AudioManager> audio_man(AudioManager::Create()); + scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting()); if (!audio_man->HasAudioOutputDevices()) { LOG(WARNING) << "No output device detected."; return; @@ -510,7 +509,7 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzToneLowLatency) { // Check that the pending bytes value is correct what the stream starts. TEST(WinAudioTest, PCMWaveStreamPendingBytes) { - scoped_ptr<AudioManager> audio_man(AudioManager::Create()); + scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting()); if (!audio_man->HasAudioOutputDevices()) { LOG(WARNING) << "No output device detected."; return; @@ -539,28 +538,22 @@ TEST(WinAudioTest, PCMWaveStreamPendingBytes) { EXPECT_CALL(source, OnMoreData(NotNull(), Field(&AudioBuffersState::pending_bytes, 0))) .WillOnce(Invoke(MockAudioSource::ClearData)); - switch (NumberOfWaveOutBuffers()) { - case 2: - break; // Calls are the same as at end of 3-buffer scheme. - case 3: - EXPECT_CALL(source, OnMoreData(NotNull(), - Field(&AudioBuffersState::pending_bytes, - bytes_100_ms))) - .WillOnce(Invoke(MockAudioSource::ClearData)); - EXPECT_CALL(source, OnMoreData(NotNull(), - Field(&AudioBuffersState::pending_bytes, - 2 * bytes_100_ms))) - .WillOnce(Invoke(MockAudioSource::ClearData)); - EXPECT_CALL(source, OnMoreData(NotNull(), - Field(&AudioBuffersState::pending_bytes, - 2 * bytes_100_ms))) - .Times(AnyNumber()) - .WillRepeatedly(Return(0)); - break; - default: - ASSERT_TRUE(false) - << "Unexpected number of buffers: " << NumberOfWaveOutBuffers(); - } + + // Note: If AudioManagerWin::NumberOfWaveOutBuffers() ever changes, or if this + // test is run on Vista, these expectations will fail. + EXPECT_CALL(source, OnMoreData(NotNull(), + Field(&AudioBuffersState::pending_bytes, + bytes_100_ms))) + .WillOnce(Invoke(MockAudioSource::ClearData)); + EXPECT_CALL(source, OnMoreData(NotNull(), + Field(&AudioBuffersState::pending_bytes, + 2 * bytes_100_ms))) + .WillOnce(Invoke(MockAudioSource::ClearData)); + EXPECT_CALL(source, OnMoreData(NotNull(), + Field(&AudioBuffersState::pending_bytes, + 2 * bytes_100_ms))) + .Times(AnyNumber()) + .WillRepeatedly(Return(0)); EXPECT_CALL(source, OnMoreData(NotNull(), Field(&AudioBuffersState::pending_bytes, bytes_100_ms))) @@ -667,7 +660,7 @@ DWORD __stdcall SyncSocketThread(void* context) { // related to the two different audio-layers for AUDIO_PCM_LOW_LATENCY. // In this test you should hear a continuous 200Hz tone for 2 seconds. TEST(WinAudioTest, SyncSocketBasic) { - scoped_ptr<AudioManager> audio_man(AudioManager::Create()); + scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting()); if (!audio_man->HasAudioOutputDevices()) { LOG(WARNING) << "No output device detected."; return; diff --git a/chromium/media/audio/win/audio_unified_win.cc b/chromium/media/audio/win/audio_unified_win.cc index 5c1594ef8f8..901c8b897fa 100644 --- a/chromium/media/audio/win/audio_unified_win.cc +++ b/chromium/media/audio/win/audio_unified_win.cc @@ -51,23 +51,6 @@ static const char kUnifiedAudioDebugFileName[] = "unified_win_debug.txt"; static const char kUnifiedAudioParamsFileName[] = "unified_win_params.txt"; #endif -typedef uint32 ChannelConfig; - -// Retrieves an integer mask which corresponds to the channel layout the -// audio engine uses for its internal processing/mixing of shared-mode -// streams. This mask indicates which channels are present in the multi- -// channel stream. The least significant bit corresponds with the Front Left -// speaker, the next least significant bit corresponds to the Front Right -// speaker, and so on, continuing in the order defined in KsMedia.h. -// See http://msdn.microsoft.com/en-us/library/windows/hardware/ff537083(v=vs.85).aspx -// for more details. -static ChannelConfig GetChannelConfig(EDataFlow data_flow) { - WAVEFORMATPCMEX format; - return SUCCEEDED(media::CoreAudioUtil::GetDefaultSharedModeMixFormat( - data_flow, eConsole, &format)) ? - static_cast<int>(format.dwChannelMask) : 0; -} - // Use the acquired IAudioClock interface to derive a time stamp of the audio // sample which is currently playing through the speakers. static double SpeakerStreamPosInMilliseconds(IAudioClock* clock) { @@ -178,7 +161,7 @@ WASAPIUnifiedStream::~WASAPIUnifiedStream() { base::FilePath data_file_name; PathService::Get(base::DIR_EXE, &data_file_name); data_file_name = data_file_name.AppendASCII(kUnifiedAudioDebugFileName); - data_file_ = file_util::OpenFile(data_file_name, "wt"); + data_file_ = base::OpenFile(data_file_name, "wt"); DVLOG(1) << ">> Output file " << data_file_name.value() << " is created."; size_t n = 0; @@ -192,16 +175,16 @@ WASAPIUnifiedStream::~WASAPIUnifiedStream() { fifo_rate_comps_[n]); ++n; } - file_util::CloseFile(data_file_); + base::CloseFile(data_file_); base::FilePath param_file_name; PathService::Get(base::DIR_EXE, ¶m_file_name); param_file_name = param_file_name.AppendASCII(kUnifiedAudioParamsFileName); - param_file_ = file_util::OpenFile(param_file_name, "wt"); + param_file_ = base::OpenFile(param_file_name, "wt"); DVLOG(1) << ">> Output file " << param_file_name.value() << " is created."; fprintf(param_file_, "%d %d\n", input_params_[0], input_params_[1]); fprintf(param_file_, "%d %d\n", output_params_[0], output_params_[1]); - file_util::CloseFile(param_file_); + base::CloseFile(param_file_); #endif } @@ -575,8 +558,9 @@ void WASAPIUnifiedStream::SetIOFormats(const AudioParameters& input_params, // Add the parts which are unique to WAVE_FORMAT_EXTENSIBLE. // Note that we always open up using the native channel layout. (*xformat).Samples.wValidBitsPerSample = format->wBitsPerSample; - (*xformat).dwChannelMask = (n == 0) ? - GetChannelConfig(eCapture) : GetChannelConfig(eRender); + (*xformat).dwChannelMask = + CoreAudioUtil::GetChannelConfig( + std::string(), n == 0 ? eCapture : eRender); (*xformat).SubFormat = KSDATAFORMAT_SUBTYPE_PCM; } diff --git a/chromium/media/audio/win/audio_unified_win_unittest.cc b/chromium/media/audio/win/audio_unified_win_unittest.cc index 011c36348b5..15573aec76a 100644 --- a/chromium/media/audio/win/audio_unified_win_unittest.cc +++ b/chromium/media/audio/win/audio_unified_win_unittest.cc @@ -12,7 +12,6 @@ #include "base/win/scoped_com_initializer.h" #include "media/audio/audio_io.h" #include "media/audio/audio_manager.h" -#include "media/audio/audio_util.h" #include "media/audio/win/audio_unified_win.h" #include "media/audio/win/core_audio_util_win.h" #include "media/base/channel_mixer.h" @@ -75,9 +74,9 @@ class UnifiedSourceCallback : public AudioOutputStream::AudioSourceCallback { file_name = file_name.AppendASCII(kDeltaTimeMsFileName); EXPECT_TRUE(!text_file_); - text_file_ = file_util::OpenFile(file_name, "wt"); + text_file_ = base::OpenFile(file_name, "wt"); DLOG_IF(ERROR, !text_file_) << "Failed to open log file."; - LOG(INFO) << ">> Output file " << file_name.value() << " has been created."; + VLOG(0) << ">> Output file " << file_name.value() << " has been created."; // Write the array which contains delta times to a text file. size_t elements_written = 0; @@ -85,7 +84,7 @@ class UnifiedSourceCallback : public AudioOutputStream::AudioSourceCallback { fprintf(text_file_, "%d\n", delta_times_[elements_written]); ++elements_written; } - file_util::CloseFile(text_file_); + base::CloseFile(text_file_); } virtual int OnMoreData(AudioBus* dest, @@ -264,7 +263,7 @@ static WASAPIUnifiedStream* CreateDefaultUnifiedStream( // Test Open(), Close() calling sequence. TEST(WASAPIUnifiedStreamTest, OpenAndClose) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunUnifiedAudioTests(audio_manager.get())) return; @@ -275,7 +274,7 @@ TEST(WASAPIUnifiedStreamTest, OpenAndClose) { // Test Open(), Close() calling sequence for all available capture devices. TEST(WASAPIUnifiedStreamTest, OpenAndCloseForAllInputDevices) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunUnifiedAudioTests(audio_manager.get())) return; @@ -292,7 +291,7 @@ TEST(WASAPIUnifiedStreamTest, OpenAndCloseForAllInputDevices) { // Test Open(), Start(), Close() calling sequence. TEST(WASAPIUnifiedStreamTest, OpenStartAndClose) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunUnifiedAudioTests(audio_manager.get())) return; @@ -312,7 +311,7 @@ TEST(WASAPIUnifiedStreamTest, OpenStartAndClose) { // Verify that IO callbacks starts as they should. TEST(WASAPIUnifiedStreamTest, StartLoopbackAudio) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunUnifiedAudioTests(audio_manager.get())) return; @@ -348,7 +347,7 @@ TEST(WASAPIUnifiedStreamTest, StartLoopbackAudio) { // back to the speaker. This test allows the user to verify that the audio // sounds OK. A text file with name |kDeltaTimeMsFileName| is also generated. TEST(WASAPIUnifiedStreamTest, DISABLED_RealTimePlayThrough) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); if (!CanRunUnifiedAudioTests(audio_manager.get())) return; diff --git a/chromium/media/audio/win/core_audio_util_win.cc b/chromium/media/audio/win/core_audio_util_win.cc index 4adfdda090a..790b2b140f7 100644 --- a/chromium/media/audio/win/core_audio_util_win.cc +++ b/chromium/media/audio/win/core_audio_util_win.cc @@ -25,8 +25,6 @@ namespace media { enum { KSAUDIO_SPEAKER_UNSUPPORTED = 0 }; -typedef uint32 ChannelConfig; - // Converts Microsoft's channel configuration to ChannelLayout. // This mapping is not perfect but the best we can do given the current // ChannelLayout enumerator and the Windows-specific speaker configurations @@ -401,7 +399,7 @@ std::string CoreAudioUtil::GetMatchingOutputDeviceID( ScopedComPtr<IMMDevice> output_device; for (UINT i = 0; i < count; ++i) { collection->Item(i, output_device.Receive()); - std::string output_controller_id(CoreAudioUtil::GetAudioControllerID( + std::string output_controller_id(GetAudioControllerID( output_device, enumerator)); if (output_controller_id == controller_id) break; @@ -478,6 +476,18 @@ ScopedComPtr<IAudioClient> CoreAudioUtil::CreateDefaultClient( ScopedComPtr<IAudioClient>()); } +ScopedComPtr<IAudioClient> CoreAudioUtil::CreateClient( + const std::string& device_id, EDataFlow data_flow, ERole role) { + if (device_id.empty()) + return CreateDefaultClient(data_flow, role); + + ScopedComPtr<IMMDevice> device(CreateDevice(device_id)); + if (!device) + return ScopedComPtr<IAudioClient>(); + + return CreateClient(device); +} + HRESULT CoreAudioUtil::GetSharedModeMixFormat( IAudioClient* client, WAVEFORMATPCMEX* format) { DCHECK(IsSupported()); @@ -496,18 +506,6 @@ HRESULT CoreAudioUtil::GetSharedModeMixFormat( return hr; } -HRESULT CoreAudioUtil::GetDefaultSharedModeMixFormat( - EDataFlow data_flow, ERole role, WAVEFORMATPCMEX* format) { - DCHECK(IsSupported()); - ScopedComPtr<IAudioClient> client(CreateDefaultClient(data_flow, role)); - if (!client) { - // Map NULL-pointer to new error code which can be different from the - // actual error code. The exact value is not important here. - return AUDCLNT_E_ENDPOINT_CREATE_FAILED; - } - return CoreAudioUtil::GetSharedModeMixFormat(client, format); -} - bool CoreAudioUtil::IsFormatSupported(IAudioClient* client, AUDCLNT_SHAREMODE share_mode, const WAVEFORMATPCMEX* format) { @@ -529,18 +527,20 @@ bool CoreAudioUtil::IsFormatSupported(IAudioClient* client, return (hr == S_OK); } -bool CoreAudioUtil::IsChannelLayoutSupported(EDataFlow data_flow, ERole role, +bool CoreAudioUtil::IsChannelLayoutSupported(const std::string& device_id, + EDataFlow data_flow, + ERole role, ChannelLayout channel_layout) { DCHECK(IsSupported()); // First, get the preferred mixing format for shared mode streams. - ScopedComPtr<IAudioClient> client(CreateDefaultClient(data_flow, role)); + ScopedComPtr<IAudioClient> client(CreateClient(device_id, data_flow, role)); if (!client) return false; WAVEFORMATPCMEX format; - HRESULT hr = CoreAudioUtil::GetSharedModeMixFormat(client, &format); + HRESULT hr = GetSharedModeMixFormat(client, &format); if (FAILED(hr)) return false; @@ -623,6 +623,16 @@ HRESULT CoreAudioUtil::GetPreferredAudioParameters( // Convert Microsoft's channel configuration to genric ChannelLayout. ChannelLayout channel_layout = ChannelConfigToChannelLayout(channel_config); + // Some devices don't appear to set a valid channel layout, so guess based on + // the number of channels. See http://crbug.com/311906. + if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) { + VLOG(1) << "Unsupported channel config: " + << std::hex << channel_config + << ". Guessing layout by channel count: " + << std::dec << mix_format.Format.nChannels; + channel_layout = GuessChannelLayout(mix_format.Format.nChannels); + } + // Preferred sample rate. int sample_rate = mix_format.Format.nSamplesPerSec; @@ -684,6 +694,18 @@ HRESULT CoreAudioUtil::GetPreferredAudioParameters( return GetPreferredAudioParameters(client, params); } +ChannelConfig CoreAudioUtil::GetChannelConfig(const std::string& device_id, + EDataFlow data_flow) { + ScopedComPtr<IAudioClient> client( + CreateClient(device_id, data_flow, eConsole)); + + WAVEFORMATPCMEX format = {0}; + if (!client || FAILED(GetSharedModeMixFormat(client, &format))) + return 0; + + return static_cast<ChannelConfig>(format.dwChannelMask); +} + HRESULT CoreAudioUtil::SharedModeInitialize(IAudioClient* client, const WAVEFORMATPCMEX* format, HANDLE event_handle, diff --git a/chromium/media/audio/win/core_audio_util_win.h b/chromium/media/audio/win/core_audio_util_win.h index cdf6dfb11df..a210af906ea 100644 --- a/chromium/media/audio/win/core_audio_util_win.h +++ b/chromium/media/audio/win/core_audio_util_win.h @@ -26,6 +26,12 @@ using base::win::ScopedComPtr; namespace media { + +// Represents audio channel configuration constants as understood by Windows. +// E.g. KSAUDIO_SPEAKER_MONO. For a list of possible values see: +// http://msdn.microsoft.com/en-us/library/windows/hardware/ff537083(v=vs.85).aspx +typedef uint32 ChannelConfig; + class MEDIA_EXPORT CoreAudioUtil { public: // Returns true if Windows Core Audio is supported. @@ -106,7 +112,7 @@ class MEDIA_EXPORT CoreAudioUtil { // manage the flow of audio data between the application and an audio endpoint // device. - // Create an IAudioClient interface for the default IMMDevice where + // Create an IAudioClient instance for the default IMMDevice where // flow direction and role is define by |data_flow| and |role|. // The IAudioClient interface enables a client to create and initialize an // audio stream between an audio application and the audio engine (for a @@ -115,6 +121,12 @@ class MEDIA_EXPORT CoreAudioUtil { static ScopedComPtr<IAudioClient> CreateDefaultClient(EDataFlow data_flow, ERole role); + // Create an IAudioClient instance for a specific device _or_ the default + // device if |device_id| is empty. + static ScopedComPtr<IAudioClient> CreateClient(const std::string& device_id, + EDataFlow data_flow, + ERole role); + // Create an IAudioClient interface for an existing IMMDevice given by // |audio_device|. Flow direction and role is define by the |audio_device|. static ScopedComPtr<IAudioClient> CreateClient(IMMDevice* audio_device); @@ -126,13 +138,6 @@ class MEDIA_EXPORT CoreAudioUtil { static HRESULT GetSharedModeMixFormat(IAudioClient* client, WAVEFORMATPCMEX* format); - // Get the mix format that the audio engine uses internally for processing - // of shared-mode streams using the default IMMDevice where flow direction - // and role is define by |data_flow| and |role|. - static HRESULT GetDefaultSharedModeMixFormat(EDataFlow data_flow, - ERole role, - WAVEFORMATPCMEX* format); - // Returns true if the specified |client| supports the format in |format| // for the given |share_mode| (shared or exclusive). static bool IsFormatSupported(IAudioClient* client, @@ -144,7 +149,9 @@ class MEDIA_EXPORT CoreAudioUtil { // and |role|. If this method returns true for a certain channel layout, it // means that SharedModeInitialize() will succeed using a format based on // the preferred format where the channel layout has been modified. - static bool IsChannelLayoutSupported(EDataFlow data_flow, ERole role, + static bool IsChannelLayoutSupported(const std::string& device_id, + EDataFlow data_flow, + ERole role, ChannelLayout channel_layout); // For a shared-mode stream, the audio engine periodically processes the @@ -170,6 +177,19 @@ class MEDIA_EXPORT CoreAudioUtil { static HRESULT GetPreferredAudioParameters(const std::string& device_id, AudioParameters* params); + // Retrieves an integer mask which corresponds to the channel layout the + // audio engine uses for its internal processing/mixing of shared-mode + // streams. This mask indicates which channels are present in the multi- + // channel stream. The least significant bit corresponds with the Front Left + // speaker, the next least significant bit corresponds to the Front Right + // speaker, and so on, continuing in the order defined in KsMedia.h. + // See http://msdn.microsoft.com/en-us/library/windows/hardware/ff537083(v=vs.85).aspx + // for more details. + // To get the channel config of the default device, pass an empty string + // for |device_id|. + static ChannelConfig GetChannelConfig(const std::string& device_id, + EDataFlow data_flow); + // After activating an IAudioClient interface on an audio endpoint device, // the client must initialize it once, and only once, to initialize the audio // stream between the client and the device. In shared mode, the client 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 abef8682020..e9ed0c4f597 100644 --- a/chromium/media/audio/win/core_audio_util_win_unittest.cc +++ b/chromium/media/audio/win/core_audio_util_win_unittest.cc @@ -274,7 +274,7 @@ TEST_F(CoreAudioUtilWinTest, IsChannelLayoutSupported) { EXPECT_TRUE(SUCCEEDED(hr)); EXPECT_TRUE(mix_params.IsValid()); EXPECT_TRUE(CoreAudioUtil::IsChannelLayoutSupported( - eRender, eConsole, mix_params.channel_layout())); + std::string(), eRender, eConsole, mix_params.channel_layout())); // Check if it is possible to modify the channel layout to stereo for a // device which reports that it prefers to be openen up in an other @@ -284,7 +284,7 @@ TEST_F(CoreAudioUtilWinTest, IsChannelLayoutSupported) { // TODO(henrika): it might be too pessimistic to assume false as return // value here. EXPECT_FALSE(CoreAudioUtil::IsChannelLayoutSupported( - eRender, eConsole, channel_layout)); + std::string(), eRender, eConsole, channel_layout)); } } diff --git a/chromium/media/audio/win/wavein_input_win.cc b/chromium/media/audio/win/wavein_input_win.cc index 3c4147738df..05771250e01 100644 --- a/chromium/media/audio/win/wavein_input_win.cc +++ b/chromium/media/audio/win/wavein_input_win.cc @@ -8,7 +8,6 @@ #include "base/logging.h" #include "media/audio/audio_io.h" -#include "media/audio/audio_util.h" #include "media/audio/win/audio_manager_win.h" #include "media/audio/win/device_enumeration_win.h" diff --git a/chromium/media/audio/win/waveout_output_win.cc b/chromium/media/audio/win/waveout_output_win.cc index 47d4fa65053..0f54817b14a 100644 --- a/chromium/media/audio/win/waveout_output_win.cc +++ b/chromium/media/audio/win/waveout_output_win.cc @@ -248,39 +248,25 @@ void PCMWaveOutAudioOutputStream::Stop() { state_ = PCMA_STOPPING; base::subtle::MemoryBarrier(); - // Stop watching for buffer event, wait till all the callbacks are complete. - // Should be done before ::waveOutReset() call to avoid race condition when - // callback that is currently active and already checked that stream is still - // being played calls ::waveOutWrite() after ::waveOutReset() returns, later - // causing ::waveOutClose() to fail with WAVERR_STILLPLAYING. - // TODO(enal): that delays actual stopping of playback. Alternative can be - // to call ::waveOutReset() twice, once before - // ::UnregisterWaitEx() and once after. + // Stop watching for buffer event, waits until outstanding callbacks finish. if (waiting_handle_) { - if (!::UnregisterWaitEx(waiting_handle_, INVALID_HANDLE_VALUE)) { - state_ = PCMA_PLAYING; - HandleError(MMSYSERR_ERROR); - return; - } + if (!::UnregisterWaitEx(waiting_handle_, INVALID_HANDLE_VALUE)) + HandleError(::GetLastError()); waiting_handle_ = NULL; } // Stop playback. MMRESULT res = ::waveOutReset(waveout_); - if (res != MMSYSERR_NOERROR) { - state_ = PCMA_PLAYING; + if (res != MMSYSERR_NOERROR) HandleError(res); - return; - } // Wait for lock to ensure all outstanding callbacks have completed. base::AutoLock auto_lock(lock_); // waveOutReset() leaves buffers in the unpredictable state, causing // problems if we want to close, release, or reuse them. Fix the states. - for (int ix = 0; ix != num_buffers_; ++ix) { + for (int ix = 0; ix != num_buffers_; ++ix) GetBuffer(ix)->dwFlags = WHDR_PREPARED; - } // Don't use callback after Stop(). callback_ = NULL; |