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 void TrivialAuthorizedCallback(media::OutputDeviceStatus, | |
17 const media::AudioParameters&, | |
18 const std::string&) {} | |
19 | |
20 } // namespace | |
21 | |
22 // This class wraps a callback. If this class is destroyed without the callback | |
23 // being called, it will call it with an error. This is needed since the | |
24 // AudioOutputDevice could otherwise wait forever for device parameters in the | |
25 // case of a connection error. | |
o1ka
2017/05/11 10:58:41
Nice!
| |
26 class AuthorizationCallbackWrapper { | |
27 public: | |
28 using CallbackType = base::OnceCallback<void(media::OutputDeviceStatus, | |
29 const media::AudioParameters&, | |
30 const std::string&)>; | |
31 | |
32 static CallbackType Wrap(CallbackType callback) { | |
33 return base::BindOnce( | |
34 AuthorizationCallbackWrapper::Call, | |
35 base::Passed(AuthorizationCallbackWrapper(std::move(callback)))); | |
36 } | |
37 | |
38 AuthorizationCallbackWrapper(AuthorizationCallbackWrapper&& other) | |
39 : callback_(std::move(other.callback_)) { | |
40 // It's not explicitly stated that moving from a OnceCallback resets the | |
Ken Rockot(use gerrit already)
2017/05/08 16:04:24
Seems like a documentation bug then. Moving a Once
Max Morin
2017/05/11 15:31:17
Leaving this after discussion on cxx.
| |
41 // moved-from callback, so reset it here. | |
42 other.callback_ = CallbackType(); | |
43 } | |
44 | |
45 AuthorizationCallbackWrapper& operator=( | |
46 AuthorizationCallbackWrapper&& other) = delete; | |
47 | |
48 ~AuthorizationCallbackWrapper() { | |
49 if (callback_) { | |
50 std::move(callback_).Run( | |
51 media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL, | |
52 media::AudioParameters::UnavailableDeviceParams(), std::string()); | |
53 } | |
54 } | |
55 | |
56 private: | |
57 explicit AuthorizationCallbackWrapper(CallbackType callback) | |
58 : callback_(std::move(callback)) {} | |
59 | |
60 static void Call(AuthorizationCallbackWrapper callback_wrapper, | |
61 media::OutputDeviceStatus status, | |
62 const media::AudioParameters& params, | |
63 const std::string& device_id) { | |
64 if (callback_wrapper.callback_) { | |
65 std::move(callback_wrapper.callback_).Run(status, params, device_id); | |
66 // Make sure we don't call again in destructor: | |
67 callback_wrapper.callback_ = CallbackType(); | |
Ken Rockot(use gerrit already)
2017/05/08 16:04:24
Also unnecessary.
Max Morin
2017/05/11 15:31:17
Same.
| |
68 } | |
69 } | |
70 | |
71 CallbackType callback_; | |
72 | |
73 DISALLOW_COPY_AND_ASSIGN(AuthorizationCallbackWrapper); | |
74 }; | |
75 | |
76 MojoAudioOutputIPC::MojoAudioOutputIPC(FactoryAccessor factory_accessor) | |
77 : factory_accessor_(std::move(factory_accessor)), weak_factory_(this) { | |
78 thread_checker_.DetachFromThread(); | |
79 } | |
80 | |
81 MojoAudioOutputIPC::~MojoAudioOutputIPC() { | |
82 // No thread check. | |
83 DCHECK(!AuthorizationRequested()); | |
84 DCHECK(!StreamCreationRequested()); | |
85 // Destructing |weak_factory_| on any thread is safe since it's not used after | |
86 // the final call to CloseStream, where its pointers are invalidated. | |
o1ka
2017/05/11 10:58:40
What guarantees that CloseStream() has been called
Max Morin
2017/05/11 15:31:18
This is part of the AudioOutputIPC contract. It's
| |
87 } | |
88 | |
89 media::mojom::AudioOutputStreamProviderRequest | |
90 MojoAudioOutputIPC::MakeProviderRequest( | |
91 media::AudioOutputIPCDelegate* delegate) { | |
92 DCHECK(thread_checker_.CalledOnValidThread()); | |
93 DCHECK(!AuthorizationRequested()); | |
94 media::mojom::AudioOutputStreamProviderRequest request = | |
95 mojo::MakeRequest(&stream_provider_); | |
96 | |
97 // Unretained is safe because |delegate| owns |this|. | |
o1ka
2017/05/11 10:58:41
See the comment about storing a pointer to |delega
Max Morin
2017/05/11 15:31:18
Not sure what it has to do with this code?
| |
98 stream_provider_.set_connection_error_handler(base::Bind( | |
99 &media::AudioOutputIPCDelegate::OnError, base::Unretained(delegate))); | |
100 return request; | |
101 } | |
102 | |
103 void MojoAudioOutputIPC::RequestDeviceAuthorization( | |
104 media::AudioOutputIPCDelegate* delegate, | |
105 int session_id, | |
106 const std::string& device_id, | |
107 const url::Origin& security_origin) { | |
108 DCHECK(thread_checker_.CalledOnValidThread()); | |
109 DCHECK(delegate); | |
110 DCHECK(!AuthorizationRequested()); | |
111 DCHECK(!StreamCreationRequested()); | |
112 auto* factory = factory_accessor_.Run(); | |
113 if (!factory) { | |
114 LOG(ERROR) << "MojoAudioOutputIPC failed to acquire factory"; | |
115 delegate->OnIPCClosed(); // deletes |this|. | |
116 return; | |
117 } | |
118 | |
119 // We wrap the callback here so that we are sure to always get the | |
120 // authorization reply, even if the connection is closed. | |
o1ka
2017/05/11 10:58:41
Looking forward to unit tests :)
Max Morin
2017/05/11 15:31:18
:)
| |
121 factory->RequestDeviceAuthorization( | |
122 MakeProviderRequest(delegate), session_id, device_id, | |
123 AuthorizationCallbackWrapper::Wrap( | |
124 base::Bind(&MojoAudioOutputIPC::RecievedDeviceAuthorization, | |
125 weak_factory_.GetWeakPtr(), delegate))); | |
126 } | |
127 | |
128 void MojoAudioOutputIPC::RecievedDeviceAuthorization( | |
129 media::AudioOutputIPCDelegate* delegate, | |
o1ka
2017/05/11 10:58:41
What is the point of passing |delegate| around? Wh
Max Morin
2017/05/11 15:31:17
The AudioOutputIPC interface is written to get |de
o1ka
2017/05/15 13:27:08
Not sure why it is more natural. MojoAudioOutputIP
Max Morin
2017/05/16 15:51:35
I changed the class to store |delegate| so we can
| |
130 media::OutputDeviceStatus status, | |
131 const media::AudioParameters& params, | |
132 const std::string& device_id) const { | |
133 DCHECK(thread_checker_.CalledOnValidThread()); | |
134 delegate->OnDeviceAuthorized(status, params, device_id); | |
135 } | |
136 | |
137 void MojoAudioOutputIPC::CreateStream(media::AudioOutputIPCDelegate* delegate, | |
o1ka
2017/05/11 10:58:41
Could you arrange implementations in the declarati
Max Morin
2017/05/11 15:31:18
Done.
| |
138 const media::AudioParameters& params) { | |
139 DCHECK(thread_checker_.CalledOnValidThread()); | |
140 DCHECK(delegate); | |
141 DCHECK(!StreamCreationRequested()); | |
142 if (!AuthorizationRequested()) { | |
143 // No authorization requested yet. Request one for the default device. | |
144 // This also means we shouldn't callback to the delegate for the reply. | |
145 auto* factory = factory_accessor_.Run(); | |
146 if (!factory) { | |
147 LOG(ERROR) << "MojoAudioOutputIPC failed to acquire factory"; | |
148 delegate->OnIPCClosed(); // deletes |this|. | |
149 return; | |
150 } | |
151 | |
152 factory->RequestDeviceAuthorization( | |
o1ka
2017/05/11 10:58:40
Combine duplicated code here and in RequestDeviceA
Max Morin
2017/05/11 15:31:17
If I introduced a helper function, it would have t
o1ka
2017/05/15 13:27:08
Not sure what do you mean.
bool DoRequestDeviceAut
Max Morin
2017/05/16 15:51:35
Done.
| |
153 MakeProviderRequest(delegate), 0, | |
154 media::AudioDeviceDescription::kDefaultDeviceId, | |
155 base::Bind(&TrivialAuthorizedCallback)); | |
o1ka
2017/05/11 10:58:40
Comment why you don't process authorization respon
Max Morin
2017/05/11 15:31:17
Done.
| |
156 } | |
157 | |
158 // Since the creation callback won't fire if the provider binding is gone, | |
159 // unretained is safe. | |
o1ka
2017/05/11 10:58:41
Add a comment why we can't use Weak here?
Max Morin
2017/05/11 15:31:17
We can, but we don't need to.
| |
160 stream_provider_->Acquire(mojo::MakeRequest(&stream_), params, | |
161 base::Bind(&MojoAudioOutputIPC::StreamCreated, | |
162 base::Unretained(this), delegate)); | |
163 | |
164 // Unretained is safe because |delegate| owns |this|. | |
o1ka
2017/05/11 10:58:40
I'd prefer the term "outlives". The code changes q
Max Morin
2017/05/11 15:31:18
Comment is bad, I changed it. See above for storin
| |
165 stream_.set_connection_error_handler(base::Bind( | |
166 &media::AudioOutputIPCDelegate::OnError, base::Unretained(delegate))); | |
o1ka
2017/05/11 10:58:40
This is already done in MakeProviderRequest(), isn
Max Morin
2017/05/11 15:31:18
For the provider. This is for the stream.
o1ka
2017/05/15 13:27:08
Acknowledged.
| |
167 } | |
168 | |
169 void MojoAudioOutputIPC::StreamCreated( | |
170 media::AudioOutputIPCDelegate* delegate, | |
171 mojo::ScopedSharedBufferHandle shared_memory, | |
172 mojo::ScopedHandle socket) { | |
173 DCHECK(thread_checker_.CalledOnValidThread()); | |
174 DCHECK(socket.is_valid()); | |
175 DCHECK(shared_memory.is_valid()); | |
o1ka
2017/05/11 10:58:40
CHECK those two?
Max Morin
2017/05/11 15:31:18
There are no security implications of this code, s
| |
176 | |
177 base::PlatformFile socket_handle; | |
178 mojo::UnwrapPlatformFile(std::move(socket), &socket_handle); | |
o1ka
2017/05/11 10:58:41
CHECK the result?
Max Morin
2017/05/11 15:31:18
same
o1ka
2017/05/15 13:27:08
Why?
| |
179 | |
180 base::SharedMemoryHandle memory_handle; | |
181 bool read_only = false; | |
182 size_t memory_length = 0; | |
183 auto result = mojo::UnwrapSharedMemoryHandle( | |
184 std::move(shared_memory), &memory_handle, &memory_length, &read_only); | |
185 DCHECK_EQ(result, MOJO_RESULT_OK); | |
o1ka
2017/05/11 10:58:41
CHECK?
Is it really safe to continue?
Max Morin
2017/05/11 15:31:18
same
o1ka
2017/05/15 13:27:08
Why? How about out of memory scenario?
| |
186 DCHECK(!read_only); | |
o1ka
2017/05/11 10:58:41
CHECK as well?
Max Morin
2017/05/11 15:31:18
same
| |
187 | |
188 delegate->OnStreamCreated(memory_handle, socket_handle, memory_length); | |
o1ka
2017/05/11 10:58:40
CHECK(memory_length) as well?
(make sure to have
Max Morin
2017/05/11 15:31:18
same
| |
189 } | |
190 | |
191 void MojoAudioOutputIPC::PlayStream() { | |
192 DCHECK(thread_checker_.CalledOnValidThread()); | |
193 if (stream_.is_bound()) | |
194 stream_->Play(); | |
195 } | |
196 | |
197 void MojoAudioOutputIPC::PauseStream() { | |
198 DCHECK(thread_checker_.CalledOnValidThread()); | |
199 if (stream_.is_bound()) | |
200 stream_->Pause(); | |
201 } | |
202 | |
203 void MojoAudioOutputIPC::CloseStream() { | |
204 DCHECK(thread_checker_.CalledOnValidThread()); | |
205 stream_provider_.reset(); | |
206 stream_.reset(); | |
207 | |
208 // Make sure we don't send an authorization callback for this stream to the | |
209 // delegate. | |
210 weak_factory_.InvalidateWeakPtrs(); | |
211 } | |
212 | |
213 void MojoAudioOutputIPC::SetVolume(double volume) { | |
214 DCHECK(thread_checker_.CalledOnValidThread()); | |
215 if (stream_.is_bound()) | |
216 stream_->SetVolume(volume); | |
217 } | |
218 | |
219 bool MojoAudioOutputIPC::AuthorizationRequested() { | |
220 return stream_provider_.is_bound(); | |
221 } | |
222 | |
223 bool MojoAudioOutputIPC::StreamCreationRequested() { | |
224 return stream_.is_bound(); | |
225 } | |
226 | |
227 } // namespace content | |
OLD | NEW |