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() { | 135 void AudioOutputDevice::PauseOnIOThread() { |
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 state_ = PAUSED; | 139 state_ = PAUSED; |
134 } | 140 } |
135 play_on_start_ = false; | 141 play_on_start_ = false; |
136 } | 142 } |
137 | 143 |
138 void AudioOutputDevice::ShutDownOnIOThread() { | 144 void AudioOutputDevice::ShutDownOnIOThread() { |
139 DCHECK(message_loop()->BelongsToCurrentThread()); | 145 DCHECK(message_loop()->BelongsToCurrentThread()); |
140 | 146 |
141 // Make sure we don't call shutdown more than once. | 147 // Close the stream, if we haven't already. |
142 if (state_ >= CREATING_STREAM) { | 148 if (state_ >= CREATING_STREAM) { |
143 ipc_->CloseStream(stream_id_); | 149 ipc_->CloseStream(); |
144 ipc_->RemoveDelegate(stream_id_); | |
145 state_ = IDLE; | 150 state_ = IDLE; |
146 stream_id_ = 0; | |
147 } | 151 } |
148 | 152 |
149 // We can run into an issue where ShutDownOnIOThread is called right after | 153 // We can run into an issue where ShutDownOnIOThread is called right after |
150 // OnStreamCreated is called in cases where Start/Stop are called before we | 154 // OnStreamCreated is called in cases where Start/Stop are called before we |
151 // get the OnStreamCreated callback. To handle that corner case, we call | 155 // get the OnStreamCreated callback. To handle that corner case, we call |
152 // Stop(). In most cases, the thread will already be stopped. | 156 // Stop(). In most cases, the thread will already be stopped. |
153 // | 157 // |
154 // Another situation is when the IO thread goes away before Stop() is called | 158 // Another situation is when the IO thread goes away before Stop() is called |
155 // in which case, we cannot use the message loop to close the thread handle | 159 // in which case, we cannot use the message loop to close the thread handle |
156 // and can't rely on the main thread existing either. | 160 // and can't rely on the main thread existing either. |
157 base::AutoLock auto_lock_(audio_thread_lock_); | 161 base::AutoLock auto_lock_(audio_thread_lock_); |
158 base::ThreadRestrictions::ScopedAllowIO allow_io; | 162 base::ThreadRestrictions::ScopedAllowIO allow_io; |
159 audio_thread_.Stop(NULL); | 163 audio_thread_.Stop(NULL); |
160 audio_callback_.reset(); | 164 audio_callback_.reset(); |
161 stopping_hack_ = false; | 165 stopping_hack_ = false; |
162 } | 166 } |
163 | 167 |
164 void AudioOutputDevice::SetVolumeOnIOThread(double volume) { | 168 void AudioOutputDevice::SetVolumeOnIOThread(double volume) { |
165 DCHECK(message_loop()->BelongsToCurrentThread()); | 169 DCHECK(message_loop()->BelongsToCurrentThread()); |
166 if (state_ >= CREATING_STREAM) | 170 if (state_ >= CREATING_STREAM) |
167 ipc_->SetVolume(stream_id_, volume); | 171 ipc_->SetVolume(volume); |
168 } | 172 } |
169 | 173 |
170 void AudioOutputDevice::OnStateChanged(AudioOutputIPCDelegate::State state) { | 174 void AudioOutputDevice::OnStateChanged(AudioOutputIPCDelegate::State state) { |
171 DCHECK(message_loop()->BelongsToCurrentThread()); | 175 DCHECK(message_loop()->BelongsToCurrentThread()); |
172 | 176 |
173 // Do nothing if the stream has been closed. | 177 // Do nothing if the stream has been closed. |
174 if (state_ < CREATING_STREAM) | 178 if (state_ < CREATING_STREAM) |
175 return; | 179 return; |
176 | 180 |
177 if (state == AudioOutputIPCDelegate::kError) { | 181 // TODO(miu): Clean-up inconsistent and incomplete handling here. |
178 DLOG(WARNING) << "AudioOutputDevice::OnStateChanged(kError)"; | 182 // http://crbug.com/180640 |
179 // Don't dereference the callback object if the audio thread | 183 switch (state) { |
180 // is stopped or stopping. That could mean that the callback | 184 case AudioOutputIPCDelegate::kPlaying: |
181 // object has been deleted. | 185 case AudioOutputIPCDelegate::kPaused: |
182 // TODO(tommi): Add an explicit contract for clearing the callback | 186 NOTIMPLEMENTED(); |
183 // object. Possibly require calling Initialize again or provide | 187 break; |
184 // a callback object via Start() and clear it in Stop(). | 188 case AudioOutputIPCDelegate::kError: |
185 if (!audio_thread_.IsStopped()) | 189 DLOG(WARNING) << "AudioOutputDevice::OnStateChanged(kError)"; |
186 callback_->OnRenderError(); | 190 // Don't dereference the callback object if the audio thread |
| 191 // is stopped or stopping. That could mean that the callback |
| 192 // object has been deleted. |
| 193 // TODO(tommi): Add an explicit contract for clearing the callback |
| 194 // object. Possibly require calling Initialize again or provide |
| 195 // a callback object via Start() and clear it in Stop(). |
| 196 if (!audio_thread_.IsStopped()) |
| 197 callback_->OnRenderError(); |
| 198 break; |
| 199 default: |
| 200 NOTREACHED(); |
| 201 break; |
187 } | 202 } |
188 } | 203 } |
189 | 204 |
190 void AudioOutputDevice::OnStreamCreated( | 205 void AudioOutputDevice::OnStreamCreated( |
191 base::SharedMemoryHandle handle, | 206 base::SharedMemoryHandle handle, |
192 base::SyncSocket::Handle socket_handle, | 207 base::SyncSocket::Handle socket_handle, |
193 int length) { | 208 int length) { |
194 DCHECK(message_loop()->BelongsToCurrentThread()); | 209 DCHECK(message_loop()->BelongsToCurrentThread()); |
195 #if defined(OS_WIN) | 210 #if defined(OS_WIN) |
196 DCHECK(handle); | 211 DCHECK(handle); |
197 DCHECK(socket_handle); | 212 DCHECK(socket_handle); |
198 #else | 213 #else |
199 DCHECK_GE(handle.fd, 0); | 214 DCHECK_GE(handle.fd, 0); |
200 DCHECK_GE(socket_handle, 0); | 215 DCHECK_GE(socket_handle, 0); |
201 #endif | 216 #endif |
| 217 DCHECK_GT(length, 0); |
202 | 218 |
203 if (state_ != CREATING_STREAM) | 219 if (state_ != CREATING_STREAM) |
204 return; | 220 return; |
205 | 221 |
206 // We can receive OnStreamCreated() on the IO thread after the client has | 222 // We can receive OnStreamCreated() on the IO thread after the client has |
207 // called Stop() but before ShutDownOnIOThread() is processed. In such a | 223 // called Stop() but before ShutDownOnIOThread() is processed. In such a |
208 // situation |callback_| might point to freed memory. Instead of starting | 224 // situation |callback_| might point to freed memory. Instead of starting |
209 // |audio_thread_| do nothing and wait for ShutDownOnIOThread() to get called. | 225 // |audio_thread_| do nothing and wait for ShutDownOnIOThread() to get called. |
210 // | 226 // |
211 // TODO(scherkus): The real fix is to have sane ownership semantics. The fact | 227 // TODO(scherkus): The real fix is to have sane ownership semantics. The fact |
(...skipping 16 matching lines...) Expand all Loading... |
228 | 244 |
229 // We handle the case where Play() and/or Pause() may have been called | 245 // We handle the case where Play() and/or Pause() may have been called |
230 // multiple times before OnStreamCreated() gets called. | 246 // multiple times before OnStreamCreated() gets called. |
231 if (play_on_start_) | 247 if (play_on_start_) |
232 PlayOnIOThread(); | 248 PlayOnIOThread(); |
233 } | 249 } |
234 | 250 |
235 void AudioOutputDevice::OnIPCClosed() { | 251 void AudioOutputDevice::OnIPCClosed() { |
236 DCHECK(message_loop()->BelongsToCurrentThread()); | 252 DCHECK(message_loop()->BelongsToCurrentThread()); |
237 state_ = IPC_CLOSED; | 253 state_ = IPC_CLOSED; |
238 ipc_ = NULL; | 254 ipc_.reset(); |
239 } | 255 } |
240 | 256 |
241 void AudioOutputDevice::WillDestroyCurrentMessageLoop() { | 257 void AudioOutputDevice::WillDestroyCurrentMessageLoop() { |
242 LOG(ERROR) << "IO loop going away before the audio device has been stopped"; | 258 LOG(ERROR) << "IO loop going away before the audio device has been stopped"; |
243 ShutDownOnIOThread(); | 259 ShutDownOnIOThread(); |
244 } | 260 } |
245 | 261 |
246 // AudioOutputDevice::AudioThreadCallback | 262 // AudioOutputDevice::AudioThreadCallback |
247 | 263 |
248 AudioOutputDevice::AudioThreadCallback::AudioThreadCallback( | 264 AudioOutputDevice::AudioThreadCallback::AudioThreadCallback( |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
320 // TODO(dalecurtis): Technically this is not always correct. Due to channel | 336 // TODO(dalecurtis): Technically this is not always correct. Due to channel |
321 // padding for alignment, there may be more data available than this. We're | 337 // padding for alignment, there may be more data available than this. We're |
322 // relying on AudioSyncReader::Read() to parse this with that in mind. Rename | 338 // relying on AudioSyncReader::Read() to parse this with that in mind. Rename |
323 // these methods to Set/GetActualFrameCount(). | 339 // these methods to Set/GetActualFrameCount(). |
324 SetActualDataSizeInBytes( | 340 SetActualDataSizeInBytes( |
325 &shared_memory_, memory_length_, | 341 &shared_memory_, memory_length_, |
326 num_frames * sizeof(*output_bus_->channel(0)) * output_bus_->channels()); | 342 num_frames * sizeof(*output_bus_->channel(0)) * output_bus_->channels()); |
327 } | 343 } |
328 | 344 |
329 } // namespace media. | 345 } // namespace media. |
OLD | NEW |