| 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/basictypes.h" |
| 7 #include "base/debug/trace_event.h" | 8 #include "base/debug/trace_event.h" |
| 8 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
| 9 #include "base/threading/thread_restrictions.h" | 10 #include "base/threading/thread_restrictions.h" |
| 10 #include "base/time.h" | 11 #include "base/time.h" |
| 11 #include "media/audio/audio_output_controller.h" | 12 #include "media/audio/audio_output_controller.h" |
| 12 #include "media/audio/audio_util.h" | 13 #include "media/audio/audio_util.h" |
| 13 #include "media/audio/shared_memory_util.h" | 14 #include "media/audio/shared_memory_util.h" |
| 14 #include "media/base/limits.h" | 15 #include "media/base/limits.h" |
| 15 | 16 |
| 16 namespace media { | 17 namespace media { |
| (...skipping 16 matching lines...) Expand all Loading... |
| 33 virtual void Process(int pending_data) OVERRIDE; | 34 virtual void Process(int pending_data) OVERRIDE; |
| 34 | 35 |
| 35 private: | 36 private: |
| 36 AudioRendererSink::RenderCallback* render_callback_; | 37 AudioRendererSink::RenderCallback* render_callback_; |
| 37 scoped_ptr<AudioBus> input_bus_; | 38 scoped_ptr<AudioBus> input_bus_; |
| 38 scoped_ptr<AudioBus> output_bus_; | 39 scoped_ptr<AudioBus> output_bus_; |
| 39 DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback); | 40 DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback); |
| 40 }; | 41 }; |
| 41 | 42 |
| 42 AudioOutputDevice::AudioOutputDevice( | 43 AudioOutputDevice::AudioOutputDevice( |
| 43 AudioOutputIPC* ipc, | 44 scoped_ptr<AudioOutputIPC> ipc, |
| 44 const scoped_refptr<base::MessageLoopProxy>& io_loop) | 45 const scoped_refptr<base::MessageLoopProxy>& io_loop) |
| 45 : ScopedLoopObserver(io_loop), | 46 : ScopedLoopObserver(io_loop), |
| 46 callback_(NULL), | 47 callback_(NULL), |
| 47 ipc_(ipc), | 48 ipc_(ipc.Pass()), |
| 48 stream_id_(0), | |
| 49 state_(IDLE), | 49 state_(IDLE), |
| 50 play_on_start_(true), | 50 play_on_start_(true), |
| 51 stopping_hack_(false) { | 51 stopping_hack_(false) { |
| 52 CHECK(ipc_); | 52 CHECK(ipc_); |
| 53 |
| 54 // The correctness of the code depends on the relative values assigned in the |
| 55 // State enum. |
| 56 COMPILE_ASSERT(IPC_CLOSED < IDLE, invalid_enum_value_assignment_0); |
| 57 COMPILE_ASSERT(IDLE < CREATING_STREAM, invalid_enum_value_assignment_1); |
| 58 COMPILE_ASSERT(CREATING_STREAM < PAUSED, invalid_enum_value_assignment_2); |
| 59 COMPILE_ASSERT(PAUSED < PLAYING, invalid_enum_value_assignment_3); |
| 53 } | 60 } |
| 54 | 61 |
| 55 void AudioOutputDevice::Initialize(const AudioParameters& params, | 62 void AudioOutputDevice::Initialize(const AudioParameters& params, |
| 56 RenderCallback* callback) { | 63 RenderCallback* callback) { |
| 57 DCHECK(!callback_) << "Calling Initialize() twice?"; | 64 DCHECK(!callback_) << "Calling Initialize() twice?"; |
| 58 DCHECK(params.IsValid()); | 65 DCHECK(params.IsValid()); |
| 59 audio_parameters_ = params; | 66 audio_parameters_ = params; |
| 60 callback_ = callback; | 67 callback_ = callback; |
| 61 } | 68 } |
| 62 | 69 |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 102 base::Bind(&AudioOutputDevice::SetVolumeOnIOThread, this, volume))) { | 109 base::Bind(&AudioOutputDevice::SetVolumeOnIOThread, this, volume))) { |
| 103 return false; | 110 return false; |
| 104 } | 111 } |
| 105 | 112 |
| 106 return true; | 113 return true; |
| 107 } | 114 } |
| 108 | 115 |
| 109 void AudioOutputDevice::CreateStreamOnIOThread(const AudioParameters& params) { | 116 void AudioOutputDevice::CreateStreamOnIOThread(const AudioParameters& params) { |
| 110 DCHECK(message_loop()->BelongsToCurrentThread()); | 117 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 111 if (state_ == IDLE) { | 118 if (state_ == IDLE) { |
| 112 stream_id_ = ipc_->AddDelegate(this); | |
| 113 state_ = CREATING_STREAM; | 119 state_ = CREATING_STREAM; |
| 114 ipc_->CreateStream(stream_id_, params); | 120 ipc_->CreateStream(this, params); |
| 115 } | 121 } |
| 116 } | 122 } |
| 117 | 123 |
| 118 void AudioOutputDevice::PlayOnIOThread() { | 124 void AudioOutputDevice::PlayOnIOThread() { |
| 119 DCHECK(message_loop()->BelongsToCurrentThread()); | 125 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 120 if (state_ == PAUSED) { | 126 if (state_ == PAUSED) { |
| 121 ipc_->PlayStream(stream_id_); | 127 ipc_->PlayStream(); |
| 122 state_ = PLAYING; | 128 state_ = PLAYING; |
| 123 play_on_start_ = false; | 129 play_on_start_ = false; |
| 124 } else { | 130 } else { |
| 125 play_on_start_ = true; | 131 play_on_start_ = true; |
| 126 } | 132 } |
| 127 } | 133 } |
| 128 | 134 |
| 129 void AudioOutputDevice::PauseOnIOThread(bool flush) { | 135 void AudioOutputDevice::PauseOnIOThread(bool flush) { |
| 130 DCHECK(message_loop()->BelongsToCurrentThread()); | 136 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 131 if (state_ == PLAYING) { | 137 if (state_ == PLAYING) { |
| 132 ipc_->PauseStream(stream_id_); | 138 ipc_->PauseStream(); |
| 133 if (flush) | 139 if (flush) |
| 134 ipc_->FlushStream(stream_id_); | 140 ipc_->FlushStream(); |
| 135 state_ = PAUSED; | 141 state_ = PAUSED; |
| 136 } else { | 142 } else { |
| 137 // Note that |flush| isn't relevant here since this is the case where | 143 // Note that |flush| isn't relevant here since this is the case where |
| 138 // the stream is first starting. | 144 // the stream is first starting. |
| 139 } | 145 } |
| 140 play_on_start_ = false; | 146 play_on_start_ = false; |
| 141 } | 147 } |
| 142 | 148 |
| 143 void AudioOutputDevice::ShutDownOnIOThread() { | 149 void AudioOutputDevice::ShutDownOnIOThread() { |
| 144 DCHECK(message_loop()->BelongsToCurrentThread()); | 150 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 145 | 151 |
| 146 // Make sure we don't call shutdown more than once. | 152 // Close the stream, if we haven't already. |
| 147 if (state_ >= CREATING_STREAM) { | 153 if (state_ >= CREATING_STREAM) { |
| 148 ipc_->CloseStream(stream_id_); | 154 ipc_->CloseStream(); |
| 149 ipc_->RemoveDelegate(stream_id_); | |
| 150 state_ = IDLE; | 155 state_ = IDLE; |
| 151 stream_id_ = 0; | |
| 152 } | 156 } |
| 153 | 157 |
| 154 // We can run into an issue where ShutDownOnIOThread is called right after | 158 // We can run into an issue where ShutDownOnIOThread is called right after |
| 155 // OnStreamCreated is called in cases where Start/Stop are called before we | 159 // OnStreamCreated is called in cases where Start/Stop are called before we |
| 156 // get the OnStreamCreated callback. To handle that corner case, we call | 160 // get the OnStreamCreated callback. To handle that corner case, we call |
| 157 // Stop(). In most cases, the thread will already be stopped. | 161 // Stop(). In most cases, the thread will already be stopped. |
| 158 // | 162 // |
| 159 // Another situation is when the IO thread goes away before Stop() is called | 163 // Another situation is when the IO thread goes away before Stop() is called |
| 160 // in which case, we cannot use the message loop to close the thread handle | 164 // in which case, we cannot use the message loop to close the thread handle |
| 161 // and can't rely on the main thread existing either. | 165 // and can't rely on the main thread existing either. |
| 162 base::AutoLock auto_lock_(audio_thread_lock_); | 166 base::AutoLock auto_lock_(audio_thread_lock_); |
| 163 base::ThreadRestrictions::ScopedAllowIO allow_io; | 167 base::ThreadRestrictions::ScopedAllowIO allow_io; |
| 164 audio_thread_.Stop(NULL); | 168 audio_thread_.Stop(NULL); |
| 165 audio_callback_.reset(); | 169 audio_callback_.reset(); |
| 166 stopping_hack_ = false; | 170 stopping_hack_ = false; |
| 167 } | 171 } |
| 168 | 172 |
| 169 void AudioOutputDevice::SetVolumeOnIOThread(double volume) { | 173 void AudioOutputDevice::SetVolumeOnIOThread(double volume) { |
| 170 DCHECK(message_loop()->BelongsToCurrentThread()); | 174 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 171 if (state_ >= CREATING_STREAM) | 175 if (state_ >= CREATING_STREAM) |
| 172 ipc_->SetVolume(stream_id_, volume); | 176 ipc_->SetVolume(volume); |
| 173 } | 177 } |
| 174 | 178 |
| 175 void AudioOutputDevice::OnStateChanged(AudioOutputIPCDelegate::State state) { | 179 void AudioOutputDevice::OnStateChanged(AudioOutputIPCDelegate::State state) { |
| 176 DCHECK(message_loop()->BelongsToCurrentThread()); | 180 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 177 | 181 |
| 178 // Do nothing if the stream has been closed. | 182 // Do nothing if the stream has been closed. |
| 179 if (state_ < CREATING_STREAM) | 183 if (state_ < CREATING_STREAM) |
| 180 return; | 184 return; |
| 181 | 185 |
| 182 if (state == AudioOutputIPCDelegate::kError) { | 186 // TODO(miu): Clean-up inconsistent and incomplete handling here. |
| 183 DLOG(WARNING) << "AudioOutputDevice::OnStateChanged(kError)"; | 187 // http://crbug.com/180640 |
| 184 // Don't dereference the callback object if the audio thread | 188 switch (state) { |
| 185 // is stopped or stopping. That could mean that the callback | 189 case AudioOutputIPCDelegate::kPlaying: |
| 186 // object has been deleted. | 190 case AudioOutputIPCDelegate::kPaused: |
| 187 // TODO(tommi): Add an explicit contract for clearing the callback | 191 NOTIMPLEMENTED(); |
| 188 // object. Possibly require calling Initialize again or provide | 192 break; |
| 189 // a callback object via Start() and clear it in Stop(). | 193 case AudioOutputIPCDelegate::kError: |
| 190 if (!audio_thread_.IsStopped()) | 194 DLOG(WARNING) << "AudioOutputDevice::OnStateChanged(kError)"; |
| 191 callback_->OnRenderError(); | 195 // Don't dereference the callback object if the audio thread |
| 196 // is stopped or stopping. That could mean that the callback |
| 197 // object has been deleted. |
| 198 // TODO(tommi): Add an explicit contract for clearing the callback |
| 199 // object. Possibly require calling Initialize again or provide |
| 200 // a callback object via Start() and clear it in Stop(). |
| 201 if (!audio_thread_.IsStopped()) |
| 202 callback_->OnRenderError(); |
| 203 break; |
| 204 default: |
| 205 NOTREACHED(); |
| 206 break; |
| 192 } | 207 } |
| 193 } | 208 } |
| 194 | 209 |
| 195 void AudioOutputDevice::OnStreamCreated( | 210 void AudioOutputDevice::OnStreamCreated( |
| 196 base::SharedMemoryHandle handle, | 211 base::SharedMemoryHandle handle, |
| 197 base::SyncSocket::Handle socket_handle, | 212 base::SyncSocket::Handle socket_handle, |
| 198 int length) { | 213 int length) { |
| 199 DCHECK(message_loop()->BelongsToCurrentThread()); | 214 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 200 #if defined(OS_WIN) | 215 #if defined(OS_WIN) |
| 201 DCHECK(handle); | 216 DCHECK(handle); |
| 202 DCHECK(socket_handle); | 217 DCHECK(socket_handle); |
| 203 #else | 218 #else |
| 204 DCHECK_GE(handle.fd, 0); | 219 DCHECK_GE(handle.fd, 0); |
| 205 DCHECK_GE(socket_handle, 0); | 220 DCHECK_GE(socket_handle, 0); |
| 206 #endif | 221 #endif |
| 222 DCHECK_GT(length, 0); |
| 207 | 223 |
| 208 if (state_ != CREATING_STREAM) | 224 if (state_ != CREATING_STREAM) |
| 209 return; | 225 return; |
| 210 | 226 |
| 211 // We can receive OnStreamCreated() on the IO thread after the client has | 227 // We can receive OnStreamCreated() on the IO thread after the client has |
| 212 // called Stop() but before ShutDownOnIOThread() is processed. In such a | 228 // called Stop() but before ShutDownOnIOThread() is processed. In such a |
| 213 // situation |callback_| might point to freed memory. Instead of starting | 229 // situation |callback_| might point to freed memory. Instead of starting |
| 214 // |audio_thread_| do nothing and wait for ShutDownOnIOThread() to get called. | 230 // |audio_thread_| do nothing and wait for ShutDownOnIOThread() to get called. |
| 215 // | 231 // |
| 216 // TODO(scherkus): The real fix is to have sane ownership semantics. The fact | 232 // TODO(scherkus): The real fix is to have sane ownership semantics. The fact |
| (...skipping 16 matching lines...) Expand all Loading... |
| 233 | 249 |
| 234 // We handle the case where Play() and/or Pause() may have been called | 250 // We handle the case where Play() and/or Pause() may have been called |
| 235 // multiple times before OnStreamCreated() gets called. | 251 // multiple times before OnStreamCreated() gets called. |
| 236 if (play_on_start_) | 252 if (play_on_start_) |
| 237 PlayOnIOThread(); | 253 PlayOnIOThread(); |
| 238 } | 254 } |
| 239 | 255 |
| 240 void AudioOutputDevice::OnIPCClosed() { | 256 void AudioOutputDevice::OnIPCClosed() { |
| 241 DCHECK(message_loop()->BelongsToCurrentThread()); | 257 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 242 state_ = IPC_CLOSED; | 258 state_ = IPC_CLOSED; |
| 243 ipc_ = NULL; | 259 ipc_.reset(); |
| 244 } | 260 } |
| 245 | 261 |
| 246 void AudioOutputDevice::WillDestroyCurrentMessageLoop() { | 262 void AudioOutputDevice::WillDestroyCurrentMessageLoop() { |
| 247 LOG(ERROR) << "IO loop going away before the audio device has been stopped"; | 263 LOG(ERROR) << "IO loop going away before the audio device has been stopped"; |
| 248 ShutDownOnIOThread(); | 264 ShutDownOnIOThread(); |
| 249 } | 265 } |
| 250 | 266 |
| 251 // AudioOutputDevice::AudioThreadCallback | 267 // AudioOutputDevice::AudioThreadCallback |
| 252 | 268 |
| 253 AudioOutputDevice::AudioThreadCallback::AudioThreadCallback( | 269 AudioOutputDevice::AudioThreadCallback::AudioThreadCallback( |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 323 // TODO(dalecurtis): Technically this is not always correct. Due to channel | 339 // TODO(dalecurtis): Technically this is not always correct. Due to channel |
| 324 // padding for alignment, there may be more data available than this. We're | 340 // padding for alignment, there may be more data available than this. We're |
| 325 // relying on AudioSyncReader::Read() to parse this with that in mind. Rename | 341 // relying on AudioSyncReader::Read() to parse this with that in mind. Rename |
| 326 // these methods to Set/GetActualFrameCount(). | 342 // these methods to Set/GetActualFrameCount(). |
| 327 SetActualDataSizeInBytes( | 343 SetActualDataSizeInBytes( |
| 328 &shared_memory_, memory_length_, | 344 &shared_memory_, memory_length_, |
| 329 num_frames * sizeof(*output_bus_->channel(0)) * output_bus_->channels()); | 345 num_frames * sizeof(*output_bus_->channel(0)) * output_bus_->channels()); |
| 330 } | 346 } |
| 331 | 347 |
| 332 } // namespace media. | 348 } // namespace media. |
| OLD | NEW |