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