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 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, |
| 83 DCHECK(!callback_) << "Calling Initialize() twice?"; | 82 AudioRendererSink::RenderCallback* callback) { |
| 84 DCHECK(params.IsValid()); | 83 DCHECK(params.IsValid()); |
| 85 audio_parameters_ = params; | 84 |
| 86 callback_ = callback; | 85 // We set the callback synchronously here, so that it's serialized with |
| 86 // clearing it in Stop(). Note that after this it is possible to get stale | |
|
o1ka
2016/05/03 15:47:32
Could you explain here how exactly it can happen?
Henrik Grunell
2016/05/04 09:05:11
Done.
| |
| 87 // callbacks from previous runs (i.e. from before Stop() was called earlier). | |
|
o1ka
2016/05/03 15:47:32
Taking into account current usage model, we are no
Henrik Grunell
2016/05/04 09:05:11
Yes, that's right, but we allow it. I suspect you
| |
| 88 // | |
| 89 // Using the IO thread at all in this class will go away soon when we move IPC | |
| 90 // to use Mojo, and we'll then get rid of the thread hopping and locks. See | |
| 91 // http://crbug.com/606707. | |
| 92 { | |
| 93 base::AutoLock auto_lock(callback_lock_); | |
| 94 DCHECK(!callback_) << "Calling Initialize() twice?"; | |
| 95 callback_ = callback; | |
| 96 } | |
| 97 | |
| 98 task_runner()->PostTask( | |
| 99 FROM_HERE, | |
| 100 base::Bind(&AudioOutputDevice::InitializeOnIOThread, this, params)); | |
| 87 } | 101 } |
| 88 | 102 |
| 89 AudioOutputDevice::~AudioOutputDevice() { | 103 AudioOutputDevice::~AudioOutputDevice() { |
| 90 // The current design requires that the user calls Stop() before deleting | 104 // The current design requires that the user calls Stop() before deleting |
| 91 // this class. | 105 // this class to ensure that stream and authorization data is cleaned up on |
| 92 DCHECK(audio_thread_.IsStopped()); | 106 // the browser side, and that we don't get more IPC calls from AudioOutputIPC. |
| 107 // With all posted tasks on IO thread done and no more IPC calls, it's safe to | |
| 108 // access |audio_thread_| here on any thread. | |
| 109 DCHECK(!callback_); | |
| 110 | |
| 111 audio_thread_.Stop(base::MessageLoop::current()); | |
| 93 } | 112 } |
| 94 | 113 |
| 95 void AudioOutputDevice::RequestDeviceAuthorization() { | 114 void AudioOutputDevice::RequestDeviceAuthorization() { |
| 96 task_runner()->PostTask( | 115 task_runner()->PostTask( |
| 97 FROM_HERE, | 116 FROM_HERE, |
| 98 base::Bind(&AudioOutputDevice::RequestDeviceAuthorizationOnIOThread, | 117 base::Bind(&AudioOutputDevice::RequestDeviceAuthorizationOnIOThread, |
| 99 this)); | 118 this)); |
| 100 } | 119 } |
| 101 | 120 |
| 102 void AudioOutputDevice::Start() { | 121 void AudioOutputDevice::Start() { |
| 103 DCHECK(callback_) << "Initialize hasn't been called"; | 122 #if !defined(NDEBUG) |
| 104 task_runner()->PostTask(FROM_HERE, | 123 { |
| 105 base::Bind(&AudioOutputDevice::CreateStreamOnIOThread, this, | 124 base::AutoLock auto_lock(callback_lock_); |
| 106 audio_parameters_)); | 125 DCHECK(callback_) << "Initialize hasn't been called"; |
| 126 } | |
| 127 #endif | |
| 128 | |
| 129 task_runner()->PostTask( | |
| 130 FROM_HERE, base::Bind(&AudioOutputDevice::CreateStreamOnIOThread, this)); | |
| 107 } | 131 } |
| 108 | 132 |
| 109 void AudioOutputDevice::Stop() { | 133 void AudioOutputDevice::Stop() { |
| 134 // Clear the callback synchronously to ensure no callbacks after returning. | |
|
o1ka
2016/05/03 15:47:32
This is nice.
Henrik Grunell
2016/05/04 09:05:12
Thanks.
| |
| 110 { | 135 { |
| 111 base::AutoLock auto_lock(audio_thread_lock_); | 136 base::AutoLock auto_lock(callback_lock_); |
| 112 audio_thread_.Stop(base::MessageLoop::current()); | 137 DCHECK(callback_) << "Initialize hasn't been called"; |
| 113 stopping_hack_ = true; | 138 callback_ = nullptr; |
| 114 } | 139 } |
| 115 | 140 |
| 116 task_runner()->PostTask(FROM_HERE, | 141 task_runner()->PostTask(FROM_HERE, |
| 117 base::Bind(&AudioOutputDevice::ShutDownOnIOThread, this)); | 142 base::Bind(&AudioOutputDevice::StopOnIOThread, this)); |
| 118 } | 143 } |
| 119 | 144 |
| 120 void AudioOutputDevice::Play() { | 145 void AudioOutputDevice::Play() { |
| 121 task_runner()->PostTask(FROM_HERE, | 146 task_runner()->PostTask(FROM_HERE, |
| 122 base::Bind(&AudioOutputDevice::PlayOnIOThread, this)); | 147 base::Bind(&AudioOutputDevice::PlayOnIOThread, this)); |
| 123 } | 148 } |
| 124 | 149 |
| 125 void AudioOutputDevice::Pause() { | 150 void AudioOutputDevice::Pause() { |
| 126 task_runner()->PostTask(FROM_HERE, | 151 task_runner()->PostTask(FROM_HERE, |
| 127 base::Bind(&AudioOutputDevice::PauseOnIOThread, this)); | 152 base::Bind(&AudioOutputDevice::PauseOnIOThread, this)); |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 142 OutputDeviceInfo AudioOutputDevice::GetOutputDeviceInfo() { | 167 OutputDeviceInfo AudioOutputDevice::GetOutputDeviceInfo() { |
| 143 CHECK(!task_runner()->BelongsToCurrentThread()); | 168 CHECK(!task_runner()->BelongsToCurrentThread()); |
| 144 did_receive_auth_.Wait(); | 169 did_receive_auth_.Wait(); |
| 145 return OutputDeviceInfo(AudioDeviceDescription::UseSessionIdToSelectDevice( | 170 return OutputDeviceInfo(AudioDeviceDescription::UseSessionIdToSelectDevice( |
| 146 session_id_, device_id_) | 171 session_id_, device_id_) |
| 147 ? matched_device_id_ | 172 ? matched_device_id_ |
| 148 : device_id_, | 173 : device_id_, |
| 149 device_status_, output_params_); | 174 device_status_, output_params_); |
| 150 } | 175 } |
| 151 | 176 |
| 177 int AudioOutputDevice::Render(media::AudioBus* audio_bus, | |
| 178 uint32_t frames_delayed, | |
| 179 uint32_t frames_skipped) { | |
| 180 base::AutoLock auto_lock(callback_lock_); | |
| 181 return callback_ | |
| 182 ? callback_->Render(audio_bus, frames_delayed, frames_skipped) | |
| 183 : 0; | |
|
o1ka
2016/05/03 15:47:32
Shouldn't we zero-out the bus?
Henrik Grunell
2016/05/04 09:05:12
That would not be correct, we should return the nu
| |
| 184 } | |
| 185 | |
| 186 void AudioOutputDevice::OnRenderError() { | |
| 187 base::AutoLock auto_lock(callback_lock_); | |
| 188 if (callback_) | |
| 189 callback_->OnRenderError(); | |
| 190 } | |
| 191 | |
| 152 void AudioOutputDevice::RequestDeviceAuthorizationOnIOThread() { | 192 void AudioOutputDevice::RequestDeviceAuthorizationOnIOThread() { |
| 153 DCHECK(task_runner()->BelongsToCurrentThread()); | 193 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 154 DCHECK_EQ(state_, IDLE); | 194 DCHECK_EQ(state_, IDLE); |
| 155 state_ = AUTHORIZING; | 195 state_ = AUTHORIZING; |
| 196 | |
| 197 // TODO(grunell): Store authorization so that we don't have to authorize | |
| 198 // again when restarting. http://crbug.com/608619. | |
| 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(const AudioParameters& params) { |
| 161 DCHECK(task_runner()->BelongsToCurrentThread()); | 204 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 205 audio_parameters_ = params; | |
| 206 } | |
| 207 | |
| 208 void AudioOutputDevice::CreateStreamOnIOThread() { | |
| 209 DCHECK(task_runner()->BelongsToCurrentThread()); | |
| 210 | |
| 162 switch (state_) { | 211 switch (state_) { |
| 163 case IPC_CLOSED: | 212 case IPC_CLOSED: { |
| 213 base::AutoLock auto_lock(callback_lock_); | |
|
o1ka
2016/05/03 15:47:32
It's just AOD::OnRenderError()
Henrik Grunell
2016/05/04 09:05:12
Of course, thanks.
| |
| 164 if (callback_) | 214 if (callback_) |
| 165 callback_->OnRenderError(); | 215 callback_->OnRenderError(); |
| 166 break; | 216 } break; |
| 167 | 217 |
| 168 case IDLE: | 218 case IDLE: |
| 169 if (did_receive_auth_.IsSignaled() && device_id_.empty() && | 219 if (did_receive_auth_.IsSignaled() && device_id_.empty() && |
| 170 security_origin_.unique()) { | 220 security_origin_.unique()) { |
| 171 state_ = CREATING_STREAM; | 221 state_ = CREATING_STREAM; |
| 172 ipc_->CreateStream(this, params); | 222 ipc_->CreateStream(this, audio_parameters_); |
| 173 } else { | 223 } else { |
| 174 RequestDeviceAuthorizationOnIOThread(); | 224 RequestDeviceAuthorizationOnIOThread(); |
| 175 start_on_authorized_ = true; | 225 start_on_authorized_ = true; |
| 176 } | 226 } |
| 177 break; | 227 break; |
| 178 | 228 |
| 179 case AUTHORIZING: | 229 case AUTHORIZING: |
| 180 start_on_authorized_ = true; | 230 start_on_authorized_ = true; |
| 181 break; | 231 break; |
| 182 | 232 |
| 183 case AUTHORIZED: | 233 case AUTHORIZED: |
| 184 state_ = CREATING_STREAM; | 234 state_ = CREATING_STREAM; |
| 185 ipc_->CreateStream(this, params); | 235 ipc_->CreateStream(this, audio_parameters_); |
| 186 start_on_authorized_ = false; | 236 start_on_authorized_ = false; |
| 187 break; | 237 break; |
| 188 | 238 |
| 189 case CREATING_STREAM: | 239 case CREATING_STREAM: |
| 190 case PAUSED: | 240 case PAUSED: |
| 191 case PLAYING: | 241 case PLAYING: |
| 192 NOTREACHED(); | 242 NOTREACHED(); |
| 193 break; | 243 break; |
| 194 } | 244 } |
| 195 } | 245 } |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 211 DCHECK(task_runner()->BelongsToCurrentThread()); | 261 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 212 if (state_ == PLAYING) { | 262 if (state_ == PLAYING) { |
| 213 TRACE_EVENT_ASYNC_END0( | 263 TRACE_EVENT_ASYNC_END0( |
| 214 "audio", "StartingPlayback", audio_callback_.get()); | 264 "audio", "StartingPlayback", audio_callback_.get()); |
| 215 ipc_->PauseStream(); | 265 ipc_->PauseStream(); |
| 216 state_ = PAUSED; | 266 state_ = PAUSED; |
| 217 } | 267 } |
| 218 play_on_start_ = false; | 268 play_on_start_ = false; |
| 219 } | 269 } |
| 220 | 270 |
| 221 void AudioOutputDevice::ShutDownOnIOThread() { | 271 void AudioOutputDevice::StopOnIOThread() { |
| 222 DCHECK(task_runner()->BelongsToCurrentThread()); | 272 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 223 | 273 |
| 224 // Close the stream, if we haven't already. | 274 // Close the stream, if we haven't already. After CloseStream(), we are |
| 275 // guaranteed to not get IPC calls for any outstanding operations. This holds | |
| 276 // also if we restart, i.e. request authorization and create a new stream, | |
| 277 // before the previous request and/or create stream finished. | |
| 225 if (state_ >= AUTHORIZING) { | 278 if (state_ >= AUTHORIZING) { |
| 226 ipc_->CloseStream(); | 279 ipc_->CloseStream(); |
| 227 state_ = IDLE; | 280 state_ = IDLE; |
| 228 } | 281 } |
| 229 start_on_authorized_ = false; | 282 start_on_authorized_ = false; |
| 230 | 283 audio_thread_.Pause(); |
| 231 // We can run into an issue where ShutDownOnIOThread is called right after | |
| 232 // OnStreamCreated is called in cases where Start/Stop are called before we | |
| 233 // get the OnStreamCreated callback. To handle that corner case, we call | |
| 234 // Stop(). In most cases, the thread will already be stopped. | |
| 235 // | |
| 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_); | |
| 240 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
| 241 audio_thread_.Stop(NULL); | |
| 242 audio_callback_.reset(); | 284 audio_callback_.reset(); |
| 243 stopping_hack_ = false; | 285 did_receive_auth_.Reset(); |
|
o1ka
2016/05/03 15:47:32
Add a comment why it's needed?
Henrik Grunell
2016/05/04 09:05:11
Done.
| |
| 244 } | 286 } |
| 245 | 287 |
| 246 void AudioOutputDevice::SetVolumeOnIOThread(double volume) { | 288 void AudioOutputDevice::SetVolumeOnIOThread(double volume) { |
| 247 DCHECK(task_runner()->BelongsToCurrentThread()); | 289 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 248 if (state_ >= CREATING_STREAM) | 290 if (state_ >= CREATING_STREAM) |
| 249 ipc_->SetVolume(volume); | 291 ipc_->SetVolume(volume); |
| 250 } | 292 } |
| 251 | 293 |
| 252 void AudioOutputDevice::OnStateChanged(AudioOutputIPCDelegateState state) { | 294 void AudioOutputDevice::OnStateChanged(AudioOutputIPCDelegateState state) { |
| 253 DCHECK(task_runner()->BelongsToCurrentThread()); | 295 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 254 | 296 |
| 255 // Do nothing if the stream has been closed. | 297 // Do nothing if the stream has been closed. |
| 256 if (state_ < CREATING_STREAM) | 298 if (state_ < CREATING_STREAM) |
| 257 return; | 299 return; |
| 258 | 300 |
| 259 // TODO(miu): Clean-up inconsistent and incomplete handling here. | 301 // TODO(miu): Clean-up inconsistent and incomplete handling here. |
| 260 // http://crbug.com/180640 | 302 // http://crbug.com/180640 |
| 261 switch (state) { | 303 switch (state) { |
| 262 case AUDIO_OUTPUT_IPC_DELEGATE_STATE_PLAYING: | 304 case AUDIO_OUTPUT_IPC_DELEGATE_STATE_PLAYING: |
| 263 case AUDIO_OUTPUT_IPC_DELEGATE_STATE_PAUSED: | 305 case AUDIO_OUTPUT_IPC_DELEGATE_STATE_PAUSED: |
| 264 break; | 306 break; |
| 265 case AUDIO_OUTPUT_IPC_DELEGATE_STATE_ERROR: | 307 case AUDIO_OUTPUT_IPC_DELEGATE_STATE_ERROR: |
| 266 DLOG(WARNING) << "AudioOutputDevice::OnStateChanged(ERROR)"; | 308 DLOG(WARNING) << "AudioOutputDevice::OnStateChanged(ERROR)"; |
| 267 // Don't dereference the callback object if the audio thread | 309 { |
| 268 // is stopped or stopping. That could mean that the callback | 310 base::AutoLock auto_lock(callback_lock_); |
|
o1ka
2016/05/03 15:47:32
AOD::OnRenderError()
Henrik Grunell
2016/05/04 09:05:12
Done.
| |
| 269 // object has been deleted. | 311 if (callback_) |
| 270 // TODO(tommi): Add an explicit contract for clearing the callback | 312 callback_->OnRenderError(); |
| 271 // object. Possibly require calling Initialize again or provide | 313 } |
| 272 // a callback object via Start() and clear it in Stop(). | |
| 273 if (!audio_thread_.IsStopped()) | |
| 274 callback_->OnRenderError(); | |
| 275 break; | 314 break; |
| 276 default: | 315 default: |
| 277 NOTREACHED(); | 316 NOTREACHED(); |
| 278 break; | 317 break; |
| 279 } | 318 } |
| 280 } | 319 } |
| 281 | 320 |
| 282 void AudioOutputDevice::OnDeviceAuthorized( | 321 void AudioOutputDevice::OnDeviceAuthorized( |
| 283 OutputDeviceStatus device_status, | 322 OutputDeviceStatus device_status, |
| 284 const media::AudioParameters& output_params, | 323 const media::AudioParameters& output_params, |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 310 matched_device_id_.empty()); | 349 matched_device_id_.empty()); |
| 311 matched_device_id_ = matched_device_id; | 350 matched_device_id_ = matched_device_id; |
| 312 | 351 |
| 313 DVLOG(1) << "AudioOutputDevice authorized, session_id: " << session_id_ | 352 DVLOG(1) << "AudioOutputDevice authorized, session_id: " << session_id_ |
| 314 << ", device_id: " << device_id_ | 353 << ", device_id: " << device_id_ |
| 315 << ", matched_device_id: " << matched_device_id_; | 354 << ", matched_device_id: " << matched_device_id_; |
| 316 | 355 |
| 317 did_receive_auth_.Signal(); | 356 did_receive_auth_.Signal(); |
| 318 } | 357 } |
| 319 if (start_on_authorized_) | 358 if (start_on_authorized_) |
| 320 CreateStreamOnIOThread(audio_parameters_); | 359 CreateStreamOnIOThread(); |
| 321 } else { | 360 } else { |
| 322 // Closing IPC forces a Signal(), so no clients are locked waiting | 361 // Closing IPC forces a Signal(), so no clients are locked waiting |
| 323 // indefinitely after this method returns. | 362 // indefinitely after this method returns. |
| 324 ipc_->CloseStream(); | 363 ipc_->CloseStream(); |
| 325 OnIPCClosed(); | 364 OnIPCClosed(); |
| 365 base::AutoLock auto_lock(callback_lock_); | |
|
o1ka
2016/05/03 15:47:32
ditto
Henrik Grunell
2016/05/04 09:05:11
Done.
| |
| 326 if (callback_) | 366 if (callback_) |
| 327 callback_->OnRenderError(); | 367 callback_->OnRenderError(); |
| 328 } | 368 } |
| 329 } | 369 } |
| 330 | 370 |
| 331 void AudioOutputDevice::OnStreamCreated( | 371 void AudioOutputDevice::OnStreamCreated( |
| 332 base::SharedMemoryHandle handle, | 372 base::SharedMemoryHandle handle, |
| 333 base::SyncSocket::Handle socket_handle, | 373 base::SyncSocket::Handle socket_handle, |
| 334 int length) { | 374 int length) { |
| 335 DCHECK(task_runner()->BelongsToCurrentThread()); | 375 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 336 DCHECK(base::SharedMemory::IsHandleValid(handle)); | 376 DCHECK(base::SharedMemory::IsHandleValid(handle)); |
| 337 #if defined(OS_WIN) | 377 #if defined(OS_WIN) |
| 338 DCHECK(socket_handle); | 378 DCHECK(socket_handle); |
| 339 #else | 379 #else |
| 340 DCHECK_GE(socket_handle, 0); | 380 DCHECK_GE(socket_handle, 0); |
| 341 #endif | 381 #endif |
| 342 DCHECK_GT(length, 0); | 382 DCHECK_GT(length, 0); |
| 343 | 383 |
| 344 if (state_ != CREATING_STREAM) | 384 if (state_ != CREATING_STREAM) |
| 345 return; | 385 return; |
| 346 | 386 |
| 347 // We can receive OnStreamCreated() on the IO thread after the client has | 387 // We can receive OnStreamCreated() on the IO thread after the client has |
| 348 // called Stop() but before ShutDownOnIOThread() is processed. In such a | 388 // called Stop() but before StopOnIOThread() is processed. This is OK, since |
| 349 // situation |callback_| might point to freed memory. Instead of starting | 389 // we pass callbacks though |this| and clear |callback_| in Stop(). |
| 350 // |audio_thread_| do nothing and wait for ShutDownOnIOThread() to get called. | |
| 351 // | 390 // |
| 352 // TODO(scherkus): The real fix is to have sane ownership semantics. The fact | 391 // TODO(grunell): AudioRendererSink should be non-refcounted and |
| 353 // that |callback_| (which should own and outlive this object!) can point to | 392 // AudioOutputDevice should internally use WeakPtr to handle teardown and |
| 354 // freed memory is a mess. AudioRendererSink should be non-refcounted so that | 393 // thread hopping. See http://crbug.com/151051 for details. |
| 355 // owners (WebRtcAudioDeviceImpl, AudioRendererImpl, etc...) can Stop() and | 394 // Note however that switching the IPC to Mojo will remove usage of the IO |
| 356 // delete as they see fit. AudioOutputDevice should internally use WeakPtr | 395 // thread, so we'll eliminate thread concurrency issues here then. See |
| 357 // to handle teardown and thread hopping. See http://crbug.com/151051 for | 396 // http://crbug.com/606707. |
| 358 // details. | |
| 359 { | |
| 360 base::AutoLock auto_lock(audio_thread_lock_); | |
| 361 if (stopping_hack_) | |
| 362 return; | |
| 363 | 397 |
| 364 DCHECK(audio_thread_.IsStopped()); | 398 DCHECK(audio_thread_.IsStopped()); |
| 365 audio_callback_.reset(new AudioOutputDevice::AudioThreadCallback( | 399 audio_callback_.reset(new AudioOutputDevice::AudioThreadCallback( |
| 366 audio_parameters_, handle, length, callback_)); | 400 audio_parameters_, handle, length, this)); |
| 367 audio_thread_.Start(audio_callback_.get(), socket_handle, | |
| 368 "AudioOutputDevice", true); | |
| 369 state_ = PAUSED; | |
| 370 | 401 |
| 371 // We handle the case where Play() and/or Pause() may have been called | 402 // If the audio thread hasn't been started before, start it. |
| 372 // multiple times before OnStreamCreated() gets called. | 403 if (audio_thread_.IsStopped()) { |
| 373 if (play_on_start_) | 404 audio_thread_.Start("AudioOutputDevice", true); |
| 374 PlayOnIOThread(); | |
| 375 } | 405 } |
| 406 audio_thread_.Resume(audio_callback_.get(), socket_handle); | |
| 407 | |
| 408 state_ = PAUSED; | |
| 409 | |
| 410 // We handle the case where Play() and/or Pause() may have been called | |
| 411 // multiple times before OnStreamCreated() gets called. | |
| 412 if (play_on_start_) | |
| 413 PlayOnIOThread(); | |
| 376 } | 414 } |
| 377 | 415 |
| 378 void AudioOutputDevice::OnIPCClosed() { | 416 void AudioOutputDevice::OnIPCClosed() { |
| 379 DCHECK(task_runner()->BelongsToCurrentThread()); | 417 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 380 state_ = IPC_CLOSED; | 418 state_ = IPC_CLOSED; |
| 381 ipc_.reset(); | 419 ipc_.reset(); |
| 382 | 420 |
| 383 // Signal to unblock any blocked threads waiting for parameters | 421 // Signal to unblock any blocked threads waiting for parameters |
| 384 did_receive_auth_.Signal(); | 422 did_receive_auth_.Signal(); |
| 385 } | 423 } |
| 386 | 424 |
| 387 void AudioOutputDevice::WillDestroyCurrentMessageLoop() { | 425 void AudioOutputDevice::WillDestroyCurrentMessageLoop() { |
| 388 LOG(ERROR) << "IO loop going away before the audio device has been stopped"; | 426 LOG(ERROR) << "IO loop going away before the audio device has been stopped"; |
| 389 ShutDownOnIOThread(); | 427 StopOnIOThread(); |
| 390 } | 428 } |
| 391 | 429 |
| 392 // AudioOutputDevice::AudioThreadCallback | 430 // AudioOutputDevice::AudioThreadCallback |
| 393 | 431 |
| 394 AudioOutputDevice::AudioThreadCallback::AudioThreadCallback( | 432 AudioOutputDevice::AudioThreadCallback::AudioThreadCallback( |
| 395 const AudioParameters& audio_parameters, | 433 const AudioParameters& audio_parameters, |
| 396 base::SharedMemoryHandle memory, | 434 base::SharedMemoryHandle memory, |
| 397 int memory_length, | 435 int memory_length, |
| 398 AudioRendererSink::RenderCallback* render_callback) | 436 AudioRendererSink::RenderCallback* render_callback) |
| 399 : AudioDeviceThread::Callback(audio_parameters, memory, memory_length, 1), | 437 : AudioDeviceThread::Callback(audio_parameters, memory, memory_length, 1), |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 443 | 481 |
| 444 // Update the audio-delay measurement, inform about the number of skipped | 482 // Update the audio-delay measurement, inform about the number of skipped |
| 445 // frames, and ask client to render audio. Since |output_bus_| is wrapping | 483 // 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 | 484 // the shared memory the Render() call is writing directly into the shared |
| 447 // memory. | 485 // memory. |
| 448 render_callback_->Render(output_bus_.get(), std::round(frames_delayed), | 486 render_callback_->Render(output_bus_.get(), std::round(frames_delayed), |
| 449 frames_skipped); | 487 frames_skipped); |
| 450 } | 488 } |
| 451 | 489 |
| 452 } // namespace media | 490 } // namespace media |
| OLD | NEW |