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 "absl/strings/string_view.h"
16 #include "rtc_base/checks.h"
17 #include "rtc_base/logging.h"
18 #include "rtc_base/platform_thread.h"
19 #include "rtc_base/time_utils.h"
20 #include "system_wrappers/include/sleep.h"
21
22 namespace webrtc {
23
24 const int kRecordingFixedSampleRate = 48000;
25 const size_t kRecordingNumChannels = 2;
26 const int kPlayoutFixedSampleRate = 48000;
27 const size_t kPlayoutNumChannels = 2;
28 const size_t kPlayoutBufferSize =
29 kPlayoutFixedSampleRate / 100 * kPlayoutNumChannels * 2;
30 const size_t kRecordingBufferSize =
31 kRecordingFixedSampleRate / 100 * kRecordingNumChannels * 2;
32
FileAudioDevice(absl::string_view inputFilename,absl::string_view outputFilename)33 FileAudioDevice::FileAudioDevice(absl::string_view inputFilename,
34 absl::string_view outputFilename)
35 : _ptrAudioBuffer(NULL),
36 _recordingBuffer(NULL),
37 _playoutBuffer(NULL),
38 _recordingFramesLeft(0),
39 _playoutFramesLeft(0),
40 _recordingBufferSizeIn10MS(0),
41 _recordingFramesIn10MS(0),
42 _playoutFramesIn10MS(0),
43 _playing(false),
44 _recording(false),
45 _lastCallPlayoutMillis(0),
46 _lastCallRecordMillis(0),
47 _outputFilename(outputFilename),
48 _inputFilename(inputFilename) {}
49
~FileAudioDevice()50 FileAudioDevice::~FileAudioDevice() {}
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 MutexLock lock(&mutex_);
144
145 if (_playing) {
146 return -1;
147 }
148
149 _playoutFramesIn10MS = static_cast<size_t>(kPlayoutFixedSampleRate / 100);
150
151 if (_ptrAudioBuffer) {
152 // Update webrtc audio buffer with the selected parameters
153 _ptrAudioBuffer->SetPlayoutSampleRate(kPlayoutFixedSampleRate);
154 _ptrAudioBuffer->SetPlayoutChannels(kPlayoutNumChannels);
155 }
156 return 0;
157 }
158
PlayoutIsInitialized() const159 bool FileAudioDevice::PlayoutIsInitialized() const {
160 return _playoutFramesIn10MS != 0;
161 }
162
RecordingIsAvailable(bool & available)163 int32_t FileAudioDevice::RecordingIsAvailable(bool& available) {
164 if (_record_index == 0) {
165 available = true;
166 return _record_index;
167 }
168 available = false;
169 return -1;
170 }
171
InitRecording()172 int32_t FileAudioDevice::InitRecording() {
173 MutexLock lock(&mutex_);
174
175 if (_recording) {
176 return -1;
177 }
178
179 _recordingFramesIn10MS = static_cast<size_t>(kRecordingFixedSampleRate / 100);
180
181 if (_ptrAudioBuffer) {
182 _ptrAudioBuffer->SetRecordingSampleRate(kRecordingFixedSampleRate);
183 _ptrAudioBuffer->SetRecordingChannels(kRecordingNumChannels);
184 }
185 return 0;
186 }
187
RecordingIsInitialized() const188 bool FileAudioDevice::RecordingIsInitialized() const {
189 return _recordingFramesIn10MS != 0;
190 }
191
StartPlayout()192 int32_t FileAudioDevice::StartPlayout() {
193 if (_playing) {
194 return 0;
195 }
196
197 _playing = true;
198 _playoutFramesLeft = 0;
199
200 if (!_playoutBuffer) {
201 _playoutBuffer = new int8_t[kPlayoutBufferSize];
202 }
203 if (!_playoutBuffer) {
204 _playing = false;
205 return -1;
206 }
207
208 // PLAYOUT
209 if (!_outputFilename.empty()) {
210 _outputFile = FileWrapper::OpenWriteOnly(_outputFilename);
211 if (!_outputFile.is_open()) {
212 RTC_LOG(LS_ERROR) << "Failed to open playout file: " << _outputFilename;
213 _playing = false;
214 delete[] _playoutBuffer;
215 _playoutBuffer = NULL;
216 return -1;
217 }
218 }
219
220 _ptrThreadPlay = rtc::PlatformThread::SpawnJoinable(
221 [this] {
222 while (PlayThreadProcess()) {
223 }
224 },
225 "webrtc_audio_module_play_thread",
226 rtc::ThreadAttributes().SetPriority(rtc::ThreadPriority::kRealtime));
227
228 RTC_LOG(LS_INFO) << "Started playout capture to output file: "
229 << _outputFilename;
230 return 0;
231 }
232
StopPlayout()233 int32_t FileAudioDevice::StopPlayout() {
234 {
235 MutexLock lock(&mutex_);
236 _playing = false;
237 }
238
239 // stop playout thread first
240 if (!_ptrThreadPlay.empty())
241 _ptrThreadPlay.Finalize();
242
243 MutexLock lock(&mutex_);
244
245 _playoutFramesLeft = 0;
246 delete[] _playoutBuffer;
247 _playoutBuffer = NULL;
248 _outputFile.Close();
249
250 RTC_LOG(LS_INFO) << "Stopped playout capture to output file: "
251 << _outputFilename;
252 return 0;
253 }
254
Playing() const255 bool FileAudioDevice::Playing() const {
256 return _playing;
257 }
258
StartRecording()259 int32_t FileAudioDevice::StartRecording() {
260 _recording = true;
261
262 // Make sure we only create the buffer once.
263 _recordingBufferSizeIn10MS =
264 _recordingFramesIn10MS * kRecordingNumChannels * 2;
265 if (!_recordingBuffer) {
266 _recordingBuffer = new int8_t[_recordingBufferSizeIn10MS];
267 }
268
269 if (!_inputFilename.empty()) {
270 _inputFile = FileWrapper::OpenReadOnly(_inputFilename);
271 if (!_inputFile.is_open()) {
272 RTC_LOG(LS_ERROR) << "Failed to open audio input file: "
273 << _inputFilename;
274 _recording = false;
275 delete[] _recordingBuffer;
276 _recordingBuffer = NULL;
277 return -1;
278 }
279 }
280
281 _ptrThreadRec = rtc::PlatformThread::SpawnJoinable(
282 [this] {
283 while (RecThreadProcess()) {
284 }
285 },
286 "webrtc_audio_module_capture_thread",
287 rtc::ThreadAttributes().SetPriority(rtc::ThreadPriority::kRealtime));
288
289 RTC_LOG(LS_INFO) << "Started recording from input file: " << _inputFilename;
290
291 return 0;
292 }
293
StopRecording()294 int32_t FileAudioDevice::StopRecording() {
295 {
296 MutexLock lock(&mutex_);
297 _recording = false;
298 }
299
300 if (!_ptrThreadRec.empty())
301 _ptrThreadRec.Finalize();
302
303 MutexLock lock(&mutex_);
304 _recordingFramesLeft = 0;
305 if (_recordingBuffer) {
306 delete[] _recordingBuffer;
307 _recordingBuffer = NULL;
308 }
309 _inputFile.Close();
310
311 RTC_LOG(LS_INFO) << "Stopped recording from input file: " << _inputFilename;
312 return 0;
313 }
314
Recording() const315 bool FileAudioDevice::Recording() const {
316 return _recording;
317 }
318
InitSpeaker()319 int32_t FileAudioDevice::InitSpeaker() {
320 return -1;
321 }
322
SpeakerIsInitialized() const323 bool FileAudioDevice::SpeakerIsInitialized() const {
324 return false;
325 }
326
InitMicrophone()327 int32_t FileAudioDevice::InitMicrophone() {
328 return 0;
329 }
330
MicrophoneIsInitialized() const331 bool FileAudioDevice::MicrophoneIsInitialized() const {
332 return true;
333 }
334
SpeakerVolumeIsAvailable(bool & available)335 int32_t FileAudioDevice::SpeakerVolumeIsAvailable(bool& available) {
336 return -1;
337 }
338
SetSpeakerVolume(uint32_t volume)339 int32_t FileAudioDevice::SetSpeakerVolume(uint32_t volume) {
340 return -1;
341 }
342
SpeakerVolume(uint32_t & volume) const343 int32_t FileAudioDevice::SpeakerVolume(uint32_t& volume) const {
344 return -1;
345 }
346
MaxSpeakerVolume(uint32_t & maxVolume) const347 int32_t FileAudioDevice::MaxSpeakerVolume(uint32_t& maxVolume) const {
348 return -1;
349 }
350
MinSpeakerVolume(uint32_t & minVolume) const351 int32_t FileAudioDevice::MinSpeakerVolume(uint32_t& minVolume) const {
352 return -1;
353 }
354
MicrophoneVolumeIsAvailable(bool & available)355 int32_t FileAudioDevice::MicrophoneVolumeIsAvailable(bool& available) {
356 return -1;
357 }
358
SetMicrophoneVolume(uint32_t volume)359 int32_t FileAudioDevice::SetMicrophoneVolume(uint32_t volume) {
360 return -1;
361 }
362
MicrophoneVolume(uint32_t & volume) const363 int32_t FileAudioDevice::MicrophoneVolume(uint32_t& volume) const {
364 return -1;
365 }
366
MaxMicrophoneVolume(uint32_t & maxVolume) const367 int32_t FileAudioDevice::MaxMicrophoneVolume(uint32_t& maxVolume) const {
368 return -1;
369 }
370
MinMicrophoneVolume(uint32_t & minVolume) const371 int32_t FileAudioDevice::MinMicrophoneVolume(uint32_t& minVolume) const {
372 return -1;
373 }
374
SpeakerMuteIsAvailable(bool & available)375 int32_t FileAudioDevice::SpeakerMuteIsAvailable(bool& available) {
376 return -1;
377 }
378
SetSpeakerMute(bool enable)379 int32_t FileAudioDevice::SetSpeakerMute(bool enable) {
380 return -1;
381 }
382
SpeakerMute(bool & enabled) const383 int32_t FileAudioDevice::SpeakerMute(bool& enabled) const {
384 return -1;
385 }
386
MicrophoneMuteIsAvailable(bool & available)387 int32_t FileAudioDevice::MicrophoneMuteIsAvailable(bool& available) {
388 return -1;
389 }
390
SetMicrophoneMute(bool enable)391 int32_t FileAudioDevice::SetMicrophoneMute(bool enable) {
392 return -1;
393 }
394
MicrophoneMute(bool & enabled) const395 int32_t FileAudioDevice::MicrophoneMute(bool& enabled) const {
396 return -1;
397 }
398
StereoPlayoutIsAvailable(bool & available)399 int32_t FileAudioDevice::StereoPlayoutIsAvailable(bool& available) {
400 available = true;
401 return 0;
402 }
SetStereoPlayout(bool enable)403 int32_t FileAudioDevice::SetStereoPlayout(bool enable) {
404 return 0;
405 }
406
StereoPlayout(bool & enabled) const407 int32_t FileAudioDevice::StereoPlayout(bool& enabled) const {
408 enabled = true;
409 return 0;
410 }
411
StereoRecordingIsAvailable(bool & available)412 int32_t FileAudioDevice::StereoRecordingIsAvailable(bool& available) {
413 available = true;
414 return 0;
415 }
416
SetStereoRecording(bool enable)417 int32_t FileAudioDevice::SetStereoRecording(bool enable) {
418 return 0;
419 }
420
StereoRecording(bool & enabled) const421 int32_t FileAudioDevice::StereoRecording(bool& enabled) const {
422 enabled = true;
423 return 0;
424 }
425
PlayoutDelay(uint16_t & delayMS) const426 int32_t FileAudioDevice::PlayoutDelay(uint16_t& delayMS) const {
427 return 0;
428 }
429
AttachAudioBuffer(AudioDeviceBuffer * audioBuffer)430 void FileAudioDevice::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
431 MutexLock lock(&mutex_);
432
433 _ptrAudioBuffer = audioBuffer;
434
435 // Inform the AudioBuffer about default settings for this implementation.
436 // Set all values to zero here since the actual settings will be done by
437 // InitPlayout and InitRecording later.
438 _ptrAudioBuffer->SetRecordingSampleRate(0);
439 _ptrAudioBuffer->SetPlayoutSampleRate(0);
440 _ptrAudioBuffer->SetRecordingChannels(0);
441 _ptrAudioBuffer->SetPlayoutChannels(0);
442 }
443
PlayThreadProcess()444 bool FileAudioDevice::PlayThreadProcess() {
445 if (!_playing) {
446 return false;
447 }
448 int64_t currentTime = rtc::TimeMillis();
449 mutex_.Lock();
450
451 if (_lastCallPlayoutMillis == 0 ||
452 currentTime - _lastCallPlayoutMillis >= 10) {
453 mutex_.Unlock();
454 _ptrAudioBuffer->RequestPlayoutData(_playoutFramesIn10MS);
455 mutex_.Lock();
456
457 _playoutFramesLeft = _ptrAudioBuffer->GetPlayoutData(_playoutBuffer);
458 RTC_DCHECK_EQ(_playoutFramesIn10MS, _playoutFramesLeft);
459 if (_outputFile.is_open()) {
460 _outputFile.Write(_playoutBuffer, kPlayoutBufferSize);
461 }
462 _lastCallPlayoutMillis = currentTime;
463 }
464 _playoutFramesLeft = 0;
465 mutex_.Unlock();
466
467 int64_t deltaTimeMillis = rtc::TimeMillis() - currentTime;
468 if (deltaTimeMillis < 10) {
469 SleepMs(10 - deltaTimeMillis);
470 }
471
472 return true;
473 }
474
RecThreadProcess()475 bool FileAudioDevice::RecThreadProcess() {
476 if (!_recording) {
477 return false;
478 }
479
480 int64_t currentTime = rtc::TimeMillis();
481 mutex_.Lock();
482
483 if (_lastCallRecordMillis == 0 || currentTime - _lastCallRecordMillis >= 10) {
484 if (_inputFile.is_open()) {
485 if (_inputFile.Read(_recordingBuffer, kRecordingBufferSize) > 0) {
486 _ptrAudioBuffer->SetRecordedBuffer(_recordingBuffer,
487 _recordingFramesIn10MS);
488 } else {
489 _inputFile.Rewind();
490 }
491 _lastCallRecordMillis = currentTime;
492 mutex_.Unlock();
493 _ptrAudioBuffer->DeliverRecordedData();
494 mutex_.Lock();
495 }
496 }
497
498 mutex_.Unlock();
499
500 int64_t deltaTimeMillis = rtc::TimeMillis() - currentTime;
501 if (deltaTimeMillis < 10) {
502 SleepMs(10 - deltaTimeMillis);
503 }
504
505 return true;
506 }
507
508 } // namespace webrtc
509