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