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

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

Issue 2821203005: Add a mojo implementation of AudioOutputIPC. (Closed)
Patch Set: Death tests, Olga's comments 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 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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698