• 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 "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
12 #include "webrtc/engine_configurations.h"
13 #include "webrtc/modules/media_file/interface/media_file.h"
14 #include "webrtc/modules/utility/source/file_recorder_impl.h"
15 #include "webrtc/system_wrappers/interface/logging.h"
16 
17 #ifdef WEBRTC_MODULE_UTILITY_VIDEO
18     #include "critical_section_wrapper.h"
19     #include "frame_scaler.h"
20     #include "video_coder.h"
21     #include "video_frames_queue.h"
22 #endif
23 
24 namespace webrtc {
CreateFileRecorder(uint32_t instanceID,FileFormats fileFormat)25 FileRecorder* FileRecorder::CreateFileRecorder(uint32_t instanceID,
26                                                FileFormats fileFormat)
27 {
28     switch(fileFormat)
29     {
30     case kFileFormatWavFile:
31     case kFileFormatCompressedFile:
32     case kFileFormatPreencodedFile:
33     case kFileFormatPcm16kHzFile:
34     case kFileFormatPcm8kHzFile:
35     case kFileFormatPcm32kHzFile:
36         return new FileRecorderImpl(instanceID, fileFormat);
37     case kFileFormatAviFile:
38 #ifdef WEBRTC_MODULE_UTILITY_VIDEO
39         return new AviRecorder(instanceID, fileFormat);
40 #else
41         assert(false);
42         return NULL;
43 #endif
44     }
45     assert(false);
46     return NULL;
47 }
48 
DestroyFileRecorder(FileRecorder * recorder)49 void FileRecorder::DestroyFileRecorder(FileRecorder* recorder)
50 {
51     delete recorder;
52 }
53 
FileRecorderImpl(uint32_t instanceID,FileFormats fileFormat)54 FileRecorderImpl::FileRecorderImpl(uint32_t instanceID,
55                                    FileFormats fileFormat)
56     : _instanceID(instanceID),
57       _fileFormat(fileFormat),
58       _moduleFile(MediaFile::CreateMediaFile(_instanceID)),
59       codec_info_(),
60       _amrFormat(AMRFileStorage),
61       _audioBuffer(),
62       _audioEncoder(instanceID),
63       _audioResampler()
64 {
65 }
66 
~FileRecorderImpl()67 FileRecorderImpl::~FileRecorderImpl()
68 {
69     MediaFile::DestroyMediaFile(_moduleFile);
70 }
71 
RecordingFileFormat() const72 FileFormats FileRecorderImpl::RecordingFileFormat() const
73 {
74     return _fileFormat;
75 }
76 
RegisterModuleFileCallback(FileCallback * callback)77 int32_t FileRecorderImpl::RegisterModuleFileCallback(
78     FileCallback* callback)
79 {
80     if(_moduleFile == NULL)
81     {
82         return -1;
83     }
84     return _moduleFile->SetModuleFileCallback(callback);
85 }
86 
StartRecordingAudioFile(const char * fileName,const CodecInst & codecInst,uint32_t notificationTimeMs,ACMAMRPackingFormat amrFormat)87 int32_t FileRecorderImpl::StartRecordingAudioFile(
88     const char* fileName,
89     const CodecInst& codecInst,
90     uint32_t notificationTimeMs,
91     ACMAMRPackingFormat amrFormat)
92 {
93     if(_moduleFile == NULL)
94     {
95         return -1;
96     }
97     codec_info_ = codecInst;
98     _amrFormat = amrFormat;
99 
100     int32_t retVal = 0;
101     if(_fileFormat != kFileFormatAviFile)
102     {
103         // AVI files should be started using StartRecordingVideoFile(..) all
104         // other formats should use this API.
105         retVal =_moduleFile->StartRecordingAudioFile(fileName, _fileFormat,
106                                                      codecInst,
107                                                      notificationTimeMs);
108     }
109 
110     if( retVal == 0)
111     {
112         retVal = SetUpAudioEncoder();
113     }
114     if( retVal != 0)
115     {
116         LOG(LS_WARNING) << "Failed to initialize file " << fileName
117                         << " for recording.";
118 
119         if(IsRecording())
120         {
121             StopRecording();
122         }
123     }
124     return retVal;
125 }
126 
StartRecordingAudioFile(OutStream & destStream,const CodecInst & codecInst,uint32_t notificationTimeMs,ACMAMRPackingFormat amrFormat)127 int32_t FileRecorderImpl::StartRecordingAudioFile(
128     OutStream& destStream,
129     const CodecInst& codecInst,
130     uint32_t notificationTimeMs,
131     ACMAMRPackingFormat amrFormat)
132 {
133     codec_info_ = codecInst;
134     _amrFormat = amrFormat;
135 
136     int32_t retVal = _moduleFile->StartRecordingAudioStream(
137         destStream,
138         _fileFormat,
139         codecInst,
140         notificationTimeMs);
141 
142     if( retVal == 0)
143     {
144         retVal = SetUpAudioEncoder();
145     }
146     if( retVal != 0)
147     {
148         LOG(LS_WARNING) << "Failed to initialize outStream for recording.";
149 
150         if(IsRecording())
151         {
152             StopRecording();
153         }
154     }
155     return retVal;
156 }
157 
StopRecording()158 int32_t FileRecorderImpl::StopRecording()
159 {
160     memset(&codec_info_, 0, sizeof(CodecInst));
161     return _moduleFile->StopRecording();
162 }
163 
IsRecording() const164 bool FileRecorderImpl::IsRecording() const
165 {
166     return _moduleFile->IsRecording();
167 }
168 
RecordAudioToFile(const AudioFrame & incomingAudioFrame,const TickTime * playoutTS)169 int32_t FileRecorderImpl::RecordAudioToFile(
170     const AudioFrame& incomingAudioFrame,
171     const TickTime* playoutTS)
172 {
173     if (codec_info_.plfreq == 0)
174     {
175         LOG(LS_WARNING) << "RecordAudioToFile() recording audio is not "
176                         << "turned on.";
177         return -1;
178     }
179     AudioFrame tempAudioFrame;
180     tempAudioFrame.samples_per_channel_ = 0;
181     if( incomingAudioFrame.num_channels_ == 2 &&
182         !_moduleFile->IsStereo())
183     {
184         // Recording mono but incoming audio is (interleaved) stereo.
185         tempAudioFrame.num_channels_ = 1;
186         tempAudioFrame.sample_rate_hz_ = incomingAudioFrame.sample_rate_hz_;
187         tempAudioFrame.samples_per_channel_ =
188           incomingAudioFrame.samples_per_channel_;
189         for (uint16_t i = 0;
190              i < (incomingAudioFrame.samples_per_channel_); i++)
191         {
192             // Sample value is the average of left and right buffer rounded to
193             // closest integer value. Note samples can be either 1 or 2 byte.
194              tempAudioFrame.data_[i] =
195                  ((incomingAudioFrame.data_[2 * i] +
196                    incomingAudioFrame.data_[(2 * i) + 1] + 1) >> 1);
197         }
198     }
199     else if( incomingAudioFrame.num_channels_ == 1 &&
200         _moduleFile->IsStereo())
201     {
202         // Recording stereo but incoming audio is mono.
203         tempAudioFrame.num_channels_ = 2;
204         tempAudioFrame.sample_rate_hz_ = incomingAudioFrame.sample_rate_hz_;
205         tempAudioFrame.samples_per_channel_ =
206           incomingAudioFrame.samples_per_channel_;
207         for (uint16_t i = 0;
208              i < (incomingAudioFrame.samples_per_channel_); i++)
209         {
210             // Duplicate sample to both channels
211              tempAudioFrame.data_[2*i] =
212                incomingAudioFrame.data_[i];
213              tempAudioFrame.data_[2*i+1] =
214                incomingAudioFrame.data_[i];
215         }
216     }
217 
218     const AudioFrame* ptrAudioFrame = &incomingAudioFrame;
219     if(tempAudioFrame.samples_per_channel_ != 0)
220     {
221         // If ptrAudioFrame is not empty it contains the audio to be recorded.
222         ptrAudioFrame = &tempAudioFrame;
223     }
224 
225     // Encode the audio data before writing to file. Don't encode if the codec
226     // is PCM.
227     // NOTE: stereo recording is only supported for WAV files.
228     // TODO (hellner): WAV expect PCM in little endian byte order. Not
229     // "encoding" with PCM coder should be a problem for big endian systems.
230     uint32_t encodedLenInBytes = 0;
231     if (_fileFormat == kFileFormatPreencodedFile ||
232         STR_CASE_CMP(codec_info_.plname, "L16") != 0)
233     {
234         if (_audioEncoder.Encode(*ptrAudioFrame, _audioBuffer,
235                                  encodedLenInBytes) == -1)
236         {
237             LOG(LS_WARNING) << "RecordAudioToFile() codec "
238                             << codec_info_.plname
239                             << " not supported or failed to encode stream.";
240             return -1;
241         }
242     } else {
243         int outLen = 0;
244         if(ptrAudioFrame->num_channels_ == 2)
245         {
246             // ptrAudioFrame contains interleaved stereo audio.
247             _audioResampler.ResetIfNeeded(ptrAudioFrame->sample_rate_hz_,
248                                           codec_info_.plfreq,
249                                           kResamplerSynchronousStereo);
250             _audioResampler.Push(ptrAudioFrame->data_,
251                                  ptrAudioFrame->samples_per_channel_ *
252                                  ptrAudioFrame->num_channels_,
253                                  (int16_t*)_audioBuffer,
254                                  MAX_AUDIO_BUFFER_IN_BYTES, outLen);
255         } else {
256             _audioResampler.ResetIfNeeded(ptrAudioFrame->sample_rate_hz_,
257                                           codec_info_.plfreq,
258                                           kResamplerSynchronous);
259             _audioResampler.Push(ptrAudioFrame->data_,
260                                  ptrAudioFrame->samples_per_channel_,
261                                  (int16_t*)_audioBuffer,
262                                  MAX_AUDIO_BUFFER_IN_BYTES, outLen);
263         }
264         encodedLenInBytes = outLen * sizeof(int16_t);
265     }
266 
267     // Codec may not be operating at a frame rate of 10 ms. Whenever enough
268     // 10 ms chunks of data has been pushed to the encoder an encoded frame
269     // will be available. Wait until then.
270     if (encodedLenInBytes)
271     {
272         uint16_t msOfData =
273             ptrAudioFrame->samples_per_channel_ /
274             uint16_t(ptrAudioFrame->sample_rate_hz_ / 1000);
275         if (WriteEncodedAudioData(_audioBuffer,
276                                   (uint16_t)encodedLenInBytes,
277                                   msOfData, playoutTS) == -1)
278         {
279             return -1;
280         }
281     }
282     return 0;
283 }
284 
SetUpAudioEncoder()285 int32_t FileRecorderImpl::SetUpAudioEncoder()
286 {
287     if (_fileFormat == kFileFormatPreencodedFile ||
288         STR_CASE_CMP(codec_info_.plname, "L16") != 0)
289     {
290         if(_audioEncoder.SetEncodeCodec(codec_info_,_amrFormat) == -1)
291         {
292             LOG(LS_ERROR) << "SetUpAudioEncoder() codec "
293                           << codec_info_.plname << " not supported.";
294             return -1;
295         }
296     }
297     return 0;
298 }
299 
codec_info(CodecInst & codecInst) const300 int32_t FileRecorderImpl::codec_info(CodecInst& codecInst) const
301 {
302     if(codec_info_.plfreq == 0)
303     {
304         return -1;
305     }
306     codecInst = codec_info_;
307     return 0;
308 }
309 
WriteEncodedAudioData(const int8_t * audioBuffer,uint16_t bufferLength,uint16_t,const TickTime *)310 int32_t FileRecorderImpl::WriteEncodedAudioData(
311     const int8_t* audioBuffer,
312     uint16_t bufferLength,
313     uint16_t /*millisecondsOfData*/,
314     const TickTime* /*playoutTS*/)
315 {
316     return _moduleFile->IncomingAudioData(audioBuffer, bufferLength);
317 }
318 
319 
320 #ifdef WEBRTC_MODULE_UTILITY_VIDEO
AviRecorder(uint32_t instanceID,FileFormats fileFormat)321 AviRecorder::AviRecorder(uint32_t instanceID, FileFormats fileFormat)
322     : FileRecorderImpl(instanceID, fileFormat),
323       _videoOnly(false),
324       _thread( 0),
325       _timeEvent(*EventWrapper::Create()),
326       _critSec(CriticalSectionWrapper::CreateCriticalSection()),
327       _writtenVideoFramesCounter(0),
328       _writtenAudioMS(0),
329       _writtenVideoMS(0)
330 {
331     _videoEncoder = new VideoCoder();
332     _frameScaler = new FrameScaler();
333     _videoFramesQueue = new VideoFramesQueue();
334     _thread = ThreadWrapper::CreateThread(Run, this, kNormalPriority,
335                                           "AviRecorder()");
336 }
337 
~AviRecorder()338 AviRecorder::~AviRecorder( )
339 {
340     StopRecording( );
341 
342     delete _videoEncoder;
343     delete _frameScaler;
344     delete _videoFramesQueue;
345     delete _thread;
346     delete &_timeEvent;
347     delete _critSec;
348 }
349 
StartRecordingVideoFile(const char * fileName,const CodecInst & audioCodecInst,const VideoCodec & videoCodecInst,ACMAMRPackingFormat amrFormat,bool videoOnly)350 int32_t AviRecorder::StartRecordingVideoFile(
351     const char* fileName,
352     const CodecInst& audioCodecInst,
353     const VideoCodec& videoCodecInst,
354     ACMAMRPackingFormat amrFormat,
355     bool videoOnly)
356 {
357     _firstAudioFrameReceived = false;
358     _videoCodecInst = videoCodecInst;
359     _videoOnly = videoOnly;
360 
361     if(_moduleFile->StartRecordingVideoFile(fileName, _fileFormat,
362                                             audioCodecInst, videoCodecInst,
363                                             videoOnly) != 0)
364     {
365         return -1;
366     }
367 
368     if(!videoOnly)
369     {
370         if(FileRecorderImpl::StartRecordingAudioFile(fileName,audioCodecInst, 0,
371                                                      amrFormat) !=0)
372         {
373             StopRecording();
374             return -1;
375         }
376     }
377     if( SetUpVideoEncoder() != 0)
378     {
379         StopRecording();
380         return -1;
381     }
382     if(_videoOnly)
383     {
384         // Writing to AVI file is non-blocking.
385         // Start non-blocking timer if video only. If recording both video and
386         // audio let the pushing of audio frames be the timer.
387         _timeEvent.StartTimer(true, 1000 / _videoCodecInst.maxFramerate);
388     }
389     StartThread();
390     return 0;
391 }
392 
StopRecording()393 int32_t AviRecorder::StopRecording()
394 {
395     _timeEvent.StopTimer();
396 
397     StopThread();
398     return FileRecorderImpl::StopRecording();
399 }
400 
CalcI420FrameSize() const401 int32_t AviRecorder::CalcI420FrameSize( ) const
402 {
403     return 3 * _videoCodecInst.width * _videoCodecInst.height / 2;
404 }
405 
SetUpVideoEncoder()406 int32_t AviRecorder::SetUpVideoEncoder()
407 {
408     // Size of unencoded data (I420) should be the largest possible frame size
409     // in a file.
410     _videoMaxPayloadSize = CalcI420FrameSize();
411     _videoEncodedData.VerifyAndAllocate(_videoMaxPayloadSize);
412 
413     _videoCodecInst.plType = _videoEncoder->DefaultPayloadType(
414         _videoCodecInst.plName);
415 
416     int32_t useNumberOfCores = 1;
417     // Set the max payload size to 16000. This means that the codec will try to
418     // create slices that will fit in 16000 kByte packets. However, the
419     // Encode() call will still generate one full frame.
420     if(_videoEncoder->SetEncodeCodec(_videoCodecInst, useNumberOfCores,
421                                      16000))
422     {
423         return -1;
424     }
425     return 0;
426 }
427 
RecordVideoToFile(const I420VideoFrame & videoFrame)428 int32_t AviRecorder::RecordVideoToFile(const I420VideoFrame& videoFrame)
429 {
430     CriticalSectionScoped lock(_critSec);
431     if(!IsRecording() || videoFrame.IsZeroSize())
432     {
433         return -1;
434     }
435     // The frame is written to file in AviRecorder::Process().
436     int32_t retVal = _videoFramesQueue->AddFrame(videoFrame);
437     if(retVal != 0)
438     {
439         StopRecording();
440     }
441     return retVal;
442 }
443 
StartThread()444 bool AviRecorder::StartThread()
445 {
446     unsigned int id;
447     if( _thread == 0)
448     {
449         return false;
450     }
451 
452     return _thread->Start(id);
453 }
454 
StopThread()455 bool AviRecorder::StopThread()
456 {
457     _critSec->Enter();
458 
459     if(_thread)
460     {
461         _thread->SetNotAlive();
462 
463         ThreadWrapper* thread = _thread;
464         _thread = NULL;
465 
466         _timeEvent.Set();
467 
468         _critSec->Leave();
469 
470         if(thread->Stop())
471         {
472             delete thread;
473         } else {
474             return false;
475         }
476     } else {
477         _critSec->Leave();
478     }
479     return true;
480 }
481 
Run(ThreadObj threadObj)482 bool AviRecorder::Run( ThreadObj threadObj)
483 {
484     return static_cast<AviRecorder*>( threadObj)->Process();
485 }
486 
ProcessAudio()487 int32_t AviRecorder::ProcessAudio()
488 {
489     if (_writtenVideoFramesCounter == 0)
490     {
491         // Get the most recent frame that is due for writing to file. Since
492         // frames are unencoded it's safe to throw away frames if necessary
493         // for synchronizing audio and video.
494         I420VideoFrame* frameToProcess = _videoFramesQueue->FrameToRecord();
495         if(frameToProcess)
496         {
497             // Syncronize audio to the current frame to process by throwing away
498             // audio samples with older timestamp than the video frame.
499             size_t numberOfAudioElements =
500                 _audioFramesToWrite.size();
501             for (size_t i = 0; i < numberOfAudioElements; ++i)
502             {
503                 AudioFrameFileInfo* frameInfo = _audioFramesToWrite.front();
504                 if(TickTime::TicksToMilliseconds(
505                        frameInfo->_playoutTS.Ticks()) <
506                    frameToProcess->render_time_ms())
507                 {
508                     delete frameInfo;
509                     _audioFramesToWrite.pop_front();
510                 } else
511                 {
512                     break;
513                 }
514             }
515         }
516     }
517     // Write all audio up to current timestamp.
518     int32_t error = 0;
519     size_t numberOfAudioElements = _audioFramesToWrite.size();
520     for (size_t i = 0; i < numberOfAudioElements; ++i)
521     {
522         AudioFrameFileInfo* frameInfo = _audioFramesToWrite.front();
523         if((TickTime::Now() - frameInfo->_playoutTS).Milliseconds() > 0)
524         {
525             _moduleFile->IncomingAudioData(frameInfo->_audioData,
526                                            frameInfo->_audioSize);
527             _writtenAudioMS += frameInfo->_audioMS;
528             delete frameInfo;
529             _audioFramesToWrite.pop_front();
530         } else {
531             break;
532         }
533     }
534     return error;
535 }
536 
Process()537 bool AviRecorder::Process()
538 {
539     switch(_timeEvent.Wait(500))
540     {
541     case kEventSignaled:
542         if(_thread == NULL)
543         {
544             return false;
545         }
546         break;
547     case kEventError:
548         return false;
549     case kEventTimeout:
550         // No events triggered. No work to do.
551         return true;
552     }
553     CriticalSectionScoped lock( _critSec);
554 
555     // Get the most recent frame to write to file (if any). Synchronize it with
556     // the audio stream (if any). Synchronization the video based on its render
557     // timestamp (i.e. VideoFrame::RenderTimeMS())
558     I420VideoFrame* frameToProcess = _videoFramesQueue->FrameToRecord();
559     if( frameToProcess == NULL)
560     {
561         return true;
562     }
563     int32_t error = 0;
564     if(!_videoOnly)
565     {
566         if(!_firstAudioFrameReceived)
567         {
568             // Video and audio can only be synchronized if both have been
569             // received.
570             return true;
571         }
572         error = ProcessAudio();
573 
574         while (_writtenAudioMS > _writtenVideoMS)
575         {
576             error = EncodeAndWriteVideoToFile( *frameToProcess);
577             if( error != 0)
578             {
579                 LOG(LS_ERROR) << "AviRecorder::Process() error writing to "
580                               << "file.";
581                 break;
582             } else {
583                 uint32_t frameLengthMS = 1000 /
584                     _videoCodecInst.maxFramerate;
585                 _writtenVideoFramesCounter++;
586                 _writtenVideoMS += frameLengthMS;
587                 // A full seconds worth of frames have been written.
588                 if(_writtenVideoFramesCounter%_videoCodecInst.maxFramerate == 0)
589                 {
590                     // Frame rate is in frames per seconds. Frame length is
591                     // calculated as an integer division which means it may
592                     // be rounded down. Compensate for this every second.
593                     uint32_t rest = 1000 % frameLengthMS;
594                     _writtenVideoMS += rest;
595                 }
596             }
597         }
598     } else {
599         // Frame rate is in frames per seconds. Frame length is calculated as an
600         // integer division which means it may be rounded down. This introduces
601         // drift. Once a full frame worth of drift has happened, skip writing
602         // one frame. Note that frame rate is in frames per second so the
603         // drift is completely compensated for.
604         uint32_t frameLengthMS = 1000/_videoCodecInst.maxFramerate;
605         uint32_t restMS = 1000 % frameLengthMS;
606         uint32_t frameSkip = (_videoCodecInst.maxFramerate *
607                               frameLengthMS) / restMS;
608 
609         _writtenVideoFramesCounter++;
610         if(_writtenVideoFramesCounter % frameSkip == 0)
611         {
612             _writtenVideoMS += frameLengthMS;
613             return true;
614         }
615 
616         error = EncodeAndWriteVideoToFile( *frameToProcess);
617         if(error != 0)
618         {
619             LOG(LS_ERROR) << "AviRecorder::Process() error writing to file.";
620         } else {
621             _writtenVideoMS += frameLengthMS;
622         }
623     }
624     return error == 0;
625 }
626 
EncodeAndWriteVideoToFile(I420VideoFrame & videoFrame)627 int32_t AviRecorder::EncodeAndWriteVideoToFile(I420VideoFrame& videoFrame)
628 {
629     if (!IsRecording() || videoFrame.IsZeroSize())
630     {
631         return -1;
632     }
633 
634     if(_frameScaler->ResizeFrameIfNeeded(&videoFrame, _videoCodecInst.width,
635                                          _videoCodecInst.height) != 0)
636     {
637         return -1;
638     }
639 
640     _videoEncodedData.payloadSize = 0;
641 
642     if( STR_CASE_CMP(_videoCodecInst.plName, "I420") == 0)
643     {
644        int length  = CalcBufferSize(kI420, videoFrame.width(),
645                                     videoFrame.height());
646         _videoEncodedData.VerifyAndAllocate(length);
647 
648         // I420 is raw data. No encoding needed (each sample is represented by
649         // 1 byte so there is no difference depending on endianness).
650         int ret_length = ExtractBuffer(videoFrame, length,
651                                        _videoEncodedData.payloadData);
652         if (ret_length < 0)
653           return -1;
654 
655         _videoEncodedData.payloadSize = ret_length;
656         _videoEncodedData.frameType = kVideoFrameKey;
657     }else {
658         if( _videoEncoder->Encode(videoFrame, _videoEncodedData) != 0)
659         {
660             return -1;
661         }
662     }
663 
664     if(_videoEncodedData.payloadSize > 0)
665     {
666         if(_moduleFile->IncomingAVIVideoData(
667                (int8_t*)(_videoEncodedData.payloadData),
668                _videoEncodedData.payloadSize))
669         {
670             LOG(LS_ERROR) << "Error writing AVI file.";
671             return -1;
672         }
673     } else {
674         LOG(LS_ERROR) << "FileRecorder::RecordVideoToFile() frame dropped by "
675                       << "encoder, bitrate likely too low.";
676     }
677     return 0;
678 }
679 
680 // Store audio frame in the _audioFramesToWrite buffer. The writing to file
681 // happens in AviRecorder::Process().
WriteEncodedAudioData(const int8_t * audioBuffer,uint16_t bufferLength,uint16_t millisecondsOfData,const TickTime * playoutTS)682 int32_t AviRecorder::WriteEncodedAudioData(
683     const int8_t* audioBuffer,
684     uint16_t bufferLength,
685     uint16_t millisecondsOfData,
686     const TickTime* playoutTS)
687 {
688     CriticalSectionScoped lock(_critSec);
689 
690     if (!IsRecording())
691     {
692         return -1;
693     }
694     if (bufferLength > MAX_AUDIO_BUFFER_IN_BYTES)
695     {
696         return -1;
697     }
698     if (_videoOnly)
699     {
700         return -1;
701     }
702     if (_audioFramesToWrite.size() > kMaxAudioBufferQueueLength)
703     {
704         StopRecording();
705         return -1;
706     }
707     _firstAudioFrameReceived = true;
708 
709     if(playoutTS)
710     {
711         _audioFramesToWrite.push_back(new AudioFrameFileInfo(audioBuffer,
712                                                              bufferLength,
713                                                              millisecondsOfData,
714                                                              *playoutTS));
715     } else {
716         _audioFramesToWrite.push_back(new AudioFrameFileInfo(audioBuffer,
717                                                              bufferLength,
718                                                              millisecondsOfData,
719                                                              TickTime::Now()));
720     }
721     _timeEvent.Set();
722     return 0;
723 }
724 
725 #endif // WEBRTC_MODULE_UTILITY_VIDEO
726 }  // namespace webrtc
727