| Index: content/browser/media/audio_output_impl.cc
 | 
| diff --git a/content/browser/media/audio_output_impl.cc b/content/browser/media/audio_output_impl.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..c0a2439d373c7b27659da3ed4fd1b1f062b226fb
 | 
| --- /dev/null
 | 
| +++ b/content/browser/media/audio_output_impl.cc
 | 
| @@ -0,0 +1,217 @@
 | 
| +// Copyright 2016 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.
 | 
| +
 | 
| +#if defined(OS_WIN)
 | 
| +#include <windows.h>
 | 
| +#endif
 | 
| +
 | 
| +#include "base/memory/shared_memory.h"
 | 
| +#include "content/browser/frame_host/render_frame_host_impl.h"
 | 
| +#include "content/browser/media/audio_output_impl.h"
 | 
| +#include "content/browser/media/audio_output_stream_impl.h"
 | 
| +#include "content/browser/renderer_host/render_process_host_impl.h"
 | 
| +#include "content/public/browser/render_frame_host.h"
 | 
| +#include "mojo/edk/embedder/embedder.h"
 | 
| +#include "mojo/public/cpp/system/handle.h"
 | 
| +#include "mojo/public/cpp/system/platform_handle.h"
 | 
| +
 | 
| +namespace content {
 | 
| +
 | 
| +namespace {
 | 
| +
 | 
| +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;
 | 
| +}
 | 
| +
 | 
| +}  // namespace
 | 
| +
 | 
| +AudioOutputImpl::AudioOutputImpl(RenderFrameHostImpl* frame,
 | 
| +                                 media::mojom::AudioOutputRequest request)
 | 
| +    : frame_(frame) {
 | 
| +  binding_.reset(
 | 
| +      new mojo::Binding<media::mojom::AudioOutput>(this, std::move(request)));
 | 
| +
 | 
| +}
 | 
| +
 | 
| +AudioOutputImpl::~AudioOutputImpl() {
 | 
| +  auto audio_renderer_host = audio_renderer_host_.get();
 | 
| +
 | 
| +  if (audio_renderer_host)
 | 
| +    audio_renderer_host->CleanAudioOutputImpl(this);
 | 
| +
 | 
| +  for (auto it = create_stream_callbacks_.begin();
 | 
| +       it != create_stream_callbacks_.end(); ++it) {
 | 
| +    it->second.reset();
 | 
| +  }
 | 
| +  create_stream_callbacks_.clear();
 | 
| +}
 | 
| +
 | 
| +// static
 | 
| +void AudioOutputImpl::CreateService(RenderFrameHostImpl* frame,
 | 
| +                                    media::mojom::AudioOutputRequest request) {
 | 
| +  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 | 
| +
 | 
| +  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
 | 
| +                          base::Bind(&AudioOutputImpl::CreateServiceOnIOThread,
 | 
| +                                     frame, base::Passed(&request)));
 | 
| +}
 | 
| +
 | 
| +void AudioOutputImpl::Reset() {
 | 
| +
 | 
| +  if (!BrowserThread::CurrentlyOn(BrowserThread::IO))
 | 
| +    BrowserThread::PostTask(
 | 
| +        BrowserThread::IO, FROM_HERE,
 | 
| +        base::Bind(&AudioOutputImpl::Reset, base::Unretained(this)));
 | 
| +  else
 | 
| +    delete this;
 | 
| +}
 | 
| +
 | 
| +// static
 | 
| +void AudioOutputImpl::CreateServiceOnIOThread(
 | 
| +    RenderFrameHostImpl* frame,
 | 
| +    media::mojom::AudioOutputRequest request) {
 | 
| +  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 | 
| +  frame->set_audio_output_impl(new AudioOutputImpl(frame, std::move(request)));
 | 
| +}
 | 
| +
 | 
| +void AudioOutputImpl::CreateStream(int stream_id,
 | 
| +                                   const media::AudioParameters& params,
 | 
| +                                   const CreateStreamCallback& callback) {
 | 
| +  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 | 
| +
 | 
| +  create_stream_callbacks_.insert(std::make_pair(stream_id, callback));
 | 
| +  InitAudioRendererHost();
 | 
| +  if (audio_renderer_host_)
 | 
| +    audio_renderer_host_->CreateStream(stream_id, frame_->GetRoutingID(),
 | 
| +                                       params, this);
 | 
| +}
 | 
| +
 | 
| +void AudioOutputImpl::CreateStreamFactory(
 | 
| +    int stream_id,
 | 
| +    base::SharedMemory* shared_memory,
 | 
| +    base::SyncSocket::TransitDescriptor socket_descriptor) {
 | 
| +  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 | 
| +
 | 
| +  auto callback = create_stream_callbacks_.find(stream_id);
 | 
| +  if (callback == create_stream_callbacks_.end()) {
 | 
| +    ReportErrorAndCloseStream(stream_id);
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  media::mojom::AudioOutputStreamPtr stream_ptr =
 | 
| +      media::mojom::AudioOutputStreamPtr();
 | 
| +  std::unique_ptr<AudioOutputStreamImpl> stream(
 | 
| +      new AudioOutputStreamImpl(stream_id, this, mojo::GetProxy(&stream_ptr)));
 | 
| +  stream_impls_.insert(std::make_pair(stream_id, std::move(stream)));
 | 
| +
 | 
| +  base::SharedMemoryHandle shared_memory_handle =
 | 
| +      base::SharedMemory::DuplicateHandle(shared_memory->handle());
 | 
| +  if (!base::SharedMemory::IsHandleValid(shared_memory_handle)) {
 | 
| +    ReportErrorAndCloseStream(stream_id);
 | 
| +    return;
 | 
| +  }
 | 
| +  mojo::ScopedSharedBufferHandle shared_buffer_handle =
 | 
| +      mojo::WrapSharedMemoryHandle(shared_memory_handle,
 | 
| +                                   shared_memory->requested_size(), false);
 | 
| +  // The socket that we will send from the browser to the renderer is a
 | 
| +  // |foreign_socket_| which is a part AudioSyncReader that is owned by
 | 
| +  // AudioEntry. The socket handle will be closed when the AudioEntry it
 | 
| +  // belongs to will be closed. However, with mojo and unlike IPC, the
 | 
| +  // ownership of the handle is transferred to the target process. It's no
 | 
| +  // longer a valid handle in the sending process and it is an error to try
 | 
| +  // closing it there. So, if the socket handle is closed when the AudioEntry
 | 
| +  // is deleted, we will have an error. To fix this error we could just
 | 
| +  // duplicate the socket and send the duplicate to the renderer. Thus, we
 | 
| +  // will avoid having an problem when closing the socket.
 | 
| +  // See https://goo.gl/ACwSfa.
 | 
| +  base::SyncSocket::TransitDescriptor socket_descriptor_dup =
 | 
| +      DuplicateSocket(socket_descriptor);
 | 
| +#if defined(OS_WIN)
 | 
| +  mojo::ScopedHandle socket_handle =
 | 
| +      mojo::WrapPlatformFile(socket_descriptor_dup);
 | 
| +#else
 | 
| +  mojo::ScopedHandle socket_handle =
 | 
| +      mojo::WrapPlatformFile(socket_descriptor_dup.fd);
 | 
| +#endif
 | 
| +
 | 
| +  callback->second.Run(stream_id, std::move(stream_ptr),
 | 
| +                       std::move(shared_buffer_handle),
 | 
| +                       std::move(socket_handle));
 | 
| +  callback->second.reset();
 | 
| +  create_stream_callbacks_.erase(stream_id);
 | 
| +}
 | 
| +
 | 
| +bool AudioOutputImpl::CloseStream(int stream_id) {
 | 
| +  if (audio_renderer_host_.get())
 | 
| +    audio_renderer_host_->CloseStream(stream_id);
 | 
| +
 | 
| +  if (stream_impls_.erase(stream_id) == 0)
 | 
| +    return false;
 | 
| +
 | 
| +  auto callback = create_stream_callbacks_.find(stream_id);
 | 
| +
 | 
| +  if (callback != create_stream_callbacks_.end()) {
 | 
| +    callback->second.reset();
 | 
| +    create_stream_callbacks_.erase(stream_id);
 | 
| +  }
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +void AudioOutputImpl::ReportErrorAndCloseStream(int stream_id) {
 | 
| +  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.
 | 
| +  auto callback = create_stream_callbacks_.find(stream_id);
 | 
| +
 | 
| +  mojo::ScopedSharedBufferHandle shared_buffer_handle =
 | 
| +      mojo::ScopedSharedBufferHandle(mojo::SharedBufferHandle());
 | 
| +
 | 
| +  mojo::ScopedHandle socket_handle = mojo::ScopedHandle(mojo::Handle());
 | 
| +
 | 
| +  callback->second.Run(stream_id, media::mojom::AudioOutputStreamPtr(),
 | 
| +                       std::move(shared_buffer_handle),
 | 
| +                       std::move(socket_handle));
 | 
| +
 | 
| +  if (audio_renderer_host_)
 | 
| +    audio_renderer_host_->get_audio_log()->OnError(stream_id);
 | 
| +  CloseStream(stream_id);
 | 
| +}
 | 
| +
 | 
| +void AudioOutputImpl::InitAudioRendererHost() {
 | 
| +  RenderProcessHost* process = frame_->GetProcess();
 | 
| +  // It's safe to use static casting of the |process| to RenderProcessHostImpl
 | 
| +  // outside the tests: there is just RenderProcessHostImpl and
 | 
| +  // MockRenderProcessHost that inherit from RenderProcessHost and
 | 
| +  // MockRenderProcessHost is used in just some tests that doesn't involve
 | 
| +  // AudioOutputImpl. It's also a common practice used in many places to access
 | 
| +  // RenderProcessHostImpl hidden methods  from RenderProcessHost.
 | 
| +  // See https://goo.gl/tkKvkd.
 | 
| +
 | 
| +  auto host =
 | 
| +      static_cast<RenderProcessHostImpl*>(process)->audio_renderer_host();
 | 
| +  if (host.get())
 | 
| +    audio_renderer_host_ = host;
 | 
| +}
 | 
| +
 | 
| +}  // namespace content
 | 
| 
 |