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/media/audio_output_impl.h" |
| 11 #include "content/browser/media/audio_output_stream_impl.h" |
| 12 #include "content/public/browser/render_frame_host.h" |
| 13 #include "mojo/edk/embedder/embedder.h" |
| 14 #include "mojo/public/cpp/system/handle.h" |
| 15 |
| 16 namespace content { |
| 17 |
| 18 namespace { |
| 19 base::SyncSocket::TransitDescriptor DuplicateSocket( |
| 20 base::SyncSocket::TransitDescriptor socket_descriptor) { |
| 21 base::SyncSocket::TransitDescriptor socket_descriptor_dup; |
| 22 |
| 23 #if defined(OS_WIN) |
| 24 socket_descriptor_dup = 0; |
| 25 if (!::DuplicateHandle(GetCurrentProcess(), // hSourceProcessHandle |
| 26 socket_descriptor, |
| 27 GetCurrentProcess(), // hTargetProcessHandle |
| 28 &socket_descriptor_dup, |
| 29 0, // dwDesiredAccess ignored due to SAME_ACCESS |
| 30 FALSE, // !bInheritHandle |
| 31 DUPLICATE_SAME_ACCESS)) { |
| 32 LOG(ERROR) << "Unable to duplicate socket handle."; |
| 33 } |
| 34 |
| 35 #else |
| 36 socket_descriptor_dup.fd = dup(socket_descriptor.fd); |
| 37 #endif |
| 38 return socket_descriptor_dup; |
| 39 } |
| 40 } |
| 41 |
| 42 AudioOutputImpl::AudioOutputImpl(RenderProcessHost* process, |
| 43 int render_frame_id, |
| 44 media::mojom::AudioOutputRequest request) |
| 45 : process_(process), render_frame_id_(render_frame_id), binding_(this) { |
| 46 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 47 binding_.Bind(std::move(request)); |
| 48 binding_.set_connection_error_handler(base::Bind( |
| 49 &AudioOutputImpl::Disconnect, process->GetID(), render_frame_id)); |
| 50 |
| 51 LOG(ERROR) << "AudioOutputImpl"; |
| 52 } |
| 53 |
| 54 AudioOutputImpl::~AudioOutputImpl() { |
| 55 Disconnect(render_frame_id_, render_frame_id_); |
| 56 for (auto it = create_stream_callbacks_.begin(); |
| 57 it != create_stream_callbacks_.end(); ++it) |
| 58 it->second.reset(); |
| 59 create_stream_callbacks_.clear(); |
| 60 LOG(ERROR) << "~AudioOutputImpl"; |
| 61 } |
| 62 |
| 63 // static |
| 64 void AudioOutputImpl::CreateService(RenderProcessHost* process, |
| 65 int render_frame_id, |
| 66 media::mojom::AudioOutputRequest request) { |
| 67 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 68 |
| 69 BrowserThread::PostTask( |
| 70 BrowserThread::IO, FROM_HERE, |
| 71 base::Bind(&AudioOutputImpl::CreateServiceOnIOThread, process, |
| 72 render_frame_id, base::Passed(&request))); |
| 73 } |
| 74 |
| 75 // static |
| 76 void AudioOutputImpl::CreateServiceOnIOThread( |
| 77 RenderProcessHost* process, |
| 78 int render_frame_id, |
| 79 media::mojom::AudioOutputRequest request) { |
| 80 if (request.is_pending()) { |
| 81 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 82 } |
| 83 |
| 84 new AudioOutputImpl(process, render_frame_id, std::move(request)); |
| 85 } |
| 86 |
| 87 void AudioOutputImpl::CreateStream(int stream_id, |
| 88 const media::AudioParameters& params, |
| 89 const CreateStreamCallback& callback) { |
| 90 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 91 |
| 92 create_stream_callbacks_.insert(std::make_pair(stream_id, callback)); |
| 93 process_->audio_renderer_host()->CreateStream(stream_id, render_frame_id_, |
| 94 params, this); |
| 95 } |
| 96 |
| 97 void AudioOutputImpl::StreamFactory( |
| 98 int stream_id, |
| 99 base::SharedMemory* shared_memory, |
| 100 base::SyncSocket::TransitDescriptor socket_descriptor) { |
| 101 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 102 |
| 103 auto callback = create_stream_callbacks_.find(stream_id); |
| 104 if (callback == create_stream_callbacks_.end()) { |
| 105 return; |
| 106 } |
| 107 |
| 108 media::mojom::AudioOutputStreamPtr stream_ptr = |
| 109 media::mojom::AudioOutputStreamPtr(); |
| 110 std::unique_ptr<AudioOutputStreamImpl> stream(new AudioOutputStreamImpl( |
| 111 stream_id, render_frame_id_, this, mojo::GetProxy(&stream_ptr))); |
| 112 |
| 113 DCHECK(stream_impls_.insert(std::make_pair(stream_id, std::move(stream))) |
| 114 .second); |
| 115 |
| 116 base::SharedMemoryHandle shared_memory_handle = |
| 117 base::SharedMemory::DuplicateHandle(shared_memory->handle()); |
| 118 |
| 119 MojoHandle mojo_foreign_memory_handle; |
| 120 |
| 121 MojoResult shared_buffer_result = mojo::edk::CreateSharedBufferWrapper( |
| 122 shared_memory_handle, shared_memory->requested_size(), false, |
| 123 &mojo_foreign_memory_handle); |
| 124 |
| 125 if (shared_buffer_result != MOJO_RESULT_OK) { |
| 126 DLOG(WARNING) << "Failed to wrap transit descriptor. Closing: " |
| 127 << shared_buffer_result; |
| 128 ReportErrorAndCloseStream(stream_id); |
| 129 return; |
| 130 } |
| 131 |
| 132 mojo::ScopedSharedBufferHandle shared_buffer_handle = |
| 133 mojo::ScopedSharedBufferHandle( |
| 134 mojo::SharedBufferHandle(mojo_foreign_memory_handle)); |
| 135 |
| 136 MojoHandle socket_descriptor_handle; |
| 137 MojoResult platform_handle_result; |
| 138 |
| 139 // The socket handle is going to be closed when |mojo_application_host_| is |
| 140 // reset. The renderer is going to need to close the socket handle |
| 141 // too when the output steam is closed. This is going to cause an issue |
| 142 // because both of them are going to close the same socket handle. |
| 143 // This is why a duplicate of the socket handler is going to be created, |
| 144 // stored in |socket_descriptor_dup| and sent through Mojo to the renderer so |
| 145 // it can be used by AudioOutputIPCDelegate. |
| 146 |
| 147 base::SyncSocket::TransitDescriptor socket_descriptor_dup = |
| 148 DuplicateSocket(socket_descriptor); |
| 149 #if defined(OS_WIN) |
| 150 platform_handle_result = mojo::edk::CreatePlatformHandleWrapper( |
| 151 mojo::edk::ScopedPlatformHandle( |
| 152 mojo::edk::PlatformHandle(socket_descriptor_dup), |
| 153 &socket_descriptor_handle); |
| 154 #else |
| 155 platform_handle_result = mojo::edk::CreatePlatformHandleWrapper( |
| 156 mojo::edk::ScopedPlatformHandle( |
| 157 mojo::edk::PlatformHandle(socket_descriptor_dup.fd)), |
| 158 &socket_descriptor_handle); |
| 159 #endif |
| 160 |
| 161 if (platform_handle_result != MOJO_RESULT_OK) { |
| 162 DLOG(WARNING) << "Failed to wrap platform handle. Closing: " |
| 163 << platform_handle_result; |
| 164 ReportErrorAndCloseStream(stream_id); |
| 165 return; |
| 166 } |
| 167 |
| 168 mojo::ScopedHandle socket_handle = |
| 169 mojo::ScopedHandle(mojo::Handle(socket_descriptor_handle)); |
| 170 callback->second.Run( |
| 171 stream_id, std::move(stream_ptr), |
| 172 std::move(shared_buffer_handle), std::move(socket_handle)); |
| 173 |
| 174 callback->second.reset(); |
| 175 create_stream_callbacks_.erase(stream_id); |
| 176 } |
| 177 |
| 178 bool AudioOutputImpl::CloseStream(int stream_id) { |
| 179 process_->audio_renderer_host()->CloseStream(stream_id); |
| 180 |
| 181 auto stream = stream_impls_.find(stream_id); |
| 182 if (stream == stream_impls_.end()) { |
| 183 return false; |
| 184 } |
| 185 stream_impls_.erase(stream_id); |
| 186 |
| 187 auto callback = create_stream_callbacks_.find(stream_id); |
| 188 |
| 189 if (callback != create_stream_callbacks_.end()) { |
| 190 callback->second.reset(); |
| 191 create_stream_callbacks_.erase(stream_id); |
| 192 } |
| 193 return true; |
| 194 } |
| 195 |
| 196 // static |
| 197 void AudioOutputImpl::Disconnect(int process_id, int render_frame_id) { |
| 198 LOG(ERROR) << "Disconnect"; |
| 199 } |
| 200 |
| 201 void AudioOutputImpl::ReportErrorAndCloseStream(int stream_id) { |
| 202 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 203 |
| 204 // Make sure this isn't a stray callback executing after the stream has been |
| 205 // closed, so error notifications aren't sent after clients believe the stream |
| 206 // is closed. |
| 207 auto callback = create_stream_callbacks_.find(stream_id); |
| 208 |
| 209 mojo::ScopedSharedBufferHandle shared_buffer_handle = |
| 210 mojo::ScopedSharedBufferHandle(mojo::SharedBufferHandle()); |
| 211 |
| 212 mojo::ScopedHandle socket_handle = mojo::ScopedHandle(mojo::Handle()); |
| 213 |
| 214 callback->second.Run(stream_id, media::mojom::AudioOutputStreamPtr(), |
| 215 std::move(shared_buffer_handle), |
| 216 std::move(socket_handle)); |
| 217 |
| 218 process_->audio_renderer_host()->get_audio_log()->OnError(stream_id); |
| 219 CloseStream(stream_id); |
| 220 } |
| 221 |
| 222 } // namespace content |
OLD | NEW |