diff options
Diffstat (limited to 'chromium/media/audio')
87 files changed, 1149 insertions, 1067 deletions
diff --git a/chromium/media/audio/BUILD.gn b/chromium/media/audio/BUILD.gn index bc999b57ef4..7ee82dfab0a 100644 --- a/chromium/media/audio/BUILD.gn +++ b/chromium/media/audio/BUILD.gn @@ -20,7 +20,6 @@ if (!link_pulseaudio) { ] stubs_filename_root = "pulse_stubs" - # TODO(ajwong): these need to be included in the pulse build. outputs = [ "$target_gen_dir/pulse/$stubs_filename_root.cc", "$target_gen_dir/pulse/$stubs_filename_root.h", @@ -51,7 +50,19 @@ config("platform_config") { } source_set("audio") { - visibility = [ "//media/*" ] + # Do not expand the visibility here without double-checking with OWNERS, this + # is a roll-up target which is part of the //media component. Most other DEPs + # should be using //media and not directly DEP this roll-up target. + visibility = [ + "//media", + + # TODO(dalecurtis): CoreAudioUtil::IsCoreAudioSupported() should probably + # move into media/base/win. + "//media/device_monitors", + + # TODO(dalecurtis): Move android audio pieces into //media/audio. + "//media/base/android", + ] sources = [ "agc_audio_stream.h", "audio_debug_file_writer.cc", @@ -117,12 +128,8 @@ source_set("audio") { "fake_audio_manager.h", "fake_audio_output_stream.cc", "fake_audio_output_stream.h", - "fake_audio_worker.cc", - "fake_audio_worker.h", "null_audio_sink.cc", "null_audio_sink.h", - "sample_rates.cc", - "sample_rates.h", "scoped_task_runner_observer.cc", "scoped_task_runner_observer.h", "simple_sources.cc", @@ -142,15 +149,13 @@ source_set("audio") { ] deps = [ "//base", - "//media:shared_memory_support", "//media/base", "//url", ] libs = [] configs += [ ":platform_config", - "//media:media_config", - "//media:media_implementation", + "//media:subcomponent_config", ] if (is_mac) { @@ -165,6 +170,8 @@ source_set("audio") { "mac/audio_low_latency_input_mac.h", "mac/audio_manager_mac.cc", "mac/audio_manager_mac.h", + "mac/coreaudio_dispatch_override.cc", + "mac/coreaudio_dispatch_override.h", "mac/scoped_audio_unit.cc", "mac/scoped_audio_unit.h", ] @@ -209,6 +216,8 @@ source_set("audio") { "android/audio_manager_android.h", "android/audio_record_input.cc", "android/audio_record_input.h", + "android/audio_track_output_stream.cc", + "android/audio_track_output_stream.h", "android/muteable_audio_output_stream.h", "android/opensles_input.cc", "android/opensles_input.h", @@ -218,6 +227,7 @@ source_set("audio") { "android/opensles_util.h", "android/opensles_wrapper.cc", ] + deps += [ "//media/base/android:media_jni_headers" ] } @@ -269,7 +279,6 @@ source_set("audio") { if (link_pulseaudio) { configs += [ ":libpulse" ] } else { - # TODO(ajwong): Technically, this dl should go in the action. libs += [ "dl" ] deps += [ ":pulse_generate_stubs" ] sources += get_target_outputs(":pulse_generate_stubs") @@ -298,7 +307,10 @@ if (use_pulseaudio && link_pulseaudio) { } } +# Note: This is a roll-up only target; do not expand the visibility. DEPS should +# depend on the //media:test_support target instead. static_library("test_support") { + visibility = [ "//media:test_support" ] testonly = true sources = [ "audio_device_info_accessor_for_tests.cc", @@ -320,7 +332,11 @@ static_library("test_support") { ] deps = [ "//base", - "//media", + + # Do not add any other //media deps except this; it will automatically pull + # a dep on //media which is required to ensure test_support targets all use + # the same //media component and not build a target's sources individually. + "//media/base:test_support", "//testing/gmock", "//testing/gtest", ] @@ -341,17 +357,15 @@ source_set("unit_tests") { "audio_output_proxy_unittest.cc", "audio_power_monitor_unittest.cc", "audio_system_impl_unittest.cc", - "fake_audio_worker_unittest.cc", "simple_sources_unittest.cc", "virtual_audio_input_stream_unittest.cc", "virtual_audio_output_stream_unittest.cc", ] + deps = [ - ":test_support", "//base", "//base/test:test_support", - "//media", - "//media/base:test_support", + "//media:test_support", "//testing/gmock", "//testing/gtest", "//url", diff --git a/chromium/media/audio/OWNERS b/chromium/media/audio/OWNERS index 6485207e45d..9bb055240f5 100644 --- a/chromium/media/audio/OWNERS +++ b/chromium/media/audio/OWNERS @@ -1,4 +1,6 @@ tommi@chromium.org +olka@chromium.org +maxmorin@chromium.org # Windows henrika@chromium.org diff --git a/chromium/media/audio/alsa/alsa_input.cc b/chromium/media/audio/alsa/alsa_input.cc index be414868ad9..6d756c8ec24 100644 --- a/chromium/media/audio/alsa/alsa_input.cc +++ b/chromium/media/audio/alsa/alsa_input.cc @@ -160,6 +160,8 @@ bool AlsaPcmInputStream::Recover(int original_error) { snd_pcm_sframes_t AlsaPcmInputStream::GetCurrentDelay() { snd_pcm_sframes_t delay = -1; + // TODO(dalecurtis): This should probably use snd_pcm_htimestamp() so that we + // can have |capture_time| directly instead of computing it as Now() - delay. int error = wrapper_->PcmDelay(device_handle_, &delay); if (error < 0) Recover(error); @@ -199,25 +201,26 @@ void AlsaPcmInputStream::ReadAudio() { return; } - int num_buffers = frames / params_.frames_per_buffer(); - uint32_t hardware_delay_bytes = - static_cast<uint32_t>(GetCurrentDelay() * params_.GetBytesPerFrame()); - double normalized_volume = 0.0; - // Update the AGC volume level once every second. Note that, |volume| is // also updated each time SetVolume() is called through IPC by the // render-side AGC. + double normalized_volume = 0.0; GetAgcVolume(&normalized_volume); + int num_buffers = frames / params_.frames_per_buffer(); while (num_buffers--) { int frames_read = wrapper_->PcmReadi(device_handle_, audio_buffer_.get(), params_.frames_per_buffer()); if (frames_read == params_.frames_per_buffer()) { - audio_bus_->FromInterleaved(audio_buffer_.get(), - audio_bus_->frames(), + audio_bus_->FromInterleaved(audio_buffer_.get(), audio_bus_->frames(), params_.bits_per_sample() / 8); - callback_->OnData( - this, audio_bus_.get(), hardware_delay_bytes, normalized_volume); + + base::TimeDelta hardware_delay = base::TimeDelta::FromSecondsD( + GetCurrentDelay() / static_cast<double>(params_.sample_rate())); + + callback_->OnData(this, audio_bus_.get(), + base::TimeTicks::Now() - hardware_delay, + normalized_volume); } else { LOG(WARNING) << "PcmReadi returning less than expected frames: " << frames_read << " vs. " << params_.frames_per_buffer() diff --git a/chromium/media/audio/alsa/audio_manager_alsa.cc b/chromium/media/audio/alsa/audio_manager_alsa.cc index 33fc0b2ddb9..5f5bc6d7e64 100644 --- a/chromium/media/audio/alsa/audio_manager_alsa.cc +++ b/chromium/media/audio/alsa/audio_manager_alsa.cc @@ -7,14 +7,10 @@ #include <stddef.h> #include "base/command_line.h" -#include "base/environment.h" -#include "base/files/file_path.h" #include "base/logging.h" #include "base/macros.h" #include "base/memory/free_deleter.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_device_description.h" #include "media/audio/audio_output_dispatcher.h" @@ -44,40 +40,10 @@ static const int kDefaultSampleRate = 48000; // 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 const char* const kInvalidAudioInputDevices[] = { + "default", "dmix", "null", "pulse", "surround", }; -// static -void AudioManagerAlsa::ShowLinuxAudioInputSettings() { - std::unique_ptr<base::Environment> env(base::Environment::Create()); - base::CommandLine command_line(base::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: - case base::nix::DESKTOP_ENVIRONMENT_KDE5: - 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()); -} - AudioManagerAlsa::AudioManagerAlsa(std::unique_ptr<AudioThread> audio_thread, AudioLogFactory* audio_log_factory) : AudioManagerBase(std::move(audio_thread), audio_log_factory), @@ -95,10 +61,6 @@ bool AudioManagerAlsa::HasAudioInputDevices() { return HasAnyAlsaAudioDevice(kStreamCapture); } -void AudioManagerAlsa::ShowAudioInputSettings() { - ShowLinuxAudioInputSettings(); -} - void AudioManagerAlsa::GetAudioInputDeviceNames( AudioDeviceNames* device_names) { DCHECK(device_names->empty()); @@ -219,17 +181,16 @@ bool AudioManagerAlsa::IsAlsaDeviceAvailable( 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; } + + 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 diff --git a/chromium/media/audio/alsa/audio_manager_alsa.h b/chromium/media/audio/alsa/audio_manager_alsa.h index a5c57dcfc3b..ab146785c55 100644 --- a/chromium/media/audio/alsa/audio_manager_alsa.h +++ b/chromium/media/audio/alsa/audio_manager_alsa.h @@ -24,12 +24,9 @@ class MEDIA_EXPORT AudioManagerAlsa : public AudioManagerBase { AudioLogFactory* audio_log_factory); ~AudioManagerAlsa() override; - static void ShowLinuxAudioInputSettings(); - // Implementation of AudioManager. bool HasAudioOutputDevices() override; bool HasAudioInputDevices() override; - void ShowAudioInputSettings() override; void GetAudioInputDeviceNames(AudioDeviceNames* device_names) override; void GetAudioOutputDeviceNames(AudioDeviceNames* device_names) override; AudioParameters GetInputStreamParameters( diff --git a/chromium/media/audio/android/audio_android_unittest.cc b/chromium/media/audio/android/audio_android_unittest.cc index cae9490c289..7005dc82498 100644 --- a/chromium/media/audio/android/audio_android_unittest.cc +++ b/chromium/media/audio/android/audio_android_unittest.cc @@ -163,7 +163,7 @@ class MockAudioInputCallback : public AudioInputStream::AudioInputCallback { MOCK_METHOD4(OnData, void(AudioInputStream* stream, const AudioBus* src, - uint32_t hardware_delay_bytes, + base::TimeTicks capture_time, double volume)); MOCK_METHOD1(OnError, void(AudioInputStream* stream)); }; @@ -277,7 +277,7 @@ class FileAudioSink : public AudioInputStream::AudioInputCallback { // AudioInputStream::AudioInputCallback implementation. void OnData(AudioInputStream* stream, const AudioBus* src, - uint32_t hardware_delay_bytes, + base::TimeTicks capture_time, double volume) override { const int num_samples = src->frames() * src->channels(); std::unique_ptr<int16_t> interleaved(new int16_t[num_samples]); @@ -325,7 +325,7 @@ class FullDuplexAudioSinkSource // AudioInputStream::AudioInputCallback implementation void OnData(AudioInputStream* stream, const AudioBus* src, - uint32_t hardware_delay_bytes, + base::TimeTicks capture_time, double volume) override { const base::TimeTicks now_time = base::TimeTicks::Now(); const int diff = (now_time - previous_time_).InMilliseconds(); diff --git a/chromium/media/audio/android/audio_manager_android.cc b/chromium/media/audio/android/audio_manager_android.cc index 17567885331..61abb0cc283 100644 --- a/chromium/media/audio/android/audio_manager_android.cc +++ b/chromium/media/audio/android/audio_manager_android.cc @@ -15,6 +15,7 @@ #include "base/strings/string_number_conversions.h" #include "jni/AudioManagerAndroid_jni.h" #include "media/audio/android/audio_record_input.h" +#include "media/audio/android/audio_track_output_stream.h" #include "media/audio/android/opensles_input.h" #include "media/audio/android/opensles_output.h" #include "media/audio/audio_device_description.h" @@ -28,6 +29,7 @@ using base::android::AttachCurrentThread; using base::android::ConvertJavaStringToUTF8; using base::android::ConvertUTF8ToJavaString; using base::android::JavaParamRef; +using base::android::JavaRef; using base::android::ScopedJavaLocalRef; namespace media { @@ -239,9 +241,8 @@ AudioOutputStream* AudioManagerAndroid::MakeBitstreamOutputStream( const AudioParameters& params, const std::string& device_id, const LogCallback& log_callback) { - // TODO(tsunghung): add output stream for audio bitstream formats. - NOTREACHED(); - return nullptr; + DCHECK(params.IsBitstreamFormat()); + return new AudioTrackOutputStream(this, params); } AudioInputStream* AudioManagerAndroid::MakeLinearInputStream( @@ -284,8 +285,9 @@ AudioInputStream* AudioManagerAndroid::MakeLowLatencyInputStream( } // static -bool AudioManagerAndroid::RegisterAudioManager(JNIEnv* env) { - return RegisterNativesImpl(env); +bool AudioManagerAndroid::SupportsPerformanceModeForOutput() { + return base::android::BuildInfo::GetInstance()->sdk_int() >= + base::android::SDK_VERSION_NOUGAT_MR1; } void AudioManagerAndroid::SetMute(JNIEnv* env, @@ -339,8 +341,16 @@ AudioParameters AudioManagerAndroid::GetPreferredOutputStreamParameters( channel_layout = input_params.channel_layout(); } - buffer_size = GetOptimalOutputFrameSize( - sample_rate, ChannelLayoutToChannelCount(channel_layout)); + // For high latency playback on supported platforms, pass through the + // requested buffer size; this provides significant power savings (~25%) and + // reduces the potential for glitches under load. + if (SupportsPerformanceModeForOutput() && + input_params.latency_tag() == AudioLatency::LATENCY_PLAYBACK) { + buffer_size = input_params.frames_per_buffer(); + } else { + buffer_size = GetOptimalOutputFrameSize( + sample_rate, ChannelLayoutToChannelCount(channel_layout)); + } } int user_buffer_size = GetUserBufferSize(); @@ -355,7 +365,7 @@ bool AudioManagerAndroid::HasNoAudioInputStreams() { return input_stream_count() == 0; } -jobject AudioManagerAndroid::GetJavaAudioManager() { +const JavaRef<jobject>& AudioManagerAndroid::GetJavaAudioManager() { DCHECK(GetTaskRunner()->BelongsToCurrentThread()); if (j_audio_manager_.is_null()) { // Create the Android audio manager on the audio thread. @@ -369,7 +379,7 @@ jobject AudioManagerAndroid::GetJavaAudioManager() { Java_AudioManagerAndroid_init(base::android::AttachCurrentThread(), j_audio_manager_); } - return j_audio_manager_.obj(); + return j_audio_manager_; } void AudioManagerAndroid::SetCommunicationAudioModeOn(bool on) { diff --git a/chromium/media/audio/android/audio_manager_android.h b/chromium/media/audio/android/audio_manager_android.h index c65b6340b5d..5075ed514f1 100644 --- a/chromium/media/audio/android/audio_manager_android.h +++ b/chromium/media/audio/android/audio_manager_android.h @@ -67,7 +67,10 @@ class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase { const std::string& device_id, const LogCallback& log_callback) override; - static bool RegisterAudioManager(JNIEnv* env); + // Indicates if there's support for the OpenSLES performance mode keys. See + // OpenSLESOutputStream for specific details. Essentially this allows for low + // power audio when large buffer sizes can be used. + static bool SupportsPerformanceModeForOutput(); void SetMute(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj, @@ -85,7 +88,7 @@ class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase { const AudioParameters& input_params) override; private: - jobject GetJavaAudioManager(); + const base::android::JavaRef<jobject>& GetJavaAudioManager(); bool HasNoAudioInputStreams(); void SetCommunicationAudioModeOn(bool on); bool SetAudioDevice(const std::string& device_id); diff --git a/chromium/media/audio/android/audio_record_input.cc b/chromium/media/audio/android/audio_record_input.cc index 8e436ff328b..521676a8c04 100644 --- a/chromium/media/audio/android/audio_record_input.cc +++ b/chromium/media/audio/android/audio_record_input.cc @@ -48,23 +48,21 @@ void AudioRecordInputStream::CacheDirectBufferAddress( static_cast<uint8_t*>(env->GetDirectBufferAddress(byte_buffer)); } -// static -bool AudioRecordInputStream::RegisterAudioRecordInput(JNIEnv* env) { - return RegisterNativesImpl(env); -} - void AudioRecordInputStream::OnData(JNIEnv* env, const JavaParamRef<jobject>& obj, jint size, - jint hardware_delay_bytes) { + jint hardware_delay_ms) { DCHECK(direct_buffer_address_); DCHECK_EQ(size, audio_bus_->frames() * audio_bus_->channels() * bytes_per_sample_); // Passing zero as the volume parameter indicates there is no access to a // hardware volume slider. - audio_bus_->FromInterleaved( - direct_buffer_address_, audio_bus_->frames(), bytes_per_sample_); - callback_->OnData(this, audio_bus_.get(), hardware_delay_bytes, 0.0); + audio_bus_->FromInterleaved(direct_buffer_address_, audio_bus_->frames(), + bytes_per_sample_); + callback_->OnData(this, audio_bus_.get(), + base::TimeTicks::Now() - + base::TimeDelta::FromMilliseconds(hardware_delay_ms), + 0.0); } bool AudioRecordInputStream::Open() { diff --git a/chromium/media/audio/android/audio_record_input.h b/chromium/media/audio/android/audio_record_input.h index 1e9f0ebd087..f1cf02ee12a 100644 --- a/chromium/media/audio/android/audio_record_input.h +++ b/chromium/media/audio/android/audio_record_input.h @@ -44,13 +44,11 @@ class MEDIA_EXPORT AudioRecordInputStream : public AudioInputStream { bool GetAutomaticGainControl() override; bool IsMuted() override; - static bool RegisterAudioRecordInput(JNIEnv* env); - // Called from Java when data is available. void OnData(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj, jint size, - jint hardware_delay_bytes); + jint hardware_delay_ms); // Called from Java so that we can cache the address of the Java-managed // |byte_buffer| in |direct_buffer_address_|. diff --git a/chromium/media/audio/android/audio_track_output_stream.cc b/chromium/media/audio/android/audio_track_output_stream.cc new file mode 100644 index 00000000000..07f5cd5153b --- /dev/null +++ b/chromium/media/audio/android/audio_track_output_stream.cc @@ -0,0 +1,181 @@ +// Copyright 2017 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_track_output_stream.h" + +#include <cmath> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/logging.h" +#include "base/single_thread_task_runner.h" +#include "base/time/default_tick_clock.h" +#include "jni/AudioTrackOutputStream_jni.h" +#include "media/audio/audio_manager_base.h" +#include "media/base/audio_sample_types.h" +#include "media/base/audio_timestamp_helper.h" + +using base::android::AttachCurrentThread; +using base::android::ScopedJavaLocalRef; + +namespace media { + +// Android audio format. For more information, please see: +// https://developer.android.com/reference/android/media/AudioFormat.html +enum { + kEncodingPcm16bit = 2, // ENCODING_PCM_16BIT + kEncodingAc3 = 5, // ENCODING_AC3 + kEncodingEac3 = 6, // ENCODING_E_AC3 +}; + +AudioTrackOutputStream::AudioTrackOutputStream(AudioManagerBase* manager, + const AudioParameters& params) + : params_(params), + audio_manager_(manager), + tick_clock_(new base::DefaultTickClock()) { + if (!params_.IsBitstreamFormat()) { + audio_bus_ = AudioBus::Create(params_); + } +} + +AudioTrackOutputStream::~AudioTrackOutputStream() { + DCHECK(!callback_); +} + +bool AudioTrackOutputStream::Open() { + DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); + JNIEnv* env = AttachCurrentThread(); + j_audio_output_stream_.Reset(Java_AudioTrackOutputStream_create(env)); + + int format = kEncodingPcm16bit; + if (params_.IsBitstreamFormat()) { + if (params_.format() == AudioParameters::AUDIO_BITSTREAM_AC3) { + format = kEncodingAc3; + } else if (params_.format() == AudioParameters::AUDIO_BITSTREAM_EAC3) { + format = kEncodingEac3; + } else { + NOTREACHED(); + } + } + + return Java_AudioTrackOutputStream_open(env, j_audio_output_stream_, + params_.channels(), + params_.sample_rate(), format); +} + +void AudioTrackOutputStream::Start(AudioSourceCallback* callback) { + DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); + callback_ = callback; + Java_AudioTrackOutputStream_start(AttachCurrentThread(), + j_audio_output_stream_, + reinterpret_cast<intptr_t>(this)); +} + +void AudioTrackOutputStream::Stop() { + DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); + Java_AudioTrackOutputStream_stop(AttachCurrentThread(), + j_audio_output_stream_); + callback_ = nullptr; +} + +void AudioTrackOutputStream::Close() { + DCHECK(!callback_); + DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); + + Java_AudioTrackOutputStream_close(AttachCurrentThread(), + j_audio_output_stream_); + audio_manager_->ReleaseOutputStream(this); +} + +void AudioTrackOutputStream::SetMute(bool muted) { + if (params_.IsBitstreamFormat() && muted) { + LOG(WARNING) + << "Mute is not supported for compressed audio bitstream formats."; + return; + } + + if (muted_ == muted) + return; + + muted_ = muted; + Java_AudioTrackOutputStream_setVolume( + AttachCurrentThread(), j_audio_output_stream_, muted_ ? 0.0 : volume_); +} + +void AudioTrackOutputStream::SetVolume(double volume) { + if (params_.IsBitstreamFormat()) { + LOG(WARNING) << "Volume change is not supported for compressed audio " + "bitstream formats."; + return; + } + + // Track |volume_| since AudioTrack uses a scaled value. + volume_ = volume; + if (muted_) + return; + + Java_AudioTrackOutputStream_setVolume(AttachCurrentThread(), + j_audio_output_stream_, volume); +}; + +void AudioTrackOutputStream::GetVolume(double* volume) { + *volume = volume_; +}; + +// AudioOutputStream::SourceCallback implementation methods called from Java. +ScopedJavaLocalRef<jobject> AudioTrackOutputStream::OnMoreData( + JNIEnv* env, + jobject obj, + jobject audio_data, + jlong delay_in_frame) { + DCHECK(callback_); + + base::TimeDelta delay = + AudioTimestampHelper::FramesToTime(delay_in_frame, params_.sample_rate()); + + void* native_buffer = env->GetDirectBufferAddress(audio_data); + + if (params_.IsBitstreamFormat()) { + // For bitstream formats, use the direct buffer memory to avoid additional + // memory copy. + std::unique_ptr<AudioBus> audio_bus( + AudioBus::WrapMemory(params_, native_buffer)); + audio_bus->set_is_bitstream_format(true); + + callback_->OnMoreData(delay, tick_clock_->NowTicks(), 0, audio_bus.get()); + + if (audio_bus->GetBitstreamDataSize() <= 0) + return nullptr; + + return Java_AudioTrackOutputStream_createAudioBufferInfo( + env, j_audio_output_stream_, audio_bus->GetBitstreamFrames(), + audio_bus->GetBitstreamDataSize()); + } + + // For PCM format, we need extra memory to convert planar float32 into + // interleaved int16. + + callback_->OnMoreData(delay, tick_clock_->NowTicks(), 0, audio_bus_.get()); + + int16_t* native_bus = reinterpret_cast<int16_t*>(native_buffer); + audio_bus_->ToInterleaved<SignedInt16SampleTypeTraits>(audio_bus_->frames(), + native_bus); + + return Java_AudioTrackOutputStream_createAudioBufferInfo( + env, j_audio_output_stream_, audio_bus_->frames(), + sizeof(*native_bus) * audio_bus_->channels() * audio_bus_->frames()); +} + +void AudioTrackOutputStream::OnError(JNIEnv* env, jobject obj) { + DCHECK(callback_); + callback_->OnError(); +} + +jlong AudioTrackOutputStream::GetAddress(JNIEnv* env, + jobject obj, + jobject byte_buffer) { + return reinterpret_cast<jlong>(env->GetDirectBufferAddress(byte_buffer)); +} + +} // namespace media diff --git a/chromium/media/audio/android/audio_track_output_stream.h b/chromium/media/audio/android/audio_track_output_stream.h new file mode 100644 index 00000000000..1b01a687d46 --- /dev/null +++ b/chromium/media/audio/android/audio_track_output_stream.h @@ -0,0 +1,67 @@ +// Copyright 2017 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_TRACK_OUTPUT_STREAM_H_ +#define MEDIA_AUDIO_ANDROID_AUDIO_TRACK_OUTPUT_STREAM_H_ + +#include <memory> + +#include "base/android/jni_android.h" +#include "base/time/tick_clock.h" +#include "media/audio/android/muteable_audio_output_stream.h" +#include "media/base/audio_parameters.h" + +namespace media { + +class AudioManagerBase; + +// A MuteableAudioOutputStream implementation based on the Android AudioTrack +// API. +class MEDIA_EXPORT AudioTrackOutputStream : public MuteableAudioOutputStream { + public: + AudioTrackOutputStream(AudioManagerBase* manager, + const AudioParameters& params); + ~AudioTrackOutputStream() override; + + // AudioOutputStream implementation. + bool Open() override; + void Start(AudioSourceCallback* callback) override; + void Stop() override; + void SetVolume(double volume) override; + void GetVolume(double* volume) override; + void Close() override; + + // MuteableAudioOutputStream implementation. + void SetMute(bool muted) override; + + // AudioOutputStream::SourceCallback implementation methods called from Java. + base::android::ScopedJavaLocalRef<jobject> OnMoreData(JNIEnv* env, + jobject obj, + jobject audio_data, + jlong delay); + void OnError(JNIEnv* env, jobject obj); + jlong GetAddress(JNIEnv* env, jobject obj, jobject byte_buffer); + + private: + const AudioParameters params_; + + AudioManagerBase* audio_manager_; + AudioSourceCallback* callback_ = nullptr; + bool muted_ = false; + double volume_ = 1.0; + + // Extra buffer for PCM format. + std::unique_ptr<AudioBus> audio_bus_; + + std::unique_ptr<base::TickClock> tick_clock_; + + // Java AudioTrackOutputStream instance. + base::android::ScopedJavaGlobalRef<jobject> j_audio_output_stream_; + + DISALLOW_COPY_AND_ASSIGN(AudioTrackOutputStream); +}; + +} // namespace media + +#endif // MEDIA_AUDIO_ANDROID_AUDIO_TRACK_OUTPUT_STREAM_H_ diff --git a/chromium/media/audio/android/opensles_input.cc b/chromium/media/audio/android/opensles_input.cc index 748a9cca802..bccda08be62 100644 --- a/chromium/media/audio/android/opensles_input.cc +++ b/chromium/media/audio/android/opensles_input.cc @@ -42,6 +42,8 @@ OpenSLESInputStream::OpenSLESInputStream(AudioManagerAndroid* audio_manager, format_.channelMask = ChannelCountToSLESChannelMask(params.channels()); buffer_size_bytes_ = params.GetBytesPerBuffer(); + hardware_delay_ = base::TimeDelta::FromSecondsD( + params.frames_per_buffer() / static_cast<double>(params.sample_rate())); memset(&audio_data_, 0, sizeof(audio_data_)); } @@ -306,7 +308,8 @@ void OpenSLESInputStream::ReadBufferQueue() { // TODO(henrika): Investigate if it is possible to get an accurate // delay estimation. - callback_->OnData(this, audio_bus_.get(), buffer_size_bytes_, 0.0); + callback_->OnData(this, audio_bus_.get(), + base::TimeTicks::Now() - hardware_delay_, 0.0); // Done with this buffer. Send it to device for recording. SLresult err = diff --git a/chromium/media/audio/android/opensles_input.h b/chromium/media/audio/android/opensles_input.h index 7a99cd79021..f7385dea8de 100644 --- a/chromium/media/audio/android/opensles_input.h +++ b/chromium/media/audio/android/opensles_input.h @@ -100,6 +100,8 @@ class OpenSLESInputStream : public AudioInputStream { bool started_; + base::TimeDelta hardware_delay_; + std::unique_ptr<media::AudioBus> audio_bus_; DISALLOW_COPY_AND_ASSIGN(OpenSLESInputStream); diff --git a/chromium/media/audio/android/opensles_output.cc b/chromium/media/audio/android/opensles_output.cc index 5893de36904..81a02dbdbf8 100644 --- a/chromium/media/audio/android/opensles_output.cc +++ b/chromium/media/audio/android/opensles_output.cc @@ -23,6 +23,28 @@ } \ } while (0) +// On N MR1+ we want to use high buffer sizes for power saving. Per Android +// audio team, this should be in N MR1+ SDK, but it's not, so use a defined() +// check instead of __API_LEVEL__ check. +#if !defined(SL_ANDROID_KEY_PERFORMANCE_MODE) +#define SL_ANDROID_KEY_PERFORMANCE_MODE \ + ((const SLchar*)"androidPerformanceMode") + +// No specific performance requirement. Allows HW and SW pre/post processing. +#define SL_ANDROID_PERFORMANCE_NONE ((SLuint32)0x00000000) + +// Priority given to latency. No HW or software pre/post processing. This is the +// default if no performance mode is specified. +#define SL_ANDROID_PERFORMANCE_LATENCY ((SLuint32)0x00000001) + +// Priority given to latency while still allowing HW pre and post processing. +#define SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS ((SLuint32)0x00000002) + +// Priority given to power saving if latency is not a concern. Allows HW and SW +// pre/post processing. +#define SL_ANDROID_PERFORMANCE_POWER_SAVING ((SLuint32)0x00000003) +#endif + namespace media { OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager, @@ -52,10 +74,18 @@ OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager, buffer_size_bytes_(have_float_output_ ? bytes_per_frame_ * params.frames_per_buffer() : params.GetBytesPerBuffer()), + performance_mode_(SL_ANDROID_PERFORMANCE_NONE), delay_calculator_(samples_per_second_) { DVLOG(2) << "OpenSLESOutputStream::OpenSLESOutputStream(" << "stream_type=" << stream_type << ")"; + if (AudioManagerAndroid::SupportsPerformanceModeForOutput()) { + if (params.latency_tag() == AudioLatency::LATENCY_PLAYBACK) + performance_mode_ = SL_ANDROID_PERFORMANCE_POWER_SAVING; + else if (params.latency_tag() == AudioLatency::LATENCY_RTC) + performance_mode_ = SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS; + } + audio_bus_ = AudioBus::Create(params); if (have_float_output_) { @@ -307,12 +337,20 @@ bool OpenSLESOutputStream::CreatePlayer() { // Set configuration using the stream type provided at construction. LOG_ON_FAILURE_AND_RETURN( - (*player_config)->SetConfiguration(player_config, - SL_ANDROID_KEY_STREAM_TYPE, - &stream_type_, - sizeof(SLint32)), + (*player_config) + ->SetConfiguration(player_config, SL_ANDROID_KEY_STREAM_TYPE, + &stream_type_, sizeof(SLint32)), false); + // Set configuration using the stream type provided at construction. + if (performance_mode_ > SL_ANDROID_PERFORMANCE_NONE) { + LOG_ON_FAILURE_AND_RETURN( + (*player_config) + ->SetConfiguration(player_config, SL_ANDROID_KEY_PERFORMANCE_MODE, + &performance_mode_, sizeof(SLuint32)), + false); + } + // Realize the player object in synchronous mode. LOG_ON_FAILURE_AND_RETURN( player_object_->Realize(player_object_.Get(), SL_BOOLEAN_FALSE), false); diff --git a/chromium/media/audio/android/opensles_output.h b/chromium/media/audio/android/opensles_output.h index 17a91aaa524..db9b75e7fa5 100644 --- a/chromium/media/audio/android/opensles_output.h +++ b/chromium/media/audio/android/opensles_output.h @@ -147,6 +147,10 @@ class OpenSLESOutputStream : public MuteableAudioOutputStream { int bytes_per_frame_; size_t buffer_size_bytes_; + // On API level 25+ we can provide hints to OpenSLES about what type of + // content the stream is being used for. + SLuint32 performance_mode_; + // Used to calculate the delay value for each OnMoreData() call. AudioTimestampHelper delay_calculator_; diff --git a/chromium/media/audio/audio_debug_recording_helper.h b/chromium/media/audio/audio_debug_recording_helper.h index b0f4c127d23..362b07f8ce0 100644 --- a/chromium/media/audio/audio_debug_recording_helper.h +++ b/chromium/media/audio/audio_debug_recording_helper.h @@ -47,8 +47,7 @@ class AudioDebugRecorder { // soundcard thread -> control thread -> file thread, // and with the merge we should be able to do // soundcard thread -> file thread. -class MEDIA_EXPORT AudioDebugRecordingHelper - : public NON_EXPORTED_BASE(AudioDebugRecorder) { +class MEDIA_EXPORT AudioDebugRecordingHelper : public AudioDebugRecorder { public: AudioDebugRecordingHelper( const AudioParameters& params, diff --git a/chromium/media/audio/audio_input_controller.cc b/chromium/media/audio/audio_input_controller.cc index 5ba8ebfcd7c..13564802479 100644 --- a/chromium/media/audio/audio_input_controller.cc +++ b/chromium/media/audio/audio_input_controller.cc @@ -117,13 +117,13 @@ class AudioInputController::AudioCallback private: void OnData(AudioInputStream* stream, const AudioBus* source, - uint32_t hardware_delay_bytes, + base::TimeTicks capture_time, double volume) override { TRACE_EVENT0("audio", "AC::OnData"); received_callback_ = true; - DeliverDataToSyncWriter(source, hardware_delay_bytes, volume); + DeliverDataToSyncWriter(source, capture_time, volume); #if BUILDFLAG(ENABLE_WEBRTC) controller_->debug_recording_helper_.OnData(source); @@ -138,11 +138,10 @@ class AudioInputController::AudioCallback } void DeliverDataToSyncWriter(const AudioBus* source, - uint32_t hardware_delay_bytes, + base::TimeTicks capture_time, double volume) { bool key_pressed = controller_->CheckForKeyboardInput(); - controller_->sync_writer_->Write(source, volume, key_pressed, - hardware_delay_bytes); + controller_->sync_writer_->Write(source, volume, key_pressed, capture_time); // The way the two classes interact here, could be done in a nicer way. // As is, we call the AIC here to check the audio power, return and then diff --git a/chromium/media/audio/audio_input_controller.h b/chromium/media/audio/audio_input_controller.h index 2b8669c2755..c6be0fdb215 100644 --- a/chromium/media/audio/audio_input_controller.h +++ b/chromium/media/audio/audio_input_controller.h @@ -130,7 +130,7 @@ class MEDIA_EXPORT AudioInputController virtual void Write(const AudioBus* data, double volume, bool key_pressed, - uint32_t hardware_delay_bytes) = 0; + base::TimeTicks capture_time) = 0; // Close this synchronous writer. virtual void Close() = 0; diff --git a/chromium/media/audio/audio_input_controller_unittest.cc b/chromium/media/audio/audio_input_controller_unittest.cc index 0b8ef9219e6..6aa45e09764 100644 --- a/chromium/media/audio/audio_input_controller_unittest.cc +++ b/chromium/media/audio/audio_input_controller_unittest.cc @@ -82,7 +82,7 @@ class MockSyncWriter : public AudioInputController::SyncWriter { void(const AudioBus* data, double volume, bool key_pressed, - uint32_t hardware_delay_bytes)); + base::TimeTicks capture_time)); MOCK_METHOD0(Close, void()); }; diff --git a/chromium/media/audio/audio_input_device.cc b/chromium/media/audio/audio_input_device.cc index 63672c1939d..37365a42eaf 100644 --- a/chromium/media/audio/audio_input_device.cc +++ b/chromium/media/audio/audio_input_device.cc @@ -438,9 +438,14 @@ void AudioInputDevice::AudioThreadCallback::Process(uint32_t pending_data) { // Deliver captured data to the client in floating point format and update // the audio delay measurement. + // TODO(olka, tommi): Take advantage of |capture_time| in the renderer. + const base::TimeTicks capture_time = + base::TimeTicks() + + base::TimeDelta::FromMicroseconds(buffer->params.capture_time); + DCHECK_GE(base::TimeTicks::Now(), capture_time); + capture_callback_->Capture( - audio_bus, - buffer->params.hardware_delay_bytes / bytes_per_ms_, // Delay in ms + audio_bus, (base::TimeTicks::Now() - capture_time).InMilliseconds(), buffer->params.volume, buffer->params.key_pressed); if (++current_segment_id_ >= total_segments_) diff --git a/chromium/media/audio/audio_input_device.h b/chromium/media/audio/audio_input_device.h index d6562569db0..baee6632b31 100644 --- a/chromium/media/audio/audio_input_device.h +++ b/chromium/media/audio/audio_input_device.h @@ -76,10 +76,9 @@ namespace media { // TODO(henrika): Add support for event handling (e.g. OnStateChanged, // OnCaptureStopped etc.) and ensure that we can deliver these notifications // to any clients using this class. -class MEDIA_EXPORT AudioInputDevice - : NON_EXPORTED_BASE(public AudioCapturerSource), - NON_EXPORTED_BASE(public AudioInputIPCDelegate), - NON_EXPORTED_BASE(public ScopedTaskRunnerObserver) { +class MEDIA_EXPORT AudioInputDevice : public AudioCapturerSource, + public AudioInputIPCDelegate, + public ScopedTaskRunnerObserver { public: // NOTE: Clients must call Initialize() before using. AudioInputDevice( diff --git a/chromium/media/audio/audio_input_unittest.cc b/chromium/media/audio/audio_input_unittest.cc index 609114d591d..232354e716e 100644 --- a/chromium/media/audio/audio_input_unittest.cc +++ b/chromium/media/audio/audio_input_unittest.cc @@ -27,13 +27,10 @@ namespace media { // expected and if any error has been reported. class TestInputCallback : public AudioInputStream::AudioInputCallback { public: - explicit TestInputCallback() - : callback_count_(0), - had_error_(0) { - } + TestInputCallback() : callback_count_(0), had_error_(0) {} void OnData(AudioInputStream* stream, const AudioBus* source, - uint32_t hardware_delay_bytes, + base::TimeTicks capture_time, double volume) override { ++callback_count_; } diff --git a/chromium/media/audio/audio_io.h b/chromium/media/audio/audio_io.h index d8d0e699a3d..7ac43cc3cf5 100644 --- a/chromium/media/audio/audio_io.h +++ b/chromium/media/audio/audio_io.h @@ -9,6 +9,7 @@ #include "base/time/time.h" #include "media/base/audio_bus.h" +#include "media/base/media_export.h" // Low-level audio output support. To make sound there are 3 objects involved: // - AudioSource : produces audio samples on a pull model. Implements @@ -119,9 +120,14 @@ class MEDIA_EXPORT AudioInputStream { // Called by the audio recorder when a full packet of audio data is // available. This is called from a special audio thread and the // implementation should return as soon as possible. + // + // |capture_time| is the time at which the first sample in |source| was + // received. The age of the audio data may be calculated by subtracting + // |capture_time| from base::TimeTicks::Now(). |capture_time| is always + // monotonically increasing. virtual void OnData(AudioInputStream* stream, const AudioBus* source, - uint32_t hardware_delay_bytes, + base::TimeTicks capture_time, double volume) = 0; // There was an error while recording audio. The audio sink cannot be 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 69fcf3a98ca..4b2fa7f8fa7 100644 --- a/chromium/media/audio/audio_low_latency_input_output_unittest.cc +++ b/chromium/media/audio/audio_low_latency_input_output_unittest.cc @@ -153,7 +153,7 @@ class FullDuplexAudioSinkSource // AudioInputStream::AudioInputCallback. void OnData(AudioInputStream* stream, const AudioBus* src, - uint32_t hardware_delay_bytes, + base::TimeTicks capture_time, double volume) override { base::AutoLock lock(lock_); @@ -167,7 +167,7 @@ class FullDuplexAudioSinkSource delay_states_[input_elements_to_write_].buffer_delay_ms = BytesToMilliseconds(buffer_->forward_bytes()); delay_states_[input_elements_to_write_].input_delay_ms = - BytesToMilliseconds(hardware_delay_bytes); + (base::TimeTicks::Now() - capture_time).InMilliseconds(); ++input_elements_to_write_; } diff --git a/chromium/media/audio/audio_manager.cc b/chromium/media/audio/audio_manager.cc index 54eb1bc1562..dff8b8faa59 100644 --- a/chromium/media/audio/audio_manager.cc +++ b/chromium/media/audio/audio_manager.cc @@ -22,13 +22,8 @@ #include "media/audio/fake_audio_log_factory.h" #include "media/base/media_switches.h" -#if defined(OS_MACOSX) -#include "media/audio/mac/audio_manager_mac.h" -#endif - #if defined(OS_WIN) #include "base/win/scoped_com_initializer.h" -#include "media/audio/win/core_audio_util_win.h" #endif namespace media { diff --git a/chromium/media/audio/audio_manager.h b/chromium/media/audio/audio_manager.h index f91f6bd2a59..3b484cdbbf2 100644 --- a/chromium/media/audio/audio_manager.h +++ b/chromium/media/audio/audio_manager.h @@ -88,7 +88,7 @@ class MEDIA_EXPORT AudioManager { // was created. // Returns true on success but false if AudioManager could not be shutdown. // AudioManager instance must not be deleted if shutdown failed. - bool Shutdown(); + virtual bool Shutdown(); // Log callback used for sending log messages from a stream to the object // that manages the stream. @@ -214,12 +214,6 @@ class MEDIA_EXPORT AudioManager { // input device for this computer. virtual base::string16 GetAudioInputDeviceModel() = 0; - // Opens the platform default audio input settings UI. - // Note: This could invoke an external application/preferences pane, so - // ideally must not be called from the UI thread or other time sensitive - // threads to avoid blocking the rest of the application. - virtual void ShowAudioInputSettings() = 0; - // Appends a list of available input devices to |device_descriptions|, // which must initially be empty. It is not guaranteed that all the // devices in the list support all formats and sample rates for diff --git a/chromium/media/audio/audio_manager_base.cc b/chromium/media/audio/audio_manager_base.cc index ae429862ef9..5602fb28783 100644 --- a/chromium/media/audio/audio_manager_base.cc +++ b/chromium/media/audio/audio_manager_base.cc @@ -286,6 +286,7 @@ AudioOutputStream* AudioManagerBase::MakeAudioOutputStreamProxy( // Turn off effects that weren't requested. output_params.set_effects(params.effects() & output_params.effects()); } + output_params.set_latency_tag(params.latency_tag()); } std::unique_ptr<DispatcherParams> dispatcher_params = @@ -322,9 +323,6 @@ AudioOutputStream* AudioManagerBase::MakeAudioOutputStreamProxy( return output_dispatchers_.back()->dispatcher->CreateStreamProxy(); } -void AudioManagerBase::ShowAudioInputSettings() { -} - void AudioManagerBase::GetAudioInputDeviceNames( AudioDeviceNames* device_names) { } @@ -357,21 +355,6 @@ void AudioManagerBase::ShutdownOnAudioThread() { // Close all output streams. output_dispatchers_.clear(); - -#if defined(OS_MACOSX) - // On mac, AudioManager runs on the main thread, loop for which stops - // processing task queue at this point. So even if tasks to close the - // streams are enqueued, they would not run leading to CHECKs getting hit - // in the destructor about open streams. Close them explicitly here. - // crbug.com/608049. - for (auto iter = input_streams_.begin(); iter != input_streams_.end();) { - // Note: Closing the stream will invalidate the iterator. - // Increment the iterator before closing the stream. - AudioInputStream* stream = *iter++; - stream->Close(); - } - CHECK(input_streams_.empty()); -#endif // OS_MACOSX } void AudioManagerBase::AddOutputDeviceChangeListener( diff --git a/chromium/media/audio/audio_manager_base.h b/chromium/media/audio/audio_manager_base.h index 8e2fc15b1e9..b165d06f90b 100644 --- a/chromium/media/audio/audio_manager_base.h +++ b/chromium/media/audio/audio_manager_base.h @@ -107,7 +107,6 @@ class MEDIA_EXPORT AudioManagerBase : public AudioManager { // AudioManager: void ShutdownOnAudioThread() override; base::string16 GetAudioInputDeviceModel() override; - void ShowAudioInputSettings() override; void GetAudioInputDeviceDescriptions( AudioDeviceDescriptions* device_descriptions) final; diff --git a/chromium/media/audio/audio_output_controller.cc b/chromium/media/audio/audio_output_controller.cc index 90bd2cc2c52..dd03cc93aac 100644 --- a/chromium/media/audio/audio_output_controller.cc +++ b/chromium/media/audio/audio_output_controller.cc @@ -48,11 +48,12 @@ AudioOutputController::AudioOutputController( params.sample_rate(), TimeDelta::FromMilliseconds(kPowerMeasurementTimeConstantMillis)), on_more_io_data_called_(0), - ignore_errors_during_stop_close_(false) { + weak_factory_for_errors_(this) { DCHECK(audio_manager); DCHECK(handler_); DCHECK(sync_reader_); DCHECK(message_loop_.get()); + weak_this_for_errors_ = weak_factory_for_errors_.GetWeakPtr(); } AudioOutputController::~AudioOutputController() { @@ -284,7 +285,8 @@ int AudioOutputController::OnMoreData(base::TimeDelta delay, sync_reader_->Read(dest); - const int frames = dest->frames(); + const int frames = + dest->is_bitstream_format() ? dest->GetBitstreamFrames() : dest->frames(); delay += AudioTimestampHelper::FramesToTime(frames, params_.sample_rate()); sync_reader_->RequestMoreData(delay, delay_timestamp, prior_frames_skipped); @@ -341,15 +343,14 @@ void AudioOutputController::LogAudioPowerLevel(const std::string& call_name) { } void AudioOutputController::OnError() { - { - base::AutoLock auto_lock(error_lock_); - if (ignore_errors_during_stop_close_) - return; - } - - // Handle error on the audio controller thread. - message_loop_->PostTask( - FROM_HERE, base::BindOnce(&AudioOutputController::DoReportError, this)); + // Handle error on the audio controller thread. We defer errors for one + // second in case they are the result of a device change; delay chosen to + // exceed duration of device changes which take a few hundred milliseconds. + message_loop_->PostDelayedTask( + FROM_HERE, + base::BindOnce(&AudioOutputController::DoReportError, + weak_this_for_errors_), + base::TimeDelta::FromSeconds(1)); } void AudioOutputController::DoStopCloseAndClearStream() { @@ -357,10 +358,9 @@ void AudioOutputController::DoStopCloseAndClearStream() { // Allow calling unconditionally and bail if we don't have a stream_ to close. if (stream_) { - { - base::AutoLock auto_lock(error_lock_); - ignore_errors_during_stop_close_ = true; - } + // Ensure no errors will be delivered while we cycle streams and any that + // occurred immediately prior to the device change are dropped. + weak_factory_for_errors_.InvalidateWeakPtrs(); // De-register from state change callbacks if stream_ was created via // AudioManager. @@ -374,8 +374,8 @@ void AudioOutputController::DoStopCloseAndClearStream() { diverting_to_stream_ = NULL; stream_ = NULL; - // Since the stream is no longer running, no lock is necessary. - ignore_errors_during_stop_close_ = false; + // Since the stream is stopped, we can now update |weak_this_for_errors_|. + weak_this_for_errors_ = weak_factory_for_errors_.GetWeakPtr(); } state_ = kEmpty; diff --git a/chromium/media/audio/audio_output_controller.h b/chromium/media/audio/audio_output_controller.h index 19fba1f9a68..537bf03250f 100644 --- a/chromium/media/audio/audio_output_controller.h +++ b/chromium/media/audio/audio_output_controller.h @@ -15,6 +15,7 @@ #include "base/callback.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" #include "base/threading/thread_checker.h" #include "base/timer/timer.h" #include "build/build_config.h" @@ -65,7 +66,7 @@ class MEDIA_EXPORT AudioOutputController : public base::RefCountedThreadSafe<AudioOutputController>, public AudioOutputStream::AudioSourceCallback, public AudioSourceDiverter, - NON_EXPORTED_BASE(public AudioManager::AudioDeviceListener) { + public AudioManager::AudioDeviceListener { public: // An event handler that receives events from the AudioOutputController. The // following methods are called on the audio manager thread. @@ -266,15 +267,16 @@ class MEDIA_EXPORT AudioOutputController base::AtomicRefCount on_more_io_data_called_; std::unique_ptr<base::OneShotTimer> wedge_timer_; - // Flag which indicates errors received during Stop/Close should be ignored. - // These errors are generally harmless since a fresh stream is about to be - // recreated, but if forwarded, renderer side clients may consider them - // catastrophic and abort their operations. + // WeakPtrFactory and WeakPtr for ignoring errors which occur arround a + // Stop/Close cycle; e.g., device changes. These errors are generally harmless + // since a fresh stream is about to be recreated, but if forwarded, renderer + // side clients may consider them catastrophic and abort their operations. // - // If |stream_| is started then |ignore_errors_during_stop_close_| must only - // be accessed while |error_lock_| is held. - bool ignore_errors_during_stop_close_; - base::Lock error_lock_; + // |weak_this_for_errors_| must not be reassigned while a stream is active or + // we'll have concurrent access from different threads. Only the factory may + // be used to invalidate WeakPtrs while the stream is active. + base::WeakPtr<AudioOutputController> weak_this_for_errors_; + base::WeakPtrFactory<AudioOutputController> weak_factory_for_errors_; 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 2919808af64..a2f03f3161d 100644 --- a/chromium/media/audio/audio_output_controller_unittest.cc +++ b/chromium/media/audio/audio_output_controller_unittest.cc @@ -115,6 +115,7 @@ class AudioOutputControllerTest : public testing::Test { AudioOutputControllerTest() : audio_manager_(AudioManager::CreateForTesting( base::MakeUnique<TestAudioThread>())) { + EXPECT_CALL(mock_event_handler_, OnLog(_)).Times(testing::AnyNumber()); base::RunLoop().RunUntilIdle(); } @@ -212,7 +213,7 @@ class AudioOutputControllerTest : public testing::Test { void ReadDuplicatedAudioData(const std::vector<MockAudioPushSink*>& sinks) { for (size_t i = 0; i < sinks.size(); i++) { - EXPECT_CALL(*sinks[i], OnDataCheck(kBufferNonZeroData)); + EXPECT_CALL(*sinks[i], OnDataCheck(kBufferNonZeroData)).Times(AtLeast(1)); } std::unique_ptr<AudioBus> dest = AudioBus::Create(params_); @@ -256,6 +257,13 @@ class AudioOutputControllerTest : public testing::Test { run_loop.Run(); } + void SimulateErrorThenDeviceChange() { + audio_manager_->GetTaskRunner()->PostTask( + FROM_HERE, + base::BindOnce(&AudioOutputControllerTest::TriggerErrorThenDeviceChange, + base::Unretained(this))); + } + // These help make test sequences more readable. void DivertNeverPlaying() { Divert(false, 0); } void DivertWillEventuallyBeTwicePlayed() { Divert(false, 2); } @@ -264,6 +272,18 @@ class AudioOutputControllerTest : public testing::Test { void RevertWhilePlaying() { Revert(true); } private: + void TriggerErrorThenDeviceChange() { + DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); + + // Errors should be deferred; the device change should ensure it's dropped. + EXPECT_CALL(mock_event_handler_, OnControllerError()).Times(0); + controller_->OnError(); + + EXPECT_CALL(mock_event_handler_, OnControllerPlaying()); + EXPECT_CALL(mock_event_handler_, OnControllerPaused()).Times(0); + controller_->OnDeviceChange(); + } + base::TestMessageLoop message_loop_; std::unique_ptr<AudioManager> audio_manager_; MockAudioOutputControllerEventHandler mock_event_handler_; @@ -308,6 +328,13 @@ TEST_F(AudioOutputControllerTest, PlayDeviceChangeClose) { Close(); } +TEST_F(AudioOutputControllerTest, PlayDeviceChangeError) { + Create(kSamplesPerPacket); + Play(); + SimulateErrorThenDeviceChange(); + Close(); +} + TEST_F(AudioOutputControllerTest, PlayDivertRevertClose) { Create(kSamplesPerPacket); Play(); diff --git a/chromium/media/audio/audio_output_device.cc b/chromium/media/audio/audio_output_device.cc index 1013ce6ad73..00cfe034dd1 100644 --- a/chromium/media/audio/audio_output_device.cc +++ b/chromium/media/audio/audio_output_device.cc @@ -14,7 +14,6 @@ #include "base/macros.h" #include "base/metrics/histogram_macros.h" #include "base/threading/thread_restrictions.h" -#include "base/time/time.h" #include "base/timer/timer.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" @@ -179,7 +178,9 @@ bool AudioOutputDevice::CurrentThreadIsRenderingThread() { void AudioOutputDevice::RequestDeviceAuthorizationOnIOThread() { DCHECK(task_runner()->BelongsToCurrentThread()); DCHECK_EQ(state_, IDLE); + state_ = AUTHORIZING; + auth_start_time_ = base::TimeTicks::Now(); ipc_->RequestDeviceAuthorization(this, session_id_, device_id_, security_origin_); @@ -319,6 +320,12 @@ void AudioOutputDevice::OnDeviceAuthorized( DCHECK(task_runner()->BelongsToCurrentThread()); auth_timeout_action_.reset(); + // Times over 15 s should be very rare, so we don't lose interesting data by + // making it the upper limit. + UMA_HISTOGRAM_CUSTOM_TIMES("Media.Audio.Render.OutputDeviceAuthorizationTime", + base::TimeTicks::Now() - auth_start_time_, + base::TimeDelta::FromMilliseconds(1), + base::TimeDelta::FromSeconds(15), 100); // Do nothing if late authorization is received after timeout. if (state_ == IPC_CLOSED) @@ -463,6 +470,7 @@ void AudioOutputDevice::AudioThreadCallback::MapSharedMemory() { AudioOutputBuffer* buffer = reinterpret_cast<AudioOutputBuffer*>(shared_memory_.memory()); output_bus_ = AudioBus::WrapMemory(audio_parameters_, buffer->audio); + output_bus_->set_is_bitstream_format(audio_parameters_.IsBitstreamFormat()); } // Called whenever we receive notifications about pending data. @@ -500,6 +508,11 @@ void AudioOutputDevice::AudioThreadCallback::Process(uint32_t control_signal) { // memory. render_callback_->Render(delay, delay_timestamp, frames_skipped, output_bus_.get()); + + if (audio_parameters_.IsBitstreamFormat()) { + buffer->params.bitstream_data_size = output_bus_->GetBitstreamDataSize(); + buffer->params.bitstream_frames = output_bus_->GetBitstreamFrames(); + } } bool AudioOutputDevice::AudioThreadCallback:: diff --git a/chromium/media/audio/audio_output_device.h b/chromium/media/audio/audio_output_device.h index 9a883928666..88e18431b83 100644 --- a/chromium/media/audio/audio_output_device.h +++ b/chromium/media/audio/audio_output_device.h @@ -69,6 +69,7 @@ #include "base/macros.h" #include "base/memory/shared_memory.h" #include "base/synchronization/waitable_event.h" +#include "base/time/time.h" #include "media/audio/audio_device_thread.h" #include "media/audio/audio_output_ipc.h" #include "media/audio/scoped_task_runner_observer.h" @@ -83,10 +84,9 @@ class OneShotTimer; namespace media { -class MEDIA_EXPORT AudioOutputDevice - : NON_EXPORTED_BASE(public AudioRendererSink), - NON_EXPORTED_BASE(public AudioOutputIPCDelegate), - NON_EXPORTED_BASE(public ScopedTaskRunnerObserver) { +class MEDIA_EXPORT AudioOutputDevice : public AudioRendererSink, + public AudioOutputIPCDelegate, + public ScopedTaskRunnerObserver { public: // NOTE: Clients must call Initialize() before using. AudioOutputDevice( @@ -218,6 +218,9 @@ class MEDIA_EXPORT AudioOutputDevice const base::TimeDelta auth_timeout_; std::unique_ptr<base::OneShotTimer> auth_timeout_action_; + // Set when authorization starts, for UMA stats. + base::TimeTicks auth_start_time_; + DISALLOW_COPY_AND_ASSIGN(AudioOutputDevice); }; diff --git a/chromium/media/audio/audio_output_device_unittest.cc b/chromium/media/audio/audio_output_device_unittest.cc index bad1f8c72ae..9d05f6423fc 100644 --- a/chromium/media/audio/audio_output_device_unittest.cc +++ b/chromium/media/audio/audio_output_device_unittest.cc @@ -22,7 +22,7 @@ #include "base/threading/thread.h" #include "base/threading/thread_task_runner_handle.h" #include "media/audio/audio_output_device.h" -#include "media/audio/sample_rates.h" +#include "media/base/sample_rates.h" #include "media/base/test_helpers.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock_mutant.h" @@ -48,6 +48,8 @@ const char kNonDefaultDeviceId[] = "valid-nondefault-device-id"; const char kUnauthorizedDeviceId[] = "unauthorized-device-id"; const int kAuthTimeoutForTestingMs = 500; const int kOutputDelayMs = 20; +const uint32_t kBitstreamFrames = 1024; +const size_t kBitstreamDataSize = 512; class MockRenderCallback : public AudioRendererSink::RenderCallback { public: @@ -62,6 +64,16 @@ class MockRenderCallback : public AudioRendererSink::RenderCallback { MOCK_METHOD0(OnRenderError, void()); }; +void RenderAudioBus(base::TimeDelta delay, + base::TimeTicks timestamp, + int prior_frames_skipped, + AudioBus* dest) { + if (dest->is_bitstream_format()) { + dest->SetBitstreamFrames(kBitstreamFrames); + dest->SetBitstreamDataSize(kBitstreamDataSize); + } +} + class MockAudioOutputIPC : public AudioOutputIPC { public: MockAudioOutputIPC() {} @@ -109,15 +121,18 @@ class AudioOutputDeviceTest AudioOutputDeviceTest(); ~AudioOutputDeviceTest(); + void SetupBitstreamParameters(); void ReceiveAuthorization(OutputDeviceStatus device_status); void StartAudioDevice(); void CreateStream(); void ExpectRenderCallback(); void WaitUntilRenderCallback(); + void WaitForAudioThreadCallbackProcessCompletion(); void StopAudioDevice(); void CreateDevice(const std::string& device_id); void SetDevice(const std::string& device_id); void CheckDeviceStatus(OutputDeviceStatus device_status); + void VerifyBitstreamFields(); protected: // Used to clean up TLS pointers that the test(s) will initialize. @@ -258,7 +273,7 @@ void AudioOutputDeviceTest::ExpectRenderCallback() { EXPECT_CALL( callback_, Render(base::TimeDelta::FromMilliseconds(kOutputDelayMs), _, _, _)) - .WillOnce(DoAll(QuitLoop(io_loop_.task_runner()), + .WillOnce(DoAll(Invoke(RenderAudioBus), QuitLoop(io_loop_.task_runner()), Return(kNumberOfFramesToProcess))); } @@ -270,6 +285,14 @@ void AudioOutputDeviceTest::WaitUntilRenderCallback() { base::RunLoop().Run(); } +void AudioOutputDeviceTest::WaitForAudioThreadCallbackProcessCompletion() { + uint32_t buffer_index; + size_t bytes_read = browser_socket_.ReceiveWithTimeout( + &buffer_index, sizeof(buffer_index), + base::TimeDelta::FromMilliseconds(900)); + EXPECT_EQ(bytes_read, sizeof(buffer_index)); +} + void AudioOutputDeviceTest::StopAudioDevice() { if (device_status_ == OUTPUT_DEVICE_STATUS_OK) EXPECT_CALL(*audio_output_ipc_, CloseStream()); @@ -278,6 +301,19 @@ void AudioOutputDeviceTest::StopAudioDevice() { base::RunLoop().RunUntilIdle(); } +void AudioOutputDeviceTest::SetupBitstreamParameters() { + default_audio_parameters_.Reset(AudioParameters::AUDIO_BITSTREAM_EAC3, + CHANNEL_LAYOUT_STEREO, 48000, 16, 1024); + SetDevice(kNonDefaultDeviceId); +} + +void AudioOutputDeviceTest::VerifyBitstreamFields() { + AudioOutputBuffer* buffer = + reinterpret_cast<AudioOutputBuffer*>(shared_memory_.memory()); + EXPECT_EQ(kBitstreamDataSize, buffer->params.bitstream_data_size); + EXPECT_EQ(kBitstreamFrames, buffer->params.bitstream_frames); +} + TEST_P(AudioOutputDeviceTest, Initialize) { // Tests that the object can be constructed, initialized and destructed // without having ever been started. @@ -384,6 +420,17 @@ TEST_P(AudioOutputDeviceTest, AuthorizationTimedOut) { base::RunLoop().RunUntilIdle(); } +TEST_P(AudioOutputDeviceTest, BitstreamFormatTest) { + SetupBitstreamParameters(); + StartAudioDevice(); + ExpectRenderCallback(); + CreateStream(); + WaitUntilRenderCallback(); + WaitForAudioThreadCallbackProcessCompletion(); + VerifyBitstreamFields(); + StopAudioDevice(); +} + INSTANTIATE_TEST_CASE_P(Render, AudioOutputDeviceTest, Values(false)); } // namespace media. diff --git a/chromium/media/audio/audio_output_proxy_unittest.cc b/chromium/media/audio/audio_output_proxy_unittest.cc index bb137ed90fa..5d932137dda 100644 --- a/chromium/media/audio/audio_output_proxy_unittest.cc +++ b/chromium/media/audio/audio_output_proxy_unittest.cc @@ -140,7 +140,6 @@ class MockAudioManager : public AudioManagerBase { MOCK_METHOD0(HasAudioOutputDevices, bool()); MOCK_METHOD0(HasAudioInputDevices, bool()); MOCK_METHOD0(GetAudioInputDeviceModel, base::string16()); - MOCK_METHOD0(ShowAudioInputSettings, void()); MOCK_METHOD1(GetAudioInputDeviceNames, void(media::AudioDeviceNames* device_name)); MOCK_METHOD2(GetPreferredOutputStreamParameters, AudioParameters( diff --git a/chromium/media/audio/audio_output_resampler.cc b/chromium/media/audio/audio_output_resampler.cc index 907955f91ee..9cac015748a 100644 --- a/chromium/media/audio/audio_output_resampler.cc +++ b/chromium/media/audio/audio_output_resampler.cc @@ -21,10 +21,10 @@ #include "base/trace_event/trace_event.h" #include "build/build_config.h" #include "media/audio/audio_output_proxy.h" -#include "media/audio/sample_rates.h" #include "media/base/audio_converter.h" #include "media/base/audio_timestamp_helper.h" #include "media/base/limits.h" +#include "media/base/sample_rates.h" namespace media { @@ -93,19 +93,15 @@ class OnMoreDataConverter // Record UMA statistics for hardware output configuration. static void RecordStats(const AudioParameters& output_params) { - // Note the 'PRESUBMIT_IGNORE_UMA_MAX's below, these silence the PRESUBMIT.py - // check for uma enum max usage, since we're abusing UMA_HISTOGRAM_ENUMERATION - // to report a discrete value. - UMA_HISTOGRAM_ENUMERATION( - "Media.HardwareAudioBitsPerChannel", - output_params.bits_per_sample(), - limits::kMaxBitsPerSample); // PRESUBMIT_IGNORE_UMA_MAX + UMA_HISTOGRAM_EXACT_LINEAR("Media.HardwareAudioBitsPerChannel", + output_params.bits_per_sample(), + static_cast<int>(limits::kMaxBitsPerSample)); UMA_HISTOGRAM_ENUMERATION( "Media.HardwareAudioChannelLayout", output_params.channel_layout(), CHANNEL_LAYOUT_MAX + 1); - UMA_HISTOGRAM_ENUMERATION( - "Media.HardwareAudioChannelCount", output_params.channels(), - limits::kMaxChannels); // PRESUBMIT_IGNORE_UMA_MAX + UMA_HISTOGRAM_EXACT_LINEAR("Media.HardwareAudioChannelCount", + output_params.channels(), + static_cast<int>(limits::kMaxChannels)); AudioSampleRate asr; if (ToAudioSampleRate(output_params.sample_rate(), &asr)) { @@ -121,19 +117,15 @@ static void RecordStats(const AudioParameters& output_params) { // Record UMA statistics for hardware output configuration after fallback. static void RecordFallbackStats(const AudioParameters& output_params) { UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", true); - // Note the 'PRESUBMIT_IGNORE_UMA_MAX's below, these silence the PRESUBMIT.py - // check for uma enum max usage, since we're abusing UMA_HISTOGRAM_ENUMERATION - // to report a discrete value. - UMA_HISTOGRAM_ENUMERATION( - "Media.FallbackHardwareAudioBitsPerChannel", - output_params.bits_per_sample(), - limits::kMaxBitsPerSample); // PRESUBMIT_IGNORE_UMA_MAX + UMA_HISTOGRAM_EXACT_LINEAR("Media.FallbackHardwareAudioBitsPerChannel", + output_params.bits_per_sample(), + static_cast<int>(limits::kMaxBitsPerSample)); UMA_HISTOGRAM_ENUMERATION( "Media.FallbackHardwareAudioChannelLayout", output_params.channel_layout(), CHANNEL_LAYOUT_MAX + 1); - UMA_HISTOGRAM_ENUMERATION( - "Media.FallbackHardwareAudioChannelCount", output_params.channels(), - limits::kMaxChannels); // PRESUBMIT_IGNORE_UMA_MAX + UMA_HISTOGRAM_EXACT_LINEAR("Media.FallbackHardwareAudioChannelCount", + output_params.channels(), + static_cast<int>(limits::kMaxChannels)); AudioSampleRate asr; if (ToAudioSampleRate(output_params.sample_rate(), &asr)) { diff --git a/chromium/media/audio/audio_output_stream_sink.h b/chromium/media/audio/audio_output_stream_sink.h index 19a5c464712..8407e9ee54a 100644 --- a/chromium/media/audio/audio_output_stream_sink.h +++ b/chromium/media/audio/audio_output_stream_sink.h @@ -27,7 +27,7 @@ namespace media { // TODO(dalecurtis): Delete this class once we have a proper mojo audio service; // tracked by http://crbug.com/425368 class MEDIA_EXPORT AudioOutputStreamSink - : NON_EXPORTED_BASE(public RestartableAudioRendererSink), + : public RestartableAudioRendererSink, public AudioOutputStream::AudioSourceCallback { public: AudioOutputStreamSink(); diff --git a/chromium/media/audio/audio_system.h b/chromium/media/audio/audio_system.h index 8df7fa70a94..1338d887a0b 100644 --- a/chromium/media/audio/audio_system.h +++ b/chromium/media/audio/audio_system.h @@ -12,16 +12,10 @@ #include "media/base/audio_parameters.h" #include "media/base/media_export.h" -namespace base { -class SingleThreadTaskRunner; -} - namespace media { class AudioManager; -// Work in progress: Provides asynchronous interface to AudioManager. All the -// AudioManager clients will be switched to it, in preparation for moving -// to Mojo audio service. +// Provides asynchronous interface to access audio device information class MEDIA_EXPORT AudioSystem { public: // Replies are asynchronously sent from audio system thread to the thread the @@ -36,8 +30,6 @@ class MEDIA_EXPORT AudioSystem { using OnInputDeviceInfoCallback = base::OnceCallback< void(const AudioParameters&, const AudioParameters&, const std::string&)>; - // Must not be called on audio system thread if it differs from the one - // AudioSystem is destroyed on. See http://crbug.com/705455. static AudioSystem* Get(); virtual ~AudioSystem(); @@ -47,9 +39,8 @@ class MEDIA_EXPORT AudioSystem { // of the device. // TODO(olka,tommi): fix all AudioManager implementations to return invalid // parameters if the device is not found. - virtual void GetInputStreamParameters( - const std::string& device_id, - OnAudioParamsCallback on_params_cb) const = 0; + virtual void GetInputStreamParameters(const std::string& device_id, + OnAudioParamsCallback on_params_cb) = 0; // If media::AudioDeviceDescription::IsDefaultDevice(device_id) is true, // callback will receive the parameters of the default output device. @@ -60,17 +51,17 @@ class MEDIA_EXPORT AudioSystem { // parameters if the device is not found. virtual void GetOutputStreamParameters( const std::string& device_id, - OnAudioParamsCallback on_params_cb) const = 0; + OnAudioParamsCallback on_params_cb) = 0; - virtual void HasInputDevices(OnBoolCallback on_has_devices_cb) const = 0; + virtual void HasInputDevices(OnBoolCallback on_has_devices_cb) = 0; - virtual void HasOutputDevices(OnBoolCallback on_has_devices_cb) const = 0; + virtual void HasOutputDevices(OnBoolCallback on_has_devices_cb) = 0; // Replies with device descriptions of input audio devices if |for_input| is // true, and of output audio devices otherwise. virtual void GetDeviceDescriptions( - OnDeviceDescriptionsCallback on_descriptions_cb, - bool for_input) = 0; + bool for_input, + OnDeviceDescriptionsCallback on_descriptions_cb) = 0; // Replies with an empty string if there is no associated output device found. virtual void GetAssociatedOutputDeviceID( @@ -84,8 +75,6 @@ class MEDIA_EXPORT AudioSystem { const std::string& input_device_id, OnInputDeviceInfoCallback on_input_device_info_cb) = 0; - virtual base::SingleThreadTaskRunner* GetTaskRunner() const = 0; - protected: // Sets the global AudioSystem pointer to the specified non-null value. static void SetInstance(AudioSystem* audio_system); diff --git a/chromium/media/audio/audio_system_impl.cc b/chromium/media/audio/audio_system_impl.cc index 707ae2d9305..fb1e5b4341a 100644 --- a/chromium/media/audio/audio_system_impl.cc +++ b/chromium/media/audio/audio_system_impl.cc @@ -38,7 +38,7 @@ std::unique_ptr<AudioSystem> AudioSystemImpl::Create( void AudioSystemImpl::GetInputStreamParameters( const std::string& device_id, - OnAudioParamsCallback on_params_cb) const { + OnAudioParamsCallback on_params_cb) { if (GetTaskRunner()->BelongsToCurrentThread()) { GetTaskRunner()->PostTask(FROM_HERE, base::BindOnce(std::move(on_params_cb), @@ -55,7 +55,7 @@ void AudioSystemImpl::GetInputStreamParameters( void AudioSystemImpl::GetOutputStreamParameters( const std::string& device_id, - OnAudioParamsCallback on_params_cb) const { + OnAudioParamsCallback on_params_cb) { if (GetTaskRunner()->BelongsToCurrentThread()) { GetTaskRunner()->PostTask(FROM_HERE, base::BindOnce(std::move(on_params_cb), @@ -70,7 +70,7 @@ void AudioSystemImpl::GetOutputStreamParameters( std::move(on_params_cb)); } -void AudioSystemImpl::HasInputDevices(OnBoolCallback on_has_devices_cb) const { +void AudioSystemImpl::HasInputDevices(OnBoolCallback on_has_devices_cb) { if (GetTaskRunner()->BelongsToCurrentThread()) { GetTaskRunner()->PostTask( FROM_HERE, base::BindOnce(std::move(on_has_devices_cb), @@ -84,7 +84,7 @@ void AudioSystemImpl::HasInputDevices(OnBoolCallback on_has_devices_cb) const { std::move(on_has_devices_cb)); } -void AudioSystemImpl::HasOutputDevices(OnBoolCallback on_has_devices_cb) const { +void AudioSystemImpl::HasOutputDevices(OnBoolCallback on_has_devices_cb) { if (GetTaskRunner()->BelongsToCurrentThread()) { GetTaskRunner()->PostTask( FROM_HERE, base::BindOnce(std::move(on_has_devices_cb), @@ -99,8 +99,8 @@ void AudioSystemImpl::HasOutputDevices(OnBoolCallback on_has_devices_cb) const { } void AudioSystemImpl::GetDeviceDescriptions( - OnDeviceDescriptionsCallback on_descriptions_cb, - bool for_input) { + bool for_input, + OnDeviceDescriptionsCallback on_descriptions_cb) { if (GetTaskRunner()->BelongsToCurrentThread()) { GetTaskRunner()->PostTask( FROM_HERE, @@ -151,10 +151,6 @@ void AudioSystemImpl::GetInputDeviceInfo( : media::BindToCurrentLoop(std::move(on_input_device_info_cb)))); } -base::SingleThreadTaskRunner* AudioSystemImpl::GetTaskRunner() const { - return audio_manager_->GetTaskRunner(); -} - // static AudioParameters AudioSystemImpl::GetInputParametersOnDeviceThread( AudioManager* audio_manager, @@ -225,4 +221,8 @@ void AudioSystemImpl::GetInputDeviceInfoOnDeviceThread( associated_output_device_id); } +base::SingleThreadTaskRunner* AudioSystemImpl::GetTaskRunner() const { + return audio_manager_->GetTaskRunner(); +} + } // namespace media diff --git a/chromium/media/audio/audio_system_impl.h b/chromium/media/audio/audio_system_impl.h index 4e799b87673..0850d8e54e6 100644 --- a/chromium/media/audio/audio_system_impl.h +++ b/chromium/media/audio/audio_system_impl.h @@ -21,20 +21,19 @@ class MEDIA_EXPORT AudioSystemImpl : public AudioSystem { ~AudioSystemImpl() override; // AudioSystem implementation. - void GetInputStreamParameters( - const std::string& device_id, - OnAudioParamsCallback on_params_cb) const override; + void GetInputStreamParameters(const std::string& device_id, + OnAudioParamsCallback on_params_cb) override; - void GetOutputStreamParameters( - const std::string& device_id, - OnAudioParamsCallback on_params_cb) const override; + void GetOutputStreamParameters(const std::string& device_id, + OnAudioParamsCallback on_params_cb) override; - void HasInputDevices(OnBoolCallback on_has_devices_cb) const override; + void HasInputDevices(OnBoolCallback on_has_devices_cb) override; - void HasOutputDevices(OnBoolCallback on_has_devices_cb) const override; + void HasOutputDevices(OnBoolCallback on_has_devices_cb) override; - void GetDeviceDescriptions(OnDeviceDescriptionsCallback on_descriptions_cp, - bool for_input) override; + void GetDeviceDescriptions( + bool for_input, + OnDeviceDescriptionsCallback on_descriptions_cp) override; void GetAssociatedOutputDeviceID(const std::string& input_device_id, OnDeviceIdCallback on_device_id_cb) override; @@ -43,14 +42,10 @@ class MEDIA_EXPORT AudioSystemImpl : public AudioSystem { const std::string& input_device_id, OnInputDeviceInfoCallback on_input_device_info_cb) override; - base::SingleThreadTaskRunner* GetTaskRunner() const override; - protected: AudioSystemImpl(AudioManager* audio_manager); private: - AudioManager* const audio_manager_; - static AudioParameters GetInputParametersOnDeviceThread( AudioManager* audio_manager, const std::string& device_id); @@ -68,6 +63,10 @@ class MEDIA_EXPORT AudioSystemImpl : public AudioSystem { const std::string& input_device_id, AudioSystem::OnInputDeviceInfoCallback on_input_device_info_cb); + base::SingleThreadTaskRunner* GetTaskRunner() const; + + AudioManager* const audio_manager_; + DISALLOW_COPY_AND_ASSIGN(AudioSystemImpl); }; diff --git a/chromium/media/audio/audio_system_impl_unittest.cc b/chromium/media/audio/audio_system_impl_unittest.cc index 7f0037935c2..8601050dc2c 100644 --- a/chromium/media/audio/audio_system_impl_unittest.cc +++ b/chromium/media/audio/audio_system_impl_unittest.cc @@ -241,9 +241,8 @@ TEST_P(AudioSystemImplTest, GetInputDeviceDescriptionsNoInputDevices) { EXPECT_EQ(1, static_cast<int>(output_device_descriptions_.size())); EXPECT_CALL(*this, DeviceDescriptionsReceived()); audio_system_->GetDeviceDescriptions( - base::Bind(&AudioSystemImplTest::OnGetDeviceDescriptions, - base::Unretained(this), input_device_descriptions_), - true); + true, base::Bind(&AudioSystemImplTest::OnGetDeviceDescriptions, + base::Unretained(this), input_device_descriptions_)); WaitForCallback(); } @@ -258,9 +257,8 @@ TEST_P(AudioSystemImplTest, GetInputDeviceDescriptions) { EXPECT_EQ(1, static_cast<int>(output_device_descriptions_.size())); EXPECT_CALL(*this, DeviceDescriptionsReceived()); audio_system_->GetDeviceDescriptions( - base::Bind(&AudioSystemImplTest::OnGetDeviceDescriptions, - base::Unretained(this), input_device_descriptions_), - true); + true, base::Bind(&AudioSystemImplTest::OnGetDeviceDescriptions, + base::Unretained(this), input_device_descriptions_)); WaitForCallback(); } @@ -271,9 +269,8 @@ TEST_P(AudioSystemImplTest, GetOutputDeviceDescriptionsNoInputDevices) { EXPECT_EQ(1, static_cast<int>(input_device_descriptions_.size())); EXPECT_CALL(*this, DeviceDescriptionsReceived()); audio_system_->GetDeviceDescriptions( - base::Bind(&AudioSystemImplTest::OnGetDeviceDescriptions, - base::Unretained(this), output_device_descriptions_), - false); + false, base::Bind(&AudioSystemImplTest::OnGetDeviceDescriptions, + base::Unretained(this), output_device_descriptions_)); WaitForCallback(); } @@ -288,9 +285,8 @@ TEST_P(AudioSystemImplTest, GetOutputDeviceDescriptions) { EXPECT_EQ(1, static_cast<int>(input_device_descriptions_.size())); EXPECT_CALL(*this, DeviceDescriptionsReceived()); audio_system_->GetDeviceDescriptions( - base::Bind(&AudioSystemImplTest::OnGetDeviceDescriptions, - base::Unretained(this), output_device_descriptions_), - false); + false, base::Bind(&AudioSystemImplTest::OnGetDeviceDescriptions, + base::Unretained(this), output_device_descriptions_)); WaitForCallback(); } diff --git a/chromium/media/audio/clockless_audio_sink.cc b/chromium/media/audio/clockless_audio_sink.cc index bea6fe0943e..d95f40e05f9 100644 --- a/chromium/media/audio/clockless_audio_sink.cc +++ b/chromium/media/audio/clockless_audio_sink.cc @@ -84,7 +84,8 @@ ClocklessAudioSink::ClocklessAudioSink(const OutputDeviceInfo& device_info) : device_info_(device_info), initialized_(false), playing_(false), - hashing_(false) {} + hashing_(false), + is_optimized_for_hw_params_(true) {} ClocklessAudioSink::~ClocklessAudioSink() {} @@ -135,7 +136,7 @@ OutputDeviceInfo ClocklessAudioSink::GetOutputDeviceInfo() { } bool ClocklessAudioSink::IsOptimizedForHardwareParameters() { - return false; + return is_optimized_for_hw_params_; } bool ClocklessAudioSink::CurrentThreadIsRenderingThread() { @@ -152,4 +153,9 @@ std::string ClocklessAudioSink::GetAudioHashForTesting() { return thread_ && hashing_ ? thread_->GetAudioHash() : std::string(); } +void ClocklessAudioSink::SetIsOptimizedForHardwareParametersForTesting( + bool value) { + is_optimized_for_hw_params_ = value; +} + } // namespace media diff --git a/chromium/media/audio/clockless_audio_sink.h b/chromium/media/audio/clockless_audio_sink.h index 029932c9ff9..fec3dd53165 100644 --- a/chromium/media/audio/clockless_audio_sink.h +++ b/chromium/media/audio/clockless_audio_sink.h @@ -17,8 +17,7 @@ class ClocklessAudioSinkThread; // Implementation of an AudioRendererSink that consumes the audio as fast as // possible. This class does not support multiple Play()/Pause() events. -class MEDIA_EXPORT ClocklessAudioSink - : NON_EXPORTED_BASE(public AudioRendererSink) { +class MEDIA_EXPORT ClocklessAudioSink : public AudioRendererSink { public: ClocklessAudioSink(); explicit ClocklessAudioSink(const OutputDeviceInfo& device_info); @@ -44,6 +43,8 @@ class MEDIA_EXPORT ClocklessAudioSink // Returns the hash of all audio frames seen since construction. std::string GetAudioHashForTesting(); + void SetIsOptimizedForHardwareParametersForTesting(bool value); + protected: ~ClocklessAudioSink() override; @@ -53,6 +54,7 @@ class MEDIA_EXPORT ClocklessAudioSink bool initialized_; bool playing_; bool hashing_; + bool is_optimized_for_hw_params_; // Time taken in last set of Render() calls. base::TimeDelta playback_time_; diff --git a/chromium/media/audio/cras/audio_manager_cras.cc b/chromium/media/audio/cras/audio_manager_cras.cc index 1029ebe8e49..3221531661f 100644 --- a/chromium/media/audio/cras/audio_manager_cras.cc +++ b/chromium/media/audio/cras/audio_manager_cras.cc @@ -13,12 +13,12 @@ #include "base/command_line.h" #include "base/environment.h" #include "base/logging.h" -#include "base/metrics/field_trial.h" -#include "base/metrics/histogram_macros.h" #include "base/nix/xdg_util.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" +#include "base/synchronization/waitable_event.h" #include "base/sys_info.h" +#include "base/threading/thread_task_runner_handle.h" #include "chromeos/audio/audio_device.h" #include "chromeos/audio/cras_audio_handler.h" #include "media/audio/audio_device_description.h" @@ -46,13 +46,12 @@ const int kDefaultSampleRate = 48000; // Default input buffer size. const int kDefaultInputBufferSize = 1024; -const char kBeamformingOnDeviceId[] = "default-beamforming-on"; -const char kBeamformingOffDeviceId[] = "default-beamforming-off"; - const char kInternalInputVirtualDevice[] = "Built-in mic"; const char kInternalOutputVirtualDevice[] = "Built-in speaker"; const char kHeadphoneLineOutVirtualDevice[] = "Headphone/Line Out"; +// Used for the Media.CrosBeamformingDeviceState histogram, currently not used +// since beamforming is disabled. enum CrosBeamformingDeviceState { BEAMFORMING_DEFAULT_ENABLED = 0, BEAMFORMING_USER_ENABLED, @@ -61,30 +60,24 @@ enum CrosBeamformingDeviceState { BEAMFORMING_STATE_MAX = BEAMFORMING_USER_DISABLED }; -void RecordBeamformingDeviceState(CrosBeamformingDeviceState state) { - UMA_HISTOGRAM_ENUMERATION("Media.CrosBeamformingDeviceState", state, - BEAMFORMING_STATE_MAX + 1); -} - -bool IsBeamformingDefaultEnabled() { - return base::FieldTrialList::FindFullName("ChromebookBeamforming") == - "Enabled"; +bool HasKeyboardMic(const chromeos::AudioDeviceList& devices) { + for (const auto& device : devices) { + if (device.is_input && device.type == chromeos::AUDIO_TYPE_KEYBOARD_MIC) { + return true; + } + } + return false; } -// Returns a mic positions string if the machine has a beamforming capable -// internal mic and otherwise an empty string. -std::string MicPositions() { - // Get the list of devices from CRAS. An internal mic with a non-empty - // positions field indicates the machine has a beamforming capable mic array. - chromeos::AudioDeviceList devices; - chromeos::CrasAudioHandler::Get()->GetAudioDevices(&devices); +const chromeos::AudioDevice* GetDeviceFromId( + const chromeos::AudioDeviceList& devices, + uint64_t id) { for (const auto& device : devices) { - if (device.type == chromeos::AUDIO_TYPE_INTERNAL_MIC) { - // There should be only one internal mic device. - return device.mic_positions; + if (device.id == id) { + return &device; } } - return ""; + return nullptr; } // Process |device_list| that two shares the same dev_index by creating a @@ -110,44 +103,13 @@ void ProcessVirtualDeviceName(AudioDeviceNames* device_names, } // namespace -// Adds the beamforming on and off devices to |device_names|. -void AudioManagerCras::AddBeamformingDevices(AudioDeviceNames* device_names) { - DCHECK(device_names->empty()); - const std::string beamforming_on_name = - GetLocalizedStringUTF8(BEAMFORMING_ON_DEFAULT_AUDIO_INPUT_DEVICE_NAME); - const std::string beamforming_off_name = - GetLocalizedStringUTF8(BEAMFORMING_OFF_DEFAULT_AUDIO_INPUT_DEVICE_NAME); - - if (IsBeamformingDefaultEnabled()) { - // The first device in the list is expected to have a "default" device ID. - // Web apps may depend on this behavior. - beamforming_on_device_id_ = AudioDeviceDescription::kDefaultDeviceId; - beamforming_off_device_id_ = kBeamformingOffDeviceId; - - // Users in the experiment will have the "beamforming on" device appear - // first in the list. This causes it to be selected by default. - device_names->push_back( - AudioDeviceName(beamforming_on_name, beamforming_on_device_id_)); - device_names->push_back( - AudioDeviceName(beamforming_off_name, beamforming_off_device_id_)); - } else { - beamforming_off_device_id_ = AudioDeviceDescription::kDefaultDeviceId; - beamforming_on_device_id_ = kBeamformingOnDeviceId; - - device_names->push_back( - AudioDeviceName(beamforming_off_name, beamforming_off_device_id_)); - device_names->push_back( - AudioDeviceName(beamforming_on_name, beamforming_on_device_id_)); - } -} - bool AudioManagerCras::HasAudioOutputDevices() { return true; } bool AudioManagerCras::HasAudioInputDevices() { chromeos::AudioDeviceList devices; - chromeos::CrasAudioHandler::Get()->GetAudioDevices(&devices); + GetAudioDevices(&devices); for (size_t i = 0; i < devices.size(); ++i) { if (devices[i].is_input && devices[i].is_for_simple_usage()) return true; @@ -158,31 +120,25 @@ bool AudioManagerCras::HasAudioInputDevices() { AudioManagerCras::AudioManagerCras(std::unique_ptr<AudioThread> audio_thread, AudioLogFactory* audio_log_factory) : AudioManagerBase(std::move(audio_thread), audio_log_factory), - beamforming_on_device_id_(nullptr), - beamforming_off_device_id_(nullptr) { + on_shutdown_(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED), + main_task_runner_(base::ThreadTaskRunnerHandle::Get()), + weak_ptr_factory_(this) { + weak_this_ = weak_ptr_factory_.GetWeakPtr(); SetMaxOutputStreamsAllowed(kMaxOutputStreams); } AudioManagerCras::~AudioManagerCras() = default; -void AudioManagerCras::ShowAudioInputSettings() { - NOTIMPLEMENTED(); -} - void AudioManagerCras::GetAudioDeviceNamesImpl(bool is_input, AudioDeviceNames* device_names) { DCHECK(device_names->empty()); - // At least two mic positions indicates we have a beamforming capable mic - // array. Add the virtual beamforming device to the list. When this device is - // queried through GetInputStreamParameters, provide the cached mic positions. - if (is_input && mic_positions_.size() > 1) - AddBeamformingDevices(device_names); - else - device_names->push_back(AudioDeviceName::CreateDefault()); + + device_names->push_back(AudioDeviceName::CreateDefault()); if (base::FeatureList::IsEnabled(features::kEnumerateAudioDevices)) { chromeos::AudioDeviceList devices; - chromeos::CrasAudioHandler::Get()->GetAudioDevices(&devices); + GetAudioDevices(&devices); // |dev_idx_map| is a map of dev_index and their audio devices. std::map<int, chromeos::AudioDeviceList> dev_idx_map; @@ -209,7 +165,6 @@ void AudioManagerCras::GetAudioDeviceNamesImpl(bool is_input, void AudioManagerCras::GetAudioInputDeviceNames( AudioDeviceNames* device_names) { - mic_positions_ = ParsePointsFromString(MicPositions()); GetAudioDeviceNamesImpl(true, device_names); } @@ -231,34 +186,11 @@ AudioParameters AudioManagerCras::GetInputStreamParameters( AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, kDefaultSampleRate, 16, buffer_size); - if (chromeos::CrasAudioHandler::Get()->HasKeyboardMic()) + chromeos::AudioDeviceList devices; + GetAudioDevices(&devices); + if (HasKeyboardMic(devices)) params.set_effects(AudioParameters::KEYBOARD_MIC); - if (mic_positions_.size() > 1) { - // We have the mic_positions_ check here because one of the beamforming - // devices will have been assigned the "default" ID, which could otherwise - // be confused with the ID in the non-beamforming-capable-device case. - DCHECK(beamforming_on_device_id_); - DCHECK(beamforming_off_device_id_); - - if (device_id == beamforming_on_device_id_) { - params.set_mic_positions(mic_positions_); - - // Record a UMA metric based on the state of the experiment and the - // selected device. This will tell us i) how common it is for users to - // manually adjust the beamforming device and ii) how contaminated our - // metric experiment buckets are. - if (IsBeamformingDefaultEnabled()) - RecordBeamformingDeviceState(BEAMFORMING_DEFAULT_ENABLED); - else - RecordBeamformingDeviceState(BEAMFORMING_USER_ENABLED); - } else if (device_id == beamforming_off_device_id_) { - if (!IsBeamformingDefaultEnabled()) - RecordBeamformingDeviceState(BEAMFORMING_DEFAULT_DISABLED); - else - RecordBeamformingDeviceState(BEAMFORMING_USER_DISABLED); - } - } return params; } @@ -268,27 +200,22 @@ std::string AudioManagerCras::GetAssociatedOutputDeviceID( return ""; chromeos::AudioDeviceList devices; - chromeos::CrasAudioHandler* audio_handler = chromeos::CrasAudioHandler::Get(); - audio_handler->GetAudioDevices(&devices); - - if ((beamforming_on_device_id_ && - input_device_id == beamforming_on_device_id_) || - (beamforming_off_device_id_ && - input_device_id == beamforming_off_device_id_)) { - // These are special devices derived from the internal mic array, so they - // should be associated to the internal speaker. - const chromeos::AudioDevice* internal_speaker = - audio_handler->GetDeviceByType(chromeos::AUDIO_TYPE_INTERNAL_SPEAKER); - return internal_speaker ? base::Uint64ToString(internal_speaker->id) : ""; - } + GetAudioDevices(&devices); - // At this point, we know we have an ordinary input device, so we look up its - // device_name, which identifies which hardware device it belongs to. uint64_t device_id = 0; - if (!base::StringToUint64(input_device_id, &device_id)) - return ""; + if (input_device_id == AudioDeviceDescription::kDefaultDeviceId) { + return AudioDeviceDescription::kDefaultDeviceId; + } else { + // At this point, we know we have an ordinary input device id, so we parse + // the string for its device_id. + if (!base::StringToUint64(input_device_id, &device_id)) + return ""; + } + + // Find the device in the device list to get the device name (identifying the + // hardware device). const chromeos::AudioDevice* input_device = - audio_handler->GetDeviceFromId(device_id); + GetDeviceFromId(devices, device_id); if (!input_device) return ""; @@ -306,14 +233,36 @@ std::string AudioManagerCras::GetAssociatedOutputDeviceID( } std::string AudioManagerCras::GetDefaultOutputDeviceID() { - return base::Uint64ToString( - chromeos::CrasAudioHandler::Get()->GetPrimaryActiveOutputNode()); + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); + base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + uint64_t active_output_node_id = 0; + if (main_task_runner_->BelongsToCurrentThread()) { + // Unittest may use the same thread for audio thread. + GetPrimaryActiveOutputNodeOnMainThread(&active_output_node_id, &event); + } else { + main_task_runner_->PostTask( + FROM_HERE, + base::BindOnce( + &AudioManagerCras::GetPrimaryActiveOutputNodeOnMainThread, + weak_this_, base::Unretained(&active_output_node_id), + base::Unretained(&event))); + } + WaitEventOrShutdown(&event); + return base::Uint64ToString(active_output_node_id); } const char* AudioManagerCras::GetName() { return "CRAS"; } +bool AudioManagerCras::Shutdown() { + DCHECK(main_task_runner_->BelongsToCurrentThread()); + weak_ptr_factory_.InvalidateWeakPtrs(); + on_shutdown_.Signal(); + return AudioManager::Shutdown(); +} + AudioOutputStream* AudioManagerCras::MakeLinearOutputStream( const AudioParameters& params, const LogCallback& log_callback) { @@ -347,15 +296,24 @@ AudioInputStream* AudioManagerCras::MakeLowLatencyInputStream( return MakeInputStream(params, device_id); } -int AudioManagerCras::GetMinimumOutputBufferSizePerBoard() { - // On faster boards we can use smaller buffer size for lower latency. - // On slower boards we should use larger buffer size to prevent underrun. - std::string board = base::SysInfo::GetLsbReleaseBoard(); - if (board == "kevin") - return 768; - else if (board == "samus") - return 256; - return 512; +int AudioManagerCras::GetDefaultOutputBufferSizePerBoard() { + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); + int32_t buffer_size = 512; + base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + if (main_task_runner_->BelongsToCurrentThread()) { + // Unittest may use the same thread for audio thread. + GetDefaultOutputBufferSizeOnMainThread(&buffer_size, &event); + } else { + main_task_runner_->PostTask( + FROM_HERE, + base::BindOnce( + &AudioManagerCras::GetDefaultOutputBufferSizeOnMainThread, + weak_this_, base::Unretained(&buffer_size), + base::Unretained(&event))); + } + WaitEventOrShutdown(&event); + return static_cast<int>(buffer_size); } AudioParameters AudioManagerCras::GetPreferredOutputStreamParameters( @@ -363,7 +321,7 @@ AudioParameters AudioManagerCras::GetPreferredOutputStreamParameters( const AudioParameters& input_params) { ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; int sample_rate = kDefaultSampleRate; - int buffer_size = GetMinimumOutputBufferSizePerBoard(); + int buffer_size = GetDefaultOutputBufferSizePerBoard(); int bits_per_sample = 16; if (input_params.IsValid()) { sample_rate = input_params.sample_rate(); @@ -417,4 +375,85 @@ bool AudioManagerCras::IsDefault(const std::string& device_id, bool is_input) { return device_name.unique_id == device_id; } +void AudioManagerCras::GetAudioDevices(chromeos::AudioDeviceList* devices) { + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); + base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + if (main_task_runner_->BelongsToCurrentThread()) { + GetAudioDevicesOnMainThread(devices, &event); + } else { + main_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&AudioManagerCras::GetAudioDevicesOnMainThread, + weak_this_, base::Unretained(devices), + base::Unretained(&event))); + } + WaitEventOrShutdown(&event); +} + +void AudioManagerCras::GetAudioDevicesOnMainThread( + chromeos::AudioDeviceList* devices, + base::WaitableEvent* event) { + DCHECK(main_task_runner_->BelongsToCurrentThread()); + // CrasAudioHandler is shut down before AudioManagerCras. + if (chromeos::CrasAudioHandler::IsInitialized()) { + chromeos::CrasAudioHandler::Get()->GetAudioDevices(devices); + } + event->Signal(); +} + +uint64_t AudioManagerCras::GetPrimaryActiveInputNode() { + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); + uint64_t device_id = 0; + base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + if (main_task_runner_->BelongsToCurrentThread()) { + GetPrimaryActiveInputNodeOnMainThread(&device_id, &event); + } else { + main_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&AudioManagerCras::GetPrimaryActiveInputNodeOnMainThread, + weak_this_, &device_id, &event)); + } + WaitEventOrShutdown(&event); + return device_id; +} + +void AudioManagerCras::GetPrimaryActiveInputNodeOnMainThread( + uint64_t* active_input_node_id, + base::WaitableEvent* event) { + DCHECK(main_task_runner_->BelongsToCurrentThread()); + if (chromeos::CrasAudioHandler::IsInitialized()) { + *active_input_node_id = + chromeos::CrasAudioHandler::Get()->GetPrimaryActiveInputNode(); + } + event->Signal(); +} + +void AudioManagerCras::GetPrimaryActiveOutputNodeOnMainThread( + uint64_t* active_output_node_id, + base::WaitableEvent* event) { + DCHECK(main_task_runner_->BelongsToCurrentThread()); + if (chromeos::CrasAudioHandler::IsInitialized()) { + *active_output_node_id = + chromeos::CrasAudioHandler::Get()->GetPrimaryActiveOutputNode(); + } + event->Signal(); +} + +void AudioManagerCras::GetDefaultOutputBufferSizeOnMainThread( + int32_t* buffer_size, + base::WaitableEvent* event) { + DCHECK(main_task_runner_->BelongsToCurrentThread()); + if (chromeos::CrasAudioHandler::IsInitialized()) { + chromeos::CrasAudioHandler::Get()->GetDefaultOutputBufferSize(buffer_size); + } + event->Signal(); +} + +void AudioManagerCras::WaitEventOrShutdown(base::WaitableEvent* event) { + base::WaitableEvent* waitables[] = {event, &on_shutdown_}; + base::WaitableEvent::WaitMany(waitables, arraysize(waitables)); +} + } // namespace media diff --git a/chromium/media/audio/cras/audio_manager_cras.h b/chromium/media/audio/cras/audio_manager_cras.h index 7cf1200dde1..bf8a3dbf2db 100644 --- a/chromium/media/audio/cras/audio_manager_cras.h +++ b/chromium/media/audio/cras/audio_manager_cras.h @@ -14,6 +14,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "chromeos/audio/audio_device.h" #include "media/audio/audio_manager_base.h" namespace media { @@ -27,7 +28,6 @@ class MEDIA_EXPORT AudioManagerCras : public AudioManagerBase { // AudioManager implementation. bool HasAudioOutputDevices() override; bool HasAudioInputDevices() override; - void ShowAudioInputSettings() override; void GetAudioInputDeviceNames(AudioDeviceNames* device_names) override; void GetAudioOutputDeviceNames(AudioDeviceNames* device_names) override; AudioParameters GetInputStreamParameters( @@ -36,6 +36,7 @@ class MEDIA_EXPORT AudioManagerCras : public AudioManagerBase { const std::string& input_device_id) override; std::string GetDefaultOutputDeviceID() override; const char* GetName() override; + bool Shutdown() override; // AudioManagerBase implementation. AudioOutputStream* MakeLinearOutputStream( @@ -74,18 +75,35 @@ class MEDIA_EXPORT AudioManagerCras : public AudioManagerBase { AudioInputStream* MakeInputStream(const AudioParameters& params, const std::string& device_id); - // Get minimum output buffer size for this board. - int GetMinimumOutputBufferSizePerBoard(); + // Get default output buffer size for this board. + int GetDefaultOutputBufferSizePerBoard(); void GetAudioDeviceNamesImpl(bool is_input, AudioDeviceNames* device_names); - void AddBeamformingDevices(AudioDeviceNames* device_names); + void GetAudioDevices(chromeos::AudioDeviceList* devices); + void GetAudioDevicesOnMainThread(chromeos::AudioDeviceList* devices, + base::WaitableEvent* event); + uint64_t GetPrimaryActiveInputNode(); + void GetPrimaryActiveInputNodeOnMainThread(uint64_t* active_input_node_id, + base::WaitableEvent* event); + void GetPrimaryActiveOutputNodeOnMainThread(uint64_t* active_output_node_id, + base::WaitableEvent* event); + void GetDefaultOutputBufferSizeOnMainThread(int32_t* buffer_size, + base::WaitableEvent* event); - // Stores the mic positions field from the device. - std::vector<Point> mic_positions_; + void WaitEventOrShutdown(base::WaitableEvent* event); - const char* beamforming_on_device_id_; - const char* beamforming_off_device_id_; + // Signaled if AudioManagerCras is shutting down. + base::WaitableEvent on_shutdown_; + + // Task runner of browser main thread. CrasAudioHandler should be only + // accessed on this thread. + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; + + // For posting tasks from audio thread to |main_task_runner_|. + base::WeakPtr<AudioManagerCras> weak_this_; + + base::WeakPtrFactory<AudioManagerCras> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(AudioManagerCras); }; diff --git a/chromium/media/audio/cras/cras_input.cc b/chromium/media/audio/cras/cras_input.cc index b5ac5012ff3..5243a5714b2 100644 --- a/chromium/media/audio/cras/cras_input.cc +++ b/chromium/media/audio/cras/cras_input.cc @@ -288,29 +288,23 @@ void CrasInputStream::ReadAudio(size_t frames, const timespec* sample_ts) { DCHECK(callback_); - timespec latency_ts = {0, 0}; - - // Determine latency and pass that on to the sink. sample_ts is the wall time - // indicating when the first sample in the buffer was captured. Convert that - // to latency in bytes. - cras_client_calc_capture_latency(sample_ts, &latency_ts); - double latency_usec = - latency_ts.tv_sec * base::Time::kMicrosecondsPerSecond + - latency_ts.tv_nsec / base::Time::kNanosecondsPerMicrosecond; - double frames_latency = - latency_usec * params_.sample_rate() / base::Time::kMicrosecondsPerSecond; - unsigned int bytes_latency = - static_cast<unsigned int>(frames_latency * bytes_per_frame_); - // Update the AGC volume level once every second. Note that, |volume| is // also updated each time SetVolume() is called through IPC by the // render-side AGC. double normalized_volume = 0.0; GetAgcVolume(&normalized_volume); - audio_bus_->FromInterleaved( - buffer, audio_bus_->frames(), params_.bits_per_sample() / 8); - callback_->OnData(this, audio_bus_.get(), bytes_latency, normalized_volume); + // Warning: It is generally unsafe to manufacture TimeTicks values; but + // here it is required for interfacing with cras. Assumption: cras + // is providing the timestamp from the CLOCK_MONOTONIC POSIX clock. + const base::TimeTicks capture_time = + base::TimeTicks() + base::TimeDelta::FromTimeSpec(*sample_ts); + DCHECK_EQ(base::TimeTicks::GetClock(), + base::TimeTicks::Clock::LINUX_CLOCK_MONOTONIC); + + audio_bus_->FromInterleaved(buffer, audio_bus_->frames(), + params_.bits_per_sample() / 8); + callback_->OnData(this, audio_bus_.get(), capture_time, normalized_volume); } void CrasInputStream::NotifyStreamError(int err) { diff --git a/chromium/media/audio/cras/cras_input_unittest.cc b/chromium/media/audio/cras/cras_input_unittest.cc index 46bad85b881..4015b7f0908 100644 --- a/chromium/media/audio/cras/cras_input_unittest.cc +++ b/chromium/media/audio/cras/cras_input_unittest.cc @@ -14,6 +14,7 @@ #include "base/test/test_timeouts.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" +#include "chromeos/audio/cras_audio_handler.h" #include "media/audio/audio_device_description.h" #include "media/audio/cras/audio_manager_cras.h" #include "media/audio/fake_audio_log_factory.h" @@ -37,8 +38,9 @@ namespace media { class MockAudioInputCallback : public AudioInputStream::AudioInputCallback { public: - MOCK_METHOD4(OnData, - void(AudioInputStream*, const AudioBus*, uint32_t, double)); + MOCK_METHOD4( + OnData, + void(AudioInputStream*, const AudioBus*, base::TimeTicks, double)); MOCK_METHOD1(OnError, void(AudioInputStream*)); }; @@ -64,11 +66,15 @@ class MockAudioManagerCrasInput : public AudioManagerCras { class CrasInputStreamTest : public testing::Test { protected: CrasInputStreamTest() { + chromeos::CrasAudioHandler::InitializeForTesting(); mock_manager_.reset(new StrictMock<MockAudioManagerCrasInput>()); base::RunLoop().RunUntilIdle(); } - ~CrasInputStreamTest() override { mock_manager_->Shutdown(); } + ~CrasInputStreamTest() override { + chromeos::CrasAudioHandler::Shutdown(); + mock_manager_->Shutdown(); + } CrasInputStream* CreateStream(ChannelLayout layout) { return CreateStream(layout, kTestFramesPerPacket); diff --git a/chromium/media/audio/cras/cras_unified_unittest.cc b/chromium/media/audio/cras/cras_unified_unittest.cc index 3bb52fc7244..7da8fc04513 100644 --- a/chromium/media/audio/cras/cras_unified_unittest.cc +++ b/chromium/media/audio/cras/cras_unified_unittest.cc @@ -14,6 +14,7 @@ #include "base/test/test_timeouts.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" +#include "chromeos/audio/cras_audio_handler.h" #include "media/audio/audio_device_description.h" #include "media/audio/cras/audio_manager_cras.h" #include "media/audio/fake_audio_log_factory.h" @@ -59,11 +60,15 @@ class MockAudioManagerCras : public AudioManagerCras { class CrasUnifiedStreamTest : public testing::Test { protected: CrasUnifiedStreamTest() { + chromeos::CrasAudioHandler::InitializeForTesting(); mock_manager_.reset(new StrictMock<MockAudioManagerCras>()); base::RunLoop().RunUntilIdle(); } - ~CrasUnifiedStreamTest() override { mock_manager_->Shutdown(); } + ~CrasUnifiedStreamTest() override { + chromeos::CrasAudioHandler::Shutdown(); + mock_manager_->Shutdown(); + } CrasUnifiedStream* CreateStream(ChannelLayout layout) { return CreateStream(layout, kTestFramesPerPacket); diff --git a/chromium/media/audio/fake_audio_input_stream.cc b/chromium/media/audio/fake_audio_input_stream.cc index 188d05c7fb1..acf2561c8fc 100644 --- a/chromium/media/audio/fake_audio_input_stream.cc +++ b/chromium/media/audio/fake_audio_input_stream.cc @@ -106,7 +106,7 @@ void FakeAudioInputStream::ReadAudioFromSource() { audio_source_->OnMoreData(base::TimeDelta(), base::TimeTicks::Now(), 0, audio_bus_.get()); - callback_->OnData(this, audio_bus_.get(), 0, 1.0); + callback_->OnData(this, audio_bus_.get(), base::TimeTicks::Now(), 1.0); } using AudioSourceCallback = AudioOutputStream::AudioSourceCallback; diff --git a/chromium/media/audio/fake_audio_input_stream.h b/chromium/media/audio/fake_audio_input_stream.h index bafa7a1c70d..0be1b7f6fbf 100644 --- a/chromium/media/audio/fake_audio_input_stream.h +++ b/chromium/media/audio/fake_audio_input_stream.h @@ -13,8 +13,8 @@ #include "base/callback_forward.h" #include "base/macros.h" #include "media/audio/audio_io.h" -#include "media/audio/fake_audio_worker.h" #include "media/base/audio_parameters.h" +#include "media/base/fake_audio_worker.h" namespace media { diff --git a/chromium/media/audio/fake_audio_log_factory.h b/chromium/media/audio/fake_audio_log_factory.h index d78d533361e..2a6d62a9d60 100644 --- a/chromium/media/audio/fake_audio_log_factory.h +++ b/chromium/media/audio/fake_audio_log_factory.h @@ -13,8 +13,7 @@ namespace media { // Creates stub AudioLog instances, for testing, which do nothing. -class MEDIA_EXPORT FakeAudioLogFactory - : NON_EXPORTED_BASE(public AudioLogFactory) { +class MEDIA_EXPORT FakeAudioLogFactory : public AudioLogFactory { public: FakeAudioLogFactory(); ~FakeAudioLogFactory() override; diff --git a/chromium/media/audio/fake_audio_output_stream.h b/chromium/media/audio/fake_audio_output_stream.h index d4a9adec956..fea278be25a 100644 --- a/chromium/media/audio/fake_audio_output_stream.h +++ b/chromium/media/audio/fake_audio_output_stream.h @@ -9,8 +9,8 @@ #include "base/macros.h" #include "media/audio/audio_io.h" -#include "media/audio/fake_audio_worker.h" #include "media/base/audio_parameters.h" +#include "media/base/fake_audio_worker.h" namespace media { diff --git a/chromium/media/audio/fake_audio_worker.cc b/chromium/media/audio/fake_audio_worker.cc deleted file mode 100644 index a68b6ef7504..00000000000 --- a/chromium/media/audio/fake_audio_worker.cc +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (c) 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_worker.h" - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/cancelable_callback.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/single_thread_task_runner.h" -#include "base/synchronization/lock.h" -#include "base/threading/thread_checker.h" -#include "base/time/time.h" -#include "media/base/audio_parameters.h" - -namespace media { - -class FakeAudioWorker::Worker - : public base::RefCountedThreadSafe<FakeAudioWorker::Worker> { - public: - Worker(const scoped_refptr<base::SingleThreadTaskRunner>& worker_task_runner, - const AudioParameters& params); - - bool IsStopped(); - void Start(const base::Closure& worker_cb); - void Stop(); - - private: - friend class base::RefCountedThreadSafe<Worker>; - ~Worker(); - - // Initialize and start regular calls to DoRead() on the worker thread. - void DoStart(); - - // Cancel any delayed callbacks to DoRead() in the worker loop's queue. - void DoCancel(); - - // Task that regularly calls |worker_cb_| according to the playback rate as - // determined by the audio parameters given during construction. Runs on - // the worker loop. - void DoRead(); - - const scoped_refptr<base::SingleThreadTaskRunner> worker_task_runner_; - const base::TimeDelta buffer_duration_; - - base::Lock worker_cb_lock_; // Held while mutating or running |worker_cb_|. - base::Closure worker_cb_; - base::TimeTicks next_read_time_; - - // Used to cancel any delayed tasks still inside the worker loop's queue. - base::CancelableClosure worker_task_cb_; - - base::ThreadChecker thread_checker_; - - DISALLOW_COPY_AND_ASSIGN(Worker); -}; - -FakeAudioWorker::FakeAudioWorker( - const scoped_refptr<base::SingleThreadTaskRunner>& worker_task_runner, - const AudioParameters& params) - : worker_(new Worker(worker_task_runner, params)) { -} - -FakeAudioWorker::~FakeAudioWorker() { - DCHECK(worker_->IsStopped()); -} - -void FakeAudioWorker::Start(const base::Closure& worker_cb) { - DCHECK(worker_->IsStopped()); - worker_->Start(worker_cb); -} - -void FakeAudioWorker::Stop() { - worker_->Stop(); -} - -FakeAudioWorker::Worker::Worker( - const scoped_refptr<base::SingleThreadTaskRunner>& worker_task_runner, - const AudioParameters& params) - : worker_task_runner_(worker_task_runner), - buffer_duration_(base::TimeDelta::FromMicroseconds( - params.frames_per_buffer() * base::Time::kMicrosecondsPerSecond / - static_cast<float>(params.sample_rate()))) { - // Worker can be constructed on any thread, but will DCHECK that its - // Start/Stop methods are called from the same thread. - thread_checker_.DetachFromThread(); -} - -FakeAudioWorker::Worker::~Worker() { - DCHECK(worker_cb_.is_null()); -} - -bool FakeAudioWorker::Worker::IsStopped() { - base::AutoLock scoped_lock(worker_cb_lock_); - return worker_cb_.is_null(); -} - -void FakeAudioWorker::Worker::Start(const base::Closure& worker_cb) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(!worker_cb.is_null()); - { - base::AutoLock scoped_lock(worker_cb_lock_); - DCHECK(worker_cb_.is_null()); - worker_cb_ = worker_cb; - } - worker_task_runner_->PostTask(FROM_HERE, base::Bind(&Worker::DoStart, this)); -} - -void FakeAudioWorker::Worker::DoStart() { - DCHECK(worker_task_runner_->BelongsToCurrentThread()); - next_read_time_ = base::TimeTicks::Now(); - worker_task_cb_.Reset(base::Bind(&Worker::DoRead, this)); - worker_task_cb_.callback().Run(); -} - -void FakeAudioWorker::Worker::Stop() { - DCHECK(thread_checker_.CalledOnValidThread()); - { - base::AutoLock scoped_lock(worker_cb_lock_); - if (worker_cb_.is_null()) - return; - worker_cb_.Reset(); - } - worker_task_runner_->PostTask(FROM_HERE, base::Bind(&Worker::DoCancel, this)); -} - -void FakeAudioWorker::Worker::DoCancel() { - DCHECK(worker_task_runner_->BelongsToCurrentThread()); - worker_task_cb_.Cancel(); -} - -void FakeAudioWorker::Worker::DoRead() { - DCHECK(worker_task_runner_->BelongsToCurrentThread()); - - { - base::AutoLock scoped_lock(worker_cb_lock_); - if (!worker_cb_.is_null()) - worker_cb_.Run(); - } - - // Need to account for time spent here due to the cost of |worker_cb| as well - // as the imprecision of PostDelayedTask(). - const base::TimeTicks now = base::TimeTicks::Now(); - base::TimeDelta delay = next_read_time_ + buffer_duration_ - now; - - // If we're behind, find the next nearest ontime interval. - if (delay < base::TimeDelta()) - delay += buffer_duration_ * (-delay / buffer_duration_ + 1); - next_read_time_ = now + delay; - - worker_task_runner_->PostDelayedTask( - FROM_HERE, worker_task_cb_.callback(), delay); -} - -} // namespace media diff --git a/chromium/media/audio/fake_audio_worker.h b/chromium/media/audio/fake_audio_worker.h deleted file mode 100644 index 4b0241bebc3..00000000000 --- a/chromium/media/audio/fake_audio_worker.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 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_WORKER_H_ -#define MEDIA_AUDIO_FAKE_AUDIO_WORKER_H_ - -#include "base/callback_forward.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "media/base/media_export.h" - -namespace base { -class SingleThreadTaskRunner; -} - -namespace media { -class AudioParameters; - -// A fake audio worker. Using a provided message loop, FakeAudioWorker will -// call back the provided callback like a real audio consumer or producer would. -class MEDIA_EXPORT FakeAudioWorker { - public: - // |worker_task_runner| is the task runner on which the closure provided to - // Start() will be executed on. This may or may not be the be for the same - // thread that invokes the Start/Stop methods. - // |params| is used to determine the frequency of callbacks. - FakeAudioWorker( - const scoped_refptr<base::SingleThreadTaskRunner>& worker_task_runner, - const AudioParameters& params); - ~FakeAudioWorker(); - - // Start executing |worker_cb| at a regular intervals. Stop() must be called - // by the same thread before destroying FakeAudioWorker. - void Start(const base::Closure& worker_cb); - - // Stop executing the closure provided to Start(). Blocks until the worker - // loop is not inside a closure invocation. Safe to call multiple times. - // Must be called on the same thread that called Start(). - void Stop(); - - private: - // All state and implementation is kept within this ref-counted class because - // cancellation of posted tasks must happen on the worker thread some time - // after the call to Stop() (on the main thread) returns. - class Worker; - const scoped_refptr<Worker> worker_; - - DISALLOW_COPY_AND_ASSIGN(FakeAudioWorker); -}; - -} // namespace media - -#endif // MEDIA_AUDIO_FAKE_AUDIO_WORKER_H_ diff --git a/chromium/media/audio/fake_audio_worker_unittest.cc b/chromium/media/audio/fake_audio_worker_unittest.cc deleted file mode 100644 index e78ddb7d75a..00000000000 --- a/chromium/media/audio/fake_audio_worker_unittest.cc +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/bind.h" -#include "base/macros.h" -#include "base/message_loop/message_loop.h" -#include "base/run_loop.h" -#include "base/single_thread_task_runner.h" -#include "base/time/time.h" -#include "media/audio/fake_audio_worker.h" -#include "media/audio/simple_sources.h" -#include "media/base/audio_parameters.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace media { - -static const int kTestCallbacks = 5; - -class FakeAudioWorkerTest : public testing::Test { - public: - FakeAudioWorkerTest() - : params_( - AudioParameters::AUDIO_FAKE, CHANNEL_LAYOUT_STEREO, 44100, 8, 128), - fake_worker_(message_loop_.task_runner(), params_), - seen_callbacks_(0) { - time_between_callbacks_ = base::TimeDelta::FromMicroseconds( - params_.frames_per_buffer() * base::Time::kMicrosecondsPerSecond / - static_cast<float>(params_.sample_rate())); - } - - ~FakeAudioWorkerTest() override {} - - void CalledByFakeWorker() { - seen_callbacks_++; - } - - void RunOnAudioThread() { - ASSERT_TRUE(message_loop_.task_runner()->BelongsToCurrentThread()); - fake_worker_.Start(base::Bind( - &FakeAudioWorkerTest::CalledByFakeWorker, base::Unretained(this))); - } - - void RunOnceOnAudioThread() { - ASSERT_TRUE(message_loop_.task_runner()->BelongsToCurrentThread()); - RunOnAudioThread(); - // Start() should immediately post a task to run the callback, so we - // should end up with only a single callback being run. - message_loop_.task_runner()->PostTask( - FROM_HERE, - base::Bind(&FakeAudioWorkerTest::EndTest, base::Unretained(this), 1)); - } - - void StopStartOnAudioThread() { - ASSERT_TRUE(message_loop_.task_runner()->BelongsToCurrentThread()); - fake_worker_.Stop(); - RunOnAudioThread(); - } - - void TimeCallbacksOnAudioThread(int callbacks) { - ASSERT_TRUE(message_loop_.task_runner()->BelongsToCurrentThread()); - - if (seen_callbacks_ == 0) { - RunOnAudioThread(); - start_time_ = base::TimeTicks::Now(); - } - - // Keep going until we've seen the requested number of callbacks. - if (seen_callbacks_ < callbacks) { - message_loop_.task_runner()->PostDelayedTask( - FROM_HERE, - base::Bind(&FakeAudioWorkerTest::TimeCallbacksOnAudioThread, - base::Unretained(this), callbacks), - time_between_callbacks_ / 2); - } else { - end_time_ = base::TimeTicks::Now(); - EndTest(callbacks); - } - } - - void EndTest(int callbacks) { - ASSERT_TRUE(message_loop_.task_runner()->BelongsToCurrentThread()); - fake_worker_.Stop(); - EXPECT_LE(callbacks, seen_callbacks_); - message_loop_.task_runner()->PostTask( - FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); - } - - protected: - base::MessageLoop message_loop_; - AudioParameters params_; - FakeAudioWorker fake_worker_; - base::TimeTicks start_time_; - base::TimeTicks end_time_; - base::TimeDelta time_between_callbacks_; - int seen_callbacks_; - - private: - DISALLOW_COPY_AND_ASSIGN(FakeAudioWorkerTest); -}; - -// Ensure the worker runs on the audio thread and fires callbacks. -TEST_F(FakeAudioWorkerTest, FakeBasicCallback) { - message_loop_.task_runner()->PostTask( - FROM_HERE, base::Bind(&FakeAudioWorkerTest::RunOnceOnAudioThread, - base::Unretained(this))); - base::RunLoop().Run(); -} - -// Ensure the time between callbacks is sane. -TEST_F(FakeAudioWorkerTest, TimeBetweenCallbacks) { - message_loop_.task_runner()->PostTask( - FROM_HERE, base::Bind(&FakeAudioWorkerTest::TimeCallbacksOnAudioThread, - base::Unretained(this), kTestCallbacks)); - base::RunLoop().Run(); - - // There are only (kTestCallbacks - 1) intervals between kTestCallbacks. - base::TimeDelta actual_time_between_callbacks = - (end_time_ - start_time_) / (seen_callbacks_ - 1); - - // Ensure callback time is no faster than the expected time between callbacks. - EXPECT_GE(actual_time_between_callbacks, time_between_callbacks_); - - // Softly check if the callback time is no slower than twice the expected time - // between callbacks. Since this test runs on the bots we can't be too strict - // with the bounds. - if (actual_time_between_callbacks > 2 * time_between_callbacks_) - LOG(ERROR) << "Time between fake audio callbacks is too large!"; -} - -// Ensure Start()/Stop() on the worker doesn't generate too many callbacks. See -// http://crbug.com/159049. -TEST_F(FakeAudioWorkerTest, StartStopClearsCallbacks) { - message_loop_.task_runner()->PostTask( - FROM_HERE, base::Bind(&FakeAudioWorkerTest::TimeCallbacksOnAudioThread, - base::Unretained(this), kTestCallbacks)); - - // Issue a Stop() / Start() in between expected callbacks to maximize the - // chance of catching the worker doing the wrong thing. - message_loop_.task_runner()->PostDelayedTask( - FROM_HERE, base::Bind(&FakeAudioWorkerTest::StopStartOnAudioThread, - base::Unretained(this)), - time_between_callbacks_ / 2); - - // EndTest() will ensure the proper number of callbacks have occurred. - base::RunLoop().Run(); -} - -} // namespace media diff --git a/chromium/media/audio/fuchsia/audio_manager_fuchsia.cc b/chromium/media/audio/fuchsia/audio_manager_fuchsia.cc index 661e6816d5f..7bc5d23315c 100644 --- a/chromium/media/audio/fuchsia/audio_manager_fuchsia.cc +++ b/chromium/media/audio/fuchsia/audio_manager_fuchsia.cc @@ -26,10 +26,6 @@ bool AudioManagerFuchsia::HasAudioInputDevices() { return false; } -void AudioManagerFuchsia::ShowAudioInputSettings() { - NOTIMPLEMENTED(); -} - void AudioManagerFuchsia::GetAudioInputDeviceNames( AudioDeviceNames* device_names) { device_names->clear(); diff --git a/chromium/media/audio/fuchsia/audio_manager_fuchsia.h b/chromium/media/audio/fuchsia/audio_manager_fuchsia.h index 9ab3a17860a..7ce59254984 100644 --- a/chromium/media/audio/fuchsia/audio_manager_fuchsia.h +++ b/chromium/media/audio/fuchsia/audio_manager_fuchsia.h @@ -18,7 +18,6 @@ class AudioManagerFuchsia : public AudioManagerBase { // Implementation of AudioManager. bool HasAudioOutputDevices() override; bool HasAudioInputDevices() override; - void ShowAudioInputSettings() override; void GetAudioInputDeviceNames(AudioDeviceNames* device_names) override; void GetAudioOutputDeviceNames(AudioDeviceNames* device_names) override; AudioParameters GetInputStreamParameters( diff --git a/chromium/media/audio/mac/audio_input_mac.cc b/chromium/media/audio/mac/audio_input_mac.cc index aebe146ea7e..106b03e8240 100644 --- a/chromium/media/audio/mac/audio_input_mac.cc +++ b/chromium/media/audio/mac/audio_input_mac.cc @@ -252,6 +252,8 @@ void PCMQueueInAudioInputStream::HandleInputBuffer( // TODO(dalecurtis): This is a HACK. Long term the AudioQueue path is going // away in favor of the AudioUnit based AUAudioInputStream(). Tracked by // http://crbug.com/161383. + // TODO(dalecurtis): Delete all this. It shouldn't be necessary now that we + // have a ring buffer and FIFO on the actual shared memory. base::TimeDelta elapsed = base::TimeTicks::Now() - last_fill_; const base::TimeDelta kMinDelay = base::TimeDelta::FromMilliseconds(5); if (elapsed < kMinDelay) { @@ -260,11 +262,19 @@ void PCMQueueInAudioInputStream::HandleInputBuffer( base::PlatformThread::Sleep(kMinDelay - elapsed); } + // TODO(dalecurtis): This should be updated to include the device latency, + // but really since Pepper (which ignores the delay value) is on the only + // one creating AUDIO_PCM_LINEAR input devices, it doesn't matter. + // https://lists.apple.com/archives/coreaudio-api/2017/Jul/msg00035.html + const base::TimeTicks capture_time = + start_time->mFlags & kAudioTimeStampHostTimeValid + ? base::TimeTicks::FromMachAbsoluteTime(start_time->mHostTime) + : base::TimeTicks::Now(); + uint8_t* audio_data = reinterpret_cast<uint8_t*>(audio_buffer->mAudioData); - audio_bus_->FromInterleaved( - audio_data, audio_bus_->frames(), format_.mBitsPerChannel / 8); - callback_->OnData( - this, audio_bus_.get(), audio_buffer->mAudioDataByteSize, 0.0); + audio_bus_->FromInterleaved(audio_data, audio_bus_->frames(), + format_.mBitsPerChannel / 8); + callback_->OnData(this, audio_bus_.get(), capture_time, 0.0); last_fill_ = base::TimeTicks::Now(); } 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 f0dc947ce77..c112138bd06 100644 --- a/chromium/media/audio/mac/audio_low_latency_input_mac.cc +++ b/chromium/media/audio/mac/audio_low_latency_input_mac.cc @@ -18,6 +18,7 @@ #include "base/trace_event/trace_event.h" #include "media/audio/mac/audio_manager_mac.h" #include "media/base/audio_bus.h" +#include "media/base/audio_timestamp_helper.h" #include "media/base/data_buffer.h" namespace media { @@ -229,7 +230,6 @@ AUAudioInputStream::AUAudioInputStream( sink_(nullptr), audio_unit_(0), input_device_id_(audio_device_id), - hardware_latency_frames_(0), number_of_channels_in_frame_(0), fifo_(input_params.channels(), number_of_frames_, @@ -485,7 +485,7 @@ bool AUAudioInputStream::Open() { } // The hardware latency is fixed and will not change during the call. - hardware_latency_frames_ = GetHardwareLatency(); + hardware_latency_ = GetHardwareLatency(); // The master channel is 0, Left and right are channels 1 and 2. // And the master channel is not counted in |number_of_channels_in_frame_|. @@ -901,8 +901,7 @@ OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames, if (number_of_frames != number_of_frames_ && number_of_frames_provided_ == 0) number_of_frames_provided_ = number_of_frames; - // Update the capture latency. - double capture_latency_frames = GetCaptureLatency(time_stamp); + base::TimeTicks capture_time = GetCaptureTime(time_stamp); // The AGC volume level is updated once every second on a separate thread. // Note that, |volume| is also updated each time SetVolume() is called @@ -912,8 +911,6 @@ OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames, AudioBuffer& buffer = io_data->mBuffers[0]; uint8_t* audio_data = reinterpret_cast<uint8_t*>(buffer.mData); - uint32_t capture_delay_bytes = static_cast<uint32_t>( - (capture_latency_frames + 0.5) * format_.mBytesPerFrame); DCHECK(audio_data); if (!audio_data) return kAudioUnitErr_InvalidElement; @@ -936,6 +933,10 @@ OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames, fifo_.IncreaseCapacity(blocks); } + // Compensate the capture time for the FIFO before pushing an new frames. + capture_time -= AudioTimestampHelper::FramesToTime(fifo_.GetAvailableFrames(), + format_.mSampleRate); + // Copy captured (and interleaved) data into FIFO. fifo_.Push(audio_data, number_of_frames, format_.mBitsPerChannel / 8); @@ -944,9 +945,11 @@ OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames, const AudioBus* audio_bus = fifo_.Consume(); DCHECK_EQ(audio_bus->frames(), static_cast<int>(number_of_frames_)); - // Compensate the audio delay caused by the FIFO. - capture_delay_bytes += fifo_.GetAvailableFrames() * format_.mBytesPerFrame; - sink_->OnData(this, audio_bus, capture_delay_bytes, normalized_volume); + sink_->OnData(this, audio_bus, capture_time, normalized_volume); + + // Move the capture time forward for each vended block. + capture_time += AudioTimestampHelper::FramesToTime(audio_bus->frames(), + format_.mSampleRate); } return noErr; @@ -1056,10 +1059,10 @@ int AUAudioInputStream::HardwareSampleRate() { return static_cast<int>(nominal_sample_rate); } -double AUAudioInputStream::GetHardwareLatency() { +base::TimeDelta AUAudioInputStream::GetHardwareLatency() { if (!audio_unit_ || input_device_id_ == kAudioObjectUnknown) { DLOG(WARNING) << "Audio unit object is NULL or device ID is unknown"; - return 0.0; + return base::TimeDelta(); } // Get audio unit latency. @@ -1081,23 +1084,21 @@ double AUAudioInputStream::GetHardwareLatency() { nullptr, &size, &device_latency_frames); DLOG_IF(WARNING, result != noErr) << "Could not get audio device latency."; - return static_cast<double>((audio_unit_latency_sec * format_.mSampleRate) + - device_latency_frames); + return base::TimeDelta::FromSecondsD(audio_unit_latency_sec) + + AudioTimestampHelper::FramesToTime(device_latency_frames, + format_.mSampleRate); } -double AUAudioInputStream::GetCaptureLatency( +base::TimeTicks AUAudioInputStream::GetCaptureTime( const AudioTimeStamp* input_time_stamp) { - // Get the delay between between the actual recording instant and the time - // when the data packet is provided as a callback. - UInt64 capture_time_ns = - AudioConvertHostTimeToNanos(input_time_stamp->mHostTime); - UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); - double delay_frames = static_cast<double>(1e-9 * (now_ns - capture_time_ns) * - format_.mSampleRate); - // Total latency is composed by the dynamic latency and the fixed // hardware latency. - return (delay_frames + hardware_latency_frames_); + // https://lists.apple.com/archives/coreaudio-api/2017/Jul/msg00035.html + return (input_time_stamp->mFlags & kAudioTimeStampHostTimeValid + ? base::TimeTicks::FromMachAbsoluteTime( + input_time_stamp->mHostTime) + : base::TimeTicks::Now()) - + hardware_latency_; } int AUAudioInputStream::GetNumberOfChannelsFromStream() { diff --git a/chromium/media/audio/mac/audio_low_latency_input_mac.h b/chromium/media/audio/mac/audio_low_latency_input_mac.h index df2f7a73154..7d03db5b4a2 100644 --- a/chromium/media/audio/mac/audio_low_latency_input_mac.h +++ b/chromium/media/audio/mac/audio_low_latency_input_mac.h @@ -138,10 +138,10 @@ class MEDIA_EXPORT AUAudioInputStream // Gets the fixed capture hardware latency and store it during initialization. // Returns 0 if not available. - double GetHardwareLatency(); + base::TimeDelta GetHardwareLatency(); - // Gets the current capture delay value. - double GetCaptureLatency(const AudioTimeStamp* input_time_stamp); + // Gets the current capture time. + base::TimeTicks GetCaptureTime(const AudioTimeStamp* input_time_stamp); // Gets the number of channels for a stream of audio data. int GetNumberOfChannelsFromStream(); @@ -220,8 +220,8 @@ class MEDIA_EXPORT AUAudioInputStream // array as soon as a frame of the desired buffer size has been recorded. std::unique_ptr<uint8_t[]> audio_data_buffer_; - // Fixed capture hardware latency in frames. - double hardware_latency_frames_; + // Fixed capture hardware latency. + base::TimeDelta hardware_latency_; // The number of channels in each frame of audio data, which is used // when querying the volume of each channel. 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 6002e2030e0..e54af7c3f1e 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 @@ -43,7 +43,7 @@ class MockAudioInputCallback : public AudioInputStream::AudioInputCallback { MOCK_METHOD4(OnData, void(AudioInputStream* stream, const AudioBus* src, - uint32_t hardware_delay_bytes, + base::TimeTicks capture_time, double volume)); MOCK_METHOD1(OnError, void(AudioInputStream* stream)); }; @@ -86,7 +86,7 @@ class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback { // AudioInputStream::AudioInputCallback implementation. void OnData(AudioInputStream* stream, const AudioBus* src, - uint32_t hardware_delay_bytes, + base::TimeTicks capture_time, double volume) override { const int num_samples = src->frames() * src->channels(); std::unique_ptr<int16_t> interleaved(new int16_t[num_samples]); @@ -217,8 +217,8 @@ TEST_F(MacAudioInputTest, AUAudioInputStreamVerifyMonoRecording) { base::RunLoop run_loop; EXPECT_CALL(sink, OnData(ais, NotNull(), _, _)) .Times(AtLeast(10)) - .WillRepeatedly(CheckCountAndPostQuitTask( - &count, 10, &message_loop_, run_loop.QuitClosure())); + .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &message_loop_, + run_loop.QuitClosure())); ais->Start(&sink); run_loop.Run(); ais->Stop(); @@ -252,8 +252,8 @@ TEST_F(MacAudioInputTest, AUAudioInputStreamVerifyStereoRecording) { base::RunLoop run_loop; EXPECT_CALL(sink, OnData(ais, NotNull(), _, _)) .Times(AtLeast(10)) - .WillRepeatedly(CheckCountAndPostQuitTask( - &count, 10, &message_loop_, run_loop.QuitClosure())); + .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &message_loop_, + run_loop.QuitClosure())); ais->Start(&sink); run_loop.Run(); ais->Stop(); diff --git a/chromium/media/audio/mac/audio_manager_mac.cc b/chromium/media/audio/mac/audio_manager_mac.cc index 8478e0f70c5..7e406a2ee2f 100644 --- a/chromium/media/audio/mac/audio_manager_mac.cc +++ b/chromium/media/audio/mac/audio_manager_mac.cc @@ -10,6 +10,7 @@ #include "base/bind.h" #include "base/command_line.h" +#include "base/feature_list.h" #include "base/mac/mac_logging.h" #include "base/mac/scoped_cftyperef.h" #include "base/macros.h" @@ -22,6 +23,7 @@ #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" +#include "media/audio/mac/coreaudio_dispatch_override.h" #include "media/audio/mac/scoped_audio_unit.h" #include "media/base/audio_parameters.h" #include "media/base/bind_to_current_loop.h" @@ -529,6 +531,27 @@ void AudioManagerMac::ShutdownOnAudioThread() { // and IncreaseIOBufferSizeIfPossible() which both touches native Core Audio // APIs and they can fail and disrupt tests during shutdown. in_shutdown_ = true; + + // Even if tasks to close the streams are enqueued, they would not run + // leading to CHECKs getting hit in the destructor about open streams. Close + // them explicitly here. crbug.com/608049. + for (auto iter = basic_input_streams_.begin(); + iter != basic_input_streams_.end();) { + // Note: Closing the stream will invalidate the iterator. + // Increment the iterator before closing the stream. + AudioInputStream* stream = *iter++; + stream->Close(); + } + for (auto iter = low_latency_input_streams_.begin(); + iter != low_latency_input_streams_.end();) { + // Note: Closing the stream will invalidate the iterator. + // Increment the iterator before closing the stream. + AudioInputStream* stream = *iter++; + stream->Close(); + } + CHECK(basic_input_streams_.empty()); + CHECK(low_latency_input_streams_.empty()); + AudioManagerBase::ShutdownOnAudioThread(); } @@ -870,6 +893,8 @@ AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters( void AudioManagerMac::InitializeOnAudioThread() { DCHECK(GetTaskRunner()->BelongsToCurrentThread()); + if (base::FeatureList::IsEnabled(kSerializeCoreAudioPauseResume)) + InitializeCoreAudioDispatchOverride(); power_observer_.reset(new AudioPowerObserver()); } diff --git a/chromium/media/audio/mac/audio_manager_mac.h b/chromium/media/audio/mac/audio_manager_mac.h index 0dbaec35cb5..80ceeb19150 100644 --- a/chromium/media/audio/mac/audio_manager_mac.h +++ b/chromium/media/audio/mac/audio_manager_mac.h @@ -84,7 +84,7 @@ class MEDIA_EXPORT AudioManagerMac : public AudioManagerBase { // Streams should consult ShouldDeferStreamStart() and if true check the value // again after |kStartDelayInSecsForPowerEvents| has elapsed. If false, the // stream may be started immediately. - // TOOD(henrika): track UMA statistics related to defer start to come up with + // TODO(henrika): track UMA statistics related to defer start to come up with // a suitable delay value. enum { kStartDelayInSecsForPowerEvents = 5 }; bool ShouldDeferStreamStart() const; diff --git a/chromium/media/audio/mac/coreaudio_dispatch_override.cc b/chromium/media/audio/mac/coreaudio_dispatch_override.cc new file mode 100644 index 00000000000..0bd1fc5f3f9 --- /dev/null +++ b/chromium/media/audio/mac/coreaudio_dispatch_override.cc @@ -0,0 +1,147 @@ +// Copyright 2017 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/mac/coreaudio_dispatch_override.h" + +#include <dispatch/dispatch.h> +#include <dlfcn.h> +#include <mach-o/loader.h> + +#include "base/atomicops.h" +#include "base/logging.h" +#include "base/mac/mac_util.h" + +namespace { +struct dyld_interpose_tuple { + template <typename T> + dyld_interpose_tuple(const T* replacement, const T* replacee) + : replacement(reinterpret_cast<const void*>(replacement)), + replacee(reinterpret_cast<const void*>(replacee)) {} + const void* replacement; + const void* replacee; +}; +} // namespace + +// This method, and the tuple above, is defined in dyld_priv.h; see: +// https://github.com/opensource-apple/dyld/blob/master/include/mach-o/dyld_priv.h +extern "C" void dyld_dynamic_interpose( + const struct mach_header* mh, + const struct dyld_interpose_tuple array[], + size_t count) __attribute__((weak_import)); + +namespace media { +namespace { +const char kCoreAudioPath[] = + "/System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio"; + +dispatch_queue_t g_pause_resume_queue = nullptr; +bool g_dispatch_override_installed = false; +base::subtle::AtomicWord g_resumeio_callsite = 0; +base::subtle::AtomicWord g_pauseio_callsite = 0; + +bool AddressIsPauseOrResume(intptr_t address) { + if (address == 0) + return false; + + intptr_t resumeio_callsite = + base::subtle::NoBarrier_Load(&g_resumeio_callsite); + + if (address == resumeio_callsite) + return true; + + intptr_t pauseio_callsite = base::subtle::NoBarrier_Load(&g_pauseio_callsite); + if (address == pauseio_callsite) + return true; + + if (resumeio_callsite && pauseio_callsite) + return false; + + // We don't know both callsites yet, so try to look up the caller. + Dl_info info; + if (!dladdr(reinterpret_cast<const void*>(address), &info)) + return false; + + DCHECK_EQ(strcmp(info.dli_fname, kCoreAudioPath), 0); + + if (!resumeio_callsite && info.dli_sname && + strcmp(info.dli_sname, "HALC_IOContext_ResumeIO") == 0) { + resumeio_callsite = address; + base::subtle::NoBarrier_CompareAndSwap(&g_resumeio_callsite, 0, + resumeio_callsite); + } + if (!pauseio_callsite && info.dli_sname && + strcmp(info.dli_sname, "HALC_IOContext_PauseIO") == 0) { + pauseio_callsite = address; + base::subtle::NoBarrier_CompareAndSwap(&g_pauseio_callsite, 0, + pauseio_callsite); + } + + return address == pauseio_callsite || address == resumeio_callsite; +} + +dispatch_queue_t GetGlobalQueueOverride(long identifier, unsigned long flags) { + // Get the return address. + const intptr_t* rbp = 0; + asm("movq %%rbp, %0;" : "=r"(rbp)); + const intptr_t caller = rbp[1]; + + // Check if it's one we should override. + if (identifier == DISPATCH_QUEUE_PRIORITY_HIGH && + AddressIsPauseOrResume(caller)) { + return g_pause_resume_queue; + } + + return dispatch_get_global_queue(identifier, flags); +} + +} // namespace + +bool InitializeCoreAudioDispatchOverride() { + if (g_dispatch_override_installed) + return true; + + DCHECK_EQ(g_pause_resume_queue, nullptr); + + if (!base::mac::IsAtLeastOS10_10()) { + return false; + } + + // This function should be available in macOS > 10.10. + if (dyld_dynamic_interpose == nullptr) { + LOG(ERROR) << "Unable to resolve dyld_dynamic_interpose()"; + return false; + } + // Get CoreAudio handle + void* coreaudio = dlopen(kCoreAudioPath, RTLD_LAZY); + if (!coreaudio) { + LOG(ERROR) << "Could not load CoreAudio while trying to initialize " + "dispatch override"; + return false; + } + // Retrieve the base address (also address of Mach header). For this + // we need any external symbol to look up. + const void* symbol = dlsym(coreaudio, "AudioObjectGetPropertyData"); + if (!symbol) { + LOG(ERROR) << "Unable to resolve AudioObjectGetPropertyData in " + "CoreAudio library"; + return false; + } + // From the address of that symbol, we can get the address of the library's + // header. + Dl_info info = {}; + if (!dladdr(symbol, &info)) { + LOG(ERROR) << "Unable to find Mach header for CoreAudio library."; + return false; + } + const auto* header = reinterpret_cast<const mach_header*>(info.dli_fbase); + g_pause_resume_queue = + dispatch_queue_create("org.chromium.CoreAudioPauseResumeQueue", nullptr); + dyld_interpose_tuple interposition(&GetGlobalQueueOverride, + &dispatch_get_global_queue); + dyld_dynamic_interpose(header, &interposition, 1); + g_dispatch_override_installed = true; + return true; +} + +} // namespace media diff --git a/chromium/media/audio/mac/coreaudio_dispatch_override.h b/chromium/media/audio/mac/coreaudio_dispatch_override.h new file mode 100644 index 00000000000..efd9f8c12b2 --- /dev/null +++ b/chromium/media/audio/mac/coreaudio_dispatch_override.h @@ -0,0 +1,29 @@ +// Copyright 2017 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_MAC_COREAUDIO_DISPATCH_OVERRIDE_H_ +#define MEDIA_AUDIO_MAC_COREAUDIO_DISPATCH_OVERRIDE_H_ + +namespace media { +// Initializes a CoreAudio hotfix, if supported (macOS >= 10.10). +// See: http://crbug.com/772410 +// The hotfix overrides calls to dispatch_get_global_queue() from two CoreAudio +// functions: HALC_IOContext_PauseIO and HALC_IOContext_ResumeIO. These dispatch +// blocks that should execute in-order, but the global queue does not guarantee +// this. When the calls execute out-of-order, we stop receiving callbacks for +// audio streams on one or more devices. +// +// To circumvent this problem, these two functions get handed an internal serial +// queue instead. For all other callers, the override will just defer to the +// normal dispatch_get_global_queue() implementation. +// +// Calls to this function must be serialized. Will do nothing if called when +// already initialized. +// +// Returns true if the hotfix is supported and initialization succeeded, or if +// it was already initialized; false otherwise. +bool InitializeCoreAudioDispatchOverride(); +} // namespace media + +#endif // MEDIA_AUDIO_MAC_COREAUDIO_DISPATCH_OVERRIDE_H_ diff --git a/chromium/media/audio/mock_audio_manager.cc b/chromium/media/audio/mock_audio_manager.cc index d05921c846c..74fe7fee7c7 100644 --- a/chromium/media/audio/mock_audio_manager.cc +++ b/chromium/media/audio/mock_audio_manager.cc @@ -34,9 +34,6 @@ base::string16 MockAudioManager::GetAudioInputDeviceModel() { return base::string16(); } -void MockAudioManager::ShowAudioInputSettings() { -} - void MockAudioManager::GetAudioInputDeviceDescriptions( AudioDeviceDescriptions* device_descriptions) { DCHECK(GetTaskRunner()->BelongsToCurrentThread()); diff --git a/chromium/media/audio/mock_audio_manager.h b/chromium/media/audio/mock_audio_manager.h index 18cc3c9fb4a..0d7b19e5ecb 100644 --- a/chromium/media/audio/mock_audio_manager.h +++ b/chromium/media/audio/mock_audio_manager.h @@ -76,8 +76,6 @@ class MockAudioManager : public AudioManager { base::string16 GetAudioInputDeviceModel() override; - void ShowAudioInputSettings() override; - void GetAudioInputDeviceDescriptions( media::AudioDeviceDescriptions* device_descriptions) override; diff --git a/chromium/media/audio/null_audio_sink.cc b/chromium/media/audio/null_audio_sink.cc index 151f387e08b..36bc8d18918 100644 --- a/chromium/media/audio/null_audio_sink.cc +++ b/chromium/media/audio/null_audio_sink.cc @@ -7,8 +7,8 @@ #include "base/bind.h" #include "base/location.h" #include "base/single_thread_task_runner.h" -#include "media/audio/fake_audio_worker.h" #include "media/base/audio_hash.h" +#include "media/base/fake_audio_worker.h" namespace media { diff --git a/chromium/media/audio/null_audio_sink.h b/chromium/media/audio/null_audio_sink.h index 84e0263ea3d..9a2eb647d1d 100644 --- a/chromium/media/audio/null_audio_sink.h +++ b/chromium/media/audio/null_audio_sink.h @@ -20,8 +20,7 @@ class AudioBus; class AudioHash; class FakeAudioWorker; -class MEDIA_EXPORT NullAudioSink - : NON_EXPORTED_BASE(public SwitchableAudioRendererSink) { +class MEDIA_EXPORT NullAudioSink : public SwitchableAudioRendererSink { public: NullAudioSink(const scoped_refptr<base::SingleThreadTaskRunner>& task_runner); diff --git a/chromium/media/audio/pulse/audio_manager_pulse.cc b/chromium/media/audio/pulse/audio_manager_pulse.cc index 6fa330f0e82..8cd8711be09 100644 --- a/chromium/media/audio/pulse/audio_manager_pulse.cc +++ b/chromium/media/audio/pulse/audio_manager_pulse.cc @@ -9,9 +9,6 @@ #include "base/logging.h" #include "base/nix/xdg_util.h" #include "base/stl_util.h" -#if defined(USE_ALSA) -#include "media/audio/alsa/audio_manager_alsa.h" -#endif #include "media/audio/audio_device_description.h" #include "media/audio/pulse/pulse_input.h" #include "media/audio/pulse/pulse_output.h" @@ -70,12 +67,6 @@ bool AudioManagerPulse::HasAudioInputDevices() { return !devices.empty(); } -void AudioManagerPulse::ShowAudioInputSettings() { -#if defined(USE_ALSA) - AudioManagerAlsa::ShowLinuxAudioInputSettings(); -#endif -} - void AudioManagerPulse::GetAudioDeviceNames( bool input, media::AudioDeviceNames* device_names) { DCHECK(device_names->empty()); diff --git a/chromium/media/audio/pulse/audio_manager_pulse.h b/chromium/media/audio/pulse/audio_manager_pulse.h index bb1d1c08fae..316eec4a534 100644 --- a/chromium/media/audio/pulse/audio_manager_pulse.h +++ b/chromium/media/audio/pulse/audio_manager_pulse.h @@ -26,7 +26,6 @@ class MEDIA_EXPORT AudioManagerPulse : public AudioManagerBase { // Implementation of AudioManager. bool HasAudioOutputDevices() override; bool HasAudioInputDevices() override; - void ShowAudioInputSettings() override; void GetAudioInputDeviceNames(AudioDeviceNames* device_names) override; void GetAudioOutputDeviceNames(AudioDeviceNames* device_names) override; AudioParameters GetInputStreamParameters( diff --git a/chromium/media/audio/pulse/pulse_input.cc b/chromium/media/audio/pulse/pulse_input.cc index b27fb65694c..8bf943f1319 100644 --- a/chromium/media/audio/pulse/pulse_input.cc +++ b/chromium/media/audio/pulse/pulse_input.cc @@ -10,6 +10,7 @@ #include "media/audio/audio_device_description.h" #include "media/audio/pulse/audio_manager_pulse.h" #include "media/audio/pulse/pulse_util.h" +#include "media/base/audio_timestamp_helper.h" namespace media { @@ -270,9 +271,6 @@ void PulseAudioInputStream::StreamNotifyCallback(pa_stream* s, } void PulseAudioInputStream::ReadData() { - uint32_t hardware_delay = pulse::GetHardwareLatencyInBytes( - handle_, params_.sample_rate(), params_.GetBytesPerFrame()); - // Update the AGC volume level once every second. Note that, // |volume| is also updated each time SetVolume() is called // through IPC by the render-side AGC. @@ -282,6 +280,14 @@ void PulseAudioInputStream::ReadData() { GetAgcVolume(&normalized_volume); normalized_volume = volume_ / GetMaxVolume(); + // Compensate the audio delay caused by the FIFO. + // TODO(dalecurtis): This should probably use pa_stream_get_time() so we can + // get the capture time directly. + base::TimeTicks capture_time = + base::TimeTicks::Now() - + (pulse::GetHardwareLatency(handle_) + + AudioTimestampHelper::FramesToTime(fifo_.GetAvailableFrames(), + params_.sample_rate())); do { size_t length = 0; const void* data = NULL; @@ -308,12 +314,16 @@ void PulseAudioInputStream::ReadData() { while (fifo_.available_blocks()) { const AudioBus* audio_bus = fifo_.Consume(); - // Compensate the audio delay caused by the FIFO. - hardware_delay += fifo_.GetAvailableFrames() * params_.GetBytesPerFrame(); - callback_->OnData(this, audio_bus, hardware_delay, normalized_volume); + callback_->OnData(this, audio_bus, capture_time, normalized_volume); + + // Move the capture time forward for each vended block. + capture_time += AudioTimestampHelper::FramesToTime(audio_bus->frames(), + params_.sample_rate()); // Sleep 5ms to wait until render consumes the data in order to avoid // back to back OnData() method. + // TODO(dalecurtis): Delete all this. It shouldn't be necessary now that we + // have a ring buffer and FIFO on the actual shared memory., if (fifo_.available_blocks()) base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(5)); } diff --git a/chromium/media/audio/pulse/pulse_util.cc b/chromium/media/audio/pulse/pulse_util.cc index 438d4c8d68d..dc7a1882ad2 100644 --- a/chromium/media/audio/pulse/pulse_util.cc +++ b/chromium/media/audio/pulse/pulse_util.cc @@ -256,15 +256,6 @@ base::TimeDelta GetHardwareLatency(pa_stream* stream) { return base::TimeDelta::FromMicroseconds(latency_micros); } -int GetHardwareLatencyInBytes(pa_stream* stream, - int sample_rate, - int bytes_per_frame) { - DCHECK(stream); - return AudioTimestampHelper::TimeToFrames(GetHardwareLatency(stream), - sample_rate) * - bytes_per_frame; -} - // Helper macro for CreateInput/OutputStream() to avoid code spam and // string bloat. #define RETURN_ON_FAILURE(expression, message) do { \ diff --git a/chromium/media/audio/pulse/pulse_util.h b/chromium/media/audio/pulse/pulse_util.h index a39ebbfa0ff..2edb432a8d6 100644 --- a/chromium/media/audio/pulse/pulse_util.h +++ b/chromium/media/audio/pulse/pulse_util.h @@ -54,10 +54,6 @@ void WaitForOperationCompletion(pa_threaded_mainloop* mainloop, base::TimeDelta GetHardwareLatency(pa_stream* stream); -int GetHardwareLatencyInBytes(pa_stream* stream, - int sample_rate, - int bytes_per_frame); - // Create a recording stream for the threaded mainloop, return true if success, // otherwise false. |mainloop| and |context| have to be from a valid Pulse // threaded mainloop and the handle of the created stream will be returned by diff --git a/chromium/media/audio/sample_rates.cc b/chromium/media/audio/sample_rates.cc deleted file mode 100644 index 9853ef3aac5..00000000000 --- a/chromium/media/audio/sample_rates.cc +++ /dev/null @@ -1,57 +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/sample_rates.h" - -#include "base/logging.h" - -namespace media { - -bool ToAudioSampleRate(int sample_rate, AudioSampleRate* asr) { - DCHECK(asr); - switch (sample_rate) { - case 8000: - *asr = k8000Hz; - return true; - case 16000: - *asr = k16000Hz; - return true; - case 24000: - *asr = k24000Hz; - return true; - case 32000: - *asr = k32000Hz; - return true; - case 48000: - *asr = k48000Hz; - return true; - case 96000: - *asr = k96000Hz; - return true; - case 11025: - *asr = k11025Hz; - return true; - case 22050: - *asr = k22050Hz; - return true; - case 44100: - *asr = k44100Hz; - return true; - case 88200: - *asr = k88200Hz; - return true; - case 176400: - *asr = k176400Hz; - return true; - case 192000: - *asr = k192000Hz; - return true; - case 384000: - *asr = k384000Hz; - return true; - } - return false; -} - -} // namespace media diff --git a/chromium/media/audio/sample_rates.h b/chromium/media/audio/sample_rates.h deleted file mode 100644 index fdc37c26143..00000000000 --- a/chromium/media/audio/sample_rates.h +++ /dev/null @@ -1,38 +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_SAMPLE_RATES_H_ -#define MEDIA_AUDIO_SAMPLE_RATES_H_ - -#include "media/base/media_export.h" - -namespace media { - -// Enumeration used for histogramming sample rates into distinct buckets. -// Logged to UMA, so never reuse a value, always add new/greater ones! -enum AudioSampleRate { - k8000Hz = 0, - k16000Hz = 1, - k32000Hz = 2, - k48000Hz = 3, - k96000Hz = 4, - k11025Hz = 5, - k22050Hz = 6, - k44100Hz = 7, - k88200Hz = 8, - k176400Hz = 9, - k192000Hz = 10, - k24000Hz = 11, - k384000Hz = 12, - // Must always equal the largest value ever reported: - kAudioSampleRateMax = k384000Hz, -}; - -// Helper method to convert integral values to their respective enum values, -// returns false for unexpected sample rates. -MEDIA_EXPORT bool ToAudioSampleRate(int sample_rate, AudioSampleRate* asr); - -} // namespace media - -#endif // MEDIA_AUDIO_SAMPLE_RATES_H_ diff --git a/chromium/media/audio/virtual_audio_input_stream.cc b/chromium/media/audio/virtual_audio_input_stream.cc index 1252805c0d9..fbe90bbacbc 100644 --- a/chromium/media/audio/virtual_audio_input_stream.cc +++ b/chromium/media/audio/virtual_audio_input_stream.cc @@ -111,7 +111,7 @@ void VirtualAudioInputStream::PumpAudio() { } // Because the audio is being looped-back, the delay since since it was // recorded is zero. - callback_->OnData(this, audio_bus_.get(), 0, 1.0); + callback_->OnData(this, audio_bus_.get(), base::TimeTicks::Now(), 1.0); } void VirtualAudioInputStream::Close() { diff --git a/chromium/media/audio/virtual_audio_input_stream.h b/chromium/media/audio/virtual_audio_input_stream.h index cb5b1a2ca81..b794c382595 100644 --- a/chromium/media/audio/virtual_audio_input_stream.h +++ b/chromium/media/audio/virtual_audio_input_stream.h @@ -15,9 +15,9 @@ #include "base/synchronization/lock.h" #include "base/threading/thread_checker.h" #include "media/audio/audio_io.h" -#include "media/audio/fake_audio_worker.h" #include "media/base/audio_converter.h" #include "media/base/audio_parameters.h" +#include "media/base/fake_audio_worker.h" namespace base { class SingleThreadTaskRunner; diff --git a/chromium/media/audio/virtual_audio_input_stream_unittest.cc b/chromium/media/audio/virtual_audio_input_stream_unittest.cc index 14382230c17..8adcffbfee5 100644 --- a/chromium/media/audio/virtual_audio_input_stream_unittest.cc +++ b/chromium/media/audio/virtual_audio_input_stream_unittest.cc @@ -37,8 +37,9 @@ class MockInputCallback : public AudioInputStream::AudioInputCallback { MockInputCallback() : data_pushed_(base::WaitableEvent::ResetPolicy::AUTOMATIC, base::WaitableEvent::InitialState::NOT_SIGNALED) { - ON_CALL(*this, OnData(_, _, _, _)).WillByDefault( - InvokeWithoutArgs(&data_pushed_, &base::WaitableEvent::Signal)); + ON_CALL(*this, OnData(_, _, _, _)) + .WillByDefault( + InvokeWithoutArgs(&data_pushed_, &base::WaitableEvent::Signal)); } virtual ~MockInputCallback() {} @@ -46,7 +47,7 @@ class MockInputCallback : public AudioInputStream::AudioInputCallback { MOCK_METHOD4(OnData, void(AudioInputStream* stream, const AudioBus* source, - uint32_t hardware_delay_bytes, + base::TimeTicks capture_time, double volume)); MOCK_METHOD1(OnError, void(AudioInputStream* stream)); 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 60f4865c30a..5d6fc41d27b 100644 --- a/chromium/media/audio/win/audio_low_latency_input_win.cc +++ b/chromium/media/audio/win/audio_low_latency_input_win.cc @@ -19,6 +19,7 @@ #include "media/audio/win/core_audio_util_win.h" #include "media/base/audio_block_fifo.h" #include "media/base/audio_bus.h" +#include "media/base/audio_timestamp_helper.h" #include "media/base/channel_layout.h" #include "media/base/limits.h" @@ -90,16 +91,6 @@ WASAPIAudioInputStream::WASAPIAudioInputStream(AudioManagerWin* manager, // Create the event which will be set in Stop() when capturing shall stop. stop_capture_event_.Set(CreateEvent(NULL, FALSE, FALSE, NULL)); DCHECK(stop_capture_event_.IsValid()); - - ms_to_frame_count_ = static_cast<double>(params.sample_rate()) / 1000.0; - - LARGE_INTEGER performance_frequency; - if (QueryPerformanceFrequency(&performance_frequency)) { - perf_count_to_100ns_units_ = - (10000000.0 / static_cast<double>(performance_frequency.QuadPart)); - } else { - DLOG(ERROR) << "High-resolution performance counters are not supported."; - } } WASAPIAudioInputStream::~WASAPIAudioInputStream() { @@ -371,7 +362,6 @@ void WASAPIAudioInputStream::Run() { DVLOG(1) << "AudioBlockFifo buffer count: " << buffers_required; - LARGE_INTEGER now_count = {}; bool recording = true; bool error = false; double volume = GetVolume(); @@ -380,6 +370,8 @@ void WASAPIAudioInputStream::Run() { base::win::ScopedComPtr<IAudioClock> audio_clock; audio_client_->GetService(IID_PPV_ARGS(&audio_clock)); + if (!audio_clock) + LOG(WARNING) << "IAudioClock unavailable, capture times may be inaccurate."; while (recording && !error) { HRESULT hr = S_FALSE; @@ -401,28 +393,47 @@ void WASAPIAudioInputStream::Run() { UINT32 num_frames_to_read = 0; DWORD flags = 0; UINT64 device_position = 0; - UINT64 first_audio_frame_timestamp = 0; + + // Note: The units on this are 100ns intervals. Both GetBuffer() and + // GetPosition() will handle the translation from the QPC value, so we + // just need to convert from 100ns units into us. Which is just dividing + // by 10.0 since 10x100ns = 1us. + UINT64 capture_time_100ns = 0; // Retrieve the amount of data in the capture endpoint buffer, // replace it with silence if required, create callbacks for each // packet and store non-delivered data for the next event. hr = audio_capture_client_->GetBuffer(&data_ptr, &num_frames_to_read, &flags, &device_position, - &first_audio_frame_timestamp); + &capture_time_100ns); if (FAILED(hr)) { DLOG(ERROR) << "Failed to get data from the capture buffer"; continue; } + // TODO(dalecurtis, olka): Is this ever false? if (audio_clock) { // The reported timestamp from GetBuffer is not as reliable as the // clock from the client. We've seen timestamps reported for // USB audio devices, be off by several days. Furthermore we've // seen them jump back in time every 2 seconds or so. - audio_clock->GetPosition(&device_position, - &first_audio_frame_timestamp); + audio_clock->GetPosition(&device_position, &capture_time_100ns); + } + + base::TimeTicks capture_time; + if (capture_time_100ns) { + // See conversion notes on |capture_time_100ns|. + capture_time += + base::TimeDelta::FromMicroseconds(capture_time_100ns / 10.0); + } else { + // We may not have an IAudioClock or GetPosition() may return zero. + capture_time = base::TimeTicks::Now(); } + // Adjust |capture_time| for the FIFO before pushing. + capture_time -= AudioTimestampHelper::FramesToTime( + fifo_->GetAvailableFrames(), format_.nSamplesPerSec); + if (num_frames_to_read != 0) { if (flags & AUDCLNT_BUFFERFLAGS_SILENT) { fifo_->PushSilence(num_frames_to_read); @@ -435,21 +446,6 @@ void WASAPIAudioInputStream::Run() { hr = audio_capture_client_->ReleaseBuffer(num_frames_to_read); DLOG_IF(ERROR, FAILED(hr)) << "Failed to release capture buffer"; - // Derive a delay estimate for the captured audio packet. - // The value contains two parts (A+B), where A is the delay of the - // first audio frame in the packet and B is the extra delay - // contained in any stored data. Unit is in audio frames. - QueryPerformanceCounter(&now_count); - // first_audio_frame_timestamp will be 0 if we didn't get a timestamp. - double audio_delay_frames = - first_audio_frame_timestamp == 0 - ? num_frames_to_read - : ((perf_count_to_100ns_units_ * now_count.QuadPart - - first_audio_frame_timestamp) / - 10000.0) * - ms_to_frame_count_ + - fifo_->GetAvailableFrames() - num_frames_to_read; - // Get a cached AGC volume level which is updated once every second // on the audio manager thread. Note that, |volume| is also updated // each time SetVolume() is called through IPC by the render-side AGC. @@ -457,7 +453,6 @@ void WASAPIAudioInputStream::Run() { // Deliver captured data to the registered consumer using a packet // size which was specified at construction. - uint32_t delay_frames = static_cast<uint32_t>(audio_delay_frames + 0.5); while (fifo_->available_blocks()) { if (converter_) { if (imperfect_buffer_size_conversion_ && @@ -466,18 +461,18 @@ void WASAPIAudioInputStream::Run() { // convert or else we'll suffer an underrun. break; } - converter_->ConvertWithDelay(delay_frames, convert_bus_.get()); - sink_->OnData(this, convert_bus_.get(), delay_frames * frame_size_, - volume); - } else { - sink_->OnData(this, fifo_->Consume(), delay_frames * frame_size_, - volume); - } + converter_->Convert(convert_bus_.get()); + sink_->OnData(this, convert_bus_.get(), capture_time, volume); - if (delay_frames > packet_size_frames_) { - delay_frames -= packet_size_frames_; + // Move the capture time forward for each vended block. + capture_time += AudioTimestampHelper::FramesToTime( + convert_bus_->frames(), format_.nSamplesPerSec); } else { - delay_frames = 0; + sink_->OnData(this, fifo_->Consume(), capture_time, volume); + + // Move the capture time forward for each vended block. + capture_time += AudioTimestampHelper::FramesToTime( + packet_size_frames_, format_.nSamplesPerSec); } } } break; @@ -676,7 +671,6 @@ bool WASAPIAudioInputStream::DesiredFormatIsSupported() { packet_size_frames_ = new_bytes_per_buffer / format_.nBlockAlign; packet_size_bytes_ = new_bytes_per_buffer; frame_size_ = format_.nBlockAlign; - ms_to_frame_count_ = static_cast<double>(format_.nSamplesPerSec) / 1000.0; imperfect_buffer_size_conversion_ = std::modf(new_frames_per_buffer, &new_frames_per_buffer) != 0.0; diff --git a/chromium/media/audio/win/audio_low_latency_input_win.h b/chromium/media/audio/win/audio_low_latency_input_win.h index 2cb36060994..752c45f905b 100644 --- a/chromium/media/audio/win/audio_low_latency_input_win.h +++ b/chromium/media/audio/win/audio_low_latency_input_win.h @@ -188,14 +188,6 @@ class MEDIA_EXPORT WASAPIAudioInputStream // device role and is not a valid ID as such. std::string device_id_; - // Conversion factor used in delay-estimation calculations. - // Converts a raw performance counter value to 100-nanosecond unit. - double perf_count_to_100ns_units_ = 0.0; - - // Conversion factor used in delay-estimation calculations. - // Converts from milliseconds to audio frames. - double ms_to_frame_count_ = 0.0; - // Pointer to the object that will receive the recorded audio samples. AudioInputCallback* sink_ = nullptr; 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 f7ddf9b40b4..e86476b2c2f 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 @@ -51,7 +51,7 @@ class MockAudioInputCallback : public AudioInputStream::AudioInputCallback { MOCK_METHOD4(OnData, void(AudioInputStream* stream, const AudioBus* src, - uint32_t hardware_delay_bytes, + base::TimeTicks capture_time, double volume)); MOCK_METHOD1(OnError, void(AudioInputStream* stream)); }; @@ -72,10 +72,9 @@ class FakeAudioInputCallback : public AudioInputStream::AudioInputCallback { void OnData(AudioInputStream* stream, const AudioBus* src, - uint32_t hardware_delay_bytes, + base::TimeTicks capture_time, double volume) override { - EXPECT_GE(hardware_delay_bytes, 0u); - EXPECT_LT(hardware_delay_bytes, 0xFFFFu); // Arbitrarily picked. + EXPECT_GE(capture_time, base::TimeTicks()); num_received_audio_frames_ += src->frames(); data_event_.Signal(); } @@ -132,7 +131,7 @@ class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback { // AudioInputStream::AudioInputCallback implementation. void OnData(AudioInputStream* stream, const AudioBus* src, - uint32_t hardware_delay_bytes, + base::TimeTicks capture_time, double volume) override { EXPECT_EQ(bits_per_sample_, 16); const int num_samples = src->frames() * src->channels(); diff --git a/chromium/media/audio/win/audio_manager_win.cc b/chromium/media/audio/win/audio_manager_win.cc index 6b96668c5d3..0443071e91f 100644 --- a/chromium/media/audio/win/audio_manager_win.cc +++ b/chromium/media/audio/win/audio_manager_win.cc @@ -16,11 +16,8 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/command_line.h" -#include "base/files/file_path.h" #include "base/message_loop/message_loop.h" #include "base/metrics/histogram_macros.h" -#include "base/path_service.h" -#include "base/process/launch.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/win/windows_version.h" @@ -122,13 +119,7 @@ static int NumberOfWaveOutBuffers() { 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; + return 3; } AudioManagerWin::AudioManagerWin(std::unique_ptr<AudioThread> audio_thread, @@ -243,15 +234,6 @@ base::string16 AudioManagerWin::GetAudioInputDeviceModel() { return base::string16(); } -void AudioManagerWin::ShowAudioInputSettings() { - base::FilePath path; - PathService::Get(base::DIR_SYSTEM, &path); - path = path.Append(L"control.exe"); - base::CommandLine command_line(path); - command_line.AppendArg("mmsys.cpl,,1"); - base::LaunchProcess(command_line, base::LaunchOptions()); -} - void AudioManagerWin::GetAudioDeviceNamesImpl(bool input, AudioDeviceNames* device_names) { DCHECK(device_names->empty()); diff --git a/chromium/media/audio/win/audio_manager_win.h b/chromium/media/audio/win/audio_manager_win.h index da15a39f645..3f65fe3d57f 100644 --- a/chromium/media/audio/win/audio_manager_win.h +++ b/chromium/media/audio/win/audio_manager_win.h @@ -28,7 +28,6 @@ class MEDIA_EXPORT AudioManagerWin : public AudioManagerBase { bool HasAudioOutputDevices() override; bool HasAudioInputDevices() override; base::string16 GetAudioInputDeviceModel() override; - void ShowAudioInputSettings() override; void GetAudioInputDeviceNames(AudioDeviceNames* device_names) override; void GetAudioOutputDeviceNames(AudioDeviceNames* device_names) override; AudioParameters GetInputStreamParameters( diff --git a/chromium/media/audio/win/audio_output_win_unittest.cc b/chromium/media/audio/win/audio_output_win_unittest.cc index 8231b11da90..24f0793ce0b 100644 --- a/chromium/media/audio/win/audio_output_win_unittest.cc +++ b/chromium/media/audio/win/audio_output_win_unittest.cc @@ -17,7 +17,6 @@ #include "base/sync_socket.h" #include "base/time/time.h" #include "base/win/scoped_com_initializer.h" -#include "base/win/windows_version.h" #include "media/audio/audio_device_info_accessor_for_tests.h" #include "media/audio/audio_io.h" #include "media/audio/audio_manager.h" @@ -452,11 +451,9 @@ TEST_F(WinAudioTest, PCMWaveStreamPlay200HzToneLowLatency) { audio_manager_device_info_->GetDefaultOutputStreamParameters(); int sample_rate = params.sample_rate(); uint32_t samples_10_ms = sample_rate / 100; - int n = 1; - (base::win::GetVersion() <= base::win::VERSION_XP) ? n = 5 : n = 1; AudioOutputStream* oas = audio_manager_->MakeAudioOutputStream( AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY, - CHANNEL_LAYOUT_MONO, sample_rate, 16, n * samples_10_ms), + CHANNEL_LAYOUT_MONO, sample_rate, 16, samples_10_ms), std::string(), AudioManager::LogCallback()); ASSERT_TRUE(NULL != oas); diff --git a/chromium/media/audio/win/core_audio_util_win.cc b/chromium/media/audio/win/core_audio_util_win.cc index 73edb83e7a9..b5871b81416 100644 --- a/chromium/media/audio/win/core_audio_util_win.cc +++ b/chromium/media/audio/win/core_audio_util_win.cc @@ -19,7 +19,6 @@ #include "base/win/scoped_handle.h" #include "base/win/scoped_propvariant.h" #include "base/win/scoped_variant.h" -#include "base/win/windows_version.h" #include "media/audio/audio_device_description.h" #include "media/base/media_switches.h" @@ -204,12 +203,6 @@ static bool IsSupportedInternal() { return false; } - // Microsoft does not plan to make the Core Audio APIs available for use - // with earlier versions of Windows, including Microsoft Windows Server 2003, - // Windows XP, Windows Millennium Edition, Windows 2000, and Windows 98. - if (base::win::GetVersion() < base::win::VERSION_VISTA) - return false; - // The audio core APIs are implemented in the Mmdevapi.dll and Audioses.dll // system components. // Dependency Walker shows that it is enough to verify possibility to load |