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