- add sources.
[platform/framework/web/crosswalk.git] / src / media / audio / audio_input_device.cc
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/message_loop.h"
10 #include "base/threading/thread_restrictions.h"
11 #include "base/time/time.h"
12 #include "media/audio/audio_manager_base.h"
13 #include "media/base/audio_bus.h"
14
15 namespace 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.
21 static 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().
26 class 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
48 AudioInputDevice::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
67 void 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
78 void 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
85 void 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
98 void 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
108 void 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
115 void 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(new AudioInputDevice::AudioThreadCallback(
142       audio_parameters_, handle, length, total_segments, callback_));
143   audio_thread_.Start(
144       audio_callback_.get(), socket_handle, "AudioInputDevice", false);
145
146   state_ = RECORDING;
147   ipc_->RecordStream();
148 }
149
150 void AudioInputDevice::OnVolume(double volume) {
151   NOTIMPLEMENTED();
152 }
153
154 void 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
188 void AudioInputDevice::OnIPCClosed() {
189   DCHECK(message_loop()->BelongsToCurrentThread());
190   state_ = IPC_CLOSED;
191   ipc_.reset();
192 }
193
194 AudioInputDevice::~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
200 void 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
217 void 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
242 void AudioInputDevice::SetVolumeOnIOThread(double volume) {
243   DCHECK(message_loop()->BelongsToCurrentThread());
244   if (state_ >= CREATING_STREAM)
245     ipc_->SetVolume(volume);
246 }
247
248 void 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
261 void AudioInputDevice::WillDestroyCurrentMessageLoop() {
262   LOG(ERROR) << "IO loop going away before the input device has been stopped";
263   ShutDownOnIOThread();
264 }
265
266 // AudioInputDevice::AudioThreadCallback
267 AudioInputDevice::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
280 AudioInputDevice::AudioThreadCallback::~AudioThreadCallback() {
281 }
282
283 void AudioInputDevice::AudioThreadCallback::MapSharedMemory() {
284   shared_memory_.Map(memory_length_);
285 }
286
287 void 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   // Usually this will be equal but in the case of low sample rate (e.g. 8kHz,
295   // the buffer may be bigger (on mac at least)).
296   DCHECK_GE(buffer->params.size,
297             segment_length_ - sizeof(AudioInputBufferParameters));
298   double volume = buffer->params.volume;
299   bool key_pressed = buffer->params.key_pressed;
300
301   int audio_delay_milliseconds = pending_data / bytes_per_ms_;
302   int16* memory = reinterpret_cast<int16*>(&buffer->audio[0]);
303   const int bytes_per_sample = sizeof(memory[0]);
304
305   if (++current_segment_id_ >= total_segments_)
306     current_segment_id_ = 0;
307
308   // Deinterleave each channel and convert to 32-bit floating-point
309   // with nominal range -1.0 -> +1.0.
310   audio_bus_->FromInterleaved(memory, audio_bus_->frames(), bytes_per_sample);
311
312   // Deliver captured data to the client in floating point format
313   // and update the audio-delay measurement.
314   capture_callback_->Capture(
315       audio_bus_.get(), audio_delay_milliseconds, volume, key_pressed);
316 }
317
318 }  // namespace media