Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1541)

Side by Side Diff: content/renderer/media/mojo_audio_output_ipc.cc

Issue 2821203005: Add a mojo implementation of AudioOutputIPC. (Closed)
Patch Set: Fix test issues Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698