audio_input_device.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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_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/base/audio_bus.h"
13
14namespace media {
15
16AudioInputDevice::CaptureCallback::~CaptureCallback() {}
17AudioInputDevice::CaptureEventHandler::~CaptureEventHandler() {}
18
19// Takes care of invoking the capture callback on the audio thread.
20// An instance of this class is created for each capture stream in
21// OnLowLatencyCreated().
22class AudioInputDevice::AudioThreadCallback
23    : public AudioDeviceThread::Callback {
24 public:
25  AudioThreadCallback(const AudioParameters& audio_parameters,
26                      base::SharedMemoryHandle memory,
27                      int memory_length,
28                      CaptureCallback* capture_callback);
29  virtual ~AudioThreadCallback();
30
31  virtual void MapSharedMemory() OVERRIDE;
32
33  // Called whenever we receive notifications about pending data.
34  virtual void Process(int pending_data) OVERRIDE;
35
36 private:
37  CaptureCallback* capture_callback_;
38  scoped_ptr<AudioBus> audio_bus_;
39  DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback);
40};
41
42AudioInputDevice::AudioInputDevice(
43    AudioInputIPC* ipc,
44    const scoped_refptr<base::MessageLoopProxy>& io_loop)
45    : ScopedLoopObserver(io_loop),
46      callback_(NULL),
47      event_handler_(NULL),
48      ipc_(ipc),
49      stream_id_(0),
50      session_id_(0),
51      pending_device_ready_(false),
52      agc_is_enabled_(false) {
53  CHECK(ipc_);
54}
55
56void AudioInputDevice::Initialize(const AudioParameters& params,
57                                  CaptureCallback* callback,
58                                  CaptureEventHandler* event_handler) {
59  DCHECK(!callback_);
60  DCHECK(!event_handler_);
61  audio_parameters_ = params;
62  callback_ = callback;
63  event_handler_ = event_handler;
64}
65
66void AudioInputDevice::SetDevice(int session_id) {
67  DVLOG(1) << "SetDevice (session_id=" << session_id << ")";
68  message_loop()->PostTask(FROM_HERE,
69      base::Bind(&AudioInputDevice::SetSessionIdOnIOThread, this, session_id));
70}
71
72void AudioInputDevice::Start() {
73  DVLOG(1) << "Start()";
74  message_loop()->PostTask(FROM_HERE,
75      base::Bind(&AudioInputDevice::InitializeOnIOThread, this));
76}
77
78void AudioInputDevice::Stop() {
79  DVLOG(1) << "Stop()";
80
81  {
82    base::AutoLock auto_lock(audio_thread_lock_);
83    audio_thread_.Stop(MessageLoop::current());
84  }
85
86  message_loop()->PostTask(FROM_HERE,
87      base::Bind(&AudioInputDevice::ShutDownOnIOThread, this));
88}
89
90void AudioInputDevice::SetVolume(double volume) {
91  if (volume < 0 || volume > 1.0) {
92    DLOG(ERROR) << "Invalid volume value specified";
93    return;
94  }
95
96  message_loop()->PostTask(FROM_HERE,
97      base::Bind(&AudioInputDevice::SetVolumeOnIOThread, this, volume));
98}
99
100void AudioInputDevice::SetAutomaticGainControl(bool enabled) {
101  DVLOG(1) << "SetAutomaticGainControl(enabled=" << enabled << ")";
102  message_loop()->PostTask(FROM_HERE,
103      base::Bind(&AudioInputDevice::SetAutomaticGainControlOnIOThread,
104          this, enabled));
105}
106
107void AudioInputDevice::OnStreamCreated(
108    base::SharedMemoryHandle handle,
109    base::SyncSocket::Handle socket_handle,
110    int length) {
111  DCHECK(message_loop()->BelongsToCurrentThread());
112#if defined(OS_WIN)
113  DCHECK(handle);
114  DCHECK(socket_handle);
115#else
116  DCHECK_GE(handle.fd, 0);
117  DCHECK_GE(socket_handle, 0);
118#endif
119  DCHECK(length);
120  DVLOG(1) << "OnStreamCreated (stream_id=" << stream_id_ << ")";
121
122  // We should only get this callback if stream_id_ is valid.  If it is not,
123  // the IPC layer should have closed the shared memory and socket handles
124  // for us and not invoked the callback.  The basic assertion is that when
125  // stream_id_ is 0 the AudioInputDevice instance is not registered as a
126  // delegate and hence it should not receive callbacks.
127  DCHECK(stream_id_);
128
129  base::AutoLock auto_lock(audio_thread_lock_);
130
131  DCHECK(audio_thread_.IsStopped());
132  audio_callback_.reset(
133      new AudioInputDevice::AudioThreadCallback(audio_parameters_, handle,
134                                                length, callback_));
135  audio_thread_.Start(audio_callback_.get(), socket_handle, "AudioInputDevice");
136
137  MessageLoop::current()->PostTask(FROM_HERE,
138      base::Bind(&AudioInputDevice::StartOnIOThread, this));
139}
140
141void AudioInputDevice::OnVolume(double volume) {
142  NOTIMPLEMENTED();
143}
144
145void AudioInputDevice::OnStateChanged(
146    AudioInputIPCDelegate::State state) {
147  DCHECK(message_loop()->BelongsToCurrentThread());
148
149  // Do nothing if the stream has been closed.
150  if (!stream_id_)
151    return;
152
153  switch (state) {
154    case AudioInputIPCDelegate::kStopped:
155      // TODO(xians): Should we just call ShutDownOnIOThread here instead?
156      ipc_->RemoveDelegate(stream_id_);
157
158      audio_thread_.Stop(MessageLoop::current());
159      audio_callback_.reset();
160
161      if (event_handler_)
162        event_handler_->OnDeviceStopped();
163
164      stream_id_ = 0;
165      pending_device_ready_ = false;
166      break;
167    case AudioInputIPCDelegate::kRecording:
168      NOTIMPLEMENTED();
169      break;
170    case AudioInputIPCDelegate::kError:
171      DLOG(WARNING) << "AudioInputDevice::OnStateChanged(kError)";
172      // Don't dereference the callback object if the audio thread
173      // is stopped or stopping.  That could mean that the callback
174      // object has been deleted.
175      // TODO(tommi): Add an explicit contract for clearing the callback
176      // object.  Possibly require calling Initialize again or provide
177      // a callback object via Start() and clear it in Stop().
178      if (!audio_thread_.IsStopped())
179        callback_->OnCaptureError();
180      break;
181    default:
182      NOTREACHED();
183      break;
184  }
185}
186
187void AudioInputDevice::OnDeviceReady(const std::string& device_id) {
188  DCHECK(message_loop()->BelongsToCurrentThread());
189  DVLOG(1) << "OnDeviceReady (device_id=" << device_id << ")";
190
191  // Takes care of the case when Stop() is called before OnDeviceReady().
192  if (!pending_device_ready_)
193    return;
194
195  // If AudioInputDeviceManager returns an empty string, it means no device
196  // is ready for start.
197  if (device_id.empty()) {
198    ipc_->RemoveDelegate(stream_id_);
199    stream_id_ = 0;
200  } else {
201    ipc_->CreateStream(stream_id_, audio_parameters_, device_id,
202                       agc_is_enabled_);
203  }
204
205  pending_device_ready_ = false;
206  // Notify the client that the device has been started.
207  if (event_handler_)
208    event_handler_->OnDeviceStarted(device_id);
209}
210
211void AudioInputDevice::OnIPCClosed() {
212  ipc_ = NULL;
213}
214
215AudioInputDevice::~AudioInputDevice() {
216  // TODO(henrika): The current design requires that the user calls
217  // Stop before deleting this class.
218  CHECK_EQ(0, stream_id_);
219}
220
221void AudioInputDevice::InitializeOnIOThread() {
222  DCHECK(message_loop()->BelongsToCurrentThread());
223  // Make sure we don't call Start() more than once.
224  DCHECK_EQ(0, stream_id_);
225  if (stream_id_)
226    return;
227
228  stream_id_ = ipc_->AddDelegate(this);
229  // If |session_id_| is not specified, it will directly create the stream;
230  // otherwise it will send a AudioInputHostMsg_StartDevice msg to the browser
231  // and create the stream when getting a OnDeviceReady() callback.
232  if (!session_id_) {
233    ipc_->CreateStream(stream_id_, audio_parameters_,
234        AudioManagerBase::kDefaultDeviceId, agc_is_enabled_);
235  } else {
236    ipc_->StartDevice(stream_id_, session_id_);
237    pending_device_ready_ = true;
238  }
239}
240
241void AudioInputDevice::SetSessionIdOnIOThread(int session_id) {
242  DCHECK(message_loop()->BelongsToCurrentThread());
243  session_id_ = session_id;
244}
245
246void AudioInputDevice::StartOnIOThread() {
247  DCHECK(message_loop()->BelongsToCurrentThread());
248  if (stream_id_)
249    ipc_->RecordStream(stream_id_);
250}
251
252void AudioInputDevice::ShutDownOnIOThread() {
253  DCHECK(message_loop()->BelongsToCurrentThread());
254  // NOTE: |completion| may be NULL.
255  // Make sure we don't call shutdown more than once.
256  if (stream_id_) {
257    if (ipc_) {
258      ipc_->CloseStream(stream_id_);
259      ipc_->RemoveDelegate(stream_id_);
260    }
261
262    stream_id_ = 0;
263    session_id_ = 0;
264    pending_device_ready_ = false;
265    agc_is_enabled_ = false;
266  }
267
268  // We can run into an issue where ShutDownOnIOThread is called right after
269  // OnStreamCreated is called in cases where Start/Stop are called before we
270  // get the OnStreamCreated callback.  To handle that corner case, we call
271  // Stop(). In most cases, the thread will already be stopped.
272  // Another situation is when the IO thread goes away before Stop() is called
273  // in which case, we cannot use the message loop to close the thread handle
274  // and can't not rely on the main thread existing either.
275  base::ThreadRestrictions::ScopedAllowIO allow_io;
276  audio_thread_.Stop(NULL);
277  audio_callback_.reset();
278}
279
280void AudioInputDevice::SetVolumeOnIOThread(double volume) {
281  DCHECK(message_loop()->BelongsToCurrentThread());
282  if (stream_id_)
283    ipc_->SetVolume(stream_id_, volume);
284}
285
286void AudioInputDevice::SetAutomaticGainControlOnIOThread(bool enabled) {
287  DCHECK(message_loop()->BelongsToCurrentThread());
288  DCHECK_EQ(0, stream_id_) <<
289      "The AGC state can not be modified while capturing is active.";
290  if (stream_id_)
291    return;
292
293  // We simply store the new AGC setting here. This value will be used when
294  // a new stream is initialized and by GetAutomaticGainControl().
295  agc_is_enabled_ = enabled;
296}
297
298void AudioInputDevice::WillDestroyCurrentMessageLoop() {
299  LOG(ERROR) << "IO loop going away before the input device has been stopped";
300  ShutDownOnIOThread();
301}
302
303// AudioInputDevice::AudioThreadCallback
304AudioInputDevice::AudioThreadCallback::AudioThreadCallback(
305    const AudioParameters& audio_parameters,
306    base::SharedMemoryHandle memory,
307    int memory_length,
308    CaptureCallback* capture_callback)
309    : AudioDeviceThread::Callback(audio_parameters, 0, memory, memory_length),
310      capture_callback_(capture_callback) {
311  audio_bus_ = AudioBus::Create(audio_parameters_);
312}
313
314AudioInputDevice::AudioThreadCallback::~AudioThreadCallback() {
315}
316
317void AudioInputDevice::AudioThreadCallback::MapSharedMemory() {
318  shared_memory_.Map(memory_length_);
319}
320
321void AudioInputDevice::AudioThreadCallback::Process(int pending_data) {
322  // The shared memory represents parameters, size of the data buffer and the
323  // actual data buffer containing audio data. Map the memory into this
324  // structure and parse out parameters and the data area.
325  AudioInputBuffer* buffer =
326      reinterpret_cast<AudioInputBuffer*>(shared_memory_.memory());
327  DCHECK_EQ(buffer->params.size,
328            memory_length_ - sizeof(AudioInputBufferParameters));
329  double volume = buffer->params.volume;
330
331  int audio_delay_milliseconds = pending_data / bytes_per_ms_;
332  int16* memory = reinterpret_cast<int16*>(&buffer->audio[0]);
333  const int bytes_per_sample = sizeof(memory[0]);
334
335  // Deinterleave each channel and convert to 32-bit floating-point
336  // with nominal range -1.0 -> +1.0.
337  audio_bus_->FromInterleaved(memory, audio_bus_->frames(), bytes_per_sample);
338
339  // Deliver captured data to the client in floating point format
340  // and update the audio-delay measurement.
341  capture_callback_->Capture(audio_bus_.get(),
342                             audio_delay_milliseconds, volume);
343}
344
345}  // namespace media
346