| 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 eaa884b11de1f62e7abc178ece888cc7f9856ae9..5e5c96546612f1c793fecb752895559aabded77c 100644
|
| --- a/content/browser/renderer_host/media/audio_renderer_host.cc
|
| +++ b/content/browser/renderer_host/media/audio_renderer_host.cc
|
| @@ -6,6 +6,9 @@
|
|
|
| #include <stdint.h>
|
| #include <utility>
|
| +#if defined(OS_WIN)
|
| +#include <windows.h>
|
| +#endif
|
|
|
| #include "base/bind.h"
|
| #include "base/bind_helpers.h"
|
| @@ -16,6 +19,7 @@
|
| #include "base/process/process.h"
|
| #include "content/browser/bad_message.h"
|
| #include "content/browser/browser_main_loop.h"
|
| +#include "content/browser/media/audio_output_impl.h"
|
| #include "content/browser/media/audio_stream_monitor.h"
|
| #include "content/browser/media/capture/audio_mirroring_manager.h"
|
| #include "content/browser/media/media_internals.h"
|
| @@ -34,6 +38,8 @@
|
| #include "media/audio/audio_streams_tracker.h"
|
| #include "media/base/audio_bus.h"
|
| #include "media/base/limits.h"
|
| +#include "mojo/edk/embedder/embedder.h"
|
| +#include "mojo/public/cpp/system/handle.h"
|
|
|
| using media::AudioBus;
|
| using media::AudioManager;
|
| @@ -55,6 +61,28 @@ std::pair<int, std::pair<bool, std::string>> MakeAuthorizationData(
|
| std::make_pair(authorized, device_unique_id));
|
| }
|
|
|
| +base::SyncSocket::TransitDescriptor DuplicateSocket(
|
| + base::SyncSocket::TransitDescriptor socket_descriptor) {
|
| + base::SyncSocket::TransitDescriptor socket_descriptor_dup;
|
| +
|
| +#if defined(OS_WIN)
|
| + socket_descriptor_dup = 0;
|
| + if (!::DuplicateHandle(GetCurrentProcess(), // hSourceProcessHandle
|
| + socket_descriptor,
|
| + GetCurrentProcess(), // hTargetProcessHandle
|
| + &socket_descriptor_dup,
|
| + 0, // dwDesiredAccess ignored due to SAME_ACCESS
|
| + FALSE, // !bInheritHandle
|
| + DUPLICATE_SAME_ACCESS)) {
|
| + LOG(ERROR) << "Unable to duplicate socket handle.";
|
| + }
|
| +
|
| +#else
|
| + socket_descriptor_dup.fd = dup(socket_descriptor.fd);
|
| +#endif
|
| + return socket_descriptor_dup;
|
| +}
|
| +
|
| bool IsValidDeviceId(const std::string& device_id) {
|
| static const std::string::size_type kValidLength = 64;
|
|
|
| @@ -120,7 +148,9 @@ class AudioRendererHost::AudioEntry
|
| 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,
|
| + const media::mojom::AudioOutput::CreateStreamCallback&
|
| + create_stream_callback);
|
| ~AudioEntry() override;
|
|
|
| int stream_id() const {
|
| @@ -142,6 +172,11 @@ class AudioRendererHost::AudioEntry
|
| bool playing() const { return playing_; }
|
| void set_playing(bool playing) { playing_ = playing; }
|
|
|
| + media::mojom::AudioOutput::CreateStreamCallback*
|
| + get_create_stream_callback() {
|
| + return &create_stream_callback_;
|
| + }
|
| +
|
| private:
|
| // media::AudioOutputController::EventHandler implementation.
|
| void OnCreated() override;
|
| @@ -165,6 +200,9 @@ class AudioRendererHost::AudioEntry
|
| const scoped_refptr<media::AudioOutputController> controller_;
|
|
|
| bool playing_;
|
| +
|
| + // Callback for media::mojom::AudioOutput::CreateStream.
|
| + media::mojom::AudioOutput::CreateStreamCallback create_stream_callback_;
|
| };
|
|
|
| AudioRendererHost::AudioEntry::AudioEntry(
|
| @@ -174,7 +212,9 @@ AudioRendererHost::AudioEntry::AudioEntry(
|
| 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,
|
| + const media::mojom::AudioOutput::CreateStreamCallback&
|
| + create_stream_callback)
|
| : host_(host),
|
| stream_id_(stream_id),
|
| render_frame_id_(render_frame_id),
|
| @@ -185,7 +225,8 @@ AudioRendererHost::AudioEntry::AudioEntry(
|
| params,
|
| output_device_id,
|
| reader_.get())),
|
| - playing_(false) {
|
| + playing_(false),
|
| + create_stream_callback_(create_stream_callback) {
|
| DCHECK(controller_.get());
|
| }
|
|
|
| @@ -244,8 +285,8 @@ void AudioRendererHost::GetOutputControllers(
|
| void AudioRendererHost::OnChannelClosing() {
|
| // 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);
|
| + // Note: CloseStream() removes the entries from audio_entries_.
|
| + CloseStream(audio_entries_.begin()->first);
|
| }
|
|
|
| // Remove any authorizations for streams that were not yet created
|
| @@ -258,9 +299,9 @@ void AudioRendererHost::OnDestruct() const {
|
|
|
| void AudioRendererHost::AudioEntry::OnCreated() {
|
| BrowserThread::PostTask(
|
| - BrowserThread::IO,
|
| - FROM_HERE,
|
| - base::Bind(&AudioRendererHost::DoCompleteCreation, host_, stream_id_));
|
| + BrowserThread::IO, FROM_HERE,
|
| + base::Bind(&AudioRendererHost::DoCompleteCreation, host_, stream_id_,
|
| + create_stream_callback_));
|
| }
|
|
|
| void AudioRendererHost::AudioEntry::OnPlaying() {
|
| @@ -290,29 +331,41 @@ void AudioRendererHost::AudioEntry::OnError() {
|
| base::Bind(&AudioRendererHost::ReportErrorAndClose, host_, stream_id_));
|
| }
|
|
|
| -void AudioRendererHost::DoCompleteCreation(int stream_id) {
|
| +void AudioRendererHost::DoCompleteCreation(
|
| + int stream_id,
|
| + const media::mojom::AudioOutput::CreateStreamCallback&
|
| + create_stream_callback) {
|
| DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
|
|
| if (!PeerHandle()) {
|
| DLOG(WARNING) << "Renderer process handle is invalid.";
|
| - ReportErrorAndClose(stream_id);
|
| + ReportErrorAndCloseStream(stream_id, create_stream_callback);
|
| return;
|
| }
|
|
|
| - AudioEntry* const entry = LookupById(stream_id);
|
| + AudioEntry* entry = LookupById(stream_id);
|
| if (!entry) {
|
| - ReportErrorAndClose(stream_id);
|
| + ReportErrorAndCloseStream(stream_id, create_stream_callback);
|
| 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());
|
| + media::mojom::AudioOutputStreamPtr stream_ptr =
|
| + (audio_output_impls_[entry->render_frame_id()]->StreamFactory(
|
| + stream_id, entry->render_frame_id(), this));
|
| +
|
| + base::SharedMemoryHandle shared_memory_handle =
|
| + base::SharedMemory::DuplicateHandle(entry->shared_memory()->handle());
|
| +
|
| + MojoHandle mojo_foreign_memory_handle;
|
| +
|
| + MojoResult shared_buffer_result = mojo::edk::CreateSharedBufferWrapper(
|
| + shared_memory_handle, entry->shared_memory()->requested_size(), false,
|
| + &mojo_foreign_memory_handle);
|
| +
|
| + if (shared_buffer_result != MOJO_RESULT_OK) {
|
| + DLOG(WARNING) << "Failed to wrap transit descriptor. Closing: "
|
| + << shared_buffer_result;
|
| + ReportErrorAndCloseStream(entry->stream_id(), create_stream_callback);
|
| return;
|
| }
|
|
|
| @@ -323,13 +376,52 @@ void AudioRendererHost::DoCompleteCreation(int stream_id) {
|
| // 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());
|
| + ReportErrorAndCloseStream(entry->stream_id(), create_stream_callback);
|
| return;
|
| }
|
| + mojo::ScopedSharedBufferHandle shared_buffer_handle =
|
| + mojo::ScopedSharedBufferHandle(
|
| + mojo::SharedBufferHandle(mojo_foreign_memory_handle));
|
| +
|
| + MojoHandle socket_descriptor_handle;
|
| + MojoResult platform_handle_result;
|
| +
|
| + // The socket handle is going to be closed when |mojo_application_host_| is
|
| + // reset. The renderer is going to need to close the socket handle
|
| + // too when the output steam is closed. This is going to cause an issue
|
| + // because both of them are going to close the same socket handle.
|
| + // This is why a duplicate of the socket handler is going to be created,
|
| + // stored in |socket_descriptor_dup| and sent through Mojo to the renderer so
|
| + // it can be used by AudioOutputIPCDelegate.
|
| +
|
| + base::SyncSocket::TransitDescriptor socket_descriptor_dup =
|
| + DuplicateSocket(socket_descriptor);
|
| +#if defined(OS_WIN)
|
| + platform_handle_result = mojo::edk::CreatePlatformHandleWrapper(
|
| + mojo::edk::ScopedPlatformHandle(
|
| + mojo::edk::PlatformHandle(socket_descriptor_dup),
|
| + &socket_descriptor_handle);
|
| +#else
|
| + platform_handle_result = mojo::edk::CreatePlatformHandleWrapper(
|
| + mojo::edk::ScopedPlatformHandle(
|
| + mojo::edk::PlatformHandle(socket_descriptor_dup.fd)),
|
| + &socket_descriptor_handle);
|
| +#endif
|
| +
|
| + if (platform_handle_result != MOJO_RESULT_OK) {
|
| + DLOG(WARNING) << "Failed to wrap platform handle. Closing: "
|
| + << platform_handle_result;
|
| + ReportErrorAndCloseStream(stream_id, create_stream_callback);
|
| + return;
|
| + }
|
| +
|
| + mojo::ScopedHandle socket_handle =
|
| + mojo::ScopedHandle(mojo::Handle(socket_descriptor_handle));
|
| + create_stream_callback.Run(
|
| + stream_id, std::move(stream_ptr),
|
| + std::move(shared_buffer_handle), std::move(socket_handle));
|
|
|
| - Send(new AudioMsg_NotifyStreamCreated(
|
| - entry->stream_id(), foreign_memory_handle, socket_descriptor,
|
| - entry->shared_memory()->requested_size()));
|
| + entry->get_create_stream_callback()->reset();
|
| }
|
|
|
| void AudioRendererHost::DoNotifyStreamStateChanged(int stream_id,
|
| @@ -380,10 +472,9 @@ bool AudioRendererHost::OnMessageReceived(const IPC::Message& message) {
|
| 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_CloseStream, CloseStream)
|
| IPC_MESSAGE_HANDLER(AudioHostMsg_SetVolume, OnSetVolume)
|
| IPC_MESSAGE_UNHANDLED(handled = false)
|
| IPC_END_MESSAGE_MAP()
|
| @@ -517,30 +608,38 @@ void AudioRendererHost::OnDeviceIDTranslated(
|
| 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) {
|
| +void AudioRendererHost::CreateStream(
|
| + int stream_id,
|
| + int render_frame_id,
|
| + const media::AudioParameters& params,
|
| + const media::mojom::AudioOutput::CreateStreamCallback&
|
| + create_stream_callback) {
|
| DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
| - DVLOG(1) << "AudioRendererHost@" << this << "::OnCreateStream"
|
| + DVLOG(1) << "AudioRendererHost@" << this << "::CreateStream"
|
| << "(stream_id=" << stream_id << ")";
|
|
|
| const auto& auth_data = authorizations_.find(stream_id);
|
|
|
| // If no previous authorization requested, assume default device
|
| if (auth_data == authorizations_.end()) {
|
| - DoCreateStream(stream_id, render_frame_id, params, std::string());
|
| + DoCreateStream(stream_id, render_frame_id, params, std::string(),
|
| + create_stream_callback);
|
| return;
|
| }
|
|
|
| CHECK(auth_data->second.first);
|
| - DoCreateStream(stream_id, render_frame_id, params, auth_data->second.second);
|
| + DoCreateStream(stream_id, render_frame_id, params, auth_data->second.second,
|
| + create_stream_callback);
|
| authorizations_.erase(auth_data);
|
| }
|
|
|
| -void AudioRendererHost::DoCreateStream(int stream_id,
|
| - int render_frame_id,
|
| - const media::AudioParameters& params,
|
| - const std::string& device_unique_id) {
|
| +void AudioRendererHost::DoCreateStream(
|
| + int stream_id,
|
| + int render_frame_id,
|
| + const media::AudioParameters& params,
|
| + const std::string& device_unique_id,
|
| + const media::mojom::AudioOutput::CreateStreamCallback&
|
| + create_stream_callback) {
|
| DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
|
|
| // media::AudioParameters is validated in the deserializer.
|
| @@ -570,9 +669,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, stream_id, render_frame_id, params, device_unique_id,
|
| + std::move(shared_memory), std::move(reader), create_stream_callback));
|
| if (mirroring_manager_) {
|
| mirroring_manager_->AddDiverter(
|
| render_process_id_, entry->render_frame_id(), entry->controller());
|
| @@ -635,7 +734,7 @@ void AudioRendererHost::SendErrorMessage(int stream_id) {
|
| stream_id, media::AUDIO_OUTPUT_IPC_DELEGATE_STATE_ERROR));
|
| }
|
|
|
| -void AudioRendererHost::OnCloseStream(int stream_id) {
|
| +void AudioRendererHost::CloseStream(int stream_id) {
|
| DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
| authorizations_.erase(stream_id);
|
|
|
| @@ -682,7 +781,30 @@ void AudioRendererHost::ReportErrorAndClose(int stream_id) {
|
| SendErrorMessage(stream_id);
|
|
|
| audio_log_->OnError(stream_id);
|
| - OnCloseStream(stream_id);
|
| + CloseStream(stream_id);
|
| +}
|
| +
|
| +void AudioRendererHost::ReportErrorAndCloseStream(
|
| + int stream_id,
|
| + const media::mojom::AudioOutput::CreateStreamCallback& callback) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
| +
|
| + // Make sure this isn't a stray callback executing after the stream has been
|
| + // closed, so error notifications aren't sent after clients believe the stream
|
| + // is closed.
|
| + if (!LookupById(stream_id))
|
| + return;
|
| +
|
| + mojo::ScopedSharedBufferHandle shared_buffer_handle =
|
| + mojo::ScopedSharedBufferHandle(mojo::SharedBufferHandle());
|
| +
|
| + mojo::ScopedHandle socket_handle = mojo::ScopedHandle(mojo::Handle());
|
| +
|
| + callback.Run(stream_id, media::mojom::AudioOutputStreamPtr(),
|
| + std::move(shared_buffer_handle), std::move(socket_handle));
|
| +
|
| + audio_log_->OnError(stream_id);
|
| + CloseStream(stream_id);
|
| }
|
|
|
| AudioRendererHost::AudioEntry* AudioRendererHost::LookupById(int stream_id) {
|
| @@ -737,6 +859,22 @@ bool AudioRendererHost::RenderFrameHasActiveAudio(int render_frame_id) const {
|
| return false;
|
| }
|
|
|
| +AudioOutputImpl* AudioRendererHost::get_audio_output_impl(
|
| + int render_frame_id) const {
|
| + auto result = audio_output_impls_.find(render_frame_id);
|
| + if (result != audio_output_impls_.end())
|
| + return result->second;
|
| + else
|
| + return NULL;
|
| +}
|
| +
|
| +void AudioRendererHost::set_audio_output_impl(
|
| + int render_frame_id,
|
| + AudioOutputImpl* audio_output_impl) {
|
| + audio_output_impls_.insert(
|
| + std::make_pair(render_frame_id, audio_output_impl));
|
| +}
|
| +
|
| void AudioRendererHost::CheckOutputDeviceAccess(
|
| int render_frame_id,
|
| const std::string& device_id,
|
|
|