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 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 50 }; | 50 }; |
| 51 | 51 |
| 52 AudioOutputDevice::AudioOutputDevice( | 52 AudioOutputDevice::AudioOutputDevice( |
| 53 std::unique_ptr<AudioOutputIPC> ipc, | 53 std::unique_ptr<AudioOutputIPC> ipc, |
| 54 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner, | 54 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner, |
| 55 int session_id, | 55 int session_id, |
| 56 const std::string& device_id, | 56 const std::string& device_id, |
| 57 const url::Origin& security_origin, | 57 const url::Origin& security_origin, |
| 58 base::TimeDelta authorization_timeout) | 58 base::TimeDelta authorization_timeout) |
| 59 : ScopedTaskRunnerObserver(io_task_runner), | 59 : ScopedTaskRunnerObserver(io_task_runner), |
| 60 callback_(NULL), | |
| 61 ipc_(std::move(ipc)), | 60 ipc_(std::move(ipc)), |
| 62 state_(IDLE), | 61 state_(IDLE), |
| 63 start_on_authorized_(false), | 62 start_on_authorized_(false), |
| 64 play_on_start_(true), | 63 play_on_start_(true), |
| 65 session_id_(session_id), | 64 session_id_(session_id), |
| 66 device_id_(device_id), | 65 device_id_(device_id), |
| 67 security_origin_(security_origin), | 66 security_origin_(security_origin), |
| 67 render_callback_(nullptr), | |
| 68 stopping_hack_(false), | 68 stopping_hack_(false), |
| 69 did_receive_auth_(base::WaitableEvent::ResetPolicy::MANUAL, | 69 did_receive_auth_(base::WaitableEvent::ResetPolicy::MANUAL, |
| 70 base::WaitableEvent::InitialState::NOT_SIGNALED), | 70 base::WaitableEvent::InitialState::NOT_SIGNALED), |
| 71 device_status_(OUTPUT_DEVICE_STATUS_ERROR_INTERNAL), | 71 device_status_(OUTPUT_DEVICE_STATUS_ERROR_INTERNAL), |
| 72 auth_timeout_(authorization_timeout) { | 72 auth_timeout_(authorization_timeout) { |
| 73 CHECK(ipc_); | 73 CHECK(ipc_); |
| 74 | 74 |
| 75 // The correctness of the code depends on the relative values assigned in the | 75 // The correctness of the code depends on the relative values assigned in the |
| 76 // State enum. | 76 // State enum. |
| 77 static_assert(IPC_CLOSED < IDLE, "invalid enum value assignment 0"); | 77 static_assert(IPC_CLOSED < IDLE, "invalid enum value assignment 0"); |
| 78 static_assert(IDLE < AUTHORIZING, "invalid enum value assignment 1"); | 78 static_assert(IDLE < AUTHORIZING, "invalid enum value assignment 1"); |
| 79 static_assert(AUTHORIZING < AUTHORIZED, "invalid enum value assignment 2"); | 79 static_assert(AUTHORIZING < AUTHORIZED, "invalid enum value assignment 2"); |
| 80 static_assert(AUTHORIZED < CREATING_STREAM, | 80 static_assert(AUTHORIZED < CREATING_STREAM, |
| 81 "invalid enum value assignment 3"); | 81 "invalid enum value assignment 3"); |
| 82 static_assert(CREATING_STREAM < PAUSED, "invalid enum value assignment 4"); | 82 static_assert(CREATING_STREAM < PAUSED, "invalid enum value assignment 4"); |
| 83 static_assert(PAUSED < PLAYING, "invalid enum value assignment 5"); | 83 static_assert(PAUSED < PLAYING, "invalid enum value assignment 5"); |
| 84 | |
| 85 thread_checker_.DetachFromThread(); | |
|
DaleCurtis
2016/06/15 16:51:41
Needs a more specific name in light of your other
Henrik Grunell
2016/06/16 12:39:41
Done.
| |
| 84 } | 86 } |
| 85 | 87 |
| 86 void AudioOutputDevice::Initialize(const AudioParameters& params, | 88 void AudioOutputDevice::Initialize( |
| 87 RenderCallback* callback) { | 89 const AudioParameters& params, |
| 88 DCHECK(!callback_) << "Calling Initialize() twice?"; | 90 AudioRendererSink::RenderCallback* callback) { |
| 91 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 92 DCHECK(!render_callback_) << "Already initialized."; | |
| 89 DCHECK(params.IsValid()); | 93 DCHECK(params.IsValid()); |
| 90 audio_parameters_ = params; | 94 audio_parameters_ = params; |
| 91 callback_ = callback; | 95 { |
| 96 base::AutoLock auto_lock(lock_); | |
|
DaleCurtis
2016/06/15 16:51:42
Locking shouldn't be necessary here, right? The th
Henrik Grunell
2016/06/16 12:39:42
|render_callback_| isn't only coupled with the thr
| |
| 97 render_callback_ = callback; | |
| 98 } | |
| 92 } | 99 } |
| 93 | 100 |
| 94 AudioOutputDevice::~AudioOutputDevice() { | 101 AudioOutputDevice::~AudioOutputDevice() { |
| 95 // The current design requires that the user calls Stop() before deleting | 102 // The current design requires that the user calls Stop() before deleting |
| 96 // this class. | 103 // this class to ensure that stream and authorization data is cleaned up on |
| 104 // the browser side, and that we don't get more IPC calls from AudioOutputIPC. | |
| 105 // With all posted tasks on IO thread done and no more IPC calls, it's safe to | |
| 106 // access |audio_thread_| here on any thread. | |
| 97 DCHECK(audio_thread_.IsStopped()); | 107 DCHECK(audio_thread_.IsStopped()); |
| 98 } | 108 } |
| 99 | 109 |
| 100 void AudioOutputDevice::RequestDeviceAuthorization() { | 110 void AudioOutputDevice::RequestDeviceAuthorization() { |
| 101 task_runner()->PostTask( | 111 task_runner()->PostTask( |
| 102 FROM_HERE, | 112 FROM_HERE, |
| 103 base::Bind(&AudioOutputDevice::RequestDeviceAuthorizationOnIOThread, | 113 base::Bind(&AudioOutputDevice::RequestDeviceAuthorizationOnIOThread, |
| 104 this)); | 114 this)); |
| 105 } | 115 } |
| 106 | 116 |
| 107 void AudioOutputDevice::Start() { | 117 void AudioOutputDevice::Start() { |
| 108 DCHECK(callback_) << "Initialize hasn't been called"; | 118 DCHECK(thread_checker_.CalledOnValidThread()); |
| 109 task_runner()->PostTask(FROM_HERE, | 119 DCHECK(render_callback_) << "Not initialized."; |
| 110 base::Bind(&AudioOutputDevice::CreateStreamOnIOThread, this, | 120 { |
|
DaleCurtis
2016/06/15 16:51:41
Again, shouldn't be necessary, the thread is stopp
Henrik Grunell
2016/06/16 12:39:41
Right, it's cleared in StoponIOThread. That should
| |
| 111 audio_parameters_)); | 121 base::AutoLock auto_lock(lock_); |
| 122 stopping_hack_ = false; | |
| 123 } | |
| 124 | |
| 125 task_runner()->PostTask( | |
| 126 FROM_HERE, base::Bind(&AudioOutputDevice::CreateStreamOnIOThread, this)); | |
| 112 } | 127 } |
| 113 | 128 |
| 114 void AudioOutputDevice::Stop() { | 129 void AudioOutputDevice::Stop() { |
| 130 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 115 { | 131 { |
| 116 base::AutoLock auto_lock(audio_thread_lock_); | 132 base::AutoLock auto_lock(lock_); |
| 117 audio_thread_.Stop(base::MessageLoop::current()); | 133 audio_thread_.Stop(base::MessageLoop::current()); |
| 118 stopping_hack_ = true; | 134 stopping_hack_ = true; |
| 135 render_callback_ = nullptr; | |
| 119 } | 136 } |
| 120 | 137 |
| 121 task_runner()->PostTask(FROM_HERE, | 138 task_runner()->PostTask(FROM_HERE, |
| 122 base::Bind(&AudioOutputDevice::ShutDownOnIOThread, this)); | 139 base::Bind(&AudioOutputDevice::StopOnIOThread, this)); |
| 123 } | 140 } |
| 124 | 141 |
| 125 void AudioOutputDevice::Play() { | 142 void AudioOutputDevice::Play() { |
| 126 task_runner()->PostTask(FROM_HERE, | 143 task_runner()->PostTask(FROM_HERE, |
| 127 base::Bind(&AudioOutputDevice::PlayOnIOThread, this)); | 144 base::Bind(&AudioOutputDevice::PlayOnIOThread, this)); |
| 128 } | 145 } |
| 129 | 146 |
| 130 void AudioOutputDevice::Pause() { | 147 void AudioOutputDevice::Pause() { |
| 131 task_runner()->PostTask(FROM_HERE, | 148 task_runner()->PostTask(FROM_HERE, |
| 132 base::Bind(&AudioOutputDevice::PauseOnIOThread, this)); | 149 base::Bind(&AudioOutputDevice::PauseOnIOThread, this)); |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 147 OutputDeviceInfo AudioOutputDevice::GetOutputDeviceInfo() { | 164 OutputDeviceInfo AudioOutputDevice::GetOutputDeviceInfo() { |
| 148 CHECK(!task_runner()->BelongsToCurrentThread()); | 165 CHECK(!task_runner()->BelongsToCurrentThread()); |
| 149 did_receive_auth_.Wait(); | 166 did_receive_auth_.Wait(); |
| 150 return OutputDeviceInfo(AudioDeviceDescription::UseSessionIdToSelectDevice( | 167 return OutputDeviceInfo(AudioDeviceDescription::UseSessionIdToSelectDevice( |
| 151 session_id_, device_id_) | 168 session_id_, device_id_) |
| 152 ? matched_device_id_ | 169 ? matched_device_id_ |
| 153 : device_id_, | 170 : device_id_, |
| 154 device_status_, output_params_); | 171 device_status_, output_params_); |
| 155 } | 172 } |
| 156 | 173 |
| 174 void AudioOutputDevice::ReportRenderError() { | |
| 175 base::AutoLock auto_lock(lock_); | |
| 176 if (render_callback_) | |
| 177 render_callback_->OnRenderError(); | |
| 178 } | |
| 179 | |
| 157 void AudioOutputDevice::RequestDeviceAuthorizationOnIOThread() { | 180 void AudioOutputDevice::RequestDeviceAuthorizationOnIOThread() { |
| 158 DCHECK(task_runner()->BelongsToCurrentThread()); | 181 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 159 DCHECK_EQ(state_, IDLE); | 182 DCHECK_EQ(state_, IDLE); |
| 160 state_ = AUTHORIZING; | 183 state_ = AUTHORIZING; |
| 184 | |
| 185 // TODO(grunell): Store authorization so that we don't have to authorize | |
| 186 // again when restarting. http://crbug.com/608619. | |
| 161 ipc_->RequestDeviceAuthorization(this, session_id_, device_id_, | 187 ipc_->RequestDeviceAuthorization(this, session_id_, device_id_, |
| 162 security_origin_); | 188 security_origin_); |
| 163 | 189 |
| 164 // Create the timer on the thread it's used on. It's guaranteed to be | 190 // Create the timer on the thread it's used on. It's guaranteed to be |
| 165 // deleted on the same thread since users must call Stop() before deleting | 191 // deleted on the same thread since users must call Stop() before deleting |
| 166 // AudioOutputDevice; see ShutDownOnIOThread(). | 192 // AudioOutputDevice; see ShutDownOnIOThread(). |
| 167 auth_timeout_action_.reset(new base::OneShotTimer()); | 193 auth_timeout_action_.reset(new base::OneShotTimer()); |
| 168 auth_timeout_action_->Start( | 194 auth_timeout_action_->Start( |
| 169 FROM_HERE, auth_timeout_, | 195 FROM_HERE, auth_timeout_, |
| 170 base::Bind(&AudioOutputDevice::OnDeviceAuthorized, this, | 196 base::Bind(&AudioOutputDevice::OnDeviceAuthorized, this, |
| 171 OUTPUT_DEVICE_STATUS_ERROR_TIMED_OUT, media::AudioParameters(), | 197 OUTPUT_DEVICE_STATUS_ERROR_TIMED_OUT, media::AudioParameters(), |
| 172 std::string())); | 198 std::string())); |
| 173 } | 199 } |
| 174 | 200 |
| 175 void AudioOutputDevice::CreateStreamOnIOThread(const AudioParameters& params) { | 201 void AudioOutputDevice::CreateStreamOnIOThread() { |
| 176 DCHECK(task_runner()->BelongsToCurrentThread()); | 202 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 203 | |
| 177 switch (state_) { | 204 switch (state_) { |
| 178 case IPC_CLOSED: | 205 case IPC_CLOSED: |
| 179 if (callback_) | 206 ReportRenderError(); |
| 180 callback_->OnRenderError(); | |
| 181 break; | 207 break; |
| 182 | 208 |
| 183 case IDLE: | 209 case IDLE: |
| 184 if (did_receive_auth_.IsSignaled() && device_id_.empty() && | 210 if (did_receive_auth_.IsSignaled() && device_id_.empty() && |
| 185 security_origin_.unique()) { | 211 security_origin_.unique()) { |
| 186 state_ = CREATING_STREAM; | 212 state_ = CREATING_STREAM; |
| 187 ipc_->CreateStream(this, params); | 213 ipc_->CreateStream(this, audio_parameters_); |
| 188 } else { | 214 } else { |
| 189 RequestDeviceAuthorizationOnIOThread(); | 215 RequestDeviceAuthorizationOnIOThread(); |
| 190 start_on_authorized_ = true; | 216 start_on_authorized_ = true; |
| 191 } | 217 } |
| 192 break; | 218 break; |
| 193 | 219 |
| 194 case AUTHORIZING: | 220 case AUTHORIZING: |
| 195 start_on_authorized_ = true; | 221 start_on_authorized_ = true; |
| 196 break; | 222 break; |
| 197 | 223 |
| 198 case AUTHORIZED: | 224 case AUTHORIZED: |
| 199 state_ = CREATING_STREAM; | 225 state_ = CREATING_STREAM; |
| 200 ipc_->CreateStream(this, params); | 226 ipc_->CreateStream(this, audio_parameters_); |
| 201 start_on_authorized_ = false; | 227 start_on_authorized_ = false; |
| 202 break; | 228 break; |
| 203 | 229 |
| 204 case CREATING_STREAM: | 230 case CREATING_STREAM: |
| 205 case PAUSED: | 231 case PAUSED: |
| 206 case PLAYING: | 232 case PLAYING: |
| 207 NOTREACHED(); | 233 NOTREACHED(); |
| 208 break; | 234 break; |
| 209 } | 235 } |
| 210 } | 236 } |
| 211 | 237 |
| 212 void AudioOutputDevice::PlayOnIOThread() { | 238 void AudioOutputDevice::PlayOnIOThread() { |
| 213 DCHECK(task_runner()->BelongsToCurrentThread()); | 239 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 214 if (state_ == PAUSED) { | 240 if (state_ == PAUSED) { |
| 215 TRACE_EVENT_ASYNC_BEGIN0( | 241 TRACE_EVENT_ASYNC_BEGIN0("audio", "StartingPlayback", |
| 216 "audio", "StartingPlayback", audio_callback_.get()); | 242 audio_thread_callback_.get()); |
| 217 ipc_->PlayStream(); | 243 ipc_->PlayStream(); |
| 218 state_ = PLAYING; | 244 state_ = PLAYING; |
| 219 play_on_start_ = false; | 245 play_on_start_ = false; |
| 220 } else { | 246 } else { |
| 221 play_on_start_ = true; | 247 play_on_start_ = true; |
| 222 } | 248 } |
| 223 } | 249 } |
| 224 | 250 |
| 225 void AudioOutputDevice::PauseOnIOThread() { | 251 void AudioOutputDevice::PauseOnIOThread() { |
| 226 DCHECK(task_runner()->BelongsToCurrentThread()); | 252 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 227 if (state_ == PLAYING) { | 253 if (state_ == PLAYING) { |
| 228 TRACE_EVENT_ASYNC_END0( | 254 TRACE_EVENT_ASYNC_END0("audio", "StartingPlayback", |
| 229 "audio", "StartingPlayback", audio_callback_.get()); | 255 audio_thread_callback_.get()); |
| 230 ipc_->PauseStream(); | 256 ipc_->PauseStream(); |
| 231 state_ = PAUSED; | 257 state_ = PAUSED; |
| 232 } | 258 } |
| 233 play_on_start_ = false; | 259 play_on_start_ = false; |
| 234 } | 260 } |
| 235 | 261 |
| 236 void AudioOutputDevice::ShutDownOnIOThread() { | 262 void AudioOutputDevice::StopOnIOThread() { |
| 237 DCHECK(task_runner()->BelongsToCurrentThread()); | 263 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 238 | 264 |
| 239 // Close the stream, if we haven't already. | 265 // Close the stream, if we haven't already. After CloseStream(), we are |
| 266 // guaranteed to not get IPC calls for any outstanding operations. This holds | |
| 267 // also if we restart, i.e. request authorization and create a new stream, | |
| 268 // before the previous request and/or create stream finished. | |
| 240 if (state_ >= AUTHORIZING) { | 269 if (state_ >= AUTHORIZING) { |
| 241 ipc_->CloseStream(); | 270 ipc_->CloseStream(); |
| 242 state_ = IDLE; | 271 state_ = IDLE; |
| 243 } | 272 } |
| 244 start_on_authorized_ = false; | 273 start_on_authorized_ = false; |
| 245 | 274 |
| 275 // We need to re-authorize after stopping. | |
| 276 did_receive_auth_.Reset(); | |
| 277 | |
| 246 // Destoy the timer on the thread it's used on. | 278 // Destoy the timer on the thread it's used on. |
| 247 auth_timeout_action_.reset(); | 279 auth_timeout_action_.reset(); |
| 248 | 280 |
| 249 // We can run into an issue where ShutDownOnIOThread is called right after | 281 // We can run into an issue where ShutDownOnIOThread is called right after |
| 250 // OnStreamCreated is called in cases where Start/Stop are called before we | 282 // OnStreamCreated is called in cases where Start/Stop are called before we |
| 251 // get the OnStreamCreated callback. To handle that corner case, we call | 283 // get the OnStreamCreated callback. To handle that corner case, we call |
| 252 // Stop(). In most cases, the thread will already be stopped. | 284 // Stop(). In most cases, the thread will already be stopped. |
| 253 // | 285 // |
| 254 // Another situation is when the IO thread goes away before Stop() is called | 286 // Another situation is when the IO thread goes away before Stop() is called |
| 255 // in which case, we cannot use the message loop to close the thread handle | 287 // in which case, we cannot use the message loop to close the thread handle |
| 256 // and can't rely on the main thread existing either. | 288 // and can't rely on the main thread existing either. |
| 257 base::AutoLock auto_lock_(audio_thread_lock_); | 289 base::AutoLock auto_lock_(lock_); |
| 258 base::ThreadRestrictions::ScopedAllowIO allow_io; | 290 base::ThreadRestrictions::ScopedAllowIO allow_io; |
| 259 audio_thread_.Stop(NULL); | 291 audio_thread_.Stop(NULL); |
| 260 audio_callback_.reset(); | 292 audio_thread_callback_.reset(); |
| 261 stopping_hack_ = false; | 293 stopping_hack_ = false; |
| 294 play_on_start_ = true; | |
| 262 } | 295 } |
| 263 | 296 |
| 264 void AudioOutputDevice::SetVolumeOnIOThread(double volume) { | 297 void AudioOutputDevice::SetVolumeOnIOThread(double volume) { |
| 265 DCHECK(task_runner()->BelongsToCurrentThread()); | 298 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 266 if (state_ >= CREATING_STREAM) | 299 if (state_ >= CREATING_STREAM) |
| 267 ipc_->SetVolume(volume); | 300 ipc_->SetVolume(volume); |
| 268 } | 301 } |
| 269 | 302 |
| 270 void AudioOutputDevice::OnStateChanged(AudioOutputIPCDelegateState state) { | 303 void AudioOutputDevice::OnStateChanged(AudioOutputIPCDelegateState state) { |
| 271 DCHECK(task_runner()->BelongsToCurrentThread()); | 304 DCHECK(task_runner()->BelongsToCurrentThread()); |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 282 break; | 315 break; |
| 283 case AUDIO_OUTPUT_IPC_DELEGATE_STATE_ERROR: | 316 case AUDIO_OUTPUT_IPC_DELEGATE_STATE_ERROR: |
| 284 DLOG(WARNING) << "AudioOutputDevice::OnStateChanged(ERROR)"; | 317 DLOG(WARNING) << "AudioOutputDevice::OnStateChanged(ERROR)"; |
| 285 // Don't dereference the callback object if the audio thread | 318 // Don't dereference the callback object if the audio thread |
| 286 // is stopped or stopping. That could mean that the callback | 319 // is stopped or stopping. That could mean that the callback |
| 287 // object has been deleted. | 320 // object has been deleted. |
| 288 // TODO(tommi): Add an explicit contract for clearing the callback | 321 // TODO(tommi): Add an explicit contract for clearing the callback |
| 289 // object. Possibly require calling Initialize again or provide | 322 // object. Possibly require calling Initialize again or provide |
| 290 // a callback object via Start() and clear it in Stop(). | 323 // a callback object via Start() and clear it in Stop(). |
| 291 if (!audio_thread_.IsStopped()) | 324 if (!audio_thread_.IsStopped()) |
| 292 callback_->OnRenderError(); | 325 ReportRenderError(); |
| 293 break; | 326 break; |
| 294 default: | 327 default: |
| 295 NOTREACHED(); | 328 NOTREACHED(); |
| 296 break; | 329 break; |
| 297 } | 330 } |
| 298 } | 331 } |
| 299 | 332 |
| 300 void AudioOutputDevice::OnDeviceAuthorized( | 333 void AudioOutputDevice::OnDeviceAuthorized( |
| 301 OutputDeviceStatus device_status, | 334 OutputDeviceStatus device_status, |
| 302 const media::AudioParameters& output_params, | 335 const media::AudioParameters& output_params, |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 338 matched_device_id_.empty()); | 371 matched_device_id_.empty()); |
| 339 matched_device_id_ = matched_device_id; | 372 matched_device_id_ = matched_device_id; |
| 340 | 373 |
| 341 DVLOG(1) << "AudioOutputDevice authorized, session_id: " << session_id_ | 374 DVLOG(1) << "AudioOutputDevice authorized, session_id: " << session_id_ |
| 342 << ", device_id: " << device_id_ | 375 << ", device_id: " << device_id_ |
| 343 << ", matched_device_id: " << matched_device_id_; | 376 << ", matched_device_id: " << matched_device_id_; |
| 344 | 377 |
| 345 did_receive_auth_.Signal(); | 378 did_receive_auth_.Signal(); |
| 346 } | 379 } |
| 347 if (start_on_authorized_) | 380 if (start_on_authorized_) |
| 348 CreateStreamOnIOThread(audio_parameters_); | 381 CreateStreamOnIOThread(); |
| 349 } else { | 382 } else { |
| 350 // Closing IPC forces a Signal(), so no clients are locked waiting | 383 // Closing IPC forces a Signal(), so no clients are locked waiting |
| 351 // indefinitely after this method returns. | 384 // indefinitely after this method returns. |
| 352 ipc_->CloseStream(); | 385 ipc_->CloseStream(); |
| 353 OnIPCClosed(); | 386 OnIPCClosed(); |
| 354 if (callback_) | 387 ReportRenderError(); |
| 355 callback_->OnRenderError(); | |
| 356 } | 388 } |
| 357 } | 389 } |
| 358 | 390 |
| 359 void AudioOutputDevice::OnStreamCreated( | 391 void AudioOutputDevice::OnStreamCreated( |
| 360 base::SharedMemoryHandle handle, | 392 base::SharedMemoryHandle handle, |
| 361 base::SyncSocket::Handle socket_handle, | 393 base::SyncSocket::Handle socket_handle, |
| 362 int length) { | 394 int length) { |
| 363 DCHECK(task_runner()->BelongsToCurrentThread()); | 395 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 364 DCHECK(base::SharedMemory::IsHandleValid(handle)); | 396 DCHECK(base::SharedMemory::IsHandleValid(handle)); |
| 365 #if defined(OS_WIN) | 397 #if defined(OS_WIN) |
| 366 DCHECK(socket_handle); | 398 DCHECK(socket_handle); |
| 367 #else | 399 #else |
| 368 DCHECK_GE(socket_handle, 0); | 400 DCHECK_GE(socket_handle, 0); |
| 369 #endif | 401 #endif |
| 370 DCHECK_GT(length, 0); | 402 DCHECK_GT(length, 0); |
| 371 | 403 |
| 372 if (state_ != CREATING_STREAM) | 404 if (state_ != CREATING_STREAM) |
| 373 return; | 405 return; |
| 374 | 406 |
| 375 // We can receive OnStreamCreated() on the IO thread after the client has | 407 // We can receive OnStreamCreated() on the IO thread after the client has |
| 376 // called Stop() but before ShutDownOnIOThread() is processed. In such a | 408 // called Stop() but before ShutDownOnIOThread() is processed. In such a |
| 377 // situation |callback_| might point to freed memory. Instead of starting | 409 // situation |callback_| might point to freed memory. Instead of starting |
| 378 // |audio_thread_| do nothing and wait for ShutDownOnIOThread() to get called. | 410 // |audio_thread_| do nothing and wait for ShutDownOnIOThread() to get called. |
| 379 // | 411 // |
| 380 // TODO(scherkus): The real fix is to have sane ownership semantics. The fact | 412 // TODO(grunell): AudioRendererSink should be non-refcounted and |
| 381 // that |callback_| (which should own and outlive this object!) can point to | 413 // AudioOutputDevice should internally use WeakPtr to handle teardown and |
| 382 // freed memory is a mess. AudioRendererSink should be non-refcounted so that | 414 // thread hopping. See http://crbug.com/151051 for details. |
| 383 // owners (WebRtcAudioDeviceImpl, AudioRendererImpl, etc...) can Stop() and | 415 // Note however that switching the IPC to Mojo will remove usage of the IO |
| 384 // delete as they see fit. AudioOutputDevice should internally use WeakPtr | 416 // thread, so we'll eliminate thread concurrency issues here then. See |
| 385 // to handle teardown and thread hopping. See http://crbug.com/151051 for | 417 // http://crbug.com/606707. |
| 386 // details. | 418 |
| 387 { | 419 { |
| 388 base::AutoLock auto_lock(audio_thread_lock_); | 420 base::AutoLock auto_lock(lock_); |
| 389 if (stopping_hack_) | 421 if (stopping_hack_) |
| 390 return; | 422 return; |
| 391 | 423 |
| 392 DCHECK(audio_thread_.IsStopped()); | 424 DCHECK(audio_thread_.IsStopped()); |
| 393 audio_callback_.reset(new AudioOutputDevice::AudioThreadCallback( | 425 audio_thread_callback_.reset(new AudioOutputDevice::AudioThreadCallback( |
| 394 audio_parameters_, handle, length, callback_)); | 426 audio_parameters_, handle, length, render_callback_)); |
| 395 audio_thread_.Start(audio_callback_.get(), socket_handle, | 427 audio_thread_.Start(audio_thread_callback_.get(), socket_handle, |
| 396 "AudioOutputDevice", true); | 428 "AudioOutputDevice", true); |
| 397 state_ = PAUSED; | 429 state_ = PAUSED; |
| 398 | 430 |
| 399 // We handle the case where Play() and/or Pause() may have been called | 431 // We handle the case where Play() and/or Pause() may have been called |
| 400 // multiple times before OnStreamCreated() gets called. | 432 // multiple times before OnStreamCreated() gets called. |
| 401 if (play_on_start_) | 433 if (play_on_start_) |
| 402 PlayOnIOThread(); | 434 PlayOnIOThread(); |
| 403 } | 435 } |
| 404 } | 436 } |
| 405 | 437 |
| 406 void AudioOutputDevice::OnIPCClosed() { | 438 void AudioOutputDevice::OnIPCClosed() { |
| 407 DCHECK(task_runner()->BelongsToCurrentThread()); | 439 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 408 state_ = IPC_CLOSED; | 440 state_ = IPC_CLOSED; |
| 409 ipc_.reset(); | 441 ipc_.reset(); |
| 410 | 442 |
| 411 // Signal to unblock any blocked threads waiting for parameters | 443 // Signal to unblock any blocked threads waiting for parameters |
| 412 did_receive_auth_.Signal(); | 444 did_receive_auth_.Signal(); |
| 413 } | 445 } |
| 414 | 446 |
| 415 void AudioOutputDevice::WillDestroyCurrentMessageLoop() { | 447 void AudioOutputDevice::WillDestroyCurrentMessageLoop() { |
| 416 LOG(ERROR) << "IO loop going away before the audio device has been stopped"; | 448 LOG(ERROR) << "IO loop going away before the audio device has been stopped"; |
| 417 ShutDownOnIOThread(); | 449 StopOnIOThread(); |
| 418 } | 450 } |
| 419 | 451 |
| 420 // AudioOutputDevice::AudioThreadCallback | 452 // AudioOutputDevice::AudioThreadCallback |
| 421 | 453 |
| 422 AudioOutputDevice::AudioThreadCallback::AudioThreadCallback( | 454 AudioOutputDevice::AudioThreadCallback::AudioThreadCallback( |
| 423 const AudioParameters& audio_parameters, | 455 const AudioParameters& audio_parameters, |
| 424 base::SharedMemoryHandle memory, | 456 base::SharedMemoryHandle memory, |
| 425 int memory_length, | 457 int memory_length, |
| 426 AudioRendererSink::RenderCallback* render_callback) | 458 AudioRendererSink::RenderCallback* render_callback) |
| 427 : AudioDeviceThread::Callback(audio_parameters, memory, memory_length, 1), | 459 : AudioDeviceThread::Callback(audio_parameters, memory, memory_length, 1), |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 471 | 503 |
| 472 // Update the audio-delay measurement, inform about the number of skipped | 504 // Update the audio-delay measurement, inform about the number of skipped |
| 473 // frames, and ask client to render audio. Since |output_bus_| is wrapping | 505 // frames, and ask client to render audio. Since |output_bus_| is wrapping |
| 474 // the shared memory the Render() call is writing directly into the shared | 506 // the shared memory the Render() call is writing directly into the shared |
| 475 // memory. | 507 // memory. |
| 476 render_callback_->Render(output_bus_.get(), std::round(frames_delayed), | 508 render_callback_->Render(output_bus_.get(), std::round(frames_delayed), |
| 477 frames_skipped); | 509 frames_skipped); |
| 478 } | 510 } |
| 479 | 511 |
| 480 } // namespace media | 512 } // namespace media |
| OLD | NEW |