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_input_device.h" | 5 #include "media/audio/audio_input_device.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.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 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
42 CaptureCallback* capture_callback_; | 42 CaptureCallback* capture_callback_; |
43 scoped_ptr<AudioBus> audio_bus_; | 43 scoped_ptr<AudioBus> audio_bus_; |
44 DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback); | 44 DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback); |
45 }; | 45 }; |
46 | 46 |
47 AudioInputDevice::AudioInputDevice( | 47 AudioInputDevice::AudioInputDevice( |
48 AudioInputIPC* ipc, | 48 AudioInputIPC* ipc, |
49 const scoped_refptr<base::MessageLoopProxy>& io_loop) | 49 const scoped_refptr<base::MessageLoopProxy>& io_loop) |
50 : ScopedLoopObserver(io_loop), | 50 : ScopedLoopObserver(io_loop), |
51 callback_(NULL), | 51 callback_(NULL), |
52 event_handler_(NULL), | |
53 ipc_(ipc), | 52 ipc_(ipc), |
54 stream_id_(0), | 53 stream_id_(0), |
55 session_id_(0), | 54 session_id_(0), |
56 pending_device_ready_(false), | |
57 agc_is_enabled_(false) { | 55 agc_is_enabled_(false) { |
58 CHECK(ipc_); | 56 CHECK(ipc_); |
59 } | 57 } |
60 | 58 |
61 void AudioInputDevice::Initialize(const AudioParameters& params, | 59 void AudioInputDevice::Initialize(const AudioParameters& params, |
62 CaptureCallback* callback, | 60 CaptureCallback* callback, |
63 CaptureEventHandler* event_handler) { | 61 int session_id) { |
64 DCHECK(!callback_); | 62 DCHECK(!callback_); |
65 DCHECK(!event_handler_); | 63 DCHECK_EQ(0, session_id_); |
66 audio_parameters_ = params; | 64 audio_parameters_ = params; |
67 callback_ = callback; | 65 callback_ = callback; |
68 event_handler_ = event_handler; | 66 session_id_ = session_id; |
69 } | |
70 | |
71 void AudioInputDevice::SetDevice(int session_id) { | |
72 DVLOG(1) << "SetDevice (session_id=" << session_id << ")"; | |
73 message_loop()->PostTask(FROM_HERE, | |
74 base::Bind(&AudioInputDevice::SetSessionIdOnIOThread, this, session_id)); | |
75 } | 67 } |
76 | 68 |
77 void AudioInputDevice::Start() { | 69 void AudioInputDevice::Start() { |
78 DVLOG(1) << "Start()"; | 70 DVLOG(1) << "Start()"; |
79 message_loop()->PostTask(FROM_HERE, | 71 message_loop()->PostTask(FROM_HERE, |
80 base::Bind(&AudioInputDevice::InitializeOnIOThread, this)); | 72 base::Bind(&AudioInputDevice::InitializeOnIOThread, this)); |
81 } | 73 } |
82 | 74 |
83 void AudioInputDevice::Stop() { | 75 void AudioInputDevice::Stop() { |
84 DVLOG(1) << "Stop()"; | 76 DVLOG(1) << "Stop()"; |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
157 return; | 149 return; |
158 | 150 |
159 switch (state) { | 151 switch (state) { |
160 case AudioInputIPCDelegate::kStopped: | 152 case AudioInputIPCDelegate::kStopped: |
161 // TODO(xians): Should we just call ShutDownOnIOThread here instead? | 153 // TODO(xians): Should we just call ShutDownOnIOThread here instead? |
162 ipc_->RemoveDelegate(stream_id_); | 154 ipc_->RemoveDelegate(stream_id_); |
163 | 155 |
164 audio_thread_.Stop(MessageLoop::current()); | 156 audio_thread_.Stop(MessageLoop::current()); |
165 audio_callback_.reset(); | 157 audio_callback_.reset(); |
166 | 158 |
167 if (event_handler_) | |
168 event_handler_->OnDeviceStopped(); | |
169 | |
170 stream_id_ = 0; | 159 stream_id_ = 0; |
171 pending_device_ready_ = false; | |
172 break; | 160 break; |
173 case AudioInputIPCDelegate::kRecording: | 161 case AudioInputIPCDelegate::kRecording: |
174 NOTIMPLEMENTED(); | 162 NOTIMPLEMENTED(); |
175 break; | 163 break; |
176 case AudioInputIPCDelegate::kError: | 164 case AudioInputIPCDelegate::kError: |
177 DLOG(WARNING) << "AudioInputDevice::OnStateChanged(kError)"; | 165 DLOG(WARNING) << "AudioInputDevice::OnStateChanged(kError)"; |
178 // Don't dereference the callback object if the audio thread | 166 // Don't dereference the callback object if the audio thread |
179 // is stopped or stopping. That could mean that the callback | 167 // is stopped or stopping. That could mean that the callback |
180 // object has been deleted. | 168 // object has been deleted. |
181 // TODO(tommi): Add an explicit contract for clearing the callback | 169 // TODO(tommi): Add an explicit contract for clearing the callback |
182 // object. Possibly require calling Initialize again or provide | 170 // object. Possibly require calling Initialize again or provide |
183 // a callback object via Start() and clear it in Stop(). | 171 // a callback object via Start() and clear it in Stop(). |
184 if (!audio_thread_.IsStopped()) | 172 if (!audio_thread_.IsStopped()) |
185 callback_->OnCaptureError(); | 173 callback_->OnCaptureError(); |
186 break; | 174 break; |
187 default: | 175 default: |
188 NOTREACHED(); | 176 NOTREACHED(); |
189 break; | 177 break; |
190 } | 178 } |
191 } | 179 } |
192 | 180 |
193 void AudioInputDevice::OnDeviceReady(const std::string& device_id) { | |
194 DCHECK(message_loop()->BelongsToCurrentThread()); | |
195 DVLOG(1) << "OnDeviceReady (device_id=" << device_id << ")"; | |
196 | |
197 // Takes care of the case when Stop() is called before OnDeviceReady(). | |
198 if (!pending_device_ready_) | |
199 return; | |
200 | |
201 // If AudioInputDeviceManager returns an empty string, it means no device | |
202 // is ready for start. | |
203 if (device_id.empty()) { | |
204 ipc_->RemoveDelegate(stream_id_); | |
205 stream_id_ = 0; | |
206 } else { | |
207 ipc_->CreateStream(stream_id_, audio_parameters_, device_id, | |
208 agc_is_enabled_, kRequestedSharedMemoryCount); | |
209 } | |
210 | |
211 pending_device_ready_ = false; | |
212 // Notify the client that the device has been started. | |
213 if (event_handler_) | |
214 event_handler_->OnDeviceStarted(device_id); | |
215 } | |
216 | |
217 void AudioInputDevice::OnIPCClosed() { | 181 void AudioInputDevice::OnIPCClosed() { |
218 ipc_ = NULL; | 182 ipc_ = NULL; |
219 } | 183 } |
220 | 184 |
221 AudioInputDevice::~AudioInputDevice() { | 185 AudioInputDevice::~AudioInputDevice() { |
222 // TODO(henrika): The current design requires that the user calls | 186 // TODO(henrika): The current design requires that the user calls |
223 // Stop before deleting this class. | 187 // Stop before deleting this class. |
224 CHECK_EQ(0, stream_id_); | 188 CHECK_EQ(0, stream_id_); |
225 } | 189 } |
226 | 190 |
227 void AudioInputDevice::InitializeOnIOThread() { | 191 void AudioInputDevice::InitializeOnIOThread() { |
228 DCHECK(message_loop()->BelongsToCurrentThread()); | 192 DCHECK(message_loop()->BelongsToCurrentThread()); |
229 // Make sure we don't call Start() more than once. | 193 // Make sure we don't call Start() more than once. |
230 DCHECK_EQ(0, stream_id_); | 194 DCHECK_EQ(0, stream_id_); |
231 if (stream_id_) | 195 if (stream_id_) |
232 return; | 196 return; |
233 | 197 |
| 198 if (session_id_ <= 0) { |
| 199 DLOG(WARNING) << "Invalid session id for the input stream " << session_id_; |
| 200 return; |
| 201 } |
| 202 |
234 stream_id_ = ipc_->AddDelegate(this); | 203 stream_id_ = ipc_->AddDelegate(this); |
235 // If |session_id_| is not specified, it will directly create the stream; | 204 ipc_->CreateStream(stream_id_, session_id_, audio_parameters_, |
236 // otherwise it will send a AudioInputHostMsg_StartDevice msg to the browser | 205 agc_is_enabled_, kRequestedSharedMemoryCount); |
237 // and create the stream when getting a OnDeviceReady() callback. | |
238 if (!session_id_) { | |
239 ipc_->CreateStream(stream_id_, audio_parameters_, | |
240 AudioManagerBase::kDefaultDeviceId, agc_is_enabled_, | |
241 kRequestedSharedMemoryCount); | |
242 } else { | |
243 ipc_->StartDevice(stream_id_, session_id_); | |
244 pending_device_ready_ = true; | |
245 } | |
246 } | |
247 | |
248 void AudioInputDevice::SetSessionIdOnIOThread(int session_id) { | |
249 DCHECK(message_loop()->BelongsToCurrentThread()); | |
250 session_id_ = session_id; | |
251 } | 206 } |
252 | 207 |
253 void AudioInputDevice::StartOnIOThread() { | 208 void AudioInputDevice::StartOnIOThread() { |
254 DCHECK(message_loop()->BelongsToCurrentThread()); | 209 DCHECK(message_loop()->BelongsToCurrentThread()); |
255 if (stream_id_) | 210 if (stream_id_) |
256 ipc_->RecordStream(stream_id_); | 211 ipc_->RecordStream(stream_id_); |
257 } | 212 } |
258 | 213 |
259 void AudioInputDevice::ShutDownOnIOThread() { | 214 void AudioInputDevice::ShutDownOnIOThread() { |
260 DCHECK(message_loop()->BelongsToCurrentThread()); | 215 DCHECK(message_loop()->BelongsToCurrentThread()); |
261 // NOTE: |completion| may be NULL. | 216 // NOTE: |completion| may be NULL. |
262 // Make sure we don't call shutdown more than once. | 217 // Make sure we don't call shutdown more than once. |
263 if (stream_id_) { | 218 if (stream_id_) { |
264 if (ipc_) { | 219 if (ipc_) { |
265 ipc_->CloseStream(stream_id_); | 220 ipc_->CloseStream(stream_id_); |
266 ipc_->RemoveDelegate(stream_id_); | 221 ipc_->RemoveDelegate(stream_id_); |
267 } | 222 } |
268 | 223 |
269 stream_id_ = 0; | 224 stream_id_ = 0; |
270 session_id_ = 0; | |
271 pending_device_ready_ = false; | |
272 agc_is_enabled_ = false; | 225 agc_is_enabled_ = false; |
273 } | 226 } |
274 | 227 |
275 // We can run into an issue where ShutDownOnIOThread is called right after | 228 // We can run into an issue where ShutDownOnIOThread is called right after |
276 // OnStreamCreated is called in cases where Start/Stop are called before we | 229 // OnStreamCreated is called in cases where Start/Stop are called before we |
277 // get the OnStreamCreated callback. To handle that corner case, we call | 230 // get the OnStreamCreated callback. To handle that corner case, we call |
278 // Stop(). In most cases, the thread will already be stopped. | 231 // Stop(). In most cases, the thread will already be stopped. |
279 // Another situation is when the IO thread goes away before Stop() is called | 232 // Another situation is when the IO thread goes away before Stop() is called |
280 // in which case, we cannot use the message loop to close the thread handle | 233 // in which case, we cannot use the message loop to close the thread handle |
281 // and can't not rely on the main thread existing either. | 234 // and can't not rely on the main thread existing either. |
282 base::ThreadRestrictions::ScopedAllowIO allow_io; | 235 base::ThreadRestrictions::ScopedAllowIO allow_io; |
283 audio_thread_.Stop(NULL); | 236 audio_thread_.Stop(NULL); |
284 audio_callback_.reset(); | 237 audio_callback_.reset(); |
| 238 session_id_ = 0; |
285 } | 239 } |
286 | 240 |
287 void AudioInputDevice::SetVolumeOnIOThread(double volume) { | 241 void AudioInputDevice::SetVolumeOnIOThread(double volume) { |
288 DCHECK(message_loop()->BelongsToCurrentThread()); | 242 DCHECK(message_loop()->BelongsToCurrentThread()); |
289 if (stream_id_) | 243 if (stream_id_) |
290 ipc_->SetVolume(stream_id_, volume); | 244 ipc_->SetVolume(stream_id_, volume); |
291 } | 245 } |
292 | 246 |
293 void AudioInputDevice::SetAutomaticGainControlOnIOThread(bool enabled) { | 247 void AudioInputDevice::SetAutomaticGainControlOnIOThread(bool enabled) { |
294 DCHECK(message_loop()->BelongsToCurrentThread()); | 248 DCHECK(message_loop()->BelongsToCurrentThread()); |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
350 // with nominal range -1.0 -> +1.0. | 304 // with nominal range -1.0 -> +1.0. |
351 audio_bus_->FromInterleaved(memory, audio_bus_->frames(), bytes_per_sample); | 305 audio_bus_->FromInterleaved(memory, audio_bus_->frames(), bytes_per_sample); |
352 | 306 |
353 // Deliver captured data to the client in floating point format | 307 // Deliver captured data to the client in floating point format |
354 // and update the audio-delay measurement. | 308 // and update the audio-delay measurement. |
355 capture_callback_->Capture(audio_bus_.get(), | 309 capture_callback_->Capture(audio_bus_.get(), |
356 audio_delay_milliseconds, volume); | 310 audio_delay_milliseconds, volume); |
357 } | 311 } |
358 | 312 |
359 } // namespace media | 313 } // namespace media |
OLD | NEW |