1 /*
2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "modules/audio_device/dummy/file_audio_device.h"
12
13 #include <string.h>
14
15 #include "rtc_base/checks.h"
16 #include "rtc_base/logging.h"
17 #include "rtc_base/platform_thread.h"
18 #include "rtc_base/time_utils.h"
19 #include "system_wrappers/include/sleep.h"
20
21 namespace webrtc {
22
23 const int kRecordingFixedSampleRate = 48000;
24 const size_t kRecordingNumChannels = 2;
25 const int kPlayoutFixedSampleRate = 48000;
26 const size_t kPlayoutNumChannels = 2;
27 const size_t kPlayoutBufferSize =
28 kPlayoutFixedSampleRate / 100 * kPlayoutNumChannels * 2;
29 const size_t kRecordingBufferSize =
30 kRecordingFixedSampleRate / 100 * kRecordingNumChannels * 2;
31
FileAudioDevice(const char * inputFilename,const char * outputFilename)32 FileAudioDevice::FileAudioDevice(const char* inputFilename,
33 const char* outputFilename)
34 : _ptrAudioBuffer(NULL),
35 _recordingBuffer(NULL),
36 _playoutBuffer(NULL),
37 _recordingFramesLeft(0),
38 _playoutFramesLeft(0),
39 _recordingBufferSizeIn10MS(0),
40 _recordingFramesIn10MS(0),
41 _playoutFramesIn10MS(0),
42 _playing(false),
43 _recording(false),
44 _lastCallPlayoutMillis(0),
45 _lastCallRecordMillis(0),
46 _outputFilename(outputFilename),
47 _inputFilename(inputFilename) {}
48
~FileAudioDevice()49 FileAudioDevice::~FileAudioDevice() {}
50
ActiveAudioLayer(AudioDeviceModule::AudioLayer & audioLayer) const51 int32_t FileAudioDevice::ActiveAudioLayer(
52 AudioDeviceModule::AudioLayer& audioLayer) const {
53 return -1;
54 }
55
Init()56 AudioDeviceGeneric::InitStatus FileAudioDevice::Init() {
57 return InitStatus::OK;
58 }
59
Terminate()60 int32_t FileAudioDevice::Terminate() {
61 return 0;
62 }
63
Initialized() const64 bool FileAudioDevice::Initialized() const {
65 return true;
66 }
67
PlayoutDevices()68 int16_t FileAudioDevice::PlayoutDevices() {
69 return 1;
70 }
71
RecordingDevices()72 int16_t FileAudioDevice::RecordingDevices() {
73 return 1;
74 }
75
PlayoutDeviceName(uint16_t index,char name[kAdmMaxDeviceNameSize],char guid[kAdmMaxGuidSize])76 int32_t FileAudioDevice::PlayoutDeviceName(uint16_t index,
77 char name[kAdmMaxDeviceNameSize],
78 char guid[kAdmMaxGuidSize]) {
79 const char* kName = "dummy_device";
80 const char* kGuid = "dummy_device_unique_id";
81 if (index < 1) {
82 memset(name, 0, kAdmMaxDeviceNameSize);
83 memset(guid, 0, kAdmMaxGuidSize);
84 memcpy(name, kName, strlen(kName));
85 memcpy(guid, kGuid, strlen(guid));
86 return 0;
87 }
88 return -1;
89 }
90
RecordingDeviceName(uint16_t index,char name[kAdmMaxDeviceNameSize],char guid[kAdmMaxGuidSize])91 int32_t FileAudioDevice::RecordingDeviceName(uint16_t index,
92 char name[kAdmMaxDeviceNameSize],
93 char guid[kAdmMaxGuidSize]) {
94 const char* kName = "dummy_device";
95 const char* kGuid = "dummy_device_unique_id";
96 if (index < 1) {
97 memset(name, 0, kAdmMaxDeviceNameSize);
98 memset(guid, 0, kAdmMaxGuidSize);
99 memcpy(name, kName, strlen(kName));
100 memcpy(guid, kGuid, strlen(guid));
101 return 0;
102 }
103 return -1;
104 }
105
SetPlayoutDevice(uint16_t index)106 int32_t FileAudioDevice::SetPlayoutDevice(uint16_t index) {
107 if (index == 0) {
108 _playout_index = index;
109 return 0;
110 }
111 return -1;
112 }
113
SetPlayoutDevice(AudioDeviceModule::WindowsDeviceType device)114 int32_t FileAudioDevice::SetPlayoutDevice(
115 AudioDeviceModule::WindowsDeviceType device) {
116 return -1;
117 }
118
SetRecordingDevice(uint16_t index)119 int32_t FileAudioDevice::SetRecordingDevice(uint16_t index) {
120 if (index == 0) {
121 _record_index = index;
122 return _record_index;
123 }
124 return -1;
125 }
126
SetRecordingDevice(AudioDeviceModule::WindowsDeviceType device)127 int32_t FileAudioDevice::SetRecordingDevice(
128 AudioDeviceModule::WindowsDeviceType device) {
129 return -1;
130 }
131
PlayoutIsAvailable(bool & available)132 int32_t FileAudioDevice::PlayoutIsAvailable(bool& available) {
133 if (_playout_index == 0) {
134 available = true;
135 return _playout_index;
136 }
137 available = false;
138 return -1;
139 }
140
InitPlayout()141 int32_t FileAudioDevice::InitPlayout() {
142 MutexLock lock(&mutex_);
143
144 if (_playing) {
145 return -1;
146 }
147
148 _playoutFramesIn10MS = static_cast<size_t>(kPlayoutFixedSampleRate / 100);
149
150 if (_ptrAudioBuffer) {
151 // Update webrtc audio buffer with the selected parameters
152 _ptrAudioBuffer->SetPlayoutSampleRate(kPlayoutFixedSampleRate);
153 _ptrAudioBuffer->SetPlayoutChannels(kPlayoutNumChannels);
154 }
155 return 0;
156 }
157
PlayoutIsInitialized() const158 bool FileAudioDevice::PlayoutIsInitialized() const {
159 return _playoutFramesIn10MS != 0;
160 }
161
RecordingIsAvailable(bool & available)162 int32_t FileAudioDevice::RecordingIsAvailable(bool& available) {
163 if (_record_index == 0) {
164 available = true;
165 return _record_index;
166 }
167 available = false;
168 return -1;
169 }
170
InitRecording()171 int32_t FileAudioDevice::InitRecording() {
172 MutexLock lock(&mutex_);
173
174 if (_recording) {
175 return -1;
176 }
177
178 _recordingFramesIn10MS = static_cast<size_t>(kRecordingFixedSampleRate / 100);
179
180 if (_ptrAudioBuffer) {
181 _ptrAudioBuffer->SetRecordingSampleRate(kRecordingFixedSampleRate);
182 _ptrAudioBuffer->SetRecordingChannels(kRecordingNumChannels);
183 }
184 return 0;
185 }
186
RecordingIsInitialized() const187 bool FileAudioDevice::RecordingIsInitialized() const {
188 return _recordingFramesIn10MS != 0;
189 }
190
StartPlayout()191 int32_t FileAudioDevice::StartPlayout() {
192 if (_playing) {
193 return 0;
194 }
195
196 _playing = true;
197 _playoutFramesLeft = 0;
198
199 if (!_playoutBuffer) {
200 _playoutBuffer = new int8_t[kPlayoutBufferSize];
201 }
202 if (!_playoutBuffer) {
203 _playing = false;
204 return -1;
205 }
206
207 // PLAYOUT
208 if (!_outputFilename.empty()) {
209 _outputFile = FileWrapper::OpenWriteOnly(_outputFilename.c_str());
210 if (!_outputFile.is_open()) {
211 RTC_LOG(LS_ERROR) << "Failed to open playout file: " << _outputFilename;
212 _playing = false;
213 delete[] _playoutBuffer;
214 _playoutBuffer = NULL;
215 return -1;
216 }
217 }
218
219 _ptrThreadPlay.reset(new rtc::PlatformThread(
220 PlayThreadFunc, this, "webrtc_audio_module_play_thread",
221 rtc::kRealtimePriority));
222 _ptrThreadPlay->Start();
223
224 RTC_LOG(LS_INFO) << "Started playout capture to output file: "
225 << _outputFilename;
226 return 0;
227 }
228
StopPlayout()229 int32_t FileAudioDevice::StopPlayout() {
230 {
231 MutexLock lock(&mutex_);
232 _playing = false;
233 }
234
235 // stop playout thread first
236 if (_ptrThreadPlay) {
237 _ptrThreadPlay->Stop();
238 _ptrThreadPlay.reset();
239 }
240
241 MutexLock lock(&mutex_);
242
243 _playoutFramesLeft = 0;
244 delete[] _playoutBuffer;
245 _playoutBuffer = NULL;
246 _outputFile.Close();
247
248 RTC_LOG(LS_INFO) << "Stopped playout capture to output file: "
249 << _outputFilename;
250 return 0;
251 }
252
Playing() const253 bool FileAudioDevice::Playing() const {
254 return _playing;
255 }
256
StartRecording()257 int32_t FileAudioDevice::StartRecording() {
258 _recording = true;
259
260 // Make sure we only create the buffer once.
261 _recordingBufferSizeIn10MS =
262 _recordingFramesIn10MS * kRecordingNumChannels * 2;
263 if (!_recordingBuffer) {
264 _recordingBuffer = new int8_t[_recordingBufferSizeIn10MS];
265 }
266
267 if (!_inputFilename.empty()) {
268 _inputFile = FileWrapper::OpenReadOnly(_inputFilename.c_str());
269 if (!_inputFile.is_open()) {
270 RTC_LOG(LS_ERROR) << "Failed to open audio input file: "
271 << _inputFilename;
272 _recording = false;
273 delete[] _recordingBuffer;
274 _recordingBuffer = NULL;
275 return -1;
276 }
277 }
278
279 _ptrThreadRec.reset(new rtc::PlatformThread(
280 RecThreadFunc, this, "webrtc_audio_module_capture_thread",
281 rtc::kRealtimePriority));
282
283 _ptrThreadRec->Start();
284
285 RTC_LOG(LS_INFO) << "Started recording from input file: " << _inputFilename;
286
287 return 0;
288 }
289
StopRecording()290 int32_t FileAudioDevice::StopRecording() {
291 {
292 MutexLock lock(&mutex_);
293 _recording = false;
294 }
295
296 if (_ptrThreadRec) {
297 _ptrThreadRec->Stop();
298 _ptrThreadRec.reset();
299 }
300
301 MutexLock lock(&mutex_);
302 _recordingFramesLeft = 0;
303 if (_recordingBuffer) {
304 delete[] _recordingBuffer;
305 _recordingBuffer = NULL;
306 }
307 _inputFile.Close();
308
309 RTC_LOG(LS_INFO) << "Stopped recording from input file: " << _inputFilename;
310 return 0;
311 }
312
Recording() const313 bool FileAudioDevice::Recording() const {
314 return _recording;
315 }
316
InitSpeaker()317 int32_t FileAudioDevice::InitSpeaker() {
318 return -1;
319 }
320
SpeakerIsInitialized() const321 bool FileAudioDevice::SpeakerIsInitialized() const {
322 return false;
323 }
324
InitMicrophone()325 int32_t FileAudioDevice::InitMicrophone() {
326 return 0;
327 }
328
MicrophoneIsInitialized() const329 bool FileAudioDevice::MicrophoneIsInitialized() const {
330 return true;
331 }
332
SpeakerVolumeIsAvailable(bool & available)333 int32_t FileAudioDevice::SpeakerVolumeIsAvailable(bool& available) {
334 return -1;
335 }
336
SetSpeakerVolume(uint32_t volume)337 int32_t FileAudioDevice::SetSpeakerVolume(uint32_t volume) {
338 return -1;
339 }
340
SpeakerVolume(uint32_t & volume) const341 int32_t FileAudioDevice::SpeakerVolume(uint32_t& volume) const {
342 return -1;
343 }
344
MaxSpeakerVolume(uint32_t & maxVolume) const345 int32_t FileAudioDevice::MaxSpeakerVolume(uint32_t& maxVolume) const {
346 return -1;
347 }
348
MinSpeakerVolume(uint32_t & minVolume) const349 int32_t FileAudioDevice::MinSpeakerVolume(uint32_t& minVolume) const {
350 return -1;
351 }
352
MicrophoneVolumeIsAvailable(bool & available)353 int32_t FileAudioDevice::MicrophoneVolumeIsAvailable(bool& available) {
354 return -1;
355 }
356
SetMicrophoneVolume(uint32_t volume)357 int32_t FileAudioDevice::SetMicrophoneVolume(uint32_t volume) {
358 return -1;
359 }
360
MicrophoneVolume(uint32_t & volume) const361 int32_t FileAudioDevice::MicrophoneVolume(uint32_t& volume) const {
362 return -1;
363 }
364
MaxMicrophoneVolume(uint32_t & maxVolume) const365 int32_t FileAudioDevice::MaxMicrophoneVolume(uint32_t& maxVolume) const {
366 return -1;
367 }
368
MinMicrophoneVolume(uint32_t & minVolume) const369 int32_t FileAudioDevice::MinMicrophoneVolume(uint32_t& minVolume) const {
370 return -1;
371 }
372
SpeakerMuteIsAvailable(bool & available)373 int32_t FileAudioDevice::SpeakerMuteIsAvailable(bool& available) {
374 return -1;
375 }
376
SetSpeakerMute(bool enable)377 int32_t FileAudioDevice::SetSpeakerMute(bool enable) {
378 return -1;
379 }
380
SpeakerMute(bool & enabled) const381 int32_t FileAudioDevice::SpeakerMute(bool& enabled) const {
382 return -1;
383 }
384
MicrophoneMuteIsAvailable(bool & available)385 int32_t FileAudioDevice::MicrophoneMuteIsAvailable(bool& available) {
386 return -1;
387 }
388
SetMicrophoneMute(bool enable)389 int32_t FileAudioDevice::SetMicrophoneMute(bool enable) {
390 return -1;
391 }
392
MicrophoneMute(bool & enabled) const393 int32_t FileAudioDevice::MicrophoneMute(bool& enabled) const {
394 return -1;
395 }
396
StereoPlayoutIsAvailable(bool & available)397 int32_t FileAudioDevice::StereoPlayoutIsAvailable(bool& available) {
398 available = true;
399 return 0;
400 }
SetStereoPlayout(bool enable)401 int32_t FileAudioDevice::SetStereoPlayout(bool enable) {
402 return 0;
403 }
404
StereoPlayout(bool & enabled) const405 int32_t FileAudioDevice::StereoPlayout(bool& enabled) const {
406 enabled = true;
407 return 0;
408 }
409
StereoRecordingIsAvailable(bool & available)410 int32_t FileAudioDevice::StereoRecordingIsAvailable(bool& available) {
411 available = true;
412 return 0;
413 }
414
SetStereoRecording(bool enable)415 int32_t FileAudioDevice::SetStereoRecording(bool enable) {
416 return 0;
417 }
418
StereoRecording(bool & enabled) const419 int32_t FileAudioDevice::StereoRecording(bool& enabled) const {
420 enabled = true;
421 return 0;
422 }
423
PlayoutDelay(uint16_t & delayMS) const424 int32_t FileAudioDevice::PlayoutDelay(uint16_t& delayMS) const {
425 return 0;
426 }
427
AttachAudioBuffer(AudioDeviceBuffer * audioBuffer)428 void FileAudioDevice::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
429 MutexLock lock(&mutex_);
430
431 _ptrAudioBuffer = audioBuffer;
432
433 // Inform the AudioBuffer about default settings for this implementation.
434 // Set all values to zero here since the actual settings will be done by
435 // InitPlayout and InitRecording later.
436 _ptrAudioBuffer->SetRecordingSampleRate(0);
437 _ptrAudioBuffer->SetPlayoutSampleRate(0);
438 _ptrAudioBuffer->SetRecordingChannels(0);
439 _ptrAudioBuffer->SetPlayoutChannels(0);
440 }
441
PlayThreadFunc(void * pThis)442 void FileAudioDevice::PlayThreadFunc(void* pThis) {
443 FileAudioDevice* device = static_cast<FileAudioDevice*>(pThis);
444 while (device->PlayThreadProcess()) {
445 }
446 }
447
RecThreadFunc(void * pThis)448 void FileAudioDevice::RecThreadFunc(void* pThis) {
449 FileAudioDevice* device = static_cast<FileAudioDevice*>(pThis);
450 while (device->RecThreadProcess()) {
451 }
452 }
453
PlayThreadProcess()454 bool FileAudioDevice::PlayThreadProcess() {
455 if (!_playing) {
456 return false;
457 }
458 int64_t currentTime = rtc::TimeMillis();
459 mutex_.Lock();
460
461 if (_lastCallPlayoutMillis == 0 ||
462 currentTime - _lastCallPlayoutMillis >= 10) {
463 mutex_.Unlock();
464 _ptrAudioBuffer->RequestPlayoutData(_playoutFramesIn10MS);
465 mutex_.Lock();
466
467 _playoutFramesLeft = _ptrAudioBuffer->GetPlayoutData(_playoutBuffer);
468 RTC_DCHECK_EQ(_playoutFramesIn10MS, _playoutFramesLeft);
469 if (_outputFile.is_open()) {
470 _outputFile.Write(_playoutBuffer, kPlayoutBufferSize);
471 }
472 _lastCallPlayoutMillis = currentTime;
473 }
474 _playoutFramesLeft = 0;
475 mutex_.Unlock();
476
477 int64_t deltaTimeMillis = rtc::TimeMillis() - currentTime;
478 if (deltaTimeMillis < 10) {
479 SleepMs(10 - deltaTimeMillis);
480 }
481
482 return true;
483 }
484
RecThreadProcess()485 bool FileAudioDevice::RecThreadProcess() {
486 if (!_recording) {
487 return false;
488 }
489
490 int64_t currentTime = rtc::TimeMillis();
491 mutex_.Lock();
492
493 if (_lastCallRecordMillis == 0 || currentTime - _lastCallRecordMillis >= 10) {
494 if (_inputFile.is_open()) {
495 if (_inputFile.Read(_recordingBuffer, kRecordingBufferSize) > 0) {
496 _ptrAudioBuffer->SetRecordedBuffer(_recordingBuffer,
497 _recordingFramesIn10MS);
498 } else {
499 _inputFile.Rewind();
500 }
501 _lastCallRecordMillis = currentTime;
502 mutex_.Unlock();
503 _ptrAudioBuffer->DeliverRecordedData();
504 mutex_.Lock();
505 }
506 }
507
508 mutex_.Unlock();
509
510 int64_t deltaTimeMillis = rtc::TimeMillis() - currentTime;
511 if (deltaTimeMillis < 10) {
512 SleepMs(10 - deltaTimeMillis);
513 }
514
515 return true;
516 }
517
518 } // namespace webrtc
519