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