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