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 control_thread_checker_.DetachFromThread(); | |
| 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(control_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 { |
|
DaleCurtis
2016/06/16 16:39:14
Drop the lock? DCHECK(!thread.started()) ?
Henrik Grunell
2016/06/16 20:23:00
SG, I'll do that tomorrow.
| |
| 96 base::AutoLock auto_lock(lock_); | |
| 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(control_thread_checker_.CalledOnValidThread()); |
| 109 task_runner()->PostTask(FROM_HERE, | 119 DCHECK(render_callback_) << "Not initialized."; |
| 110 base::Bind(&AudioOutputDevice::CreateStreamOnIOThread, this, | 120 task_runner()->PostTask( |
| 111 audio_parameters_)); | 121 FROM_HERE, base::Bind(&AudioOutputDevice::CreateStreamOnIOThread, this)); |
| 112 } | 122 } |
| 113 | 123 |
| 114 void AudioOutputDevice::Stop() { | 124 void AudioOutputDevice::Stop() { |
| 125 DCHECK(control_thread_checker_.CalledOnValidThread()); | |
| 115 { | 126 { |
| 116 base::AutoLock auto_lock(audio_thread_lock_); | 127 base::AutoLock auto_lock(lock_); |
| 117 audio_thread_.Stop(base::MessageLoop::current()); | 128 audio_thread_.Stop(base::MessageLoop::current()); |
| 118 stopping_hack_ = true; | 129 stopping_hack_ = true; |
| 130 render_callback_ = nullptr; | |
| 119 } | 131 } |
| 120 | 132 |
| 121 task_runner()->PostTask(FROM_HERE, | 133 task_runner()->PostTask(FROM_HERE, |
| 122 base::Bind(&AudioOutputDevice::ShutDownOnIOThread, this)); | 134 base::Bind(&AudioOutputDevice::StopOnIOThread, this)); |
| 123 } | 135 } |
| 124 | 136 |
| 125 void AudioOutputDevice::Play() { | 137 void AudioOutputDevice::Play() { |
| 126 task_runner()->PostTask(FROM_HERE, | 138 task_runner()->PostTask(FROM_HERE, |
| 127 base::Bind(&AudioOutputDevice::PlayOnIOThread, this)); | 139 base::Bind(&AudioOutputDevice::PlayOnIOThread, this)); |
| 128 } | 140 } |
| 129 | 141 |
| 130 void AudioOutputDevice::Pause() { | 142 void AudioOutputDevice::Pause() { |
|
DaleCurtis
2016/06/16 22:00:53
Note: This is called from the Render thread by Ren
| |
| 131 task_runner()->PostTask(FROM_HERE, | 143 task_runner()->PostTask(FROM_HERE, |
| 132 base::Bind(&AudioOutputDevice::PauseOnIOThread, this)); | 144 base::Bind(&AudioOutputDevice::PauseOnIOThread, this)); |
| 133 } | 145 } |
| 134 | 146 |
| 135 bool AudioOutputDevice::SetVolume(double volume) { | 147 bool AudioOutputDevice::SetVolume(double volume) { |
| 136 if (volume < 0 || volume > 1.0) | 148 if (volume < 0 || volume > 1.0) |
| 137 return false; | 149 return false; |
| 138 | 150 |
| 139 if (!task_runner()->PostTask(FROM_HERE, | 151 if (!task_runner()->PostTask(FROM_HERE, |
| 140 base::Bind(&AudioOutputDevice::SetVolumeOnIOThread, this, volume))) { | 152 base::Bind(&AudioOutputDevice::SetVolumeOnIOThread, this, volume))) { |
| 141 return false; | 153 return false; |
| 142 } | 154 } |
| 143 | 155 |
| 144 return true; | 156 return true; |
| 145 } | 157 } |
| 146 | 158 |
| 147 OutputDeviceInfo AudioOutputDevice::GetOutputDeviceInfo() { | 159 OutputDeviceInfo AudioOutputDevice::GetOutputDeviceInfo() { |
| 148 CHECK(!task_runner()->BelongsToCurrentThread()); | 160 CHECK(!task_runner()->BelongsToCurrentThread()); |
| 149 did_receive_auth_.Wait(); | 161 did_receive_auth_.Wait(); |
| 150 return OutputDeviceInfo(AudioDeviceDescription::UseSessionIdToSelectDevice( | 162 return OutputDeviceInfo(AudioDeviceDescription::UseSessionIdToSelectDevice( |
| 151 session_id_, device_id_) | 163 session_id_, device_id_) |
| 152 ? matched_device_id_ | 164 ? matched_device_id_ |
| 153 : device_id_, | 165 : device_id_, |
| 154 device_status_, output_params_); | 166 device_status_, output_params_); |
| 155 } | 167 } |
| 156 | 168 |
| 169 void AudioOutputDevice::ReportRenderError() { | |
| 170 base::AutoLock auto_lock(lock_); | |
| 171 if (render_callback_) | |
| 172 render_callback_->OnRenderError(); | |
| 173 } | |
| 174 | |
| 157 void AudioOutputDevice::RequestDeviceAuthorizationOnIOThread() { | 175 void AudioOutputDevice::RequestDeviceAuthorizationOnIOThread() { |
| 158 DCHECK(task_runner()->BelongsToCurrentThread()); | 176 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 159 DCHECK_EQ(state_, IDLE); | 177 DCHECK_EQ(state_, IDLE); |
| 160 state_ = AUTHORIZING; | 178 state_ = AUTHORIZING; |
| 179 | |
| 180 // TODO(grunell): Store authorization so that we don't have to authorize | |
| 181 // again when restarting. http://crbug.com/608619. | |
| 161 ipc_->RequestDeviceAuthorization(this, session_id_, device_id_, | 182 ipc_->RequestDeviceAuthorization(this, session_id_, device_id_, |
| 162 security_origin_); | 183 security_origin_); |
| 163 | 184 |
| 164 // Create the timer on the thread it's used on. It's guaranteed to be | 185 // 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 | 186 // deleted on the same thread since users must call Stop() before deleting |
| 166 // AudioOutputDevice; see ShutDownOnIOThread(). | 187 // AudioOutputDevice; see ShutDownOnIOThread(). |
| 167 auth_timeout_action_.reset(new base::OneShotTimer()); | 188 auth_timeout_action_.reset(new base::OneShotTimer()); |
| 168 auth_timeout_action_->Start( | 189 auth_timeout_action_->Start( |
| 169 FROM_HERE, auth_timeout_, | 190 FROM_HERE, auth_timeout_, |
| 170 base::Bind(&AudioOutputDevice::OnDeviceAuthorized, this, | 191 base::Bind(&AudioOutputDevice::OnDeviceAuthorized, this, |
| 171 OUTPUT_DEVICE_STATUS_ERROR_TIMED_OUT, media::AudioParameters(), | 192 OUTPUT_DEVICE_STATUS_ERROR_TIMED_OUT, media::AudioParameters(), |
| 172 std::string())); | 193 std::string())); |
| 173 } | 194 } |
| 174 | 195 |
| 175 void AudioOutputDevice::CreateStreamOnIOThread(const AudioParameters& params) { | 196 void AudioOutputDevice::CreateStreamOnIOThread() { |
| 176 DCHECK(task_runner()->BelongsToCurrentThread()); | 197 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 198 | |
| 177 switch (state_) { | 199 switch (state_) { |
| 178 case IPC_CLOSED: | 200 case IPC_CLOSED: |
| 179 if (callback_) | 201 ReportRenderError(); |
| 180 callback_->OnRenderError(); | |
| 181 break; | 202 break; |
| 182 | 203 |
| 183 case IDLE: | 204 case IDLE: |
| 184 if (did_receive_auth_.IsSignaled() && device_id_.empty() && | 205 if (did_receive_auth_.IsSignaled() && device_id_.empty() && |
| 185 security_origin_.unique()) { | 206 security_origin_.unique()) { |
| 186 state_ = CREATING_STREAM; | 207 state_ = CREATING_STREAM; |
| 187 ipc_->CreateStream(this, params); | 208 ipc_->CreateStream(this, audio_parameters_); |
| 188 } else { | 209 } else { |
| 189 RequestDeviceAuthorizationOnIOThread(); | 210 RequestDeviceAuthorizationOnIOThread(); |
| 190 start_on_authorized_ = true; | 211 start_on_authorized_ = true; |
| 191 } | 212 } |
| 192 break; | 213 break; |
| 193 | 214 |
| 194 case AUTHORIZING: | 215 case AUTHORIZING: |
| 195 start_on_authorized_ = true; | 216 start_on_authorized_ = true; |
| 196 break; | 217 break; |
| 197 | 218 |
| 198 case AUTHORIZED: | 219 case AUTHORIZED: |
| 199 state_ = CREATING_STREAM; | 220 state_ = CREATING_STREAM; |
| 200 ipc_->CreateStream(this, params); | 221 ipc_->CreateStream(this, audio_parameters_); |
| 201 start_on_authorized_ = false; | 222 start_on_authorized_ = false; |
| 202 break; | 223 break; |
| 203 | 224 |
| 204 case CREATING_STREAM: | 225 case CREATING_STREAM: |
| 205 case PAUSED: | 226 case PAUSED: |
| 206 case PLAYING: | 227 case PLAYING: |
| 207 NOTREACHED(); | 228 NOTREACHED(); |
| 208 break; | 229 break; |
| 209 } | 230 } |
| 210 } | 231 } |
| 211 | 232 |
| 212 void AudioOutputDevice::PlayOnIOThread() { | 233 void AudioOutputDevice::PlayOnIOThread() { |
| 213 DCHECK(task_runner()->BelongsToCurrentThread()); | 234 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 214 if (state_ == PAUSED) { | 235 if (state_ == PAUSED) { |
| 215 TRACE_EVENT_ASYNC_BEGIN0( | 236 TRACE_EVENT_ASYNC_BEGIN0("audio", "StartingPlayback", |
| 216 "audio", "StartingPlayback", audio_callback_.get()); | 237 audio_thread_callback_.get()); |
| 217 ipc_->PlayStream(); | 238 ipc_->PlayStream(); |
| 218 state_ = PLAYING; | 239 state_ = PLAYING; |
| 219 play_on_start_ = false; | 240 play_on_start_ = false; |
| 220 } else { | 241 } else { |
| 221 play_on_start_ = true; | 242 play_on_start_ = true; |
| 222 } | 243 } |
| 223 } | 244 } |
| 224 | 245 |
| 225 void AudioOutputDevice::PauseOnIOThread() { | 246 void AudioOutputDevice::PauseOnIOThread() { |
| 226 DCHECK(task_runner()->BelongsToCurrentThread()); | 247 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 227 if (state_ == PLAYING) { | 248 if (state_ == PLAYING) { |
| 228 TRACE_EVENT_ASYNC_END0( | 249 TRACE_EVENT_ASYNC_END0("audio", "StartingPlayback", |
| 229 "audio", "StartingPlayback", audio_callback_.get()); | 250 audio_thread_callback_.get()); |
| 230 ipc_->PauseStream(); | 251 ipc_->PauseStream(); |
| 231 state_ = PAUSED; | 252 state_ = PAUSED; |
| 232 } | 253 } |
| 233 play_on_start_ = false; | 254 play_on_start_ = false; |
| 234 } | 255 } |
| 235 | 256 |
| 236 void AudioOutputDevice::ShutDownOnIOThread() { | 257 void AudioOutputDevice::StopOnIOThread() { |
| 237 DCHECK(task_runner()->BelongsToCurrentThread()); | 258 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 238 | 259 |
| 239 // Close the stream, if we haven't already. | 260 // Close the stream, if we haven't already. After CloseStream(), we are |
| 261 // guaranteed to not get IPC calls for any outstanding operations. This holds | |
| 262 // also if we restart, i.e. request authorization and create a new stream, | |
| 263 // before the previous request and/or create stream finished. | |
| 240 if (state_ >= AUTHORIZING) { | 264 if (state_ >= AUTHORIZING) { |
| 241 ipc_->CloseStream(); | 265 ipc_->CloseStream(); |
| 242 state_ = IDLE; | 266 state_ = IDLE; |
| 243 } | 267 } |
| 244 start_on_authorized_ = false; | 268 start_on_authorized_ = false; |
| 245 | 269 |
| 270 // We need to re-authorize after stopping. | |
| 271 did_receive_auth_.Reset(); | |
| 272 | |
| 246 // Destoy the timer on the thread it's used on. | 273 // Destoy the timer on the thread it's used on. |
| 247 auth_timeout_action_.reset(); | 274 auth_timeout_action_.reset(); |
| 248 | 275 |
| 249 // We can run into an issue where ShutDownOnIOThread is called right after | 276 // 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 | 277 // OnStreamCreated is called in cases where Start/Stop are called before we |
| 251 // get the OnStreamCreated callback. To handle that corner case, we call | 278 // get the OnStreamCreated callback. To handle that corner case, we call |
| 252 // Stop(). In most cases, the thread will already be stopped. | 279 // Stop(). In most cases, the thread will already be stopped. |
| 253 // | 280 // |
| 254 // Another situation is when the IO thread goes away before Stop() is called | 281 // 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 | 282 // 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. | 283 // and can't rely on the main thread existing either. |
| 257 base::AutoLock auto_lock_(audio_thread_lock_); | 284 base::AutoLock auto_lock_(lock_); |
|
DaleCurtis
2016/06/16 16:39:14
I'm worried this can deadlock now. You're waiting
Henrik Grunell
2016/06/16 20:23:00
This lock is never grabbed on the audio render thr
DaleCurtis
2016/06/16 22:00:53
No, I had forgotten we're not using your old patch
| |
| 258 base::ThreadRestrictions::ScopedAllowIO allow_io; | 285 base::ThreadRestrictions::ScopedAllowIO allow_io; |
| 259 audio_thread_.Stop(NULL); | 286 audio_thread_.Stop(NULL); |
| 260 audio_callback_.reset(); | 287 audio_thread_callback_.reset(); |
| 261 stopping_hack_ = false; | 288 stopping_hack_ = false; |
| 289 play_on_start_ = true; | |
| 262 } | 290 } |
| 263 | 291 |
| 264 void AudioOutputDevice::SetVolumeOnIOThread(double volume) { | 292 void AudioOutputDevice::SetVolumeOnIOThread(double volume) { |
| 265 DCHECK(task_runner()->BelongsToCurrentThread()); | 293 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 266 if (state_ >= CREATING_STREAM) | 294 if (state_ >= CREATING_STREAM) |
| 267 ipc_->SetVolume(volume); | 295 ipc_->SetVolume(volume); |
| 268 } | 296 } |
| 269 | 297 |
| 270 void AudioOutputDevice::OnStateChanged(AudioOutputIPCDelegateState state) { | 298 void AudioOutputDevice::OnStateChanged(AudioOutputIPCDelegateState state) { |
| 271 DCHECK(task_runner()->BelongsToCurrentThread()); | 299 DCHECK(task_runner()->BelongsToCurrentThread()); |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 282 break; | 310 break; |
| 283 case AUDIO_OUTPUT_IPC_DELEGATE_STATE_ERROR: | 311 case AUDIO_OUTPUT_IPC_DELEGATE_STATE_ERROR: |
| 284 DLOG(WARNING) << "AudioOutputDevice::OnStateChanged(ERROR)"; | 312 DLOG(WARNING) << "AudioOutputDevice::OnStateChanged(ERROR)"; |
| 285 // Don't dereference the callback object if the audio thread | 313 // Don't dereference the callback object if the audio thread |
| 286 // is stopped or stopping. That could mean that the callback | 314 // is stopped or stopping. That could mean that the callback |
| 287 // object has been deleted. | 315 // object has been deleted. |
| 288 // TODO(tommi): Add an explicit contract for clearing the callback | 316 // TODO(tommi): Add an explicit contract for clearing the callback |
| 289 // object. Possibly require calling Initialize again or provide | 317 // object. Possibly require calling Initialize again or provide |
| 290 // a callback object via Start() and clear it in Stop(). | 318 // a callback object via Start() and clear it in Stop(). |
| 291 if (!audio_thread_.IsStopped()) | 319 if (!audio_thread_.IsStopped()) |
| 292 callback_->OnRenderError(); | 320 ReportRenderError(); |
| 293 break; | 321 break; |
| 294 default: | 322 default: |
| 295 NOTREACHED(); | 323 NOTREACHED(); |
| 296 break; | 324 break; |
| 297 } | 325 } |
| 298 } | 326 } |
| 299 | 327 |
| 300 void AudioOutputDevice::OnDeviceAuthorized( | 328 void AudioOutputDevice::OnDeviceAuthorized( |
| 301 OutputDeviceStatus device_status, | 329 OutputDeviceStatus device_status, |
| 302 const media::AudioParameters& output_params, | 330 const media::AudioParameters& output_params, |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 338 matched_device_id_.empty()); | 366 matched_device_id_.empty()); |
| 339 matched_device_id_ = matched_device_id; | 367 matched_device_id_ = matched_device_id; |
| 340 | 368 |
| 341 DVLOG(1) << "AudioOutputDevice authorized, session_id: " << session_id_ | 369 DVLOG(1) << "AudioOutputDevice authorized, session_id: " << session_id_ |
| 342 << ", device_id: " << device_id_ | 370 << ", device_id: " << device_id_ |
| 343 << ", matched_device_id: " << matched_device_id_; | 371 << ", matched_device_id: " << matched_device_id_; |
| 344 | 372 |
| 345 did_receive_auth_.Signal(); | 373 did_receive_auth_.Signal(); |
| 346 } | 374 } |
| 347 if (start_on_authorized_) | 375 if (start_on_authorized_) |
| 348 CreateStreamOnIOThread(audio_parameters_); | 376 CreateStreamOnIOThread(); |
| 349 } else { | 377 } else { |
| 350 // Closing IPC forces a Signal(), so no clients are locked waiting | 378 // Closing IPC forces a Signal(), so no clients are locked waiting |
| 351 // indefinitely after this method returns. | 379 // indefinitely after this method returns. |
| 352 ipc_->CloseStream(); | 380 ipc_->CloseStream(); |
| 353 OnIPCClosed(); | 381 OnIPCClosed(); |
| 354 if (callback_) | 382 ReportRenderError(); |
| 355 callback_->OnRenderError(); | |
| 356 } | 383 } |
| 357 } | 384 } |
| 358 | 385 |
| 359 void AudioOutputDevice::OnStreamCreated( | 386 void AudioOutputDevice::OnStreamCreated( |
| 360 base::SharedMemoryHandle handle, | 387 base::SharedMemoryHandle handle, |
| 361 base::SyncSocket::Handle socket_handle, | 388 base::SyncSocket::Handle socket_handle, |
| 362 int length) { | 389 int length) { |
| 363 DCHECK(task_runner()->BelongsToCurrentThread()); | 390 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 364 DCHECK(base::SharedMemory::IsHandleValid(handle)); | 391 DCHECK(base::SharedMemory::IsHandleValid(handle)); |
| 365 #if defined(OS_WIN) | 392 #if defined(OS_WIN) |
| 366 DCHECK(socket_handle); | 393 DCHECK(socket_handle); |
| 367 #else | 394 #else |
| 368 DCHECK_GE(socket_handle, 0); | 395 DCHECK_GE(socket_handle, 0); |
| 369 #endif | 396 #endif |
| 370 DCHECK_GT(length, 0); | 397 DCHECK_GT(length, 0); |
| 371 | 398 |
| 372 if (state_ != CREATING_STREAM) | 399 if (state_ != CREATING_STREAM) |
| 373 return; | 400 return; |
| 374 | 401 |
| 375 // We can receive OnStreamCreated() on the IO thread after the client has | 402 // We can receive OnStreamCreated() on the IO thread after the client has |
| 376 // called Stop() but before ShutDownOnIOThread() is processed. In such a | 403 // called Stop() but before ShutDownOnIOThread() is processed. In such a |
| 377 // situation |callback_| might point to freed memory. Instead of starting | 404 // situation |callback_| might point to freed memory. Instead of starting |
| 378 // |audio_thread_| do nothing and wait for ShutDownOnIOThread() to get called. | 405 // |audio_thread_| do nothing and wait for ShutDownOnIOThread() to get called. |
| 379 // | 406 // |
| 380 // TODO(scherkus): The real fix is to have sane ownership semantics. The fact | 407 // TODO(grunell): AudioRendererSink should be non-refcounted and |
| 381 // that |callback_| (which should own and outlive this object!) can point to | 408 // AudioOutputDevice should internally use WeakPtr to handle teardown and |
| 382 // freed memory is a mess. AudioRendererSink should be non-refcounted so that | 409 // thread hopping. See http://crbug.com/151051 for details. |
| 383 // owners (WebRtcAudioDeviceImpl, AudioRendererImpl, etc...) can Stop() and | 410 // 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 | 411 // thread, so we'll eliminate thread concurrency issues here then. See |
| 385 // to handle teardown and thread hopping. See http://crbug.com/151051 for | 412 // http://crbug.com/606707. |
| 386 // details. | 413 |
| 387 { | 414 { |
| 388 base::AutoLock auto_lock(audio_thread_lock_); | 415 base::AutoLock auto_lock(lock_); |
| 389 if (stopping_hack_) | 416 if (stopping_hack_) |
| 390 return; | 417 return; |
| 391 | 418 |
| 392 DCHECK(audio_thread_.IsStopped()); | 419 DCHECK(audio_thread_.IsStopped()); |
| 393 audio_callback_.reset(new AudioOutputDevice::AudioThreadCallback( | 420 audio_thread_callback_.reset(new AudioOutputDevice::AudioThreadCallback( |
| 394 audio_parameters_, handle, length, callback_)); | 421 audio_parameters_, handle, length, render_callback_)); |
| 395 audio_thread_.Start(audio_callback_.get(), socket_handle, | 422 audio_thread_.Start(audio_thread_callback_.get(), socket_handle, |
| 396 "AudioOutputDevice", true); | 423 "AudioOutputDevice", true); |
| 397 state_ = PAUSED; | 424 state_ = PAUSED; |
| 398 | 425 |
| 399 // We handle the case where Play() and/or Pause() may have been called | 426 // We handle the case where Play() and/or Pause() may have been called |
| 400 // multiple times before OnStreamCreated() gets called. | 427 // multiple times before OnStreamCreated() gets called. |
| 401 if (play_on_start_) | 428 if (play_on_start_) |
| 402 PlayOnIOThread(); | 429 PlayOnIOThread(); |
| 403 } | 430 } |
| 404 } | 431 } |
| 405 | 432 |
| 406 void AudioOutputDevice::OnIPCClosed() { | 433 void AudioOutputDevice::OnIPCClosed() { |
| 407 DCHECK(task_runner()->BelongsToCurrentThread()); | 434 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 408 state_ = IPC_CLOSED; | 435 state_ = IPC_CLOSED; |
| 409 ipc_.reset(); | 436 ipc_.reset(); |
| 410 | 437 |
| 411 // Signal to unblock any blocked threads waiting for parameters | 438 // Signal to unblock any blocked threads waiting for parameters |
| 412 did_receive_auth_.Signal(); | 439 did_receive_auth_.Signal(); |
| 413 } | 440 } |
| 414 | 441 |
| 415 void AudioOutputDevice::WillDestroyCurrentMessageLoop() { | 442 void AudioOutputDevice::WillDestroyCurrentMessageLoop() { |
| 416 LOG(ERROR) << "IO loop going away before the audio device has been stopped"; | 443 LOG(ERROR) << "IO loop going away before the audio device has been stopped"; |
| 417 ShutDownOnIOThread(); | 444 StopOnIOThread(); |
| 418 } | 445 } |
| 419 | 446 |
| 420 // AudioOutputDevice::AudioThreadCallback | 447 // AudioOutputDevice::AudioThreadCallback |
| 421 | 448 |
| 422 AudioOutputDevice::AudioThreadCallback::AudioThreadCallback( | 449 AudioOutputDevice::AudioThreadCallback::AudioThreadCallback( |
| 423 const AudioParameters& audio_parameters, | 450 const AudioParameters& audio_parameters, |
| 424 base::SharedMemoryHandle memory, | 451 base::SharedMemoryHandle memory, |
| 425 int memory_length, | 452 int memory_length, |
| 426 AudioRendererSink::RenderCallback* render_callback) | 453 AudioRendererSink::RenderCallback* render_callback) |
| 427 : AudioDeviceThread::Callback(audio_parameters, memory, memory_length, 1), | 454 : AudioDeviceThread::Callback(audio_parameters, memory, memory_length, 1), |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 471 | 498 |
| 472 // Update the audio-delay measurement, inform about the number of skipped | 499 // Update the audio-delay measurement, inform about the number of skipped |
| 473 // frames, and ask client to render audio. Since |output_bus_| is wrapping | 500 // 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 | 501 // the shared memory the Render() call is writing directly into the shared |
| 475 // memory. | 502 // memory. |
| 476 render_callback_->Render(output_bus_.get(), std::round(frames_delayed), | 503 render_callback_->Render(output_bus_.get(), std::round(frames_delayed), |
| 477 frames_skipped); | 504 frames_skipped); |
| 478 } | 505 } |
| 479 | 506 |
| 480 } // namespace media | 507 } // namespace media |
| OLD | NEW |