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