• 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 // TODO(henrike): reassess the error handling in this class. Currently failure
12 // is detected by asserts in many places. Also a refactoring of this class would
13 // be beneficial.
14 
15 #include "webrtc/modules/media_file/source/avi_file.h"
16 
17 #include <assert.h>
18 #include <string.h>
19 
20 #ifdef _WIN32
21 #include <windows.h>
22 #endif
23 
24 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
25 #include "webrtc/system_wrappers/interface/file_wrapper.h"
26 #include "webrtc/system_wrappers/interface/trace.h"
27 
28 // http://msdn2.microsoft.com/en-us/library/ms779636.aspx
29 // A chunk has the following form:
30 // ckID ckSize ckData
31 // where ckID is a FOURCC that identifies the data contained in the
32 // chunk, ckData is a 4-byte value giving the size of the data in
33 // ckData, and ckData is zero or more bytes of data. The data is always
34 // padded to nearest WORD boundary. ckSize gives the size of the valid
35 // data in the chunk; it does not include the padding, the size of
36 // ckID, or the size of ckSize.
37 //http://msdn2.microsoft.com/en-us/library/ms779632.aspx
38 //NOTE: Workaround to make MPEG4 files play on WMP. MPEG files
39 //      place the config parameters efter the BITMAPINFOHEADER and
40 //      *NOT* in the 'strd'!
41 // http://msdn.microsoft.com/en-us/library/dd183375.aspx
42 // http://msdn.microsoft.com/en-us/library/dd183376.aspx
43 
44 namespace webrtc {
45 namespace {
46 static const uint32_t kAvifHasindex       = 0x00000010;
47 static const uint32_t kAvifMustuseindex   = 0x00000020;
48 static const uint32_t kAvifIsinterleaved  = 0x00000100;
49 static const uint32_t kAvifTrustcktype    = 0x00000800;
50 static const uint32_t kAvifWascapturefile = 0x00010000;
51 
52 template <class T>
MinValue(T a,T b)53 T MinValue(T a, T b)
54 {
55     return a < b ? a : b;
56 }
57 }  // namespace
58 
AVIMAINHEADER()59 AviFile::AVIMAINHEADER::AVIMAINHEADER()
60     : fcc(                  0),
61       cb(                   0),
62       dwMicroSecPerFrame(   0),
63       dwMaxBytesPerSec(     0),
64       dwPaddingGranularity( 0),
65       dwFlags(              0),
66       dwTotalFrames(        0),
67       dwInitialFrames(      0),
68       dwStreams(            0),
69       dwSuggestedBufferSize(0),
70       dwWidth(              0),
71       dwHeight(             0)
72 {
73     dwReserved[0] = 0;
74     dwReserved[1] = 0;
75     dwReserved[2] = 0;
76     dwReserved[3] = 0;
77 }
78 
AVISTREAMHEADER()79 AVISTREAMHEADER::AVISTREAMHEADER()
80     : fcc(                  0),
81       cb(                   0),
82       fccType(              0),
83       fccHandler(           0),
84       dwFlags(              0),
85       wPriority(            0),
86       wLanguage(            0),
87       dwInitialFrames(      0),
88       dwScale(              0),
89       dwRate(               0),
90       dwStart(              0),
91       dwLength(             0),
92       dwSuggestedBufferSize(0),
93       dwQuality(            0),
94       dwSampleSize(         0)
95 {
96     rcFrame.left   = 0;
97     rcFrame.top    = 0;
98     rcFrame.right  = 0;
99     rcFrame.bottom = 0;
100 }
101 
BITMAPINFOHEADER()102 BITMAPINFOHEADER::BITMAPINFOHEADER()
103     : biSize(         0),
104       biWidth(        0),
105       biHeight(       0),
106       biPlanes(       0),
107       biBitCount(     0),
108       biCompression(  0),
109       biSizeImage(    0),
110       biXPelsPerMeter(0),
111       biYPelsPerMeter(0),
112       biClrUsed(      0),
113       biClrImportant( 0)
114 {
115 }
116 
WAVEFORMATEX()117 WAVEFORMATEX::WAVEFORMATEX()
118     : wFormatTag(     0),
119       nChannels(      0),
120       nSamplesPerSec( 0),
121       nAvgBytesPerSec(0),
122       nBlockAlign(    0),
123       wBitsPerSample( 0),
124       cbSize(         0)
125 {
126 }
127 
AVIINDEXENTRY(uint32_t inckid,uint32_t indwFlags,uint32_t indwChunkOffset,uint32_t indwChunkLength)128 AviFile::AVIINDEXENTRY::AVIINDEXENTRY(uint32_t inckid,
129                                       uint32_t indwFlags,
130                                       uint32_t indwChunkOffset,
131                                       uint32_t indwChunkLength)
132     : ckid(inckid),
133       dwFlags(indwFlags),
134       dwChunkOffset(indwChunkOffset),
135       dwChunkLength(indwChunkLength)
136 {
137 }
138 
AviFile()139 AviFile::AviFile()
140     : _crit(CriticalSectionWrapper::CreateCriticalSection()),
141       _aviFile(NULL),
142       _aviHeader(),
143       _videoStreamHeader(),
144       _audioStreamHeader(),
145       _videoFormatHeader(),
146       _audioFormatHeader(),
147       _videoConfigParameters(),
148       _videoConfigLength(0),
149       _videoStreamName(),
150       _audioConfigParameters(),
151       _audioStreamName(),
152       _videoStream(),
153       _audioStream(),
154       _nrStreams(0),
155       _aviLength(0),
156       _dataLength(0),
157       _bytesRead(0),
158       _dataStartByte(0),
159       _framesRead(0),
160       _videoFrames(0),
161       _audioFrames(0),
162       _reading(false),
163       _openedAs(AVI_AUDIO),
164       _loop(false),
165       _writing(false),
166       _bytesWritten(0),
167       _riffSizeMark(0),
168       _moviSizeMark(0),
169       _totNumFramesMark(0),
170       _videoStreamLengthMark(0),
171       _audioStreamLengthMark(0),
172       _moviListOffset(0),
173       _writeAudioStream(false),
174       _writeVideoStream(false),
175       _aviMode(NotSet),
176       _videoCodecConfigParams(NULL),
177       _videoCodecConfigParamsLength(0),
178       _videoStreamDataChunkPrefix(0),
179       _audioStreamDataChunkPrefix(0),
180       _created(false)
181 {
182   ResetComplexMembers();
183 }
184 
~AviFile()185 AviFile::~AviFile()
186 {
187     Close();
188 
189     delete[] _videoCodecConfigParams;
190     delete _crit;
191 }
192 
Open(AVIStreamType streamType,const char * fileName,bool loop)193 int32_t AviFile::Open(AVIStreamType streamType, const char* fileName, bool loop)
194 {
195     WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1,  "OpenAVIFile(%s)",
196                  fileName);
197     _crit->Enter();
198 
199     if (_aviMode != NotSet)
200     {
201         _crit->Leave();
202         return -1;
203     }
204 
205     _aviMode = Read;
206 
207     if (!fileName)
208     {
209         _crit->Leave();
210         WEBRTC_TRACE(kTraceError, kTraceVideo, -1,  "\tfileName not valid!");
211         return -1;
212     }
213 
214 #ifdef _WIN32
215     // fopen does not support wide characters on Windows, ergo _wfopen.
216     wchar_t wideFileName[FileWrapper::kMaxFileNameSize];
217     wideFileName[0] = 0;
218     MultiByteToWideChar(CP_UTF8,0,fileName, -1, // convert the whole string
219                         wideFileName, FileWrapper::kMaxFileNameSize);
220 
221     _aviFile = _wfopen(wideFileName, L"rb");
222 #else
223     _aviFile = fopen(fileName, "rb");
224 #endif
225 
226     if (!_aviFile)
227     {
228         _crit->Leave();
229         WEBRTC_TRACE(kTraceError, kTraceVideo, -1,  "Could not open file!");
230         return -1;
231     }
232 
233     // ReadRIFF verifies that the file is AVI and figures out the file length.
234     int32_t err = ReadRIFF();
235     if (err)
236     {
237         if (_aviFile)
238         {
239             fclose(_aviFile);
240             _aviFile = NULL;
241         }
242         _crit->Leave();
243         return -1;
244     }
245 
246    err = ReadHeaders();
247     if (err)
248     {
249         if (_aviFile)
250         {
251             fclose(_aviFile);
252             _aviFile = NULL;
253         }
254         _crit->Leave();
255         WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
256                      "Unsupported or corrupt AVI format");
257         return -1;
258     }
259 
260     _dataStartByte = _bytesRead;
261     _reading = true;
262     _openedAs = streamType;
263     _loop = loop;
264     _crit->Leave();
265     return 0;
266 }
267 
Close()268 int32_t AviFile::Close()
269 {
270     _crit->Enter();
271     switch (_aviMode)
272     {
273     case Read:
274         CloseRead();
275         break;
276     case Write:
277         CloseWrite();
278         break;
279     default:
280         break;
281     }
282 
283     if (_videoCodecConfigParams)
284     {
285         delete [] _videoCodecConfigParams;
286         _videoCodecConfigParams = 0;
287     }
288     ResetMembers();
289     _crit->Leave();
290     return 0;
291 }
292 
MakeFourCc(uint8_t ch0,uint8_t ch1,uint8_t ch2,uint8_t ch3)293 uint32_t AviFile::MakeFourCc(uint8_t ch0, uint8_t ch1, uint8_t ch2, uint8_t ch3)
294 {
295     return ((uint32_t)(uint8_t)(ch0)         |
296             ((uint32_t)(uint8_t)(ch1) << 8)  |
297             ((uint32_t)(uint8_t)(ch2) << 16) |
298             ((uint32_t)(uint8_t)(ch3) << 24 ));
299 }
300 
GetVideoStreamInfo(AVISTREAMHEADER & videoStreamHeader,BITMAPINFOHEADER & bitmapInfo,char * codecConfigParameters,int32_t & configLength)301 int32_t AviFile::GetVideoStreamInfo(AVISTREAMHEADER& videoStreamHeader,
302                                     BITMAPINFOHEADER& bitmapInfo,
303                                     char* codecConfigParameters,
304                                     int32_t& configLength)
305 {
306     _crit->Enter();
307     if (!_reading && !_created)
308     {
309         _crit->Leave();
310         return -1;
311     }
312 
313     memcpy(&videoStreamHeader, &_videoStreamHeader, sizeof(_videoStreamHeader));
314     memcpy(&bitmapInfo, &_videoFormatHeader, sizeof(_videoFormatHeader));
315 
316     if (configLength <= _videoConfigLength)
317     {
318         memcpy(codecConfigParameters, _videoConfigParameters,
319                _videoConfigLength);
320         configLength = _videoConfigLength;
321     }
322     else
323     {
324         configLength = 0;
325     }
326     _crit->Leave();
327     return 0;
328 }
329 
GetDuration(int32_t & durationMs)330 int32_t AviFile::GetDuration(int32_t& durationMs)
331 {
332     _crit->Enter();
333     if (_videoStreamHeader.dwRate==0 || _videoStreamHeader.dwScale==0)
334     {
335         _crit->Leave();
336         return -1;
337     }
338 
339     durationMs = _videoStreamHeader.dwLength * 1000 /
340         (_videoStreamHeader.dwRate/_videoStreamHeader.dwScale);
341     _crit->Leave();
342     return 0;
343 }
344 
GetAudioStreamInfo(WAVEFORMATEX & waveHeader)345 int32_t AviFile::GetAudioStreamInfo(WAVEFORMATEX& waveHeader)
346 {
347     _crit->Enter();
348     if (_aviMode != Read)
349     {
350         _crit->Leave();
351         return -1;
352     }
353     if (!_reading && !_created)
354     {
355         _crit->Leave();
356         return -1;
357     }
358     memcpy(&waveHeader, &_audioFormatHeader, sizeof(_audioFormatHeader));
359     _crit->Leave();
360     return 0;
361 }
362 
WriteAudio(const uint8_t * data,int32_t length)363 int32_t AviFile::WriteAudio(const uint8_t* data, int32_t length)
364 {
365     _crit->Enter();
366     size_t newBytesWritten = _bytesWritten;
367 
368     if (_aviMode != Write)
369     {
370         _crit->Leave();
371         return -1;
372     }
373     if (!_created)
374     {
375         _crit->Leave();
376         return -1;
377     }
378     if (!_writeAudioStream)
379     {
380         _crit->Leave();
381         return -1;
382     }
383 
384     // Start of chunk.
385     const uint32_t chunkOffset = ftell(_aviFile) - _moviListOffset;
386     _bytesWritten += PutLE32(_audioStreamDataChunkPrefix);
387     // Size is unknown at this point. Update later.
388     _bytesWritten += PutLE32(0);
389     const size_t chunkSizeMark = _bytesWritten;
390 
391     _bytesWritten += PutBuffer(data, length);
392 
393     const long chunkSize = PutLE32LengthFromCurrent(
394         static_cast<long>(chunkSizeMark));
395 
396     // Make sure that the chunk is aligned on 2 bytes (= 1 sample).
397     if (chunkSize % 2)
398     {
399         _bytesWritten += PutByte(0);
400     }
401     // End of chunk
402 
403     // Save chunk information for use when closing file.
404     AddChunkToIndexList(_audioStreamDataChunkPrefix, 0, // No flags.
405                         chunkOffset, chunkSize);
406 
407     ++_audioFrames;
408     newBytesWritten = _bytesWritten - newBytesWritten;
409     _crit->Leave();
410     return static_cast<int32_t>(newBytesWritten);
411 }
412 
WriteVideo(const uint8_t * data,int32_t length)413 int32_t AviFile::WriteVideo(const uint8_t* data, int32_t length)
414 {
415     _crit->Enter();
416     size_t newBytesWritten = _bytesWritten;
417     if (_aviMode != Write)
418     {
419         _crit->Leave();
420         return -1;
421     }
422     if (!_created)
423     {
424         _crit->Leave();
425         return -1;
426     }
427     if (!_writeVideoStream)
428     {
429         _crit->Leave();
430         return -1;
431     }
432 
433     // Start of chunk.
434     const uint32_t chunkOffset = ftell(_aviFile) - _moviListOffset;
435     _bytesWritten += PutLE32(_videoStreamDataChunkPrefix);
436     // Size is unknown at this point. Update later.
437     _bytesWritten += PutLE32(0);
438     const size_t chunkSizeMark = _bytesWritten;
439 
440     _bytesWritten += PutBuffer(data, length);
441 
442     const long chunkSize = PutLE32LengthFromCurrent(
443         static_cast<long>(chunkSizeMark));
444 
445     // Make sure that the chunk is aligned on 2 bytes (= 1 sample).
446     if (chunkSize % 2)
447     {
448         //Pad one byte, to WORD align.
449         _bytesWritten += PutByte(0);
450     }
451      //End chunk!
452     AddChunkToIndexList(_videoStreamDataChunkPrefix, 0, // No flags.
453                         chunkOffset, static_cast<uint32_t>(chunkSize));
454 
455     ++_videoFrames;
456     newBytesWritten = _bytesWritten - newBytesWritten;
457     _crit->Leave();
458     return static_cast<int32_t>(newBytesWritten);
459 }
460 
PrepareDataChunkHeaders()461 int32_t AviFile::PrepareDataChunkHeaders()
462 {
463     // 00 video stream, 01 audio stream.
464     // db uncompresses video,  dc compressed video, wb WAV audio
465     if (_writeVideoStream)
466     {
467         if (strncmp((const char*) &_videoStreamHeader.fccHandler, "I420", 4) ==
468             0)
469         {
470             _videoStreamDataChunkPrefix = MakeFourCc('0', '0', 'd', 'b');
471         }
472         else
473         {
474             _videoStreamDataChunkPrefix = MakeFourCc('0', '0', 'd', 'c');
475         }
476         _audioStreamDataChunkPrefix = MakeFourCc('0', '1', 'w', 'b');
477     }
478     else
479     {
480         _audioStreamDataChunkPrefix = MakeFourCc('0', '0', 'w', 'b');
481     }
482     return 0;
483 }
484 
ReadMoviSubChunk(uint8_t * data,int32_t & length,uint32_t tag1,uint32_t tag2)485 int32_t AviFile::ReadMoviSubChunk(uint8_t* data, int32_t& length, uint32_t tag1,
486                                   uint32_t tag2)
487 {
488     if (!_reading)
489     {
490         WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1,
491                      "AviFile::ReadMoviSubChunk(): File not open!");
492         length = 0;
493         return -1;
494     }
495 
496     uint32_t size;
497     bool isEOFReached = false;
498     // Try to read one data chunk header
499     while (true)
500     {
501         // TODO (hellner): what happens if an empty AVI file is opened with
502         // _loop set to true? Seems like this while-loop would never exit!
503 
504         // tag = db uncompresses video,  dc compressed video or wb WAV audio.
505         uint32_t tag;
506         _bytesRead += GetLE32(tag);
507         _bytesRead += GetLE32(size);
508 
509         const int32_t eof = feof(_aviFile);
510         if (!eof)
511         {
512             if (tag == tag1)
513             {
514                 // Supported tag found.
515                 break;
516             }
517             else if ((tag == tag2) && (tag2 != 0))
518             {
519                 // Supported tag found.
520                 break;
521             }
522 
523             // Jump to next chunk. The size is in bytes but chunks are aligned
524             // on 2 byte boundaries.
525             const uint32_t seekSize = (size % 2) ? size + 1 : size;
526             const int32_t err = fseek(_aviFile, seekSize, SEEK_CUR);
527 
528             if (err)
529             {
530                 isEOFReached = true;
531             }
532         }
533         else
534         {
535             isEOFReached = true;
536         }
537 
538         if (isEOFReached)
539         {
540             clearerr(_aviFile);
541 
542             if (_loop)
543             {
544                 WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1,
545                              "AviFile::ReadMoviSubChunk(): Reached end of AVI\
546                               data file, starting from the beginning.");
547 
548                 fseek(_aviFile, static_cast<long>(_dataStartByte), SEEK_SET);
549 
550                 _bytesRead = _dataStartByte;
551                 _framesRead = 0;
552                 isEOFReached = false;
553             }
554             else
555             {
556                 WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1,
557                              "AviFile::ReadMoviSubChunk(): Reached end of AVI\
558                              file!");
559                 length = 0;
560                 return -1;
561             }
562         }
563         _bytesRead += size;
564     }
565 
566     if (static_cast<int32_t>(size) > length)
567     {
568         WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1,
569                      "AviFile::ReadMoviSubChunk(): AVI read buffer too small!");
570 
571         // Jump to next chunk. The size is in bytes but chunks are aligned
572         // on 2 byte boundaries.
573         const uint32_t seekSize = (size % 2) ? size + 1 : size;
574         fseek(_aviFile, seekSize, SEEK_CUR);
575         _bytesRead += seekSize;
576         length = 0;
577         return -1;
578     }
579     _bytesRead += GetBuffer(data, size);
580 
581     // The size is in bytes but chunks are aligned on 2 byte boundaries.
582     if (size % 2)
583     {
584         uint8_t dummy_byte;
585         _bytesRead += GetByte(dummy_byte);
586     }
587     length = size;
588     ++_framesRead;
589     return 0;
590 }
591 
ReadAudio(uint8_t * data,int32_t & length)592 int32_t AviFile::ReadAudio(uint8_t* data, int32_t& length)
593 {
594     _crit->Enter();
595     WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1,  "AviFile::ReadAudio()");
596 
597     if (_aviMode != Read)
598     {
599         _crit->Leave();
600         return -1;
601     }
602     if (_openedAs != AVI_AUDIO)
603     {
604         length = 0;
605         _crit->Leave();
606         WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1,  "File not open as audio!");
607         return -1;
608     }
609 
610     const int32_t ret = ReadMoviSubChunk(
611         data,
612         length,
613         StreamAndTwoCharCodeToTag(_audioStream.streamNumber, "wb"));
614 
615     _crit->Leave();
616     return ret;
617 }
618 
ReadVideo(uint8_t * data,int32_t & length)619 int32_t AviFile::ReadVideo(uint8_t* data, int32_t& length)
620 {
621     WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1, "AviFile::ReadVideo()");
622 
623     _crit->Enter();
624     if (_aviMode != Read)
625     {
626         //Has to be Read!
627         _crit->Leave();
628         return -1;
629     }
630     if (_openedAs != AVI_VIDEO)
631     {
632         length = 0;
633         _crit->Leave();
634         WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1, "File not open as video!");
635         return -1;
636     }
637 
638     const int32_t ret = ReadMoviSubChunk(
639         data,
640         length,
641         StreamAndTwoCharCodeToTag(_videoStream.streamNumber, "dc"),
642         StreamAndTwoCharCodeToTag(_videoStream.streamNumber, "db"));
643     _crit->Leave();
644     return ret;
645 }
646 
Create(const char * fileName)647 int32_t AviFile::Create(const char* fileName)
648 {
649     _crit->Enter();
650     if (_aviMode != Write)
651     {
652         _crit->Leave();
653         return -1;
654     }
655 
656     if (!_writeVideoStream && !_writeAudioStream)
657     {
658         _crit->Leave();
659         return -1;
660     }
661     if (_created)
662     {
663         _crit->Leave();
664         return -1;
665     }
666 
667 #ifdef _WIN32
668     // fopen does not support wide characters on Windows, ergo _wfopen.
669     wchar_t wideFileName[FileWrapper::kMaxFileNameSize];
670     wideFileName[0] = 0;
671 
672     MultiByteToWideChar(CP_UTF8,0,fileName, -1, // convert the whole string
673                         wideFileName, FileWrapper::kMaxFileNameSize);
674 
675     _aviFile = _wfopen(wideFileName, L"w+b");
676     if (!_aviFile)
677     {
678         _crit->Leave();
679         return -1;
680     }
681 #else
682     _aviFile = fopen(fileName, "w+b");
683     if (!_aviFile)
684     {
685         _crit->Leave();
686         return -1;
687     }
688 #endif
689 
690     WriteRIFF();
691     WriteHeaders();
692 
693     _created = true;
694 
695     PrepareDataChunkHeaders();
696     ClearIndexList();
697     WriteMoviStart();
698     _aviMode = Write;
699     _crit->Leave();
700     return 0;
701 }
702 
CreateVideoStream(const AVISTREAMHEADER & videoStreamHeader,const BITMAPINFOHEADER & bitMapInfoHeader,const uint8_t * codecConfigParams,int32_t codecConfigParamsLength)703 int32_t AviFile::CreateVideoStream(
704     const AVISTREAMHEADER& videoStreamHeader,
705     const BITMAPINFOHEADER& bitMapInfoHeader,
706     const uint8_t* codecConfigParams,
707     int32_t codecConfigParamsLength)
708 {
709     _crit->Enter();
710     if (_aviMode == Read)
711     {
712         _crit->Leave();
713         return -1;
714     }
715 
716     if (_created)
717     {
718         _crit->Leave();
719         return -1;
720     }
721 
722     _aviMode = Write;
723     _writeVideoStream = true;
724 
725     _videoStreamHeader = videoStreamHeader;
726     _videoFormatHeader = bitMapInfoHeader;
727 
728     if (codecConfigParams && codecConfigParamsLength > 0)
729     {
730         if (_videoCodecConfigParams)
731         {
732             delete [] _videoCodecConfigParams;
733             _videoCodecConfigParams = 0;
734         }
735 
736         _videoCodecConfigParams = new uint8_t[codecConfigParamsLength];
737         _videoCodecConfigParamsLength = codecConfigParamsLength;
738 
739         memcpy(_videoCodecConfigParams, codecConfigParams,
740                _videoCodecConfigParamsLength);
741     }
742     _crit->Leave();
743     return 0;
744 }
745 
CreateAudioStream(const AVISTREAMHEADER & audioStreamHeader,const WAVEFORMATEX & waveFormatHeader)746 int32_t AviFile::CreateAudioStream(
747     const AVISTREAMHEADER& audioStreamHeader,
748     const WAVEFORMATEX& waveFormatHeader)
749 {
750     _crit->Enter();
751 
752     if (_aviMode == Read)
753     {
754         _crit->Leave();
755         return -1;
756     }
757 
758     if (_created)
759     {
760         _crit->Leave();
761         return -1;
762     }
763 
764     _aviMode = Write;
765     _writeAudioStream = true;
766     _audioStreamHeader = audioStreamHeader;
767     _audioFormatHeader = waveFormatHeader;
768     _crit->Leave();
769     return 0;
770 }
771 
WriteRIFF()772 int32_t AviFile::WriteRIFF()
773 {
774     const uint32_t riffTag = MakeFourCc('R', 'I', 'F', 'F');
775     _bytesWritten += PutLE32(riffTag);
776 
777     // Size is unknown at this point. Update later.
778     _bytesWritten += PutLE32(0);
779     _riffSizeMark = _bytesWritten;
780 
781     const uint32_t aviTag = MakeFourCc('A', 'V', 'I', ' ');
782     _bytesWritten += PutLE32(aviTag);
783 
784     return 0;
785 }
786 
787 
WriteHeaders()788 int32_t AviFile::WriteHeaders()
789 {
790     // Main AVI header list.
791     const uint32_t listTag = MakeFourCc('L', 'I', 'S', 'T');
792     _bytesWritten += PutLE32(listTag);
793 
794     // Size is unknown at this point. Update later.
795     _bytesWritten += PutLE32(0);
796     const size_t listhdrlSizeMark = _bytesWritten;
797 
798     const uint32_t hdrlTag = MakeFourCc('h', 'd', 'r', 'l');
799     _bytesWritten += PutLE32(hdrlTag);
800 
801     WriteAVIMainHeader();
802     WriteAVIStreamHeaders();
803 
804     const long hdrlLen = PutLE32LengthFromCurrent(
805         static_cast<long>(listhdrlSizeMark));
806 
807     // Junk chunk to align on 2048 boundry (CD-ROM sector boundary).
808     const uint32_t junkTag = MakeFourCc('J', 'U', 'N', 'K');
809     _bytesWritten += PutLE32(junkTag);
810     // Size is unknown at this point. Update later.
811     _bytesWritten += PutLE32(0);
812     const size_t junkSizeMark = _bytesWritten;
813 
814     const uint32_t junkBufferSize =
815         0x800     // 2048 byte alignment
816         - 12      // RIFF SIZE 'AVI '
817         - 8       // LIST SIZE
818         - hdrlLen //
819         - 8       // JUNK SIZE
820         - 12;     // LIST SIZE 'MOVI'
821 
822     // TODO (hellner): why not just fseek here?
823     uint8_t* junkBuffer = new uint8_t[junkBufferSize];
824     memset(junkBuffer, 0, junkBufferSize);
825     _bytesWritten += PutBuffer(junkBuffer, junkBufferSize);
826     delete [] junkBuffer;
827 
828     PutLE32LengthFromCurrent(static_cast<long>(junkSizeMark));
829     // End of JUNK chunk.
830     // End of main AVI header list.
831     return 0;
832 }
833 
WriteAVIMainHeader()834 int32_t AviFile::WriteAVIMainHeader()
835 {
836     const uint32_t avihTag = MakeFourCc('a', 'v', 'i', 'h');
837     _bytesWritten += PutLE32(avihTag);
838     _bytesWritten += PutLE32(14 * sizeof(uint32_t));
839 
840     const uint32_t scale = _videoStreamHeader.dwScale ?
841         _videoStreamHeader.dwScale : 1;
842     const uint32_t microSecPerFrame = 1000000 /
843         (_videoStreamHeader.dwRate / scale);
844     _bytesWritten += PutLE32(microSecPerFrame);
845     _bytesWritten += PutLE32(0);
846     _bytesWritten += PutLE32(0);
847 
848     uint32_t numStreams = 0;
849     if (_writeVideoStream)
850     {
851         ++numStreams;
852     }
853     if (_writeAudioStream)
854     {
855         ++numStreams;
856     }
857 
858     if (numStreams == 1)
859     {
860         _bytesWritten += PutLE32(
861             kAvifTrustcktype
862             | kAvifHasindex
863             | kAvifWascapturefile);
864     }
865     else
866     {
867         _bytesWritten += PutLE32(
868             kAvifTrustcktype
869             | kAvifHasindex
870             | kAvifWascapturefile
871             | kAvifIsinterleaved);
872     }
873 
874     _totNumFramesMark = _bytesWritten;
875     _bytesWritten += PutLE32(0);
876     _bytesWritten += PutLE32(0);
877     _bytesWritten += PutLE32(numStreams);
878 
879     if (_writeVideoStream)
880     {
881         _bytesWritten += PutLE32(
882             _videoStreamHeader.dwSuggestedBufferSize);
883         _bytesWritten += PutLE32(
884             _videoStreamHeader.rcFrame.right-_videoStreamHeader.rcFrame.left);
885         _bytesWritten += PutLE32(
886             _videoStreamHeader.rcFrame.bottom-_videoStreamHeader.rcFrame.top);
887     } else {
888         _bytesWritten += PutLE32(0);
889         _bytesWritten += PutLE32(0);
890         _bytesWritten += PutLE32(0);
891     }
892     _bytesWritten += PutLE32(0);
893     _bytesWritten += PutLE32(0);
894     _bytesWritten += PutLE32(0);
895     _bytesWritten += PutLE32(0);
896     return 0;
897 }
898 
WriteAVIStreamHeaders()899 int32_t AviFile::WriteAVIStreamHeaders()
900 {
901     if (_writeVideoStream)
902     {
903         WriteAVIVideoStreamHeaders();
904     }
905     if (_writeAudioStream)
906     {
907         WriteAVIAudioStreamHeaders();
908     }
909     return 0;
910 }
911 
WriteAVIVideoStreamHeaders()912 int32_t AviFile::WriteAVIVideoStreamHeaders()
913 {
914     const uint32_t listTag = MakeFourCc('L', 'I', 'S', 'T');
915     _bytesWritten += PutLE32(listTag);
916 
917     // Size is unknown at this point. Update later.
918     _bytesWritten += PutLE32(0);
919     const size_t liststrlSizeMark = _bytesWritten;
920 
921     const uint32_t hdrlTag = MakeFourCc('s', 't', 'r', 'l');
922     _bytesWritten += PutLE32(hdrlTag);
923 
924     WriteAVIVideoStreamHeaderChunks();
925 
926     PutLE32LengthFromCurrent(static_cast<long>(liststrlSizeMark));
927 
928     return 0;
929 }
930 
WriteAVIVideoStreamHeaderChunks()931 int32_t AviFile::WriteAVIVideoStreamHeaderChunks()
932 {
933     // Start of strh
934     const uint32_t strhTag = MakeFourCc('s', 't', 'r', 'h');
935     _bytesWritten += PutLE32(strhTag);
936 
937     // Size is unknown at this point. Update later.
938     _bytesWritten += PutLE32(0);
939     const size_t strhSizeMark = _bytesWritten;
940 
941     _bytesWritten += PutLE32(_videoStreamHeader.fccType);
942     _bytesWritten += PutLE32(_videoStreamHeader.fccHandler);
943     _bytesWritten += PutLE32(_videoStreamHeader.dwFlags);
944     _bytesWritten += PutLE16(_videoStreamHeader.wPriority);
945     _bytesWritten += PutLE16(_videoStreamHeader.wLanguage);
946     _bytesWritten += PutLE32(_videoStreamHeader.dwInitialFrames);
947     _bytesWritten += PutLE32(_videoStreamHeader.dwScale);
948     _bytesWritten += PutLE32(_videoStreamHeader.dwRate);
949     _bytesWritten += PutLE32(_videoStreamHeader.dwStart);
950 
951     _videoStreamLengthMark = _bytesWritten;
952     _bytesWritten += PutLE32(_videoStreamHeader.dwLength);
953 
954     _bytesWritten += PutLE32(_videoStreamHeader.dwSuggestedBufferSize);
955     _bytesWritten += PutLE32(_videoStreamHeader.dwQuality);
956     _bytesWritten += PutLE32(_videoStreamHeader.dwSampleSize);
957     _bytesWritten += PutLE16(_videoStreamHeader.rcFrame.left);
958     _bytesWritten += PutLE16(_videoStreamHeader.rcFrame.top);
959     _bytesWritten += PutLE16(_videoStreamHeader.rcFrame.right);
960     _bytesWritten += PutLE16(_videoStreamHeader.rcFrame.bottom);
961 
962     PutLE32LengthFromCurrent(static_cast<long>(strhSizeMark));
963     // End of strh
964 
965     // Start of strf
966     const uint32_t strfTag = MakeFourCc('s', 't', 'r', 'f');
967     _bytesWritten += PutLE32(strfTag);
968 
969     // Size is unknown at this point. Update later.
970     _bytesWritten += PutLE32(0);
971     const size_t strfSizeMark = _bytesWritten;
972 
973     _bytesWritten += PutLE32(_videoFormatHeader.biSize);
974     _bytesWritten += PutLE32(_videoFormatHeader.biWidth);
975     _bytesWritten += PutLE32(_videoFormatHeader.biHeight);
976     _bytesWritten += PutLE16(_videoFormatHeader.biPlanes);
977     _bytesWritten += PutLE16(_videoFormatHeader.biBitCount);
978     _bytesWritten += PutLE32(_videoFormatHeader.biCompression);
979     _bytesWritten += PutLE32(_videoFormatHeader.biSizeImage);
980     _bytesWritten += PutLE32(_videoFormatHeader.biXPelsPerMeter);
981     _bytesWritten += PutLE32(_videoFormatHeader.biYPelsPerMeter);
982     _bytesWritten += PutLE32(_videoFormatHeader.biClrUsed);
983     _bytesWritten += PutLE32(_videoFormatHeader.biClrImportant);
984 
985     const bool isMpegFile = _videoStreamHeader.fccHandler ==
986         AviFile::MakeFourCc('M','4','S','2');
987     if (isMpegFile)
988     {
989         if (_videoCodecConfigParams && _videoCodecConfigParamsLength > 0)
990         {
991             _bytesWritten += PutBuffer(_videoCodecConfigParams,
992                                        _videoCodecConfigParamsLength);
993         }
994     }
995 
996     PutLE32LengthFromCurrent(static_cast<long>(strfSizeMark));
997     // End of strf
998 
999     if ( _videoCodecConfigParams
1000          && (_videoCodecConfigParamsLength > 0)
1001          && !isMpegFile)
1002     {
1003         // Write strd, unless it's an MPEG file
1004         const uint32_t strdTag = MakeFourCc('s', 't', 'r', 'd');
1005         _bytesWritten += PutLE32(strdTag);
1006 
1007         // Size is unknown at this point. Update later.
1008         _bytesWritten += PutLE32(0);
1009         const size_t strdSizeMark = _bytesWritten;
1010 
1011         _bytesWritten += PutBuffer(_videoCodecConfigParams,
1012                                    _videoCodecConfigParamsLength);
1013 
1014         PutLE32LengthFromCurrent(static_cast<long>(strdSizeMark));
1015         // End of strd
1016     }
1017 
1018     // Start of strn
1019     const uint32_t strnTag = MakeFourCc('s', 't', 'r', 'n');
1020     _bytesWritten += PutLE32(strnTag);
1021 
1022     // Size is unknown at this point. Update later.
1023     _bytesWritten += PutLE32(0);
1024     const size_t strnSizeMark = _bytesWritten;
1025 
1026     _bytesWritten += PutBufferZ("WebRtc.avi ");
1027 
1028     PutLE32LengthFromCurrent(static_cast<long>(strnSizeMark));
1029     // End of strd
1030 
1031     return 0;
1032 }
1033 
WriteAVIAudioStreamHeaders()1034 int32_t AviFile::WriteAVIAudioStreamHeaders()
1035 {
1036     // Start of LIST
1037     uint32_t listTag = MakeFourCc('L', 'I', 'S', 'T');
1038     _bytesWritten += PutLE32(listTag);
1039 
1040     // Size is unknown at this point. Update later.
1041     _bytesWritten += PutLE32(0);
1042     const size_t liststrlSizeMark = _bytesWritten;
1043 
1044     uint32_t hdrlTag = MakeFourCc('s', 't', 'r', 'l');
1045     _bytesWritten += PutLE32(hdrlTag);
1046 
1047     WriteAVIAudioStreamHeaderChunks();
1048 
1049     PutLE32LengthFromCurrent(static_cast<long>(liststrlSizeMark));
1050     //End of LIST
1051     return 0;
1052 }
1053 
WriteAVIAudioStreamHeaderChunks()1054 int32_t AviFile::WriteAVIAudioStreamHeaderChunks()
1055 {
1056     // Start of strh
1057     const uint32_t strhTag = MakeFourCc('s', 't', 'r', 'h');
1058     _bytesWritten += PutLE32(strhTag);
1059 
1060     // Size is unknown at this point. Update later.
1061     _bytesWritten += PutLE32(0);
1062     const size_t strhSizeMark = _bytesWritten;
1063 
1064     _bytesWritten += PutLE32(_audioStreamHeader.fccType);
1065     _bytesWritten += PutLE32(_audioStreamHeader.fccHandler);
1066     _bytesWritten += PutLE32(_audioStreamHeader.dwFlags);
1067     _bytesWritten += PutLE16(_audioStreamHeader.wPriority);
1068     _bytesWritten += PutLE16(_audioStreamHeader.wLanguage);
1069     _bytesWritten += PutLE32(_audioStreamHeader.dwInitialFrames);
1070     _bytesWritten += PutLE32(_audioStreamHeader.dwScale);
1071     _bytesWritten += PutLE32(_audioStreamHeader.dwRate);
1072     _bytesWritten += PutLE32(_audioStreamHeader.dwStart);
1073 
1074     _audioStreamLengthMark = _bytesWritten;
1075     _bytesWritten += PutLE32(_audioStreamHeader.dwLength);
1076 
1077     _bytesWritten += PutLE32(_audioStreamHeader.dwSuggestedBufferSize);
1078     _bytesWritten += PutLE32(_audioStreamHeader.dwQuality);
1079     _bytesWritten += PutLE32(_audioStreamHeader.dwSampleSize);
1080     _bytesWritten += PutLE16(_audioStreamHeader.rcFrame.left);
1081     _bytesWritten += PutLE16(_audioStreamHeader.rcFrame.top);
1082     _bytesWritten += PutLE16(_audioStreamHeader.rcFrame.right);
1083     _bytesWritten += PutLE16(_audioStreamHeader.rcFrame.bottom);
1084 
1085     PutLE32LengthFromCurrent(static_cast<long>(strhSizeMark));
1086     // End of strh
1087 
1088     // Start of strf
1089     const uint32_t strfTag = MakeFourCc('s', 't', 'r', 'f');
1090     _bytesWritten += PutLE32(strfTag);
1091 
1092     // Size is unknown at this point. Update later.
1093     _bytesWritten += PutLE32(0);
1094     const size_t strfSizeMark = _bytesWritten;
1095 
1096     _bytesWritten += PutLE16(_audioFormatHeader.wFormatTag);
1097     _bytesWritten += PutLE16(_audioFormatHeader.nChannels);
1098     _bytesWritten += PutLE32(_audioFormatHeader.nSamplesPerSec);
1099     _bytesWritten += PutLE32(_audioFormatHeader.nAvgBytesPerSec);
1100     _bytesWritten += PutLE16(_audioFormatHeader.nBlockAlign);
1101     _bytesWritten += PutLE16(_audioFormatHeader.wBitsPerSample);
1102     _bytesWritten += PutLE16(_audioFormatHeader.cbSize);
1103 
1104     PutLE32LengthFromCurrent(static_cast<long>(strfSizeMark));
1105     // End end of strf.
1106 
1107     // Audio doesn't have strd.
1108 
1109     // Start of strn
1110     const uint32_t strnTag = MakeFourCc('s', 't', 'r', 'n');
1111     _bytesWritten += PutLE32(strnTag);
1112 
1113     // Size is unknown at this point. Update later.
1114     _bytesWritten += PutLE32(0);
1115     const size_t strnSizeMark = _bytesWritten;
1116 
1117     _bytesWritten += PutBufferZ("WebRtc.avi ");
1118 
1119     PutLE32LengthFromCurrent(static_cast<long>(strnSizeMark));
1120     // End of strd.
1121 
1122     return 0;
1123 }
1124 
WriteMoviStart()1125 int32_t AviFile::WriteMoviStart()
1126 {
1127     // Create template movi list. Fill out size when known (i.e. when closing
1128     // file).
1129     const uint32_t listTag = MakeFourCc('L', 'I', 'S', 'T');
1130     _bytesWritten += PutLE32(listTag);
1131 
1132     _bytesWritten += PutLE32(0); //Size! Change later!
1133     _moviSizeMark = _bytesWritten;
1134     _moviListOffset = ftell(_aviFile);
1135 
1136     const uint32_t moviTag = MakeFourCc('m', 'o', 'v', 'i');
1137     _bytesWritten += PutLE32(moviTag);
1138 
1139     return 0;
1140 }
1141 
PutByte(uint8_t byte)1142 size_t AviFile::PutByte(uint8_t byte)
1143 {
1144     return fwrite(&byte, sizeof(uint8_t), sizeof(uint8_t),
1145                   _aviFile);
1146 }
1147 
PutLE16(uint16_t word)1148 size_t AviFile::PutLE16(uint16_t word)
1149 {
1150     return fwrite(&word, sizeof(uint8_t), sizeof(uint16_t),
1151                   _aviFile);
1152 }
1153 
PutLE32(uint32_t word)1154 size_t AviFile::PutLE32(uint32_t word)
1155 {
1156     return fwrite(&word, sizeof(uint8_t), sizeof(uint32_t),
1157                   _aviFile);
1158 }
1159 
PutBuffer(const uint8_t * str,size_t size)1160 size_t AviFile::PutBuffer(const uint8_t* str, size_t size)
1161 {
1162     return fwrite(str, sizeof(uint8_t), size,
1163                   _aviFile);
1164 }
1165 
PutBufferZ(const char * str)1166 size_t AviFile::PutBufferZ(const char* str)
1167 {
1168     // Include NULL charachter, hence the + 1
1169     return PutBuffer(reinterpret_cast<const uint8_t*>(str),
1170                      strlen(str) + 1);
1171 }
1172 
PutLE32LengthFromCurrent(long startPos)1173 long AviFile::PutLE32LengthFromCurrent(long startPos)
1174 {
1175     const long endPos = ftell(_aviFile);
1176     if (endPos < 0) {
1177         return 0;
1178     }
1179     bool success = (0 == fseek(_aviFile, startPos - 4, SEEK_SET));
1180     if (!success) {
1181         assert(false);
1182         return 0;
1183     }
1184     const long len = endPos - startPos;
1185     if (endPos > startPos) {
1186         PutLE32(len);
1187     }
1188     else {
1189         assert(false);
1190     }
1191     success = (0 == fseek(_aviFile, endPos, SEEK_SET));
1192     assert(success);
1193     return len;
1194 }
1195 
PutLE32AtPos(long pos,uint32_t word)1196 void AviFile::PutLE32AtPos(long pos, uint32_t word)
1197 {
1198     const long currPos = ftell(_aviFile);
1199     if (currPos < 0) {
1200         assert(false);
1201         return;
1202     }
1203     bool success = (0 == fseek(_aviFile, pos, SEEK_SET));
1204     if (!success) {
1205       assert(false);
1206       return;
1207     }
1208     PutLE32(word);
1209     success = (0 == fseek(_aviFile, currPos, SEEK_SET));
1210     assert(success);
1211 }
1212 
CloseRead()1213 void AviFile::CloseRead()
1214 {
1215     if (_aviFile)
1216     {
1217         fclose(_aviFile);
1218         _aviFile = NULL;
1219     }
1220 }
1221 
CloseWrite()1222 void AviFile::CloseWrite()
1223 {
1224     if (_created)
1225     {
1226         // Update everything that isn't known until the file is closed. The
1227         // marks indicate where in the headers this update should be.
1228         PutLE32LengthFromCurrent(static_cast<long>(_moviSizeMark));
1229 
1230         PutLE32AtPos(static_cast<long>(_totNumFramesMark), _videoFrames);
1231 
1232         if (_writeVideoStream)
1233         {
1234             PutLE32AtPos(static_cast<long>(_videoStreamLengthMark),
1235                          _videoFrames);
1236         }
1237 
1238         if (_writeAudioStream)
1239         {
1240             PutLE32AtPos(static_cast<long>(_audioStreamLengthMark),
1241                          _audioFrames);
1242         }
1243 
1244         WriteIndex();
1245         PutLE32LengthFromCurrent(static_cast<long>(_riffSizeMark));
1246         ClearIndexList();
1247 
1248         if (_aviFile)
1249         {
1250             fclose(_aviFile);
1251             _aviFile = NULL;
1252         }
1253     }
1254 }
1255 
ResetMembers()1256 void AviFile::ResetMembers()
1257 {
1258     ResetComplexMembers();
1259 
1260     _aviFile = NULL;
1261 
1262     _nrStreams     = 0;
1263     _aviLength     = 0;
1264     _dataLength    = 0;
1265     _bytesRead     = 0;
1266     _dataStartByte = 0;
1267     _framesRead    = 0;
1268     _videoFrames   = 0;
1269     _audioFrames   = 0;
1270 
1271     _reading = false;
1272     _openedAs = AVI_AUDIO;
1273     _loop = false;
1274     _writing = false;
1275 
1276     _bytesWritten          = 0;
1277 
1278     _riffSizeMark          = 0;
1279     _moviSizeMark          = 0;
1280     _totNumFramesMark      = 0;
1281     _videoStreamLengthMark = 0;
1282     _audioStreamLengthMark = 0;
1283 
1284     _writeAudioStream = false;
1285     _writeVideoStream = false;
1286 
1287     _aviMode                      = NotSet;
1288     _videoCodecConfigParams       = 0;
1289     _videoCodecConfigParamsLength = 0;
1290 
1291     _videoStreamDataChunkPrefix = 0;
1292     _audioStreamDataChunkPrefix = 0;
1293 
1294     _created = false;
1295 
1296     _moviListOffset = 0;
1297 
1298     _videoConfigLength = 0;
1299 }
1300 
ResetComplexMembers()1301 void AviFile::ResetComplexMembers()
1302 {
1303     memset(&_aviHeader, 0, sizeof(AVIMAINHEADER));
1304     memset(&_videoStreamHeader, 0, sizeof(AVISTREAMHEADER));
1305     memset(&_audioStreamHeader, 0, sizeof(AVISTREAMHEADER));
1306     memset(&_videoFormatHeader, 0, sizeof(BITMAPINFOHEADER));
1307     memset(&_audioFormatHeader, 0, sizeof(WAVEFORMATEX));
1308     memset(_videoConfigParameters, 0, CODEC_CONFIG_LENGTH);
1309     memset(_videoStreamName, 0, STREAM_NAME_LENGTH);
1310     memset(_audioStreamName, 0, STREAM_NAME_LENGTH);
1311     memset(&_videoStream, 0, sizeof(AVIStream));
1312     memset(&_audioStream, 0, sizeof(AVIStream));
1313 }
1314 
GetByte(uint8_t & word)1315 size_t AviFile::GetByte(uint8_t& word)
1316 {
1317     return fread(&word, sizeof(uint8_t), sizeof(uint8_t), _aviFile);
1318 }
1319 
GetLE16(uint16_t & word)1320 size_t AviFile::GetLE16(uint16_t& word)
1321 {
1322     return fread(&word, sizeof(uint8_t), sizeof(uint16_t),
1323                  _aviFile);
1324 }
1325 
GetLE32(uint32_t & word)1326 size_t AviFile::GetLE32(uint32_t& word)
1327 {
1328     return fread(&word, sizeof(uint8_t), sizeof(uint32_t),
1329                  _aviFile);
1330 }
1331 
GetBuffer(uint8_t * str,size_t size)1332 size_t AviFile::GetBuffer(uint8_t* str, size_t size)
1333 {
1334     return fread(str, sizeof(uint8_t), size, _aviFile);
1335 }
1336 
ReadRIFF()1337 int32_t AviFile::ReadRIFF()
1338 {
1339     uint32_t tag;
1340     _bytesRead = GetLE32(tag);
1341     if (tag != MakeFourCc('R', 'I', 'F', 'F'))
1342     {
1343         WEBRTC_TRACE(kTraceError, kTraceVideo, -1,  "Not a RIFF file!");
1344         return -1;
1345     }
1346 
1347     uint32_t size;
1348     _bytesRead += GetLE32(size);
1349     _aviLength = size;
1350 
1351     _bytesRead += GetLE32(tag);
1352     if (tag != MakeFourCc('A', 'V', 'I', ' '))
1353     {
1354         WEBRTC_TRACE(kTraceError, kTraceVideo, -1,  "Not an AVI file!");
1355         return -1;
1356     }
1357 
1358     return 0;
1359 }
1360 
ReadHeaders()1361 int32_t AviFile::ReadHeaders()
1362 {
1363     uint32_t tag;
1364     _bytesRead += GetLE32(tag);
1365     uint32_t size;
1366     _bytesRead += GetLE32(size);
1367 
1368     if (tag != MakeFourCc('L', 'I', 'S', 'T'))
1369     {
1370         return -1;
1371     }
1372 
1373     uint32_t listTag;
1374     _bytesRead += GetLE32(listTag);
1375     if (listTag != MakeFourCc('h', 'd', 'r', 'l'))
1376     {
1377         return -1;
1378     }
1379 
1380     int32_t err = ReadAVIMainHeader();
1381     if (err)
1382     {
1383         return -1;
1384     }
1385 
1386     return 0;
1387 }
1388 
ReadAVIMainHeader()1389 int32_t AviFile::ReadAVIMainHeader()
1390 {
1391     _bytesRead += GetLE32(_aviHeader.fcc);
1392     _bytesRead += GetLE32(_aviHeader.cb);
1393     _bytesRead += GetLE32(_aviHeader.dwMicroSecPerFrame);
1394     _bytesRead += GetLE32(_aviHeader.dwMaxBytesPerSec);
1395     _bytesRead += GetLE32(_aviHeader.dwPaddingGranularity);
1396     _bytesRead += GetLE32(_aviHeader.dwFlags);
1397     _bytesRead += GetLE32(_aviHeader.dwTotalFrames);
1398     _bytesRead += GetLE32(_aviHeader.dwInitialFrames);
1399     _bytesRead += GetLE32(_aviHeader.dwStreams);
1400     _bytesRead += GetLE32(_aviHeader.dwSuggestedBufferSize);
1401     _bytesRead += GetLE32(_aviHeader.dwWidth);
1402     _bytesRead += GetLE32(_aviHeader.dwHeight);
1403     _bytesRead += GetLE32(_aviHeader.dwReserved[0]);
1404     _bytesRead += GetLE32(_aviHeader.dwReserved[1]);
1405     _bytesRead += GetLE32(_aviHeader.dwReserved[2]);
1406     _bytesRead += GetLE32(_aviHeader.dwReserved[3]);
1407 
1408     if (_aviHeader.fcc != MakeFourCc('a', 'v', 'i', 'h'))
1409     {
1410         return -1;
1411     }
1412 
1413     if (_aviHeader.dwFlags & kAvifMustuseindex)
1414     {
1415         return -1;
1416     }
1417 
1418     bool readVideoStreamHeader = false;
1419     bool readAudioStreamHeader = false;
1420     unsigned int streamsRead = 0;
1421     while (_aviHeader.dwStreams > streamsRead)
1422     {
1423         uint32_t strltag;
1424         _bytesRead += GetLE32(strltag);
1425         uint32_t strlsize;
1426         _bytesRead += GetLE32(strlsize);
1427         const long endSeekPos = ftell(_aviFile) +
1428             static_cast<int32_t>(strlsize);
1429 
1430         if (strltag != MakeFourCc('L', 'I', 'S', 'T'))
1431         {
1432             return -1;
1433         }
1434 
1435         uint32_t listTag;
1436         _bytesRead += GetLE32(listTag);
1437         if (listTag != MakeFourCc('s', 't', 'r', 'l'))
1438         {
1439             return -1;
1440         }
1441 
1442         uint32_t chunktag;
1443         _bytesRead += GetLE32(chunktag);
1444         uint32_t chunksize;
1445         _bytesRead += GetLE32(chunksize);
1446 
1447         if (chunktag != MakeFourCc('s', 't', 'r', 'h'))
1448         {
1449             return -1;
1450         }
1451 
1452         AVISTREAMHEADER tmpStreamHeader;
1453         tmpStreamHeader.fcc = chunktag;
1454         tmpStreamHeader.cb  = chunksize;
1455 
1456         _bytesRead += GetLE32(tmpStreamHeader.fccType);
1457         _bytesRead += GetLE32(tmpStreamHeader.fccHandler);
1458         _bytesRead += GetLE32(tmpStreamHeader.dwFlags);
1459         _bytesRead += GetLE16(tmpStreamHeader.wPriority);
1460         _bytesRead += GetLE16(tmpStreamHeader.wLanguage);
1461         _bytesRead += GetLE32(tmpStreamHeader.dwInitialFrames);
1462         _bytesRead += GetLE32(tmpStreamHeader.dwScale);
1463         _bytesRead += GetLE32(tmpStreamHeader.dwRate);
1464         _bytesRead += GetLE32(tmpStreamHeader.dwStart);
1465         _bytesRead += GetLE32(tmpStreamHeader.dwLength);
1466         _bytesRead += GetLE32(tmpStreamHeader.dwSuggestedBufferSize);
1467         _bytesRead += GetLE32(tmpStreamHeader.dwQuality);
1468         _bytesRead += GetLE32(tmpStreamHeader.dwSampleSize);
1469 
1470         uint16_t left;
1471         _bytesRead += GetLE16(left);
1472         tmpStreamHeader.rcFrame.left = left;
1473         uint16_t top;
1474         _bytesRead += GetLE16(top);
1475         tmpStreamHeader.rcFrame.top = top;
1476         uint16_t right;
1477         _bytesRead += GetLE16(right);
1478         tmpStreamHeader.rcFrame.right = right;
1479         uint16_t bottom;
1480         _bytesRead += GetLE16(bottom);
1481         tmpStreamHeader.rcFrame.bottom = bottom;
1482 
1483         if (!readVideoStreamHeader
1484             && (tmpStreamHeader.fccType == MakeFourCc('v', 'i', 'd', 's')))
1485         {
1486             _videoStreamHeader = tmpStreamHeader; //Bitwise copy is OK!
1487             const int32_t err = ReadAVIVideoStreamHeader(endSeekPos);
1488             if (err)
1489             {
1490                 return -1;
1491             }
1492             // Make sure there actually is video data in the file...
1493             if (_videoStreamHeader.dwLength == 0)
1494             {
1495                 return -1;
1496             }
1497             readVideoStreamHeader = true;
1498         } else if(!readAudioStreamHeader &&
1499                   (tmpStreamHeader.fccType == MakeFourCc('a', 'u', 'd', 's'))) {
1500             _audioStreamHeader = tmpStreamHeader;
1501             const int32_t err = ReadAVIAudioStreamHeader(endSeekPos);
1502             if (err)
1503             {
1504                 return -1;
1505             }
1506             readAudioStreamHeader = true;
1507         }
1508         else
1509         {
1510             fseek(_aviFile, endSeekPos, SEEK_SET);
1511             _bytesRead += endSeekPos;
1512         }
1513 
1514         ++streamsRead;
1515     }
1516 
1517     if (!readVideoStreamHeader && !readAudioStreamHeader)
1518     {
1519         return -1;
1520     }
1521 
1522     uint32_t tag;
1523     _bytesRead += GetLE32(tag);
1524     uint32_t size;
1525     _bytesRead += GetLE32(size);
1526 
1527     if (tag == MakeFourCc('J', 'U', 'N', 'K'))
1528     {
1529         fseek(_aviFile, size, SEEK_CUR);
1530         _bytesRead += size;
1531         _bytesRead += GetLE32(tag);
1532         _bytesRead += GetLE32(size);
1533     }
1534     if (tag != MakeFourCc('L', 'I', 'S', 'T'))
1535     {
1536         return -1;
1537     }
1538     uint32_t listTag;
1539     _bytesRead += GetLE32(listTag);
1540     if (listTag != MakeFourCc('m', 'o', 'v', 'i'))
1541     {
1542         return -1;
1543     }
1544     _dataLength = size;
1545     return 0;
1546 }
1547 
ReadAVIVideoStreamHeader(int32_t endpos)1548 int32_t AviFile::ReadAVIVideoStreamHeader(int32_t endpos)
1549 {
1550     uint32_t chunktag;
1551     _bytesRead += GetLE32(chunktag);
1552     uint32_t chunksize;
1553     _bytesRead += GetLE32(chunksize);
1554 
1555     if (chunktag != MakeFourCc('s', 't', 'r', 'f'))
1556     {
1557         return -1;
1558     }
1559 
1560     _bytesRead += GetLE32(_videoFormatHeader.biSize);
1561     _bytesRead += GetLE32(_videoFormatHeader.biWidth);
1562     _bytesRead += GetLE32(_videoFormatHeader.biHeight);
1563     _bytesRead += GetLE16(_videoFormatHeader.biPlanes);
1564     _bytesRead += GetLE16(_videoFormatHeader.biBitCount);
1565     _bytesRead += GetLE32(_videoFormatHeader.biCompression);
1566     _bytesRead += GetLE32(_videoFormatHeader.biSizeImage);
1567     _bytesRead += GetLE32(_videoFormatHeader.biXPelsPerMeter);
1568     _bytesRead += GetLE32(_videoFormatHeader.biYPelsPerMeter);
1569     _bytesRead += GetLE32(_videoFormatHeader.biClrUsed);
1570     _bytesRead += GetLE32(_videoFormatHeader.biClrImportant);
1571 
1572     if (chunksize >  _videoFormatHeader.biSize)
1573     {
1574         const uint32_t size = chunksize - _videoFormatHeader.biSize;
1575         const uint32_t readSize = MinValue(size, CODEC_CONFIG_LENGTH);
1576         _bytesRead += GetBuffer(
1577             reinterpret_cast<uint8_t*>(_videoConfigParameters), readSize);
1578         _videoConfigLength = readSize;
1579         int32_t skipSize = chunksize - _videoFormatHeader.biSize -
1580             readSize;
1581         if (skipSize > 0)
1582         {
1583             fseek(_aviFile, skipSize, SEEK_CUR);
1584             _bytesRead += skipSize;
1585         }
1586     }
1587 
1588     while (static_cast<long>(_bytesRead) < endpos)
1589     {
1590         uint32_t chunktag;
1591         _bytesRead += GetLE32(chunktag);
1592         uint32_t chunksize;
1593         _bytesRead += GetLE32(chunksize);
1594 
1595         if (chunktag == MakeFourCc('s', 't', 'r', 'n'))
1596         {
1597             const uint32_t size = MinValue(chunksize, STREAM_NAME_LENGTH);
1598             _bytesRead += GetBuffer(
1599                 reinterpret_cast<uint8_t*>(_videoStreamName), size);
1600         }
1601         else if (chunktag == MakeFourCc('s', 't', 'r', 'd'))
1602         {
1603             const uint32_t size = MinValue(chunksize, CODEC_CONFIG_LENGTH);
1604             _bytesRead += GetBuffer(
1605                 reinterpret_cast<uint8_t*>(_videoConfigParameters), size);
1606             _videoConfigLength = size;
1607         }
1608         else
1609         {
1610             fseek(_aviFile, chunksize, SEEK_CUR);
1611             _bytesRead += chunksize;
1612         }
1613 
1614         if (feof(_aviFile))
1615         {
1616             return -1;
1617         }
1618     }
1619     _videoStream.streamType = AviFile::AVI_VIDEO;
1620     _videoStream.streamNumber = _nrStreams++;
1621 
1622     return 0;
1623 }
1624 
ReadAVIAudioStreamHeader(int32_t endpos)1625 int32_t AviFile::ReadAVIAudioStreamHeader(int32_t endpos)
1626 {
1627     uint32_t chunktag;
1628     _bytesRead += GetLE32(chunktag);
1629     uint32_t chunksize;
1630     _bytesRead += GetLE32(chunksize);
1631 
1632     if (chunktag != MakeFourCc('s', 't', 'r', 'f'))
1633     {
1634         return -1;
1635     }
1636 
1637     const size_t startRead = _bytesRead;
1638     _bytesRead += GetLE16(_audioFormatHeader.wFormatTag);
1639     _bytesRead += GetLE16(_audioFormatHeader.nChannels);
1640     _bytesRead += GetLE32(_audioFormatHeader.nSamplesPerSec);
1641     _bytesRead += GetLE32(_audioFormatHeader.nAvgBytesPerSec);
1642     _bytesRead += GetLE16(_audioFormatHeader.nBlockAlign);
1643     _bytesRead += GetLE16(_audioFormatHeader.wBitsPerSample);
1644     if (chunksize > 0x10) {
1645         _bytesRead += GetLE16(_audioFormatHeader.cbSize);
1646     }
1647 
1648     const uint32_t diffRead = chunksize - (_bytesRead - startRead);
1649     if (diffRead > 0)
1650     {
1651         const uint32_t size = MinValue(diffRead, CODEC_CONFIG_LENGTH);
1652         _bytesRead += GetBuffer(
1653             reinterpret_cast<uint8_t*>(_audioConfigParameters), size);
1654     }
1655 
1656     while (static_cast<long>(_bytesRead) < endpos)
1657     {
1658         uint32_t chunktag;
1659         _bytesRead += GetLE32(chunktag);
1660         uint32_t chunksize;
1661         _bytesRead += GetLE32(chunksize);
1662 
1663         if (chunktag == MakeFourCc('s', 't', 'r', 'n'))
1664         {
1665             const uint32_t size = MinValue(chunksize, STREAM_NAME_LENGTH);
1666             _bytesRead += GetBuffer(
1667                 reinterpret_cast<uint8_t*>(_audioStreamName), size);
1668         }
1669         else if (chunktag == MakeFourCc('s', 't', 'r', 'd'))
1670         {
1671             const uint32_t size = MinValue(chunksize, CODEC_CONFIG_LENGTH);
1672             _bytesRead += GetBuffer(
1673                 reinterpret_cast<uint8_t*>(_audioConfigParameters), size);
1674         }
1675         else
1676         {
1677             fseek(_aviFile, chunksize, SEEK_CUR);
1678             _bytesRead += chunksize;
1679         }
1680 
1681         if (feof(_aviFile))
1682         {
1683             return -1;
1684         }
1685     }
1686     _audioStream.streamType = AviFile::AVI_AUDIO;
1687     _audioStream.streamNumber = _nrStreams++;
1688     return 0;
1689 }
1690 
StreamAndTwoCharCodeToTag(int32_t streamNum,const char * twoCharCode)1691 uint32_t AviFile::StreamAndTwoCharCodeToTag(int32_t streamNum,
1692                                             const char* twoCharCode)
1693 {
1694     uint8_t a = '0';
1695     uint8_t b;
1696     switch (streamNum)
1697     {
1698     case 1:
1699         b = '1';
1700         break;
1701     case 2:
1702         b = '2';
1703         break;
1704     default:
1705         b = '0';
1706     }
1707     return MakeFourCc(a, b, twoCharCode[0], twoCharCode[1]);
1708 }
1709 
ClearIndexList()1710 void AviFile::ClearIndexList()
1711 {
1712   for (IndexList::iterator iter = _indexList.begin();
1713        iter != _indexList.end(); ++iter) {
1714       delete *iter;
1715   }
1716   _indexList.clear();
1717 }
1718 
AddChunkToIndexList(uint32_t inChunkId,uint32_t inFlags,uint32_t inOffset,uint32_t inSize)1719 void AviFile::AddChunkToIndexList(uint32_t inChunkId,
1720                                   uint32_t inFlags,
1721                                   uint32_t inOffset,
1722                                   uint32_t inSize)
1723 {
1724     _indexList.push_back(new AVIINDEXENTRY(inChunkId, inFlags, inOffset,
1725                                            inSize));
1726 }
1727 
WriteIndex()1728 void AviFile::WriteIndex()
1729 {
1730     const uint32_t idxTag = MakeFourCc('i', 'd', 'x', '1');
1731     _bytesWritten += PutLE32(idxTag);
1732 
1733     // Size is unknown at this point. Update later.
1734     _bytesWritten += PutLE32(0);
1735     const size_t idxChunkSize = _bytesWritten;
1736 
1737     for (IndexList::iterator iter = _indexList.begin();
1738          iter != _indexList.end(); ++iter) {
1739         const AVIINDEXENTRY* item = *iter;
1740         _bytesWritten += PutLE32(item->ckid);
1741         _bytesWritten += PutLE32(item->dwFlags);
1742         _bytesWritten += PutLE32(item->dwChunkOffset);
1743         _bytesWritten += PutLE32(item->dwChunkLength);
1744     }
1745     PutLE32LengthFromCurrent(static_cast<long>(idxChunkSize));
1746 }
1747 }  // namespace webrtc
1748