OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 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/mojo_audio_output_ipc.h" | |
6 | |
7 #include <utility> | |
8 | |
9 #include "media/audio/audio_device_description.h" | |
10 #include "mojo/public/cpp/system/platform_handle.h" | |
11 | |
12 namespace content { | |
13 | |
14 namespace { | |
15 | |
16 using AuthorizationCallback = | |
17 mojom::RendererAudioOutputStreamFactory::RequestDeviceAuthorizationCallback; | |
18 | |
19 void TrivialAuthorizedCallback(media::OutputDeviceStatus, | |
20 const media::AudioParameters&, | |
21 const std::string&) {} | |
22 | |
23 // This class wraps a callback. If this class is destroyed without the callback | |
24 // being called, it will call it with an error. This is needed since the | |
25 // AudioOutputDevice could otherwise wait forever for device parameters in the | |
26 // case of a connection error. | |
27 class AuthorizationCallbackWrapper { | |
28 public: | |
29 static AuthorizationCallback Wrap(AuthorizationCallback callback) { | |
30 return base::BindOnce( | |
31 AuthorizationCallbackWrapper::Call, | |
32 base::Passed(AuthorizationCallbackWrapper(std::move(callback)))); | |
33 } | |
34 | |
35 AuthorizationCallbackWrapper(AuthorizationCallbackWrapper&& other) | |
36 : callback_(std::move(other.callback_)) { | |
37 // It's not explicitly stated that moving from a OnceCallback resets the | |
38 // moved-from callback, so reset it here. | |
39 other.callback_ = AuthorizationCallback(); | |
40 } | |
41 | |
42 ~AuthorizationCallbackWrapper() { | |
43 if (callback_) { | |
44 std::move(callback_).Run( | |
45 media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL, | |
46 media::AudioParameters::UnavailableDeviceParams(), std::string()); | |
47 } | |
48 } | |
49 | |
50 private: | |
51 explicit AuthorizationCallbackWrapper(AuthorizationCallback callback) | |
52 : callback_(std::move(callback)) {} | |
53 | |
54 static void Call(AuthorizationCallbackWrapper callback_wrapper, | |
55 media::OutputDeviceStatus status, | |
56 const media::AudioParameters& params, | |
57 const std::string& device_id) { | |
58 if (callback_wrapper.callback_) { | |
59 std::move(callback_wrapper.callback_).Run(status, params, device_id); | |
60 // Make sure we don't call again in destructor: | |
61 callback_wrapper.callback_ = AuthorizationCallback(); | |
62 } | |
63 } | |
64 | |
65 AuthorizationCallback callback_; | |
66 | |
67 DISALLOW_COPY_AND_ASSIGN(AuthorizationCallbackWrapper); | |
68 }; | |
69 | |
70 } // namespace | |
71 | |
72 MojoAudioOutputIPC::MojoAudioOutputIPC(FactoryAccessor factory_accessor) | |
73 : factory_accessor_(std::move(factory_accessor)), weak_factory_(this) { | |
74 DETACH_FROM_THREAD(thread_checker_); | |
75 } | |
76 | |
77 MojoAudioOutputIPC::~MojoAudioOutputIPC() { | |
78 DCHECK(!AuthorizationRequested() && !StreamCreationRequested()) | |
79 << "CloseStream must be called before destructing the AudioOutputIPC"; | |
80 // No thread check. | |
81 // Destructing |weak_factory_| on any thread is safe since it's not used after | |
82 // the final call to CloseStream, where its pointers are invalidated. | |
83 } | |
84 | |
85 void MojoAudioOutputIPC::RequestDeviceAuthorization( | |
86 media::AudioOutputIPCDelegate* delegate, | |
87 int session_id, | |
88 const std::string& device_id, | |
89 const url::Origin& security_origin) { | |
90 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | |
91 DCHECK(delegate); | |
92 DCHECK(!delegate_); | |
93 DCHECK(!AuthorizationRequested()); | |
94 DCHECK(!StreamCreationRequested()); | |
95 delegate_ = delegate; | |
96 | |
97 // We wrap the callback here so that we are sure to always get the | |
98 // authorization reply, even if the connection is closed. | |
99 DoRequestDeviceAuthorization( | |
100 session_id, device_id, | |
101 AuthorizationCallbackWrapper::Wrap( | |
DaleCurtis
2017/05/24 01:36:54
Hmm, why not just have a bool set here and then no
Max Morin
2017/05/30 14:17:11
MojoAudioOutputIPC is owned by the AudioOutputDevi
| |
102 base::BindOnce(&MojoAudioOutputIPC::RecievedDeviceAuthorization, | |
103 weak_factory_.GetWeakPtr()))); | |
104 } | |
105 | |
106 void MojoAudioOutputIPC::CreateStream(media::AudioOutputIPCDelegate* delegate, | |
107 const media::AudioParameters& params) { | |
108 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | |
109 DCHECK(delegate); | |
110 DCHECK(!StreamCreationRequested()); | |
111 if (!AuthorizationRequested()) { | |
112 DCHECK(!delegate_); | |
113 delegate_ = delegate; | |
114 // No authorization requested yet. Request one for the default device. | |
115 // Since the delegate didn't explicitly request authorization, we shouldn't | |
116 // send a callback to it. | |
117 if (!DoRequestDeviceAuthorization( | |
118 0, media::AudioDeviceDescription::kDefaultDeviceId, | |
119 base::Bind(&TrivialAuthorizedCallback))) { | |
120 return; | |
121 } | |
122 } | |
123 | |
124 DCHECK(delegate_ == delegate); | |
DaleCurtis
2017/05/24 01:36:54
DCHECK_EQ
DaleCurtis
2017/05/24 01:36:54
DCHECK_EQ
Max Morin
2017/05/30 14:17:11
Done.
| |
125 // Since the creation callback won't fire if the provider binding is gone | |
126 // and |this| owns |stream_provider_|, unretained is safe. | |
127 stream_provider_->Acquire( | |
128 mojo::MakeRequest(&stream_), params, | |
129 base::Bind(&MojoAudioOutputIPC::StreamCreated, base::Unretained(this))); | |
130 | |
131 // Unretained is safe because |delegate_| must remain valid until | |
132 // CloseStream is called, and |stream_provider_| is reset in CloseStream. | |
133 stream_.set_connection_error_handler(base::Bind( | |
134 &media::AudioOutputIPCDelegate::OnError, base::Unretained(delegate_))); | |
135 } | |
136 | |
137 void MojoAudioOutputIPC::PlayStream() { | |
138 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | |
139 if (stream_.is_bound()) | |
140 stream_->Play(); | |
141 } | |
142 | |
143 void MojoAudioOutputIPC::PauseStream() { | |
144 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | |
145 if (stream_.is_bound()) | |
146 stream_->Pause(); | |
147 } | |
148 | |
149 void MojoAudioOutputIPC::CloseStream() { | |
150 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | |
151 stream_provider_.reset(); | |
152 stream_.reset(); | |
153 delegate_ = nullptr; | |
154 | |
155 // Cancel any pending callbacks for this stream. | |
DaleCurtis
2017/05/24 01:36:54
Per above, if you use a bool you'd want to notify
Max Morin
2017/05/30 14:17:11
There could be a sequence of events like "RequestD
| |
156 weak_factory_.InvalidateWeakPtrs(); | |
157 } | |
158 | |
159 void MojoAudioOutputIPC::SetVolume(double volume) { | |
160 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | |
161 if (stream_.is_bound()) | |
162 stream_->SetVolume(volume); | |
163 } | |
164 | |
165 bool MojoAudioOutputIPC::AuthorizationRequested() { | |
166 return stream_provider_.is_bound(); | |
167 } | |
168 | |
169 bool MojoAudioOutputIPC::StreamCreationRequested() { | |
170 return stream_.is_bound(); | |
171 } | |
172 | |
173 media::mojom::AudioOutputStreamProviderRequest | |
174 MojoAudioOutputIPC::MakeProviderRequest() { | |
175 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | |
176 DCHECK(!AuthorizationRequested()); | |
177 media::mojom::AudioOutputStreamProviderRequest request = | |
178 mojo::MakeRequest(&stream_provider_); | |
179 | |
180 // Unretained is safe because |delegate_| must remain valid until | |
181 // CloseStream is called, and |stream_provider_| is reset in CloseStream. | |
182 stream_provider_.set_connection_error_handler(base::Bind( | |
183 &media::AudioOutputIPCDelegate::OnError, base::Unretained(delegate_))); | |
184 return request; | |
185 } | |
186 | |
187 bool MojoAudioOutputIPC::DoRequestDeviceAuthorization( | |
188 int session_id, | |
189 const std::string& device_id, | |
190 AuthorizationCallback callback) { | |
191 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | |
192 auto* factory = factory_accessor_.Run(); | |
193 if (!factory) { | |
194 LOG(ERROR) << "MojoAudioOutputIPC failed to acquire factory"; | |
195 | |
196 media::AudioOutputIPCDelegate* delegate = delegate_; | |
197 CloseStream(); | |
198 delegate->OnIPCClosed(); // deletes |this|. | |
199 return false; | |
200 } | |
201 | |
202 // We wrap the callback here so that we are sure to always get the | |
DaleCurtis
2017/05/24 01:36:54
No wrapping done here?
Max Morin
2017/05/30 14:17:10
Done.
| |
203 // authorization reply, even if the connection is closed. | |
204 factory->RequestDeviceAuthorization(MakeProviderRequest(), session_id, | |
205 device_id, std::move(callback)); | |
206 return true; | |
207 } | |
208 | |
209 void MojoAudioOutputIPC::RecievedDeviceAuthorization( | |
DaleCurtis
2017/05/24 01:36:54
Received
Max Morin
2017/05/30 14:17:11
Done.
| |
210 media::OutputDeviceStatus status, | |
211 const media::AudioParameters& params, | |
212 const std::string& device_id) const { | |
213 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | |
214 DCHECK(delegate_); | |
215 delegate_->OnDeviceAuthorized(status, params, device_id); | |
216 } | |
217 | |
218 void MojoAudioOutputIPC::StreamCreated( | |
219 mojo::ScopedSharedBufferHandle shared_memory, | |
220 mojo::ScopedHandle socket) { | |
221 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | |
222 DCHECK(delegate_); | |
223 DCHECK(socket.is_valid()); | |
224 DCHECK(shared_memory.is_valid()); | |
225 | |
226 base::PlatformFile socket_handle; | |
227 auto result = mojo::UnwrapPlatformFile(std::move(socket), &socket_handle); | |
228 DCHECK_EQ(result, MOJO_RESULT_OK); | |
229 | |
230 base::SharedMemoryHandle memory_handle; | |
231 bool read_only = false; | |
232 size_t memory_length = 0; | |
233 result = mojo::UnwrapSharedMemoryHandle( | |
234 std::move(shared_memory), &memory_handle, &memory_length, &read_only); | |
235 DCHECK_EQ(result, MOJO_RESULT_OK); | |
236 DCHECK(!read_only); | |
237 | |
238 delegate_->OnStreamCreated(memory_handle, socket_handle, memory_length); | |
239 } | |
240 | |
241 } // namespace content | |
OLD | NEW |