blob: 19baaa3b21833aeed63f0f78952adff0f3c2ad4a [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
henrike@webrtc.org26085e12012-02-27 21:50:40 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
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
henrike@webrtc.org26085e12012-02-27 21:50:40 +000011// 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
pbos@webrtc.org0c4e05a2013-07-16 13:05:40 +000015#include "webrtc/modules/media_file/source/avi_file.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000016
hellner@google.com9aa99962011-08-23 21:25:55 +000017#include <assert.h>
niklase@google.com470e71d2011-07-07 08:21:25 +000018#include <string.h>
19
20#ifdef _WIN32
21#include <windows.h>
22#endif
23
pbos@webrtc.org0c4e05a2013-07-16 13:05:40 +000024#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
25#include "webrtc/system_wrappers/interface/file_wrapper.h"
pbos@webrtc.org0c4e05a2013-07-16 13:05:40 +000026#include "webrtc/system_wrappers/interface/trace.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000027
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
44namespace webrtc {
45namespace {
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +000046static const uint32_t kAvifHasindex = 0x00000010;
47static const uint32_t kAvifMustuseindex = 0x00000020;
48static const uint32_t kAvifIsinterleaved = 0x00000100;
49static const uint32_t kAvifTrustcktype = 0x00000800;
50static const uint32_t kAvifWascapturefile = 0x00010000;
niklase@google.com470e71d2011-07-07 08:21:25 +000051
andrew@webrtc.orgf81f9f82011-08-19 22:56:22 +000052template <class T>
53T MinValue(T a, T b)
54{
55 return a < b ? a : b;
56}
57} // namespace
niklase@google.com470e71d2011-07-07 08:21:25 +000058
59AviFile::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
79AVISTREAMHEADER::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
102BITMAPINFOHEADER::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
117WAVEFORMATEX::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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000128AviFile::AVIINDEXENTRY::AVIINDEXENTRY(uint32_t inckid,
129 uint32_t indwFlags,
130 uint32_t indwChunkOffset,
131 uint32_t indwChunkLength)
niklase@google.com470e71d2011-07-07 08:21:25 +0000132 : ckid(inckid),
133 dwFlags(indwFlags),
134 dwChunkOffset(indwChunkOffset),
135 dwChunkLength(indwChunkLength)
136{
137}
138
139AviFile::AviFile()
henrike@webrtc.org26085e12012-02-27 21:50:40 +0000140 : _crit(CriticalSectionWrapper::CreateCriticalSection()),
141 _aviFile(NULL),
142 _aviHeader(),
143 _videoStreamHeader(),
144 _audioStreamHeader(),
145 _videoFormatHeader(),
146 _audioFormatHeader(),
147 _videoConfigParameters(),
148 _videoConfigLength(0),
149 _videoStreamName(),
henrike@webrtc.org26085e12012-02-27 21:50:40 +0000150 _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),
henrike@webrtc.org79cf3ac2014-01-13 15:21:30 +0000180 _created(false)
niklase@google.com470e71d2011-07-07 08:21:25 +0000181{
henrike@webrtc.org26085e12012-02-27 21:50:40 +0000182 ResetComplexMembers();
niklase@google.com470e71d2011-07-07 08:21:25 +0000183}
184
185AviFile::~AviFile()
186{
187 Close();
188
niklase@google.com470e71d2011-07-07 08:21:25 +0000189 delete[] _videoCodecConfigParams;
190 delete _crit;
191}
192
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000193int32_t AviFile::Open(AVIStreamType streamType, const char* fileName, bool loop)
niklase@google.com470e71d2011-07-07 08:21:25 +0000194{
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.
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000234 int32_t err = ReadRIFF();
niklase@google.com470e71d2011-07-07 08:21:25 +0000235 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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000268int32_t AviFile::Close()
niklase@google.com470e71d2011-07-07 08:21:25 +0000269{
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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000293uint32_t AviFile::MakeFourCc(uint8_t ch0, uint8_t ch1, uint8_t ch2, uint8_t ch3)
niklase@google.com470e71d2011-07-07 08:21:25 +0000294{
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000295 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 ));
niklase@google.com470e71d2011-07-07 08:21:25 +0000299}
300
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000301int32_t AviFile::GetVideoStreamInfo(AVISTREAMHEADER& videoStreamHeader,
302 BITMAPINFOHEADER& bitmapInfo,
303 char* codecConfigParameters,
304 int32_t& configLength)
niklase@google.com470e71d2011-07-07 08:21:25 +0000305{
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
henrike@webrtc.org26085e12012-02-27 21:50:40 +0000316 if (configLength <= _videoConfigLength)
niklase@google.com470e71d2011-07-07 08:21:25 +0000317 {
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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000330int32_t AviFile::GetDuration(int32_t& durationMs)
niklase@google.com470e71d2011-07-07 08:21:25 +0000331{
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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000345int32_t AviFile::GetAudioStreamInfo(WAVEFORMATEX& waveHeader)
niklase@google.com470e71d2011-07-07 08:21:25 +0000346{
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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000363int32_t AviFile::WriteAudio(const uint8_t* data, int32_t length)
niklase@google.com470e71d2011-07-07 08:21:25 +0000364{
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.
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000385 const uint32_t chunkOffset = ftell(_aviFile) - _moviListOffset;
niklase@google.com470e71d2011-07-07 08:21:25 +0000386 _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();
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000410 return static_cast<int32_t>(newBytesWritten);
niklase@google.com470e71d2011-07-07 08:21:25 +0000411}
412
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000413int32_t AviFile::WriteVideo(const uint8_t* data, int32_t length)
niklase@google.com470e71d2011-07-07 08:21:25 +0000414{
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.
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000434 const uint32_t chunkOffset = ftell(_aviFile) - _moviListOffset;
niklase@google.com470e71d2011-07-07 08:21:25 +0000435 _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.
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000453 chunkOffset, static_cast<uint32_t>(chunkSize));
niklase@google.com470e71d2011-07-07 08:21:25 +0000454
455 ++_videoFrames;
456 newBytesWritten = _bytesWritten - newBytesWritten;
457 _crit->Leave();
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000458 return static_cast<int32_t>(newBytesWritten);
niklase@google.com470e71d2011-07-07 08:21:25 +0000459}
460
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000461int32_t AviFile::PrepareDataChunkHeaders()
niklase@google.com470e71d2011-07-07 08:21:25 +0000462{
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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000485int32_t AviFile::ReadMoviSubChunk(uint8_t* data, int32_t& length, uint32_t tag1,
486 uint32_t tag2)
niklase@google.com470e71d2011-07-07 08:21:25 +0000487{
488 if (!_reading)
489 {
490 WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1,
491 "AviFile::ReadMoviSubChunk(): File not open!");
492 length = 0;
493 return -1;
494 }
495
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000496 uint32_t size;
niklase@google.com470e71d2011-07-07 08:21:25 +0000497 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.
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000505 uint32_t tag;
niklase@google.com470e71d2011-07-07 08:21:25 +0000506 _bytesRead += GetLE32(tag);
507 _bytesRead += GetLE32(size);
508
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000509 const int32_t eof = feof(_aviFile);
niklase@google.com470e71d2011-07-07 08:21:25 +0000510 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.
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000525 const uint32_t seekSize = (size % 2) ? size + 1 : size;
526 const int32_t err = fseek(_aviFile, seekSize, SEEK_CUR);
niklase@google.com470e71d2011-07-07 08:21:25 +0000527
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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000566 if (static_cast<int32_t>(size) > length)
niklase@google.com470e71d2011-07-07 08:21:25 +0000567 {
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.
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000573 const uint32_t seekSize = (size % 2) ? size + 1 : size;
niklase@google.com470e71d2011-07-07 08:21:25 +0000574 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 {
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000584 uint8_t dummy_byte;
niklase@google.com470e71d2011-07-07 08:21:25 +0000585 _bytesRead += GetByte(dummy_byte);
586 }
587 length = size;
588 ++_framesRead;
589 return 0;
590}
591
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000592int32_t AviFile::ReadAudio(uint8_t* data, int32_t& length)
niklase@google.com470e71d2011-07-07 08:21:25 +0000593{
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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000610 const int32_t ret = ReadMoviSubChunk(
niklase@google.com470e71d2011-07-07 08:21:25 +0000611 data,
612 length,
613 StreamAndTwoCharCodeToTag(_audioStream.streamNumber, "wb"));
614
615 _crit->Leave();
616 return ret;
617}
618
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000619int32_t AviFile::ReadVideo(uint8_t* data, int32_t& length)
niklase@google.com470e71d2011-07-07 08:21:25 +0000620{
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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000638 const int32_t ret = ReadMoviSubChunk(
niklase@google.com470e71d2011-07-07 08:21:25 +0000639 data,
640 length,
641 StreamAndTwoCharCodeToTag(_videoStream.streamNumber, "dc"),
642 StreamAndTwoCharCodeToTag(_videoStream.streamNumber, "db"));
643 _crit->Leave();
644 return ret;
645}
646
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000647int32_t AviFile::Create(const char* fileName)
niklase@google.com470e71d2011-07-07 08:21:25 +0000648{
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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000703int32_t AviFile::CreateVideoStream(
niklase@google.com470e71d2011-07-07 08:21:25 +0000704 const AVISTREAMHEADER& videoStreamHeader,
705 const BITMAPINFOHEADER& bitMapInfoHeader,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000706 const uint8_t* codecConfigParams,
707 int32_t codecConfigParamsLength)
niklase@google.com470e71d2011-07-07 08:21:25 +0000708{
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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000736 _videoCodecConfigParams = new uint8_t[codecConfigParamsLength];
niklase@google.com470e71d2011-07-07 08:21:25 +0000737 _videoCodecConfigParamsLength = codecConfigParamsLength;
738
739 memcpy(_videoCodecConfigParams, codecConfigParams,
740 _videoCodecConfigParamsLength);
741 }
742 _crit->Leave();
743 return 0;
744}
745
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000746int32_t AviFile::CreateAudioStream(
niklase@google.com470e71d2011-07-07 08:21:25 +0000747 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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000772int32_t AviFile::WriteRIFF()
niklase@google.com470e71d2011-07-07 08:21:25 +0000773{
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000774 const uint32_t riffTag = MakeFourCc('R', 'I', 'F', 'F');
niklase@google.com470e71d2011-07-07 08:21:25 +0000775 _bytesWritten += PutLE32(riffTag);
776
777 // Size is unknown at this point. Update later.
778 _bytesWritten += PutLE32(0);
779 _riffSizeMark = _bytesWritten;
780
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000781 const uint32_t aviTag = MakeFourCc('A', 'V', 'I', ' ');
niklase@google.com470e71d2011-07-07 08:21:25 +0000782 _bytesWritten += PutLE32(aviTag);
783
784 return 0;
785}
786
787
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000788int32_t AviFile::WriteHeaders()
niklase@google.com470e71d2011-07-07 08:21:25 +0000789{
790 // Main AVI header list.
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000791 const uint32_t listTag = MakeFourCc('L', 'I', 'S', 'T');
niklase@google.com470e71d2011-07-07 08:21:25 +0000792 _bytesWritten += PutLE32(listTag);
793
794 // Size is unknown at this point. Update later.
795 _bytesWritten += PutLE32(0);
796 const size_t listhdrlSizeMark = _bytesWritten;
797
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000798 const uint32_t hdrlTag = MakeFourCc('h', 'd', 'r', 'l');
niklase@google.com470e71d2011-07-07 08:21:25 +0000799 _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).
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000808 const uint32_t junkTag = MakeFourCc('J', 'U', 'N', 'K');
niklase@google.com470e71d2011-07-07 08:21:25 +0000809 _bytesWritten += PutLE32(junkTag);
810 // Size is unknown at this point. Update later.
811 _bytesWritten += PutLE32(0);
812 const size_t junkSizeMark = _bytesWritten;
813
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000814 const uint32_t junkBufferSize =
niklase@google.com470e71d2011-07-07 08:21:25 +0000815 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?
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000823 uint8_t* junkBuffer = new uint8_t[junkBufferSize];
niklase@google.com470e71d2011-07-07 08:21:25 +0000824 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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000834int32_t AviFile::WriteAVIMainHeader()
niklase@google.com470e71d2011-07-07 08:21:25 +0000835{
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000836 const uint32_t avihTag = MakeFourCc('a', 'v', 'i', 'h');
niklase@google.com470e71d2011-07-07 08:21:25 +0000837 _bytesWritten += PutLE32(avihTag);
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000838 _bytesWritten += PutLE32(14 * sizeof(uint32_t));
niklase@google.com470e71d2011-07-07 08:21:25 +0000839
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000840 const uint32_t scale = _videoStreamHeader.dwScale ?
niklase@google.com470e71d2011-07-07 08:21:25 +0000841 _videoStreamHeader.dwScale : 1;
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000842 const uint32_t microSecPerFrame = 1000000 /
niklase@google.com470e71d2011-07-07 08:21:25 +0000843 (_videoStreamHeader.dwRate / scale);
844 _bytesWritten += PutLE32(microSecPerFrame);
845 _bytesWritten += PutLE32(0);
846 _bytesWritten += PutLE32(0);
847
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000848 uint32_t numStreams = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000849 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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000899int32_t AviFile::WriteAVIStreamHeaders()
niklase@google.com470e71d2011-07-07 08:21:25 +0000900{
901 if (_writeVideoStream)
902 {
903 WriteAVIVideoStreamHeaders();
904 }
905 if (_writeAudioStream)
906 {
907 WriteAVIAudioStreamHeaders();
908 }
909 return 0;
910}
911
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000912int32_t AviFile::WriteAVIVideoStreamHeaders()
niklase@google.com470e71d2011-07-07 08:21:25 +0000913{
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000914 const uint32_t listTag = MakeFourCc('L', 'I', 'S', 'T');
niklase@google.com470e71d2011-07-07 08:21:25 +0000915 _bytesWritten += PutLE32(listTag);
916
917 // Size is unknown at this point. Update later.
918 _bytesWritten += PutLE32(0);
919 const size_t liststrlSizeMark = _bytesWritten;
920
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000921 const uint32_t hdrlTag = MakeFourCc('s', 't', 'r', 'l');
niklase@google.com470e71d2011-07-07 08:21:25 +0000922 _bytesWritten += PutLE32(hdrlTag);
923
924 WriteAVIVideoStreamHeaderChunks();
925
926 PutLE32LengthFromCurrent(static_cast<long>(liststrlSizeMark));
927
928 return 0;
929}
930
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000931int32_t AviFile::WriteAVIVideoStreamHeaderChunks()
niklase@google.com470e71d2011-07-07 08:21:25 +0000932{
933 // Start of strh
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000934 const uint32_t strhTag = MakeFourCc('s', 't', 'r', 'h');
niklase@google.com470e71d2011-07-07 08:21:25 +0000935 _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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000966 const uint32_t strfTag = MakeFourCc('s', 't', 'r', 'f');
niklase@google.com470e71d2011-07-07 08:21:25 +0000967 _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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001004 const uint32_t strdTag = MakeFourCc('s', 't', 'r', 'd');
niklase@google.com470e71d2011-07-07 08:21:25 +00001005 _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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001019 const uint32_t strnTag = MakeFourCc('s', 't', 'r', 'n');
niklase@google.com470e71d2011-07-07 08:21:25 +00001020 _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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001034int32_t AviFile::WriteAVIAudioStreamHeaders()
niklase@google.com470e71d2011-07-07 08:21:25 +00001035{
1036 // Start of LIST
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001037 uint32_t listTag = MakeFourCc('L', 'I', 'S', 'T');
niklase@google.com470e71d2011-07-07 08:21:25 +00001038 _bytesWritten += PutLE32(listTag);
1039
1040 // Size is unknown at this point. Update later.
1041 _bytesWritten += PutLE32(0);
1042 const size_t liststrlSizeMark = _bytesWritten;
1043
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001044 uint32_t hdrlTag = MakeFourCc('s', 't', 'r', 'l');
niklase@google.com470e71d2011-07-07 08:21:25 +00001045 _bytesWritten += PutLE32(hdrlTag);
1046
1047 WriteAVIAudioStreamHeaderChunks();
1048
1049 PutLE32LengthFromCurrent(static_cast<long>(liststrlSizeMark));
1050 //End of LIST
1051 return 0;
1052}
1053
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001054int32_t AviFile::WriteAVIAudioStreamHeaderChunks()
niklase@google.com470e71d2011-07-07 08:21:25 +00001055{
1056 // Start of strh
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001057 const uint32_t strhTag = MakeFourCc('s', 't', 'r', 'h');
niklase@google.com470e71d2011-07-07 08:21:25 +00001058 _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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001089 const uint32_t strfTag = MakeFourCc('s', 't', 'r', 'f');
niklase@google.com470e71d2011-07-07 08:21:25 +00001090 _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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001110 const uint32_t strnTag = MakeFourCc('s', 't', 'r', 'n');
niklase@google.com470e71d2011-07-07 08:21:25 +00001111 _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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001125int32_t AviFile::WriteMoviStart()
niklase@google.com470e71d2011-07-07 08:21:25 +00001126{
1127 // Create template movi list. Fill out size when known (i.e. when closing
1128 // file).
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001129 const uint32_t listTag = MakeFourCc('L', 'I', 'S', 'T');
niklase@google.com470e71d2011-07-07 08:21:25 +00001130 _bytesWritten += PutLE32(listTag);
1131
1132 _bytesWritten += PutLE32(0); //Size! Change later!
1133 _moviSizeMark = _bytesWritten;
1134 _moviListOffset = ftell(_aviFile);
1135
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001136 const uint32_t moviTag = MakeFourCc('m', 'o', 'v', 'i');
niklase@google.com470e71d2011-07-07 08:21:25 +00001137 _bytesWritten += PutLE32(moviTag);
1138
1139 return 0;
1140}
1141
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001142size_t AviFile::PutByte(uint8_t byte)
niklase@google.com470e71d2011-07-07 08:21:25 +00001143{
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001144 return fwrite(&byte, sizeof(uint8_t), sizeof(uint8_t),
niklase@google.com470e71d2011-07-07 08:21:25 +00001145 _aviFile);
1146}
1147
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001148size_t AviFile::PutLE16(uint16_t word)
niklase@google.com470e71d2011-07-07 08:21:25 +00001149{
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001150 return fwrite(&word, sizeof(uint8_t), sizeof(uint16_t),
niklase@google.com470e71d2011-07-07 08:21:25 +00001151 _aviFile);
1152}
1153
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001154size_t AviFile::PutLE32(uint32_t word)
niklase@google.com470e71d2011-07-07 08:21:25 +00001155{
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001156 return fwrite(&word, sizeof(uint8_t), sizeof(uint32_t),
niklase@google.com470e71d2011-07-07 08:21:25 +00001157 _aviFile);
1158}
1159
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001160size_t AviFile::PutBuffer(const uint8_t* str, size_t size)
niklase@google.com470e71d2011-07-07 08:21:25 +00001161{
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001162 return fwrite(str, sizeof(uint8_t), size,
niklase@google.com470e71d2011-07-07 08:21:25 +00001163 _aviFile);
1164}
1165
1166size_t AviFile::PutBufferZ(const char* str)
1167{
1168 // Include NULL charachter, hence the + 1
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001169 return PutBuffer(reinterpret_cast<const uint8_t*>(str),
niklase@google.com470e71d2011-07-07 08:21:25 +00001170 strlen(str) + 1);
1171}
1172
1173long AviFile::PutLE32LengthFromCurrent(long startPos)
1174{
1175 const long endPos = ftell(_aviFile);
henrike@webrtc.org26085e12012-02-27 21:50:40 +00001176 if (endPos < 0) {
1177 return 0;
1178 }
hellner@google.com9aa99962011-08-23 21:25:55 +00001179 bool success = (0 == fseek(_aviFile, startPos - 4, SEEK_SET));
1180 if (!success) {
1181 assert(false);
1182 return 0;
1183 }
niklase@google.com470e71d2011-07-07 08:21:25 +00001184 const long len = endPos - startPos;
hellner@google.com9aa99962011-08-23 21:25:55 +00001185 if (endPos > startPos) {
1186 PutLE32(len);
1187 }
1188 else {
1189 assert(false);
1190 }
1191 success = (0 == fseek(_aviFile, endPos, SEEK_SET));
1192 assert(success);
niklase@google.com470e71d2011-07-07 08:21:25 +00001193 return len;
1194}
1195
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001196void AviFile::PutLE32AtPos(long pos, uint32_t word)
niklase@google.com470e71d2011-07-07 08:21:25 +00001197{
1198 const long currPos = ftell(_aviFile);
henrike@webrtc.org26085e12012-02-27 21:50:40 +00001199 if (currPos < 0) {
1200 assert(false);
1201 return;
1202 }
hellner@google.com09734082011-08-29 14:10:01 +00001203 bool success = (0 == fseek(_aviFile, pos, SEEK_SET));
1204 if (!success) {
1205 assert(false);
1206 return;
1207 }
niklase@google.com470e71d2011-07-07 08:21:25 +00001208 PutLE32(word);
hellner@google.com09734082011-08-29 14:10:01 +00001209 success = (0 == fseek(_aviFile, currPos, SEEK_SET));
1210 assert(success);
niklase@google.com470e71d2011-07-07 08:21:25 +00001211}
1212
1213void AviFile::CloseRead()
1214{
1215 if (_aviFile)
1216 {
1217 fclose(_aviFile);
1218 _aviFile = NULL;
1219 }
1220}
1221
1222void 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
1256void AviFile::ResetMembers()
1257{
henrike@webrtc.org26085e12012-02-27 21:50:40 +00001258 ResetComplexMembers();
niklase@google.com470e71d2011-07-07 08:21:25 +00001259
henrike@webrtc.org26085e12012-02-27 21:50:40 +00001260 _aviFile = NULL;
niklase@google.com470e71d2011-07-07 08:21:25 +00001261
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
henrike@webrtc.org26085e12012-02-27 21:50:40 +00001301void 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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001315size_t AviFile::GetByte(uint8_t& word)
niklase@google.com470e71d2011-07-07 08:21:25 +00001316{
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001317 return fread(&word, sizeof(uint8_t), sizeof(uint8_t), _aviFile);
niklase@google.com470e71d2011-07-07 08:21:25 +00001318}
1319
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001320size_t AviFile::GetLE16(uint16_t& word)
niklase@google.com470e71d2011-07-07 08:21:25 +00001321{
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001322 return fread(&word, sizeof(uint8_t), sizeof(uint16_t),
niklase@google.com470e71d2011-07-07 08:21:25 +00001323 _aviFile);
1324}
1325
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001326size_t AviFile::GetLE32(uint32_t& word)
niklase@google.com470e71d2011-07-07 08:21:25 +00001327{
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001328 return fread(&word, sizeof(uint8_t), sizeof(uint32_t),
niklase@google.com470e71d2011-07-07 08:21:25 +00001329 _aviFile);
1330}
1331
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001332size_t AviFile::GetBuffer(uint8_t* str, size_t size)
niklase@google.com470e71d2011-07-07 08:21:25 +00001333{
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001334 return fread(str, sizeof(uint8_t), size, _aviFile);
niklase@google.com470e71d2011-07-07 08:21:25 +00001335}
1336
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001337int32_t AviFile::ReadRIFF()
niklase@google.com470e71d2011-07-07 08:21:25 +00001338{
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001339 uint32_t tag;
niklase@google.com470e71d2011-07-07 08:21:25 +00001340 _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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001347 uint32_t size;
niklase@google.com470e71d2011-07-07 08:21:25 +00001348 _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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001361int32_t AviFile::ReadHeaders()
niklase@google.com470e71d2011-07-07 08:21:25 +00001362{
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001363 uint32_t tag;
niklase@google.com470e71d2011-07-07 08:21:25 +00001364 _bytesRead += GetLE32(tag);
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001365 uint32_t size;
niklase@google.com470e71d2011-07-07 08:21:25 +00001366 _bytesRead += GetLE32(size);
1367
1368 if (tag != MakeFourCc('L', 'I', 'S', 'T'))
1369 {
1370 return -1;
1371 }
1372
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001373 uint32_t listTag;
niklase@google.com470e71d2011-07-07 08:21:25 +00001374 _bytesRead += GetLE32(listTag);
1375 if (listTag != MakeFourCc('h', 'd', 'r', 'l'))
1376 {
1377 return -1;
1378 }
1379
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001380 int32_t err = ReadAVIMainHeader();
niklase@google.com470e71d2011-07-07 08:21:25 +00001381 if (err)
1382 {
1383 return -1;
1384 }
1385
1386 return 0;
1387}
1388
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001389int32_t AviFile::ReadAVIMainHeader()
niklase@google.com470e71d2011-07-07 08:21:25 +00001390{
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 {
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001423 uint32_t strltag;
niklase@google.com470e71d2011-07-07 08:21:25 +00001424 _bytesRead += GetLE32(strltag);
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001425 uint32_t strlsize;
niklase@google.com470e71d2011-07-07 08:21:25 +00001426 _bytesRead += GetLE32(strlsize);
1427 const long endSeekPos = ftell(_aviFile) +
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001428 static_cast<int32_t>(strlsize);
niklase@google.com470e71d2011-07-07 08:21:25 +00001429
1430 if (strltag != MakeFourCc('L', 'I', 'S', 'T'))
1431 {
1432 return -1;
1433 }
1434
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001435 uint32_t listTag;
niklase@google.com470e71d2011-07-07 08:21:25 +00001436 _bytesRead += GetLE32(listTag);
1437 if (listTag != MakeFourCc('s', 't', 'r', 'l'))
1438 {
1439 return -1;
1440 }
1441
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001442 uint32_t chunktag;
niklase@google.com470e71d2011-07-07 08:21:25 +00001443 _bytesRead += GetLE32(chunktag);
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001444 uint32_t chunksize;
niklase@google.com470e71d2011-07-07 08:21:25 +00001445 _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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001470 uint16_t left;
niklase@google.com470e71d2011-07-07 08:21:25 +00001471 _bytesRead += GetLE16(left);
1472 tmpStreamHeader.rcFrame.left = left;
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001473 uint16_t top;
niklase@google.com470e71d2011-07-07 08:21:25 +00001474 _bytesRead += GetLE16(top);
1475 tmpStreamHeader.rcFrame.top = top;
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001476 uint16_t right;
niklase@google.com470e71d2011-07-07 08:21:25 +00001477 _bytesRead += GetLE16(right);
1478 tmpStreamHeader.rcFrame.right = right;
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001479 uint16_t bottom;
niklase@google.com470e71d2011-07-07 08:21:25 +00001480 _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!
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001487 const int32_t err = ReadAVIVideoStreamHeader(endSeekPos);
niklase@google.com470e71d2011-07-07 08:21:25 +00001488 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;
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001501 const int32_t err = ReadAVIAudioStreamHeader(endSeekPos);
niklase@google.com470e71d2011-07-07 08:21:25 +00001502 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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001522 uint32_t tag;
niklase@google.com470e71d2011-07-07 08:21:25 +00001523 _bytesRead += GetLE32(tag);
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001524 uint32_t size;
niklase@google.com470e71d2011-07-07 08:21:25 +00001525 _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 }
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001538 uint32_t listTag;
niklase@google.com470e71d2011-07-07 08:21:25 +00001539 _bytesRead += GetLE32(listTag);
1540 if (listTag != MakeFourCc('m', 'o', 'v', 'i'))
1541 {
1542 return -1;
1543 }
1544 _dataLength = size;
1545 return 0;
1546}
1547
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001548int32_t AviFile::ReadAVIVideoStreamHeader(int32_t endpos)
niklase@google.com470e71d2011-07-07 08:21:25 +00001549{
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001550 uint32_t chunktag;
niklase@google.com470e71d2011-07-07 08:21:25 +00001551 _bytesRead += GetLE32(chunktag);
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001552 uint32_t chunksize;
niklase@google.com470e71d2011-07-07 08:21:25 +00001553 _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 {
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001574 const uint32_t size = chunksize - _videoFormatHeader.biSize;
1575 const uint32_t readSize = MinValue(size, CODEC_CONFIG_LENGTH);
niklase@google.com470e71d2011-07-07 08:21:25 +00001576 _bytesRead += GetBuffer(
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001577 reinterpret_cast<uint8_t*>(_videoConfigParameters), readSize);
niklase@google.com470e71d2011-07-07 08:21:25 +00001578 _videoConfigLength = readSize;
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001579 int32_t skipSize = chunksize - _videoFormatHeader.biSize -
niklase@google.com470e71d2011-07-07 08:21:25 +00001580 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 {
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001590 uint32_t chunktag;
niklase@google.com470e71d2011-07-07 08:21:25 +00001591 _bytesRead += GetLE32(chunktag);
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001592 uint32_t chunksize;
niklase@google.com470e71d2011-07-07 08:21:25 +00001593 _bytesRead += GetLE32(chunksize);
1594
1595 if (chunktag == MakeFourCc('s', 't', 'r', 'n'))
1596 {
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001597 const uint32_t size = MinValue(chunksize, STREAM_NAME_LENGTH);
niklase@google.com470e71d2011-07-07 08:21:25 +00001598 _bytesRead += GetBuffer(
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001599 reinterpret_cast<uint8_t*>(_videoStreamName), size);
niklase@google.com470e71d2011-07-07 08:21:25 +00001600 }
1601 else if (chunktag == MakeFourCc('s', 't', 'r', 'd'))
1602 {
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001603 const uint32_t size = MinValue(chunksize, CODEC_CONFIG_LENGTH);
niklase@google.com470e71d2011-07-07 08:21:25 +00001604 _bytesRead += GetBuffer(
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001605 reinterpret_cast<uint8_t*>(_videoConfigParameters), size);
niklase@google.com470e71d2011-07-07 08:21:25 +00001606 _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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001625int32_t AviFile::ReadAVIAudioStreamHeader(int32_t endpos)
niklase@google.com470e71d2011-07-07 08:21:25 +00001626{
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001627 uint32_t chunktag;
niklase@google.com470e71d2011-07-07 08:21:25 +00001628 _bytesRead += GetLE32(chunktag);
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001629 uint32_t chunksize;
niklase@google.com470e71d2011-07-07 08:21:25 +00001630 _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);
braveyao@webrtc.org617e23c2012-08-20 03:58:41 +00001644 if (chunksize > 0x10) {
1645 _bytesRead += GetLE16(_audioFormatHeader.cbSize);
1646 }
niklase@google.com470e71d2011-07-07 08:21:25 +00001647
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001648 const uint32_t diffRead = chunksize - (_bytesRead - startRead);
niklase@google.com470e71d2011-07-07 08:21:25 +00001649 if (diffRead > 0)
1650 {
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001651 const uint32_t size = MinValue(diffRead, CODEC_CONFIG_LENGTH);
niklase@google.com470e71d2011-07-07 08:21:25 +00001652 _bytesRead += GetBuffer(
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001653 reinterpret_cast<uint8_t*>(_audioConfigParameters), size);
niklase@google.com470e71d2011-07-07 08:21:25 +00001654 }
1655
1656 while (static_cast<long>(_bytesRead) < endpos)
1657 {
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001658 uint32_t chunktag;
niklase@google.com470e71d2011-07-07 08:21:25 +00001659 _bytesRead += GetLE32(chunktag);
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001660 uint32_t chunksize;
niklase@google.com470e71d2011-07-07 08:21:25 +00001661 _bytesRead += GetLE32(chunksize);
1662
1663 if (chunktag == MakeFourCc('s', 't', 'r', 'n'))
1664 {
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001665 const uint32_t size = MinValue(chunksize, STREAM_NAME_LENGTH);
niklase@google.com470e71d2011-07-07 08:21:25 +00001666 _bytesRead += GetBuffer(
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001667 reinterpret_cast<uint8_t*>(_audioStreamName), size);
niklase@google.com470e71d2011-07-07 08:21:25 +00001668 }
1669 else if (chunktag == MakeFourCc('s', 't', 'r', 'd'))
1670 {
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001671 const uint32_t size = MinValue(chunksize, CODEC_CONFIG_LENGTH);
niklase@google.com470e71d2011-07-07 08:21:25 +00001672 _bytesRead += GetBuffer(
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001673 reinterpret_cast<uint8_t*>(_audioConfigParameters), size);
niklase@google.com470e71d2011-07-07 08:21:25 +00001674 }
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
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001691uint32_t AviFile::StreamAndTwoCharCodeToTag(int32_t streamNum,
1692 const char* twoCharCode)
niklase@google.com470e71d2011-07-07 08:21:25 +00001693{
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001694 uint8_t a = '0';
1695 uint8_t b;
niklase@google.com470e71d2011-07-07 08:21:25 +00001696 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
1710void AviFile::ClearIndexList()
1711{
henrike@webrtc.org79cf3ac2014-01-13 15:21:30 +00001712 for (IndexList::iterator iter = _indexList.begin();
1713 iter != _indexList.end(); ++iter) {
1714 delete *iter;
1715 }
1716 _indexList.clear();
niklase@google.com470e71d2011-07-07 08:21:25 +00001717}
1718
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001719void AviFile::AddChunkToIndexList(uint32_t inChunkId,
1720 uint32_t inFlags,
1721 uint32_t inOffset,
1722 uint32_t inSize)
niklase@google.com470e71d2011-07-07 08:21:25 +00001723{
henrike@webrtc.org79cf3ac2014-01-13 15:21:30 +00001724 _indexList.push_back(new AVIINDEXENTRY(inChunkId, inFlags, inOffset,
niklase@google.com470e71d2011-07-07 08:21:25 +00001725 inSize));
1726}
1727
1728void AviFile::WriteIndex()
1729{
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001730 const uint32_t idxTag = MakeFourCc('i', 'd', 'x', '1');
niklase@google.com470e71d2011-07-07 08:21:25 +00001731 _bytesWritten += PutLE32(idxTag);
1732
1733 // Size is unknown at this point. Update later.
1734 _bytesWritten += PutLE32(0);
1735 const size_t idxChunkSize = _bytesWritten;
1736
henrike@webrtc.org79cf3ac2014-01-13 15:21:30 +00001737 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);
niklase@google.com470e71d2011-07-07 08:21:25 +00001744 }
1745 PutLE32LengthFromCurrent(static_cast<long>(idxChunkSize));
1746}
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +00001747} // namespace webrtc