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