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

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

Issue 2821203005: Add a mojo implementation of AudioOutputIPC. (Closed)
Patch Set: rebase 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& operator=(
43 AuthorizationCallbackWrapper&& other) = delete;
o1ka 2017/05/18 09:16:50 It has a destructor, so move constructor is not ge
Max Morin 2017/05/18 12:30:18 Right, I figured it might generate one since I def
44
45 ~AuthorizationCallbackWrapper() {
46 if (callback_) {
47 std::move(callback_).Run(
48 media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL,
49 media::AudioParameters::UnavailableDeviceParams(), std::string());
50 }
51 }
52
53 private:
54 explicit AuthorizationCallbackWrapper(AuthorizationCallback callback)
55 : callback_(std::move(callback)) {}
56
57 static void Call(AuthorizationCallbackWrapper callback_wrapper,
58 media::OutputDeviceStatus status,
59 const media::AudioParameters& params,
60 const std::string& device_id) {
61 if (callback_wrapper.callback_) {
62 std::move(callback_wrapper.callback_).Run(status, params, device_id);
63 // Make sure we don't call again in destructor:
64 callback_wrapper.callback_ = AuthorizationCallback();
65 }
66 }
67
68 AuthorizationCallback callback_;
69
70 DISALLOW_COPY_AND_ASSIGN(AuthorizationCallbackWrapper);
71 };
72
73 } // namespace
74
75 MojoAudioOutputIPC::MojoAudioOutputIPC(FactoryAccessor factory_accessor)
76 : factory_accessor_(std::move(factory_accessor)), weak_factory_(this) {
77 DETACH_FROM_THREAD(thread_checker_);
78 }
79
80 MojoAudioOutputIPC::~MojoAudioOutputIPC() {
81 DCHECK(!AuthorizationRequested());
82 DCHECK(!StreamCreationRequested());
83 // No thread check.
84 // Destructing |weak_factory_| on any thread is safe since it's not used after
85 // the final call to CloseStream, where its pointers are invalidated.
86 }
87
88 void MojoAudioOutputIPC::RequestDeviceAuthorization(
89 media::AudioOutputIPCDelegate* delegate,
90 int session_id,
91 const std::string& device_id,
92 const url::Origin& security_origin) {
93 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
94 DCHECK(delegate);
95 DCHECK(!delegate_);
96 DCHECK(!AuthorizationRequested());
97 DCHECK(!StreamCreationRequested());
98 delegate_ = delegate;
99
100 // We wrap the callback here so that we are sure to always get the
101 // authorization reply, even if the connection is closed.
102 DoRequestDeviceAuthorization(
103 session_id, device_id,
104 AuthorizationCallbackWrapper::Wrap(
105 base::BindOnce(&MojoAudioOutputIPC::RecievedDeviceAuthorization,
106 weak_factory_.GetWeakPtr())));
107 }
108
109 void MojoAudioOutputIPC::CreateStream(media::AudioOutputIPCDelegate* delegate,
110 const media::AudioParameters& params) {
111 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
112 DCHECK(delegate);
113 DCHECK(!StreamCreationRequested());
114 if (!AuthorizationRequested()) {
115 DCHECK(!delegate_);
116 delegate_ = delegate;
117 // No authorization requested yet. Request one for the default device.
118 // Since the delegate didn't explicitly request authorization, we shouldn't
119 // send a callback to it.
120 if (!DoRequestDeviceAuthorization(
121 0, media::AudioDeviceDescription::kDefaultDeviceId,
122 base::Bind(&TrivialAuthorizedCallback))) {
123 return;
124 }
125 }
126
127 DCHECK(delegate_ == delegate);
128 // Since the creation callback won't fire if the provider binding is gone
129 // and |this| owns |stream_provider_|, unretained is safe.
130 stream_provider_->Acquire(
131 mojo::MakeRequest(&stream_), params,
132 base::Bind(&MojoAudioOutputIPC::StreamCreated, base::Unretained(this)));
133
134 // Unretained is safe because |delegate_| must remain valid until
135 // CloseStream is called, and |stream_provider_| is reset in CloseStream.
136 stream_.set_connection_error_handler(base::Bind(
137 &media::AudioOutputIPCDelegate::OnError, base::Unretained(delegate_)));
138 }
139
140 void MojoAudioOutputIPC::PlayStream() {
141 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
142 if (stream_.is_bound())
143 stream_->Play();
144 }
145
146 void MojoAudioOutputIPC::PauseStream() {
147 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
148 if (stream_.is_bound())
149 stream_->Pause();
150 }
151
152 void MojoAudioOutputIPC::CloseStream() {
153 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
154 stream_provider_.reset();
155 stream_.reset();
156 delegate_ = nullptr;
157
158 // Cancel any pending callbacks for this stream.
159 weak_factory_.InvalidateWeakPtrs();
160 }
161
162 void MojoAudioOutputIPC::SetVolume(double volume) {
163 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
164 if (stream_.is_bound())
165 stream_->SetVolume(volume);
166 }
167
168 bool MojoAudioOutputIPC::AuthorizationRequested() {
169 return stream_provider_.is_bound();
170 }
171
172 bool MojoAudioOutputIPC::StreamCreationRequested() {
173 return stream_.is_bound();
174 }
175
176 media::mojom::AudioOutputStreamProviderRequest
177 MojoAudioOutputIPC::MakeProviderRequest() {
178 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
179 DCHECK(!AuthorizationRequested());
180 media::mojom::AudioOutputStreamProviderRequest request =
181 mojo::MakeRequest(&stream_provider_);
182
183 // Unretained is safe because |delegate_| must remain valid until
184 // CloseStream is called, and |stream_provider_| is reset in CloseStream.
185 stream_provider_.set_connection_error_handler(base::Bind(
186 &media::AudioOutputIPCDelegate::OnError, base::Unretained(delegate_)));
187 return request;
188 }
189
190 bool MojoAudioOutputIPC::DoRequestDeviceAuthorization(
191 int session_id,
192 const std::string& device_id,
193 AuthorizationCallback callback) {
194 auto* factory = factory_accessor_.Run();
o1ka 2017/05/18 09:16:50 DCHECK_CALLED_ON_VALID_THREAD? (just for easy read
Max Morin 2017/05/18 12:30:18 Done.
195 if (!factory) {
196 LOG(ERROR) << "MojoAudioOutputIPC failed to acquire factory";
197
198 media::AudioOutputIPCDelegate* delegate = delegate_;
199 CloseStream();
200 delegate->OnIPCClosed(); // deletes |this|.
201 return false;
202 }
203
204 // We wrap the callback here so that we are sure to always get the
205 // authorization reply, even if the connection is closed.
206 factory->RequestDeviceAuthorization(MakeProviderRequest(), session_id,
207 device_id, std::move(callback));
208 return true;
209 }
210
211 void MojoAudioOutputIPC::RecievedDeviceAuthorization(
212 media::OutputDeviceStatus status,
213 const media::AudioParameters& params,
214 const std::string& device_id) const {
215 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
216 DCHECK(delegate_);
217 delegate_->OnDeviceAuthorized(status, params, device_id);
218 }
219
220 void MojoAudioOutputIPC::StreamCreated(
221 mojo::ScopedSharedBufferHandle shared_memory,
222 mojo::ScopedHandle socket) {
223 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
224 DCHECK(delegate_);
225 DCHECK(socket.is_valid());
226 DCHECK(shared_memory.is_valid());
227
228 base::PlatformFile socket_handle;
229 mojo::UnwrapPlatformFile(std::move(socket), &socket_handle);
o1ka 2017/05/18 09:16:50 Please DCHECK :)
Max Morin 2017/05/18 12:30:18 Done.
230
231 base::SharedMemoryHandle memory_handle;
232 bool read_only = false;
233 size_t memory_length = 0;
234 auto result = mojo::UnwrapSharedMemoryHandle(
235 std::move(shared_memory), &memory_handle, &memory_length, &read_only);
236 DCHECK_EQ(result, MOJO_RESULT_OK);
237 DCHECK(!read_only);
238
239 delegate_->OnStreamCreated(memory_handle, socket_handle, memory_length);
240 }
241
242 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698