| // Copyright 2018 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 "content/browser/media/audio_output_stream_broker.h" |
| |
| #include <utility> |
| |
| #include "base/metrics/histogram_macros.h" |
| #include "base/trace_event/trace_event.h" |
| #include "content/browser/media/media_internals.h" |
| #include "content/public/browser/content_browser_client.h" |
| #include "content/public/browser/media_observer.h" |
| #include "content/public/common/content_client.h" |
| #include "media/audio/audio_logging.h" |
| |
| namespace content { |
| |
| AudioOutputStreamBroker::AudioOutputStreamBroker( |
| int render_process_id, |
| int render_frame_id, |
| int stream_id, |
| const std::string& output_device_id, |
| const media::AudioParameters& params, |
| const base::UnguessableToken& group_id, |
| const base::Optional<base::UnguessableToken>& processing_id, |
| DeleterCallback deleter, |
| media::mojom::AudioOutputStreamProviderClientPtr client) |
| : AudioStreamBroker(render_process_id, render_frame_id), |
| output_device_id_(output_device_id), |
| params_(params), |
| group_id_(group_id), |
| processing_id_(processing_id), |
| deleter_(std::move(deleter)), |
| client_(std::move(client)), |
| observer_(render_process_id, render_frame_id, stream_id), |
| observer_binding_(&observer_), |
| weak_ptr_factory_(this) { |
| DCHECK(client_); |
| DCHECK(deleter_); |
| DCHECK(group_id_); |
| TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("audio", "AudioOutputStreamBroker", this); |
| |
| MediaObserver* media_observer = |
| GetContentClient()->browser()->GetMediaObserver(); |
| |
| // May be null in unit tests. |
| if (media_observer) |
| media_observer->OnCreatingAudioStream(render_process_id, render_frame_id); |
| |
| // Unretained is safe because |this| owns |client_| |
| client_.set_connection_error_handler(base::BindOnce( |
| &AudioOutputStreamBroker::ClientBindingLost, base::Unretained(this))); |
| } |
| |
| AudioOutputStreamBroker::~AudioOutputStreamBroker() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_); |
| |
| if (awaiting_created_) { |
| TRACE_EVENT_NESTABLE_ASYNC_END1("audio", "CreateStream", this, "success", |
| "failed or cancelled"); |
| } |
| TRACE_EVENT_NESTABLE_ASYNC_END1("audio", "AudioOutputStreamBroker", this, |
| "disconnect reason", |
| static_cast<uint32_t>(disconnect_reason_)); |
| |
| UMA_HISTOGRAM_ENUMERATION("Media.Audio.Render.StreamBrokerDisconnectReason", |
| disconnect_reason_); |
| } |
| |
| void AudioOutputStreamBroker::CreateStream( |
| audio::mojom::StreamFactory* factory) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_); |
| DCHECK(!observer_binding_.is_bound()); |
| TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("audio", "CreateStream", this, "device id", |
| output_device_id_); |
| awaiting_created_ = true; |
| |
| // Set up observer ptr. Unretained is safe because |this| owns |
| // |observer_binding_|. |
| media::mojom::AudioOutputStreamObserverAssociatedPtrInfo ptr_info; |
| observer_binding_.Bind(mojo::MakeRequest(&ptr_info)); |
| observer_binding_.set_connection_error_with_reason_handler(base::BindOnce( |
| &AudioOutputStreamBroker::ObserverBindingLost, base::Unretained(this))); |
| |
| media::mojom::AudioOutputStreamPtr stream; |
| media::mojom::AudioOutputStreamRequest stream_request = |
| mojo::MakeRequest(&stream); |
| |
| // Note that the component id for AudioLog is used to differentiate between |
| // several users of the same audio log. Since this audio log is for a single |
| // stream, the component id used doesn't matter. |
| constexpr int log_component_id = 0; |
| factory->CreateOutputStream( |
| std::move(stream_request), std::move(ptr_info), |
| MediaInternals::GetInstance()->CreateMojoAudioLog( |
| media::AudioLogFactory::AudioComponent::AUDIO_OUTPUT_CONTROLLER, |
| log_component_id, render_process_id(), render_frame_id()), |
| output_device_id_, params_, group_id_, processing_id_, |
| base::BindOnce(&AudioOutputStreamBroker::StreamCreated, |
| weak_ptr_factory_.GetWeakPtr(), std::move(stream))); |
| } |
| |
| void AudioOutputStreamBroker::StreamCreated( |
| media::mojom::AudioOutputStreamPtr stream, |
| media::mojom::ReadWriteAudioDataPipePtr data_pipe) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_); |
| TRACE_EVENT_NESTABLE_ASYNC_END1("audio", "CreateStream", this, "success", |
| !!data_pipe); |
| awaiting_created_ = false; |
| if (!data_pipe) { |
| // Stream creation failed. Signal error. |
| client_.ResetWithReason( |
| static_cast<uint32_t>(media::mojom::AudioOutputStreamObserver:: |
| DisconnectReason::kPlatformError), |
| std::string()); |
| disconnect_reason_ = media::mojom::AudioOutputStreamObserver:: |
| DisconnectReason::kStreamCreationFailed; |
| Cleanup(); |
| return; |
| } |
| |
| client_->Created(std::move(stream), std::move(data_pipe)); |
| } |
| |
| void AudioOutputStreamBroker::ObserverBindingLost( |
| uint32_t reason, |
| const std::string& description) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_); |
| |
| TRACE_EVENT_NESTABLE_ASYNC_INSTANT1("audio", "ObserverBindingLost", this, |
| "reset reason", reason); |
| const uint32_t maxValidReason = static_cast<uint32_t>( |
| media::mojom::AudioOutputStreamObserver::DisconnectReason::kMaxValue); |
| if (reason > maxValidReason) { |
| NOTREACHED() << "Invalid reason: " << reason; |
| } else if (disconnect_reason_ == media::mojom::AudioOutputStreamObserver:: |
| DisconnectReason::kDocumentDestroyed) { |
| disconnect_reason_ = |
| static_cast<media::mojom::AudioOutputStreamObserver::DisconnectReason>( |
| reason); |
| } |
| |
| // TODO(https://crbug.com/787806): Don't propagate errors if we can retry |
| // instead. |
| client_.ResetWithReason( |
| static_cast<uint32_t>(media::mojom::AudioOutputStreamObserver:: |
| DisconnectReason::kPlatformError), |
| std::string()); |
| |
| Cleanup(); |
| } |
| |
| void AudioOutputStreamBroker::ClientBindingLost() { |
| disconnect_reason_ = media::mojom::AudioOutputStreamObserver:: |
| DisconnectReason::kTerminatedByClient; |
| Cleanup(); |
| } |
| |
| void AudioOutputStreamBroker::Cleanup() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_); |
| std::move(deleter_).Run(this); |
| } |
| |
| } // namespace content |