Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(968)

Side by Side Diff: media/audio/win/wavein_input_win.cc

Issue 2646423005: Remove the wave based audio capture implementation for Windows (Closed)
Patch Set: Minor cleanup + documentation Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « media/audio/win/wavein_input_win.h ('k') | tools/metrics/histograms/histograms.xml » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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/win/wavein_input_win.h"
6
7 #include "base/logging.h"
8 #include "media/audio/audio_device_description.h"
9 #include "media/audio/audio_io.h"
10 #include "media/audio/win/audio_manager_win.h"
11 #include "media/audio/win/device_enumeration_win.h"
12 #include "media/base/audio_bus.h"
13
14 namespace media {
15
16 // Our sound buffers are allocated once and kept in a linked list using the
17 // the WAVEHDR::dwUser variable. The last buffer points to the first buffer.
18 static WAVEHDR* GetNextBuffer(WAVEHDR* current) {
19 return reinterpret_cast<WAVEHDR*>(current->dwUser);
20 }
21
22 PCMWaveInAudioInputStream::PCMWaveInAudioInputStream(
23 AudioManagerWin* manager,
24 const AudioParameters& params,
25 int num_buffers,
26 const std::string& device_id)
27 : state_(kStateEmpty),
28 manager_(manager),
29 callback_(NULL),
30 num_buffers_(num_buffers),
31 channels_(params.channels()),
32 device_id_(device_id),
33 wavein_(NULL),
34 buffer_(NULL),
35 audio_bus_(media::AudioBus::Create(params)) {
36 DCHECK_GT(num_buffers_, 0);
37 format_.wFormatTag = WAVE_FORMAT_PCM;
38 format_.nChannels = params.channels() > 2 ? 2 : params.channels();
39 format_.nSamplesPerSec = params.sample_rate();
40 format_.wBitsPerSample = params.bits_per_sample();
41 format_.cbSize = 0;
42 format_.nBlockAlign = (format_.nChannels * format_.wBitsPerSample) / 8;
43 format_.nAvgBytesPerSec = format_.nBlockAlign * format_.nSamplesPerSec;
44 buffer_size_ = params.frames_per_buffer() * format_.nBlockAlign;
45 // If we don't have a packet size we use 100ms.
46 if (!buffer_size_)
47 buffer_size_ = format_.nAvgBytesPerSec / 10;
48 // The event is auto-reset.
49 stopped_event_.Set(::CreateEventW(NULL, FALSE, FALSE, NULL));
50 }
51
52 PCMWaveInAudioInputStream::~PCMWaveInAudioInputStream() {
53 DCHECK(NULL == wavein_);
54 }
55
56 bool PCMWaveInAudioInputStream::Open() {
57 DCHECK(thread_checker_.CalledOnValidThread());
58 if (state_ != kStateEmpty)
59 return false;
60 if (num_buffers_ < 2 || num_buffers_ > 10)
61 return false;
62
63 // Convert the stored device id string into an unsigned integer
64 // corresponding to the selected device.
65 UINT device_id = WAVE_MAPPER;
66 if (!GetDeviceId(&device_id)) {
67 return false;
68 }
69
70 // Open the specified input device for recording.
71 MMRESULT result = MMSYSERR_NOERROR;
72 result = ::waveInOpen(&wavein_, device_id, &format_,
73 reinterpret_cast<DWORD_PTR>(WaveCallback),
74 reinterpret_cast<DWORD_PTR>(this),
75 CALLBACK_FUNCTION);
76 if (result != MMSYSERR_NOERROR)
77 return false;
78
79 SetupBuffers();
80 state_ = kStateReady;
81 return true;
82 }
83
84 void PCMWaveInAudioInputStream::SetupBuffers() {
85 WAVEHDR* last = NULL;
86 WAVEHDR* first = NULL;
87 for (int ix = 0; ix != num_buffers_; ++ix) {
88 uint32_t sz = sizeof(WAVEHDR) + buffer_size_;
89 buffer_ = reinterpret_cast<WAVEHDR*>(new char[sz]);
90 buffer_->lpData = reinterpret_cast<char*>(buffer_) + sizeof(WAVEHDR);
91 buffer_->dwBufferLength = buffer_size_;
92 buffer_->dwBytesRecorded = 0;
93 buffer_->dwUser = reinterpret_cast<DWORD_PTR>(last);
94 buffer_->dwFlags = WHDR_DONE;
95 buffer_->dwLoops = 0;
96 if (ix == 0)
97 first = buffer_;
98 last = buffer_;
99 ::waveInPrepareHeader(wavein_, buffer_, sizeof(WAVEHDR));
100 }
101 // Fix the first buffer to point to the last one.
102 first->dwUser = reinterpret_cast<DWORD_PTR>(last);
103 }
104
105 void PCMWaveInAudioInputStream::FreeBuffers() {
106 WAVEHDR* current = buffer_;
107 for (int ix = 0; ix != num_buffers_; ++ix) {
108 WAVEHDR* next = GetNextBuffer(current);
109 if (current->dwFlags & WHDR_PREPARED)
110 ::waveInUnprepareHeader(wavein_, current, sizeof(WAVEHDR));
111 delete[] reinterpret_cast<char*>(current);
112 current = next;
113 }
114 buffer_ = NULL;
115 }
116
117 void PCMWaveInAudioInputStream::Start(AudioInputCallback* callback) {
118 DCHECK(thread_checker_.CalledOnValidThread());
119 if (state_ != kStateReady)
120 return;
121
122 DCHECK(!callback_);
123 callback_ = callback;
124 state_ = kStateRecording;
125
126 WAVEHDR* buffer = buffer_;
127 for (int ix = 0; ix != num_buffers_; ++ix) {
128 QueueNextPacket(buffer);
129 buffer = GetNextBuffer(buffer);
130 }
131 buffer = buffer_;
132
133 MMRESULT result = ::waveInStart(wavein_);
134 if (result != MMSYSERR_NOERROR) {
135 HandleError(result);
136 state_ = kStateReady;
137 callback_ = NULL;
138 }
139 }
140
141 // Stopping is tricky. First, no buffer should be locked by the audio driver
142 // or else the waveInReset() will deadlock and secondly, the callback should
143 // not be inside the AudioInputCallback's OnData because waveInReset()
144 // forcefully kills the callback thread.
145 void PCMWaveInAudioInputStream::Stop() {
146 DVLOG(1) << "PCMWaveInAudioInputStream::Stop()";
147 DCHECK(thread_checker_.CalledOnValidThread());
148 if (state_ != kStateRecording)
149 return;
150
151 bool already_stopped = false;
152 {
153 // Tell the callback that we're stopping.
154 // As a result, |stopped_event_| will be signaled in callback method.
155 base::AutoLock auto_lock(lock_);
156 already_stopped = (callback_ == NULL);
157 callback_ = NULL;
158 }
159
160 if (already_stopped)
161 return;
162
163 // Wait for the callback to finish, it will signal us when ready to be reset.
164 DWORD wait = ::WaitForSingleObject(stopped_event_.Get(), INFINITE);
165 DCHECK_EQ(wait, WAIT_OBJECT_0);
166
167 // Stop input and reset the current position to zero for |wavein_|.
168 // All pending buffers are marked as done and returned to the application.
169 MMRESULT res = ::waveInReset(wavein_);
170 DCHECK_EQ(res, static_cast<MMRESULT>(MMSYSERR_NOERROR));
171
172 state_ = kStateReady;
173 }
174
175 void PCMWaveInAudioInputStream::Close() {
176 DVLOG(1) << "PCMWaveInAudioInputStream::Close()";
177 DCHECK(thread_checker_.CalledOnValidThread());
178
179 // We should not call Close() while recording. Catch it with DCHECK and
180 // implement auto-stop just in case.
181 DCHECK_NE(state_, kStateRecording);
182 Stop();
183
184 if (wavein_) {
185 FreeBuffers();
186
187 // waveInClose() generates a WIM_CLOSE callback. In case Start() was never
188 // called, force a reset to ensure close succeeds.
189 MMRESULT res = ::waveInReset(wavein_);
190 DCHECK_EQ(res, static_cast<MMRESULT>(MMSYSERR_NOERROR));
191 res = ::waveInClose(wavein_);
192 DCHECK_EQ(res, static_cast<MMRESULT>(MMSYSERR_NOERROR));
193 state_ = kStateClosed;
194 wavein_ = NULL;
195 }
196
197 // Tell the audio manager that we have been released. This can result in
198 // the manager destroying us in-place so this needs to be the last thing
199 // we do on this function.
200 manager_->ReleaseInputStream(this);
201 }
202
203 double PCMWaveInAudioInputStream::GetMaxVolume() {
204 // TODO(henrika): Add volume support using the Audio Mixer API.
205 return 0.0;
206 }
207
208 void PCMWaveInAudioInputStream::SetVolume(double volume) {
209 // TODO(henrika): Add volume support using the Audio Mixer API.
210 }
211
212 double PCMWaveInAudioInputStream::GetVolume() {
213 // TODO(henrika): Add volume support using the Audio Mixer API.
214 return 0.0;
215 }
216
217 bool PCMWaveInAudioInputStream::SetAutomaticGainControl(bool enabled) {
218 // TODO(henrika): Add AGC support when volume control has been added.
219 NOTIMPLEMENTED();
220 return false;
221 }
222
223 bool PCMWaveInAudioInputStream::GetAutomaticGainControl() {
224 // TODO(henrika): Add AGC support when volume control has been added.
225 NOTIMPLEMENTED();
226 return false;
227 }
228
229 bool PCMWaveInAudioInputStream::IsMuted() {
230 NOTIMPLEMENTED();
231 return false;
232 }
233
234 void PCMWaveInAudioInputStream::HandleError(MMRESULT error) {
235 DLOG(WARNING) << "PCMWaveInAudio error " << error;
236 if (callback_)
237 callback_->OnError(this);
238 }
239
240 void PCMWaveInAudioInputStream::QueueNextPacket(WAVEHDR *buffer) {
241 MMRESULT res = ::waveInAddBuffer(wavein_, buffer, sizeof(WAVEHDR));
242 if (res != MMSYSERR_NOERROR)
243 HandleError(res);
244 }
245
246 bool PCMWaveInAudioInputStream::GetDeviceId(UINT* device_index) {
247 // Deliver the default input device id (WAVE_MAPPER) if the default
248 // device has been selected.
249 if (device_id_ == AudioDeviceDescription::kDefaultDeviceId) {
250 *device_index = WAVE_MAPPER;
251 return true;
252 }
253
254 // Get list of all available and active devices.
255 AudioDeviceNames device_names;
256 if (!media::GetInputDeviceNamesWinXP(&device_names))
257 return false;
258
259 if (device_names.empty())
260 return false;
261
262 // Search the full list of devices and compare with the specified
263 // device id which was specified in the constructor. Stop comparing
264 // when a match is found and return the corresponding index.
265 UINT index = 0;
266 bool found_device = false;
267 AudioDeviceNames::const_iterator it = device_names.begin();
268 while (it != device_names.end()) {
269 if (it->unique_id.compare(device_id_) == 0) {
270 *device_index = index;
271 found_device = true;
272 break;
273 }
274 ++index;
275 ++it;
276 }
277
278 return found_device;
279 }
280
281 // Windows calls us back in this function when some events happen. Most notably
282 // when it has an audio buffer with recorded data.
283 void PCMWaveInAudioInputStream::WaveCallback(HWAVEIN hwi, UINT msg,
284 DWORD_PTR instance,
285 DWORD_PTR param1, DWORD_PTR) {
286 PCMWaveInAudioInputStream* obj =
287 reinterpret_cast<PCMWaveInAudioInputStream*>(instance);
288
289 // The lock ensures that Stop() can't be called during a callback.
290 base::AutoLock auto_lock(obj->lock_);
291
292 if (msg == WIM_DATA) {
293 // The WIM_DATA message is sent when waveform-audio data is present in
294 // the input buffer and the buffer is being returned to the application.
295 // The message can be sent when the buffer is full or after the
296 // waveInReset function is called.
297 if (obj->callback_) {
298 // TODO(henrika): the |volume| parameter is always set to zero since
299 // there is currently no support for controlling the microphone volume
300 // level.
301 WAVEHDR* buffer = reinterpret_cast<WAVEHDR*>(param1);
302 obj->audio_bus_->FromInterleaved(
303 reinterpret_cast<uint8_t*>(buffer->lpData), obj->audio_bus_->frames(),
304 obj->format_.wBitsPerSample / 8);
305 obj->callback_->OnData(
306 obj, obj->audio_bus_.get(), buffer->dwBytesRecorded, 0.0);
307
308 // Queue the finished buffer back with the audio driver. Since we are
309 // reusing the same buffers we can get away without calling
310 // waveInPrepareHeader.
311 obj->QueueNextPacket(buffer);
312 } else {
313 // Main thread has called Stop() and set |callback_| to NULL and is
314 // now waiting to issue waveInReset which will kill this thread.
315 // We should not call AudioSourceCallback code anymore.
316 ::SetEvent(obj->stopped_event_.Get());
317 }
318 } else if (msg == WIM_CLOSE) {
319 // Intentionaly no-op for now.
320 } else if (msg == WIM_OPEN) {
321 // Intentionaly no-op for now.
322 }
323 }
324
325 } // namespace media
OLDNEW
« no previous file with comments | « media/audio/win/wavein_input_win.h ('k') | tools/metrics/histograms/histograms.xml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698