123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- // Copyright 2021 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 "services/audio/output_device_mixer_manager.h"
- #include "base/check.h"
- #include "base/logging.h"
- #include "base/memory/raw_ptr.h"
- #include "base/metrics/histogram_functions.h"
- #include "base/trace_event/trace_event.h"
- #include "media/audio/audio_io.h"
- #include "media/audio/audio_manager.h"
- #include "media/base/audio_latency.h"
- #include "services/audio/audio_manager_power_user.h"
- #include "services/audio/device_listener_output_stream.h"
- #include "services/audio/output_device_mixer.h"
- namespace {
- const char kNormalizedDefaultDeviceId[] = "";
- // Helper function which returns a consistent representation of the default
- // device ID.
- std::string NormalizeIfDefault(const std::string& device_id) {
- return media::AudioDeviceDescription::IsDefaultDevice(device_id)
- ? kNormalizedDefaultDeviceId
- : device_id;
- }
- // These values are persisted to logs. Entries should not be renumbered and
- // numeric values should never be reused.
- // Aligned with AudioOutputDeviceMixerManagerStreamCreation enum.
- enum class StreamCreation {
- kUnmixable,
- kFallbackToUnmixable,
- kUsingNewMixer,
- kUsingExistingMixer,
- kMaxValue = kUsingExistingMixer,
- };
- } // namespace
- namespace audio {
- OutputDeviceMixerManager::OutputDeviceMixerManager(
- media::AudioManager* audio_manager,
- OutputDeviceMixer::CreateCallback create_mixer_callback)
- : audio_manager_(audio_manager),
- current_default_device_id_(
- AudioManagerPowerUser(audio_manager_).GetDefaultOutputDeviceID()),
- current_communication_device_id_(AudioManagerPowerUser(audio_manager_)
- .GetCommunicationsOutputDeviceID()),
- create_mixer_callback_(std::move(create_mixer_callback)),
- device_change_weak_ptr_factory_(this) {
- // This should be a static_assert, but there is no compile time way to run
- // AudioDeviceDescription::IsDefaultDevice().
- DCHECK(media::AudioDeviceDescription::IsDefaultDevice(
- kNormalizedDefaultDeviceId));
- }
- OutputDeviceMixerManager::~OutputDeviceMixerManager() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
- }
- media::AudioOutputStream* OutputDeviceMixerManager::MakeOutputStream(
- const std::string& device_id,
- const media::AudioParameters& params,
- base::OnceClosure close_stream_on_device_change) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
- OutputDeviceMixer* mixer = nullptr;
- StreamCreation stream_creation = StreamCreation::kUnmixable;
- if (params.format() == media::AudioParameters::AUDIO_PCM_LOW_LATENCY) {
- std::string mixer_device_id = ToMixerDeviceId(device_id);
- mixer = FindMixer(mixer_device_id);
- if (mixer) {
- stream_creation = StreamCreation::kUsingExistingMixer;
- } else {
- mixer = AddMixer(mixer_device_id);
- stream_creation = mixer ? StreamCreation::kUsingNewMixer
- : StreamCreation::kFallbackToUnmixable;
- }
- }
- base::UmaHistogramEnumeration(
- "Media.Audio.OutputDeviceMixerManager.StreamCreation", stream_creation);
- if (mixer) {
- return mixer->MakeMixableStream(params,
- std::move(close_stream_on_device_change));
- }
- DLOG(WARNING) << "Making unmixable output stream";
- return CreateDeviceListenerStream(std::move(close_stream_on_device_change),
- device_id, params);
- }
- void OutputDeviceMixerManager::OnDeviceChange() {
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("audio"),
- "OutputDeviceMixerManager::OnDeviceChange");
- DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
- current_default_device_id_ =
- AudioManagerPowerUser(audio_manager_).GetDefaultOutputDeviceID();
- current_communication_device_id_ =
- AudioManagerPowerUser(audio_manager_).GetCommunicationsOutputDeviceID();
- // Invalidate WeakPtrs, cancelling any pending device change callbacks
- // generated by the same device change event.
- device_change_weak_ptr_factory_.InvalidateWeakPtrs();
- OutputDeviceMixers old_mixers;
- output_device_mixers_.swap(old_mixers);
- // Do not call StopListening(), as |old_mixers| are being destroyed anyways.
- for (auto&& mixer : old_mixers)
- mixer->ProcessDeviceChange();
- }
- void OutputDeviceMixerManager::StartNewListener(
- ReferenceOutput::Listener* listener,
- const std::string& listener_device_id) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
- DCHECK(IsNormalizedIfDefault(listener_device_id));
- DCHECK(!listener_registration_.contains(listener));
- listener_registration_[listener] = listener_device_id;
- OutputDeviceMixer* mixer = FindMixer(ToMixerDeviceId(listener_device_id));
- if (!mixer)
- return;
- mixer->StartListening(listener);
- }
- void OutputDeviceMixerManager::StartListening(
- ReferenceOutput::Listener* listener,
- const std::string& output_device_id) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
- std::string listener_device_id = NormalizeIfDefault(output_device_id);
- if (!listener_registration_.contains(listener)) {
- StartNewListener(listener, listener_device_id);
- return;
- }
- std::string registered_listener_device_id =
- listener_registration_.at(listener);
- if (ToMixerDeviceId(registered_listener_device_id) !=
- ToMixerDeviceId(listener_device_id)) {
- // |listener| is listening to a completely different mixer.
- StopListening(listener);
- StartNewListener(listener, listener_device_id);
- return;
- }
- // |listener| is listening to the right mixer, but we might need to update
- // its registration (e.g. when switching between
- // |current_default_device_id_| and kNormalizedDefaultId, or
- // |current_communications_device_id_| and kCommunicationsDeviceId).
- if (registered_listener_device_id != listener_device_id)
- listener_registration_[listener] = listener_device_id;
- }
- void OutputDeviceMixerManager::StopListening(
- ReferenceOutput::Listener* listener) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
- const std::string listener_device_id = listener_registration_.at(listener);
- listener_registration_.erase(listener);
- OutputDeviceMixer* mixer = FindMixer(ToMixerDeviceId(listener_device_id));
- // The mixer was never created, because there was no playback to that device
- // (possibly after a device device change). Listening never started, so there
- // is nothing to stop.
- if (!mixer)
- return;
- mixer->StopListening(listener);
- }
- std::string OutputDeviceMixerManager::ToMixerDeviceId(
- const std::string& device_id) {
- if (media::AudioDeviceDescription::IsDefaultDevice(device_id))
- return kNormalizedDefaultDeviceId;
- DCHECK(!device_id.empty());
- if (device_id == current_default_device_id_)
- return kNormalizedDefaultDeviceId;
- // It's possible for |current_communication_device_id_| and
- // |current_default_device_id_| to match. In that case, replace the
- // communications mixer device ID with the default mixer device ID.
- // Similarly, replace "communications" with kNormalizedDefaultDeviceId when
- // |current_communication_device_id_| is unsupported/unconfigured.
- if (device_id == media::AudioDeviceDescription::kCommunicationsDeviceId &&
- (current_communication_device_id_.empty() ||
- current_communication_device_id_ == current_default_device_id_)) {
- return kNormalizedDefaultDeviceId;
- }
- if (device_id == current_communication_device_id_)
- return media::AudioDeviceDescription::kCommunicationsDeviceId;
- return device_id;
- }
- OutputDeviceMixer* OutputDeviceMixerManager::FindMixer(
- const std::string& device_id) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
- DCHECK_EQ(ToMixerDeviceId(device_id), device_id);
- for (const auto& mixer : output_device_mixers_) {
- if (mixer->device_id() == device_id)
- return mixer.get();
- }
- return nullptr;
- }
- OutputDeviceMixer* OutputDeviceMixerManager::AddMixer(
- const std::string& device_id) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
- DCHECK_EQ(ToMixerDeviceId(device_id), device_id);
- DCHECK(!FindMixer(device_id));
- media::AudioParameters output_params =
- AudioManagerPowerUser(audio_manager_)
- .GetOutputStreamParameters(device_id);
- if (!output_params.IsValid()) {
- LOG(ERROR) << "Adding OutputDeviceMixer failed: invalid output parameters";
- return nullptr;
- }
- output_params.set_frames_per_buffer(media::AudioLatency::GetRtcBufferSize(
- output_params.sample_rate(), output_params.frames_per_buffer()));
- // TODO(crbug/1295658): Temporary work around. Mix all audio as stereo and
- // rely on the system channel mapping.
- if (output_params.channel_layout() == media::CHANNEL_LAYOUT_DISCRETE &&
- output_params.channels() >= 2) {
- output_params.Reset(output_params.format(), media::CHANNEL_LAYOUT_STEREO,
- output_params.sample_rate(),
- output_params.frames_per_buffer());
- }
- // base::Unretained(this) is safe here, because |output_device_mixers_|
- // are owned by |this|.
- std::unique_ptr<OutputDeviceMixer> output_device_mixer =
- create_mixer_callback_.Run(
- device_id, output_params,
- base::BindRepeating(&OutputDeviceMixerManager::CreateMixerOwnedStream,
- base::Unretained(this)),
- audio_manager_->GetTaskRunner());
- // The |device_id| might no longer be valid, e.g. if a device was unplugged.
- if (!output_device_mixer) {
- LOG(ERROR) << "Adding OutputDeviceMixer failed: creation error";
- return nullptr;
- }
- auto* mixer = output_device_mixer.get();
- output_device_mixers_.push_back(std::move(output_device_mixer));
- // Attach any interested listeners.
- for (auto&& listener_device_kvp : listener_registration_) {
- if (ToMixerDeviceId(listener_device_kvp.second) == mixer->device_id())
- mixer->StartListening(listener_device_kvp.first);
- }
- return mixer;
- }
- base::OnceClosure OutputDeviceMixerManager::GetOnDeviceChangeCallback() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
- return base::BindOnce(&OutputDeviceMixerManager::OnDeviceChange,
- device_change_weak_ptr_factory_.GetWeakPtr());
- }
- media::AudioOutputStream* OutputDeviceMixerManager::CreateMixerOwnedStream(
- const std::string& device_id,
- const media::AudioParameters& params) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
- return CreateDeviceListenerStream(GetOnDeviceChangeCallback(), device_id,
- params);
- }
- media::AudioOutputStream* OutputDeviceMixerManager::CreateDeviceListenerStream(
- base::OnceClosure on_device_change_callback,
- const std::string& device_id,
- const media::AudioParameters& params) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
- media::AudioOutputStream* stream =
- audio_manager_->MakeAudioOutputStreamProxy(params, device_id);
- if (!stream) {
- LOG(ERROR) << "Stream proxy limit reached";
- return nullptr;
- }
- // If this stream is created via CreateMixerOwnedStream(),
- // |on_device_change_callback| will call OnDeviceChange(), cancel pending
- // calls to OnDeviceChange(), and release all mixer owned streams.
- //
- // If we are directly creating this stream, |on_device_change_callback| will
- // synchronously close the returned stream.
- return new DeviceListenerOutputStream(audio_manager_, stream,
- std::move(on_device_change_callback));
- }
- bool OutputDeviceMixerManager::IsNormalizedIfDefault(
- const std::string& device_id) {
- return device_id == kNormalizedDefaultDeviceId ||
- !media::AudioDeviceDescription::IsDefaultDevice(device_id);
- }
- } // namespace audio
|