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