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