| 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 "base/debug/trace_event.h" | 7 #include "base/debug/trace_event.h" |
| 8 #include "base/message_loop.h" | 8 #include "base/message_loop.h" |
| 9 #include "base/threading/thread_restrictions.h" | 9 #include "base/threading/thread_restrictions.h" |
| 10 #include "base/time.h" | 10 #include "base/time.h" |
| (...skipping 28 matching lines...) Expand all Loading... |
| 39 scoped_ptr<AudioBus> output_bus_; | 39 scoped_ptr<AudioBus> output_bus_; |
| 40 DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback); | 40 DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback); |
| 41 }; | 41 }; |
| 42 | 42 |
| 43 AudioOutputDevice::AudioOutputDevice( | 43 AudioOutputDevice::AudioOutputDevice( |
| 44 AudioOutputIPC* ipc, | 44 AudioOutputIPC* ipc, |
| 45 const scoped_refptr<base::MessageLoopProxy>& io_loop) | 45 const scoped_refptr<base::MessageLoopProxy>& io_loop) |
| 46 : ScopedLoopObserver(io_loop), | 46 : ScopedLoopObserver(io_loop), |
| 47 input_channels_(0), | 47 input_channels_(0), |
| 48 callback_(NULL), | 48 callback_(NULL), |
| 49 ipc_(ipc), | 49 stream_id_(-1), |
| 50 stream_id_(0), | 50 state_(kIpcClosed), |
| 51 play_on_start_(true), | 51 play_on_start_(true), |
| 52 is_started_(false), | |
| 53 stopping_hack_(false) { | 52 stopping_hack_(false) { |
| 54 CHECK(ipc_); | 53 CHECK(ipc); |
| 54 message_loop()->PostTask(FROM_HERE, |
| 55 base::Bind(&AudioOutputDevice::RegisterDelegateOnIOThread, this, ipc)); |
| 56 } |
| 57 |
| 58 void AudioOutputDevice::RegisterDelegateOnIOThread(AudioOutputIPC* ipc) { |
| 59 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 60 DCHECK_EQ(kIpcClosed, state_); |
| 61 ipc_ = ipc->AsWeakPtr(); |
| 62 stream_id_ = ipc_ ? ipc_->AddDelegate(this) : 0; |
| 63 if (stream_id_ > 0) |
| 64 state_ = kIdle; |
| 65 } |
| 66 |
| 67 int AudioOutputDevice::stream_id() const { |
| 68 DCHECK_LE(0, stream_id_) << "Call to stream_id() before being initialized."; |
| 69 return stream_id_; |
| 55 } | 70 } |
| 56 | 71 |
| 57 void AudioOutputDevice::Initialize(const AudioParameters& params, | 72 void AudioOutputDevice::Initialize(const AudioParameters& params, |
| 58 RenderCallback* callback) { | 73 RenderCallback* callback) { |
| 59 CHECK_EQ(0, stream_id_) << | 74 DCHECK(!callback_) << "Calling Initialize() twice?"; |
| 60 "AudioOutputDevice::Initialize() must be called before Start()"; | |
| 61 | |
| 62 CHECK(!callback_); // Calling Initialize() twice? | |
| 63 | |
| 64 audio_parameters_ = params; | 75 audio_parameters_ = params; |
| 65 callback_ = callback; | 76 callback_ = callback; |
| 66 } | 77 } |
| 67 | 78 |
| 68 void AudioOutputDevice::InitializeIO(const AudioParameters& params, | 79 void AudioOutputDevice::InitializeIO(const AudioParameters& params, |
| 69 int input_channels, | 80 int input_channels, |
| 70 RenderCallback* callback) { | 81 RenderCallback* callback) { |
| 71 DCHECK_GE(input_channels, 0); | 82 DCHECK_GE(input_channels, 0); |
| 72 DCHECK_LT(input_channels, limits::kMaxChannels); | 83 DCHECK_LT(input_channels, limits::kMaxChannels); |
| 73 input_channels_ = input_channels; | 84 input_channels_ = input_channels; |
| 74 Initialize(params, callback); | 85 Initialize(params, callback); |
| 75 } | 86 } |
| 76 | 87 |
| 77 AudioOutputDevice::~AudioOutputDevice() { | 88 AudioOutputDevice::~AudioOutputDevice() { |
| 78 // The current design requires that the user calls Stop() before deleting | 89 message_loop()->PostTask(FROM_HERE, |
| 79 // this class. | 90 base::Bind(&AudioOutputIPC::RemoveDelegate, ipc_, stream_id_)); |
| 80 CHECK_EQ(0, stream_id_); | |
| 81 } | 91 } |
| 82 | 92 |
| 83 void AudioOutputDevice::Start() { | 93 void AudioOutputDevice::Start() { |
| 84 DCHECK(callback_) << "Initialize hasn't been called"; | 94 DCHECK(callback_) << "Initialize hasn't been called"; |
| 85 message_loop()->PostTask(FROM_HERE, | 95 message_loop()->PostTask(FROM_HERE, |
| 86 base::Bind(&AudioOutputDevice::CreateStreamOnIOThread, this, | 96 base::Bind(&AudioOutputDevice::CreateStreamOnIOThread, this, |
| 87 audio_parameters_, input_channels_)); | 97 audio_parameters_, input_channels_)); |
| 88 } | 98 } |
| 89 | 99 |
| 90 void AudioOutputDevice::Stop() { | 100 void AudioOutputDevice::Stop() { |
| (...skipping 25 matching lines...) Expand all Loading... |
| 116 base::Bind(&AudioOutputDevice::SetVolumeOnIOThread, this, volume))) { | 126 base::Bind(&AudioOutputDevice::SetVolumeOnIOThread, this, volume))) { |
| 117 return false; | 127 return false; |
| 118 } | 128 } |
| 119 | 129 |
| 120 return true; | 130 return true; |
| 121 } | 131 } |
| 122 | 132 |
| 123 void AudioOutputDevice::CreateStreamOnIOThread(const AudioParameters& params, | 133 void AudioOutputDevice::CreateStreamOnIOThread(const AudioParameters& params, |
| 124 int input_channels) { | 134 int input_channels) { |
| 125 DCHECK(message_loop()->BelongsToCurrentThread()); | 135 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 126 // Make sure we don't create the stream more than once. | 136 if (state_ == kIdle && ipc_) { |
| 127 DCHECK_EQ(0, stream_id_); | 137 state_ = kCreatingStream; |
| 128 if (stream_id_) | 138 ipc_->CreateStream(stream_id_, params, input_channels); |
| 129 return; | 139 } |
| 130 | |
| 131 stream_id_ = ipc_->AddDelegate(this); | |
| 132 ipc_->CreateStream(stream_id_, params, input_channels); | |
| 133 } | 140 } |
| 134 | 141 |
| 135 void AudioOutputDevice::PlayOnIOThread() { | 142 void AudioOutputDevice::PlayOnIOThread() { |
| 136 DCHECK(message_loop()->BelongsToCurrentThread()); | 143 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 137 if (stream_id_ && is_started_) | 144 if (state_ == kPaused && ipc_) { |
| 138 ipc_->PlayStream(stream_id_); | 145 ipc_->PlayStream(stream_id_); |
| 139 else | 146 state_ = kPlaying; |
| 147 play_on_start_ = false; |
| 148 } else { |
| 140 play_on_start_ = true; | 149 play_on_start_ = true; |
| 150 } |
| 141 } | 151 } |
| 142 | 152 |
| 143 void AudioOutputDevice::PauseOnIOThread(bool flush) { | 153 void AudioOutputDevice::PauseOnIOThread(bool flush) { |
| 144 DCHECK(message_loop()->BelongsToCurrentThread()); | 154 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 145 if (stream_id_ && is_started_) { | 155 if (state_ == kPlaying) { |
| 146 ipc_->PauseStream(stream_id_); | 156 if (ipc_) { |
| 147 if (flush) | 157 ipc_->PauseStream(stream_id_); |
| 148 ipc_->FlushStream(stream_id_); | 158 if (flush) |
| 159 ipc_->FlushStream(stream_id_); |
| 160 } |
| 161 state_ = kPaused; |
| 149 } else { | 162 } else { |
| 150 // Note that |flush| isn't relevant here since this is the case where | 163 // Note that |flush| isn't relevant here since this is the case where |
| 151 // the stream is first starting. | 164 // the stream is first starting. |
| 152 play_on_start_ = false; | |
| 153 } | 165 } |
| 166 play_on_start_ = false; |
| 154 } | 167 } |
| 155 | 168 |
| 156 void AudioOutputDevice::ShutDownOnIOThread() { | 169 void AudioOutputDevice::ShutDownOnIOThread() { |
| 157 DCHECK(message_loop()->BelongsToCurrentThread()); | 170 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 158 | 171 |
| 159 // Make sure we don't call shutdown more than once. | 172 // Make sure we don't call shutdown more than once. |
| 160 if (stream_id_) { | 173 if (state_ >= kCreatingStream) { |
| 161 is_started_ = false; | 174 if (ipc_) |
| 162 | |
| 163 if (ipc_) { | |
| 164 ipc_->CloseStream(stream_id_); | 175 ipc_->CloseStream(stream_id_); |
| 165 ipc_->RemoveDelegate(stream_id_); | 176 state_ = kIdle; |
| 166 } | |
| 167 | |
| 168 stream_id_ = 0; | |
| 169 } | 177 } |
| 170 | 178 |
| 171 // We can run into an issue where ShutDownOnIOThread is called right after | 179 // We can run into an issue where ShutDownOnIOThread is called right after |
| 172 // OnStreamCreated is called in cases where Start/Stop are called before we | 180 // OnStreamCreated is called in cases where Start/Stop are called before we |
| 173 // get the OnStreamCreated callback. To handle that corner case, we call | 181 // get the OnStreamCreated callback. To handle that corner case, we call |
| 174 // Stop(). In most cases, the thread will already be stopped. | 182 // Stop(). In most cases, the thread will already be stopped. |
| 175 // | 183 // |
| 176 // Another situation is when the IO thread goes away before Stop() is called | 184 // Another situation is when the IO thread goes away before Stop() is called |
| 177 // in which case, we cannot use the message loop to close the thread handle | 185 // in which case, we cannot use the message loop to close the thread handle |
| 178 // and can't rely on the main thread existing either. | 186 // and can't rely on the main thread existing either. |
| 179 base::AutoLock auto_lock_(audio_thread_lock_); | 187 base::AutoLock auto_lock_(audio_thread_lock_); |
| 180 base::ThreadRestrictions::ScopedAllowIO allow_io; | 188 base::ThreadRestrictions::ScopedAllowIO allow_io; |
| 181 audio_thread_.Stop(NULL); | 189 audio_thread_.Stop(NULL); |
| 182 audio_callback_.reset(); | 190 audio_callback_.reset(); |
| 183 stopping_hack_ = false; | 191 stopping_hack_ = false; |
| 184 } | 192 } |
| 185 | 193 |
| 186 void AudioOutputDevice::SetVolumeOnIOThread(double volume) { | 194 void AudioOutputDevice::SetVolumeOnIOThread(double volume) { |
| 187 DCHECK(message_loop()->BelongsToCurrentThread()); | 195 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 188 if (stream_id_) | 196 if (state_ >= kPaused && ipc_) |
| 189 ipc_->SetVolume(stream_id_, volume); | 197 ipc_->SetVolume(stream_id_, volume); |
| 190 } | 198 } |
| 191 | 199 |
| 192 void AudioOutputDevice::OnStateChanged(AudioOutputIPCDelegate::State state) { | 200 void AudioOutputDevice::OnStateChanged(AudioOutputIPCDelegate::State state) { |
| 193 DCHECK(message_loop()->BelongsToCurrentThread()); | 201 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 194 | 202 |
| 195 // Do nothing if the stream has been closed. | 203 // Do nothing if the stream has been closed. |
| 196 if (!stream_id_) | 204 if (state_ < kCreatingStream) |
| 197 return; | 205 return; |
| 198 | 206 |
| 199 if (state == AudioOutputIPCDelegate::kError) { | 207 if (state == AudioOutputIPCDelegate::kError) { |
| 200 DLOG(WARNING) << "AudioOutputDevice::OnStateChanged(kError)"; | 208 DLOG(WARNING) << "AudioOutputDevice::OnStateChanged(kError)"; |
| 201 // Don't dereference the callback object if the audio thread | 209 // Don't dereference the callback object if the audio thread |
| 202 // is stopped or stopping. That could mean that the callback | 210 // is stopped or stopping. That could mean that the callback |
| 203 // object has been deleted. | 211 // object has been deleted. |
| 204 // TODO(tommi): Add an explicit contract for clearing the callback | 212 // TODO(tommi): Add an explicit contract for clearing the callback |
| 205 // object. Possibly require calling Initialize again or provide | 213 // object. Possibly require calling Initialize again or provide |
| 206 // a callback object via Start() and clear it in Stop(). | 214 // a callback object via Start() and clear it in Stop(). |
| 207 if (!audio_thread_.IsStopped()) | 215 if (!audio_thread_.IsStopped()) |
| 208 callback_->OnRenderError(); | 216 callback_->OnRenderError(); |
| 209 } | 217 } |
| 210 } | 218 } |
| 211 | 219 |
| 212 void AudioOutputDevice::OnStreamCreated( | 220 void AudioOutputDevice::OnStreamCreated( |
| 213 base::SharedMemoryHandle handle, | 221 base::SharedMemoryHandle handle, |
| 214 base::SyncSocket::Handle socket_handle, | 222 base::SyncSocket::Handle socket_handle, |
| 215 int length) { | 223 int length) { |
| 216 DCHECK(message_loop()->BelongsToCurrentThread()); | 224 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 217 #if defined(OS_WIN) | 225 #if defined(OS_WIN) |
| 218 DCHECK(handle); | 226 DCHECK(handle); |
| 219 DCHECK(socket_handle); | 227 DCHECK(socket_handle); |
| 220 #else | 228 #else |
| 221 DCHECK_GE(handle.fd, 0); | 229 DCHECK_GE(handle.fd, 0); |
| 222 DCHECK_GE(socket_handle, 0); | 230 DCHECK_GE(socket_handle, 0); |
| 223 #endif | 231 #endif |
| 224 | 232 |
| 225 // We should only get this callback if stream_id_ is valid. If it is not, | 233 if (state_ != kCreatingStream) |
| 226 // the IPC layer should have closed the shared memory and socket handles | 234 return; |
| 227 // for us and not invoked the callback. The basic assertion is that when | |
| 228 // stream_id_ is 0 the AudioOutputDevice instance is not registered as a | |
| 229 // delegate and hence it should not receive callbacks. | |
| 230 DCHECK(stream_id_); | |
| 231 | 235 |
| 232 // We can receive OnStreamCreated() on the IO thread after the client has | 236 // We can receive OnStreamCreated() on the IO thread after the client has |
| 233 // called Stop() but before ShutDownOnIOThread() is processed. In such a | 237 // called Stop() but before ShutDownOnIOThread() is processed. In such a |
| 234 // situation |callback_| might point to freed memory. Instead of starting | 238 // situation |callback_| might point to freed memory. Instead of starting |
| 235 // |audio_thread_| do nothing and wait for ShutDownOnIOThread() to get called. | 239 // |audio_thread_| do nothing and wait for ShutDownOnIOThread() to get called. |
| 236 // | 240 // |
| 237 // TODO(scherkus): The real fix is to have sane ownership semantics. The fact | 241 // TODO(scherkus): The real fix is to have sane ownership semantics. The fact |
| 238 // that |callback_| (which should own and outlive this object!) can point to | 242 // that |callback_| (which should own and outlive this object!) can point to |
| 239 // freed memory is a mess. AudioRendererSink should be non-refcounted so that | 243 // freed memory is a mess. AudioRendererSink should be non-refcounted so that |
| 240 // owners (WebRtcAudioDeviceImpl, AudioRendererImpl, etc...) can Stop() and | 244 // owners (WebRtcAudioDeviceImpl, AudioRendererImpl, etc...) can Stop() and |
| 241 // delete as they see fit. AudioOutputDevice should internally use WeakPtr | 245 // delete as they see fit. AudioOutputDevice should internally use WeakPtr |
| 242 // to handle teardown and thread hopping. See http://crbug.com/151051 for | 246 // to handle teardown and thread hopping. See http://crbug.com/151051 for |
| 243 // details. | 247 // details. |
| 244 base::AutoLock auto_lock(audio_thread_lock_); | 248 base::AutoLock auto_lock(audio_thread_lock_); |
| 245 if (stopping_hack_) | 249 if (stopping_hack_) |
| 246 return; | 250 return; |
| 247 | 251 |
| 248 DCHECK(audio_thread_.IsStopped()); | 252 DCHECK(audio_thread_.IsStopped()); |
| 249 audio_callback_.reset(new AudioOutputDevice::AudioThreadCallback( | 253 audio_callback_.reset(new AudioOutputDevice::AudioThreadCallback( |
| 250 audio_parameters_, input_channels_, handle, length, callback_)); | 254 audio_parameters_, input_channels_, handle, length, callback_)); |
| 251 audio_thread_.Start(audio_callback_.get(), socket_handle, | 255 audio_thread_.Start(audio_callback_.get(), socket_handle, |
| 252 "AudioOutputDevice"); | 256 "AudioOutputDevice"); |
| 257 state_ = kPaused; |
| 253 | 258 |
| 254 // We handle the case where Play() and/or Pause() may have been called | 259 // We handle the case where Play() and/or Pause() may have been called |
| 255 // multiple times before OnStreamCreated() gets called. | 260 // multiple times before OnStreamCreated() gets called. |
| 256 is_started_ = true; | |
| 257 if (play_on_start_) | 261 if (play_on_start_) |
| 258 PlayOnIOThread(); | 262 PlayOnIOThread(); |
| 259 } | 263 } |
| 260 | 264 |
| 261 void AudioOutputDevice::OnIPCClosed() { | 265 void AudioOutputDevice::OnIPCClosed() { |
| 262 ipc_ = NULL; | 266 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 267 state_ = kIpcClosed; |
| 268 ipc_.reset(); |
| 263 } | 269 } |
| 264 | 270 |
| 265 void AudioOutputDevice::WillDestroyCurrentMessageLoop() { | 271 void AudioOutputDevice::WillDestroyCurrentMessageLoop() { |
| 266 LOG(ERROR) << "IO loop going away before the audio device has been stopped"; | 272 LOG(ERROR) << "IO loop going away before the audio device has been stopped"; |
| 267 ShutDownOnIOThread(); | 273 ShutDownOnIOThread(); |
| 268 } | 274 } |
| 269 | 275 |
| 270 // AudioOutputDevice::AudioThreadCallback | 276 // AudioOutputDevice::AudioThreadCallback |
| 271 | 277 |
| 272 AudioOutputDevice::AudioThreadCallback::AudioThreadCallback( | 278 AudioOutputDevice::AudioThreadCallback::AudioThreadCallback( |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 342 // TODO(dalecurtis): Technically this is not always correct. Due to channel | 348 // TODO(dalecurtis): Technically this is not always correct. Due to channel |
| 343 // padding for alignment, there may be more data available than this. We're | 349 // padding for alignment, there may be more data available than this. We're |
| 344 // relying on AudioSyncReader::Read() to parse this with that in mind. Rename | 350 // relying on AudioSyncReader::Read() to parse this with that in mind. Rename |
| 345 // these methods to Set/GetActualFrameCount(). | 351 // these methods to Set/GetActualFrameCount(). |
| 346 SetActualDataSizeInBytes( | 352 SetActualDataSizeInBytes( |
| 347 &shared_memory_, memory_length_, | 353 &shared_memory_, memory_length_, |
| 348 num_frames * sizeof(*output_bus_->channel(0)) * output_bus_->channels()); | 354 num_frames * sizeof(*output_bus_->channel(0)) * output_bus_->channels()); |
| 349 } | 355 } |
| 350 | 356 |
| 351 } // namespace media. | 357 } // namespace media. |
| OLD | NEW |