OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #if defined(OS_WIN) |
| 6 #include <windows.h> |
| 7 #endif |
| 8 |
| 9 #include "base/memory/shared_memory.h" |
| 10 #include "content/browser/frame_host/render_frame_host_impl.h" |
| 11 #include "content/browser/media/audio_output_impl.h" |
| 12 #include "content/browser/media/audio_output_stream_impl.h" |
| 13 #include "content/browser/renderer_host/render_process_host_impl.h" |
| 14 #include "content/public/browser/render_frame_host.h" |
| 15 #include "mojo/edk/embedder/embedder.h" |
| 16 #include "mojo/public/cpp/system/handle.h" |
| 17 #include "mojo/public/cpp/system/platform_handle.h" |
| 18 |
| 19 namespace content { |
| 20 |
| 21 namespace { |
| 22 |
| 23 base::SyncSocket::TransitDescriptor DuplicateSocket( |
| 24 base::SyncSocket::TransitDescriptor socket_descriptor) { |
| 25 base::SyncSocket::TransitDescriptor socket_descriptor_dup; |
| 26 |
| 27 #if defined(OS_WIN) |
| 28 socket_descriptor_dup = 0; |
| 29 if (!::DuplicateHandle(GetCurrentProcess(), // hSourceProcessHandle |
| 30 socket_descriptor, |
| 31 GetCurrentProcess(), // hTargetProcessHandle |
| 32 &socket_descriptor_dup, |
| 33 0, // dwDesiredAccess ignored due to SAME_ACCESS |
| 34 FALSE, // !bInheritHandle |
| 35 DUPLICATE_SAME_ACCESS)) { |
| 36 LOG(ERROR) << "Unable to duplicate socket handle."; |
| 37 } |
| 38 |
| 39 #else |
| 40 socket_descriptor_dup.fd = dup(socket_descriptor.fd); |
| 41 #endif |
| 42 return socket_descriptor_dup; |
| 43 } |
| 44 |
| 45 } // namespace |
| 46 |
| 47 AudioOutputImpl::AudioOutputImpl(RenderFrameHostImpl* frame, |
| 48 media::mojom::AudioOutputRequest request) |
| 49 : frame_(frame) { |
| 50 binding_.reset( |
| 51 new mojo::Binding<media::mojom::AudioOutput>(this, std::move(request))); |
| 52 |
| 53 } |
| 54 |
| 55 AudioOutputImpl::~AudioOutputImpl() { |
| 56 auto audio_renderer_host = audio_renderer_host_.get(); |
| 57 |
| 58 if (audio_renderer_host) |
| 59 audio_renderer_host->CleanAudioOutputImpl(this); |
| 60 |
| 61 for (auto it = create_stream_callbacks_.begin(); |
| 62 it != create_stream_callbacks_.end(); ++it) { |
| 63 it->second.reset(); |
| 64 } |
| 65 create_stream_callbacks_.clear(); |
| 66 } |
| 67 |
| 68 // static |
| 69 void AudioOutputImpl::CreateService(RenderFrameHostImpl* frame, |
| 70 media::mojom::AudioOutputRequest request) { |
| 71 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 72 |
| 73 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
| 74 base::Bind(&AudioOutputImpl::CreateServiceOnIOThread, |
| 75 frame, base::Passed(&request))); |
| 76 } |
| 77 |
| 78 void AudioOutputImpl::Reset() { |
| 79 |
| 80 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) |
| 81 BrowserThread::PostTask( |
| 82 BrowserThread::IO, FROM_HERE, |
| 83 base::Bind(&AudioOutputImpl::Reset, base::Unretained(this))); |
| 84 else |
| 85 delete this; |
| 86 } |
| 87 |
| 88 // static |
| 89 void AudioOutputImpl::CreateServiceOnIOThread( |
| 90 RenderFrameHostImpl* frame, |
| 91 media::mojom::AudioOutputRequest request) { |
| 92 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 93 frame->set_audio_output_impl(new AudioOutputImpl(frame, std::move(request))); |
| 94 } |
| 95 |
| 96 void AudioOutputImpl::CreateStream(int stream_id, |
| 97 const media::AudioParameters& params, |
| 98 const CreateStreamCallback& callback) { |
| 99 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 100 |
| 101 create_stream_callbacks_.insert(std::make_pair(stream_id, callback)); |
| 102 InitAudioRendererHost(); |
| 103 if (audio_renderer_host_) |
| 104 audio_renderer_host_->CreateStream(stream_id, frame_->GetRoutingID(), |
| 105 params, this); |
| 106 } |
| 107 |
| 108 void AudioOutputImpl::CreateStreamFactory( |
| 109 int stream_id, |
| 110 base::SharedMemory* shared_memory, |
| 111 base::SyncSocket::TransitDescriptor socket_descriptor) { |
| 112 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 113 |
| 114 auto callback = create_stream_callbacks_.find(stream_id); |
| 115 if (callback == create_stream_callbacks_.end()) { |
| 116 ReportErrorAndCloseStream(stream_id); |
| 117 return; |
| 118 } |
| 119 |
| 120 media::mojom::AudioOutputStreamPtr stream_ptr = |
| 121 media::mojom::AudioOutputStreamPtr(); |
| 122 std::unique_ptr<AudioOutputStreamImpl> stream( |
| 123 new AudioOutputStreamImpl(stream_id, this, mojo::GetProxy(&stream_ptr))); |
| 124 stream_impls_.insert(std::make_pair(stream_id, std::move(stream))); |
| 125 |
| 126 base::SharedMemoryHandle shared_memory_handle = |
| 127 base::SharedMemory::DuplicateHandle(shared_memory->handle()); |
| 128 if (!base::SharedMemory::IsHandleValid(shared_memory_handle)) { |
| 129 ReportErrorAndCloseStream(stream_id); |
| 130 return; |
| 131 } |
| 132 mojo::ScopedSharedBufferHandle shared_buffer_handle = |
| 133 mojo::WrapSharedMemoryHandle(shared_memory_handle, |
| 134 shared_memory->requested_size(), false); |
| 135 // The socket that we will send from the browser to the renderer is a |
| 136 // |foreign_socket_| which is a part AudioSyncReader that is owned by |
| 137 // AudioEntry. The socket handle will be closed when the AudioEntry it |
| 138 // belongs to will be closed. However, with mojo and unlike IPC, the |
| 139 // ownership of the handle is transferred to the target process. It's no |
| 140 // longer a valid handle in the sending process and it is an error to try |
| 141 // closing it there. So, if the socket handle is closed when the AudioEntry |
| 142 // is deleted, we will have an error. To fix this error we could just |
| 143 // duplicate the socket and send the duplicate to the renderer. Thus, we |
| 144 // will avoid having an problem when closing the socket. |
| 145 // See https://goo.gl/ACwSfa. |
| 146 base::SyncSocket::TransitDescriptor socket_descriptor_dup = |
| 147 DuplicateSocket(socket_descriptor); |
| 148 #if defined(OS_WIN) |
| 149 mojo::ScopedHandle socket_handle = |
| 150 mojo::WrapPlatformFile(socket_descriptor_dup); |
| 151 #else |
| 152 mojo::ScopedHandle socket_handle = |
| 153 mojo::WrapPlatformFile(socket_descriptor_dup.fd); |
| 154 #endif |
| 155 |
| 156 callback->second.Run(stream_id, std::move(stream_ptr), |
| 157 std::move(shared_buffer_handle), |
| 158 std::move(socket_handle)); |
| 159 callback->second.reset(); |
| 160 create_stream_callbacks_.erase(stream_id); |
| 161 } |
| 162 |
| 163 bool AudioOutputImpl::CloseStream(int stream_id) { |
| 164 if (audio_renderer_host_.get()) |
| 165 audio_renderer_host_->CloseStream(stream_id); |
| 166 |
| 167 if (stream_impls_.erase(stream_id) == 0) |
| 168 return false; |
| 169 |
| 170 auto callback = create_stream_callbacks_.find(stream_id); |
| 171 |
| 172 if (callback != create_stream_callbacks_.end()) { |
| 173 callback->second.reset(); |
| 174 create_stream_callbacks_.erase(stream_id); |
| 175 } |
| 176 return true; |
| 177 } |
| 178 |
| 179 void AudioOutputImpl::ReportErrorAndCloseStream(int stream_id) { |
| 180 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 181 |
| 182 // Make sure this isn't a stray callback executing after the stream has been |
| 183 // closed, so error notifications aren't sent after clients believe the stream |
| 184 // is closed. |
| 185 auto callback = create_stream_callbacks_.find(stream_id); |
| 186 |
| 187 mojo::ScopedSharedBufferHandle shared_buffer_handle = |
| 188 mojo::ScopedSharedBufferHandle(mojo::SharedBufferHandle()); |
| 189 |
| 190 mojo::ScopedHandle socket_handle = mojo::ScopedHandle(mojo::Handle()); |
| 191 |
| 192 callback->second.Run(stream_id, media::mojom::AudioOutputStreamPtr(), |
| 193 std::move(shared_buffer_handle), |
| 194 std::move(socket_handle)); |
| 195 |
| 196 if (audio_renderer_host_) |
| 197 audio_renderer_host_->get_audio_log()->OnError(stream_id); |
| 198 CloseStream(stream_id); |
| 199 } |
| 200 |
| 201 void AudioOutputImpl::InitAudioRendererHost() { |
| 202 RenderProcessHost* process = frame_->GetProcess(); |
| 203 // It's safe to use static casting of the |process| to RenderProcessHostImpl |
| 204 // outside the tests: there is just RenderProcessHostImpl and |
| 205 // MockRenderProcessHost that inherit from RenderProcessHost and |
| 206 // MockRenderProcessHost is used in just some tests that doesn't involve |
| 207 // AudioOutputImpl. It's also a common practice used in many places to access |
| 208 // RenderProcessHostImpl hidden methods from RenderProcessHost. |
| 209 // See https://goo.gl/tkKvkd. |
| 210 |
| 211 auto host = |
| 212 static_cast<RenderProcessHostImpl*>(process)->audio_renderer_host(); |
| 213 if (host.get()) |
| 214 audio_renderer_host_ = host; |
| 215 } |
| 216 |
| 217 } // namespace content |
OLD | NEW |