audio_input_controller.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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 "media/audio/audio_input_controller.h"
6
7#include "base/bind.h"
8#include "base/threading/thread_restrictions.h"
9#include "media/base/limits.h"
10#include "media/base/scoped_histogram_timer.h"
11#include "media/base/user_input_monitor.h"
12
13namespace {
14const int kMaxInputChannels = 2;
15
16// TODO(henrika): remove usage of timers and add support for proper
17// notification of when the input device is removed.  This was originally added
18// to resolve http://crbug.com/79936 for Windows platforms.  This then caused
19// breakage (very hard to repro bugs!) on other platforms: See
20// http://crbug.com/226327 and http://crbug.com/230972.
21const int kTimerResetIntervalSeconds = 1;
22// We have received reports that the timer can be too trigger happy on some
23// Mac devices and the initial timer interval has therefore been increased
24// from 1 second to 5 seconds.
25const int kTimerInitialIntervalSeconds = 5;
26}
27
28namespace media {
29
30// static
31AudioInputController::Factory* AudioInputController::factory_ = NULL;
32
33AudioInputController::AudioInputController(EventHandler* handler,
34                                           SyncWriter* sync_writer,
35                                           UserInputMonitor* user_input_monitor)
36    : creator_task_runner_(base::MessageLoopProxy::current()),
37      handler_(handler),
38      stream_(NULL),
39      data_is_active_(false),
40      state_(CLOSED),
41      sync_writer_(sync_writer),
42      max_volume_(0.0),
43      user_input_monitor_(user_input_monitor),
44      prev_key_down_count_(0) {
45  DCHECK(creator_task_runner_.get());
46}
47
48AudioInputController::~AudioInputController() {
49  DCHECK_EQ(state_, CLOSED);
50}
51
52// static
53scoped_refptr<AudioInputController> AudioInputController::Create(
54    AudioManager* audio_manager,
55    EventHandler* event_handler,
56    const AudioParameters& params,
57    const std::string& device_id,
58    UserInputMonitor* user_input_monitor) {
59  DCHECK(audio_manager);
60
61  if (!params.IsValid() || (params.channels() > kMaxInputChannels))
62    return NULL;
63
64  if (factory_) {
65    return factory_->Create(
66        audio_manager, event_handler, params, user_input_monitor);
67  }
68  scoped_refptr<AudioInputController> controller(
69      new AudioInputController(event_handler, NULL, user_input_monitor));
70
71  controller->task_runner_ = audio_manager->GetTaskRunner();
72
73  // Create and open a new audio input stream from the existing
74  // audio-device thread.
75  if (!controller->task_runner_->PostTask(FROM_HERE,
76          base::Bind(&AudioInputController::DoCreate, controller,
77                     base::Unretained(audio_manager), params, device_id))) {
78    controller = NULL;
79  }
80
81  return controller;
82}
83
84// static
85scoped_refptr<AudioInputController> AudioInputController::CreateLowLatency(
86    AudioManager* audio_manager,
87    EventHandler* event_handler,
88    const AudioParameters& params,
89    const std::string& device_id,
90    SyncWriter* sync_writer,
91    UserInputMonitor* user_input_monitor) {
92  DCHECK(audio_manager);
93  DCHECK(sync_writer);
94
95  if (!params.IsValid() || (params.channels() > kMaxInputChannels))
96    return NULL;
97
98  // Create the AudioInputController object and ensure that it runs on
99  // the audio-manager thread.
100  scoped_refptr<AudioInputController> controller(
101      new AudioInputController(event_handler, sync_writer, user_input_monitor));
102  controller->task_runner_ = audio_manager->GetTaskRunner();
103
104  // Create and open a new audio input stream from the existing
105  // audio-device thread. Use the provided audio-input device.
106  if (!controller->task_runner_->PostTask(FROM_HERE,
107          base::Bind(&AudioInputController::DoCreate, controller,
108                     base::Unretained(audio_manager), params, device_id))) {
109    controller = NULL;
110  }
111
112  return controller;
113}
114
115// static
116scoped_refptr<AudioInputController> AudioInputController::CreateForStream(
117    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
118    EventHandler* event_handler,
119    AudioInputStream* stream,
120    SyncWriter* sync_writer,
121    UserInputMonitor* user_input_monitor) {
122  DCHECK(sync_writer);
123  DCHECK(stream);
124
125  // Create the AudioInputController object and ensure that it runs on
126  // the audio-manager thread.
127  scoped_refptr<AudioInputController> controller(
128      new AudioInputController(event_handler, sync_writer, user_input_monitor));
129  controller->task_runner_ = task_runner;
130
131  // TODO(miu): See TODO at top of file.  Until that's resolved, we need to
132  // disable the error auto-detection here (since the audio mirroring
133  // implementation will reliably report error and close events).  Note, of
134  // course, that we're assuming CreateForStream() has been called for the audio
135  // mirroring use case only.
136  if (!controller->task_runner_->PostTask(
137          FROM_HERE,
138          base::Bind(&AudioInputController::DoCreateForStream, controller,
139                     stream, false))) {
140    controller = NULL;
141  }
142
143  return controller;
144}
145
146void AudioInputController::Record() {
147  task_runner_->PostTask(FROM_HERE, base::Bind(
148      &AudioInputController::DoRecord, this));
149}
150
151void AudioInputController::Close(const base::Closure& closed_task) {
152  DCHECK(!closed_task.is_null());
153  DCHECK(creator_task_runner_->BelongsToCurrentThread());
154
155  task_runner_->PostTaskAndReply(
156      FROM_HERE, base::Bind(&AudioInputController::DoClose, this), closed_task);
157}
158
159void AudioInputController::SetVolume(double volume) {
160  task_runner_->PostTask(FROM_HERE, base::Bind(
161      &AudioInputController::DoSetVolume, this, volume));
162}
163
164void AudioInputController::SetAutomaticGainControl(bool enabled) {
165  task_runner_->PostTask(FROM_HERE, base::Bind(
166      &AudioInputController::DoSetAutomaticGainControl, this, enabled));
167}
168
169void AudioInputController::DoCreate(AudioManager* audio_manager,
170                                    const AudioParameters& params,
171                                    const std::string& device_id) {
172  DCHECK(task_runner_->BelongsToCurrentThread());
173  SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioInputController.CreateTime");
174  // TODO(miu): See TODO at top of file.  Until that's resolved, assume all
175  // platform audio input requires the |no_data_timer_| be used to auto-detect
176  // errors.  In reality, probably only Windows needs to be treated as
177  // unreliable here.
178  DoCreateForStream(audio_manager->MakeAudioInputStream(params, device_id),
179                    true);
180}
181
182void AudioInputController::DoCreateForStream(
183    AudioInputStream* stream_to_control, bool enable_nodata_timer) {
184  DCHECK(task_runner_->BelongsToCurrentThread());
185
186  DCHECK(!stream_);
187  stream_ = stream_to_control;
188
189  if (!stream_) {
190    handler_->OnError(this, STREAM_CREATE_ERROR);
191    return;
192  }
193
194  if (stream_ && !stream_->Open()) {
195    stream_->Close();
196    stream_ = NULL;
197    handler_->OnError(this, STREAM_OPEN_ERROR);
198    return;
199  }
200
201  DCHECK(!no_data_timer_.get());
202  if (enable_nodata_timer) {
203    // Create the data timer which will call DoCheckForNoData(). The timer
204    // is started in DoRecord() and restarted in each DoCheckForNoData()
205    // callback.
206    no_data_timer_.reset(new base::Timer(
207        FROM_HERE, base::TimeDelta::FromSeconds(kTimerInitialIntervalSeconds),
208        base::Bind(&AudioInputController::DoCheckForNoData,
209                   base::Unretained(this)), false));
210  } else {
211    DVLOG(1) << "Disabled: timer check for no data.";
212  }
213
214  state_ = CREATED;
215  handler_->OnCreated(this);
216
217  if (user_input_monitor_) {
218    user_input_monitor_->EnableKeyPressMonitoring();
219    prev_key_down_count_ = user_input_monitor_->GetKeyPressCount();
220  }
221}
222
223void AudioInputController::DoRecord() {
224  DCHECK(task_runner_->BelongsToCurrentThread());
225  SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioInputController.RecordTime");
226
227  if (state_ != CREATED)
228    return;
229
230  {
231    base::AutoLock auto_lock(lock_);
232    state_ = RECORDING;
233  }
234
235  if (no_data_timer_) {
236    // Start the data timer. Once |kTimerResetIntervalSeconds| have passed,
237    // a callback to DoCheckForNoData() is made.
238    no_data_timer_->Reset();
239  }
240
241  stream_->Start(this);
242  handler_->OnRecording(this);
243}
244
245void AudioInputController::DoClose() {
246  DCHECK(task_runner_->BelongsToCurrentThread());
247  SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioInputController.CloseTime");
248
249  if (state_ == CLOSED)
250    return;
251
252  // Delete the timer on the same thread that created it.
253  no_data_timer_.reset();
254
255  DoStopCloseAndClearStream(NULL);
256  SetDataIsActive(false);
257
258  if (LowLatencyMode())
259    sync_writer_->Close();
260
261  if (user_input_monitor_)
262    user_input_monitor_->DisableKeyPressMonitoring();
263
264  state_ = CLOSED;
265}
266
267void AudioInputController::DoReportError() {
268  DCHECK(task_runner_->BelongsToCurrentThread());
269  handler_->OnError(this, STREAM_ERROR);
270}
271
272void AudioInputController::DoSetVolume(double volume) {
273  DCHECK(task_runner_->BelongsToCurrentThread());
274  DCHECK_GE(volume, 0);
275  DCHECK_LE(volume, 1.0);
276
277  if (state_ != CREATED && state_ != RECORDING)
278    return;
279
280  // Only ask for the maximum volume at first call and use cached value
281  // for remaining function calls.
282  if (!max_volume_) {
283    max_volume_ = stream_->GetMaxVolume();
284  }
285
286  if (max_volume_ == 0.0) {
287    DLOG(WARNING) << "Failed to access input volume control";
288    return;
289  }
290
291  // Set the stream volume and scale to a range matched to the platform.
292  stream_->SetVolume(max_volume_ * volume);
293}
294
295void AudioInputController::DoSetAutomaticGainControl(bool enabled) {
296  DCHECK(task_runner_->BelongsToCurrentThread());
297  DCHECK_NE(state_, RECORDING);
298
299  // Ensure that the AGC state only can be modified before streaming starts.
300  if (state_ != CREATED)
301    return;
302
303  stream_->SetAutomaticGainControl(enabled);
304}
305
306void AudioInputController::DoCheckForNoData() {
307  DCHECK(task_runner_->BelongsToCurrentThread());
308
309  if (!GetDataIsActive()) {
310    // The data-is-active marker will be false only if it has been more than
311    // one second since a data packet was recorded. This can happen if a
312    // capture device has been removed or disabled.
313    handler_->OnError(this, NO_DATA_ERROR);
314    return;
315  }
316
317  // Mark data as non-active. The flag will be re-enabled in OnData() each
318  // time a data packet is received. Hence, under normal conditions, the
319  // flag will only be disabled during a very short period.
320  SetDataIsActive(false);
321
322  // Restart the timer to ensure that we check the flag again in
323  // |kTimerResetIntervalSeconds|.
324  no_data_timer_->Start(
325      FROM_HERE, base::TimeDelta::FromSeconds(kTimerResetIntervalSeconds),
326      base::Bind(&AudioInputController::DoCheckForNoData,
327      base::Unretained(this)));
328}
329
330void AudioInputController::OnData(AudioInputStream* stream,
331                                  const uint8* data,
332                                  uint32 size,
333                                  uint32 hardware_delay_bytes,
334                                  double volume) {
335  {
336    base::AutoLock auto_lock(lock_);
337    if (state_ != RECORDING)
338      return;
339  }
340
341  bool key_pressed = false;
342  if (user_input_monitor_) {
343    size_t current_count = user_input_monitor_->GetKeyPressCount();
344    key_pressed = current_count != prev_key_down_count_;
345    prev_key_down_count_ = current_count;
346    DVLOG_IF(6, key_pressed) << "Detected keypress.";
347  }
348
349  // Mark data as active to ensure that the periodic calls to
350  // DoCheckForNoData() does not report an error to the event handler.
351  SetDataIsActive(true);
352
353  // Use SyncSocket if we are in a low-latency mode.
354  if (LowLatencyMode()) {
355    sync_writer_->Write(data, size, volume, key_pressed);
356    sync_writer_->UpdateRecordedBytes(hardware_delay_bytes);
357    return;
358  }
359
360  handler_->OnData(this, data, size);
361}
362
363void AudioInputController::OnError(AudioInputStream* stream) {
364  // Handle error on the audio-manager thread.
365  task_runner_->PostTask(FROM_HERE, base::Bind(
366      &AudioInputController::DoReportError, this));
367}
368
369void AudioInputController::DoStopCloseAndClearStream(
370    base::WaitableEvent* done) {
371  DCHECK(task_runner_->BelongsToCurrentThread());
372
373  // Allow calling unconditionally and bail if we don't have a stream to close.
374  if (stream_ != NULL) {
375    stream_->Stop();
376    stream_->Close();
377    stream_ = NULL;
378  }
379
380  // Should be last in the method, do not touch "this" from here on.
381  if (done != NULL)
382    done->Signal();
383}
384
385void AudioInputController::SetDataIsActive(bool enabled) {
386  base::subtle::Release_Store(&data_is_active_, enabled);
387}
388
389bool AudioInputController::GetDataIsActive() {
390  return (base::subtle::Acquire_Load(&data_is_active_) != false);
391}
392
393}  // namespace media
394