OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "content/renderer/media/audio_input_device.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/message_loop.h" | |
9 #include "base/threading/thread_restrictions.h" | |
10 #include "base/time.h" | |
11 #include "media/audio/audio_manager_base.h" | |
12 #include "media/audio/audio_util.h" | |
13 | |
14 // Takes care of invoking the capture callback on the audio thread. | |
15 // An instance of this class is created for each capture stream in | |
16 // OnLowLatencyCreated(). | |
17 class AudioInputDevice::AudioThreadCallback | |
18 : public AudioDeviceThread::Callback { | |
19 public: | |
20 AudioThreadCallback(const media::AudioParameters& audio_parameters, | |
21 base::SharedMemoryHandle memory, | |
22 int memory_length, | |
23 CaptureCallback* capture_callback); | |
24 virtual ~AudioThreadCallback(); | |
25 | |
26 virtual void MapSharedMemory() OVERRIDE; | |
27 | |
28 // Called whenever we receive notifications about pending data. | |
29 virtual void Process(int pending_data) OVERRIDE; | |
30 | |
31 private: | |
32 CaptureCallback* capture_callback_; | |
33 DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback); | |
34 }; | |
35 | |
36 AudioInputDevice::AudioInputDevice( | |
37 media::AudioInputIPC* ipc, | |
38 const scoped_refptr<base::MessageLoopProxy>& io_loop) | |
39 : ScopedLoopObserver(io_loop), | |
40 callback_(NULL), | |
41 event_handler_(NULL), | |
42 ipc_(ipc), | |
43 stream_id_(0), | |
44 session_id_(0), | |
45 pending_device_ready_(false), | |
46 agc_is_enabled_(false) { | |
47 CHECK(ipc_); | |
48 } | |
49 | |
50 void AudioInputDevice::Initialize(const media::AudioParameters& params, | |
51 CaptureCallback* callback, | |
52 CaptureEventHandler* event_handler) { | |
53 DCHECK(!callback_); | |
54 DCHECK(!event_handler_); | |
55 audio_parameters_ = params; | |
56 callback_ = callback; | |
57 event_handler_ = event_handler; | |
58 } | |
59 | |
60 void AudioInputDevice::SetDevice(int session_id) { | |
61 DVLOG(1) << "SetDevice (session_id=" << session_id << ")"; | |
62 message_loop()->PostTask(FROM_HERE, | |
63 base::Bind(&AudioInputDevice::SetSessionIdOnIOThread, this, session_id)); | |
64 } | |
65 | |
66 void AudioInputDevice::Start() { | |
67 DVLOG(1) << "Start()"; | |
68 message_loop()->PostTask(FROM_HERE, | |
69 base::Bind(&AudioInputDevice::InitializeOnIOThread, this)); | |
70 } | |
71 | |
72 void AudioInputDevice::Stop() { | |
73 DVLOG(1) << "Stop()"; | |
74 | |
75 { | |
76 base::AutoLock auto_lock(audio_thread_lock_); | |
77 audio_thread_.Stop(MessageLoop::current()); | |
78 } | |
79 | |
80 message_loop()->PostTask(FROM_HERE, | |
81 base::Bind(&AudioInputDevice::ShutDownOnIOThread, this)); | |
82 } | |
83 | |
84 void AudioInputDevice::SetVolume(double volume) { | |
85 if (volume < 0 || volume > 1.0) { | |
86 DLOG(ERROR) << "Invalid volume value specified"; | |
87 return; | |
88 } | |
89 | |
90 message_loop()->PostTask(FROM_HERE, | |
91 base::Bind(&AudioInputDevice::SetVolumeOnIOThread, this, volume)); | |
92 } | |
93 | |
94 void AudioInputDevice::SetAutomaticGainControl(bool enabled) { | |
95 DVLOG(1) << "SetAutomaticGainControl(enabled=" << enabled << ")"; | |
96 message_loop()->PostTask(FROM_HERE, | |
97 base::Bind(&AudioInputDevice::SetAutomaticGainControlOnIOThread, | |
98 this, enabled)); | |
99 } | |
100 | |
101 void AudioInputDevice::OnStreamCreated( | |
102 base::SharedMemoryHandle handle, | |
103 base::SyncSocket::Handle socket_handle, | |
104 int length) { | |
105 DCHECK(message_loop()->BelongsToCurrentThread()); | |
106 #if defined(OS_WIN) | |
107 DCHECK(handle); | |
108 DCHECK(socket_handle); | |
109 #else | |
110 DCHECK_GE(handle.fd, 0); | |
111 DCHECK_GE(socket_handle, 0); | |
112 #endif | |
113 DCHECK(length); | |
114 DVLOG(1) << "OnStreamCreated (stream_id=" << stream_id_ << ")"; | |
115 | |
116 // We should only get this callback if stream_id_ is valid. If it is not, | |
117 // the IPC layer should have closed the shared memory and socket handles | |
118 // for us and not invoked the callback. The basic assertion is that when | |
119 // stream_id_ is 0 the AudioInputDevice instance is not registered as a | |
120 // delegate and hence it should not receive callbacks. | |
121 DCHECK(stream_id_); | |
122 | |
123 base::AutoLock auto_lock(audio_thread_lock_); | |
124 | |
125 DCHECK(audio_thread_.IsStopped()); | |
126 audio_callback_.reset( | |
127 new AudioInputDevice::AudioThreadCallback(audio_parameters_, handle, | |
128 length, callback_)); | |
129 audio_thread_.Start(audio_callback_.get(), socket_handle, "AudioInputDevice"); | |
130 | |
131 MessageLoop::current()->PostTask(FROM_HERE, | |
132 base::Bind(&AudioInputDevice::StartOnIOThread, this)); | |
133 } | |
134 | |
135 void AudioInputDevice::OnVolume(double volume) { | |
136 NOTIMPLEMENTED(); | |
137 } | |
138 | |
139 void AudioInputDevice::OnStateChanged( | |
140 media::AudioInputIPCDelegate::State state) { | |
141 DCHECK(message_loop()->BelongsToCurrentThread()); | |
142 | |
143 // Do nothing if the stream has been closed. | |
144 if (!stream_id_) | |
145 return; | |
146 | |
147 switch (state) { | |
148 case media::AudioInputIPCDelegate::kStopped: | |
149 // TODO(xians): Should we just call ShutDownOnIOThread here instead? | |
150 ipc_->RemoveDelegate(stream_id_); | |
151 | |
152 audio_thread_.Stop(MessageLoop::current()); | |
153 audio_callback_.reset(); | |
154 | |
155 if (event_handler_) | |
156 event_handler_->OnDeviceStopped(); | |
157 | |
158 stream_id_ = 0; | |
159 pending_device_ready_ = false; | |
160 break; | |
161 case media::AudioInputIPCDelegate::kRecording: | |
162 NOTIMPLEMENTED(); | |
163 break; | |
164 case media::AudioInputIPCDelegate::kError: | |
165 DLOG(WARNING) << "AudioInputDevice::OnStateChanged(kError)"; | |
166 // Don't dereference the callback object if the audio thread | |
167 // is stopped or stopping. That could mean that the callback | |
168 // object has been deleted. | |
169 // TODO(tommi): Add an explicit contract for clearing the callback | |
170 // object. Possibly require calling Initialize again or provide | |
171 // a callback object via Start() and clear it in Stop(). | |
172 if (!audio_thread_.IsStopped()) | |
173 callback_->OnCaptureError(); | |
174 break; | |
175 default: | |
176 NOTREACHED(); | |
177 break; | |
178 } | |
179 } | |
180 | |
181 void AudioInputDevice::OnDeviceReady(const std::string& device_id) { | |
182 DCHECK(message_loop()->BelongsToCurrentThread()); | |
183 DVLOG(1) << "OnDeviceReady (device_id=" << device_id << ")"; | |
184 | |
185 // Takes care of the case when Stop() is called before OnDeviceReady(). | |
186 if (!pending_device_ready_) | |
187 return; | |
188 | |
189 // If AudioInputDeviceManager returns an empty string, it means no device | |
190 // is ready for start. | |
191 if (device_id.empty()) { | |
192 ipc_->RemoveDelegate(stream_id_); | |
193 stream_id_ = 0; | |
194 } else { | |
195 ipc_->CreateStream(stream_id_, audio_parameters_, device_id, | |
196 agc_is_enabled_); | |
197 } | |
198 | |
199 pending_device_ready_ = false; | |
200 // Notify the client that the device has been started. | |
201 if (event_handler_) | |
202 event_handler_->OnDeviceStarted(device_id); | |
203 } | |
204 | |
205 void AudioInputDevice::OnIPCClosed() { | |
206 ipc_ = NULL; | |
207 } | |
208 | |
209 AudioInputDevice::~AudioInputDevice() { | |
210 // TODO(henrika): The current design requires that the user calls | |
211 // Stop before deleting this class. | |
212 CHECK_EQ(0, stream_id_); | |
213 } | |
214 | |
215 void AudioInputDevice::InitializeOnIOThread() { | |
216 DCHECK(message_loop()->BelongsToCurrentThread()); | |
217 // Make sure we don't call Start() more than once. | |
218 DCHECK_EQ(0, stream_id_); | |
219 if (stream_id_) | |
220 return; | |
221 | |
222 stream_id_ = ipc_->AddDelegate(this); | |
223 // If |session_id_| is not specified, it will directly create the stream; | |
224 // otherwise it will send a AudioInputHostMsg_StartDevice msg to the browser | |
225 // and create the stream when getting a OnDeviceReady() callback. | |
226 if (!session_id_) { | |
227 ipc_->CreateStream(stream_id_, audio_parameters_, | |
228 media::AudioManagerBase::kDefaultDeviceId, agc_is_enabled_); | |
229 } else { | |
230 ipc_->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 ipc_->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 ipc_->CloseStream(stream_id_); | |
252 ipc_->RemoveDelegate(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 ipc_->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::WillDestroyCurrentMessageLoop() { | |
291 LOG(ERROR) << "IO loop going away before the input device has been stopped"; | |
292 ShutDownOnIOThread(); | |
293 } | |
294 | |
295 // AudioInputDevice::AudioThreadCallback | |
296 AudioInputDevice::AudioThreadCallback::AudioThreadCallback( | |
297 const media::AudioParameters& audio_parameters, | |
298 base::SharedMemoryHandle memory, | |
299 int memory_length, | |
300 CaptureCallback* capture_callback) | |
301 : AudioDeviceThread::Callback(audio_parameters, memory, memory_length), | |
302 capture_callback_(capture_callback) { | |
303 } | |
304 | |
305 AudioInputDevice::AudioThreadCallback::~AudioThreadCallback() { | |
306 } | |
307 | |
308 void AudioInputDevice::AudioThreadCallback::MapSharedMemory() { | |
309 shared_memory_.Map(memory_length_); | |
310 } | |
311 | |
312 void AudioInputDevice::AudioThreadCallback::Process(int pending_data) { | |
313 // The shared memory represents parameters, size of the data buffer and the | |
314 // actual data buffer containing audio data. Map the memory into this | |
315 // structure and parse out parameters and the data area. | |
316 media::AudioInputBuffer* buffer = | |
317 reinterpret_cast<media::AudioInputBuffer*>(shared_memory_.memory()); | |
318 DCHECK_EQ(buffer->params.size, | |
319 memory_length_ - sizeof(media::AudioInputBufferParameters)); | |
320 double volume = buffer->params.volume; | |
321 | |
322 int audio_delay_milliseconds = pending_data / bytes_per_ms_; | |
323 int16* memory = reinterpret_cast<int16*>(&buffer->audio[0]); | |
324 const size_t number_of_frames = audio_parameters_.frames_per_buffer(); | |
325 const int bytes_per_sample = sizeof(memory[0]); | |
326 | |
327 // Deinterleave each channel and convert to 32-bit floating-point | |
328 // with nominal range -1.0 -> +1.0. | |
329 for (int channel_index = 0; channel_index < audio_parameters_.channels(); | |
330 ++channel_index) { | |
331 media::DeinterleaveAudioChannel(memory, | |
332 audio_data_[channel_index], | |
333 audio_parameters_.channels(), | |
334 channel_index, | |
335 bytes_per_sample, | |
336 number_of_frames); | |
337 } | |
338 | |
339 // Deliver captured data to the client in floating point format | |
340 // and update the audio-delay measurement. | |
341 capture_callback_->Capture(audio_data_, number_of_frames, | |
342 audio_delay_milliseconds, volume); | |
343 } | |
OLD | NEW |