1 // VirtualDub - Video processing and capture application
2 // Copyright (C) 1998-2001 Avery Lee
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 #include "stdafx.h"
19
20 #include "AVIReadHandler.h"
21 #include "AVIReadCache.h"
22 #include "AVIReadIndex.h"
23 #include <vd2/system/binary.h>
24 #include <vd2/system/bitmath.h>
25 #include <vd2/system/debug.h>
26 #include <vd2/system/error.h>
27 #include <vd2/system/list.h>
28 #include <vd2/system/file.h>
29 #include <vd2/system/log.h>
30 #include <vd2/system/math.h>
31 #include <vd2/system/text.h>
32 #include <vd2/system/vdalloc.h>
33 #include <vd2/system/vdstl.h>
34 #include <vd2/system/VDString.h>
35 #include <vd2/system/w32assist.h>
36 #include <vd2/Dita/resources.h>
37 #include "Fixes.h"
38 #include "misc.h"
39
40 //#define VDTRACE_AVISTREAMING VDDEBUG
41 #define VDTRACE_AVISTREAMING (void)sizeof
42
43 #if defined(VD_COMPILER_MSVC)
44 #pragma warning(disable: 4200) // warning C4200: nonstandard extension used : zero-sized array in struct/union
45 #endif
46
47 extern bool VDPreferencesIsAVINonZeroStartWarningEnabled();
48
49 ///////////////////////////////////////////////////////////////////////////
50
51 namespace {
52 enum { kVDST_AVIReadHandler = 2 };
53
54 enum {
55 kVDM_AvisynthDetected, // AVI: Avisynth detected. Extended error handling enabled.
56 kVDM_OpenDMLIndexDetected, // AVI: OpenDML hierarchical index detected on stream %d.
57 kVDM_IndexMissing, // AVI: Index not found or damaged -- reconstructing via file scan.
58 kVDM_InvalidChunkDetected, // AVI: Invalid chunk detected at %lld. Enabling aggressive recovery mode.
59 kVDM_StreamFailure, // AVI: Invalid block found at %lld -- disabling streaming.
60 kVDM_FixingBadSampleRate, // AVI: Stream %d has an invalid sample rate. Substituting %lu samples/sec as placeholder.
61 kVDM_PaletteChanges, // AVI: Palette changes detected. These are not currently supported -- color palette errors may appear in the output.
62 kVDM_InfoTruncated, // AVI: The text information chunk of type '%s' at %llx was not fully read because it was too long (%u bytes).
63 kVDM_NonZeroStart, // AVI: Stream %u (%s) has a start position of %lld samples (%+lld ms). VirtualDub does not currently support a non-zero start time and the stream will be interpreted as starting from zero.
64 kVDM_IndexingAborted // AVI: Indexing was aborted at byte location %llx.
65 };
66
is_palette_change(uint32 ckid)67 bool is_palette_change(uint32 ckid) {
68 return (ckid >> 16) == 'cp';
69 }
70
isValidFOURCCChar(uint8 c)71 bool isValidFOURCCChar(uint8 c) {
72 // FOURCCs must consist of a sequence of alphanumerics, padded at the end by spaces.
73 //
74 // Change: Underscores spotted out of DirectShow DV mux filter. *sigh*
75 return (uint8)(c-0x30)<10 || (uint8)(c-0x41)<26 || (uint8)(c-0x61)<26 || c==0x20 || c==(uint8)'_';
76 }
77 }
78
79 ///////////////////////////////////////////////////////////////////////////
80
81 // The following comes from the OpenDML 1.0 spec for extended AVI files
82
83 namespace {
84 // bIndexType codes
85 //
86 #define AVI_INDEX_OF_INDEXES 0x00 // when each entry in aIndex
87 // array points to an index chunk
88
89 #define AVI_INDEX_OF_CHUNKS 0x01 // when each entry in aIndex
90 // array points to a chunk in the file
91
92 #define AVI_INDEX_IS_DATA 0x80 // when each entry is aIndex is
93 // really the data
94
95 // bIndexSubtype codes for INDEX_OF_CHUNKS
96
97 #define AVI_INDEX_2FIELD 0x01 // when fields within frames
98 // are also indexed
99 struct _avisuperindex_entry {
100 uint64 qwOffset; // absolute file offset, offset 0 is
101 // unused entry??
102 uint32 dwSize; // size of index chunk at this offset
103 uint32 dwDuration; // time span in stream ticks
104 };
105 struct _avistdindex_entry {
106 uint32 dwOffset; // qwBaseOffset + this is absolute file offset
107 uint32 dwSize; // bit 31 is set if this is NOT a keyframe
108 };
109 struct _avifieldindex_entry {
110 uint32 dwOffset;
111 uint32 dwSize;
112 uint32 dwOffsetField2;
113 };
114
115 #pragma pack(push)
116 #pragma pack(2)
117
118 typedef struct _avisuperindex_chunk {
119 uint32 fcc; // �ix##�
120 uint32 cb; // size of this structure
121 uint16 wLongsPerEntry; // must be 4 (size of each entry in aIndex array)
122 uint8 bIndexSubType; // must be 0 or AVI_INDEX_2FIELD
123 uint8 bIndexType; // must be AVI_INDEX_OF_INDEXES
124 uint32 nEntriesInUse; // number of entries in aIndex array that
125 // are used
126 uint32 dwChunkId; // �##dc� or �##db� or �##wb�, etc
127 uint32 dwReserved[3]; // must be 0
128 struct _avisuperindex_entry aIndex[];
129 } AVISUPERINDEX, * PAVISUPERINDEX;
130
131 typedef struct _avistdindex_chunk {
132 uint32 fcc; // �ix##�
133 uint32 cb;
134 uint16 wLongsPerEntry; // must be sizeof(aIndex[0])/sizeof(uint32)
135 uint8 bIndexSubType; // must be 0
136 uint8 bIndexType; // must be AVI_INDEX_OF_CHUNKS
137 uint32 nEntriesInUse; //
138 uint32 dwChunkId; // �##dc� or �##db� or �##wb� etc..
139 uint64 qwBaseOffset; // all dwOffsets in aIndex array are
140 // relative to this
141 uint32 dwReserved3; // must be 0
142 struct _avistdindex_entry aIndex[];
143 } AVISTDINDEX, * PAVISTDINDEX;
144
145 typedef struct _avifieldindex_chunk {
146 uint32 fcc;
147 uint32 cb;
148 uint16 wLongsPerEntry;
149 uint8 bIndexSubType;
150 uint8 bIndexType;
151 uint32 nEntriesInUse;
152 uint32 dwChunkId;
153 uint64 qwBaseOffset;
154 uint32 dwReserved3;
155 struct _avifieldindex_entry aIndex[];
156 } AVIFIELDINDEX, * PAVIFIELDINDEX;
157
158 struct AVIIndexEntry {
159 enum {
160 kFlagKeyFrame = 0x10
161 };
162
163 uint32 ckid;
164 uint32 dwFlags;
165 uint32 dwChunkOffset;
166 uint32 dwChunkLength;
167 };
168
169 struct VDAVIMainHeader {
170 uint32 dwMicroSecPerFrame;
171 uint32 dwMaxBytesPerSec;
172 uint32 dwPaddingGranularity;
173 uint32 dwFlags;
174 uint32 dwTotalFrames;
175 uint32 dwInitialFrames;
176 uint32 dwStreams;
177 uint32 dwSuggestedBufferSize;
178 uint32 dwWidth;
179 uint32 dwHeight;
180 uint32 dwReserved[4];
181 };
182
183 #pragma pack(pop)
184
185 static const uint32 kAVIStreamTypeAudio = VDMAKEFOURCC('a', 'u', 'd', 's');
186 static const uint32 kAVIStreamTypeVideo = VDMAKEFOURCC('v', 'i', 'd', 's');
187 static const uint32 kAVIStreamTypeDV = VDMAKEFOURCC('i', 'a', 'v', 's');
188
VDAVIGetStreamFromFOURCC(uint32 fcc0)189 uint32 VDAVIGetStreamFromFOURCC(uint32 fcc0) {
190 uint32 fcc = VDFromLE32(fcc0);
191 uint32 hi = ((fcc >> 0) - '0') & 0x1f;
192 uint32 lo = ((fcc >> 8) - '0') & 0x1f;
193
194 if (lo >= 10)
195 lo -= 7;
196
197 if (hi >= 10)
198 hi -= 7;
199
200 return (hi << 4) + lo;
201 }
202 }
203
204 #ifdef _DEBUG
205 namespace {
206 class Check {
207 public:
Check()208 Check() {
209 VDASSERT(VDAVIGetStreamFromFOURCC(VDMAKEFOURCC('0', '0', 'd', 'c')) == 0);
210 VDASSERT(VDAVIGetStreamFromFOURCC(VDMAKEFOURCC('0', '1', 'd', 'c')) == 1);
211 VDASSERT(VDAVIGetStreamFromFOURCC(VDMAKEFOURCC('0', '9', 'd', 'c')) == 9);
212 VDASSERT(VDAVIGetStreamFromFOURCC(VDMAKEFOURCC('0', 'a', 'd', 'c')) == 10);
213 VDASSERT(VDAVIGetStreamFromFOURCC(VDMAKEFOURCC('0', 'f', 'd', 'c')) == 15);
214 VDASSERT(VDAVIGetStreamFromFOURCC(VDMAKEFOURCC('1', '0', 'd', 'c')) == 16);
215 VDASSERT(VDAVIGetStreamFromFOURCC(VDMAKEFOURCC('9', 'f', 'd', 'c')) == 159);
216 VDASSERT(VDAVIGetStreamFromFOURCC(VDMAKEFOURCC('a', '0', 'd', 'c')) == 160);
217 VDASSERT(VDAVIGetStreamFromFOURCC(VDMAKEFOURCC('f', 'f', 'd', 'c')) == 255);
218 }
219 } gCheck;
220 }
221 #endif
222
223
224 ///////////////////////////////////////////////////////////////////////////
225
~IAVIReadStream()226 IAVIReadStream::~IAVIReadStream() {
227 }
228
229 ///////////////////////////////////////////////////////////////////////////
230
231 class AVIStreamNode;
232 class AVIReadHandler;
233
234 ///////////////////////////////////////////////////////////////////////////
235
236 class AVIStreamNode : public ListNode2<AVIStreamNode> {
237 public:
238 AVIStreamHeader_fixed hdr;
239 void *pFormat;
240 long lFormatLen;
241 VDAVIReadIndex mIndex;
242 sint64 bytes;
243 bool keyframe_only;
244 bool is_VBR;
245 int handler_count;
246 class AVIReadCache *cache;
247 int streaming_count;
248 sint64 stream_push_pos;
249 sint64 stream_bytes;
250 int stream_pushes;
251 sint64 length;
252 sint64 frames;
253 List2<class AVIReadStream> listHandlers;
254
255 AVIStreamNode();
256 ~AVIStreamNode();
257 };
258
AVIStreamNode()259 AVIStreamNode::AVIStreamNode() {
260 pFormat = nullptr;
261 bytes = 0;
262 handler_count = 0;
263 streaming_count = 0;
264
265 stream_bytes = 0;
266 stream_pushes = 0;
267 cache = nullptr;
268
269 is_VBR = false;
270 }
271
~AVIStreamNode()272 AVIStreamNode::~AVIStreamNode() {
273 delete pFormat;
274 delete cache;
275 }
276
277 ///////////////////////////////////////////////////////////////////////////
278
279 class AVIFileDesc {
280 public:
281 VDFile mFile;
282 VDFile mFileUnbuffered;
283 sint64 mFileSize;
284 };
285
286 class AVIStreamNode;
287
288 class AVIReadHandler : public IAVIReadHandler, public IAVIReadCacheSource {
289 public:
290 AVIReadHandler(const wchar_t *);
291 ~AVIReadHandler();
292
293 void AddRef();
294 void Release();
295 IAVIReadStream *GetStream(uint32 fccType, int lParam);
296 void EnableFastIO(bool);
297 bool isOptimizedForRealtime();
298 bool isStreaming();
299 bool isIndexFabricated();
300 bool AppendFile(const wchar_t *pszFile);
301 bool getSegmentHint(const char **ppszPath);
302 void GetTextInfo(tTextInfo& textInfo);
303 void GetTextInfoEncoding(int& codePage, int& countryCode, int& language, int& dialect);
304
305 void EnableStreaming(int stream);
306 void DisableStreaming(int stream);
307 void AdjustRealTime(bool fRealTime);
308 void FixCacheProblems(class AVIReadStream *);
309 long ReadData(int stream, void *buffer, sint64 position, long len);
310
311 public: // IAVIReadCacheSource
312 bool Stream(AVIStreamNode *, _int64 pos);
313 sint64 getStreamPtr();
314
315 private:
316 friend class AVIReadStream;
317
318 bool ReadChunkHeader(uint32& type, uint32& size);
319 void SelectFile(int file);
320
321 // enum { STREAM_SIZE = 65536 };
322 enum { STREAM_SIZE = 1048576 };
323 enum { STREAM_RT_SIZE = 65536 };
324 enum { STREAM_BLOCK_SIZE = 4096 };
325
326 int mRefCount;
327 sint64 i64StreamPosition;
328 int streams;
329 char *streamBuffer;
330 int sbPosition;
331 int sbSize;
332 long fStreamsActive;
333 int nRealTime;
334 int nActiveStreamers;
335 bool fFakeIndex;
336 int nFiles;
337
338 AVIFileDesc *mpCurrentFile;
339 int mCurrentFile;
340
341 char * pSegmentHint;
342
343 bool fDisableFastIO;
344
345 // Whenever a file is aggressively recovered, do not allow streaming.
346
347 bool mbFileIsDamaged;
348 bool mbPaletteChangesDetected;
349
350 int mTextInfoCodePage;
351 int mTextInfoCountryCode;
352 int mTextInfoLanguage;
353 int mTextInfoDialect;
354
355 tTextInfo mTextInfo;
356
357 List2<AVIStreamNode> listStreams;
358 vdfastvector<AVIFileDesc *> mFiles;
359
360 void _construct(const wchar_t *pszFile);
361 void _parseFile(List2<AVIStreamNode>& streams);
362 bool _parseStreamHeader(List2<AVIStreamNode>& streams, uint32 dwLengthLeft, bool& bIndexDamaged);
363 bool _parseIndexBlock(List2<AVIStreamNode>& streams, int count, sint64);
364 void _parseExtendedIndexBlock(List2<AVIStreamNode>& streams, AVIStreamNode *pasn, sint64 fpos, uint32 dwLength);
365 void _destruct();
366
367 char * _StreamRead(long& bytes);
368 };
369
CreateAVIReadHandler(const wchar_t * pszFile)370 IAVIReadHandler *CreateAVIReadHandler(const wchar_t *pszFile) {
371 return new AVIReadHandler(pszFile);
372 }
373
374 ///////////////////////////////////////////////////////////////////////////
375
376 class AVIReadStream : public IAVIReadStream, public ListNode2<AVIReadStream> {
377 friend AVIReadHandler;
378
379 public:
380 AVIReadStream(AVIReadHandler *, AVIStreamNode *, int);
381 ~AVIReadStream();
382
383 sint32 BeginStreaming(VDPosition lStart, VDPosition lEnd, long lRate);
384 sint32 EndStreaming();
385 sint32 Info(VDAVIStreamInfo *pasi);
386 bool IsKeyFrame(VDPosition lFrame);
387 sint32 Read(VDPosition lStart, long lSamples, void *lpBuffer, long cbBuffer, long *plBytes, long *plSamples);
388 VDPosition Start();
389 VDPosition End();
390 VDPosition PrevKeyFrame(VDPosition lFrame);
391 VDPosition NextKeyFrame(VDPosition lFrame);
392 VDPosition NearestKeyFrame(VDPosition lFrame);
393 sint32 FormatSize(VDPosition lFrame, long *plSize);
394 sint32 ReadFormat(VDPosition lFrame, void *pFormat, long *plSize);
395 bool isStreaming();
396 bool isKeyframeOnly();
397 void Reinit();
398 bool getVBRInfo(double& bitrate_mean, double& bitrate_stddev, double& maxdev);
399 sint64 getSampleBytePosition(VDPosition sample_num);
400
401 VDPosition TimeToPosition(VDTime timeInMicroseconds);
402 VDTime PositionToTime(VDPosition pos);
403
404 private:
405 AVIReadHandler *parent;
406 AVIStreamNode *psnData;
407 VDAVIReadIndex *mpIndex;
408 AVIReadCache *rCache;
409 sint64& length;
410 sint64& frames;
411 long sampsize;
412 int streamno;
413 bool fStreamingEnabled;
414 bool fStreamingActive;
415 int iStreamTrackCount;
416 sint64 lStreamTrackValue;
417 sint64 lStreamTrackInterval;
418 bool fRealTime;
419 };
420
421 ///////////////////////////////////////////////////////////////////////////
422
AVIReadStream(AVIReadHandler * parent,AVIStreamNode * psnData,int streamno)423 AVIReadStream::AVIReadStream(AVIReadHandler *parent, AVIStreamNode *psnData, int streamno)
424 :length(psnData->length)
425 ,frames(psnData->frames)
426 {
427 this->parent = parent;
428 this->psnData = psnData;
429 this->streamno = streamno;
430
431 fStreamingEnabled = false;
432 fStreamingActive = false;
433 fRealTime = false;
434
435 parent->AddRef();
436
437 mpIndex = &psnData->mIndex;
438 sampsize = psnData->hdr.dwSampleSize;
439
440 // Hack to imitate Microsoft's parser. It seems to ignore this value
441 // for audio streams.
442
443 if (psnData->hdr.fccType == kAVIStreamTypeAudio && !psnData->is_VBR) {
444 sampsize = ((WAVEFORMATEX *)psnData->pFormat)->nBlockAlign;
445
446 // wtf....
447 if (!sampsize)
448 sampsize = 1;
449
450 length = mpIndex->GetSampleCount();
451 }
452
453 psnData->listHandlers.AddTail(this);
454 }
455
~AVIReadStream()456 AVIReadStream::~AVIReadStream() {
457 EndStreaming();
458 Remove();
459 parent->Release();
460 }
461
Reinit()462 void AVIReadStream::Reinit() {
463 length = mpIndex->GetSampleCount();
464 }
465
BeginStreaming(VDPosition lStart,VDPosition lEnd,long lRate)466 sint32 AVIReadStream::BeginStreaming(VDPosition lStart, VDPosition lEnd, long lRate) {
467 if (fStreamingEnabled)
468 return 0;
469
470 if (lRate <= 1500) {
471 parent->AdjustRealTime(true);
472 fRealTime = true;
473 } else
474 fRealTime = false;
475
476 if (parent->fDisableFastIO)
477 return 0;
478
479 if (!psnData->streaming_count) {
480 psnData->stream_bytes = 0;
481 psnData->stream_pushes = 0;
482 psnData->stream_push_pos = 0;
483 }
484 ++psnData->streaming_count;
485
486 fStreamingEnabled = true;
487 fStreamingActive = false;
488 iStreamTrackCount = 0;
489 lStreamTrackValue = -1;
490 lStreamTrackInterval = -1;
491 return 0;
492 }
493
EndStreaming()494 sint32 AVIReadStream::EndStreaming() {
495 if (!fStreamingEnabled)
496 return 0;
497
498 if (fRealTime)
499 parent->AdjustRealTime(false);
500
501 if (fStreamingActive)
502 parent->DisableStreaming(streamno);
503
504 fStreamingEnabled = false;
505 fStreamingActive = false;
506
507 if (!--psnData->streaming_count) {
508 delete psnData->cache;
509 psnData->cache = nullptr;
510 }
511 return 0;
512 }
513
Info(VDAVIStreamInfo * pasi)514 sint32 AVIReadStream::Info(VDAVIStreamInfo *pasi) {
515 VDAVIStreamInfo& asi = *pasi;
516
517 asi.fccType = psnData->hdr.fccType;
518 asi.fccHandler = psnData->hdr.fccHandler;
519 asi.dwFlags = psnData->hdr.dwFlags;
520 asi.wPriority = psnData->hdr.wPriority;
521 asi.wLanguage = psnData->hdr.wLanguage;
522 asi.dwScale = psnData->hdr.dwScale;
523 asi.dwRate = psnData->hdr.dwRate;
524 asi.dwStart = psnData->hdr.dwStart;
525 asi.dwLength = psnData->hdr.dwLength;
526 asi.dwInitialFrames = psnData->hdr.dwInitialFrames;
527 asi.dwSuggestedBufferSize = psnData->hdr.dwSuggestedBufferSize;
528 asi.dwQuality = psnData->hdr.dwQuality;
529 asi.dwSampleSize = psnData->hdr.dwSampleSize;
530 asi.rcFrameTop = psnData->hdr.rcFrame.top;
531 asi.rcFrameLeft = psnData->hdr.rcFrame.left;
532 asi.rcFrameRight = psnData->hdr.rcFrame.right;
533 asi.rcFrameBottom = psnData->hdr.rcFrame.bottom;
534 return 0;
535 }
536
IsKeyFrame(VDPosition lFrame)537 bool AVIReadStream::IsKeyFrame(VDPosition lFrame) {
538 return mpIndex->IsKey(lFrame);
539 }
540
Read(VDPosition lStart,long lSamples,void * lpBuffer,long cbBuffer,long * plBytes,long * plSamples)541 sint32 AVIReadStream::Read(VDPosition lStart, long lSamples, void *lpBuffer, long cbBuffer, long *plBytes, long *plSamples) {
542 long lActual;
543
544 if (lStart < 0 || lStart >= length || (lSamples <= 0 && lSamples != kConvenient)) {
545 // umm... dummy! can't read outside of stream!
546
547 if (plBytes) *plBytes = 0;
548 if (plSamples) *plSamples = 0;
549
550 return 0;
551 }
552
553 // blocked or discrete?
554
555 if (sampsize) {
556 // too small to hold a sample?
557 if (lpBuffer && cbBuffer < sampsize) {
558 if (plBytes) *plBytes = sampsize * lSamples;
559 if (plSamples) *plSamples = lSamples;
560
561 return kBufferTooSmall;
562 }
563
564 // client too lazy to specify a size?
565 bool stopOnRead = false;
566 if (lSamples == kConvenient) {
567 lSamples = 0x10000;
568 stopOnRead = true;
569 }
570
571 // locate first sample
572 VDAVIReadIndexIterator it;
573
574 mpIndex->FindSampleRange(it, lStart, lSamples);
575
576 sint64 chunkPos;
577 uint32 chunkOffset;
578 uint32 byteSize;
579 bool more = mpIndex->GetNextSampleRange(it, chunkPos, chunkOffset, byteSize);
580
581 sint64 actual_bytes = 0;
582
583 // trim down sample count
584 if (lpBuffer && lSamples > cbBuffer / sampsize)
585 lSamples = cbBuffer / sampsize;
586
587 if (lStart+lSamples > length)
588 lSamples = (long)(length - lStart);
589
590 uint64 bytecnt = lSamples * sampsize;
591
592 // begin reading frames from this point on
593 if (lpBuffer) {
594 // detect streaming
595
596 if (fStreamingEnabled) {
597
598 // We consider the client to be streaming if we detect at least
599 // 3 consecutive accesses
600
601 if (lStart == lStreamTrackValue) {
602 ++iStreamTrackCount;
603
604 if (iStreamTrackCount >= 15) {
605
606 sint64 streamptr = parent->getStreamPtr();
607 sint64 fptrdiff = streamptr - (chunkPos - 8);
608
609 if (!parent->isStreaming() || streamptr<0 || (fptrdiff<4194304 && fptrdiff>-4194304)) {
610 if (!psnData->cache)
611 psnData->cache = new AVIReadCache(psnData->hdr.fccType == 'sdiv' ? 131072 : 16384, streamno, parent, psnData);
612 else
613 psnData->cache->ResetStatistics();
614
615 if (!fStreamingActive) {
616 fStreamingActive = true;
617 parent->EnableStreaming(streamno);
618 }
619
620 VDTRACE_AVISTREAMING("[a] streaming enabled\n");
621 }
622 } else {
623 VDTRACE_AVISTREAMING("[a] streaming detected\n");
624 }
625 } else {
626 VDTRACE_AVISTREAMING("[a] streaming disabled\n");
627
628 iStreamTrackCount = 0;
629
630 if (fStreamingActive) {
631 fStreamingActive = false;
632 parent->DisableStreaming(streamno);
633 }
634 }
635 }
636
637 while(bytecnt > 0) {
638 uint32 tc = byteSize;
639 if (tc > bytecnt)
640 tc = (uint32)bytecnt;
641
642 if (psnData->cache && fStreamingActive && tc < psnData->cache->getMaxRead()) {
643 lActual = psnData->cache->Read(lpBuffer, chunkPos - 8, chunkPos + chunkOffset, tc);
644 psnData->stream_bytes += lActual;
645 } else
646 lActual = parent->ReadData(streamno, lpBuffer, chunkPos + chunkOffset, tc);
647
648 if (lActual < 0)
649 break;
650
651 actual_bytes += lActual;
652
653 if (lActual < tc)
654 break;
655
656 bytecnt -= tc;
657 lpBuffer = (char *)lpBuffer + tc;
658
659 if (!more)
660 break;
661
662 more = mpIndex->GetNextSampleRange(it, chunkPos, chunkOffset, byteSize);
663 }
664
665 if (actual_bytes < sampsize) {
666 if (plBytes) *plBytes = 0;
667 if (plSamples) *plSamples = 0;
668 return kFileReadError;
669 }
670
671 actual_bytes -= actual_bytes % sampsize;
672
673 if (plBytes) *plBytes = (long)actual_bytes;
674 if (plSamples) *plSamples = (long)actual_bytes / sampsize;
675
676 lStreamTrackValue = lStart + (long)actual_bytes / sampsize;
677
678 } else {
679 if (plBytes) *plBytes = (long)bytecnt;
680 if (plSamples) *plSamples = lSamples;
681 }
682
683 } else {
684 VDAVIReadIndexIterator it;
685 mpIndex->FindSampleRange(it, lStart, lSamples);
686
687 sint64 chunkPos;
688 uint32 chunkOffset;
689 uint32 byteSize;
690 mpIndex->GetNextSampleRange(it, chunkPos, chunkOffset, byteSize);
691
692 if (lpBuffer && byteSize > cbBuffer) {
693 if (plBytes) *plBytes = byteSize;
694 if (plSamples) *plSamples = 1;
695
696 return kBufferTooSmall;
697 }
698
699 if (lpBuffer) {
700
701 // detect streaming
702
703 if (fStreamingEnabled && lStart != lStreamTrackValue) {
704 if (lStreamTrackValue>=0 && lStart-lStreamTrackValue == lStreamTrackInterval) {
705 if (++iStreamTrackCount >= 15) {
706
707 sint64 streamptr = parent->getStreamPtr();
708 sint64 fptrdiff = streamptr - (chunkPos - 8);
709
710 if (!parent->isStreaming() || streamptr<0 || (fptrdiff<4194304 && fptrdiff>-4194304)) {
711 if (!psnData->cache)
712 psnData->cache = new AVIReadCache(psnData->hdr.fccType == 'sdiv' ? 131072 : 16384, streamno, parent, psnData);
713 else
714 psnData->cache->ResetStatistics();
715
716 if (!fStreamingActive) {
717 fStreamingActive = true;
718 parent->EnableStreaming(streamno);
719 }
720
721 VDTRACE_AVISTREAMING("[v] streaming activated\n");
722 }
723 } else {
724 VDTRACE_AVISTREAMING("[v] streaming detected\n");
725 }
726 } else {
727 iStreamTrackCount = 0;
728
729 VDTRACE_AVISTREAMING("[v] streaming disabled\n");
730
731 if (lStreamTrackValue>=0 && lStart > lStreamTrackValue) {
732 lStreamTrackInterval = lStart - lStreamTrackValue;
733 } else
734 lStreamTrackInterval = -1;
735
736 if (fStreamingActive) {
737 fStreamingActive = false;
738 parent->DisableStreaming(streamno);
739 }
740 }
741
742 lStreamTrackValue = lStart;
743 }
744
745 // read data
746
747 if (psnData->cache && fStreamingActive && byteSize < psnData->cache->getMaxRead()) {
748 //OutputDebugString("[v] attempting cached read\n");
749 lActual = psnData->cache->Read(lpBuffer, chunkPos - 8, chunkPos + chunkOffset, byteSize);
750 psnData->stream_bytes += lActual;
751 } else
752 lActual = parent->ReadData(streamno, lpBuffer, chunkPos + chunkOffset, byteSize);
753
754 if (lActual != (long)byteSize) {
755 if (plBytes) *plBytes = 0;
756 if (plSamples) *plSamples = 0;
757 return kFileReadError;
758 }
759 }
760
761 if (plBytes) *plBytes = byteSize;
762 if (plSamples) *plSamples = 1;
763 }
764
765 if (psnData->cache && fStreamingActive) {
766
767 // Are we experiencing a high rate of cache misses?
768
769 if (psnData->cache->cache_miss_bytes*2 > psnData->cache->cache_hit_bytes && psnData->cache->reads > 50) {
770
771 // sh*t, notify the parent that we have cache misses so it can check which stream is
772 // screwing up, and disable streaming on feeds that are too far off
773
774 parent->FixCacheProblems(this);
775 iStreamTrackCount = 0;
776 }
777 }
778
779 return 0;
780 }
781
getSampleBytePosition(VDPosition pos)782 sint64 AVIReadStream::getSampleBytePosition(VDPosition pos) {
783 if (pos < 0 || pos >= length)
784 return -1;
785
786 VDAVIReadIndexIterator it;
787 mpIndex->FindSampleRange(it, pos, 1);
788
789 sint64 chunkPos;
790 uint32 chunkOffset;
791 uint32 byteSize;
792 mpIndex->GetNextSampleRange(it, chunkPos, chunkOffset, byteSize);
793
794 return (chunkPos + chunkOffset) & 0x0000FFFFFFFFFFFF;
795 }
796
Start()797 VDPosition AVIReadStream::Start() {
798 return 0;
799 }
800
End()801 VDPosition AVIReadStream::End() {
802 return length;
803 }
804
PrevKeyFrame(VDPosition lFrame)805 VDPosition AVIReadStream::PrevKeyFrame(VDPosition lFrame) {
806 if (sampsize)
807 return lFrame>0 ? lFrame-1 : -1;
808
809 if (lFrame < 0)
810 return -1;
811
812 if (lFrame >= length)
813 lFrame = length;
814
815 return mpIndex->PrevKey(lFrame);
816 }
817
NextKeyFrame(VDPosition lFrame)818 VDPosition AVIReadStream::NextKeyFrame(VDPosition lFrame) {
819 if (sampsize)
820 return lFrame<length ? lFrame+1 : -1;
821
822 if (lFrame < 0)
823 return 0;
824
825 if (lFrame >= length)
826 return -1;
827
828 return mpIndex->NextKey(lFrame);
829 }
830
NearestKeyFrame(VDPosition lFrame)831 VDPosition AVIReadStream::NearestKeyFrame(VDPosition lFrame) {
832 if (sampsize)
833 return lFrame;
834
835 if (lFrame < 0)
836 return -1;
837
838 if (lFrame >= length)
839 lFrame = length - 1;
840
841 return mpIndex->NearestKey(lFrame);
842 }
843
FormatSize(VDPosition lFrame,long * plSize)844 sint32 AVIReadStream::FormatSize(VDPosition lFrame, long *plSize) {
845 *plSize = psnData->lFormatLen;
846 return 0;
847 }
848
ReadFormat(VDPosition lFrame,void * pFormat,long * plSize)849 sint32 AVIReadStream::ReadFormat(VDPosition lFrame, void *pFormat, long *plSize) {
850 if (!pFormat) {
851 *plSize = psnData->lFormatLen;
852 return 0;
853 }
854
855 if (*plSize < psnData->lFormatLen) {
856 memcpy(pFormat, psnData->pFormat, *plSize);
857 } else {
858 memcpy(pFormat, psnData->pFormat, psnData->lFormatLen);
859 *plSize = psnData->lFormatLen;
860 }
861
862 return 0;
863 }
864
isStreaming()865 bool AVIReadStream::isStreaming() {
866 return psnData->cache && fStreamingActive && parent->isStreaming();
867 }
868
isKeyframeOnly()869 bool AVIReadStream::isKeyframeOnly() {
870 return psnData->keyframe_only;
871 }
872
getVBRInfo(double & bitrate_mean,double & bitrate_stddev,double & maxdev)873 bool AVIReadStream::getVBRInfo(double& bitrate_mean, double& bitrate_stddev, double& maxdev) {
874 if (!psnData->is_VBR)
875 return false;
876
877 sint64 size_accum = 0;
878 double max_dev = 0;
879 double size_sq_sum = 0.0;
880
881 VDAVIReadIndexIterator it;
882 mpIndex->GetFirstSampleRange(it);
883
884 sint64 chunkPos;
885 uint32 offset;
886 uint32 byteSize;
887 double bytes = (double)mpIndex->GetByteCount();
888 double bytesPerChunk = bytes / (double)frames;
889 int i = 0;
890 while(mpIndex->GetNextSampleRange(it, chunkPos, offset, byteSize)) {
891 double mean_center = bytesPerChunk * (i+0.5);
892 double dev = fabs(mean_center - (double)(size_accum + (byteSize>>1)));
893
894 if (dev > max_dev)
895 max_dev = dev;
896
897 size_accum += byteSize;
898 size_sq_sum += (double)byteSize * byteSize;
899 ++i;
900 }
901
902 // I hate probability & sadistics.
903 //
904 // Var(X) = E(X2) - E(X)^2
905 // = S(X2)/n - (S(x)/n)^2
906 // = (n*S(X2) - S(X)^2)/n^2
907 //
908 // SD(x) = sqrt(n*S(X2) - S(X)^2) / n
909
910 double frames_per_second = (double)psnData->hdr.dwRate / (double)psnData->hdr.dwScale;
911 double sum1_bits = bytes * 8.0;
912 double sum2_bits = size_sq_sum * 64.0;
913
914 bitrate_mean = (sum1_bits / frames) * frames_per_second;
915 bitrate_stddev = sqrt(std::max<double>(0.0, frames * sum2_bits - sum1_bits * sum1_bits)) / frames * frames_per_second;
916 maxdev = max_dev * 8.0 / bitrate_mean;
917 return true;
918 }
919
TimeToPosition(VDTime timeInUs)920 VDPosition AVIReadStream::TimeToPosition(VDTime timeInUs) {
921 return VDRoundToInt64(timeInUs * (double)psnData->hdr.dwRate / (double)psnData->hdr.dwScale * (1.0 / 1000000.0));
922 }
923
PositionToTime(VDPosition sample)924 VDTime AVIReadStream::PositionToTime(VDPosition sample) {
925 return VDRoundToInt64(sample * (double)psnData->hdr.dwScale / (double)psnData->hdr.dwRate * 1000000.0);
926 }
927
928 ///////////////////////////////////////////////////////////////////////////
929
AVIReadHandler(const wchar_t * s)930 AVIReadHandler::AVIReadHandler(const wchar_t *s)
931 : mbFileIsDamaged(false)
932 , mTextInfoCodePage(0)
933 , mTextInfoCountryCode(0)
934 , mTextInfoLanguage(0)
935 , mTextInfoDialect(0)
936 {
937 mRefCount = 1;
938 streams=0;
939 fStreamsActive = 0;
940 fDisableFastIO = false;
941 streamBuffer = nullptr;
942 nRealTime = 0;
943 nActiveStreamers = 0;
944 fFakeIndex = false;
945 nFiles = 1;
946 pSegmentHint = nullptr;
947
948 _construct(s);
949 }
950
~AVIReadHandler()951 AVIReadHandler::~AVIReadHandler() {
952 _destruct();
953 }
954
_construct(const wchar_t * pszFile)955 void AVIReadHandler::_construct(const wchar_t *pszFile) {
956
957 try {
958 // create first link
959 vdautoptr<AVIFileDesc> pDesc(new_nothrow AVIFileDesc);
960
961 if (!pDesc)
962 throw MyMemoryError();
963
964 // open file
965 pDesc->mFile.open(pszFile, nsVDFile::kRead | nsVDFile::kDenyWrite | nsVDFile::kOpenExisting | nsVDFile::kSequential);
966 pDesc->mFileUnbuffered.openNT(pszFile, nsVDFile::kRead | nsVDFile::kDenyWrite | nsVDFile::kOpenExisting | nsVDFile::kUnbuffered);
967 pDesc->mFileSize = pDesc->mFile.size();
968
969 mpCurrentFile = pDesc;
970 mCurrentFile = -1;
971 mFiles.push_back(pDesc.release());
972
973 // recursively parse file
974
975 _parseFile(listStreams);
976
977 } catch(...) {
978 _destruct();
979 throw;
980 }
981 }
982
AppendFile(const wchar_t * pszFile)983 bool AVIReadHandler::AppendFile(const wchar_t *pszFile) {
984 List2<AVIStreamNode> newstreams;
985 AVIStreamNode *pasn_old, *pasn_new, *pasn_old_next=nullptr, *pasn_new_next=nullptr;
986
987 // open file
988
989 vdautoptr<AVIFileDesc> pDesc(new_nothrow AVIFileDesc);
990
991 if (!pDesc)
992 throw MyMemoryError();
993
994 pDesc->mFile.open(pszFile, nsVDFile::kRead | nsVDFile::kDenyWrite | nsVDFile::kOpenExisting | nsVDFile::kSequential);
995 pDesc->mFileUnbuffered.openNT(pszFile, nsVDFile::kRead | nsVDFile::kDenyWrite | nsVDFile::kOpenExisting | nsVDFile::kUnbuffered);
996 pDesc->mFileSize = pDesc->mFile.size();
997 mFiles.push_back(pDesc.release());
998
999 mpCurrentFile = mFiles.back();
1000 mCurrentFile = mFiles.size() - 1;
1001
1002 try {
1003 _parseFile(newstreams);
1004
1005 pasn_old = listStreams.AtHead();
1006 pasn_new = newstreams.AtHead();
1007
1008 while(!!(pasn_old_next = pasn_old->NextFromHead()) & !!(pasn_new_next = pasn_new->NextFromHead())) {
1009 const char *szStreamType = nullptr;
1010
1011 switch(pasn_old->hdr.fccType) {
1012 case kAVIStreamTypeAudio: szStreamType = "audio"; break;
1013 case kAVIStreamTypeVideo: szStreamType = "video"; break;
1014 case kAVIStreamTypeDV: szStreamType = "DV"; break;
1015 }
1016
1017 // If it's not an audio or video stream, why do we care?
1018
1019 if (szStreamType) {
1020 // allow ivas as a synonym for vids
1021
1022 uint32 fccOld = pasn_old->hdr.fccType;
1023 uint32 fccNew = pasn_new->hdr.fccType;
1024
1025 if (fccOld != fccNew)
1026 throw MyError("Cannot append segment \"%ls\": The segment has a different set of streams.", pszFile);
1027
1028 // A/B ?= C/D ==> AD ?= BC
1029
1030 if ((sint64)pasn_old->hdr.dwScale * pasn_new->hdr.dwRate != (sint64)pasn_new->hdr.dwScale * pasn_old->hdr.dwRate)
1031 throw MyError("Cannot append segment \"%ls\": The %s streams do not share a common sampling rate.\n"
1032 "\n"
1033 "First stream: %08x / %08x = %.5f samples/sec\n"
1034 "Second stream: %08x / %08x = %.5f samples/sec"
1035 ,pszFile
1036 ,szStreamType
1037 ,pasn_old->hdr.dwRate
1038 ,pasn_old->hdr.dwScale
1039 ,(double)pasn_old->hdr.dwRate / pasn_old->hdr.dwScale
1040 ,pasn_new->hdr.dwRate
1041 ,pasn_new->hdr.dwScale
1042 ,(double)pasn_new->hdr.dwRate / pasn_new->hdr.dwScale
1043 );
1044
1045 if (pasn_old->hdr.dwSampleSize != pasn_new->hdr.dwSampleSize)
1046 throw MyError("Cannot append segment \"%ls\": The %s streams have different sample sizes (%d vs %d).", pszFile, szStreamType, pasn_old->hdr.dwSampleSize, pasn_new->hdr.dwSampleSize);
1047
1048 // I hate PCMWAVEFORMAT.
1049
1050 uint32 oldFormatLen = pasn_old->lFormatLen;
1051 uint32 newFormatLen = pasn_new->lFormatLen;
1052 uint32 basicFormatLen = 0;
1053
1054 if (pasn_old->hdr.fccType == kAVIStreamTypeAudio) {
1055 const WAVEFORMATEX& wfex1 = *(const WAVEFORMATEX *)pasn_old->pFormat;
1056 const WAVEFORMATEX& wfex2 = *(const WAVEFORMATEX *)pasn_new->pFormat;
1057
1058 if (wfex1.wFormatTag != wfex2.wFormatTag)
1059 throw MyError("Cannot append segment \"%ls\": The audio streams use different compression formats.", pszFile);
1060
1061 if (wfex1.nSamplesPerSec != wfex2.nSamplesPerSec)
1062 throw MyError("Cannot append segment \"%ls\": The audio streams use different sampling rates (%u vs. %u)", pszFile, wfex1.nSamplesPerSec, wfex2.nSamplesPerSec);
1063
1064 if (wfex1.nAvgBytesPerSec != wfex2.nAvgBytesPerSec)
1065 throw MyError("Cannot append segment \"%ls\": The audio streams use different data rates (%u bytes/sec vs. %u bytes/sec)", pszFile, wfex1.nAvgBytesPerSec, wfex2.nAvgBytesPerSec);
1066
1067 if (wfex1.wFormatTag == WAVE_FORMAT_PCM
1068 && wfex2.wFormatTag == WAVE_FORMAT_PCM)
1069 {
1070 oldFormatLen = sizeof(PCMWAVEFORMAT);
1071 newFormatLen = sizeof(PCMWAVEFORMAT);
1072 basicFormatLen = sizeof(PCMWAVEFORMAT);
1073 } else {
1074 basicFormatLen = sizeof(WAVEFORMATEX);
1075 }
1076
1077 } else if (pasn_new->hdr.fccType == kAVIStreamTypeVideo) {
1078 basicFormatLen = sizeof(BITMAPINFOHEADER);
1079
1080 const BITMAPINFOHEADER& hdr1 = *(const BITMAPINFOHEADER *)pasn_old->pFormat;
1081 const BITMAPINFOHEADER& hdr2 = *(const BITMAPINFOHEADER *)pasn_new->pFormat;
1082
1083 if (hdr1.biWidth != hdr2.biWidth || hdr1.biHeight != hdr2.biHeight)
1084 throw MyError("Cannot append segment \"%ls\": The video streams are of different sizes (%dx%d vs. %dx%d)", pszFile, hdr1.biWidth, hdr1.biHeight, hdr2.biWidth, hdr2.biHeight);
1085
1086 if (hdr1.biCompression != hdr2.biCompression)
1087 throw MyError("Cannot append segment \"%ls\": The video streams use different compression schemes.", pszFile);
1088 }
1089
1090 uint32 minFormatLen = std::min<uint32>(oldFormatLen, newFormatLen);
1091 uint32 i = 0;
1092
1093 for(; i<minFormatLen; ++i) {
1094 if (((const char *)pasn_old->pFormat)[i] != ((const char *)pasn_new->pFormat)[i])
1095 break;
1096 }
1097
1098 bool fOk = (i == oldFormatLen && i == newFormatLen);
1099
1100 if (!fOk)
1101 throw MyError("Cannot append segment \"%ls\": The %s streams have incompatible data formats.\n\n(Mismatch detected in opaque codec data at byte %u of the format data.)", pszFile, szStreamType, i);
1102 }
1103
1104 pasn_old = pasn_old_next;
1105 pasn_new = pasn_new_next;
1106 }
1107
1108 if (pasn_old_next || pasn_new_next)
1109 throw MyError("Cannot append segment \"%ls\": The segment has a different number of streams.", pszFile);
1110
1111 } catch(const MyError&) {
1112 while(pasn_new = newstreams.RemoveHead())
1113 delete pasn_new;
1114
1115 mpCurrentFile = nullptr;
1116 mCurrentFile = -1;
1117 delete mFiles.back();
1118 mFiles.pop_back();
1119 throw;
1120 }
1121
1122 // Accept segment; begin merging process.
1123
1124 pasn_old = listStreams.AtHead();
1125
1126 while(pasn_old_next = pasn_old->NextFromHead()) {
1127 pasn_new = newstreams.RemoveHead();
1128
1129 // Fix up header.
1130
1131 pasn_old->hdr.dwLength += pasn_new->hdr.dwLength;
1132
1133 if (pasn_new->hdr.dwSuggestedBufferSize > pasn_old->hdr.dwSuggestedBufferSize)
1134 pasn_old->hdr.dwSuggestedBufferSize = pasn_new->hdr.dwSuggestedBufferSize;
1135
1136 pasn_old->bytes += pasn_new->bytes;
1137 pasn_old->frames += pasn_new->frames;
1138 pasn_old->length += pasn_new->length;
1139
1140 // Merge indices.
1141 pasn_old->mIndex.Append(pasn_new->mIndex, (sint64)nFiles << 48);
1142
1143 // Notify all handlers.
1144
1145 AVIReadStream *pStream, *pStreamNext;
1146
1147 pStream = pasn_old->listHandlers.AtHead();
1148 while(pStreamNext = pStream->NextFromHead()) {
1149 pStream->Reinit();
1150
1151 pStream = pStreamNext;
1152 }
1153
1154 // Next!
1155
1156 pasn_old = pasn_old_next;
1157 delete pasn_new;
1158 }
1159
1160 ++nFiles;
1161 return true;
1162 }
1163
_parseFile(List2<AVIStreamNode> & streamlist)1164 void AVIReadHandler::_parseFile(List2<AVIStreamNode>& streamlist) {
1165 uint32 fccType;
1166 uint32 dwLength;
1167 bool index_found = false;
1168 bool fAcceptIndexOnly = true;
1169 bool hyperindexed = false;
1170 bool bScanRequired = false;
1171 AVIStreamNode *pasn, *pasn_next;
1172 VDAVIMainHeader avihdr;
1173 bool bMainAVIHeaderFound = false;
1174
1175 sint64 i64ChunkMoviPos = 0;
1176 uint32 dwChunkMoviLength = 0;
1177
1178 if (!ReadChunkHeader(fccType, dwLength))
1179 throw MyError("Invalid AVI file: File is less than 8 bytes");
1180
1181 if (fccType != FOURCC_RIFF)
1182 throw MyError("Invalid AVI file: Not a RIFF file");
1183
1184 // If the RIFF header is <4 bytes, assume it was an improperly closed
1185 // file.
1186
1187 mpCurrentFile->mFile.read(&fccType, 4);
1188
1189 if (fccType != ' IVA')
1190 throw MyError("Invalid AVI file: RIFF type is not 'AVI'");
1191
1192 // Aggressive mode recovery does extensive validation and searching to attempt to
1193 // recover an AVI. It is significantly slower, however. We place the flag here
1194 // so we can set it if something dreadfully wrong is with the AVI.
1195
1196 bool bAggressive = false;
1197
1198 // begin parsing chunks
1199 mbPaletteChangesDetected = false;
1200
1201 sint64 infoChunkEnd = 0;
1202 sint64 fileSize = mpCurrentFile->mFile.size();
1203
1204 while(ReadChunkHeader(fccType, dwLength)) {
1205
1206 // _RPT4(0,"%08I64x %08I64x Chunk '%-4s', length %08lx\n", _posFile()+dwLengthLeft, _posFile(), &fccType, dwLength);
1207
1208 if (!isValidFOURCC(fccType))
1209 break;
1210
1211 if (fccType == FOURCC_LIST) {
1212 mpCurrentFile->mFile.read(&fccType, 4);
1213
1214 // If we find a LIST/movi chunk with zero size, jump straight to reindexing
1215 // (unclosed AVIFile output).
1216
1217 if (!dwLength && fccType == 'ivom') {
1218 i64ChunkMoviPos = mpCurrentFile->mFile.tell();
1219 dwChunkMoviLength = 0xFFFFFFF0;
1220 goto terminate_scan;
1221 }
1222
1223 // Some idiot Premiere plugin is writing AVI files with an invalid
1224 // size field in the LIST/hdrl chunk.
1225
1226 if (dwLength<4 && fccType != VDMAKEFOURCC('h', 'd', 'r', 'l'))
1227 throw MyError("Invalid AVI file: LIST chunk <4 bytes");
1228
1229 if (dwLength < 4)
1230 dwLength = 0;
1231 else
1232 dwLength -= 4;
1233
1234 switch(fccType) {
1235 case 'ivom':
1236
1237 if (dwLength < 8) {
1238 i64ChunkMoviPos = mpCurrentFile->mFile.tell();
1239 dwChunkMoviLength = 0xFFFFFFF0;
1240 dwLength = 0;
1241 } else {
1242 i64ChunkMoviPos = mpCurrentFile->mFile.tell();
1243 dwChunkMoviLength = dwLength;
1244 }
1245
1246 if (fAcceptIndexOnly)
1247 goto terminate_scan;
1248
1249 break;
1250 case ' cer': // silently enter grouping blocks
1251 case VDMAKEFOURCC('h', 'd', 'r', 'l'): // silently enter header blocks
1252 dwLength = 0;
1253 break;
1254 case VDMAKEFOURCC('s', 't', 'r', 'l'):
1255 if (!_parseStreamHeader(streamlist, dwLength, bScanRequired))
1256 fAcceptIndexOnly = false;
1257 else {
1258 int s = streams;
1259 //VDLogAppMessage(kVDLogInfo, kVDST_AVIReadHandler, kVDM_OpenDMLIndexDetected, 1, &s);
1260 hyperindexed = true;
1261 }
1262
1263 ++streams;
1264 dwLength = 0;
1265 break;
1266 case 'OFNI':
1267 infoChunkEnd = mpCurrentFile->mFile.tell() + dwLength;
1268 dwLength = 0;
1269 break;
1270 }
1271 } else {
1272 // Check if the chunk extends outside of the boundaries, and bail if so. We don't do
1273 // this for LIST chunks because those are often done incorrectly and we don't actually
1274 // maintain a nesting stack anyway.
1275 if (mpCurrentFile->mFile.tell() + dwLength > fileSize)
1276 break;
1277
1278 switch(fccType) {
1279 case VDMAKEFOURCC('i', 'd', 'x', '1'):
1280 if (!hyperindexed) {
1281 index_found = _parseIndexBlock(streamlist, dwLength/16, i64ChunkMoviPos);
1282 dwLength &= 15;
1283 }
1284 break;
1285
1286 case VDMAKEFOURCC('J', 'U', 'N', 'K'):
1287 break;
1288
1289 case 'mges': // VirtualDub segment hint block
1290 delete pSegmentHint;
1291 if (!(pSegmentHint = new char[dwLength]))
1292 throw MyMemoryError();
1293
1294 mpCurrentFile->mFile.read(pSegmentHint, dwLength);
1295
1296 if (dwLength&1)
1297 mpCurrentFile->mFile.skip(1);
1298
1299 dwLength = 0;
1300 break;
1301
1302 case 'lrdh':
1303 if (!bMainAVIHeaderFound) {
1304 uint32 tc = std::min<uint32>(dwLength, sizeof avihdr);
1305 memset(&avihdr, 0, sizeof avihdr);
1306 mpCurrentFile->mFile.read(&avihdr, tc);
1307 dwLength -= tc;
1308 bMainAVIHeaderFound = true;
1309 }
1310 break;
1311
1312 case 'TESC': // CSET (character set)
1313 if (dwLength >= 8) {
1314 struct {
1315 uint16 wCodePage;
1316 uint16 wCountryCode;
1317 uint16 wLanguageCode;
1318 uint16 wDialect;
1319 } csetData;
1320
1321 mpCurrentFile->mFile.read(&csetData, 8);
1322 dwLength -= 8;
1323
1324 mTextInfoCodePage = csetData.wCodePage;
1325 mTextInfoCountryCode = csetData.wCountryCode;
1326 mTextInfoLanguage = csetData.wLanguageCode;
1327 mTextInfoDialect = csetData.wDialect;
1328 }
1329 break;
1330
1331 default:
1332 if (infoChunkEnd && (mpCurrentFile->mFile.tell() < infoChunkEnd)) {
1333 switch(fccType) {
1334 case 'LRAI':
1335 case 'TRAI':
1336 case 'SMCI':
1337 case 'TMCI':
1338 case 'POCI':
1339 case 'DRCI':
1340 case 'PRCI':
1341 case 'MIDI':
1342 case 'IPDI':
1343 case 'GNEI':
1344 case 'RNGI':
1345 case 'YEKI':
1346 case 'TGLI':
1347 case 'DEMI':
1348 case 'MANI':
1349 case 'TLPI':
1350 case 'DRPI':
1351 case 'JBSI':
1352 case 'TFSI':
1353 case 'PHSI':
1354 case 'CRSI':
1355 case 'FRSI':
1356 case 'HCTI':
1357 uint32 tc = dwLength;
1358 if (tc > 4096) {
1359 char fccstr[5]={0};
1360 *(uint32 *)fccstr = fccType;
1361 const char *fccptr = fccstr;
1362 sint64 pos = mpCurrentFile->mFile.tell();
1363 unsigned len = dwLength;
1364
1365 //VDLogAppMessage(kVDLogWarning, kVDST_AVIReadHandler, kVDM_InfoTruncated, 3, &fccptr, &pos, &len);
1366 tc = 4096;
1367 }
1368
1369 vdblock<char> data((tc + 1) & ~1);
1370
1371 dwLength -= tc;
1372 mpCurrentFile->mFile.read(data.data(), data.size());
1373
1374 int len = tc;
1375
1376 while(len > 0 && !data[len-1])
1377 --len;
1378
1379 if (len > 0)
1380 mTextInfo.push_back(tTextInfo::value_type(fccType, VDStringA(data.data(), len)));
1381
1382 break;
1383 }
1384 }
1385 break;
1386 }
1387 }
1388
1389 if (dwLength) {
1390 if (!mpCurrentFile->mFile.skipNT(dwLength + (dwLength&1)))
1391 break;
1392 }
1393 }
1394
1395 if (i64ChunkMoviPos == 0)
1396 throw MyError("Invalid AVI file: The main 'movi' block is missing.");
1397
1398 terminate_scan:
1399
1400 if (!hyperindexed && !index_found)
1401 bScanRequired = true;
1402
1403 if (bScanRequired) {
1404 //VDLogAppMessage(kVDLogWarning, kVDST_AVIReadHandler, kVDM_IndexMissing);
1405
1406 // It's possible that we were in the middle of reading an index when an error
1407 // occurred, so we need to clear all of the indices for all streams.
1408
1409 pasn = streamlist.AtHead();
1410
1411 while(pasn_next = pasn->NextFromHead()) {
1412 pasn->mIndex.Clear();
1413 pasn = pasn_next;
1414 }
1415
1416 // obtain length of file and limit scanning if so
1417 uint32 dwLengthLeft = dwChunkMoviLength;
1418
1419 long short_length = (long)((dwChunkMoviLength + 1023i64) >> 10);
1420 long long_length = (long)((fileSize - i64ChunkMoviPos + 1023i64) >> 10);
1421
1422 // fix short length if the movi length happens to be beyond the end of the file
1423 if (short_length > long_length)
1424 short_length = long_length;
1425
1426 long length = (hyperindexed || bAggressive) ? long_length : short_length;
1427
1428 fFakeIndex = true;
1429
1430 mpCurrentFile->mFile.seek(i64ChunkMoviPos);
1431
1432 // For standard AVI files, stop as soon as the movi chunk is exhausted or
1433 // the end of the AVI file is hit. For OpenDML files, continue as long as
1434 // valid chunks are found.
1435
1436 bool bStopWhenLengthExhausted = !hyperindexed && !bAggressive;
1437
1438 try {
1439 for(;;) {
1440 // Exit if we are out of movi chunk -- except for OpenDML files.
1441
1442 if (!bStopWhenLengthExhausted && dwLengthLeft < 8)
1443 break;
1444
1445 // Validate the FOURCC itself but avoid validating the size, since it
1446 // may be too large for the last LIST/movi.
1447 if (!ReadChunkHeader(fccType, dwLength))
1448 break;
1449
1450 bool bValid = isValidFOURCC(fccType) && ((mpCurrentFile->mFile.tell() + dwLength) <= fileSize);
1451
1452 // In aggressive mode, verify that a valid FOURCC follows this chunk.
1453
1454 if (bValid && bAggressive) {
1455 sint64 current_pos = mpCurrentFile->mFile.tell();
1456 sint64 rounded_length = (dwLength+1i64) & ~1i64;
1457
1458 if (current_pos + dwLength > fileSize)
1459 bValid = false;
1460 else if (current_pos + rounded_length <= fileSize-8) {
1461 uint32 fccNext;
1462 uint32 dwLengthNext;
1463
1464 mpCurrentFile->mFile.seek(current_pos + rounded_length);
1465 if (!ReadChunkHeader(fccNext, dwLengthNext))
1466 break;
1467
1468 bValid &= isValidFOURCC(fccNext) && ((mpCurrentFile->mFile.tell() + dwLengthNext) <= fileSize);
1469
1470 mpCurrentFile->mFile.seek(current_pos);
1471 }
1472 }
1473
1474 if (!bValid) {
1475 // Notify the user that recovering this file requires stronger measures.
1476
1477 if (!bAggressive) {
1478 sint64 bad_pos = mpCurrentFile->mFile.tell();
1479 //VDLogAppMessage(kVDLogWarning, kVDST_AVIReadHandler, kVDM_InvalidChunkDetected, 1, &bad_pos);
1480
1481 bAggressive = true;
1482 bStopWhenLengthExhausted = false;
1483 }
1484
1485 // Backup by up to seven bytes and continue.
1486 //
1487 // We are looking for a contiguous sequence of four valid bytes starting at position 1.
1488
1489 union {
1490 uint32 i[2];
1491 unsigned char b[8];
1492 } conv = { { VDToLE32(fccType), VDToLE32(dwLength) } };
1493
1494 uint32 validBits = 0xF00;
1495 for(int i=1; i<8; ++i) {
1496 if (isValidFOURCCChar(conv.b[i]))
1497 validBits |= (1 << i);
1498 }
1499
1500 validBits &= (validBits >> 1);
1501 validBits &= (validBits >> 2);
1502
1503 int invalidBytes = VDFindLowestSetBit(validBits);
1504
1505 mpCurrentFile->mFile.skip(invalidBytes-8);
1506 continue;
1507 }
1508
1509 dwLengthLeft -= 8+(dwLength + (dwLength&1));
1510
1511 // Skip over the chunk. Don't skip over RIFF chunks of type AVIX, or
1512 // LIST chunks of type 'movi'.
1513
1514 if (dwLength) {
1515 if (fccType == 'FFIR' || fccType == 'TSIL') {
1516 uint32 fccType2;
1517
1518 if (4 != mpCurrentFile->mFile.readData(&fccType2, 4))
1519 break;
1520
1521 if (fccType2 != 'XIVA' && fccType2 != 'ivom' && fccType2 != ' cer') {
1522 if (!mpCurrentFile->mFile.skipNT(dwLength + (dwLength&1) - 4))
1523 break;
1524 }
1525 } else {
1526 if (!mpCurrentFile->mFile.skipNT(dwLength + (dwLength&1)))
1527 break;
1528 }
1529 }
1530
1531 if (mpCurrentFile->mFile.tell() > fileSize)
1532 break;
1533
1534
1535 // TODO: This isn't necessarily correct for OpenDML, for which the MS parser accepts
1536 // non-sequential IDs (according to alexnoe)
1537 if (isxdigit(fccType&0xff) && isxdigit((fccType>>8)&0xff)) {
1538 if (is_palette_change(fccType)) {
1539 mbPaletteChangesDetected = true;
1540 } else {
1541 int stream = VDAVIGetStreamFromFOURCC(fccType);
1542
1543 if (stream >=0 && stream < streams) {
1544
1545 pasn = streamlist.AtHead();
1546
1547 while((pasn_next = pasn->NextFromHead()) && stream--)
1548 pasn = pasn_next;
1549
1550 if (pasn && pasn_next) {
1551
1552 // Set the keyframe flag for the first sample in the stream, or
1553 // if this is known to be a keyframe-only stream. Do not set the
1554 // keyframe flag if the frame has zero bytes (drop frame).
1555 uint32 sizeAndKey = dwLength;
1556
1557 if ((!pasn->bytes || pasn->keyframe_only) && dwLength>0)
1558 sizeAndKey |= 0x80000000;
1559
1560 pasn->mIndex.AddChunk(mpCurrentFile->mFile.tell()-(dwLength + (dwLength&1)), sizeAndKey);
1561 pasn->bytes += dwLength;
1562 }
1563 }
1564 }
1565 }
1566 }
1567 } catch(const MyUserAbortError&) {
1568 sint64 pos = mpCurrentFile->mFile.tell();
1569 //VDLogAppMessage(kVDLogInfo, kVDST_AVIReadHandler, kVDM_IndexingAborted, 1, &pos);
1570 throw;
1571 }
1572 }
1573
1574 if (mbPaletteChangesDetected) {
1575 //VDLogAppMessage(kVDLogWarning, kVDST_AVIReadHandler, kVDM_PaletteChanges);
1576 }
1577
1578 mbFileIsDamaged |= bAggressive;
1579
1580 // glue together indices
1581
1582 pasn = streamlist.AtHead();
1583
1584 int nStream = 0;
1585 while(pasn_next = pasn->NextFromHead()) {
1586 pasn->mIndex.Finalize();
1587 pasn->is_VBR = pasn->mIndex.IsVBR();
1588
1589 pasn->frames = pasn->mIndex.GetChunkCount();
1590
1591 // Attempt to fix invalid dwRate/dwScale fractions (can result from unclosed
1592 // AVI files being written by DirectShow).
1593
1594 if (pasn->hdr.dwRate==0 || pasn->hdr.dwScale == 0) {
1595 // If we're dealing with a video stream, try the frame rate in the AVI header.
1596 // If we're dealing with an audio stream, try the frame rate in the audio
1597 // format.
1598 // Otherwise, just use... uh, 15.
1599
1600 if (pasn->hdr.fccType == kAVIStreamTypeVideo) {
1601 if (bMainAVIHeaderFound) {
1602 pasn->hdr.dwRate = avihdr.dwMicroSecPerFrame; // This can be zero, in which case the default '15' will kick in below.
1603 pasn->hdr.dwScale = 1000000;
1604 }
1605 } else if (pasn->hdr.fccType == kAVIStreamTypeAudio) {
1606 const WAVEFORMATEX *pwfex = (const WAVEFORMATEX *)pasn->pFormat;
1607
1608 pasn->hdr.dwRate = pwfex->nAvgBytesPerSec;
1609 pasn->hdr.dwScale = pwfex->nBlockAlign;
1610 }
1611
1612 if (pasn->hdr.dwRate==0 || pasn->hdr.dwScale == 0) {
1613 pasn->hdr.dwRate = 15;
1614 pasn->hdr.dwScale = 1;
1615 }
1616
1617 const int badstream = nStream;
1618 const double newrate = pasn->hdr.dwRate / (double)pasn->hdr.dwScale;
1619 //VDLogAppMessage(kVDLogWarning, kVDST_AVIReadHandler, kVDM_FixingBadSampleRate, 2, &badstream, &newrate);
1620 }
1621
1622 // Check and warn on non-zero dwStart values.
1623 if (pasn->hdr.dwStart) {
1624 const char *badstreamtype = "unknown";
1625 switch(pasn->hdr.fccType) {
1626 case kAVIStreamTypeAudio: badstreamtype = "audio"; break;
1627 case kAVIStreamTypeVideo: badstreamtype = "video"; break;
1628 case kAVIStreamTypeDV: badstreamtype = "DV"; break;
1629 }
1630
1631 unsigned badstream = nStream;
1632 uint32 offsetInSamples = pasn->hdr.dwStart;
1633 sint64 offsetInTime = VDMulDiv64(pasn->hdr.dwStart, pasn->hdr.dwScale * VD64(1000), pasn->hdr.dwRate);
1634
1635 //VDLogAppMessage(
1636 // kVDLogInfo,
1637 // kVDST_AVIReadHandler, kVDM_NonZeroStart, 4, &badstream, &badstreamtype, &offsetInSamples, &offsetInTime);
1638 }
1639
1640 pasn->length = pasn->mIndex.GetSampleCount();
1641
1642 pasn = pasn_next;
1643 ++nStream;
1644 }
1645
1646 // throw MyError("Parse complete. Aborting.");
1647 }
1648
_parseStreamHeader(List2<AVIStreamNode> & streamlist,uint32 dwLengthLeft,bool & bIndexDamaged)1649 bool AVIReadHandler::_parseStreamHeader(List2<AVIStreamNode>& streamlist, uint32 dwLengthLeft, bool& bIndexDamaged) {
1650 vdautoptr<AVIStreamNode> pasn(new_nothrow AVIStreamNode());
1651 uint32 fccType;
1652 uint32 dwLength;
1653 bool hyperindexed = false;
1654
1655 if (!pasn)
1656 throw MyMemoryError();
1657
1658 sint64 extendedIndexPos = -1;
1659
1660 while(dwLengthLeft >= 8 && ReadChunkHeader(fccType, dwLength)) {
1661
1662 dwLengthLeft -= 8;
1663
1664 if (dwLength > dwLengthLeft)
1665 throw MyError("Invalid AVI file: chunk size extends outside of parent");
1666
1667 dwLengthLeft -= (dwLength + (dwLength&1));
1668
1669 switch(fccType) {
1670
1671 case VDMAKEFOURCC('s', 't', 'r', 'h'):
1672 memset(&pasn->hdr, 0, sizeof pasn->hdr);
1673
1674 if (dwLength < sizeof pasn->hdr) {
1675 mpCurrentFile->mFile.read(&pasn->hdr, dwLength);
1676 if (dwLength & 1)
1677 mpCurrentFile->mFile.skip(1);
1678 } else {
1679 mpCurrentFile->mFile.read(&pasn->hdr, sizeof pasn->hdr);
1680 mpCurrentFile->mFile.skip(dwLength+(dwLength&1) - sizeof pasn->hdr);
1681 }
1682 dwLength = 0;
1683
1684 pasn->keyframe_only = false;
1685
1686 // Clear sample size for video streams!
1687 if (pasn->hdr.fccType == kAVIStreamTypeVideo)
1688 pasn->hdr.dwSampleSize=0;
1689
1690 break;
1691
1692 case VDMAKEFOURCC('s', 't', 'r', 'f'):
1693 if (!(pasn->pFormat = new char[pasn->lFormatLen = dwLength]))
1694 throw MyMemoryError();
1695
1696 mpCurrentFile->mFile.read(pasn->pFormat, dwLength);
1697
1698 if (pasn->hdr.fccType == kAVIStreamTypeDV) {
1699 pasn->keyframe_only = true;
1700 } else if (pasn->hdr.fccType == kAVIStreamTypeVideo) {
1701 switch(((BITMAPINFOHEADER *)pasn->pFormat)->biCompression) {
1702 case 0:
1703 case ' WAR':
1704 case ' BID':
1705 case '1bmd':
1706 case 'gpjm':
1707 case 'GPJM':
1708 case 'YUYV':
1709 case '2YUY':
1710 case 'YVYU':
1711 case 'UYVY':
1712 case '21VY':
1713 case '024I':
1714 case 'P14Y':
1715 case 'vuyc':
1716 case 'UYFH':
1717 case '02tb':
1718 case 'dsvd':
1719 pasn->keyframe_only = true;
1720 break;
1721 }
1722 }
1723
1724 if (dwLength & 1)
1725 mpCurrentFile->mFile.skip(1);
1726 dwLength = 0;
1727 break;
1728
1729 case 'xdni': // OpenDML extended index
1730 extendedIndexPos = mpCurrentFile->mFile.tell();
1731 break;
1732
1733 case VDMAKEFOURCC('J', 'U', 'N', 'K'): // JUNK
1734 break;
1735 }
1736
1737 if (dwLength) {
1738 if (!mpCurrentFile->mFile.skipNT(dwLength + (dwLength&1)))
1739 break;
1740 }
1741 }
1742
1743 if (dwLengthLeft)
1744 mpCurrentFile->mFile.skipNT(dwLengthLeft);
1745
1746 uint32 sampsize = pasn->hdr.dwSampleSize;
1747 if (pasn->hdr.fccType == kAVIStreamTypeAudio) {
1748 sampsize = ((const WAVEFORMATEX *)pasn->pFormat)->nBlockAlign;
1749
1750 if (!sampsize)
1751 sampsize = 1;
1752 }
1753
1754 pasn->mIndex.Init(sampsize);
1755
1756 if (extendedIndexPos >= 0) {
1757 try {
1758 _parseExtendedIndexBlock(streamlist, pasn, extendedIndexPos, dwLength);
1759 } catch(const MyError&) {
1760 bIndexDamaged = true;
1761 }
1762
1763 hyperindexed = true;
1764 }
1765
1766 streamlist.AddTail(pasn.release());
1767
1768 return hyperindexed;
1769 }
1770
_parseIndexBlock(List2<AVIStreamNode> & streamlist,int count,sint64 movi_offset)1771 bool AVIReadHandler::_parseIndexBlock(List2<AVIStreamNode>& streamlist, int count, sint64 movi_offset) {
1772 enum { kIndicesPerLoop = 4096};
1773 AVIIndexEntry avie[kIndicesPerLoop]; // Note: 64K
1774 AVIStreamNode *pasn, *pasn_next;
1775 bool absolute_addr = true;
1776 bool first_chunk = true;
1777
1778 // Some AVI files have relative addresses in their AVI index chunks, and some
1779 // absolute. They're supposed to be relative to the 'movi' chunk; all versions
1780 // of VirtualDub using fast write routines prior to build 4936 generate absolute
1781 // addresses (oops). AVIFile and ActiveMovie are both ambivalent. I guess we'd
1782 // better be as well.
1783
1784 while(count > 0) {
1785 int tc = count;
1786 int i;
1787
1788 if (tc > kIndicesPerLoop)
1789 tc = kIndicesPerLoop;
1790 count -= tc;
1791
1792 if (tc*sizeof(AVIIndexEntry) != (size_t)mpCurrentFile->mFile.readData(avie, tc*sizeof(AVIIndexEntry))) {
1793 pasn = streamlist.AtHead();
1794
1795 while(pasn_next = pasn->NextFromHead()) {
1796 pasn->mIndex.Clear();
1797 pasn->bytes = 0;
1798
1799 pasn = pasn_next;
1800 }
1801 return false;
1802 }
1803
1804 if (first_chunk) {
1805 first_chunk = false;
1806
1807 // If the chunk offset is prior to the 'movi' chunk, then we know that we have to
1808 // use relative addressing.
1809 uint32 chunk_offset = avie[0].dwChunkOffset;
1810 if (chunk_offset < movi_offset)
1811 absolute_addr = false;
1812 else {
1813 // Okay, both relative and absolute are potentially good. We try the lower one
1814 // (absolute) and see if the chunk at that location matches in FOURCC (we do
1815 // NOT check the length as some interleavers play tricks with that). If not,
1816 // then we use relative positioning, which is what should be used anyway.
1817
1818 sint64 savepos = mpCurrentFile->mFile.tell();
1819
1820 mpCurrentFile->mFile.seek(chunk_offset);
1821
1822 uint32 fcc;
1823 mpCurrentFile->mFile.read(&fcc, sizeof fcc);
1824
1825 if (fcc != avie[0].ckid)
1826 absolute_addr = false;
1827
1828 mpCurrentFile->mFile.seek(savepos);
1829 }
1830 }
1831
1832 for(i=0; i<tc; i++) {
1833 const AVIIndexEntry& idxent = avie[i];
1834 int stream = VDAVIGetStreamFromFOURCC(idxent.ckid);
1835
1836 if (is_palette_change(idxent.ckid)) {
1837 mbPaletteChangesDetected = true;
1838 continue;
1839 }
1840
1841 pasn = streamlist.AtHead();
1842
1843 while((pasn_next = (AVIStreamNode *)pasn->NextFromHead()) && stream--)
1844 pasn = pasn_next;
1845
1846 if (pasn && pasn_next) {
1847 // It's fairly important that we not add entries with zero bytes as
1848 // key frames, as these have been seen in the wild. AVIFile silently
1849 // kills the key frame flag for these.
1850 sint64 bytePos = (sint64)idxent.dwChunkOffset + 8;
1851 if (!absolute_addr)
1852 bytePos += movi_offset - 4;
1853
1854 uint32 sizeAndKey = idxent.dwChunkLength;
1855 if ((idxent.dwFlags & AVIIndexEntry::kFlagKeyFrame) && idxent.dwChunkLength > 0)
1856 sizeAndKey |= 0x80000000;
1857
1858 pasn->mIndex.AddChunk(bytePos, sizeAndKey);
1859
1860 pasn->bytes += idxent.dwChunkLength;
1861 }
1862 }
1863 }
1864
1865 return true;
1866
1867 }
1868
_parseExtendedIndexBlock(List2<AVIStreamNode> & streamlist,AVIStreamNode * pasn,sint64 fpos,uint32 dwLength)1869 void AVIReadHandler::_parseExtendedIndexBlock(List2<AVIStreamNode>& streamlist, AVIStreamNode *pasn, sint64 fpos, uint32 dwLength) {
1870 #pragma warning(push)
1871 #pragma warning(disable: 4815) // warning C4815: '$S1' : zero-sized array in stack object will have no elements (unless the object is an aggregate that has been aggregate initialized)
1872 union {
1873 AVISUPERINDEX idxsuper;
1874 AVISTDINDEX idxstd;
1875 };
1876 #pragma warning(pop)
1877
1878 int entries, tp;
1879 int i;
1880 sint64 i64FPSave = mpCurrentFile->mFile.tell();
1881
1882 if (fpos>=0)
1883 mpCurrentFile->mFile.seek(fpos);
1884
1885 try {
1886 mpCurrentFile->mFile.read((char *)&idxsuper + 8, sizeof(AVISUPERINDEX) - 8);
1887
1888 switch(idxsuper.bIndexType) {
1889 case AVI_INDEX_OF_INDEXES:
1890 {
1891 // sanity check
1892
1893 if (idxsuper.wLongsPerEntry != 4)
1894 throw MyError("Invalid superindex block in stream");
1895
1896 // Compute buffer size -- the smaller of the number needed or 64K (16K dwords).
1897 uint32 maxcount = 65536 / sizeof(_avisuperindex_entry);
1898
1899 entries = idxsuper.nEntriesInUse;
1900 if (maxcount > entries)
1901 maxcount = entries;
1902
1903 vdblock<_avisuperindex_entry> buf(maxcount);
1904 _avisuperindex_entry *heap = buf.data();
1905
1906 while(entries > 0) {
1907 tp = maxcount;
1908 if (tp > maxcount)
1909 tp = maxcount;
1910
1911 mpCurrentFile->mFile.read(heap, tp * sizeof(heap[0]));
1912
1913 for(i=0; i<tp; i++)
1914 _parseExtendedIndexBlock(streamlist, pasn, heap[i].qwOffset+8, heap[i].dwSize-8);
1915
1916 entries -= tp;
1917 }
1918 }
1919 break;
1920
1921 case AVI_INDEX_OF_CHUNKS:
1922
1923 // if (idxstd.bIndexSubType != 0)
1924 // throw MyError("Frame indexes not supported");
1925
1926 entries = idxstd.nEntriesInUse;
1927
1928 // In theory, if bIndexSubType==AVI_INDEX_2FIELD it's supposed to have
1929 // wLongsPerEntry=3, and bIndexSubType==0 gives wLongsPerEntry=2.
1930 // Matrox's MPEG-2 stuff generates bIndexSubType=16 and wLongsPerEntry=6.
1931 // *sigh*
1932 //
1933 // For wLongsPerEntry==2 and ==3, dwOffset is at 0 and dwLength at 1;
1934 // for wLongsPerEntry==6, dwOffset is at 2 and all are keyframes.
1935
1936 if (entries) {
1937 if (idxstd.wLongsPerEntry!=2 && idxstd.wLongsPerEntry!=3 && idxstd.wLongsPerEntry!=6)
1938 throw MyError("Invalid OpenDML index block in stream (wLongsPerEntry=%d)", idxstd.wLongsPerEntry);
1939
1940 // Compute buffer size -- the smaller of the number needed or 64K (16K dwords).
1941 uint32 maxcount = 16384 / idxstd.wLongsPerEntry;
1942
1943 if (maxcount > entries)
1944 maxcount = entries;
1945
1946 vdblock<uint32> buf(maxcount * idxstd.wLongsPerEntry);
1947 uint32 *heap = buf.data();
1948
1949 while(entries > 0) {
1950 tp = maxcount;
1951 if (tp > entries)
1952 tp = entries;
1953
1954 mpCurrentFile->mFile.read(heap, tp*idxstd.wLongsPerEntry*sizeof(uint32));
1955
1956 if (idxstd.wLongsPerEntry == 6)
1957 for(i=0; i<tp; i++) {
1958 uint32 dwOffset = heap[i*idxstd.wLongsPerEntry + 0];
1959 uint32 dwSize = heap[i*idxstd.wLongsPerEntry + 2];
1960
1961 pasn->mIndex.AddChunk(idxstd.qwBaseOffset+dwOffset, dwSize | 0x80000000);
1962
1963 pasn->bytes += dwSize;
1964 }
1965 else
1966 for(i=0; i<tp; i++) {
1967 uint32 dwOffset = heap[i*idxstd.wLongsPerEntry + 0];
1968 uint32 dwSize = heap[i*idxstd.wLongsPerEntry + 1];
1969
1970 pasn->mIndex.AddChunk(idxstd.qwBaseOffset+dwOffset, dwSize ^ 0x80000000);
1971
1972 pasn->bytes += dwSize & 0x7FFFFFFF;
1973 }
1974
1975 entries -= tp;
1976 }
1977 }
1978
1979 break;
1980
1981 default:
1982 throw MyError("Unknown hyperindex type");
1983 }
1984
1985 mpCurrentFile->mFile.seek(i64FPSave);
1986 } catch(const MyError&) {
1987 mpCurrentFile->mFile.seekNT(i64FPSave);
1988 throw;
1989 }
1990 }
1991
_destruct()1992 void AVIReadHandler::_destruct() {
1993 AVIStreamNode *pasn;
1994
1995 while(pasn = listStreams.RemoveTail())
1996 delete pasn;
1997
1998 delete streamBuffer;
1999
2000 while(!mFiles.empty()) {
2001 AVIFileDesc *desc = mFiles.back();
2002 mFiles.pop_back();
2003 delete desc;
2004 }
2005
2006 delete pSegmentHint;
2007 }
2008
2009 ///////////////////////////////////////////////////////////////////////////
2010
Release()2011 void AVIReadHandler::Release() {
2012 if (!--mRefCount)
2013 delete this;
2014 }
2015
AddRef()2016 void AVIReadHandler::AddRef() {
2017 ++mRefCount;
2018 }
2019
GetStream(uint32 fccType,int lParam)2020 IAVIReadStream *AVIReadHandler::GetStream(uint32 fccType, int lParam) {
2021 AVIStreamNode *pasn, *pasn_next;
2022 int streamno = 0;
2023
2024 pasn = listStreams.AtHead();
2025
2026 while(pasn_next = pasn->NextFromHead()) {
2027 if (pasn->hdr.fccType == fccType && !lParam--)
2028 break;
2029
2030 pasn = pasn_next;
2031 ++streamno;
2032 }
2033
2034 if (pasn_next) {
2035 return new AVIReadStream(this, pasn, streamno);
2036 }
2037
2038 return nullptr;
2039 }
2040
EnableFastIO(bool f)2041 void AVIReadHandler::EnableFastIO(bool f) {
2042 fDisableFastIO = !f;
2043 }
2044
isOptimizedForRealtime()2045 bool AVIReadHandler::isOptimizedForRealtime() {
2046 return nRealTime!=0;
2047 }
2048
isStreaming()2049 bool AVIReadHandler::isStreaming() {
2050 return nActiveStreamers!=0 && !mbFileIsDamaged;
2051 }
2052
isIndexFabricated()2053 bool AVIReadHandler::isIndexFabricated() {
2054 return fFakeIndex;
2055 }
2056
getSegmentHint(const char ** ppszPath)2057 bool AVIReadHandler::getSegmentHint(const char **ppszPath) {
2058 if (!pSegmentHint) {
2059 if (ppszPath)
2060 *ppszPath = nullptr;
2061
2062 return false;
2063 }
2064
2065 if (ppszPath)
2066 *ppszPath = pSegmentHint+1;
2067
2068 return !!pSegmentHint[0];
2069 }
2070
GetTextInfo(tTextInfo & textInfo)2071 void AVIReadHandler::GetTextInfo(tTextInfo& textInfo) {
2072 textInfo = mTextInfo;
2073 }
2074
GetTextInfoEncoding(int & codePage,int & countryCode,int & language,int & dialect)2075 void AVIReadHandler::GetTextInfoEncoding(int& codePage, int& countryCode, int& language, int& dialect) {
2076 codePage = mTextInfoCodePage;
2077 countryCode = mTextInfoCountryCode;
2078 language = mTextInfoLanguage;
2079 dialect = mTextInfoDialect;
2080 }
2081
2082 ///////////////////////////////////////////////////////////////////////////
2083
EnableStreaming(int stream)2084 void AVIReadHandler::EnableStreaming(int stream) {
2085 if (!fStreamsActive) {
2086 if (!(streamBuffer = new char[STREAM_SIZE]))
2087 throw MyMemoryError();
2088
2089 i64StreamPosition = -1;
2090 sbPosition = sbSize = 0;
2091 }
2092
2093 fStreamsActive |= (1<<stream);
2094 ++nActiveStreamers;
2095 }
2096
DisableStreaming(int stream)2097 void AVIReadHandler::DisableStreaming(int stream) {
2098 fStreamsActive &= ~(1<<stream);
2099
2100 if (!fStreamsActive) {
2101 delete streamBuffer;
2102 streamBuffer = nullptr;
2103 }
2104 --nActiveStreamers;
2105 }
2106
AdjustRealTime(bool fInc)2107 void AVIReadHandler::AdjustRealTime(bool fInc) {
2108 if (fInc)
2109 ++nRealTime;
2110 else
2111 --nRealTime;
2112 }
2113
_StreamRead(long & bytes)2114 char *AVIReadHandler::_StreamRead(long& bytes) {
2115 if (mCurrentFile<0 || mCurrentFile != (int)(i64StreamPosition>>48))
2116 SelectFile((int)(i64StreamPosition>>48));
2117
2118 if (sbPosition >= sbSize) {
2119 if (!mpCurrentFile->mFileUnbuffered.isOpen() || nRealTime || (((i64StreamPosition&0x0000FFFFFFFFFFFFi64)+sbSize) & -STREAM_BLOCK_SIZE)+STREAM_SIZE > mpCurrentFile->mFileSize) {
2120 i64StreamPosition += sbSize;
2121 sbPosition = 0;
2122 mpCurrentFile->mFile.seek(i64StreamPosition & 0x0000FFFFFFFFFFFFi64);
2123
2124 sbSize = mpCurrentFile->mFile.readData(streamBuffer, STREAM_RT_SIZE);
2125
2126 if (sbSize < 0) {
2127 sbSize = 0;
2128 throw MyWin32Error("Failure streaming AVI file: %%s.",GetLastError());
2129 }
2130 } else {
2131 i64StreamPosition += sbSize;
2132 sbPosition = (int)i64StreamPosition & (STREAM_BLOCK_SIZE-1);
2133 i64StreamPosition &= -STREAM_BLOCK_SIZE;
2134 mpCurrentFile->mFileUnbuffered.seek(i64StreamPosition & 0x0000FFFFFFFFFFFFi64);
2135
2136 sbSize = mpCurrentFile->mFileUnbuffered.readData(streamBuffer, STREAM_SIZE);
2137 uint32 error = GetLastError();
2138
2139 if (sbSize < 0) {
2140 sbSize = 0;
2141 throw MyWin32Error("Failure streaming AVI file: %%s.", error);
2142 }
2143 }
2144 }
2145
2146 if (sbPosition >= sbSize)
2147 return nullptr;
2148
2149 if (bytes > sbSize - sbPosition)
2150 bytes = sbSize - sbPosition;
2151
2152 sbPosition += bytes;
2153
2154 return streamBuffer + sbPosition - bytes;
2155 }
2156
Stream(AVIStreamNode * pusher,sint64 pos)2157 bool AVIReadHandler::Stream(AVIStreamNode *pusher, sint64 pos) {
2158
2159 // Do not stream aggressively recovered files.
2160
2161 if (mbFileIsDamaged)
2162 return false;
2163
2164 bool read_something = false;
2165
2166 if (!streamBuffer)
2167 return false;
2168
2169 if (i64StreamPosition == -1) {
2170 i64StreamPosition = pos;
2171 sbPosition = 0;
2172 }
2173
2174 if (pos < i64StreamPosition+sbPosition)
2175 return false;
2176
2177 // >4Mb past current position!?
2178
2179 if (pos > i64StreamPosition+sbPosition+4194304) {
2180 // OutputDebugString("Resetting streaming position!\n");
2181 i64StreamPosition = pos;
2182 sbSize = sbPosition = 0;
2183 }
2184
2185 /* if (pusher->hdr.fccType == 'sdiv')
2186 OutputDebugString("pushed by video\n");
2187 else
2188 OutputDebugString("pushed by audio\n");*/
2189
2190 ++pusher->stream_pushes;
2191 pusher->stream_push_pos = pos;
2192
2193 while(pos >= i64StreamPosition+sbPosition) {
2194 long actual, left;
2195 char *src;
2196 uint32 hdr[2];
2197 int stream;
2198
2199 // read next header
2200
2201 left = 8;
2202 while(left > 0) {
2203 actual = left;
2204 src = _StreamRead(actual);
2205
2206 if (!src)
2207 return read_something;
2208
2209 memcpy((char *)hdr + (8-left), src, actual);
2210 left -= actual;
2211 }
2212
2213 stream = VDAVIGetStreamFromFOURCC(hdr[0]);
2214
2215 if (isxdigit(hdr[0]&0xff) && isxdigit((hdr[0]>>8)&0xff) && stream<32 &&
2216 ((1L<<stream) & fStreamsActive)) {
2217
2218 // _RPT3(0,"\tstream: reading chunk at %I64x, length %6ld, stream %ld\n", i64StreamPosition+sbPosition-8, hdr[1], stream);
2219
2220 AVIStreamNode *pasn, *pasn_next;
2221 int streamno = 0;
2222
2223 pasn = listStreams.AtHead();
2224
2225 while(pasn_next = pasn->NextFromHead()) {
2226 if (streamno == stream) {
2227 unsigned chunk_size = hdr[1] + (hdr[1]&1);
2228
2229 if (chunk_size >= 0x7ffffff0) {
2230 // Uh oh... assume the file has been damaged. Disable streaming.
2231 sint64 bad_pos = i64StreamPosition+sbPosition-8;
2232
2233 //VDLogAppMessage(kVDLogInfo, kVDST_AVIReadHandler, kVDM_StreamFailure, 1, &bad_pos);
2234 mbFileIsDamaged = true;
2235 i64StreamPosition = -1;
2236 sbPosition = sbSize = 0;
2237 return false;
2238 }
2239
2240 long left = chunk_size;
2241 bool fWrite = pasn->cache->WriteBegin(i64StreamPosition + sbPosition, left);
2242 char *dst;
2243
2244 while(left > 0) {
2245 actual = left;
2246
2247 dst = _StreamRead(actual);
2248
2249 if (!dst) {
2250 if (fWrite)
2251 pasn->cache->WriteEnd();
2252 return read_something;
2253 }
2254
2255 if (fWrite)
2256 pasn->cache->Write(dst, actual);
2257
2258 left -= actual;
2259 }
2260
2261 if (fWrite)
2262 pasn->cache->WriteEnd();
2263
2264 read_something = true;
2265
2266 break;
2267 }
2268
2269 pasn = pasn_next;
2270 ++streamno;
2271 }
2272 } else {
2273
2274 if (hdr[0] != FOURCC_LIST && hdr[0] != FOURCC_RIFF) {
2275 long actual;
2276
2277 // skip chunk
2278
2279 unsigned chunk_size = hdr[1] + (hdr[1] & 1);
2280
2281 if (chunk_size >= 0x7ffffff0) {
2282 mbFileIsDamaged = true;
2283 i64StreamPosition = -1;
2284 sbPosition = sbSize = 0;
2285 return false;
2286 }
2287
2288 // Determine if the chunk is overly large. If the chunk is too large, don't
2289 // stream through it.
2290
2291 if (chunk_size > 262144) {
2292 // Force resynchronization on next read.
2293 i64StreamPosition += chunk_size;
2294 sbPosition = sbSize = 0;
2295 return read_something;
2296 }
2297
2298 left = chunk_size;
2299
2300 while(left > 0) {
2301 actual = left;
2302
2303 if (!_StreamRead(actual))
2304 return read_something;
2305
2306 left -= actual;
2307 }
2308 } else {
2309 left = 4;
2310
2311 while(left > 0) {
2312 actual = left;
2313
2314 if (!_StreamRead(actual))
2315 return read_something;
2316
2317 left -= actual;
2318 }
2319 }
2320
2321 }
2322 }
2323
2324 return true;
2325 }
2326
getStreamPtr()2327 sint64 AVIReadHandler::getStreamPtr() {
2328 return i64StreamPosition + sbPosition;
2329 }
2330
FixCacheProblems(AVIReadStream * arse)2331 void AVIReadHandler::FixCacheProblems(AVIReadStream *arse) {
2332 AVIStreamNode *pasn, *pasn_next;
2333
2334 // The simplest fix is simply to disable caching on the stream that's
2335 // cache-missing. However, this is a bad idea if the problem is a low-bandwidth
2336 // audio stream that's outrunning a high-bandwidth video stream behind it.
2337 // Check the streaming leader, and if the streaming leader is comparatively
2338 // low bandwidth and running at least 512K ahead of the cache-missing stream,
2339 // disable its cache.
2340
2341 AVIStreamNode *stream_leader = nullptr;
2342 int stream_leader_no;
2343 int streamno=0;
2344
2345 pasn = listStreams.AtHead();
2346
2347 while(pasn_next = pasn->NextFromHead()) {
2348 if (pasn->cache)
2349 if (!stream_leader || pasn->stream_pushes > stream_leader->stream_pushes) {
2350 stream_leader = pasn;
2351 stream_leader_no = streamno;
2352 }
2353
2354 pasn = pasn_next;
2355 ++streamno;
2356 }
2357
2358 if (stream_leader && stream_leader->stream_bytes*2 < arse->psnData->stream_bytes
2359 && stream_leader->stream_push_pos >= arse->psnData->stream_push_pos+524288) {
2360
2361 VDTRACE_AVISTREAMING("caching disabled on fast puny leader\n");
2362
2363 delete stream_leader->cache;
2364 stream_leader->cache = nullptr;
2365
2366 DisableStreaming(stream_leader_no);
2367
2368 i64StreamPosition = -1;
2369 sbPosition = sbSize = 0;
2370 } else {
2371
2372 VDTRACE_AVISTREAMING("disabling caching at request of client\n");
2373
2374 arse->EndStreaming();
2375
2376 if (arse->psnData == stream_leader) {
2377 i64StreamPosition = -1;
2378 sbPosition = sbSize = 0;
2379 }
2380 }
2381
2382 // Reset cache statistics on all caches.
2383
2384 pasn = listStreams.AtHead();
2385
2386 while(pasn_next = pasn->NextFromHead()) {
2387 if (pasn->cache)
2388 pasn->cache->ResetStatistics();
2389
2390 pasn = pasn_next;
2391 }
2392 }
2393
ReadData(int stream,void * buffer,sint64 position,long len)2394 long AVIReadHandler::ReadData(int stream, void *buffer, sint64 position, long len) {
2395 if (mCurrentFile<0 || mCurrentFile != (int)(position>>48))
2396 SelectFile((int)(position>>48));
2397
2398 // _RPT3(0,"Reading from file %d, position %I64x, size %d\n", nCurrentFile, position, len);
2399
2400 if (!mpCurrentFile->mFile.seekNT(position & 0x0000FFFFFFFFFFFFi64))
2401 return -1;
2402 return mpCurrentFile->mFile.readData(buffer, len);
2403 }
2404
SelectFile(int file)2405 void AVIReadHandler::SelectFile(int file) {
2406 mCurrentFile = file;
2407 mpCurrentFile = mFiles[file];
2408 }
2409
ReadChunkHeader(uint32 & type,uint32 & size)2410 bool AVIReadHandler::ReadChunkHeader(uint32& type, uint32& size) {
2411 uint32 buf[2];
2412
2413 if (mpCurrentFile->mFile.readData(buf, 8) < 8)
2414 return false;
2415
2416 type = VDFromLE32(buf[0]);
2417 size = VDFromLE32(buf[1]);
2418 return true;
2419 }
2420