Home | History | Annotate | Download | only in source
      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 {
     20 MediaFile* MediaFile::CreateMediaFile(const int32_t id)
     21 {
     22     return new MediaFileImpl(id);
     23 }
     24 
     25 void MediaFile::DestroyMediaFile(MediaFile* module)
     26 {
     27     delete static_cast<MediaFileImpl*>(module);
     28 }
     29 
     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 
     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 
     87 int32_t MediaFileImpl::ChangeUniqueId(const int32_t id)
     88 {
     89     _id = id;
     90     return 0;
     91 }
     92 
     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 
    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 
    110 int32_t MediaFileImpl::PlayoutAVIVideoData(
    111     int8_t* buffer,
    112     uint32_t& dataLengthInBytes)
    113 {
    114     return PlayoutData( buffer, dataLengthInBytes, true);
    115 }
    116 
    117 int32_t MediaFileImpl::PlayoutAudioData(int8_t* buffer,
    118                                         uint32_t& dataLengthInBytes)
    119 {
    120     return PlayoutData( buffer, dataLengthInBytes, false);
    121 }
    122 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    684 bool MediaFileImpl::IsPlaying()
    685 {
    686     WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsPlaying()");
    687     CriticalSectionScoped lock(_crit);
    688     return _playingActive;
    689 }
    690 
    691 int32_t MediaFileImpl::IncomingAudioData(
    692     const int8_t*  buffer,
    693     const uint32_t bufferLengthInBytes)
    694 {
    695     return IncomingAudioVideoData( buffer, bufferLengthInBytes, false);
    696 }
    697 
    698 int32_t MediaFileImpl::IncomingAVIVideoData(
    699     const int8_t*  buffer,
    700     const uint32_t bufferLengthInBytes)
    701 {
    702     return IncomingAudioVideoData( buffer, bufferLengthInBytes, true);
    703 }
    704 
    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 
    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 
    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 
    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 
    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 
    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 
   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 
   1172 bool MediaFileImpl::IsRecording()
   1173 {
   1174     WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsRecording()");
   1175     CriticalSectionScoped lock(_crit);
   1176     return _recordingActive;
   1177 }
   1178 
   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 
   1192 bool MediaFileImpl::IsStereo()
   1193 {
   1194     WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsStereo()");
   1195     CriticalSectionScoped lock(_crit);
   1196     return _isStereo;
   1197 }
   1198 
   1199 int32_t MediaFileImpl::SetModuleFileCallback(FileCallback* callback)
   1200 {
   1201 
   1202     CriticalSectionScoped lock(_callbackCrit);
   1203 
   1204     _ptrCallback = callback;
   1205     return 0;
   1206 }
   1207 
   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 
   1244 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 
   1256 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 
   1276 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 
   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 
   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 
   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 
   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