Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |