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 "content/renderer/media/audio_input_device.h" | 5 #include "content/renderer/media/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 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
45 callback_(callback), | 45 callback_(callback), |
46 event_handler_(event_handler), | 46 event_handler_(event_handler), |
47 volume_(1.0), | 47 volume_(1.0), |
48 stream_id_(0), | 48 stream_id_(0), |
49 session_id_(0), | 49 session_id_(0), |
50 pending_device_ready_(false), | 50 pending_device_ready_(false), |
51 agc_is_enabled_(false) { | 51 agc_is_enabled_(false) { |
52 filter_ = RenderThreadImpl::current()->audio_input_message_filter(); | 52 filter_ = RenderThreadImpl::current()->audio_input_message_filter(); |
53 } | 53 } |
54 | 54 |
55 AudioInputDevice::~AudioInputDevice() { | 55 void AudioInputDevice::SetDevice(int session_id) { |
56 // TODO(henrika): The current design requires that the user calls | 56 DVLOG(1) << "SetDevice (session_id=" << session_id << ")"; |
57 // Stop before deleting this class. | 57 message_loop()->PostTask(FROM_HERE, |
58 CHECK_EQ(0, stream_id_); | 58 base::Bind(&AudioInputDevice::SetSessionIdOnIOThread, this, session_id)); |
59 } | 59 } |
60 | 60 |
61 void AudioInputDevice::Start() { | 61 void AudioInputDevice::Start() { |
62 DVLOG(1) << "Start()"; | 62 DVLOG(1) << "Start()"; |
63 message_loop()->PostTask(FROM_HERE, | 63 message_loop()->PostTask(FROM_HERE, |
64 base::Bind(&AudioInputDevice::InitializeOnIOThread, this)); | 64 base::Bind(&AudioInputDevice::InitializeOnIOThread, this)); |
65 } | 65 } |
66 | 66 |
67 void AudioInputDevice::SetDevice(int session_id) { | |
68 DVLOG(1) << "SetDevice (session_id=" << session_id << ")"; | |
69 message_loop()->PostTask(FROM_HERE, | |
70 base::Bind(&AudioInputDevice::SetSessionIdOnIOThread, this, session_id)); | |
71 } | |
72 | |
73 void AudioInputDevice::Stop() { | 67 void AudioInputDevice::Stop() { |
74 DVLOG(1) << "Stop()"; | 68 DVLOG(1) << "Stop()"; |
75 | 69 |
76 { | 70 { |
77 base::AutoLock auto_lock(audio_thread_lock_); | 71 base::AutoLock auto_lock(audio_thread_lock_); |
78 audio_thread_.Stop(MessageLoop::current()); | 72 audio_thread_.Stop(MessageLoop::current()); |
79 } | 73 } |
80 | 74 |
81 message_loop()->PostTask(FROM_HERE, | 75 message_loop()->PostTask(FROM_HERE, |
82 base::Bind(&AudioInputDevice::ShutDownOnIOThread, this)); | 76 base::Bind(&AudioInputDevice::ShutDownOnIOThread, this)); |
(...skipping 14 matching lines...) Expand all Loading... |
97 return false; | 91 return false; |
98 } | 92 } |
99 | 93 |
100 void AudioInputDevice::SetAutomaticGainControl(bool enabled) { | 94 void AudioInputDevice::SetAutomaticGainControl(bool enabled) { |
101 DVLOG(1) << "SetAutomaticGainControl(enabled=" << enabled << ")"; | 95 DVLOG(1) << "SetAutomaticGainControl(enabled=" << enabled << ")"; |
102 message_loop()->PostTask(FROM_HERE, | 96 message_loop()->PostTask(FROM_HERE, |
103 base::Bind(&AudioInputDevice::SetAutomaticGainControlOnIOThread, | 97 base::Bind(&AudioInputDevice::SetAutomaticGainControlOnIOThread, |
104 this, enabled)); | 98 this, enabled)); |
105 } | 99 } |
106 | 100 |
107 void AudioInputDevice::InitializeOnIOThread() { | |
108 DCHECK(message_loop()->BelongsToCurrentThread()); | |
109 // Make sure we don't call Start() more than once. | |
110 DCHECK_EQ(0, stream_id_); | |
111 if (stream_id_) | |
112 return; | |
113 | |
114 stream_id_ = filter_->AddDelegate(this); | |
115 // If |session_id_| is not specified, it will directly create the stream; | |
116 // otherwise it will send a AudioInputHostMsg_StartDevice msg to the browser | |
117 // and create the stream when getting a OnDeviceReady() callback. | |
118 if (!session_id_) { | |
119 Send(new AudioInputHostMsg_CreateStream( | |
120 stream_id_, audio_parameters_, | |
121 media::AudioManagerBase::kDefaultDeviceId, | |
122 agc_is_enabled_)); | |
123 } else { | |
124 Send(new AudioInputHostMsg_StartDevice(stream_id_, session_id_)); | |
125 pending_device_ready_ = true; | |
126 } | |
127 } | |
128 | |
129 void AudioInputDevice::SetSessionIdOnIOThread(int session_id) { | |
130 DCHECK(message_loop()->BelongsToCurrentThread()); | |
131 session_id_ = session_id; | |
132 } | |
133 | |
134 void AudioInputDevice::StartOnIOThread() { | |
135 DCHECK(message_loop()->BelongsToCurrentThread()); | |
136 if (stream_id_) | |
137 Send(new AudioInputHostMsg_RecordStream(stream_id_)); | |
138 } | |
139 | |
140 void AudioInputDevice::ShutDownOnIOThread() { | |
141 DCHECK(message_loop()->BelongsToCurrentThread()); | |
142 // NOTE: |completion| may be NULL. | |
143 // Make sure we don't call shutdown more than once. | |
144 if (stream_id_) { | |
145 filter_->RemoveDelegate(stream_id_); | |
146 Send(new AudioInputHostMsg_CloseStream(stream_id_)); | |
147 | |
148 stream_id_ = 0; | |
149 session_id_ = 0; | |
150 pending_device_ready_ = false; | |
151 agc_is_enabled_ = false; | |
152 } | |
153 | |
154 // 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 | |
156 // get the OnStreamCreated callback. To handle that corner case, we call | |
157 // Stop(). In most cases, the thread will already be stopped. | |
158 // Another situation is when the IO thread goes away before Stop() is called | |
159 // in which case, we cannot use the message loop to close the thread handle | |
160 // and can't not rely on the main thread existing either. | |
161 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
162 audio_thread_.Stop(NULL); | |
163 audio_callback_.reset(); | |
164 } | |
165 | |
166 void AudioInputDevice::SetVolumeOnIOThread(double volume) { | |
167 DCHECK(message_loop()->BelongsToCurrentThread()); | |
168 if (stream_id_) | |
169 Send(new AudioInputHostMsg_SetVolume(stream_id_, volume)); | |
170 } | |
171 | |
172 void AudioInputDevice::SetAutomaticGainControlOnIOThread(bool enabled) { | |
173 DCHECK(message_loop()->BelongsToCurrentThread()); | |
174 DCHECK_EQ(0, stream_id_) << | |
175 "The AGC state can not be modified while capturing is active."; | |
176 if (stream_id_) | |
177 return; | |
178 | |
179 // We simply store the new AGC setting here. This value will be used when | |
180 // a new stream is initialized and by GetAutomaticGainControl(). | |
181 agc_is_enabled_ = enabled; | |
182 } | |
183 | |
184 void AudioInputDevice::OnStreamCreated( | 101 void AudioInputDevice::OnStreamCreated( |
185 base::SharedMemoryHandle handle, | 102 base::SharedMemoryHandle handle, |
186 base::SyncSocket::Handle socket_handle, | 103 base::SyncSocket::Handle socket_handle, |
187 uint32 length) { | 104 uint32 length) { |
188 DCHECK(message_loop()->BelongsToCurrentThread()); | 105 DCHECK(message_loop()->BelongsToCurrentThread()); |
189 #if defined(OS_WIN) | 106 #if defined(OS_WIN) |
190 DCHECK(handle); | 107 DCHECK(handle); |
191 DCHECK(socket_handle); | 108 DCHECK(socket_handle); |
192 #else | 109 #else |
193 DCHECK_GE(handle.fd, 0); | 110 DCHECK_GE(handle.fd, 0); |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
280 Send(new AudioInputHostMsg_CreateStream(stream_id_, audio_parameters_, | 197 Send(new AudioInputHostMsg_CreateStream(stream_id_, audio_parameters_, |
281 device_id, agc_is_enabled_)); | 198 device_id, agc_is_enabled_)); |
282 } | 199 } |
283 | 200 |
284 pending_device_ready_ = false; | 201 pending_device_ready_ = false; |
285 // Notify the client that the device has been started. | 202 // Notify the client that the device has been started. |
286 if (event_handler_) | 203 if (event_handler_) |
287 event_handler_->OnDeviceStarted(device_id); | 204 event_handler_->OnDeviceStarted(device_id); |
288 } | 205 } |
289 | 206 |
| 207 AudioInputDevice::~AudioInputDevice() { |
| 208 // TODO(henrika): The current design requires that the user calls |
| 209 // Stop before deleting this class. |
| 210 CHECK_EQ(0, stream_id_); |
| 211 } |
| 212 |
| 213 void AudioInputDevice::InitializeOnIOThread() { |
| 214 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 215 // Make sure we don't call Start() more than once. |
| 216 DCHECK_EQ(0, stream_id_); |
| 217 if (stream_id_) |
| 218 return; |
| 219 |
| 220 stream_id_ = filter_->AddDelegate(this); |
| 221 // If |session_id_| is not specified, it will directly create the stream; |
| 222 // otherwise it will send a AudioInputHostMsg_StartDevice msg to the browser |
| 223 // and create the stream when getting a OnDeviceReady() callback. |
| 224 if (!session_id_) { |
| 225 Send(new AudioInputHostMsg_CreateStream( |
| 226 stream_id_, audio_parameters_, |
| 227 media::AudioManagerBase::kDefaultDeviceId, |
| 228 agc_is_enabled_)); |
| 229 } else { |
| 230 Send(new AudioInputHostMsg_StartDevice(stream_id_, session_id_)); |
| 231 pending_device_ready_ = true; |
| 232 } |
| 233 } |
| 234 |
| 235 void AudioInputDevice::SetSessionIdOnIOThread(int session_id) { |
| 236 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 237 session_id_ = session_id; |
| 238 } |
| 239 |
| 240 void AudioInputDevice::StartOnIOThread() { |
| 241 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 242 if (stream_id_) |
| 243 Send(new AudioInputHostMsg_RecordStream(stream_id_)); |
| 244 } |
| 245 |
| 246 void AudioInputDevice::ShutDownOnIOThread() { |
| 247 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 248 // NOTE: |completion| may be NULL. |
| 249 // Make sure we don't call shutdown more than once. |
| 250 if (stream_id_) { |
| 251 filter_->RemoveDelegate(stream_id_); |
| 252 Send(new AudioInputHostMsg_CloseStream(stream_id_)); |
| 253 |
| 254 stream_id_ = 0; |
| 255 session_id_ = 0; |
| 256 pending_device_ready_ = false; |
| 257 agc_is_enabled_ = false; |
| 258 } |
| 259 |
| 260 // We can run into an issue where ShutDownOnIOThread is called right after |
| 261 // OnStreamCreated is called in cases where Start/Stop are called before we |
| 262 // get the OnStreamCreated callback. To handle that corner case, we call |
| 263 // Stop(). In most cases, the thread will already be stopped. |
| 264 // Another situation is when the IO thread goes away before Stop() is called |
| 265 // in which case, we cannot use the message loop to close the thread handle |
| 266 // and can't not rely on the main thread existing either. |
| 267 base::ThreadRestrictions::ScopedAllowIO allow_io; |
| 268 audio_thread_.Stop(NULL); |
| 269 audio_callback_.reset(); |
| 270 } |
| 271 |
| 272 void AudioInputDevice::SetVolumeOnIOThread(double volume) { |
| 273 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 274 if (stream_id_) |
| 275 Send(new AudioInputHostMsg_SetVolume(stream_id_, volume)); |
| 276 } |
| 277 |
| 278 void AudioInputDevice::SetAutomaticGainControlOnIOThread(bool enabled) { |
| 279 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 280 DCHECK_EQ(0, stream_id_) << |
| 281 "The AGC state can not be modified while capturing is active."; |
| 282 if (stream_id_) |
| 283 return; |
| 284 |
| 285 // We simply store the new AGC setting here. This value will be used when |
| 286 // a new stream is initialized and by GetAutomaticGainControl(). |
| 287 agc_is_enabled_ = enabled; |
| 288 } |
| 289 |
290 void AudioInputDevice::Send(IPC::Message* message) { | 290 void AudioInputDevice::Send(IPC::Message* message) { |
291 filter_->Send(message); | 291 filter_->Send(message); |
292 } | 292 } |
293 | 293 |
294 void AudioInputDevice::WillDestroyCurrentMessageLoop() { | 294 void AudioInputDevice::WillDestroyCurrentMessageLoop() { |
295 LOG(ERROR) << "IO loop going away before the input device has been stopped"; | 295 LOG(ERROR) << "IO loop going away before the input device has been stopped"; |
296 ShutDownOnIOThread(); | 296 ShutDownOnIOThread(); |
297 } | 297 } |
298 | 298 |
299 // AudioInputDevice::AudioThreadCallback | 299 // AudioInputDevice::AudioThreadCallback |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
338 channel_index, | 338 channel_index, |
339 bytes_per_sample, | 339 bytes_per_sample, |
340 number_of_frames); | 340 number_of_frames); |
341 } | 341 } |
342 | 342 |
343 // Deliver captured data to the client in floating point format | 343 // Deliver captured data to the client in floating point format |
344 // and update the audio-delay measurement. | 344 // and update the audio-delay measurement. |
345 capture_callback_->Capture(audio_data_, number_of_frames, | 345 capture_callback_->Capture(audio_data_, number_of_frames, |
346 audio_delay_milliseconds, volume); | 346 audio_delay_milliseconds, volume); |
347 } | 347 } |
OLD | NEW |