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