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