audio_input_device.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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/basictypes.h"
8#include "base/bind.h"
9#include "base/message_loop.h"
10#include "base/threading/thread_restrictions.h"
11#include "base/time.h"
12#include "media/audio/audio_manager_base.h"
13#include "media/base/audio_bus.h"
14
15namespace media {
16
17// The number of shared memory buffer segments indicated to browser process
18// in order to avoid data overwriting. This number can be any positive number,
19// dependent how fast the renderer process can pick up captured data from
20// shared memory.
21static const int kRequestedSharedMemoryCount = 10;
22
23// Takes care of invoking the capture callback on the audio thread.
24// An instance of this class is created for each capture stream in
25// OnLowLatencyCreated().
26class AudioInputDevice::AudioThreadCallback
27    : public AudioDeviceThread::Callback {
28 public:
29  AudioThreadCallback(const AudioParameters& audio_parameters,
30                      base::SharedMemoryHandle memory,
31                      int memory_length,
32                      int total_segments,
33                      CaptureCallback* capture_callback);
34  virtual ~AudioThreadCallback();
35
36  virtual void MapSharedMemory() OVERRIDE;
37
38  // Called whenever we receive notifications about pending data.
39  virtual void Process(int pending_data) OVERRIDE;
40
41 private:
42  int current_segment_id_;
43  CaptureCallback* capture_callback_;
44  scoped_ptr<AudioBus> audio_bus_;
45  DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback);
46};
47
48AudioInputDevice::AudioInputDevice(
49    scoped_ptr<AudioInputIPC> ipc,
50    const scoped_refptr<base::MessageLoopProxy>& io_loop)
51    : ScopedLoopObserver(io_loop),
52      callback_(NULL),
53      ipc_(ipc.Pass()),
54      state_(IDLE),
55      session_id_(0),
56      agc_is_enabled_(false),
57      stopping_hack_(false) {
58  CHECK(ipc_);
59
60  // The correctness of the code depends on the relative values assigned in the
61  // State enum.
62  COMPILE_ASSERT(IPC_CLOSED < IDLE, invalid_enum_value_assignment_0);
63  COMPILE_ASSERT(IDLE < CREATING_STREAM, invalid_enum_value_assignment_1);
64  COMPILE_ASSERT(CREATING_STREAM < RECORDING, invalid_enum_value_assignment_2);
65}
66
67void AudioInputDevice::Initialize(const AudioParameters& params,
68                                  CaptureCallback* callback,
69                                  int session_id) {
70  DCHECK(params.IsValid());
71  DCHECK(!callback_);
72  DCHECK_EQ(0, session_id_);
73  audio_parameters_ = params;
74  callback_ = callback;
75  session_id_ = session_id;
76}
77
78void AudioInputDevice::Start() {
79  DCHECK(callback_) << "Initialize hasn't been called";
80  DVLOG(1) << "Start()";
81  message_loop()->PostTask(FROM_HERE,
82      base::Bind(&AudioInputDevice::StartUpOnIOThread, this));
83}
84
85void AudioInputDevice::Stop() {
86  DVLOG(1) << "Stop()";
87
88  {
89    base::AutoLock auto_lock(audio_thread_lock_);
90    audio_thread_.Stop(base::MessageLoop::current());
91    stopping_hack_ = true;
92  }
93
94  message_loop()->PostTask(FROM_HERE,
95      base::Bind(&AudioInputDevice::ShutDownOnIOThread, this));
96}
97
98void AudioInputDevice::SetVolume(double volume) {
99  if (volume < 0 || volume > 1.0) {
100    DLOG(ERROR) << "Invalid volume value specified";
101    return;
102  }
103
104  message_loop()->PostTask(FROM_HERE,
105      base::Bind(&AudioInputDevice::SetVolumeOnIOThread, this, volume));
106}
107
108void AudioInputDevice::SetAutomaticGainControl(bool enabled) {
109  DVLOG(1) << "SetAutomaticGainControl(enabled=" << enabled << ")";
110  message_loop()->PostTask(FROM_HERE,
111      base::Bind(&AudioInputDevice::SetAutomaticGainControlOnIOThread,
112          this, enabled));
113}
114
115void AudioInputDevice::OnStreamCreated(
116    base::SharedMemoryHandle handle,
117    base::SyncSocket::Handle socket_handle,
118    int length,
119    int total_segments) {
120  DCHECK(message_loop()->BelongsToCurrentThread());
121#if defined(OS_WIN)
122  DCHECK(handle);
123  DCHECK(socket_handle);
124#else
125  DCHECK_GE(handle.fd, 0);
126  DCHECK_GE(socket_handle, 0);
127#endif
128  DCHECK_GT(length, 0);
129
130  if (state_ != CREATING_STREAM)
131    return;
132
133  base::AutoLock auto_lock(audio_thread_lock_);
134  // TODO(miu): See TODO in OnStreamCreated method for AudioOutputDevice.
135  // Interface changes need to be made; likely, after AudioInputDevice is merged
136  // into AudioOutputDevice (http://crbug.com/179597).
137  if (stopping_hack_)
138    return;
139
140  DCHECK(audio_thread_.IsStopped());
141  audio_callback_.reset(
142      new AudioInputDevice::AudioThreadCallback(
143          audio_parameters_, handle, length, total_segments, callback_));
144  audio_thread_.Start(audio_callback_.get(), socket_handle, "AudioInputDevice");
145
146  state_ = RECORDING;
147  ipc_->RecordStream();
148}
149
150void AudioInputDevice::OnVolume(double volume) {
151  NOTIMPLEMENTED();
152}
153
154void AudioInputDevice::OnStateChanged(
155    AudioInputIPCDelegate::State state) {
156  DCHECK(message_loop()->BelongsToCurrentThread());
157
158  // Do nothing if the stream has been closed.
159  if (state_ < CREATING_STREAM)
160    return;
161
162  // TODO(miu): Clean-up inconsistent and incomplete handling here.
163  // http://crbug.com/180640
164  switch (state) {
165    case AudioInputIPCDelegate::kStopped:
166      ShutDownOnIOThread();
167      break;
168    case AudioInputIPCDelegate::kRecording:
169      NOTIMPLEMENTED();
170      break;
171    case AudioInputIPCDelegate::kError:
172      DLOG(WARNING) << "AudioInputDevice::OnStateChanged(kError)";
173      // Don't dereference the callback object if the audio thread
174      // is stopped or stopping.  That could mean that the callback
175      // object has been deleted.
176      // TODO(tommi): Add an explicit contract for clearing the callback
177      // object.  Possibly require calling Initialize again or provide
178      // a callback object via Start() and clear it in Stop().
179      if (!audio_thread_.IsStopped())
180        callback_->OnCaptureError();
181      break;
182    default:
183      NOTREACHED();
184      break;
185  }
186}
187
188void AudioInputDevice::OnIPCClosed() {
189  DCHECK(message_loop()->BelongsToCurrentThread());
190  state_ = IPC_CLOSED;
191  ipc_.reset();
192}
193
194AudioInputDevice::~AudioInputDevice() {
195  // TODO(henrika): The current design requires that the user calls
196  // Stop before deleting this class.
197  DCHECK(audio_thread_.IsStopped());
198}
199
200void AudioInputDevice::StartUpOnIOThread() {
201  DCHECK(message_loop()->BelongsToCurrentThread());
202
203  // Make sure we don't call Start() more than once.
204  if (state_ != IDLE)
205    return;
206
207  if (session_id_ <= 0) {
208    DLOG(WARNING) << "Invalid session id for the input stream " << session_id_;
209    return;
210  }
211
212  state_ = CREATING_STREAM;
213  ipc_->CreateStream(this, session_id_, audio_parameters_,
214                     agc_is_enabled_, kRequestedSharedMemoryCount);
215}
216
217void AudioInputDevice::ShutDownOnIOThread() {
218  DCHECK(message_loop()->BelongsToCurrentThread());
219
220  // Close the stream, if we haven't already.
221  if (state_ >= CREATING_STREAM) {
222    ipc_->CloseStream();
223    state_ = IDLE;
224    agc_is_enabled_ = false;
225  }
226
227  // We can run into an issue where ShutDownOnIOThread is called right after
228  // OnStreamCreated is called in cases where Start/Stop are called before we
229  // get the OnStreamCreated callback.  To handle that corner case, we call
230  // Stop(). In most cases, the thread will already be stopped.
231  //
232  // Another situation is when the IO thread goes away before Stop() is called
233  // in which case, we cannot use the message loop to close the thread handle
234  // and can't not rely on the main thread existing either.
235  base::AutoLock auto_lock_(audio_thread_lock_);
236  base::ThreadRestrictions::ScopedAllowIO allow_io;
237  audio_thread_.Stop(NULL);
238  audio_callback_.reset();
239  stopping_hack_ = false;
240}
241
242void AudioInputDevice::SetVolumeOnIOThread(double volume) {
243  DCHECK(message_loop()->BelongsToCurrentThread());
244  if (state_ >= CREATING_STREAM)
245    ipc_->SetVolume(volume);
246}
247
248void AudioInputDevice::SetAutomaticGainControlOnIOThread(bool enabled) {
249  DCHECK(message_loop()->BelongsToCurrentThread());
250
251  if (state_ >= CREATING_STREAM) {
252    DLOG(WARNING) << "The AGC state can not be modified after starting.";
253    return;
254  }
255
256  // We simply store the new AGC setting here. This value will be used when
257  // a new stream is initialized and by GetAutomaticGainControl().
258  agc_is_enabled_ = enabled;
259}
260
261void AudioInputDevice::WillDestroyCurrentMessageLoop() {
262  LOG(ERROR) << "IO loop going away before the input device has been stopped";
263  ShutDownOnIOThread();
264}
265
266// AudioInputDevice::AudioThreadCallback
267AudioInputDevice::AudioThreadCallback::AudioThreadCallback(
268    const AudioParameters& audio_parameters,
269    base::SharedMemoryHandle memory,
270    int memory_length,
271    int total_segments,
272    CaptureCallback* capture_callback)
273    : AudioDeviceThread::Callback(audio_parameters, memory, memory_length,
274                                  total_segments),
275      current_segment_id_(0),
276      capture_callback_(capture_callback) {
277  audio_bus_ = AudioBus::Create(audio_parameters_);
278}
279
280AudioInputDevice::AudioThreadCallback::~AudioThreadCallback() {
281}
282
283void AudioInputDevice::AudioThreadCallback::MapSharedMemory() {
284  shared_memory_.Map(memory_length_);
285}
286
287void AudioInputDevice::AudioThreadCallback::Process(int pending_data) {
288  // The shared memory represents parameters, size of the data buffer and the
289  // actual data buffer containing audio data. Map the memory into this
290  // structure and parse out parameters and the data area.
291  uint8* ptr = static_cast<uint8*>(shared_memory_.memory());
292  ptr += current_segment_id_ * segment_length_;
293  AudioInputBuffer* buffer = reinterpret_cast<AudioInputBuffer*>(ptr);
294  DCHECK_EQ(buffer->params.size,
295            segment_length_ - sizeof(AudioInputBufferParameters));
296  double volume = buffer->params.volume;
297
298  int audio_delay_milliseconds = pending_data / bytes_per_ms_;
299  int16* memory = reinterpret_cast<int16*>(&buffer->audio[0]);
300  const int bytes_per_sample = sizeof(memory[0]);
301
302  if (++current_segment_id_ >= total_segments_)
303    current_segment_id_ = 0;
304
305  // Deinterleave each channel and convert to 32-bit floating-point
306  // with nominal range -1.0 -> +1.0.
307  audio_bus_->FromInterleaved(memory, audio_bus_->frames(), bytes_per_sample);
308
309  // Deliver captured data to the client in floating point format
310  // and update the audio-delay measurement.
311  capture_callback_->Capture(audio_bus_.get(),
312                             audio_delay_milliseconds, volume);
313}
314
315}  // namespace media
316