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

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

Issue 1830933002: Making AudioThread::Thread restartable *** DRAFT *** (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 9 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 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
72 static_assert(IDLE < AUTHORIZING, "invalid enum value assignment 1"); 72 static_assert(IDLE < AUTHORIZING, "invalid enum value assignment 1");
73 static_assert(AUTHORIZING < AUTHORIZED, "invalid enum value assignment 2"); 73 static_assert(AUTHORIZING < AUTHORIZED, "invalid enum value assignment 2");
74 static_assert(AUTHORIZED < CREATING_STREAM, 74 static_assert(AUTHORIZED < CREATING_STREAM,
75 "invalid enum value assignment 3"); 75 "invalid enum value assignment 3");
76 static_assert(CREATING_STREAM < PAUSED, "invalid enum value assignment 4"); 76 static_assert(CREATING_STREAM < PAUSED, "invalid enum value assignment 4");
77 static_assert(PAUSED < PLAYING, "invalid enum value assignment 5"); 77 static_assert(PAUSED < PLAYING, "invalid enum value assignment 5");
78 } 78 }
79 79
80 void AudioOutputDevice::Initialize(const AudioParameters& params, 80 void AudioOutputDevice::Initialize(const AudioParameters& params,
81 RenderCallback* callback) { 81 RenderCallback* callback) {
82 DCHECK(!callback_) << "Calling Initialize() twice?"; 82 DCHECK(!callback_) << "Calling Initialize() twice?";
Guido Urdaneta 2016/04/01 10:17:02 Why should Initialize by async? A common idiom is
Henrik Grunell 2016/04/04 08:08:59 Initialize() and Start() are serialized, so we're
83 DCHECK(params.IsValid()); 83 DCHECK(params.IsValid());
84 audio_parameters_ = params; 84 task_runner()->PostTask(
85 callback_ = callback; 85 FROM_HERE, base::Bind(&AudioOutputDevice::InitializeOnIOThread, this,
86 params, callback));
86 } 87 }
87 88
88 AudioOutputDevice::~AudioOutputDevice() { 89 AudioOutputDevice::~AudioOutputDevice() {
90 // I'm not sure if Stop() should remain mandatory. The purpose of that was
91 // to fix object lifetime issues. Probably we can do it in a cleaner way.
Guido Urdaneta 2016/04/01 10:17:01 The purpose of mandatory Stop() is to make sure yo
Henrik Grunell 2016/04/04 08:08:59 I wonder if we can post a task on the IO thread in
89 // The current design requires that the user calls Stop() before deleting 92 // The current design requires that the user calls Stop() before deleting
90 // this class. 93 // this class. Since this object is reference counted, we're the only one
91 DCHECK(audio_thread_.IsStopped()); 94 // accessing data when we destruct.
95 DCHECK(state_ == IDLE || state_ == IPC_CLOSED);
96 // We need to ensure all the object lifetime stuff still works.
97 // Or even better, fix it to be more clear.
98 audio_thread_.Stop(base::MessageLoop::current());
92 } 99 }
93 100
94 void AudioOutputDevice::RequestDeviceAuthorization() { 101 void AudioOutputDevice::RequestDeviceAuthorization() {
95 task_runner()->PostTask( 102 task_runner()->PostTask(
96 FROM_HERE, 103 FROM_HERE,
97 base::Bind(&AudioOutputDevice::RequestDeviceAuthorizationOnIOThread, 104 base::Bind(&AudioOutputDevice::RequestDeviceAuthorizationOnIOThread,
98 this)); 105 this));
99 } 106 }
100 107
101 void AudioOutputDevice::Start() { 108 void AudioOutputDevice::Start() {
102 DCHECK(callback_) << "Initialize hasn't been called"; 109 DCHECK(callback_) << "Initialize hasn't been called";
103 task_runner()->PostTask(FROM_HERE, 110 task_runner()->PostTask(
104 base::Bind(&AudioOutputDevice::CreateStreamOnIOThread, this, 111 FROM_HERE, base::Bind(&AudioOutputDevice::CreateStreamOnIOThread, this));
105 audio_parameters_));
106 } 112 }
107 113
108 void AudioOutputDevice::Stop() { 114 void AudioOutputDevice::Stop() {
115 // TODO(olka): We need to clean this up; audio_thread_.Pause() must be called
116 // on IO thread only.
Henrik Grunell 2016/04/04 08:08:59 The problem here is that after Stop() returns, the
Henrik Grunell 2016/05/09 12:13:37 This has been addressed in the other CL.
109 { 117 {
110 base::AutoLock auto_lock(audio_thread_lock_); 118 base::AutoLock auto_lock(audio_thread_lock_);
111 audio_thread_.Stop(base::MessageLoop::current()); 119 // Play() is always called on IO thread. It means it can arrive after this
120 // Pause was executed, even if it was issued first. But we are safe, since
121 // Pause() will be called again in StopOnIOThread, so it's guaranteed to be
122 // called again after that sneaky Play(). What a hack.
123 // I'm not sure if we haven't broken this or about to break.
124 audio_thread_.Pause();
112 stopping_hack_ = true; 125 stopping_hack_ = true;
113 } 126 }
114 127
115 task_runner()->PostTask(FROM_HERE, 128 task_runner()->PostTask(FROM_HERE,
116 base::Bind(&AudioOutputDevice::ShutDownOnIOThread, this)); 129 base::Bind(&AudioOutputDevice::StopOnIOThread, this));
117 } 130 }
118 131
119 void AudioOutputDevice::Play() { 132 void AudioOutputDevice::Play() {
120 task_runner()->PostTask(FROM_HERE, 133 task_runner()->PostTask(FROM_HERE,
121 base::Bind(&AudioOutputDevice::PlayOnIOThread, this)); 134 base::Bind(&AudioOutputDevice::PlayOnIOThread, this));
122 } 135 }
123 136
124 void AudioOutputDevice::Pause() { 137 void AudioOutputDevice::Pause() {
125 task_runner()->PostTask(FROM_HERE, 138 task_runner()->PostTask(FROM_HERE,
126 base::Bind(&AudioOutputDevice::PauseOnIOThread, this)); 139 base::Bind(&AudioOutputDevice::PauseOnIOThread, this));
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
158 OutputDeviceStatus AudioOutputDevice::GetDeviceStatus() { 171 OutputDeviceStatus AudioOutputDevice::GetDeviceStatus() {
159 CHECK(!task_runner()->BelongsToCurrentThread()); 172 CHECK(!task_runner()->BelongsToCurrentThread());
160 did_receive_auth_.Wait(); 173 did_receive_auth_.Wait();
161 return device_status_; 174 return device_status_;
162 } 175 }
163 176
164 void AudioOutputDevice::RequestDeviceAuthorizationOnIOThread() { 177 void AudioOutputDevice::RequestDeviceAuthorizationOnIOThread() {
165 DCHECK(task_runner()->BelongsToCurrentThread()); 178 DCHECK(task_runner()->BelongsToCurrentThread());
166 DCHECK_EQ(state_, IDLE); 179 DCHECK_EQ(state_, IDLE);
167 state_ = AUTHORIZING; 180 state_ = AUTHORIZING;
181
182 // TODO(grunell): Store authorization so that we don't have to authorize
183 // again when re-starting. This involves mostly changes on the browser side:
184 // associate authorization by a new id (currently the stream id), don't forget
185 // authorization away after start or stop. Here we would have to explicitly
186 // make the browser side forget authorization in the dtor with a new
187 // AudioOutputIPC function.
168 ipc_->RequestDeviceAuthorization(this, session_id_, device_id_, 188 ipc_->RequestDeviceAuthorization(this, session_id_, device_id_,
169 security_origin_); 189 security_origin_);
170 } 190 }
171 191
172 void AudioOutputDevice::CreateStreamOnIOThread(const AudioParameters& params) { 192 void AudioOutputDevice::InitializeOnIOThread(const AudioParameters& params,
193 RenderCallback* callback) {
173 DCHECK(task_runner()->BelongsToCurrentThread()); 194 DCHECK(task_runner()->BelongsToCurrentThread());
195 DCHECK(!callback_);
196 audio_parameters_ = params;
197 callback_ = callback;
198 }
199
200 void AudioOutputDevice::CreateStreamOnIOThread() {
201 DCHECK(task_runner()->BelongsToCurrentThread());
202 DCHECK(callback_ || state_ == IPC_CLOSED);
203
174 switch (state_) { 204 switch (state_) {
175 case IPC_CLOSED: 205 case IPC_CLOSED:
176 if (callback_) 206 if (callback_)
177 callback_->OnRenderError(); 207 callback_->OnRenderError();
178 break; 208 break;
179 209
180 case IDLE: 210 case IDLE:
181 if (did_receive_auth_.IsSignaled() && device_id_.empty() && 211 if (did_receive_auth_.IsSignaled() && device_id_.empty() &&
182 security_origin_.unique()) { 212 security_origin_.unique()) {
183 state_ = CREATING_STREAM; 213 state_ = CREATING_STREAM;
184 ipc_->CreateStream(this, params); 214 ipc_->CreateStream(this, audio_parameters_);
185 } else { 215 } else {
186 RequestDeviceAuthorizationOnIOThread(); 216 RequestDeviceAuthorizationOnIOThread();
187 start_on_authorized_ = true; 217 start_on_authorized_ = true;
188 } 218 }
189 break; 219 break;
190 220
191 case AUTHORIZING: 221 case AUTHORIZING:
192 start_on_authorized_ = true; 222 start_on_authorized_ = true;
193 break; 223 break;
194 224
195 case AUTHORIZED: 225 case AUTHORIZED:
196 state_ = CREATING_STREAM; 226 state_ = CREATING_STREAM;
197 ipc_->CreateStream(this, params); 227 ipc_->CreateStream(this, audio_parameters_);
198 start_on_authorized_ = false; 228 start_on_authorized_ = false;
199 break; 229 break;
200 230
201 case CREATING_STREAM: 231 case CREATING_STREAM:
202 case PAUSED: 232 case PAUSED:
203 case PLAYING: 233 case PLAYING:
204 NOTREACHED(); 234 NOTREACHED();
205 break; 235 break;
206 } 236 }
207 } 237 }
(...skipping 15 matching lines...) Expand all
223 DCHECK(task_runner()->BelongsToCurrentThread()); 253 DCHECK(task_runner()->BelongsToCurrentThread());
224 if (state_ == PLAYING) { 254 if (state_ == PLAYING) {
225 TRACE_EVENT_ASYNC_END0( 255 TRACE_EVENT_ASYNC_END0(
226 "audio", "StartingPlayback", audio_callback_.get()); 256 "audio", "StartingPlayback", audio_callback_.get());
227 ipc_->PauseStream(); 257 ipc_->PauseStream();
228 state_ = PAUSED; 258 state_ = PAUSED;
229 } 259 }
230 play_on_start_ = false; 260 play_on_start_ = false;
231 } 261 }
232 262
233 void AudioOutputDevice::ShutDownOnIOThread() { 263 void AudioOutputDevice::StopOnIOThread() {
234 DCHECK(task_runner()->BelongsToCurrentThread()); 264 DCHECK(task_runner()->BelongsToCurrentThread());
235 265
236 // Close the stream, if we haven't already. 266 // Close the stream, if we haven't already.
237 if (state_ >= AUTHORIZING) { 267 if (state_ >= AUTHORIZING) {
238 ipc_->CloseStream(); 268 ipc_->CloseStream();
239 state_ = IDLE; 269 state_ = IDLE;
240 } 270 }
241 start_on_authorized_ = false; 271 start_on_authorized_ = false;
242 272
243 // We can run into an issue where ShutDownOnIOThread is called right after 273 // We can run into an issue where StopOnIOThread is called right after
244 // OnStreamCreated is called in cases where Start/Stop are called before we 274 // OnStreamCreated is called in cases where Start/Stop are called before we
245 // get the OnStreamCreated callback. To handle that corner case, we call 275 // get the OnStreamCreated callback. To handle that corner case, we call
246 // Stop(). In most cases, the thread will already be stopped. 276 // Stop(). In most cases, the thread will already be stopped.
Guido Urdaneta 2016/04/01 10:17:01 now you call Pause() instead of Stop()
Henrik Grunell 2016/05/09 12:13:37 Done in other CL.
247 // 277
248 // Another situation is when the IO thread goes away before Stop() is called
249 // in which case, we cannot use the message loop to close the thread handle
250 // and can't rely on the main thread existing either.
251 base::AutoLock auto_lock_(audio_thread_lock_); 278 base::AutoLock auto_lock_(audio_thread_lock_);
252 base::ThreadRestrictions::ScopedAllowIO allow_io; 279 audio_thread_.Pause();
253 audio_thread_.Stop(NULL);
254 audio_callback_.reset(); 280 audio_callback_.reset();
255 stopping_hack_ = false; 281 stopping_hack_ = false;
282 callback_ = nullptr;
256 } 283 }
257 284
258 void AudioOutputDevice::SetVolumeOnIOThread(double volume) { 285 void AudioOutputDevice::SetVolumeOnIOThread(double volume) {
259 DCHECK(task_runner()->BelongsToCurrentThread()); 286 DCHECK(task_runner()->BelongsToCurrentThread());
260 if (state_ >= CREATING_STREAM) 287 if (state_ >= CREATING_STREAM)
261 ipc_->SetVolume(volume); 288 ipc_->SetVolume(volume);
262 } 289 }
263 290
264 void AudioOutputDevice::OnStateChanged(AudioOutputIPCDelegateState state) { 291 void AudioOutputDevice::OnStateChanged(AudioOutputIPCDelegateState state) {
265 DCHECK(task_runner()->BelongsToCurrentThread()); 292 DCHECK(task_runner()->BelongsToCurrentThread());
266 293
267 // Do nothing if the stream has been closed. 294 // Do nothing if the stream has been closed.
268 if (state_ < CREATING_STREAM) 295 if (state_ < CREATING_STREAM)
269 return; 296 return;
270 297
271 // TODO(miu): Clean-up inconsistent and incomplete handling here. 298 // TODO(miu): Clean-up inconsistent and incomplete handling here.
272 // http://crbug.com/180640 299 // http://crbug.com/180640
273 switch (state) { 300 switch (state) {
274 case AUDIO_OUTPUT_IPC_DELEGATE_STATE_PLAYING: 301 case AUDIO_OUTPUT_IPC_DELEGATE_STATE_PLAYING:
275 case AUDIO_OUTPUT_IPC_DELEGATE_STATE_PAUSED: 302 case AUDIO_OUTPUT_IPC_DELEGATE_STATE_PAUSED:
276 break; 303 break;
277 case AUDIO_OUTPUT_IPC_DELEGATE_STATE_ERROR: 304 case AUDIO_OUTPUT_IPC_DELEGATE_STATE_ERROR:
278 DLOG(WARNING) << "AudioOutputDevice::OnStateChanged(ERROR)"; 305 DLOG(WARNING) << "AudioOutputDevice::OnStateChanged(ERROR)";
279 // Don't dereference the callback object if the audio thread 306 // The callback object is cleared in Stop().
280 // is stopped or stopping. That could mean that the callback 307 if (callback_)
281 // object has been deleted.
282 // TODO(tommi): Add an explicit contract for clearing the callback
283 // object. Possibly require calling Initialize again or provide
284 // a callback object via Start() and clear it in Stop().
285 if (!audio_thread_.IsStopped())
286 callback_->OnRenderError(); 308 callback_->OnRenderError();
287 break; 309 break;
288 default: 310 default:
289 NOTREACHED(); 311 NOTREACHED();
290 break; 312 break;
291 } 313 }
292 } 314 }
293 315
294 void AudioOutputDevice::OnDeviceAuthorized( 316 void AudioOutputDevice::OnDeviceAuthorized(
295 OutputDeviceStatus device_status, 317 OutputDeviceStatus device_status,
(...skipping 12 matching lines...) Expand all
308 if (!did_receive_auth_.IsSignaled()) 330 if (!did_receive_auth_.IsSignaled())
309 device_status_ = device_status; 331 device_status_ = device_status;
310 332
311 if (device_status == OUTPUT_DEVICE_STATUS_OK) { 333 if (device_status == OUTPUT_DEVICE_STATUS_OK) {
312 state_ = AUTHORIZED; 334 state_ = AUTHORIZED;
313 if (!did_receive_auth_.IsSignaled()) { 335 if (!did_receive_auth_.IsSignaled()) {
314 output_params_ = output_params; 336 output_params_ = output_params;
315 did_receive_auth_.Signal(); 337 did_receive_auth_.Signal();
316 } 338 }
317 if (start_on_authorized_) 339 if (start_on_authorized_)
318 CreateStreamOnIOThread(audio_parameters_); 340 CreateStreamOnIOThread();
319 } else { 341 } else {
320 // Closing IPC forces a Signal(), so no clients are locked waiting 342 // Closing IPC forces a Signal(), so no clients are locked waiting
321 // indefinitely after this method returns. 343 // indefinitely after this method returns.
322 ipc_->CloseStream(); 344 ipc_->CloseStream();
323 OnIPCClosed(); 345 OnIPCClosed();
324 if (callback_) 346 if (callback_)
325 callback_->OnRenderError(); 347 callback_->OnRenderError();
326 } 348 }
327 } 349 }
328 350
329 void AudioOutputDevice::OnStreamCreated( 351 void AudioOutputDevice::OnStreamCreated(
330 base::SharedMemoryHandle handle, 352 base::SharedMemoryHandle handle,
331 base::SyncSocket::Handle socket_handle, 353 base::SyncSocket::Handle socket_handle,
332 int length) { 354 int length) {
333 DCHECK(task_runner()->BelongsToCurrentThread()); 355 DCHECK(task_runner()->BelongsToCurrentThread());
334 DCHECK(base::SharedMemory::IsHandleValid(handle)); 356 DCHECK(base::SharedMemory::IsHandleValid(handle));
335 #if defined(OS_WIN) 357 #if defined(OS_WIN)
336 DCHECK(socket_handle); 358 DCHECK(socket_handle);
337 #else 359 #else
338 DCHECK_GE(socket_handle, 0); 360 DCHECK_GE(socket_handle, 0);
339 #endif 361 #endif
340 DCHECK_GT(length, 0); 362 DCHECK_GT(length, 0);
341 363
342 if (state_ != CREATING_STREAM) 364 if (state_ != CREATING_STREAM)
343 return; 365 return;
344 366
345 // We can receive OnStreamCreated() on the IO thread after the client has 367 // We can receive OnStreamCreated() on the IO thread after the client has
346 // called Stop() but before ShutDownOnIOThread() is processed. In such a 368 // called Stop() but before StopOnIOThread() is processed. In such a
347 // situation |callback_| might point to freed memory. Instead of starting 369 // situation |callback_| might point to freed memory. Instead of starting
348 // |audio_thread_| do nothing and wait for ShutDownOnIOThread() to get called. 370 // |audio_thread_| do nothing and wait for StopOnIOThread() to get called.
349 // 371 //
350 // TODO(scherkus): The real fix is to have sane ownership semantics. The fact 372 // TODO(scherkus): The real fix is to have sane ownership semantics. The fact
351 // that |callback_| (which should own and outlive this object!) can point to 373 // that |callback_| (which should own and outlive this object!) can point to
352 // freed memory is a mess. AudioRendererSink should be non-refcounted so that 374 // freed memory is a mess. AudioRendererSink should be non-refcounted so that
353 // owners (WebRtcAudioDeviceImpl, AudioRendererImpl, etc...) can Stop() and 375 // owners (WebRtcAudioDeviceImpl, AudioRendererImpl, etc...) can Stop() and
354 // delete as they see fit. AudioOutputDevice should internally use WeakPtr 376 // delete as they see fit. AudioOutputDevice should internally use WeakPtr
355 // to handle teardown and thread hopping. See http://crbug.com/151051 for 377 // to handle teardown and thread hopping. See http://crbug.com/151051 for
356 // details. 378 // details.
357 { 379 {
358 base::AutoLock auto_lock(audio_thread_lock_); 380 base::AutoLock auto_lock(audio_thread_lock_);
359 if (stopping_hack_) 381 if (stopping_hack_)
360 return; 382 return;
361 383
362 DCHECK(audio_thread_.IsStopped()); 384 DCHECK(audio_thread_.IsStopped());
363 audio_callback_.reset(new AudioOutputDevice::AudioThreadCallback( 385 audio_callback_.reset(new AudioOutputDevice::AudioThreadCallback(
364 audio_parameters_, handle, length, callback_)); 386 audio_parameters_, handle, length, callback_));
365 audio_thread_.Start(audio_callback_.get(), socket_handle, 387
366 "AudioOutputDevice", true); 388 // If the audio thread hasn't been started before, start it.
389 if (audio_thread_.IsStopped()) {
390 audio_thread_.Start("AudioOutputDevice", true);
391 }
392 audio_thread_.Play(audio_callback_.get(), socket_handle);
393
367 state_ = PAUSED; 394 state_ = PAUSED;
368 395
369 // We handle the case where Play() and/or Pause() may have been called 396 // We handle the case where Play() and/or Pause() may have been called
370 // multiple times before OnStreamCreated() gets called. 397 // multiple times before OnStreamCreated() gets called.
371 if (play_on_start_) 398 if (play_on_start_)
372 PlayOnIOThread(); 399 PlayOnIOThread();
373 } 400 }
374 } 401 }
375 402
376 void AudioOutputDevice::OnIPCClosed() { 403 void AudioOutputDevice::OnIPCClosed() {
377 DCHECK(task_runner()->BelongsToCurrentThread()); 404 DCHECK(task_runner()->BelongsToCurrentThread());
378 state_ = IPC_CLOSED; 405 state_ = IPC_CLOSED;
379 ipc_.reset(); 406 ipc_.reset();
380 407
381 // Signal to unblock any blocked threads waiting for parameters 408 // Signal to unblock any blocked threads waiting for parameters
382 did_receive_auth_.Signal(); 409 did_receive_auth_.Signal();
383 } 410 }
384 411
385 void AudioOutputDevice::WillDestroyCurrentMessageLoop() { 412 void AudioOutputDevice::WillDestroyCurrentMessageLoop() {
386 LOG(ERROR) << "IO loop going away before the audio device has been stopped"; 413 LOG(ERROR) << "IO loop going away before the audio device has been stopped";
387 ShutDownOnIOThread(); 414 StopOnIOThread();
388 } 415 }
389 416
390 // AudioOutputDevice::AudioThreadCallback 417 // AudioOutputDevice::AudioThreadCallback
391 418
392 AudioOutputDevice::AudioThreadCallback::AudioThreadCallback( 419 AudioOutputDevice::AudioThreadCallback::AudioThreadCallback(
393 const AudioParameters& audio_parameters, 420 const AudioParameters& audio_parameters,
394 base::SharedMemoryHandle memory, 421 base::SharedMemoryHandle memory,
395 int memory_length, 422 int memory_length,
396 AudioRendererSink::RenderCallback* render_callback) 423 AudioRendererSink::RenderCallback* render_callback)
397 : AudioDeviceThread::Callback(audio_parameters, memory, memory_length, 1), 424 : AudioDeviceThread::Callback(audio_parameters, memory, memory_length, 1),
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
441 468
442 // Update the audio-delay measurement, inform about the number of skipped 469 // Update the audio-delay measurement, inform about the number of skipped
443 // frames, and ask client to render audio. Since |output_bus_| is wrapping 470 // frames, and ask client to render audio. Since |output_bus_| is wrapping
444 // the shared memory the Render() call is writing directly into the shared 471 // the shared memory the Render() call is writing directly into the shared
445 // memory. 472 // memory.
446 render_callback_->Render(output_bus_.get(), std::round(frames_delayed), 473 render_callback_->Render(output_bus_.get(), std::round(frames_delayed),
447 frames_skipped); 474 frames_skipped);
448 } 475 }
449 476
450 } // namespace media 477 } // namespace media
OLDNEW
« media/audio/audio_output_device.h ('K') | « media/audio/audio_output_device.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698