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