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 #include "content/renderer/media/audio_output_ipc_factory.h" | |
6 | |
7 #include <string> | |
8 #include <utility> | |
9 | |
10 #include "base/files/file.h" | |
11 #include "base/memory/shared_memory_handle.h" | |
12 #include "base/single_thread_task_runner.h" | |
13 #include "base/sync_socket.h" | |
14 #include "content/renderer/media/webrtc_logging.h" | |
15 #include "media/base/audio_parameters.h" | |
16 #include "media/mojo/interfaces/audio_output.mojom.h" | |
17 #include "mojo/edk/embedder/embedder.h" | |
18 #include "mojo/public/c/system/buffer.h" | |
19 #include "mojo/public/cpp/bindings/binding.h" | |
20 #include "mojo/public/cpp/bindings/interface_request.h" | |
21 #include "mojo/public/cpp/system/handle.h" | |
22 #include "mojo/public/cpp/system/platform_handle.h" | |
23 #include "services/shell/public/cpp/interface_provider.h" | |
24 | |
25 namespace content { | |
26 | |
27 class AudioOutputIPCFactory::MojoAudioOutputIPC | |
28 : public media::AudioOutputIPC, | |
29 private media::mojom::AudioOutputStreamClient { | |
30 public: | |
31 MojoAudioOutputIPC(const scoped_refptr<AudioOutputIPCFactory> output_client, | |
32 const media::mojom::AudioOutputPtr* output_service, | |
33 int render_frame_id) | |
34 : output_client_(output_client), | |
35 output_service_(output_service), | |
36 delegate_(nullptr), | |
37 render_frame_id_(render_frame_id), | |
38 stream_created_(false), | |
39 weak_ptr_factory_(this) {} | |
40 | |
41 ~MojoAudioOutputIPC() override { StopClient(false); } | |
42 | |
43 private: | |
44 // media::AudioOutputIPC implementation | |
45 void RequestDeviceAuthorization(media::AudioOutputIPCDelegate* delegate, | |
o1ka
2016/09/02 07:31:46
Could you split declaration and definition of meth
Max Morin
2016/09/02 10:27:07
Yes. I'll go through this class later.
| |
46 int session_id, | |
47 const std::string& device_id, | |
48 const url::Origin& origin) override { | |
49 DCHECK(output_client_->io_task_runner_->BelongsToCurrentThread()); | |
50 DCHECK(delegate); | |
51 DCHECK(!delegate_); // Request exists/completed | |
52 DCHECK(!stream_created_); // Active stream exists | |
53 | |
54 delegate_ = delegate; | |
55 stream_id_ = output_client_->delegates_.Add(delegate); | |
56 (*output_service_) | |
57 ->RequestDeviceAuthorization( | |
58 stream_id_, render_frame_id_, session_id, device_id, origin, | |
59 base::Bind(&MojoAudioOutputIPC::RequestDeviceAuthorizationCallback, | |
60 weak_ptr_factory_.GetWeakPtr())); | |
61 } | |
62 | |
63 void CreateStream(media::AudioOutputIPCDelegate* delegate, | |
64 const media::AudioParameters& params) override { | |
65 DCHECK(output_client_->io_task_runner_->BelongsToCurrentThread()); | |
66 DCHECK(!stream_created_); | |
67 DCHECK(delegate); | |
68 DCHECK(!delegate_ || delegate_ == delegate); // Ensure same delegate. | |
69 | |
70 if (!delegate_) | |
71 stream_id_ = output_client_->delegates_.Add(delegate); | |
72 delegate_ = delegate; | |
o1ka
2016/09/02 07:31:46
Do we really want to continue if (delegate_ == del
| |
73 media::mojom::AudioOutputStreamClientPtr client; | |
74 binding_.reset(new mojo::Binding<media::mojom::AudioOutputStreamClient>( | |
75 this, mojo::GetProxy(&client))); | |
76 binding_->set_connection_error_handler(base::Bind( | |
77 &MojoAudioOutputIPC::OnError, weak_ptr_factory_.GetWeakPtr())); | |
78 | |
79 (*output_service_) | |
80 ->CreateStream(stream_id_, render_frame_id_, std::move(client), params, | |
81 base::Bind(&MojoAudioOutputIPC::CreateStreamCallback, | |
82 weak_ptr_factory_.GetWeakPtr())); | |
83 stream_created_ = true; | |
84 } | |
85 | |
86 void PlayStream() override { | |
87 DCHECK(output_client_->io_task_runner_->BelongsToCurrentThread()); | |
88 DCHECK(stream_created_); | |
89 | |
90 output_stream_service_->Play(); | |
91 } | |
92 | |
93 void PauseStream() override { | |
94 DCHECK(output_client_->io_task_runner_->BelongsToCurrentThread()); | |
95 DCHECK(stream_created_); | |
96 | |
97 output_stream_service_->Pause(); | |
98 } | |
99 | |
100 void SetVolume(double volume) override { | |
101 DCHECK(output_client_->io_task_runner_->BelongsToCurrentThread()); | |
102 DCHECK(stream_created_); | |
103 | |
104 output_stream_service_->SetVolume(volume); | |
105 } | |
106 | |
107 void CloseStream() override { | |
108 DCHECK(output_client_->io_task_runner_->BelongsToCurrentThread()); | |
109 | |
110 stream_created_ = false; | |
111 StopClient(false); | |
112 } | |
113 | |
114 // media::mojom::AudioOutputStreamClient implementation | |
115 void OnStreamStateChange( | |
116 media::mojom::AudioOutputStreamState state) override { | |
117 DCHECK(output_client_->io_task_runner_->BelongsToCurrentThread()); | |
118 | |
119 if (state == media::mojom::AudioOutputStreamState::ERROR) { | |
120 StopClient(true); | |
121 } else if (delegate_) { | |
122 // delegate_->OnStateChanged(state); | |
123 } | |
124 } | |
125 | |
126 // This error handler is used by |output_stream_service_| on a connection | |
127 // error. | |
128 void OnError() { | |
129 DCHECK(output_client_->io_task_runner_->BelongsToCurrentThread()); | |
130 | |
131 OnStreamStateChange(media::mojom::AudioOutputStreamState::ERROR); | |
132 } | |
133 | |
134 void StopClient(bool error) { | |
135 DCHECK(output_client_->io_task_runner_->BelongsToCurrentThread()); | |
136 | |
137 if (delegate_) { | |
138 if (error) | |
139 delegate_->OnStateChanged(media::mojom::AudioOutputStreamState::ERROR); | |
140 // |output_client_| may have called NotifyConnectionClose() | |
141 if (output_client_->delegates_.Lookup(stream_id_) == delegate_) | |
142 output_client_->delegates_.Remove(stream_id_); | |
143 } | |
144 | |
145 if (output_stream_service_.is_bound()) | |
146 (*output_service_)->CloseStream(stream_id_); | |
147 | |
148 delegate_ = nullptr; | |
149 output_stream_service_.reset(); | |
150 binding_.reset(); | |
151 } | |
152 | |
153 // Callback implementations | |
154 void RequestDeviceAuthorizationCallback( | |
155 int device_status, | |
156 const media::AudioParameters& output_params, | |
157 mojo::String matched_device_id) { | |
158 DCHECK(output_client_->io_task_runner_->BelongsToCurrentThread()); | |
159 | |
160 if (!delegate_) | |
161 return; | |
162 delegate_->OnDeviceAuthorized( | |
163 static_cast<media::OutputDeviceStatus>(device_status), output_params, | |
164 matched_device_id.get()); | |
165 } | |
166 | |
167 void CreateStreamCallback(media::mojom::AudioOutputStreamPtr stream, | |
168 mojo::ScopedSharedBufferHandle shared_buffer, | |
169 mojo::ScopedHandle socket_descriptor) { | |
170 DCHECK(output_client_->io_task_runner_->BelongsToCurrentThread()); | |
171 | |
172 // If stream creation failed, inform the delegate. | |
173 if (!stream.is_bound()) | |
174 return OnError(); | |
175 output_stream_service_ = std::move(stream); | |
176 output_stream_service_.set_connection_error_handler(base::Bind( | |
177 &MojoAudioOutputIPC::OnError, weak_ptr_factory_.GetWeakPtr())); | |
178 | |
179 base::SharedMemoryHandle handle; | |
180 size_t length; | |
181 bool read_only; | |
182 MojoResult unwrap_shared_memory_result = mojo::UnwrapSharedMemoryHandle( | |
183 std::move(shared_buffer), &handle, &length, &read_only); | |
184 | |
185 if (unwrap_shared_memory_result != MOJO_RESULT_OK) { | |
186 DLOG(ERROR) << "Failed to pass shared memory. Closing: " | |
187 << unwrap_shared_memory_result; | |
188 return OnError(); | |
189 } | |
190 | |
191 base::SyncSocket::TransitDescriptor descriptor; | |
192 MojoResult unwrap_platform_file_result = | |
193 mojo::UnwrapPlatformFile(std::move(socket_descriptor), | |
194 #if defined(OS_WIN) | |
195 &descriptor); | |
196 #else | |
197 &descriptor.fd); | |
198 #endif | |
199 if (unwrap_platform_file_result != MOJO_RESULT_OK) { | |
200 DLOG(ERROR) << "Failed to pass transit descriptor. Closing: " | |
201 << unwrap_platform_file_result; | |
202 return OnError(); | |
203 } | |
204 | |
205 base::SyncSocket::Handle socket_handle = | |
206 base::SyncSocket::UnwrapHandle(descriptor); | |
207 if (!delegate_) { | |
208 base::SharedMemory::CloseHandle(handle); | |
209 base::SyncSocket socket(socket_handle); | |
210 return; | |
211 } | |
212 delegate_->OnStreamCreated(handle, socket_handle, length); | |
213 } | |
214 | |
215 const scoped_refptr<AudioOutputIPCFactory> output_client_; | |
216 const media::mojom::AudioOutputPtr* output_service_; | |
217 media::mojom::AudioOutputStreamPtr output_stream_service_; | |
218 media::AudioOutputIPCDelegate* delegate_; | |
219 | |
220 const int render_frame_id_; | |
221 int stream_id_; | |
222 bool stream_created_; | |
223 | |
224 std::unique_ptr<mojo::Binding<media::mojom::AudioOutputStreamClient>> | |
225 binding_; | |
226 | |
227 base::WeakPtrFactory<MojoAudioOutputIPC> weak_ptr_factory_; | |
228 | |
229 DISALLOW_COPY_AND_ASSIGN(MojoAudioOutputIPC); | |
230 }; | |
231 | |
232 AudioOutputIPCFactory::AudioOutputIPCFactory( | |
233 const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, | |
234 shell::InterfaceProvider* interface_provider) | |
235 : io_task_runner_(io_task_runner) { | |
236 DCHECK(!g_audio_output_ipc_factory); | |
237 g_audio_output_ipc_factory = this; | |
238 | |
239 interface_provider->GetInterface(&output_service_); | |
240 output_service_.Bind(output_service_.PassInterface(), io_task_runner_); | |
241 } | |
242 | |
243 AudioOutputIPCFactory::~AudioOutputIPCFactory() { | |
244 DCHECK(g_audio_output_ipc_factory == this); | |
245 g_audio_output_ipc_factory = nullptr; | |
246 | |
247 io_task_runner_->PostTask( | |
248 FROM_HERE, base::Bind(&AudioOutputIPCFactory::NotifyConnectionClose, | |
249 base::RetainedRef(this))); | |
250 } | |
251 | |
252 AudioOutputIPCFactory* AudioOutputIPCFactory::g_audio_output_ipc_factory = | |
253 nullptr; | |
254 | |
255 // static | |
256 const scoped_refptr<AudioOutputIPCFactory> AudioOutputIPCFactory::Get() { | |
257 return g_audio_output_ipc_factory; | |
258 } | |
259 | |
260 std::unique_ptr<media::AudioOutputIPC> | |
261 AudioOutputIPCFactory::CreateAudioOutputIPC(int render_frame_id) { | |
262 return std::unique_ptr<media::AudioOutputIPC>( | |
263 new MojoAudioOutputIPC(this, &output_service_, render_frame_id)); | |
264 } | |
265 | |
266 void AudioOutputIPCFactory::OnConnectionError() { | |
267 DCHECK(io_task_runner_->BelongsToCurrentThread()); | |
268 | |
269 NotifyConnectionClose(); | |
270 } | |
271 | |
272 void AudioOutputIPCFactory::NotifyConnectionClose() { | |
273 DCHECK(io_task_runner_->BelongsToCurrentThread()); | |
274 | |
275 output_service_.reset(); | |
276 IDMap<media::AudioOutputIPCDelegate>::iterator it(&delegates_); | |
277 while (!it.IsAtEnd()) { | |
278 it.GetCurrentValue()->OnIPCClosed(); | |
279 delegates_.Remove(it.GetCurrentKey()); | |
280 it.Advance(); | |
281 } | |
282 } | |
283 | |
284 } // namespace content | |
OLD | NEW |