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