1 /*
2 * Copyright (c) 2012 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 <assert.h>
12
13 #include "modules/media_file/media_file_impl.h"
14 #include "rtc_base/format_macros.h"
15 #include "rtc_base/logging.h"
16 #include "system_wrappers/include/file_wrapper.h"
17
18 namespace webrtc {
CreateMediaFile(const int32_t id)19 MediaFile* MediaFile::CreateMediaFile(const int32_t id) {
20 return new MediaFileImpl(id);
21 }
22
DestroyMediaFile(MediaFile * module)23 void MediaFile::DestroyMediaFile(MediaFile* module) {
24 delete static_cast<MediaFileImpl*>(module);
25 }
26
MediaFileImpl(const int32_t id)27 MediaFileImpl::MediaFileImpl(const int32_t id)
28 : _id(id),
29 _ptrFileUtilityObj(NULL),
30 codec_info_(),
31 _ptrInStream(NULL),
32 _ptrOutStream(NULL),
33 _fileFormat((FileFormats)-1),
34 _recordDurationMs(0),
35 _playoutPositionMs(0),
36 _notificationMs(0),
37 _playingActive(false),
38 _recordingActive(false),
39 _isStereo(false),
40 _openFile(false),
41 _fileName(),
42 _ptrCallback(NULL) {
43 RTC_LOG(LS_INFO) << "MediaFileImpl()";
44
45 codec_info_.plname[0] = '\0';
46 _fileName[0] = '\0';
47 }
48
~MediaFileImpl()49 MediaFileImpl::~MediaFileImpl() {
50 RTC_LOG(LS_INFO) << "~MediaFileImpl()";
51 {
52 rtc::CritScope lock(&_crit);
53
54 if (_playingActive) {
55 StopPlaying();
56 }
57
58 if (_recordingActive) {
59 StopRecording();
60 }
61
62 delete _ptrFileUtilityObj;
63
64 if (_openFile) {
65 delete _ptrInStream;
66 _ptrInStream = NULL;
67 delete _ptrOutStream;
68 _ptrOutStream = NULL;
69 }
70 }
71 }
72
TimeUntilNextProcess()73 int64_t MediaFileImpl::TimeUntilNextProcess() {
74 RTC_LOG(LS_WARNING)
75 << "TimeUntilNextProcess: This method is not used by MediaFile class.";
76 return -1;
77 }
78
Process()79 void MediaFileImpl::Process() {
80 RTC_LOG(LS_WARNING) << "Process: This method is not used by MediaFile class.";
81 }
82
PlayoutAudioData(int8_t * buffer,size_t & dataLengthInBytes)83 int32_t MediaFileImpl::PlayoutAudioData(int8_t* buffer,
84 size_t& dataLengthInBytes) {
85 RTC_LOG(LS_INFO) << "MediaFileImpl::PlayoutData(buffer= "
86 << static_cast<void*>(buffer)
87 << ", bufLen= " << dataLengthInBytes << ")";
88
89 const size_t bufferLengthInBytes = dataLengthInBytes;
90 dataLengthInBytes = 0;
91
92 if (buffer == NULL || bufferLengthInBytes == 0) {
93 RTC_LOG(LS_ERROR) << "Buffer pointer or length is NULL!";
94 return -1;
95 }
96
97 int32_t bytesRead = 0;
98 {
99 rtc::CritScope lock(&_crit);
100
101 if (!_playingActive) {
102 RTC_LOG(LS_WARNING) << "Not currently playing!";
103 return -1;
104 }
105
106 if (!_ptrFileUtilityObj) {
107 RTC_LOG(LS_ERROR) << "Playing, but no FileUtility object!";
108 StopPlaying();
109 return -1;
110 }
111
112 switch (_fileFormat) {
113 case kFileFormatPcm48kHzFile:
114 case kFileFormatPcm32kHzFile:
115 case kFileFormatPcm16kHzFile:
116 case kFileFormatPcm8kHzFile:
117 bytesRead = _ptrFileUtilityObj->ReadPCMData(*_ptrInStream, buffer,
118 bufferLengthInBytes);
119 break;
120 case kFileFormatCompressedFile:
121 bytesRead = _ptrFileUtilityObj->ReadCompressedData(
122 *_ptrInStream, buffer, bufferLengthInBytes);
123 break;
124 case kFileFormatWavFile:
125 bytesRead = _ptrFileUtilityObj->ReadWavDataAsMono(*_ptrInStream, buffer,
126 bufferLengthInBytes);
127 break;
128 case kFileFormatPreencodedFile:
129 bytesRead = _ptrFileUtilityObj->ReadPreEncodedData(
130 *_ptrInStream, buffer, bufferLengthInBytes);
131 if (bytesRead > 0) {
132 dataLengthInBytes = static_cast<size_t>(bytesRead);
133 return 0;
134 }
135 break;
136 default: {
137 RTC_LOG(LS_ERROR) << "Invalid file format: " << _fileFormat;
138 assert(false);
139 break;
140 }
141 }
142
143 if (bytesRead > 0) {
144 dataLengthInBytes = static_cast<size_t>(bytesRead);
145 }
146 }
147 HandlePlayCallbacks(bytesRead);
148 return 0;
149 }
150
HandlePlayCallbacks(int32_t bytesRead)151 void MediaFileImpl::HandlePlayCallbacks(int32_t bytesRead) {
152 bool playEnded = false;
153 uint32_t callbackNotifyMs = 0;
154
155 if (bytesRead > 0) {
156 // Check if it's time for PlayNotification(..).
157 _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs();
158 if (_notificationMs) {
159 if (_playoutPositionMs >= _notificationMs) {
160 _notificationMs = 0;
161 callbackNotifyMs = _playoutPositionMs;
162 }
163 }
164 } else {
165 // If no bytes were read assume end of file.
166 StopPlaying();
167 playEnded = true;
168 }
169
170 // Only _callbackCrit may and should be taken when making callbacks.
171 rtc::CritScope lock(&_callbackCrit);
172 if (_ptrCallback) {
173 if (callbackNotifyMs) {
174 _ptrCallback->PlayNotification(_id, callbackNotifyMs);
175 }
176 if (playEnded) {
177 _ptrCallback->PlayFileEnded(_id);
178 }
179 }
180 }
181
PlayoutStereoData(int8_t * bufferLeft,int8_t * bufferRight,size_t & dataLengthInBytes)182 int32_t MediaFileImpl::PlayoutStereoData(int8_t* bufferLeft,
183 int8_t* bufferRight,
184 size_t& dataLengthInBytes) {
185 RTC_LOG(LS_INFO) << "MediaFileImpl::PlayoutStereoData(Left = "
186 << static_cast<void*>(bufferLeft)
187 << ", Right = " << static_cast<void*>(bufferRight)
188 << ", Len= " << dataLengthInBytes << ")";
189
190 const size_t bufferLengthInBytes = dataLengthInBytes;
191 dataLengthInBytes = 0;
192
193 if (bufferLeft == NULL || bufferRight == NULL || bufferLengthInBytes == 0) {
194 RTC_LOG(LS_ERROR) << "A buffer pointer or the length is NULL!";
195 return -1;
196 }
197
198 bool playEnded = false;
199 uint32_t callbackNotifyMs = 0;
200 {
201 rtc::CritScope lock(&_crit);
202
203 if (!_playingActive || !_isStereo) {
204 RTC_LOG(LS_WARNING) << "Not currently playing stereo!";
205 return -1;
206 }
207
208 if (!_ptrFileUtilityObj) {
209 RTC_LOG(LS_ERROR)
210 << "Playing stereo, but the FileUtility objects is NULL!";
211 StopPlaying();
212 return -1;
213 }
214
215 // Stereo playout only supported for WAV files.
216 int32_t bytesRead = 0;
217 switch (_fileFormat) {
218 case kFileFormatWavFile:
219 bytesRead = _ptrFileUtilityObj->ReadWavDataAsStereo(
220 *_ptrInStream, bufferLeft, bufferRight, bufferLengthInBytes);
221 break;
222 default:
223 RTC_LOG(LS_ERROR)
224 << "Trying to read non-WAV as stereo audio (not supported)";
225 break;
226 }
227
228 if (bytesRead > 0) {
229 dataLengthInBytes = static_cast<size_t>(bytesRead);
230
231 // Check if it's time for PlayNotification(..).
232 _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs();
233 if (_notificationMs) {
234 if (_playoutPositionMs >= _notificationMs) {
235 _notificationMs = 0;
236 callbackNotifyMs = _playoutPositionMs;
237 }
238 }
239 } else {
240 // If no bytes were read assume end of file.
241 StopPlaying();
242 playEnded = true;
243 }
244 }
245
246 rtc::CritScope lock(&_callbackCrit);
247 if (_ptrCallback) {
248 if (callbackNotifyMs) {
249 _ptrCallback->PlayNotification(_id, callbackNotifyMs);
250 }
251 if (playEnded) {
252 _ptrCallback->PlayFileEnded(_id);
253 }
254 }
255 return 0;
256 }
257
StartPlayingAudioFile(const char * fileName,const uint32_t notificationTimeMs,const bool loop,const FileFormats format,const CodecInst * codecInst,const uint32_t startPointMs,const uint32_t stopPointMs)258 int32_t MediaFileImpl::StartPlayingAudioFile(const char* fileName,
259 const uint32_t notificationTimeMs,
260 const bool loop,
261 const FileFormats format,
262 const CodecInst* codecInst,
263 const uint32_t startPointMs,
264 const uint32_t stopPointMs) {
265 if (!ValidFileName(fileName)) {
266 return -1;
267 }
268 if (!ValidFileFormat(format, codecInst)) {
269 return -1;
270 }
271 if (!ValidFilePositions(startPointMs, stopPointMs)) {
272 return -1;
273 }
274
275 // Check that the file will play longer than notificationTimeMs ms.
276 if ((startPointMs && stopPointMs && !loop) &&
277 (notificationTimeMs > (stopPointMs - startPointMs))) {
278 RTC_LOG(LS_ERROR) << "specified notification time is longer than amount of"
279 << " ms that will be played";
280 return -1;
281 }
282
283 FileWrapper* inputStream = FileWrapper::Create();
284 if (inputStream == NULL) {
285 RTC_LOG(LS_INFO) << "Failed to allocate input stream for file " << fileName;
286 return -1;
287 }
288
289 if (!inputStream->OpenFile(fileName, true)) {
290 delete inputStream;
291 RTC_LOG(LS_ERROR) << "Could not open input file " << fileName;
292 return -1;
293 }
294
295 if (StartPlayingStream(*inputStream, loop, notificationTimeMs, format,
296 codecInst, startPointMs, stopPointMs) == -1) {
297 inputStream->CloseFile();
298 delete inputStream;
299 return -1;
300 }
301
302 rtc::CritScope lock(&_crit);
303 _openFile = true;
304 strncpy(_fileName, fileName, sizeof(_fileName));
305 _fileName[sizeof(_fileName) - 1] = '\0';
306 return 0;
307 }
308
StartPlayingAudioStream(InStream & stream,const uint32_t notificationTimeMs,const FileFormats format,const CodecInst * codecInst,const uint32_t startPointMs,const uint32_t stopPointMs)309 int32_t MediaFileImpl::StartPlayingAudioStream(
310 InStream& stream,
311 const uint32_t notificationTimeMs,
312 const FileFormats format,
313 const CodecInst* codecInst,
314 const uint32_t startPointMs,
315 const uint32_t stopPointMs) {
316 return StartPlayingStream(stream, false, notificationTimeMs, format,
317 codecInst, startPointMs, stopPointMs);
318 }
319
StartPlayingStream(InStream & stream,bool loop,const uint32_t notificationTimeMs,const FileFormats format,const CodecInst * codecInst,const uint32_t startPointMs,const uint32_t stopPointMs)320 int32_t MediaFileImpl::StartPlayingStream(InStream& stream,
321 bool loop,
322 const uint32_t notificationTimeMs,
323 const FileFormats format,
324 const CodecInst* codecInst,
325 const uint32_t startPointMs,
326 const uint32_t stopPointMs) {
327 if (!ValidFileFormat(format, codecInst)) {
328 return -1;
329 }
330
331 if (!ValidFilePositions(startPointMs, stopPointMs)) {
332 return -1;
333 }
334
335 rtc::CritScope lock(&_crit);
336 if (_playingActive || _recordingActive) {
337 RTC_LOG(LS_ERROR)
338 << "StartPlaying called, but already playing or recording file "
339 << ((_fileName[0] == '\0') ? "(name not set)" : _fileName);
340 return -1;
341 }
342
343 if (_ptrFileUtilityObj != NULL) {
344 RTC_LOG(LS_ERROR)
345 << "StartPlaying called, but FileUtilityObj already exists!";
346 StopPlaying();
347 return -1;
348 }
349
350 _ptrFileUtilityObj = new ModuleFileUtility();
351 if (_ptrFileUtilityObj == NULL) {
352 RTC_LOG(LS_INFO) << "Failed to create FileUtilityObj!";
353 return -1;
354 }
355
356 switch (format) {
357 case kFileFormatWavFile: {
358 if (_ptrFileUtilityObj->InitWavReading(stream, startPointMs,
359 stopPointMs) == -1) {
360 RTC_LOG(LS_ERROR) << "Not a valid WAV file!";
361 StopPlaying();
362 return -1;
363 }
364 _fileFormat = kFileFormatWavFile;
365 break;
366 }
367 case kFileFormatCompressedFile: {
368 if (_ptrFileUtilityObj->InitCompressedReading(stream, startPointMs,
369 stopPointMs) == -1) {
370 RTC_LOG(LS_ERROR) << "Not a valid Compressed file!";
371 StopPlaying();
372 return -1;
373 }
374 _fileFormat = kFileFormatCompressedFile;
375 break;
376 }
377 case kFileFormatPcm8kHzFile:
378 case kFileFormatPcm16kHzFile:
379 case kFileFormatPcm32kHzFile:
380 case kFileFormatPcm48kHzFile: {
381 // ValidFileFormat() called in the beginneing of this function
382 // prevents codecInst from being NULL here.
383 assert(codecInst != NULL);
384 if (!ValidFrequency(codecInst->plfreq) ||
385 _ptrFileUtilityObj->InitPCMReading(stream, startPointMs, stopPointMs,
386 codecInst->plfreq) == -1) {
387 RTC_LOG(LS_ERROR) << "Not a valid raw 8 or 16 KHz PCM file!";
388 StopPlaying();
389 return -1;
390 }
391
392 _fileFormat = format;
393 break;
394 }
395 case kFileFormatPreencodedFile: {
396 // ValidFileFormat() called in the beginneing of this function
397 // prevents codecInst from being NULL here.
398 assert(codecInst != NULL);
399 if (_ptrFileUtilityObj->InitPreEncodedReading(stream, *codecInst) == -1) {
400 RTC_LOG(LS_ERROR) << "Not a valid PreEncoded file!";
401 StopPlaying();
402 return -1;
403 }
404
405 _fileFormat = kFileFormatPreencodedFile;
406 break;
407 }
408 default: {
409 RTC_LOG(LS_ERROR) << "Invalid file format: " << format;
410 assert(false);
411 break;
412 }
413 }
414 if (_ptrFileUtilityObj->codec_info(codec_info_) == -1) {
415 RTC_LOG(LS_ERROR) << "Failed to retrieve codec info!";
416 StopPlaying();
417 return -1;
418 }
419
420 _isStereo = (codec_info_.channels == 2);
421 if (_isStereo && (_fileFormat != kFileFormatWavFile)) {
422 RTC_LOG(LS_WARNING) << "Stereo is only allowed for WAV files";
423 StopPlaying();
424 return -1;
425 }
426 _playingActive = true;
427 _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs();
428 _ptrInStream = &stream;
429 _notificationMs = notificationTimeMs;
430
431 return 0;
432 }
433
StopPlaying()434 int32_t MediaFileImpl::StopPlaying() {
435 rtc::CritScope lock(&_crit);
436 _isStereo = false;
437 if (_ptrFileUtilityObj) {
438 delete _ptrFileUtilityObj;
439 _ptrFileUtilityObj = NULL;
440 }
441 if (_ptrInStream) {
442 // If MediaFileImpl opened the InStream it must be reclaimed here.
443 if (_openFile) {
444 delete _ptrInStream;
445 _openFile = false;
446 }
447 _ptrInStream = NULL;
448 }
449
450 codec_info_.pltype = 0;
451 codec_info_.plname[0] = '\0';
452
453 if (!_playingActive) {
454 RTC_LOG(LS_WARNING) << "playing is not active!";
455 return -1;
456 }
457
458 _playingActive = false;
459 return 0;
460 }
461
IsPlaying()462 bool MediaFileImpl::IsPlaying() {
463 RTC_LOG(LS_VERBOSE) << "MediaFileImpl::IsPlaying()";
464 rtc::CritScope lock(&_crit);
465 return _playingActive;
466 }
467
IncomingAudioData(const int8_t * buffer,const size_t bufferLengthInBytes)468 int32_t MediaFileImpl::IncomingAudioData(const int8_t* buffer,
469 const size_t bufferLengthInBytes) {
470 RTC_LOG(LS_INFO) << "MediaFile::IncomingData(buffer= "
471 << static_cast<const void*>(buffer)
472 << ", bufLen= " << bufferLengthInBytes << ")";
473
474 if (buffer == NULL || bufferLengthInBytes == 0) {
475 RTC_LOG(LS_ERROR) << "Buffer pointer or length is NULL!";
476 return -1;
477 }
478
479 bool recordingEnded = false;
480 uint32_t callbackNotifyMs = 0;
481 {
482 rtc::CritScope lock(&_crit);
483
484 if (!_recordingActive) {
485 RTC_LOG(LS_WARNING) << "Not currently recording!";
486 return -1;
487 }
488 if (_ptrOutStream == NULL) {
489 RTC_LOG(LS_ERROR) << "Recording is active, but output stream is NULL!";
490 assert(false);
491 return -1;
492 }
493
494 int32_t bytesWritten = 0;
495 uint32_t samplesWritten = codec_info_.pacsize;
496 if (_ptrFileUtilityObj) {
497 switch (_fileFormat) {
498 case kFileFormatPcm8kHzFile:
499 case kFileFormatPcm16kHzFile:
500 case kFileFormatPcm32kHzFile:
501 case kFileFormatPcm48kHzFile:
502 bytesWritten = _ptrFileUtilityObj->WritePCMData(
503 *_ptrOutStream, buffer, bufferLengthInBytes);
504
505 // Sample size is 2 bytes.
506 if (bytesWritten > 0) {
507 samplesWritten = bytesWritten / sizeof(int16_t);
508 }
509 break;
510 case kFileFormatCompressedFile:
511 bytesWritten = _ptrFileUtilityObj->WriteCompressedData(
512 *_ptrOutStream, buffer, bufferLengthInBytes);
513 break;
514 case kFileFormatWavFile:
515 bytesWritten = _ptrFileUtilityObj->WriteWavData(
516 *_ptrOutStream, buffer, bufferLengthInBytes);
517 if (bytesWritten > 0 &&
518 STR_NCASE_CMP(codec_info_.plname, "L16", 4) == 0) {
519 // Sample size is 2 bytes.
520 samplesWritten = bytesWritten / sizeof(int16_t);
521 }
522 break;
523 case kFileFormatPreencodedFile:
524 bytesWritten = _ptrFileUtilityObj->WritePreEncodedData(
525 *_ptrOutStream, buffer, bufferLengthInBytes);
526 break;
527 default:
528 RTC_LOG(LS_ERROR) << "Invalid file format: " << _fileFormat;
529 assert(false);
530 break;
531 }
532 } else {
533 // TODO (hellner): quick look at the code makes me think that this
534 // code is never executed. Remove?
535 if (_ptrOutStream) {
536 if (_ptrOutStream->Write(buffer, bufferLengthInBytes)) {
537 bytesWritten = static_cast<int32_t>(bufferLengthInBytes);
538 }
539 }
540 }
541
542 _recordDurationMs += samplesWritten / (codec_info_.plfreq / 1000);
543
544 // Check if it's time for RecordNotification(..).
545 if (_notificationMs) {
546 if (_recordDurationMs >= _notificationMs) {
547 _notificationMs = 0;
548 callbackNotifyMs = _recordDurationMs;
549 }
550 }
551 if (bytesWritten < (int32_t)bufferLengthInBytes) {
552 RTC_LOG(LS_WARNING) << "Failed to write all requested bytes!";
553 StopRecording();
554 recordingEnded = true;
555 }
556 }
557
558 // Only _callbackCrit may and should be taken when making callbacks.
559 rtc::CritScope lock(&_callbackCrit);
560 if (_ptrCallback) {
561 if (callbackNotifyMs) {
562 _ptrCallback->RecordNotification(_id, callbackNotifyMs);
563 }
564 if (recordingEnded) {
565 _ptrCallback->RecordFileEnded(_id);
566 return -1;
567 }
568 }
569 return 0;
570 }
571
StartRecordingAudioFile(const char * fileName,const FileFormats format,const CodecInst & codecInst,const uint32_t notificationTimeMs,const uint32_t maxSizeBytes)572 int32_t MediaFileImpl::StartRecordingAudioFile(
573 const char* fileName,
574 const FileFormats format,
575 const CodecInst& codecInst,
576 const uint32_t notificationTimeMs,
577 const uint32_t maxSizeBytes) {
578 if (!ValidFileName(fileName)) {
579 return -1;
580 }
581 if (!ValidFileFormat(format, &codecInst)) {
582 return -1;
583 }
584
585 FileWrapper* outputStream = FileWrapper::Create();
586 if (outputStream == NULL) {
587 RTC_LOG(LS_INFO) << "Failed to allocate memory for output stream";
588 return -1;
589 }
590
591 if (!outputStream->OpenFile(fileName, false)) {
592 delete outputStream;
593 RTC_LOG(LS_ERROR) << "Could not open output file '" << fileName
594 << "' for writing!";
595 return -1;
596 }
597
598 if (maxSizeBytes) {
599 outputStream->SetMaxFileSize(maxSizeBytes);
600 }
601
602 if (StartRecordingAudioStream(*outputStream, format, codecInst,
603 notificationTimeMs) == -1) {
604 outputStream->CloseFile();
605 delete outputStream;
606 return -1;
607 }
608
609 rtc::CritScope lock(&_crit);
610 _openFile = true;
611 strncpy(_fileName, fileName, sizeof(_fileName));
612 _fileName[sizeof(_fileName) - 1] = '\0';
613 return 0;
614 }
615
StartRecordingAudioStream(OutStream & stream,const FileFormats format,const CodecInst & codecInst,const uint32_t notificationTimeMs)616 int32_t MediaFileImpl::StartRecordingAudioStream(
617 OutStream& stream,
618 const FileFormats format,
619 const CodecInst& codecInst,
620 const uint32_t notificationTimeMs) {
621 // Check codec info
622 if (!ValidFileFormat(format, &codecInst)) {
623 return -1;
624 }
625
626 rtc::CritScope lock(&_crit);
627 if (_recordingActive || _playingActive) {
628 RTC_LOG(LS_ERROR)
629 << "StartRecording called, but already recording or playing file "
630 << _fileName << "!";
631 return -1;
632 }
633
634 if (_ptrFileUtilityObj != NULL) {
635 RTC_LOG(LS_ERROR)
636 << "StartRecording called, but fileUtilityObj already exists!";
637 StopRecording();
638 return -1;
639 }
640
641 _ptrFileUtilityObj = new ModuleFileUtility();
642 if (_ptrFileUtilityObj == NULL) {
643 RTC_LOG(LS_INFO) << "Cannot allocate fileUtilityObj!";
644 return -1;
645 }
646
647 CodecInst tmpAudioCodec;
648 memcpy(&tmpAudioCodec, &codecInst, sizeof(CodecInst));
649 switch (format) {
650 case kFileFormatWavFile: {
651 if (_ptrFileUtilityObj->InitWavWriting(stream, codecInst) == -1) {
652 RTC_LOG(LS_ERROR) << "Failed to initialize WAV file!";
653 delete _ptrFileUtilityObj;
654 _ptrFileUtilityObj = NULL;
655 return -1;
656 }
657 _fileFormat = kFileFormatWavFile;
658 break;
659 }
660 case kFileFormatCompressedFile: {
661 // Write compression codec name at beginning of file
662 if (_ptrFileUtilityObj->InitCompressedWriting(stream, codecInst) == -1) {
663 RTC_LOG(LS_ERROR) << "Failed to initialize Compressed file!";
664 delete _ptrFileUtilityObj;
665 _ptrFileUtilityObj = NULL;
666 return -1;
667 }
668 _fileFormat = kFileFormatCompressedFile;
669 break;
670 }
671 case kFileFormatPcm8kHzFile:
672 case kFileFormatPcm16kHzFile:
673 case kFileFormatPcm32kHzFile:
674 case kFileFormatPcm48kHzFile: {
675 if (!ValidFrequency(codecInst.plfreq) ||
676 _ptrFileUtilityObj->InitPCMWriting(stream, codecInst.plfreq) == -1) {
677 RTC_LOG(LS_ERROR) << "Failed to initialize PCM file!";
678 delete _ptrFileUtilityObj;
679 _ptrFileUtilityObj = NULL;
680 return -1;
681 }
682 _fileFormat = format;
683 break;
684 }
685 case kFileFormatPreencodedFile: {
686 if (_ptrFileUtilityObj->InitPreEncodedWriting(stream, codecInst) == -1) {
687 RTC_LOG(LS_ERROR) << "Failed to initialize Pre-Encoded file!";
688 delete _ptrFileUtilityObj;
689 _ptrFileUtilityObj = NULL;
690 return -1;
691 }
692
693 _fileFormat = kFileFormatPreencodedFile;
694 break;
695 }
696 default: {
697 RTC_LOG(LS_ERROR) << "Invalid file format " << format << " specified!";
698 delete _ptrFileUtilityObj;
699 _ptrFileUtilityObj = NULL;
700 return -1;
701 }
702 }
703 _isStereo = (tmpAudioCodec.channels == 2);
704 if (_isStereo) {
705 if (_fileFormat != kFileFormatWavFile) {
706 RTC_LOG(LS_WARNING) << "Stereo is only allowed for WAV files";
707 StopRecording();
708 return -1;
709 }
710 if ((STR_NCASE_CMP(tmpAudioCodec.plname, "L16", 4) != 0) &&
711 (STR_NCASE_CMP(tmpAudioCodec.plname, "PCMU", 5) != 0) &&
712 (STR_NCASE_CMP(tmpAudioCodec.plname, "PCMA", 5) != 0)) {
713 RTC_LOG(LS_WARNING)
714 << "Stereo is only allowed for codec PCMU, PCMA and L16 ";
715 StopRecording();
716 return -1;
717 }
718 }
719 memcpy(&codec_info_, &tmpAudioCodec, sizeof(CodecInst));
720 _recordingActive = true;
721 _ptrOutStream = &stream;
722 _notificationMs = notificationTimeMs;
723 _recordDurationMs = 0;
724 return 0;
725 }
726
StopRecording()727 int32_t MediaFileImpl::StopRecording() {
728 rtc::CritScope lock(&_crit);
729 if (!_recordingActive) {
730 RTC_LOG(LS_WARNING) << "recording is not active!";
731 return -1;
732 }
733
734 _isStereo = false;
735
736 if (_ptrFileUtilityObj != NULL) {
737 // Both AVI and WAV header has to be updated before closing the stream
738 // because they contain size information.
739 if ((_fileFormat == kFileFormatWavFile) && (_ptrOutStream != NULL)) {
740 _ptrFileUtilityObj->UpdateWavHeader(*_ptrOutStream);
741 }
742 delete _ptrFileUtilityObj;
743 _ptrFileUtilityObj = NULL;
744 }
745
746 if (_ptrOutStream != NULL) {
747 // If MediaFileImpl opened the OutStream it must be reclaimed here.
748 if (_openFile) {
749 delete _ptrOutStream;
750 _openFile = false;
751 }
752 _ptrOutStream = NULL;
753 }
754
755 _recordingActive = false;
756 codec_info_.pltype = 0;
757 codec_info_.plname[0] = '\0';
758
759 return 0;
760 }
761
IsRecording()762 bool MediaFileImpl::IsRecording() {
763 RTC_LOG(LS_VERBOSE) << "MediaFileImpl::IsRecording()";
764 rtc::CritScope lock(&_crit);
765 return _recordingActive;
766 }
767
RecordDurationMs(uint32_t & durationMs)768 int32_t MediaFileImpl::RecordDurationMs(uint32_t& durationMs) {
769 rtc::CritScope lock(&_crit);
770 if (!_recordingActive) {
771 durationMs = 0;
772 return -1;
773 }
774 durationMs = _recordDurationMs;
775 return 0;
776 }
777
IsStereo()778 bool MediaFileImpl::IsStereo() {
779 RTC_LOG(LS_VERBOSE) << "MediaFileImpl::IsStereo()";
780 rtc::CritScope lock(&_crit);
781 return _isStereo;
782 }
783
SetModuleFileCallback(FileCallback * callback)784 int32_t MediaFileImpl::SetModuleFileCallback(FileCallback* callback) {
785 rtc::CritScope lock(&_callbackCrit);
786
787 _ptrCallback = callback;
788 return 0;
789 }
790
FileDurationMs(const char * fileName,uint32_t & durationMs,const FileFormats format,const uint32_t freqInHz)791 int32_t MediaFileImpl::FileDurationMs(const char* fileName,
792 uint32_t& durationMs,
793 const FileFormats format,
794 const uint32_t freqInHz) {
795 if (!ValidFileName(fileName)) {
796 return -1;
797 }
798 if (!ValidFrequency(freqInHz)) {
799 return -1;
800 }
801
802 ModuleFileUtility* utilityObj = new ModuleFileUtility();
803 if (utilityObj == NULL) {
804 RTC_LOG(LS_ERROR) << "failed to allocate utility object!";
805 return -1;
806 }
807
808 const int32_t duration =
809 utilityObj->FileDurationMs(fileName, format, freqInHz);
810 delete utilityObj;
811 if (duration == -1) {
812 durationMs = 0;
813 return -1;
814 }
815
816 durationMs = duration;
817 return 0;
818 }
819
PlayoutPositionMs(uint32_t & positionMs) const820 int32_t MediaFileImpl::PlayoutPositionMs(uint32_t& positionMs) const {
821 rtc::CritScope lock(&_crit);
822 if (!_playingActive) {
823 positionMs = 0;
824 return -1;
825 }
826 positionMs = _playoutPositionMs;
827 return 0;
828 }
829
codec_info(CodecInst & codecInst) const830 int32_t MediaFileImpl::codec_info(CodecInst& codecInst) const {
831 rtc::CritScope lock(&_crit);
832 if (!_playingActive && !_recordingActive) {
833 RTC_LOG(LS_ERROR) << "Neither playout nor recording has been initialized!";
834 return -1;
835 }
836 if (codec_info_.pltype == 0 && codec_info_.plname[0] == '\0') {
837 RTC_LOG(LS_ERROR) << "The CodecInst for "
838 << (_playingActive ? "Playback" : "Recording")
839 << " is unknown!";
840 return -1;
841 }
842 memcpy(&codecInst, &codec_info_, sizeof(CodecInst));
843 return 0;
844 }
845
ValidFileFormat(const FileFormats format,const CodecInst * codecInst)846 bool MediaFileImpl::ValidFileFormat(const FileFormats format,
847 const CodecInst* codecInst) {
848 if (codecInst == NULL) {
849 if (format == kFileFormatPreencodedFile ||
850 format == kFileFormatPcm8kHzFile || format == kFileFormatPcm16kHzFile ||
851 format == kFileFormatPcm32kHzFile ||
852 format == kFileFormatPcm48kHzFile) {
853 RTC_LOG(LS_ERROR) << "Codec info required for file format specified!";
854 return false;
855 }
856 }
857 return true;
858 }
859
ValidFileName(const char * fileName)860 bool MediaFileImpl::ValidFileName(const char* fileName) {
861 if ((fileName == NULL) || (fileName[0] == '\0')) {
862 RTC_LOG(LS_ERROR) << "FileName not specified!";
863 return false;
864 }
865 return true;
866 }
867
ValidFilePositions(const uint32_t startPointMs,const uint32_t stopPointMs)868 bool MediaFileImpl::ValidFilePositions(const uint32_t startPointMs,
869 const uint32_t stopPointMs) {
870 if (startPointMs == 0 && stopPointMs == 0) // Default values
871 {
872 return true;
873 }
874 if (stopPointMs && (startPointMs >= stopPointMs)) {
875 RTC_LOG(LS_ERROR) << "startPointMs must be less than stopPointMs!";
876 return false;
877 }
878 if (stopPointMs && ((stopPointMs - startPointMs) < 20)) {
879 RTC_LOG(LS_ERROR) << "minimum play duration for files is 20 ms!";
880 return false;
881 }
882 return true;
883 }
884
ValidFrequency(const uint32_t frequency)885 bool MediaFileImpl::ValidFrequency(const uint32_t frequency) {
886 if ((frequency == 8000) || (frequency == 16000) || (frequency == 32000) ||
887 (frequency == 48000)) {
888 return true;
889 }
890 RTC_LOG(LS_ERROR) << "Frequency should be 8000, 16000, 32000, or 48000 (Hz)";
891 return false;
892 }
893 } // namespace webrtc
894