| Index: content/browser/renderer_host/media/audio_renderer_host.cc
|
| diff --git a/content/browser/renderer_host/media/audio_renderer_host.cc b/content/browser/renderer_host/media/audio_renderer_host.cc
|
| index 4ee8318b52146df977496b4cea5b4c97d6235944..f44768aac911b8916bc86f76c510fb583e383695 100644
|
| --- a/content/browser/renderer_host/media/audio_renderer_host.cc
|
| +++ b/content/browser/renderer_host/media/audio_renderer_host.cc
|
| @@ -23,7 +23,6 @@
|
| #include "content/browser/renderer_host/media/media_stream_manager.h"
|
| #include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
|
| #include "content/browser/renderer_host/render_widget_host_impl.h"
|
| -#include "content/common/media/audio_messages.h"
|
| #include "content/public/browser/content_browser_client.h"
|
| #include "content/public/browser/media_device_id.h"
|
| #include "content/public/browser/media_observer.h"
|
| @@ -33,6 +32,11 @@
|
| #include "media/audio/audio_streams_tracker.h"
|
| #include "media/base/audio_bus.h"
|
| #include "media/base/limits.h"
|
| +#include "media/mojo/interfaces/audio_output.mojom-shared.h"
|
| +#include "media/mojo/interfaces/audio_output.mojom.h"
|
| +#include "mojo/edk/embedder/embedder.h"
|
| +#include "mojo/public/cpp/system/handle.h"
|
| +#include "mojo/public/cpp/system/platform_handle.h"
|
|
|
| using media::AudioBus;
|
| using media::AudioManager;
|
| @@ -132,15 +136,18 @@ void ValidateRenderFrameId(int render_process_id,
|
| } // namespace
|
|
|
| class AudioRendererHost::AudioEntry
|
| - : public media::AudioOutputController::EventHandler {
|
| + : public media::AudioOutputController::EventHandler,
|
| + private media::mojom::AudioOutputStream {
|
| public:
|
| AudioEntry(AudioRendererHost* host,
|
| + CreateStreamCallback callback,
|
| int stream_id,
|
| int render_frame_id,
|
| const media::AudioParameters& params,
|
| const std::string& output_device_id,
|
| std::unique_ptr<base::SharedMemory> shared_memory,
|
| - std::unique_ptr<media::AudioOutputController::SyncReader> reader);
|
| + std::unique_ptr<media::AudioOutputController::SyncReader> reader,
|
| + media::mojom::AudioOutputStreamClientPtr output_stream_client);
|
| ~AudioEntry() override;
|
|
|
| int stream_id() const {
|
| @@ -162,6 +169,15 @@ class AudioRendererHost::AudioEntry
|
| bool playing() const { return playing_; }
|
| void set_playing(bool playing) { playing_ = playing; }
|
|
|
| + // media::mojom::AudioOutputStream implementation
|
| + void Play() override;
|
| + void Pause() override;
|
| + void SetVolume(double volume) override;
|
| +
|
| + void OnConnectionError();
|
| + bool SendStreamStateResponse(media::mojom::AudioOutputStreamState state);
|
| + void BindRequest(media::mojom::AudioOutputStreamRequest);
|
| +
|
| private:
|
| // media::AudioOutputController::EventHandler implementation.
|
| void OnCreated() override;
|
| @@ -184,17 +200,30 @@ class AudioRendererHost::AudioEntry
|
| // The AudioOutputController that manages the audio stream.
|
| const scoped_refptr<media::AudioOutputController> controller_;
|
|
|
| + // Used for communication with the bound AudioOutputStreamClient.
|
| + media::mojom::AudioOutputStreamClientPtr output_stream_client_;
|
| +
|
| + // Binds |this| to an AudioOutputStreamPtr that is passed to the
|
| + // AudioOutputStreamClient (AudioOutputIPC in the renderer process).
|
| + std::unique_ptr<mojo::Binding<media::mojom::AudioOutputStream>> binding_;
|
| +
|
| + CreateStreamCallback callback_;
|
| +
|
| bool playing_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(AudioEntry);
|
| };
|
|
|
| AudioRendererHost::AudioEntry::AudioEntry(
|
| AudioRendererHost* host,
|
| + CreateStreamCallback callback,
|
| int stream_id,
|
| int render_frame_id,
|
| const media::AudioParameters& params,
|
| const std::string& output_device_id,
|
| std::unique_ptr<base::SharedMemory> shared_memory,
|
| - std::unique_ptr<media::AudioOutputController::SyncReader> reader)
|
| + std::unique_ptr<media::AudioOutputController::SyncReader> reader,
|
| + media::mojom::AudioOutputStreamClientPtr output_stream_client)
|
| : host_(host),
|
| stream_id_(stream_id),
|
| render_frame_id_(render_frame_id),
|
| @@ -205,8 +234,64 @@ AudioRendererHost::AudioEntry::AudioEntry(
|
| params,
|
| output_device_id,
|
| reader_.get())),
|
| + output_stream_client_(std::move(output_stream_client)),
|
| + callback_(callback),
|
| playing_(false) {
|
| DCHECK(controller_.get());
|
| +
|
| + if (output_stream_client_.is_bound())
|
| + output_stream_client_.set_connection_error_handler(
|
| + base::Bind(&AudioEntry::OnConnectionError, base::Unretained(this)));
|
| + else
|
| + BrowserThread::PostTask(
|
| + BrowserThread::IO, FROM_HERE,
|
| + base::Bind(&AudioRendererHost::OnCloseStream, host_, stream_id_));
|
| +}
|
| +
|
| +void AudioRendererHost::AudioEntry::Play() {
|
| + controller_->Play();
|
| + host_->audio_log_->OnStarted(stream_id_);
|
| +}
|
| +
|
| +void AudioRendererHost::AudioEntry::Pause() {
|
| + controller_->Pause();
|
| + host_->audio_log_->OnStopped(stream_id_);
|
| +}
|
| +
|
| +void AudioRendererHost::AudioEntry::SetVolume(double volume) {
|
| + if (volume < 0 || volume > 1.0)
|
| + return;
|
| +
|
| + controller_->SetVolume(volume);
|
| + host_->audio_log_->OnSetVolume(stream_id_, volume);
|
| +}
|
| +
|
| +void AudioRendererHost::AudioEntry::OnConnectionError() {
|
| + binding_.reset();
|
| + output_stream_client_.reset();
|
| +
|
| + BrowserThread::PostTask(
|
| + BrowserThread::IO, FROM_HERE,
|
| + base::Bind(&AudioRendererHost::CloseStream, host_, stream_id_));
|
| +}
|
| +
|
| +bool AudioRendererHost::AudioEntry::SendStreamStateResponse(
|
| + media::mojom::AudioOutputStreamState state) {
|
| + if (!output_stream_client_.is_bound())
|
| + return false;
|
| +
|
| + output_stream_client_->OnStreamStateChange(state);
|
| + return true;
|
| +}
|
| +
|
| +void AudioRendererHost::AudioEntry::BindRequest(
|
| + media::mojom::AudioOutputStreamRequest request) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
| +
|
| + binding_.reset(new mojo::Binding<media::mojom::AudioOutputStream>(
|
| + this, std::move(request)));
|
| + binding_->set_connection_error_handler(
|
| + base::Bind(&AudioEntry::OnConnectionError, base::Unretained(this)));
|
| }
|
|
|
| AudioRendererHost::AudioEntry::~AudioEntry() {}
|
| @@ -220,8 +305,7 @@ AudioRendererHost::AudioRendererHost(int render_process_id,
|
| MediaInternals* media_internals,
|
| MediaStreamManager* media_stream_manager,
|
| const std::string& salt)
|
| - : BrowserMessageFilter(AudioMsgStart),
|
| - render_process_id_(render_process_id),
|
| + : render_process_id_(render_process_id),
|
| audio_manager_(audio_manager),
|
| mirroring_manager_(mirroring_manager),
|
| audio_log_(media_internals->CreateAudioLog(
|
| @@ -263,96 +347,81 @@ void AudioRendererHost::GetOutputControllers(
|
| base::Bind(&AudioRendererHost::DoGetOutputControllers, this), callback);
|
| }
|
|
|
| -void AudioRendererHost::OnChannelClosing() {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
| - // Since the IPC sender is gone, close all requested audio streams.
|
| - while (!audio_entries_.empty()) {
|
| - // Note: OnCloseStream() removes the entries from audio_entries_.
|
| - OnCloseStream(audio_entries_.begin()->first);
|
| - }
|
| -
|
| - // Remove any authorizations for streams that were not yet created
|
| - authorizations_.clear();
|
| -}
|
| -
|
| -void AudioRendererHost::OnDestruct() const {
|
| - BrowserThread::DeleteOnIOThread::Destruct(this);
|
| -}
|
| -
|
| void AudioRendererHost::AudioEntry::OnCreated() {
|
| - BrowserThread::PostTask(
|
| - BrowserThread::IO,
|
| - FROM_HERE,
|
| - base::Bind(&AudioRendererHost::DoCompleteCreation, host_, stream_id_));
|
| + // TODO: make sure it's safe to capture |this| in the callback.
|
| + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
|
| + base::Bind(&AudioRendererHost::DoCompleteCreation,
|
| + host_, this, stream_id_, callback_));
|
| }
|
|
|
| void AudioRendererHost::AudioEntry::OnPlaying() {
|
| - BrowserThread::PostTask(
|
| - BrowserThread::IO,
|
| - FROM_HERE,
|
| - base::Bind(&AudioRendererHost::DoNotifyStreamStateChanged,
|
| - host_,
|
| - stream_id_,
|
| - true));
|
| + output_stream_client_->OnStreamStateChange(
|
| + ::media::mojom::AudioOutputStreamState::PLAYING);
|
| }
|
|
|
| void AudioRendererHost::AudioEntry::OnPaused() {
|
| - BrowserThread::PostTask(
|
| - BrowserThread::IO,
|
| - FROM_HERE,
|
| - base::Bind(&AudioRendererHost::DoNotifyStreamStateChanged,
|
| - host_,
|
| - stream_id_,
|
| - false));
|
| + output_stream_client_->OnStreamStateChange(
|
| + ::media::mojom::AudioOutputStreamState::PAUSED);
|
| }
|
|
|
| void AudioRendererHost::AudioEntry::OnError() {
|
| + output_stream_client_->OnStreamStateChange(
|
| + ::media::mojom::AudioOutputStreamState::ERROR);
|
| BrowserThread::PostTask(
|
| - BrowserThread::IO,
|
| - FROM_HERE,
|
| - base::Bind(&AudioRendererHost::ReportErrorAndClose, host_, stream_id_));
|
| + BrowserThread::IO, FROM_HERE,
|
| + base::Bind(&AudioRendererHost::OnCloseStream, host_, stream_id_));
|
| }
|
|
|
| -void AudioRendererHost::DoCompleteCreation(int stream_id) {
|
| +void AudioRendererHost::DoCompleteCreation(AudioEntry* entry,
|
| + int stream_id,
|
| + CreateStreamCallback callback) {
|
| DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
|
|
| - if (!PeerHandle()) {
|
| - DLOG(WARNING) << "Renderer process handle is invalid.";
|
| - ReportErrorAndClose(stream_id);
|
| - return;
|
| - }
|
| -
|
| - AudioEntry* const entry = LookupById(stream_id);
|
| - if (!entry) {
|
| - ReportErrorAndClose(stream_id);
|
| - return;
|
| - }
|
| -
|
| - // Once the audio stream is created then complete the creation process by
|
| - // mapping shared memory and sharing with the renderer process.
|
| - base::SharedMemoryHandle foreign_memory_handle;
|
| - if (!entry->shared_memory()->ShareToProcess(PeerHandle(),
|
| - &foreign_memory_handle)) {
|
| - // If we failed to map and share the shared memory then close the audio
|
| - // stream and send an error message.
|
| - ReportErrorAndClose(entry->stream_id());
|
| - return;
|
| - }
|
| + media::mojom::AudioOutputStreamPtr stream_ptr;
|
| + entry->BindRequest(mojo::GetProxy(&stream_ptr));
|
|
|
| AudioSyncReader* reader = static_cast<AudioSyncReader*>(entry->reader());
|
| -
|
| - base::SyncSocket::TransitDescriptor socket_descriptor;
|
| -
|
| - // If we failed to prepare the sync socket for the renderer then we fail
|
| - // the construction of audio stream.
|
| - if (!reader->PrepareForeignSocket(PeerHandle(), &socket_descriptor)) {
|
| - ReportErrorAndClose(entry->stream_id());
|
| - return;
|
| - }
|
| -
|
| - Send(new AudioMsg_NotifyStreamCreated(
|
| - entry->stream_id(), foreign_memory_handle, socket_descriptor,
|
| - entry->shared_memory()->requested_size()));
|
| + base::PlatformFile socket_fd = reader->GetSyncSocket();
|
| + mojo::ScopedHandle socket_handle = mojo::WrapPlatformFile(socket_fd);
|
| + DCHECK(socket_handle.is_valid());
|
| +
|
| + base::SharedMemoryHandle shared_memory_handle =
|
| + base::SharedMemory::DuplicateHandle(entry->shared_memory()->handle());
|
| + if (!base::SharedMemory::IsHandleValid(shared_memory_handle)) {
|
| + // TODO: When can this happen?
|
| + LOG(FATAL) << "Invalid shared mem handle";
|
| + return ReportErrorAndClose(stream_id);
|
| + }
|
| + mojo::ScopedSharedBufferHandle shared_buffer_handle =
|
| + mojo::WrapSharedMemoryHandle(shared_memory_handle,
|
| + entry->shared_memory()->requested_size(),
|
| + false);
|
| + DCHECK(shared_buffer_handle.is_valid());
|
| +
|
| + /*mojo::ScopedSharedBufferHandle shared_buffer_handle =
|
| + mojo::WrapSharedMemoryHandle(shared_memory_handle,
|
| + entry->shared_memory()->requested_size(),
|
| + false);*/
|
| +
|
| + /* TODO: read comment.
|
| + // The socket sent from the browser to the renderer is a ForeignSocket, which
|
| + // is a part of AudioSyncReader that is owned by AudioEntry. The socket handle
|
| + // is closed when the owning AudioEntry destructs. With mojo, the ownership of
|
| + // the handle is transferred to the target process. The handle is no longer
|
| + // valid in the sending process, and cannot be closed there. If the socket
|
| + // handle is closed when the AudioEntry is deleted, an error occurs. To WAR
|
| + // this error, duplicate the socket and send the duplicate to the renderer.
|
| + #if defined(OS_WIN)
|
| + mojo::ScopedHandle socket_handle =
|
| + mojo::WrapPlatformFile(socket_descriptor);
|
| + #else
|
| + mojo::ScopedHandle socket_handle =
|
| + mojo::WrapPlatformFile(socket_descriptor.fd);
|
| + #endif
|
| + */
|
| +
|
| + callback.Run(std::move(stream_ptr), std::move(shared_buffer_handle),
|
| + std::move(socket_handle));
|
| }
|
|
|
| void AudioRendererHost::DoNotifyStreamStateChanged(int stream_id,
|
| @@ -363,10 +432,9 @@ void AudioRendererHost::DoNotifyStreamStateChanged(int stream_id,
|
| if (!entry)
|
| return;
|
|
|
| - Send(new AudioMsg_NotifyStreamStateChanged(
|
| - stream_id,
|
| - is_playing ? media::AUDIO_OUTPUT_IPC_DELEGATE_STATE_PLAYING
|
| - : media::AUDIO_OUTPUT_IPC_DELEGATE_STATE_PAUSED));
|
| + entry->SendStreamStateResponse(
|
| + is_playing ? media::mojom::AudioOutputStreamState::PLAYING
|
| + : media::mojom::AudioOutputStreamState::PAUSED);
|
|
|
| if (is_playing) {
|
| AudioStreamMonitor::StartMonitoringStream(
|
| @@ -396,91 +464,13 @@ AudioRendererHost::DoGetOutputControllers() const {
|
| return controllers;
|
| }
|
|
|
| -///////////////////////////////////////////////////////////////////////////////
|
| -// IPC Messages handler
|
| -bool AudioRendererHost::OnMessageReceived(const IPC::Message& message) {
|
| - bool handled = true;
|
| - IPC_BEGIN_MESSAGE_MAP(AudioRendererHost, message)
|
| - IPC_MESSAGE_HANDLER(AudioHostMsg_RequestDeviceAuthorization,
|
| - OnRequestDeviceAuthorization)
|
| - IPC_MESSAGE_HANDLER(AudioHostMsg_CreateStream, OnCreateStream)
|
| - IPC_MESSAGE_HANDLER(AudioHostMsg_PlayStream, OnPlayStream)
|
| - IPC_MESSAGE_HANDLER(AudioHostMsg_PauseStream, OnPauseStream)
|
| - IPC_MESSAGE_HANDLER(AudioHostMsg_CloseStream, OnCloseStream)
|
| - IPC_MESSAGE_HANDLER(AudioHostMsg_SetVolume, OnSetVolume)
|
| - IPC_MESSAGE_UNHANDLED(handled = false)
|
| - IPC_END_MESSAGE_MAP()
|
| -
|
| - return handled;
|
| -}
|
| -
|
| -void AudioRendererHost::OnRequestDeviceAuthorization(
|
| +void AudioRendererHost::OnDeviceAuthorized(
|
| int stream_id,
|
| - int render_frame_id,
|
| - int session_id,
|
| + RequestDeviceAuthorizationCallback callback,
|
| const std::string& device_id,
|
| - const url::Origin& security_origin) {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
| - const base::TimeTicks auth_start_time = base::TimeTicks::Now();
|
| -
|
| - DVLOG(1) << "AudioRendererHost@" << this << "::OnRequestDeviceAuthorization"
|
| - << "(stream_id=" << stream_id
|
| - << ", render_frame_id=" << render_frame_id
|
| - << ", session_id=" << session_id << ", device_id=" << device_id
|
| - << ", security_origin=" << security_origin << ")";
|
| -
|
| - if (LookupById(stream_id) || IsAuthorizationStarted(stream_id))
|
| - return;
|
| -
|
| - if (!IsValidDeviceId(device_id)) {
|
| - UMALogDeviceAuthorizationTime(auth_start_time);
|
| - Send(new AudioMsg_NotifyDeviceAuthorized(
|
| - stream_id, media::OUTPUT_DEVICE_STATUS_ERROR_NOT_FOUND,
|
| - media::AudioParameters::UnavailableDeviceParams(), std::string()));
|
| - return;
|
| - }
|
| -
|
| - // If |session_id should be used for output device selection and such output
|
| - // device is found, reuse the input device permissions.
|
| - if (media::AudioDeviceDescription::UseSessionIdToSelectDevice(session_id,
|
| - device_id)) {
|
| - const StreamDeviceInfo* info =
|
| - media_stream_manager_->audio_input_device_manager()
|
| - ->GetOpenedDeviceInfoById(session_id);
|
| - if (info) {
|
| - media::AudioParameters output_params(
|
| - media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
|
| - static_cast<media::ChannelLayout>(
|
| - info->device.matched_output.channel_layout),
|
| - info->device.matched_output.sample_rate, 16,
|
| - info->device.matched_output.frames_per_buffer);
|
| - output_params.set_effects(info->device.matched_output.effects);
|
| - authorizations_.insert(MakeAuthorizationData(
|
| - stream_id, true, info->device.matched_output_device_id));
|
| - MaybeFixAudioParameters(&output_params);
|
| - UMALogDeviceAuthorizationTime(auth_start_time);
|
| - // Hash matched device id and pass it to the renderer
|
| - Send(new AudioMsg_NotifyDeviceAuthorized(
|
| - stream_id, media::OUTPUT_DEVICE_STATUS_OK, output_params,
|
| - GetHMACForMediaDeviceID(salt_, security_origin,
|
| - info->device.matched_output_device_id)));
|
| - return;
|
| - }
|
| - }
|
| -
|
| - authorizations_.insert(
|
| - MakeAuthorizationData(stream_id, false, std::string()));
|
| - CheckOutputDeviceAccess(
|
| - render_frame_id, device_id, security_origin,
|
| - base::Bind(&AudioRendererHost::OnDeviceAuthorized, this, stream_id,
|
| - device_id, security_origin, auth_start_time));
|
| -}
|
| -
|
| -void AudioRendererHost::OnDeviceAuthorized(int stream_id,
|
| - const std::string& device_id,
|
| - const url::Origin& security_origin,
|
| - base::TimeTicks auth_start_time,
|
| - bool have_access) {
|
| + const url::Origin& security_origin,
|
| + base::TimeTicks auth_start_time,
|
| + bool have_access) {
|
| DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
| const auto& auth_data = authorizations_.find(stream_id);
|
|
|
| @@ -493,9 +483,9 @@ void AudioRendererHost::OnDeviceAuthorized(int stream_id,
|
| if (!have_access) {
|
| authorizations_.erase(auth_data);
|
| UMALogDeviceAuthorizationTime(auth_start_time);
|
| - Send(new AudioMsg_NotifyDeviceAuthorized(
|
| - stream_id, media::OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED,
|
| - media::AudioParameters::UnavailableDeviceParams(), std::string()));
|
| + SendAuthorizationMessage(
|
| + callback, stream_id, media::OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED,
|
| + media::AudioParameters::UnavailableDeviceParams(), std::string());
|
| return;
|
| }
|
|
|
| @@ -510,19 +500,20 @@ void AudioRendererHost::OnDeviceAuthorized(int stream_id,
|
| audio_manager_->GetTaskRunner(), FROM_HERE,
|
| base::Bind(&GetDefaultDeviceInfoOnDeviceThread, audio_manager_),
|
| base::Bind(&AudioRendererHost::OnDeviceIDTranslated, this, stream_id,
|
| - auth_start_time, true));
|
| + auth_start_time, callback, true));
|
| } else {
|
| media_stream_manager_->audio_output_device_enumerator()->Enumerate(
|
| base::Bind(&AudioRendererHost::TranslateDeviceID, this, device_id,
|
| security_origin,
|
| base::Bind(&AudioRendererHost::OnDeviceIDTranslated, this,
|
| - stream_id, auth_start_time)));
|
| + stream_id, auth_start_time, callback)));
|
| }
|
| }
|
|
|
| void AudioRendererHost::OnDeviceIDTranslated(
|
| int stream_id,
|
| base::TimeTicks auth_start_time,
|
| + RequestDeviceAuthorizationCallback callback,
|
| bool device_found,
|
| const AudioOutputDeviceInfo& device_info) {
|
| DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
| @@ -537,9 +528,9 @@ void AudioRendererHost::OnDeviceIDTranslated(
|
| if (!device_found) {
|
| authorizations_.erase(auth_data);
|
| UMALogDeviceAuthorizationTime(auth_start_time);
|
| - Send(new AudioMsg_NotifyDeviceAuthorized(
|
| - stream_id, media::OUTPUT_DEVICE_STATUS_ERROR_NOT_FOUND,
|
| - media::AudioParameters::UnavailableDeviceParams(), std::string()));
|
| + SendAuthorizationMessage(
|
| + callback, stream_id, media::OUTPUT_DEVICE_STATUS_ERROR_NOT_FOUND,
|
| + media::AudioParameters::UnavailableDeviceParams(), std::string());
|
| return;
|
| }
|
|
|
| @@ -549,49 +540,18 @@ void AudioRendererHost::OnDeviceIDTranslated(
|
| media::AudioParameters output_params = device_info.output_params;
|
| MaybeFixAudioParameters(&output_params);
|
| UMALogDeviceAuthorizationTime(auth_start_time);
|
| - Send(new AudioMsg_NotifyDeviceAuthorized(
|
| - stream_id, media::OUTPUT_DEVICE_STATUS_OK, output_params, std::string()));
|
| -}
|
| -
|
| -void AudioRendererHost::OnCreateStream(int stream_id,
|
| - int render_frame_id,
|
| - const media::AudioParameters& params) {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
| - DVLOG(1) << "AudioRendererHost@" << this << "::OnCreateStream"
|
| - << "(stream_id=" << stream_id << ")";
|
| -
|
| - // Determine whether to use the device_unique_id from an authorization, or an
|
| - // empty string (i.e., when no previous authorization was requested, assume
|
| - // default device).
|
| - std::string device_unique_id;
|
| - const auto& auth_data = authorizations_.find(stream_id);
|
| - if (auth_data != authorizations_.end()) {
|
| - CHECK(auth_data->second.first);
|
| - device_unique_id.swap(auth_data->second.second);
|
| - authorizations_.erase(auth_data);
|
| - }
|
| -
|
| -#if DCHECK_IS_ON()
|
| - // When DCHECKs are turned on, hop over to the UI thread to validate the
|
| - // |render_frame_id|, then continue stream creation on the IO thread. See
|
| - // comment at top of DoCreateStream() for further details.
|
| - BrowserThread::PostTask(
|
| - BrowserThread::UI, FROM_HERE,
|
| - base::Bind(validate_render_frame_id_function_, render_process_id_,
|
| - render_frame_id,
|
| - base::Bind(&AudioRendererHost::DoCreateStream, this, stream_id,
|
| - render_frame_id, params, device_unique_id)));
|
| -#else
|
| - DoCreateStream(stream_id, render_frame_id, params, device_unique_id,
|
| - render_frame_id > 0);
|
| -#endif // DCHECK_IS_ON()
|
| + SendAuthorizationMessage(callback, stream_id, media::OUTPUT_DEVICE_STATUS_OK,
|
| + output_params, std::string());
|
| }
|
|
|
| -void AudioRendererHost::DoCreateStream(int stream_id,
|
| - int render_frame_id,
|
| - const media::AudioParameters& params,
|
| - const std::string& device_unique_id,
|
| - bool render_frame_id_is_valid) {
|
| +void AudioRendererHost::DoCreateStream(
|
| + int stream_id,
|
| + int render_frame_id,
|
| + const media::AudioParameters& params,
|
| + const std::string& device_unique_id,
|
| + media::mojom::AudioOutputStreamClientPtr client,
|
| + CreateStreamCallback callback,
|
| + bool render_frame_id_is_valid) {
|
| DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
|
|
| // Fail early if either of two sanity-checks fail:
|
| @@ -619,6 +579,7 @@ void AudioRendererHost::DoCreateStream(int stream_id,
|
| AudioBus::CalculateMemorySize(params);
|
| std::unique_ptr<base::SharedMemory> shared_memory(new base::SharedMemory());
|
| if (!shared_memory->CreateAndMapAnonymous(shared_memory_size)) {
|
| + client->OnStreamStateChange(media::mojom::AudioOutputStreamState::ERROR);
|
| SendErrorMessage(stream_id);
|
| return;
|
| }
|
| @@ -635,9 +596,9 @@ void AudioRendererHost::DoCreateStream(int stream_id,
|
| if (media_observer)
|
| media_observer->OnCreatingAudioStream(render_process_id_, render_frame_id);
|
|
|
| - std::unique_ptr<AudioEntry> entry(
|
| - new AudioEntry(this, stream_id, render_frame_id, params, device_unique_id,
|
| - std::move(shared_memory), std::move(reader)));
|
| + std::unique_ptr<AudioEntry> entry(new AudioEntry(
|
| + this, callback, stream_id, render_frame_id, params, device_unique_id,
|
| + std::move(shared_memory), std::move(reader), std::move(client)));
|
| if (mirroring_manager_) {
|
| mirroring_manager_->AddDiverter(
|
| render_process_id_, entry->render_frame_id(), entry->controller());
|
| @@ -696,8 +657,9 @@ void AudioRendererHost::OnSetVolume(int stream_id, double volume) {
|
| }
|
|
|
| void AudioRendererHost::SendErrorMessage(int stream_id) {
|
| - Send(new AudioMsg_NotifyStreamStateChanged(
|
| - stream_id, media::AUDIO_OUTPUT_IPC_DELEGATE_STATE_ERROR));
|
| + AudioEntry* entry = LookupById(stream_id);
|
| + if (entry)
|
| + entry->SendStreamStateResponse(media::mojom::AudioOutputStreamState::ERROR);
|
| }
|
|
|
| void AudioRendererHost::OnCloseStream(int stream_id) {
|
| @@ -791,6 +753,132 @@ bool AudioRendererHost::HasActiveAudio() {
|
| return !base::AtomicRefCountIsZero(&num_playing_streams_);
|
| }
|
|
|
| +void AudioRendererHost::BindRequest(media::mojom::AudioOutputRequest request) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
| +
|
| + binding_.reset(
|
| + new mojo::Binding<media::mojom::AudioOutput>(this, std::move(request)));
|
| +}
|
| +
|
| +// media::mojom::AudioOutput implementation
|
| +void AudioRendererHost::RequestDeviceAuthorization(
|
| + int stream_id,
|
| + int render_frame_id,
|
| + int session_id,
|
| + const mojo::String& device_id,
|
| + const url::Origin& origin,
|
| + const RequestDeviceAuthorizationCallback& callback) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
| + const base::TimeTicks auth_start_time = base::TimeTicks::Now();
|
| +
|
| + DVLOG(1) << "AudioRendererHost@" << this << "::RequestDeviceAuthorization"
|
| + << "(stream_id=" << stream_id
|
| + << ", render_frame_id=" << render_frame_id
|
| + << ", session_id=" << session_id << ", device_id=" << device_id
|
| + << ", origin=" << origin << ")";
|
| +
|
| + if (LookupById(stream_id) || IsAuthorizationStarted(stream_id))
|
| + return;
|
| +
|
| + if (!IsValidDeviceId(device_id)) {
|
| + UMALogDeviceAuthorizationTime(auth_start_time);
|
| + SendAuthorizationMessage(
|
| + callback, stream_id, media::OUTPUT_DEVICE_STATUS_ERROR_NOT_FOUND,
|
| + media::AudioParameters::UnavailableDeviceParams(), std::string());
|
| + return;
|
| + }
|
| +
|
| + // If |session_id should be used for output device selection and such output
|
| + // device is found, reuse the input device permissions.
|
| + if (media::AudioDeviceDescription::UseSessionIdToSelectDevice(session_id,
|
| + device_id)) {
|
| + const StreamDeviceInfo* info =
|
| + media_stream_manager_->audio_input_device_manager()
|
| + ->GetOpenedDeviceInfoById(session_id);
|
| + if (info) {
|
| + media::AudioParameters output_params(
|
| + media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
|
| + static_cast<media::ChannelLayout>(
|
| + info->device.matched_output.channel_layout),
|
| + info->device.matched_output.sample_rate, 16,
|
| + info->device.matched_output.frames_per_buffer);
|
| + output_params.set_effects(info->device.matched_output.effects);
|
| + authorizations_.insert(MakeAuthorizationData(
|
| + stream_id, true, info->device.matched_output_device_id));
|
| + MaybeFixAudioParameters(&output_params);
|
| + UMALogDeviceAuthorizationTime(auth_start_time);
|
| + // Hash matched device id and pass it to the renderer
|
| + SendAuthorizationMessage(
|
| + callback, stream_id, media::OUTPUT_DEVICE_STATUS_OK, output_params,
|
| + GetHMACForMediaDeviceID(salt_, origin,
|
| + info->device.matched_output_device_id));
|
| + return;
|
| + }
|
| + }
|
| +
|
| + authorizations_.insert(
|
| + MakeAuthorizationData(stream_id, false, std::string()));
|
| + CheckOutputDeviceAccess(
|
| + render_frame_id, device_id, origin,
|
| + base::Bind(&AudioRendererHost::OnDeviceAuthorized, this, stream_id,
|
| + callback, device_id, origin, auth_start_time));
|
| +}
|
| +
|
| +void AudioRendererHost::CreateStream(
|
| + int stream_id,
|
| + int render_frame_id,
|
| + media::mojom::AudioOutputStreamClientPtr client,
|
| + const media::AudioParameters& params,
|
| + const CreateStreamCallback& callback) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
| + DVLOG(1) << "AudioRendererHost@" << this << "::CreateStream"
|
| + << "(stream_id=" << stream_id << ")";
|
| +
|
| + // Determine whether to use the device_unique_id from an authorization, or an
|
| + // empty string (i.e., when no previous authorization was requested, assume
|
| + // default device).
|
| + std::string device_unique_id;
|
| + const auto& auth_data = authorizations_.find(stream_id);
|
| + if (auth_data != authorizations_.end()) {
|
| + CHECK(auth_data->second.first);
|
| + device_unique_id.swap(auth_data->second.second);
|
| + authorizations_.erase(auth_data);
|
| + }
|
| +
|
| +#if DCHECK_IS_ON()
|
| + // When DCHECKs are turned on, hop over to the UI thread to validate the
|
| + // |render_frame_id|, then continue stream creation on the IO thread. See
|
| + // comment at top of DoCreateStream() for further details.
|
| + BrowserThread::PostTask(
|
| + BrowserThread::UI, FROM_HERE,
|
| + base::Bind(validate_render_frame_id_function_, render_process_id_,
|
| + render_frame_id,
|
| + base::Bind(&AudioRendererHost::DoCreateStream, this, stream_id,
|
| + render_frame_id, params, device_unique_id,
|
| + base::Passed(&client), callback)));
|
| +#else
|
| + DoCreateStream(stream_id, render_frame_id, params, device_unique_id,
|
| + std::move(client), callback, render_frame_id > 0);
|
| +#endif // DCHECK_IS_ON()
|
| +}
|
| +
|
| +void AudioRendererHost::CloseStream(int stream_id) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
| +
|
| + OnCloseStream(stream_id);
|
| +}
|
| +
|
| +void AudioRendererHost::SendAuthorizationMessage(
|
| + RequestDeviceAuthorizationCallback callback,
|
| + int stream_id,
|
| + media::OutputDeviceStatus status,
|
| + const media::AudioParameters& params,
|
| + const std::string& matched_device_id) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
| +
|
| + callback.Run(status, params, matched_device_id);
|
| +}
|
| +
|
| void AudioRendererHost::CheckOutputDeviceAccess(
|
| int render_frame_id,
|
| const std::string& device_id,
|
| @@ -803,8 +891,8 @@ void AudioRendererHost::CheckOutputDeviceAccess(
|
| if (!media::AudioDeviceDescription::IsDefaultDevice(device_id) &&
|
| !MediaStreamManager::IsOriginAllowed(render_process_id_,
|
| security_origin)) {
|
| - content::bad_message::ReceivedBadMessage(this,
|
| - bad_message::ARH_UNAUTHORIZED_URL);
|
| + // content::bad_message::ReceivedBadMessage(this,
|
| + // bad_message::ARH_UNAUTHORIZED_URL);
|
| return;
|
| }
|
|
|
|
|