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

Side by Side Diff: media/audio/audio_output_device.cc

Issue 1703473002: Make AudioOutputDevice restartable and reinitializable (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@new_mixing
Patch Set: git cl format Created 4 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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/audio/audio_output_device.h" 5 #include "media/audio/audio_output_device.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 #include <stdint.h> 8 #include <stdint.h>
9 9
10 #include <cmath> 10 #include <cmath>
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
47 DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback); 47 DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback);
48 }; 48 };
49 49
50 AudioOutputDevice::AudioOutputDevice( 50 AudioOutputDevice::AudioOutputDevice(
51 std::unique_ptr<AudioOutputIPC> ipc, 51 std::unique_ptr<AudioOutputIPC> ipc,
52 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner, 52 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
53 int session_id, 53 int session_id,
54 const std::string& device_id, 54 const std::string& device_id,
55 const url::Origin& security_origin) 55 const url::Origin& security_origin)
56 : ScopedTaskRunnerObserver(io_task_runner), 56 : ScopedTaskRunnerObserver(io_task_runner),
57 callback_(NULL), 57 callback_(nullptr),
58 ipc_(std::move(ipc)), 58 ipc_(std::move(ipc)),
59 state_(IDLE), 59 state_(IDLE),
60 start_on_authorized_(false), 60 start_on_authorized_(false),
61 play_on_start_(true), 61 play_on_start_(true),
62 session_id_(session_id), 62 session_id_(session_id),
63 device_id_(device_id), 63 device_id_(device_id),
64 security_origin_(security_origin), 64 security_origin_(security_origin),
65 stopping_hack_(false),
66 did_receive_auth_(true, false), 65 did_receive_auth_(true, false),
67 device_status_(OUTPUT_DEVICE_STATUS_ERROR_INTERNAL) { 66 device_status_(OUTPUT_DEVICE_STATUS_ERROR_INTERNAL) {
68 CHECK(ipc_); 67 CHECK(ipc_);
69 68
70 // The correctness of the code depends on the relative values assigned in the 69 // The correctness of the code depends on the relative values assigned in the
71 // State enum. 70 // State enum.
72 static_assert(IPC_CLOSED < IDLE, "invalid enum value assignment 0"); 71 static_assert(IPC_CLOSED < IDLE, "invalid enum value assignment 0");
73 static_assert(IDLE < AUTHORIZING, "invalid enum value assignment 1"); 72 static_assert(IDLE < AUTHORIZING, "invalid enum value assignment 1");
74 static_assert(AUTHORIZING < AUTHORIZED, "invalid enum value assignment 2"); 73 static_assert(AUTHORIZING < AUTHORIZED, "invalid enum value assignment 2");
75 static_assert(AUTHORIZED < CREATING_STREAM, 74 static_assert(AUTHORIZED < CREATING_STREAM,
76 "invalid enum value assignment 3"); 75 "invalid enum value assignment 3");
77 static_assert(CREATING_STREAM < PAUSED, "invalid enum value assignment 4"); 76 static_assert(CREATING_STREAM < PAUSED, "invalid enum value assignment 4");
78 static_assert(PAUSED < PLAYING, "invalid enum value assignment 5"); 77 static_assert(PAUSED < PLAYING, "invalid enum value assignment 5");
79 } 78 }
80 79
81 void AudioOutputDevice::Initialize(const AudioParameters& params, 80 void AudioOutputDevice::Initialize(
82 RenderCallback* callback) { 81 const AudioParameters& params,
83 DCHECK(!callback_) << "Calling Initialize() twice?"; 82 AudioRendererSink::RenderCallback* callback) {
84 DCHECK(params.IsValid()); 83 DCHECK(params.IsValid());
85 audio_parameters_ = params; 84
86 callback_ = callback; 85 // We set the callback synchronously here, so that it's serialized with
86 // clearing it in Stop(). Note that after this it is possible to get stale
o1ka 2016/05/03 15:47:32 Could you explain here how exactly it can happen?
Henrik Grunell 2016/05/04 09:05:11 Done.
87 // callbacks from previous runs (i.e. from before Stop() was called earlier).
o1ka 2016/05/03 15:47:32 Taking into account current usage model, we are no
Henrik Grunell 2016/05/04 09:05:11 Yes, that's right, but we allow it. I suspect you
88 //
89 // Using the IO thread at all in this class will go away soon when we move IPC
90 // to use Mojo, and we'll then get rid of the thread hopping and locks. See
91 // http://crbug.com/606707.
92 {
93 base::AutoLock auto_lock(callback_lock_);
94 DCHECK(!callback_) << "Calling Initialize() twice?";
95 callback_ = callback;
96 }
97
98 task_runner()->PostTask(
99 FROM_HERE,
100 base::Bind(&AudioOutputDevice::InitializeOnIOThread, this, params));
87 } 101 }
88 102
89 AudioOutputDevice::~AudioOutputDevice() { 103 AudioOutputDevice::~AudioOutputDevice() {
90 // The current design requires that the user calls Stop() before deleting 104 // The current design requires that the user calls Stop() before deleting
91 // this class. 105 // this class to ensure that stream and authorization data is cleaned up on
92 DCHECK(audio_thread_.IsStopped()); 106 // the browser side, and that we don't get more IPC calls from AudioOutputIPC.
107 // With all posted tasks on IO thread done and no more IPC calls, it's safe to
108 // access |audio_thread_| here on any thread.
109 DCHECK(!callback_);
110
111 audio_thread_.Stop(base::MessageLoop::current());
93 } 112 }
94 113
95 void AudioOutputDevice::RequestDeviceAuthorization() { 114 void AudioOutputDevice::RequestDeviceAuthorization() {
96 task_runner()->PostTask( 115 task_runner()->PostTask(
97 FROM_HERE, 116 FROM_HERE,
98 base::Bind(&AudioOutputDevice::RequestDeviceAuthorizationOnIOThread, 117 base::Bind(&AudioOutputDevice::RequestDeviceAuthorizationOnIOThread,
99 this)); 118 this));
100 } 119 }
101 120
102 void AudioOutputDevice::Start() { 121 void AudioOutputDevice::Start() {
103 DCHECK(callback_) << "Initialize hasn't been called"; 122 #if !defined(NDEBUG)
104 task_runner()->PostTask(FROM_HERE, 123 {
105 base::Bind(&AudioOutputDevice::CreateStreamOnIOThread, this, 124 base::AutoLock auto_lock(callback_lock_);
106 audio_parameters_)); 125 DCHECK(callback_) << "Initialize hasn't been called";
126 }
127 #endif
128
129 task_runner()->PostTask(
130 FROM_HERE, base::Bind(&AudioOutputDevice::CreateStreamOnIOThread, this));
107 } 131 }
108 132
109 void AudioOutputDevice::Stop() { 133 void AudioOutputDevice::Stop() {
134 // Clear the callback synchronously to ensure no callbacks after returning.
o1ka 2016/05/03 15:47:32 This is nice.
Henrik Grunell 2016/05/04 09:05:12 Thanks.
110 { 135 {
111 base::AutoLock auto_lock(audio_thread_lock_); 136 base::AutoLock auto_lock(callback_lock_);
112 audio_thread_.Stop(base::MessageLoop::current()); 137 DCHECK(callback_) << "Initialize hasn't been called";
113 stopping_hack_ = true; 138 callback_ = nullptr;
114 } 139 }
115 140
116 task_runner()->PostTask(FROM_HERE, 141 task_runner()->PostTask(FROM_HERE,
117 base::Bind(&AudioOutputDevice::ShutDownOnIOThread, this)); 142 base::Bind(&AudioOutputDevice::StopOnIOThread, this));
118 } 143 }
119 144
120 void AudioOutputDevice::Play() { 145 void AudioOutputDevice::Play() {
121 task_runner()->PostTask(FROM_HERE, 146 task_runner()->PostTask(FROM_HERE,
122 base::Bind(&AudioOutputDevice::PlayOnIOThread, this)); 147 base::Bind(&AudioOutputDevice::PlayOnIOThread, this));
123 } 148 }
124 149
125 void AudioOutputDevice::Pause() { 150 void AudioOutputDevice::Pause() {
126 task_runner()->PostTask(FROM_HERE, 151 task_runner()->PostTask(FROM_HERE,
127 base::Bind(&AudioOutputDevice::PauseOnIOThread, this)); 152 base::Bind(&AudioOutputDevice::PauseOnIOThread, this));
(...skipping 14 matching lines...) Expand all
142 OutputDeviceInfo AudioOutputDevice::GetOutputDeviceInfo() { 167 OutputDeviceInfo AudioOutputDevice::GetOutputDeviceInfo() {
143 CHECK(!task_runner()->BelongsToCurrentThread()); 168 CHECK(!task_runner()->BelongsToCurrentThread());
144 did_receive_auth_.Wait(); 169 did_receive_auth_.Wait();
145 return OutputDeviceInfo(AudioDeviceDescription::UseSessionIdToSelectDevice( 170 return OutputDeviceInfo(AudioDeviceDescription::UseSessionIdToSelectDevice(
146 session_id_, device_id_) 171 session_id_, device_id_)
147 ? matched_device_id_ 172 ? matched_device_id_
148 : device_id_, 173 : device_id_,
149 device_status_, output_params_); 174 device_status_, output_params_);
150 } 175 }
151 176
177 int AudioOutputDevice::Render(media::AudioBus* audio_bus,
178 uint32_t frames_delayed,
179 uint32_t frames_skipped) {
180 base::AutoLock auto_lock(callback_lock_);
181 return callback_
182 ? callback_->Render(audio_bus, frames_delayed, frames_skipped)
183 : 0;
o1ka 2016/05/03 15:47:32 Shouldn't we zero-out the bus?
Henrik Grunell 2016/05/04 09:05:12 That would not be correct, we should return the nu
184 }
185
186 void AudioOutputDevice::OnRenderError() {
187 base::AutoLock auto_lock(callback_lock_);
188 if (callback_)
189 callback_->OnRenderError();
190 }
191
152 void AudioOutputDevice::RequestDeviceAuthorizationOnIOThread() { 192 void AudioOutputDevice::RequestDeviceAuthorizationOnIOThread() {
153 DCHECK(task_runner()->BelongsToCurrentThread()); 193 DCHECK(task_runner()->BelongsToCurrentThread());
154 DCHECK_EQ(state_, IDLE); 194 DCHECK_EQ(state_, IDLE);
155 state_ = AUTHORIZING; 195 state_ = AUTHORIZING;
196
197 // TODO(grunell): Store authorization so that we don't have to authorize
198 // again when restarting. http://crbug.com/608619.
156 ipc_->RequestDeviceAuthorization(this, session_id_, device_id_, 199 ipc_->RequestDeviceAuthorization(this, session_id_, device_id_,
157 security_origin_); 200 security_origin_);
158 } 201 }
159 202
160 void AudioOutputDevice::CreateStreamOnIOThread(const AudioParameters& params) { 203 void AudioOutputDevice::InitializeOnIOThread(const AudioParameters& params) {
161 DCHECK(task_runner()->BelongsToCurrentThread()); 204 DCHECK(task_runner()->BelongsToCurrentThread());
205 audio_parameters_ = params;
206 }
207
208 void AudioOutputDevice::CreateStreamOnIOThread() {
209 DCHECK(task_runner()->BelongsToCurrentThread());
210
162 switch (state_) { 211 switch (state_) {
163 case IPC_CLOSED: 212 case IPC_CLOSED: {
213 base::AutoLock auto_lock(callback_lock_);
o1ka 2016/05/03 15:47:32 It's just AOD::OnRenderError()
Henrik Grunell 2016/05/04 09:05:12 Of course, thanks.
164 if (callback_) 214 if (callback_)
165 callback_->OnRenderError(); 215 callback_->OnRenderError();
166 break; 216 } break;
167 217
168 case IDLE: 218 case IDLE:
169 if (did_receive_auth_.IsSignaled() && device_id_.empty() && 219 if (did_receive_auth_.IsSignaled() && device_id_.empty() &&
170 security_origin_.unique()) { 220 security_origin_.unique()) {
171 state_ = CREATING_STREAM; 221 state_ = CREATING_STREAM;
172 ipc_->CreateStream(this, params); 222 ipc_->CreateStream(this, audio_parameters_);
173 } else { 223 } else {
174 RequestDeviceAuthorizationOnIOThread(); 224 RequestDeviceAuthorizationOnIOThread();
175 start_on_authorized_ = true; 225 start_on_authorized_ = true;
176 } 226 }
177 break; 227 break;
178 228
179 case AUTHORIZING: 229 case AUTHORIZING:
180 start_on_authorized_ = true; 230 start_on_authorized_ = true;
181 break; 231 break;
182 232
183 case AUTHORIZED: 233 case AUTHORIZED:
184 state_ = CREATING_STREAM; 234 state_ = CREATING_STREAM;
185 ipc_->CreateStream(this, params); 235 ipc_->CreateStream(this, audio_parameters_);
186 start_on_authorized_ = false; 236 start_on_authorized_ = false;
187 break; 237 break;
188 238
189 case CREATING_STREAM: 239 case CREATING_STREAM:
190 case PAUSED: 240 case PAUSED:
191 case PLAYING: 241 case PLAYING:
192 NOTREACHED(); 242 NOTREACHED();
193 break; 243 break;
194 } 244 }
195 } 245 }
(...skipping 15 matching lines...) Expand all
211 DCHECK(task_runner()->BelongsToCurrentThread()); 261 DCHECK(task_runner()->BelongsToCurrentThread());
212 if (state_ == PLAYING) { 262 if (state_ == PLAYING) {
213 TRACE_EVENT_ASYNC_END0( 263 TRACE_EVENT_ASYNC_END0(
214 "audio", "StartingPlayback", audio_callback_.get()); 264 "audio", "StartingPlayback", audio_callback_.get());
215 ipc_->PauseStream(); 265 ipc_->PauseStream();
216 state_ = PAUSED; 266 state_ = PAUSED;
217 } 267 }
218 play_on_start_ = false; 268 play_on_start_ = false;
219 } 269 }
220 270
221 void AudioOutputDevice::ShutDownOnIOThread() { 271 void AudioOutputDevice::StopOnIOThread() {
222 DCHECK(task_runner()->BelongsToCurrentThread()); 272 DCHECK(task_runner()->BelongsToCurrentThread());
223 273
224 // Close the stream, if we haven't already. 274 // Close the stream, if we haven't already. After CloseStream(), we are
275 // guaranteed to not get IPC calls for any outstanding operations. This holds
276 // also if we restart, i.e. request authorization and create a new stream,
277 // before the previous request and/or create stream finished.
225 if (state_ >= AUTHORIZING) { 278 if (state_ >= AUTHORIZING) {
226 ipc_->CloseStream(); 279 ipc_->CloseStream();
227 state_ = IDLE; 280 state_ = IDLE;
228 } 281 }
229 start_on_authorized_ = false; 282 start_on_authorized_ = false;
230 283 audio_thread_.Pause();
231 // We can run into an issue where ShutDownOnIOThread is called right after
232 // OnStreamCreated is called in cases where Start/Stop are called before we
233 // get the OnStreamCreated callback. To handle that corner case, we call
234 // Stop(). In most cases, the thread will already be stopped.
235 //
236 // Another situation is when the IO thread goes away before Stop() is called
237 // in which case, we cannot use the message loop to close the thread handle
238 // and can't rely on the main thread existing either.
239 base::AutoLock auto_lock_(audio_thread_lock_);
240 base::ThreadRestrictions::ScopedAllowIO allow_io;
241 audio_thread_.Stop(NULL);
242 audio_callback_.reset(); 284 audio_callback_.reset();
243 stopping_hack_ = false; 285 did_receive_auth_.Reset();
o1ka 2016/05/03 15:47:32 Add a comment why it's needed?
Henrik Grunell 2016/05/04 09:05:11 Done.
244 } 286 }
245 287
246 void AudioOutputDevice::SetVolumeOnIOThread(double volume) { 288 void AudioOutputDevice::SetVolumeOnIOThread(double volume) {
247 DCHECK(task_runner()->BelongsToCurrentThread()); 289 DCHECK(task_runner()->BelongsToCurrentThread());
248 if (state_ >= CREATING_STREAM) 290 if (state_ >= CREATING_STREAM)
249 ipc_->SetVolume(volume); 291 ipc_->SetVolume(volume);
250 } 292 }
251 293
252 void AudioOutputDevice::OnStateChanged(AudioOutputIPCDelegateState state) { 294 void AudioOutputDevice::OnStateChanged(AudioOutputIPCDelegateState state) {
253 DCHECK(task_runner()->BelongsToCurrentThread()); 295 DCHECK(task_runner()->BelongsToCurrentThread());
254 296
255 // Do nothing if the stream has been closed. 297 // Do nothing if the stream has been closed.
256 if (state_ < CREATING_STREAM) 298 if (state_ < CREATING_STREAM)
257 return; 299 return;
258 300
259 // TODO(miu): Clean-up inconsistent and incomplete handling here. 301 // TODO(miu): Clean-up inconsistent and incomplete handling here.
260 // http://crbug.com/180640 302 // http://crbug.com/180640
261 switch (state) { 303 switch (state) {
262 case AUDIO_OUTPUT_IPC_DELEGATE_STATE_PLAYING: 304 case AUDIO_OUTPUT_IPC_DELEGATE_STATE_PLAYING:
263 case AUDIO_OUTPUT_IPC_DELEGATE_STATE_PAUSED: 305 case AUDIO_OUTPUT_IPC_DELEGATE_STATE_PAUSED:
264 break; 306 break;
265 case AUDIO_OUTPUT_IPC_DELEGATE_STATE_ERROR: 307 case AUDIO_OUTPUT_IPC_DELEGATE_STATE_ERROR:
266 DLOG(WARNING) << "AudioOutputDevice::OnStateChanged(ERROR)"; 308 DLOG(WARNING) << "AudioOutputDevice::OnStateChanged(ERROR)";
267 // Don't dereference the callback object if the audio thread 309 {
268 // is stopped or stopping. That could mean that the callback 310 base::AutoLock auto_lock(callback_lock_);
o1ka 2016/05/03 15:47:32 AOD::OnRenderError()
Henrik Grunell 2016/05/04 09:05:12 Done.
269 // object has been deleted. 311 if (callback_)
270 // TODO(tommi): Add an explicit contract for clearing the callback 312 callback_->OnRenderError();
271 // object. Possibly require calling Initialize again or provide 313 }
272 // a callback object via Start() and clear it in Stop().
273 if (!audio_thread_.IsStopped())
274 callback_->OnRenderError();
275 break; 314 break;
276 default: 315 default:
277 NOTREACHED(); 316 NOTREACHED();
278 break; 317 break;
279 } 318 }
280 } 319 }
281 320
282 void AudioOutputDevice::OnDeviceAuthorized( 321 void AudioOutputDevice::OnDeviceAuthorized(
283 OutputDeviceStatus device_status, 322 OutputDeviceStatus device_status,
284 const media::AudioParameters& output_params, 323 const media::AudioParameters& output_params,
(...skipping 25 matching lines...) Expand all
310 matched_device_id_.empty()); 349 matched_device_id_.empty());
311 matched_device_id_ = matched_device_id; 350 matched_device_id_ = matched_device_id;
312 351
313 DVLOG(1) << "AudioOutputDevice authorized, session_id: " << session_id_ 352 DVLOG(1) << "AudioOutputDevice authorized, session_id: " << session_id_
314 << ", device_id: " << device_id_ 353 << ", device_id: " << device_id_
315 << ", matched_device_id: " << matched_device_id_; 354 << ", matched_device_id: " << matched_device_id_;
316 355
317 did_receive_auth_.Signal(); 356 did_receive_auth_.Signal();
318 } 357 }
319 if (start_on_authorized_) 358 if (start_on_authorized_)
320 CreateStreamOnIOThread(audio_parameters_); 359 CreateStreamOnIOThread();
321 } else { 360 } else {
322 // Closing IPC forces a Signal(), so no clients are locked waiting 361 // Closing IPC forces a Signal(), so no clients are locked waiting
323 // indefinitely after this method returns. 362 // indefinitely after this method returns.
324 ipc_->CloseStream(); 363 ipc_->CloseStream();
325 OnIPCClosed(); 364 OnIPCClosed();
365 base::AutoLock auto_lock(callback_lock_);
o1ka 2016/05/03 15:47:32 ditto
Henrik Grunell 2016/05/04 09:05:11 Done.
326 if (callback_) 366 if (callback_)
327 callback_->OnRenderError(); 367 callback_->OnRenderError();
328 } 368 }
329 } 369 }
330 370
331 void AudioOutputDevice::OnStreamCreated( 371 void AudioOutputDevice::OnStreamCreated(
332 base::SharedMemoryHandle handle, 372 base::SharedMemoryHandle handle,
333 base::SyncSocket::Handle socket_handle, 373 base::SyncSocket::Handle socket_handle,
334 int length) { 374 int length) {
335 DCHECK(task_runner()->BelongsToCurrentThread()); 375 DCHECK(task_runner()->BelongsToCurrentThread());
336 DCHECK(base::SharedMemory::IsHandleValid(handle)); 376 DCHECK(base::SharedMemory::IsHandleValid(handle));
337 #if defined(OS_WIN) 377 #if defined(OS_WIN)
338 DCHECK(socket_handle); 378 DCHECK(socket_handle);
339 #else 379 #else
340 DCHECK_GE(socket_handle, 0); 380 DCHECK_GE(socket_handle, 0);
341 #endif 381 #endif
342 DCHECK_GT(length, 0); 382 DCHECK_GT(length, 0);
343 383
344 if (state_ != CREATING_STREAM) 384 if (state_ != CREATING_STREAM)
345 return; 385 return;
346 386
347 // We can receive OnStreamCreated() on the IO thread after the client has 387 // We can receive OnStreamCreated() on the IO thread after the client has
348 // called Stop() but before ShutDownOnIOThread() is processed. In such a 388 // called Stop() but before StopOnIOThread() is processed. This is OK, since
349 // situation |callback_| might point to freed memory. Instead of starting 389 // we pass callbacks though |this| and clear |callback_| in Stop().
350 // |audio_thread_| do nothing and wait for ShutDownOnIOThread() to get called.
351 // 390 //
352 // TODO(scherkus): The real fix is to have sane ownership semantics. The fact 391 // TODO(grunell): AudioRendererSink should be non-refcounted and
353 // that |callback_| (which should own and outlive this object!) can point to 392 // AudioOutputDevice should internally use WeakPtr to handle teardown and
354 // freed memory is a mess. AudioRendererSink should be non-refcounted so that 393 // thread hopping. See http://crbug.com/151051 for details.
355 // owners (WebRtcAudioDeviceImpl, AudioRendererImpl, etc...) can Stop() and 394 // Note however that switching the IPC to Mojo will remove usage of the IO
356 // delete as they see fit. AudioOutputDevice should internally use WeakPtr 395 // thread, so we'll eliminate thread concurrency issues here then. See
357 // to handle teardown and thread hopping. See http://crbug.com/151051 for 396 // http://crbug.com/606707.
358 // details.
359 {
360 base::AutoLock auto_lock(audio_thread_lock_);
361 if (stopping_hack_)
362 return;
363 397
364 DCHECK(audio_thread_.IsStopped()); 398 DCHECK(audio_thread_.IsStopped());
365 audio_callback_.reset(new AudioOutputDevice::AudioThreadCallback( 399 audio_callback_.reset(new AudioOutputDevice::AudioThreadCallback(
366 audio_parameters_, handle, length, callback_)); 400 audio_parameters_, handle, length, this));
367 audio_thread_.Start(audio_callback_.get(), socket_handle,
368 "AudioOutputDevice", true);
369 state_ = PAUSED;
370 401
371 // We handle the case where Play() and/or Pause() may have been called 402 // If the audio thread hasn't been started before, start it.
372 // multiple times before OnStreamCreated() gets called. 403 if (audio_thread_.IsStopped()) {
373 if (play_on_start_) 404 audio_thread_.Start("AudioOutputDevice", true);
374 PlayOnIOThread();
375 } 405 }
406 audio_thread_.Resume(audio_callback_.get(), socket_handle);
407
408 state_ = PAUSED;
409
410 // We handle the case where Play() and/or Pause() may have been called
411 // multiple times before OnStreamCreated() gets called.
412 if (play_on_start_)
413 PlayOnIOThread();
376 } 414 }
377 415
378 void AudioOutputDevice::OnIPCClosed() { 416 void AudioOutputDevice::OnIPCClosed() {
379 DCHECK(task_runner()->BelongsToCurrentThread()); 417 DCHECK(task_runner()->BelongsToCurrentThread());
380 state_ = IPC_CLOSED; 418 state_ = IPC_CLOSED;
381 ipc_.reset(); 419 ipc_.reset();
382 420
383 // Signal to unblock any blocked threads waiting for parameters 421 // Signal to unblock any blocked threads waiting for parameters
384 did_receive_auth_.Signal(); 422 did_receive_auth_.Signal();
385 } 423 }
386 424
387 void AudioOutputDevice::WillDestroyCurrentMessageLoop() { 425 void AudioOutputDevice::WillDestroyCurrentMessageLoop() {
388 LOG(ERROR) << "IO loop going away before the audio device has been stopped"; 426 LOG(ERROR) << "IO loop going away before the audio device has been stopped";
389 ShutDownOnIOThread(); 427 StopOnIOThread();
390 } 428 }
391 429
392 // AudioOutputDevice::AudioThreadCallback 430 // AudioOutputDevice::AudioThreadCallback
393 431
394 AudioOutputDevice::AudioThreadCallback::AudioThreadCallback( 432 AudioOutputDevice::AudioThreadCallback::AudioThreadCallback(
395 const AudioParameters& audio_parameters, 433 const AudioParameters& audio_parameters,
396 base::SharedMemoryHandle memory, 434 base::SharedMemoryHandle memory,
397 int memory_length, 435 int memory_length,
398 AudioRendererSink::RenderCallback* render_callback) 436 AudioRendererSink::RenderCallback* render_callback)
399 : AudioDeviceThread::Callback(audio_parameters, memory, memory_length, 1), 437 : AudioDeviceThread::Callback(audio_parameters, memory, memory_length, 1),
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
443 481
444 // Update the audio-delay measurement, inform about the number of skipped 482 // Update the audio-delay measurement, inform about the number of skipped
445 // frames, and ask client to render audio. Since |output_bus_| is wrapping 483 // frames, and ask client to render audio. Since |output_bus_| is wrapping
446 // the shared memory the Render() call is writing directly into the shared 484 // the shared memory the Render() call is writing directly into the shared
447 // memory. 485 // memory.
448 render_callback_->Render(output_bus_.get(), std::round(frames_delayed), 486 render_callback_->Render(output_bus_.get(), std::round(frames_delayed),
449 frames_skipped); 487 frames_skipped);
450 } 488 }
451 489
452 } // namespace media 490 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698