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