www.pudn.com > XvidQP.rar > AVIReadHandler.cpp
// VirtualDub - Video processing and capture application // Copyright (C) 1998-2001 Avery Lee // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include#include #include "AVIReadHandler.h" #include "FastReadStream.h" //#include "ProgressDialog.h" #include "AVIIndex.h" #include "Error.h" #include "List.h" #include "Fixes.h" #include "File64.h" #include "Avisynth.h" //#define STREAMING_DEBUG // HACK!!!! CRITICAL_SECTION g_diskcs; bool g_disklockinited=false; /////////////////////////////////////////// typedef __int64 QUADWORD; // The following comes from the OpenDML 1.0 spec for extended AVI files // bIndexType codes // #define AVI_INDEX_OF_INDEXES 0x00 // when each entry in aIndex // array points to an index chunk #define AVI_INDEX_OF_CHUNKS 0x01 // when each entry in aIndex // array points to a chunk in the file #define AVI_INDEX_IS_DATA 0x80 // when each entry is aIndex is // really the data // bIndexSubtype codes for INDEX_OF_CHUNKS #define AVI_INDEX_2FIELD 0x01 // when fields within frames // are also indexed struct _avisuperindex_entry { QUADWORD qwOffset; // absolute file offset, offset 0 is // unused entry?? DWORD dwSize; // size of index chunk at this offset DWORD dwDuration; // time span in stream ticks }; struct _avistdindex_entry { DWORD dwOffset; // qwBaseOffset + this is absolute file offset DWORD dwSize; // bit 31 is set if this is NOT a keyframe }; struct _avifieldindex_entry { DWORD dwOffset; DWORD dwSize; DWORD dwOffsetField2; }; #pragma pack(push) #pragma pack(2) typedef struct _avisuperindex_chunk { FOURCC fcc; // ’ix##’ DWORD cb; // size of this structure WORD wLongsPerEntry; // must be 4 (size of each entry in aIndex array) BYTE bIndexSubType; // must be 0 or AVI_INDEX_2FIELD BYTE bIndexType; // must be AVI_INDEX_OF_INDEXES DWORD nEntriesInUse; // number of entries in aIndex array that // are used DWORD dwChunkId; // ’##dc’ or ’##db’ or ’##wb’, etc DWORD dwReserved[3]; // must be 0 struct _avisuperindex_entry aIndex[]; } AVISUPERINDEX, * PAVISUPERINDEX; typedef struct _avistdindex_chunk { FOURCC fcc; // ’ix##’ DWORD cb; WORD wLongsPerEntry; // must be sizeof(aIndex[0])/sizeof(DWORD) BYTE bIndexSubType; // must be 0 BYTE bIndexType; // must be AVI_INDEX_OF_CHUNKS DWORD nEntriesInUse; // DWORD dwChunkId; // ’##dc’ or ’##db’ or ’##wb’ etc.. QUADWORD qwBaseOffset; // all dwOffsets in aIndex array are // relative to this DWORD dwReserved3; // must be 0 struct _avistdindex_entry aIndex[]; } AVISTDINDEX, * PAVISTDINDEX; typedef struct _avifieldindex_chunk { FOURCC fcc; DWORD cb; WORD wLongsPerEntry; BYTE bIndexSubType; BYTE bIndexType; DWORD nEntriesInUse; DWORD dwChunkId; QUADWORD qwBaseOffset; DWORD dwReserved3; struct _avifieldindex_entry aIndex[]; } AVIFIELDINDEX, * PAVIFIELDINDEX; #pragma pack(pop) /////////////////////////////////////////////////////////////////////////// IAVIReadStream::~IAVIReadStream() { } /////////////////////////////////////////////////////////////////////////// class AVIStreamNode; class AVIReadHandler; class AVIReadCache { public: long cache_hit_bytes, cache_miss_bytes; int reads; AVIReadCache(int nlines, int nstream, AVIReadHandler *root, AVIStreamNode *psnData); ~AVIReadCache(); void ResetStatistics(); bool WriteBegin(__int64 pos, long len); void Write(void *buffer, long len); void WriteEnd(); long Read(void *dest, __int64 chunk_pos, __int64 pos, long len); private: AVIStreamNode *psnData; __int64 (*buffer)[2]; int lines_max, lines; long read_tail, write_tail, write_hdr; int write_offset; int stream; AVIReadHandler *source; }; class AVIStreamNode : public ListNode2 { public: AVIStreamHeader_fixed hdr; void *pFormat; long lFormatLen; AVIIndex index; __int64 bytes; bool keyframe_only; int handler_count; class AVIReadCache *cache; int streaming_count; __int64 stream_push_pos; __int64 stream_bytes; int stream_pushes; long length; long frames; List2 listHandlers; AVIStreamNode(); ~AVIStreamNode(); }; AVIStreamNode::AVIStreamNode() { pFormat = NULL; bytes = 0; handler_count = 0; streaming_count = 0; stream_bytes = 0; stream_pushes = 0; cache = NULL; } AVIStreamNode::~AVIStreamNode() { delete pFormat; delete cache; } /////////////////////////////////////////////////////////////////////////// class AVIFileDesc : public ListNode2 { public: HANDLE hFile; HANDLE hFileUnbuffered; __int64 i64Size; }; class AVIStreamNode; class AVIReadHandler : public IAVIReadHandler, private File64 { public: bool fDisableFastIO; AVIReadHandler(const char *); AVIReadHandler(PAVIFILE); ~AVIReadHandler(); void AddRef(); void Release(); IAVIReadStream *GetStream(DWORD fccType, LONG lParam); void EnableFastIO(bool); bool isOptimizedForRealtime(); bool isStreaming(); bool isIndexFabricated(); bool AppendFile(const char *pszFile); bool getSegmentHint(const char **ppszPath); void EnableStreaming(int stream); void DisableStreaming(int stream); void AdjustRealTime(bool fRealTime); bool Stream(AVIStreamNode *, _int64 pos); __int64 getStreamPtr(); void FixCacheProblems(class AVIReadStream *); long ReadData(int stream, void *buffer, __int64 position, long len); private: // enum { STREAM_SIZE = 65536 }; enum { STREAM_SIZE = 1048576 }; enum { STREAM_RT_SIZE = 65536 }; enum { STREAM_BLOCK_SIZE = 4096 }; IAvisynthClipInfo *pAvisynthClipInfo; PAVIFILE paf; int ref_count; __int64 i64StreamPosition; int streams; char *streamBuffer; int sbPosition; int sbSize; long fStreamsActive; int nRealTime; int nActiveStreamers; bool fFakeIndex; __int64 i64Size; int nFiles, nCurrentFile; char * pSegmentHint; List2 listStreams; List2 listFiles; void _construct(const char *pszFile); void _parseFile(List2 & streams); bool _parseStreamHeader(List2 & streams, DWORD dwLengthLeft); bool _parseIndexBlock(List2 & streams, int count, __int64); void _parseExtendedIndexBlock(List2 & streams, AVIStreamNode *pasn, __int64 fpos, DWORD dwLength); void _destruct(); char * _StreamRead(long& bytes); void _SelectFile(int file); }; IAVIReadHandler *CreateAVIReadHandler(PAVIFILE paf) { return new AVIReadHandler(paf); } IAVIReadHandler *CreateAVIReadHandler(const char *pszFile) { return new AVIReadHandler(pszFile); } /////////////////////////////////////////////////////////////////////////// AVIReadCache::AVIReadCache(int nlines, int nstream, AVIReadHandler *root, AVIStreamNode *psnData) { buffer = new __int64[nlines][2]; if (!buffer) throw MyMemoryError(); this->psnData = psnData; lines = 0; lines_max = nlines; read_tail = 0; write_tail = 0; stream = nstream; source = root; ResetStatistics(); } AVIReadCache::~AVIReadCache() { delete[] buffer; } void AVIReadCache::ResetStatistics() { reads = 0; cache_hit_bytes = cache_miss_bytes = 0; } bool AVIReadCache::WriteBegin(__int64 pos, long len) { int needed; // delete lines as necessary to make room needed = 1 + (len+15)/16; if (needed > lines_max) return false; while(lines+needed > lines_max) { int siz = (int)((buffer[read_tail][1]+15)/16 + 1); read_tail += siz; lines -= siz; if (read_tail >= lines_max) read_tail -= lines_max; } // write in header // _RPT1(0,"\tbeginning write at line %ld\n", write_tail); write_hdr = write_tail; write_offset = 0; buffer[write_tail][0] = pos; buffer[write_tail][1] = 0; if (++write_tail >= lines_max) write_tail = 0; return true; } void AVIReadCache::Write(void *src, long len) { long dest; // copy in data buffer[write_hdr][1] += len; dest = write_tail + (len + write_offset + 15)/16; if (dest > lines_max) { int tc = (lines_max - write_tail)*16 - write_offset; memcpy((char *)&buffer[write_tail][0] + write_offset, src, tc); memcpy(&buffer[0][0], (char *)src + tc, len - tc); write_tail = (len-tc)/16; write_offset = (len-tc)&15; } else { memcpy((char *)&buffer[write_tail][0] + write_offset, src, len); write_tail += (len+write_offset)/16; if (write_tail >= lines_max) write_tail = 0; write_offset = (len+write_offset) & 15; } } void AVIReadCache::WriteEnd() { long cnt = (long)(1 + (buffer[write_hdr][1]+15)/16); lines += cnt; write_tail = write_hdr + cnt; if (write_tail >= lines_max) write_tail -= lines_max; // _RPT3(0,"\twrite complete -- header at line %d, size %ld, next line %ld\n", write_hdr, (long)buffer[write_hdr][1], write_tail); } #pragma function(memcpy) long AVIReadCache::Read(void *dest, __int64 chunk_pos, __int64 pos, long len) { long ptr; __int64 offset; // _RPT3(0,"Read request: chunk %16I64, pos %16I64d, %ld bytes\n", chunk_pos, pos, len); ++reads; do { // scan buffer looking for a range that contains data ptr = read_tail; while(ptr != write_tail) { offset = pos - buffer[ptr][0]; // _RPT4(0,"line %8d: pos %16I64d, len %ld bytes (%ld lines)\n", ptr, buffer[ptr][0], (long)buffer[ptr][1], (long)(buffer[ptr][1]+15)/16); if (offset>=0 && offset < buffer[ptr][1]) { long end; // cache hit // _RPT1(0, "cache hit (offset %I64d)\n", chunk_pos); cache_hit_bytes += len; while (cache_hit_bytes > 16777216) { cache_miss_bytes >>= 1; cache_hit_bytes >>= 1; } if (len > (long)(buffer[ptr][1]*16 - offset)) len = (long)(buffer[ptr][1]*16 - offset); ptr += 1+((long)offset>>4); if (ptr >= lines_max) ptr -= lines_max; end = ptr + ((len+((long)offset&15)+15)>>4); if (end > lines_max) { long tc = (lines_max - ptr)*16 - ((long)offset&15); memcpy(dest, (char *)&buffer[ptr][0] + ((long)offset&15), tc); memcpy((char *)dest + tc, (char *)&buffer[0][0], len-tc); } else { memcpy(dest, (char *)&buffer[ptr][0] + ((long)offset&15), len); } return len; } // _RPT4(0,"[x] line %8d: pos %16I64d, len %ld bytes (%ld lines)\n", ptr, buffer[ptr][0], (long)buffer[ptr][1], (long)(buffer[ptr][1]+15)/16); ptr += (long)(1+(buffer[ptr][1] + 15)/16); if (ptr >= lines_max) ptr -= lines_max; } if (source->getStreamPtr() > chunk_pos) break; } while(source->Stream(psnData, chunk_pos)); // OutputDebugString("cache miss\n"); // _RPT1(0, "cache miss (offset %I64d)\n", chunk_pos); cache_miss_bytes += len; while (cache_miss_bytes > 16777216) { cache_miss_bytes >>= 1; cache_hit_bytes >>= 1; } return source->ReadData(stream, dest, pos, len); } /////////////////////////////////////////////////////////////////////////// class AVIReadTunnelStream : public IAVIReadStream { public: AVIReadTunnelStream(AVIReadHandler *, PAVISTREAM, IAvisynthClipInfo *pClipInfo); ~AVIReadTunnelStream(); HRESULT BeginStreaming(long lStart, long lEnd, long lRate); HRESULT EndStreaming(); HRESULT Info(AVISTREAMINFO *pasi, long lSize); bool IsKeyFrame(long lFrame); HRESULT Read(long lStart, long lSamples, void *lpBuffer, long cbBuffer, long *plBytes, long *plSamples); long Start(); long End(); long PrevKeyFrame(long lFrame); long NextKeyFrame(long lFrame); long NearestKeyFrame(long lFrame); __int64 FramePos(long lFrame); HRESULT FormatSize(long lFrame, long *plSize); HRESULT ReadFormat(long lFrame, void *pFormat, long *plSize); bool isStreaming(); bool isKeyframeOnly(); private: IAvisynthClipInfo *const pAvisynthClipInfo; AVIReadHandler *const parent; const PAVISTREAM pas; }; /////////////////////////////////////////////////////////////////////////// AVIReadTunnelStream::AVIReadTunnelStream(AVIReadHandler *_parent, PAVISTREAM _pas, IAvisynthClipInfo *pClipInfo) : pAvisynthClipInfo(pClipInfo) , parent(_parent) , pas(_pas) { parent->AddRef(); } AVIReadTunnelStream::~AVIReadTunnelStream() { pas->Release(); parent->Release(); } HRESULT AVIReadTunnelStream::BeginStreaming(long lStart, long lEnd, long lRate) { return AVIStreamBeginStreaming(pas, lStart, lEnd, lRate); } HRESULT AVIReadTunnelStream::EndStreaming() { return AVIStreamEndStreaming(pas); } HRESULT AVIReadTunnelStream::Info(AVISTREAMINFO *pasi, long lSize) { return AVIStreamInfo(pas, pasi, lSize); } bool AVIReadTunnelStream::IsKeyFrame(long lFrame) { return !!AVIStreamIsKeyFrame(pas, lFrame); } HRESULT AVIReadTunnelStream::Read(long lStart, long lSamples, void *lpBuffer, long cbBuffer, long *plBytes, long *plSamples) { HRESULT hr; hr = AVIStreamRead(pas, lStart, lSamples, lpBuffer, cbBuffer, plBytes, plSamples); if (pAvisynthClipInfo) { const char *pszErr; if (pAvisynthClipInfo->GetError(&pszErr)) throw MyError("Avisynth read error:\n%s", pszErr); } return hr; } long AVIReadTunnelStream::Start() { return AVIStreamStart(pas); } long AVIReadTunnelStream::End() { return AVIStreamEnd(pas); } long AVIReadTunnelStream::PrevKeyFrame(long lFrame) { return AVIStreamPrevKeyFrame(pas, lFrame); } long AVIReadTunnelStream::NextKeyFrame(long lFrame) { return AVIStreamNextKeyFrame(pas, lFrame); } long AVIReadTunnelStream::NearestKeyFrame(long lFrame) { return AVIStreamNearestKeyFrame(pas, lFrame); } __int64 AVIReadTunnelStream::FramePos(long lFrame) { return -1; } HRESULT AVIReadTunnelStream::FormatSize(long lFrame, long *plSize) { return AVIStreamFormatSize(pas, lFrame, plSize); } HRESULT AVIReadTunnelStream::ReadFormat(long lFrame, void *pFormat, long *plSize) { return AVIStreamReadFormat(pas, lFrame, pFormat, plSize); } bool AVIReadTunnelStream::isStreaming() { return false; } bool AVIReadTunnelStream::isKeyframeOnly() { return false; } /////////////////////////////////////////////////////////////////////////// class AVIReadStream : public IAVIReadStream, public ListNode2 { friend AVIReadHandler; public: AVIReadStream(AVIReadHandler *, AVIStreamNode *, int); ~AVIReadStream(); HRESULT BeginStreaming(long lStart, long lEnd, long lRate); HRESULT EndStreaming(); HRESULT Info(AVISTREAMINFO *pasi, long lSize); bool IsKeyFrame(long lFrame); HRESULT Read(long lStart, long lSamples, void *lpBuffer, long cbBuffer, long *plBytes, long *plSamples); long Start(); long End(); long PrevKeyFrame(long lFrame); long NextKeyFrame(long lFrame); long NearestKeyFrame(long lFrame); __int64 FramePos(long lFrame); HRESULT FormatSize(long lFrame, long *plSize); HRESULT ReadFormat(long lFrame, void *pFormat, long *plSize); bool isStreaming(); bool isKeyframeOnly(); void Reinit(); private: AVIReadHandler *parent; AVIStreamNode *psnData; AVIIndexEntry2 *pIndex; AVIReadCache *rCache; long& length; long& frames; long sampsize; int streamno; bool fStreamingEnabled; bool fStreamingActive; int iStreamTrackCount; long lStreamTrackValue; long lStreamTrackInterval; bool fRealTime; __int64 i64CachedPosition; AVIIndexEntry2 *pCachedEntry; }; /////////////////////////////////////////////////////////////////////////// AVIReadStream::AVIReadStream(AVIReadHandler *parent, AVIStreamNode *psnData, int streamno) :length(psnData->length) ,frames(psnData->frames) { this->parent = parent; this->psnData = psnData; this->streamno = streamno; fStreamingEnabled = false; fStreamingActive = false; fRealTime = false; parent->AddRef(); pIndex = psnData->index.index2Ptr(); sampsize = psnData->hdr.dwSampleSize; /* Well, I don't know why, but it kills the MP3 handling So, until I've found why... // Hack to imitate Microsoft's parser. It seems to ignore this value // for audio streams. if (psnData->hdr.fccType == streamtypeAUDIO) sampsize = ((WAVEFORMATEX *)psnData->pFormat)->nBlockAlign; */ if (sampsize) { i64CachedPosition = 0; pCachedEntry = pIndex; } psnData->listHandlers.AddTail(this); } AVIReadStream::~AVIReadStream() { EndStreaming(); parent->Release(); Remove(); } void AVIReadStream::Reinit() { pIndex = psnData->index.index2Ptr(); i64CachedPosition = 0; pCachedEntry = pIndex; } HRESULT AVIReadStream::BeginStreaming(long lStart, long lEnd, long lRate) { if (fStreamingEnabled) return 0; // OutputDebugString(lRate>1500 ? "starting: fast" : "starting: slow"); if (lRate <= 1500) { parent->AdjustRealTime(true); fRealTime = true; } else fRealTime = false; if (parent->fDisableFastIO) return 0; if (!psnData->streaming_count) { // if (!(psnData->cache = new AVIReadCache(psnData->hdr.fccType == 'sdiv' ? 65536 : 16384, streamno, parent, psnData))) // return AVIERR_MEMORY; psnData->stream_bytes = 0; psnData->stream_pushes = 0; psnData->stream_push_pos = 0; } ++psnData->streaming_count; fStreamingEnabled = true; fStreamingActive = false; iStreamTrackCount = 0; lStreamTrackValue = -1; lStreamTrackInterval = -1; return 0; } HRESULT AVIReadStream::EndStreaming() { if (!fStreamingEnabled) return 0; if (fRealTime) parent->AdjustRealTime(false); if (fStreamingActive) parent->DisableStreaming(streamno); fStreamingEnabled = false; fStreamingActive = false; if (!--psnData->streaming_count) { delete psnData->cache; psnData->cache = NULL; } return 0; } HRESULT AVIReadStream::Info(AVISTREAMINFO *pasi, long lSize) { AVISTREAMINFO asi; memset(&asi, 0, sizeof asi); asi.fccType = psnData->hdr.fccType; asi.fccHandler = psnData->hdr.fccHandler; asi.dwFlags = psnData->hdr.dwFlags; asi.wPriority = psnData->hdr.wPriority; asi.wLanguage = psnData->hdr.wLanguage; asi.dwScale = psnData->hdr.dwScale; asi.dwRate = psnData->hdr.dwRate; asi.dwStart = psnData->hdr.dwStart; asi.dwLength = psnData->hdr.dwLength; asi.dwInitialFrames = psnData->hdr.dwInitialFrames; asi.dwSuggestedBufferSize = psnData->hdr.dwSuggestedBufferSize; asi.dwQuality = psnData->hdr.dwQuality; asi.dwSampleSize = psnData->hdr.dwSampleSize; asi.rcFrame.top = psnData->hdr.rcFrame.top; asi.rcFrame.left = psnData->hdr.rcFrame.left; asi.rcFrame.right = psnData->hdr.rcFrame.right; asi.rcFrame.bottom = psnData->hdr.rcFrame.bottom; if (lSize < sizeof asi) memcpy(pasi, &asi, lSize); else { memcpy(pasi, &asi, sizeof asi); memset((char *)pasi + sizeof asi, 0, lSize - sizeof asi); } return 0; } bool AVIReadStream::IsKeyFrame(long lFrame) { if (sampsize) return true; else { if (lFrame < 0 || lFrame >= length) return false; return !(pIndex[lFrame].size & 0x80000000); } } HRESULT AVIReadStream::Read(long lStart, long lSamples, void *lpBuffer, long cbBuffer, long *plBytes, long *plSamples) { long lActual; if (lStart < 0 || lStart >= length || (lSamples <= 0 && lSamples != AVISTREAMREAD_CONVENIENT)) { // umm... dummy! can't read outside of stream! if (plBytes) *plBytes = 0; if (plSamples) *plSamples = 0; return 0; } // blocked or discrete? if (sampsize) { AVIIndexEntry2 *avie2, *avie2_limit = pIndex+frames; __int64 byte_off = (__int64)lStart * sampsize; __int64 bytecnt; __int64 actual_bytes=0; __int64 block_pos; // too small to hold a sample? if (lpBuffer && cbBuffer < sampsize) { if (plBytes) *plBytes = sampsize * lSamples; if (plSamples) *plSamples = lSamples; return AVIERR_BUFFERTOOSMALL; } // find the frame that has the starting sample -- try and work // from our last position to save time if (byte_off >= i64CachedPosition) { block_pos = i64CachedPosition; avie2 = pCachedEntry; byte_off -= block_pos; } else { block_pos = 0; avie2 = pIndex; } while(byte_off >= (avie2->size & 0x7FFFFFFF)) { byte_off -= (avie2->size & 0x7FFFFFFF); block_pos += (avie2->size & 0x7FFFFFFF); ++avie2; } pCachedEntry = avie2; i64CachedPosition = block_pos; // Client too lazy to specify a size? if (lSamples == AVISTREAMREAD_CONVENIENT) { lSamples = ((avie2->size & 0x7FFFFFFF) - (long)byte_off) / sampsize; if (!lSamples && avie2+1 < avie2_limit) lSamples = ((avie2[0].size & 0x7FFFFFFF) + (avie2[1].size & 0x7FFFFFFF) - (long)byte_off) / sampsize; if (lSamples < 0) lSamples = 1; } // trim down sample count if (lpBuffer && lSamples > cbBuffer / sampsize) lSamples = cbBuffer / sampsize; if (lStart+lSamples > length) lSamples = length - lStart; bytecnt = lSamples * sampsize; // begin reading frames from this point on if (lpBuffer) { // detect streaming if (fStreamingEnabled) { // We consider the client to be streaming if we detect at least // 3 consecutive accesses if (lStart == lStreamTrackValue) { ++iStreamTrackCount; if (iStreamTrackCount >= 15) { __int64 streamptr = parent->getStreamPtr(); __int64 fptrdiff = streamptr - avie2->pos; if (!parent->isStreaming() || streamptr<0 || (fptrdiff<4194304 && fptrdiff>-4194304)) { if (!psnData->cache) psnData->cache = new AVIReadCache(psnData->hdr.fccType == 'sdiv' ? 131072 : 16384, streamno, parent, psnData); else psnData->cache->ResetStatistics(); if (!fStreamingActive) { fStreamingActive = true; parent->EnableStreaming(streamno); } #ifdef STREAMING_DEBUG OutputDebugString("[a] streaming enabled\n"); #endif } } else { #ifdef STREAMING_DEBUG OutputDebugString("[a] streaming detected\n"); #endif } } else { #ifdef STREAMING_DEBUG OutputDebugString("[a] streaming disabled\n"); #endif iStreamTrackCount = 0; if (fStreamingActive) { fStreamingActive = false; parent->DisableStreaming(streamno); } } } while(bytecnt > 0) { long tc; tc = (avie2->size & 0x7FFFFFFF) - (long)byte_off; if (tc > bytecnt) tc = (long)bytecnt; if (psnData->cache) { //OutputDebugString("[a] attempting cached read\n"); lActual = psnData->cache->Read(lpBuffer, avie2->pos, avie2->pos + byte_off + 8, tc); psnData->stream_bytes += lActual; } else lActual = parent->ReadData(streamno, lpBuffer, avie2->pos + byte_off + 8, tc); if (lActual < 0) break; actual_bytes += lActual; ++avie2; byte_off = 0; if (lActual < tc) break; bytecnt -= tc; lpBuffer = (char *)lpBuffer + tc; } if (actual_bytes < sampsize) { if (plBytes) *plBytes = 0; if (plSamples) *plSamples = 0; return AVIERR_FILEREAD; } actual_bytes -= actual_bytes % sampsize; if (plBytes) *plBytes = (long)actual_bytes; if (plSamples) *plSamples = (long)actual_bytes / sampsize; lStreamTrackValue = lStart + (long)actual_bytes / sampsize; } else { if (plBytes) *plBytes = (long)bytecnt; if (plSamples) *plSamples = lSamples; } } else { AVIIndexEntry2 *avie2 = &pIndex[lStart]; if (lpBuffer && (avie2->size & 0x7FFFFFFF) > cbBuffer) { if (plBytes) *plBytes = avie2->size & 0x7FFFFFFF; if (plSamples) *plSamples = 1; return AVIERR_BUFFERTOOSMALL; } if (lpBuffer) { // detect streaming if (fStreamingEnabled && lStart != lStreamTrackValue) { if (lStreamTrackValue>=0 && lStart-lStreamTrackValue == lStreamTrackInterval) { if (++iStreamTrackCount >= 15) { __int64 streamptr = parent->getStreamPtr(); __int64 fptrdiff = streamptr - avie2->pos; if (!parent->isStreaming() || streamptr<0 || (fptrdiff<4194304 && fptrdiff>-4194304)) { if (!psnData->cache) psnData->cache = new AVIReadCache(psnData->hdr.fccType == 'sdiv' ? 131072 : 16384, streamno, parent, psnData); else psnData->cache->ResetStatistics(); if (!fStreamingActive) { fStreamingActive = true; parent->EnableStreaming(streamno); } #ifdef STREAMING_DEBUG OutputDebugString("[v] streaming activated\n"); #endif } } else { #ifdef STREAMING_DEBUG OutputDebugString("[v] streaming detected\n"); #endif } } else { iStreamTrackCount = 0; #ifdef STREAMING_DEBUG OutputDebugString("[v] streaming disabled\n"); #endif if (lStreamTrackValue>=0 && lStart > lStreamTrackValue) { lStreamTrackInterval = lStart - lStreamTrackValue; } else lStreamTrackInterval = -1; if (fStreamingActive) { fStreamingActive = false; parent->DisableStreaming(streamno); } } lStreamTrackValue = lStart; } // read data if (psnData->cache && fStreamingActive) { //OutputDebugString("[v] attempting cached read\n"); lActual = psnData->cache->Read(lpBuffer, avie2->pos, avie2->pos + 8, avie2->size & 0x7FFFFFFF); psnData->stream_bytes += lActual; } else lActual = parent->ReadData(streamno, lpBuffer, avie2->pos+8, avie2->size & 0x7FFFFFFF); if (lActual != (avie2->size & 0x7FFFFFFF)) { if (plBytes) *plBytes = 0; if (plSamples) *plSamples = 0; return AVIERR_FILEREAD; } } if (plBytes) *plBytes = avie2->size & 0x7FFFFFFF; if (plSamples) *plSamples = 1; } if (psnData->cache && fStreamingActive) { // Are we experiencing a high rate of cache misses? if (psnData->cache->cache_miss_bytes*2 > psnData->cache->cache_hit_bytes && psnData->cache->reads > 50) { // sh*t, notify the parent that we have cache misses so it can check which stream is // screwing up, and disable streaming on feeds that are too far off parent->FixCacheProblems(this); iStreamTrackCount = 0; } }/* else if (fStreamingEnabled) { // hmm... our cache got killed! iStreamTrackCount = 0; }*/ return 0; } long AVIReadStream::Start() { return 0; } long AVIReadStream::End() { return length; } long AVIReadStream::PrevKeyFrame(long lFrame) { if (sampsize) return lFrame>0 ? lFrame-1 : -1; if (lFrame < 0) return -1; if (lFrame >= length) lFrame = length; while(--lFrame > 0) if (!(pIndex[lFrame].size & 0x80000000)) return lFrame; return -1; } long AVIReadStream::NextKeyFrame(long lFrame) { if (sampsize) return lFrame = length) return -1; while(++lFrame < length) if (!(pIndex[lFrame].size & 0x80000000)) return lFrame; return -1; } long AVIReadStream::NearestKeyFrame(long lFrame) { long lprev; if (sampsize) return lFrame; if (IsKeyFrame(lFrame)) return lFrame; lprev = PrevKeyFrame(lFrame); if (lprev < 0) return 0; else return lprev; } __int64 AVIReadStream::FramePos(long lFrame) { return pIndex[lFrame].pos; } HRESULT AVIReadStream::FormatSize(long lFrame, long *plSize) { *plSize = psnData->lFormatLen; return 0; } HRESULT AVIReadStream::ReadFormat(long lFrame, void *pFormat, long *plSize) { if (!pFormat) { *plSize = psnData->lFormatLen; return 0; } if (*plSize < psnData->lFormatLen) { memcpy(pFormat, psnData->pFormat, *plSize); } else { memcpy(pFormat, psnData->pFormat, psnData->lFormatLen); *plSize = psnData->lFormatLen; } return 0; } bool AVIReadStream::isStreaming() { return psnData->cache && fStreamingActive; } bool AVIReadStream::isKeyframeOnly() { return psnData->keyframe_only; } /////////////////////////////////////////////////////////////////////////// AVIReadHandler::AVIReadHandler(const char *s) : pAvisynthClipInfo(0) { this->hFile = INVALID_HANDLE_VALUE; this->hFileUnbuffered = INVALID_HANDLE_VALUE; this->paf = NULL; ref_count = 1; streams=0; fStreamsActive = 0; fDisableFastIO = false; streamBuffer = NULL; nRealTime = 0; nActiveStreamers = 0; fFakeIndex = false; nFiles = 1; nCurrentFile = 0; pSegmentHint = NULL; if (!g_disklockinited) { g_disklockinited=true; InitializeCriticalSection(&g_diskcs); } _construct(s); } AVIReadHandler::AVIReadHandler(PAVIFILE paf) { this->hFile = INVALID_HANDLE_VALUE; this->hFileUnbuffered = INVALID_HANDLE_VALUE; this->paf = paf; ref_count = 1; streams=0; streamBuffer = NULL; pSegmentHint = NULL; fFakeIndex = false; if (FAILED(paf->QueryInterface(IID_IAvisynthClipInfo, (void **)&pAvisynthClipInfo))) pAvisynthClipInfo = NULL; else { const char *s; if (pAvisynthClipInfo->GetError(&s)) { MyError e("Avisynth open failure:\n%s", s); pAvisynthClipInfo->Release(); paf->Release(); throw e; } } } AVIReadHandler::~AVIReadHandler() { _destruct(); } void AVIReadHandler::_construct(const char *pszFile) { try { AVIFileDesc *pDesc; // open file hFile = CreateFile(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (INVALID_HANDLE_VALUE == hFile) throw MyWin32Error("Couldn't open %s: %%s", GetLastError(), pszFile); hFileUnbuffered = CreateFile( pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING, NULL ); i64FilePosition = 0; // recursively parse file _parseFile(listStreams); // Create first link if (!(pDesc = new AVIFileDesc)) throw MyMemoryError(); pDesc->hFile = hFile; pDesc->hFileUnbuffered = hFileUnbuffered; pDesc->i64Size = i64Size = _sizeFile(); listFiles.AddHead(pDesc); } catch(...) { _destruct(); throw; } } bool AVIReadHandler::AppendFile(const char *pszFile) { List2 newstreams; AVIStreamNode *pasn_old, *pasn_new, *pasn_old_next=NULL, *pasn_new_next=NULL; AVIFileDesc *pDesc; nCurrentFile = -1; // open file hFile = CreateFile(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (INVALID_HANDLE_VALUE == hFile) throw MyWin32Error("Couldn't open %s: %%s", GetLastError(), pszFile); hFileUnbuffered = CreateFile( pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING, NULL ); try { _parseFile(newstreams); pasn_old = listStreams.AtHead(); pasn_new = newstreams.AtHead(); while(!!(pasn_old_next = pasn_old->NextFromHead()) & !!(pasn_new_next = pasn_new->NextFromHead())) { const char *szPrefix; switch(pasn_old->hdr.fccType) { case streamtypeAUDIO: szPrefix = "Cannot append segment: The audio streams "; break; case streamtypeVIDEO: szPrefix = "Cannot append segment: The video streams "; break; default: szPrefix = NULL; } // If it's not an audio or video stream, why do we care? if (szPrefix) { if (pasn_old->hdr.fccType != pasn_new->hdr.fccType) throw MyError("Cannot append segment: The stream types do not count."); if (pasn_old->hdr.fccHandler != pasn_new->hdr.fccHandler) throw MyError("%suse incompatible compression types.", szPrefix); // A/B ?= C/D ==> AD ?= BC if ((__int64)pasn_old->hdr.dwScale * pasn_new->hdr.dwRate != (__int64)pasn_new->hdr.dwScale * pasn_old->hdr.dwRate) throw MyError("%shave different sampling rates (%.5f vs. %.5f)" ,szPrefix ,(double)pasn_old->hdr.dwRate / pasn_old->hdr.dwScale ,(double)pasn_new->hdr.dwRate / pasn_new->hdr.dwScale ); if (pasn_old->hdr.dwSampleSize != pasn_new->hdr.dwSampleSize) throw MyError("%shave different block sizes (%d vs %d).", szPrefix, pasn_old->hdr.dwSampleSize, pasn_new->hdr.dwSampleSize); // I hate PCMWAVEFORMAT. bool fOk = pasn_old->lFormatLen == pasn_new->lFormatLen && !memcmp(pasn_old->pFormat, pasn_new->pFormat, pasn_old->lFormatLen); if (pasn_old->hdr.fccType == streamtypeAUDIO) if (((WAVEFORMATEX *)pasn_old->pFormat)->wFormatTag == WAVE_FORMAT_PCM && ((WAVEFORMATEX *)pasn_new->pFormat)->wFormatTag == WAVE_FORMAT_PCM) fOk = !memcmp(pasn_old->pFormat, pasn_new->pFormat, sizeof(PCMWAVEFORMAT)); if (!fOk) throw MyError("%shave different data formats.", szPrefix); } pasn_old = pasn_old_next; pasn_new = pasn_new_next; } if (pasn_old_next || pasn_new_next) throw MyError("Cannot append segment: The segment has a different number of streams."); if (!(pDesc = new AVIFileDesc)) throw MyMemoryError(); pDesc->hFile = hFile; pDesc->hFileUnbuffered = hFileUnbuffered; pDesc->i64Size = _sizeFile(); } catch(MyError e) { while(pasn_new = newstreams.RemoveHead()) delete pasn_new; CloseHandle(hFile); if (hFileUnbuffered != INVALID_HANDLE_VALUE) CloseHandle(hFileUnbuffered); throw; } // Accept segment; begin merging process. pasn_old = listStreams.AtHead(); while(pasn_old_next = pasn_old->NextFromHead()) { pasn_new = newstreams.RemoveHead(); // Fix up header. pasn_old->hdr.dwLength += pasn_new->hdr.dwLength; if (pasn_new->hdr.dwSuggestedBufferSize > pasn_old->hdr.dwSuggestedBufferSize) pasn_old->hdr.dwSuggestedBufferSize = pasn_new->hdr.dwSuggestedBufferSize; pasn_old->bytes += pasn_new->bytes; pasn_old->frames += pasn_new->frames; pasn_old->length += pasn_new->length; // Merge indices. int oldlen = pasn_old->index.indexLen(); AVIIndexEntry2 *idx_old = pasn_old->index.takeIndex2(); AVIIndexEntry2 *idx_new = pasn_new->index.index2Ptr(); int i; pasn_old->index.clear(); for(i=0; i index.add(&idx_old[i]); } delete[] idx_old; for(i=pasn_new->index.indexLen(); i; i--) { idx_new->size ^= 0x80000000; idx_new->pos += (__int64)nFiles << 48; pasn_old->index.add(idx_new++); } pasn_old->index.makeIndex2(); // Notify all handlers. AVIReadStream *pStream, *pStreamNext; pStream = pasn_old->listHandlers.AtHead(); while(pStreamNext = pStream->NextFromHead()) { pStream->Reinit(); pStream = pStreamNext; } // Next! pasn_old = pasn_old_next; delete pasn_new; } ++nFiles; listFiles.AddTail(pDesc); return true; } void AVIReadHandler::_parseFile(List2 & streamlist) { FOURCC fccType; DWORD dwLength; DWORD dwLengthLeft; bool index_found = false; bool size_invalid = false; bool fAcceptIndexOnly = true; bool hyperindexed = false; AVIStreamNode *pasn, *pasn_next; __int64 i64ChunkMoviPos = 0; DWORD dwChunkMoviLength = 0; if (!_readChunkHeader(fccType, dwLength)) throw MyError("Invalid AVI file: File is less than 8 bytes"); if (fccType != FOURCC_RIFF) throw MyError("Invalid AVI file: Not a RIFF file"); // If the RIFF header is <4 bytes, assume it was an improperly closed // file. // if (dwLength < 4) // throw MyError("Invalid AVI file: RIFF wrapper has invalid size"); if (dwLength < 4) { dwLength = 0xFFFFFFF0; size_invalid = true; } dwLengthLeft = dwLength-4; _readFile2(&fccType, 4); if (fccType != ' IVA') throw MyError("Invalid AVI file: RIFF type is not 'AVI'"); // begin parsing chunks while(dwLengthLeft >= 8 && _readChunkHeader(fccType, dwLength)) { // _RPT4(0,"%08I64x %08I64x Chunk '%-4s', length %08lx\n", _posFile()+dwLengthLeft, _posFile(), &fccType, dwLength); if (!size_invalid) { dwLengthLeft -= 8; if (dwLength > dwLengthLeft) throw MyError("Invalid AVI file: chunk at %I64d extends outside of parent by %ld bytes", _posFile()-8, dwLength-dwLengthLeft); dwLengthLeft -= (dwLength + (dwLength&1)); } switch(fccType) { case FOURCC_LIST: _readFile2(&fccType, 4); if (dwLength<4 && (fccType != 'ivom' || !size_invalid)) throw MyError("Invalid AVI file: LIST chunk <4 bytes"); dwLength -= 4; // _RPT1(0,"\tList type '%-4s'\n", &fccType); switch(fccType) { case 'ivom': if (dwLength < 8 && size_invalid) { i64ChunkMoviPos = _posFile(); dwChunkMoviLength = 0xFFFFFFF0; dwLength = 0; } else { i64ChunkMoviPos = _posFile(); dwChunkMoviLength = dwLength; } if (fAcceptIndexOnly) goto terminate_scan; break; case listtypeAVIHEADER: dwLengthLeft += (dwLength + (dwLength&1)) + 4; dwLength = 0; // silently enter the header block break; case listtypeSTREAMHEADER: if (!_parseStreamHeader(streamlist, dwLength)) fAcceptIndexOnly = false; else hyperindexed = true; ++streams; dwLength = 0; break; } break; case ckidAVINEWINDEX: // idx1 if (!hyperindexed) { index_found = _parseIndexBlock(streamlist, dwLength/16, i64ChunkMoviPos); dwLength &= 15; } break; case ckidAVIPADDING: // JUNK break; case 'mges': // VirtualDub segment hint block delete pSegmentHint; if (!(pSegmentHint = new char[dwLength])) throw MyMemoryError(); _readFile2(pSegmentHint, dwLength); if (dwLength&1) _skipFile2(1); dwLength = 0; break; } if (dwLength) { if (!_skipFile2(dwLength + (dwLength&1))) break; } // If we're trying to read a broken file, quit as soon as we see the index // block. if (size_invalid && fccType == ckidAVINEWINDEX) break; } if (i64ChunkMoviPos == 0) throw MyError("This AVI file doesn't have a movie data block (movi)!"); terminate_scan: if (!index_found && !hyperindexed) { // ProgressDialog pd(NULL, "AVI Import Filter", "Reconstructing missing index block", (dwChunkMoviLength+1023)/1024, true); // pd.setValueFormat("%ldK of %ldK"); // obtain length of file and limit scanning if so __int64 i64FileSize = 0x7FFFFFFFFFFFFFFFi64; dwLengthLeft = dwChunkMoviLength; { DWORD dwLow, dwHigh; dwLow = GetFileSize(hFile, &dwHigh); if (dwLow != 0xFFFFFFFF || GetLastError()==NO_ERROR) { i64FileSize = (__int64)(unsigned long)dwLow + ((__int64)(signed long)dwHigh << 32); } } fFakeIndex = true; _seekFile(i64ChunkMoviPos); while(dwLengthLeft >= 8 && _readChunkHeader(fccType, dwLength)) { int stream; // _RPT2(0,"(stream header) Chunk '%-4s', length %08lx\n", &fccType, dwLength); dwLengthLeft -= 8+(dwLength + (dwLength&1)); if (dwLength) { if (!_skipFile2(dwLength + (dwLength&1))) break; } if (_posFile() > i64FileSize) break; if (isxdigit(fccType&0xff) && isxdigit((fccType>>8)&0xff)) { stream = StreamFromFOURCC(fccType); if (stream >=0 && stream < streams) { pasn = streamlist.AtHead(); while(stream-- && (pasn_next = pasn->NextFromHead())) pasn = pasn_next; if (pasn && pasn_next) { pasn->index.add(fccType, _posFile()-(dwLength + (dwLength&1))-8, dwLength, pasn->keyframe_only && pasn->bytes!=0); pasn->bytes += dwLength; } } } // pd.advance((long)((_posFile() - i64ChunkMoviPos)/1024)); // pd.check(); } } // glue together indices pasn = streamlist.AtHead(); while(pasn_next = pasn->NextFromHead()) { if (!pasn->index.makeIndex2()) throw MyMemoryError(); pasn->frames = pasn->index.indexLen(); // Clear sample size for video streams! if (pasn->hdr.fccType == streamtypeVIDEO) pasn->hdr.dwSampleSize=0; if (pasn->hdr.dwSampleSize) pasn->length = (long)pasn->bytes / pasn->hdr.dwSampleSize; else pasn->length = pasn->frames; pasn = pasn_next; } // throw MyError("Parse complete. Aborting."); } bool AVIReadHandler::_parseStreamHeader(List2 & streamlist, DWORD dwLengthLeft) { AVIStreamNode *pasn; FOURCC fccType; DWORD dwLength; bool hyperindexed = false; if (!(pasn = new AVIStreamNode())) throw MyMemoryError(); try { while(dwLengthLeft >= 8 && _readChunkHeader(fccType, dwLength)) { // _RPT2(0,"(stream header) Chunk '%-4s', length %08lx\n", &fccType, dwLength); dwLengthLeft -= 8; if (dwLength > dwLengthLeft) throw MyError("Invalid AVI file: chunk size extends outside of parent"); dwLengthLeft -= (dwLength + (dwLength&1)); switch(fccType) { case ckidSTREAMHEADER: memset(&pasn->hdr, 0, sizeof pasn->hdr); if (dwLength < sizeof pasn->hdr) { _readFile2(&pasn->hdr, dwLength); if (dwLength & 1) _skipFile(1); } else { _readFile2(&pasn->hdr, sizeof pasn->hdr); _skipFile(dwLength+(dwLength&1) - sizeof pasn->hdr); } dwLength = 0; pasn->keyframe_only = false; break; case ckidSTREAMFORMAT: if (!(pasn->pFormat = new char[pasn->lFormatLen = dwLength])) throw MyMemoryError(); _readFile2(pasn->pFormat, dwLength); if (pasn->hdr.fccType == streamtypeVIDEO) { switch(((BITMAPINFOHEADER *)pasn->pFormat)->biCompression) { case NULL: case ' WAR': case ' BID': case '1bmd': case 'gpjm': case 'GPJM': case 'YUYV': case '2YUY': case 'YVYU': case 'UYVY': case '21VY': case '024I': case 'P14Y': case 'vuyc': case 'UYFH': pasn->keyframe_only = true; } } if (dwLength & 1) _skipFile(1); dwLength = 0; break; case 'xdni': // OpenDML extended index _parseExtendedIndexBlock(streamlist, pasn, -1, dwLength); hyperindexed = true; break; case ckidAVIPADDING: // JUNK break; } if (dwLength) { if (!_skipFile2(dwLength + (dwLength&1))) break; } } if (dwLengthLeft) _skipFile2(dwLengthLeft); } catch(...) { delete pasn; throw; } // _RPT1(0,"Found stream: type %s\n", pasn->hdr.fccType==streamtypeVIDEO ? "video" : pasn->hdr.fccType==streamtypeAUDIO ? "audio" : "unknown"); streamlist.AddTail(pasn); return hyperindexed; } bool AVIReadHandler::_parseIndexBlock(List2 & streamlist, int count, __int64 movi_offset) { AVIINDEXENTRY avie[32]; AVIStreamNode *pasn, *pasn_next; bool absolute_addr = true; // Some AVI files have relative addresses in their AVI index chunks, and some // relative. They're supposed to be relative to the 'movi' chunk; all versions // of VirtualDub using fast write routines prior to build 4936 generate absolute // addresses (oops). AVIFile and ActiveMovie are both ambivalent. I guess we'd // better be as well. while(count > 0) { int tc = count; int i; if (tc>32) tc=32; count -= tc; if (tc*sizeof(AVIINDEXENTRY) != _readFile(avie, tc*sizeof(AVIINDEXENTRY))) { pasn = streamlist.AtHead(); while(pasn_next = pasn->NextFromHead()) { pasn->index.clear(); pasn->bytes = 0; pasn = pasn_next; } return false; } for(i=0; i NextFromHead()) && stream--) pasn = pasn_next; if (pasn && pasn_next) { if (absolute_addr) pasn->index.add(&avie[i]); else pasn->index.add(avie[i].ckid, (movi_offset-4) + (__int64)avie[i].dwChunkOffset, avie[i].dwChunkLength, !!(avie[i].dwFlags & AVIIF_KEYFRAME)); pasn->bytes += avie[i].dwChunkLength; } } } return true; } void AVIReadHandler::_parseExtendedIndexBlock(List2 & streamlist, AVIStreamNode *pasn, __int64 fpos, DWORD dwLength) { union { AVISUPERINDEX idxsuper; AVISTDINDEX idxstd; }; union { struct _avisuperindex_entry superent[64]; DWORD dwHeap[256]; }; int entries, tp; int i; __int64 i64FPSave = _posFile(); if (fpos>=0) _seekFile(fpos); _readFile2((char *)&idxsuper + 8, sizeof(AVISUPERINDEX) - 8); switch(idxsuper.bIndexType) { case AVI_INDEX_OF_INDEXES: // sanity check if (idxsuper.wLongsPerEntry != 4) throw MyError("Invalid superindex block in stream"); // if (idxsuper.bIndexSubType != 0) // throw MyError("Field indexes not supported"); entries = idxsuper.nEntriesInUse; while(entries > 0) { tp = sizeof superent / sizeof superent[0]; if (tp>entries) tp=entries; _readFile2(superent, tp*sizeof superent[0]); for(i=0; i 0) { tp = (sizeof dwHeap / sizeof dwHeap[0]) / idxstd.wLongsPerEntry; if (tp>entries) tp=entries; _readFile2(dwHeap, tp*idxstd.wLongsPerEntry*sizeof(DWORD)); if (idxstd.wLongsPerEntry == 6) for(i=0; i index.add(idxstd.dwChunkId, (idxstd.qwBaseOffset+dwOffset)-8, dwSize, true); pasn->bytes += dwSize; } else for(i=0; i index.add(idxstd.dwChunkId, (idxstd.qwBaseOffset+dwOffset)-8, dwSize&0x7FFFFFFF, !(dwSize&0x80000000)); pasn->bytes += dwSize & 0x7FFFFFFF; } entries -= tp; } } break; default: throw MyError("Unknown hyperindex type"); } _seekFile(i64FPSave); } void AVIReadHandler::_destruct() { AVIStreamNode *pasn; AVIFileDesc *pDesc; if (pAvisynthClipInfo) pAvisynthClipInfo->Release(); if (paf) AVIFileRelease(paf); while(pasn = listStreams.RemoveTail()) delete pasn; delete streamBuffer; if (listFiles.IsEmpty()) { if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); if (hFileUnbuffered != INVALID_HANDLE_VALUE) CloseHandle(hFileUnbuffered); } else while(pDesc = listFiles.RemoveTail()) { CloseHandle(pDesc->hFile); CloseHandle(pDesc->hFileUnbuffered); delete pDesc; } delete pSegmentHint; } /////////////////////////////////////////////////////////////////////////// void AVIReadHandler::Release() { if (!--ref_count) delete this; } void AVIReadHandler::AddRef() { ++ref_count; } IAVIReadStream *AVIReadHandler::GetStream(DWORD fccType, LONG lParam) { if (paf) { PAVISTREAM pas; HRESULT hr; hr = AVIFileGetStream(paf, &pas, fccType, lParam); if (hr) return NULL; return new AVIReadTunnelStream(this, pas, pAvisynthClipInfo); } else { AVIStreamNode *pasn, *pasn_next; int streamno = 0; pasn = listStreams.AtHead(); while(pasn_next = pasn->NextFromHead()) { if (pasn->hdr.fccType == fccType && !lParam--) break; pasn = pasn_next; ++streamno; } if (pasn_next) { return new AVIReadStream(this, pasn, streamno); } return NULL; } } void AVIReadHandler::EnableFastIO(bool f) { fDisableFastIO = !f; } bool AVIReadHandler::isOptimizedForRealtime() { return nRealTime!=0; } bool AVIReadHandler::isStreaming() { return nActiveStreamers!=0; } bool AVIReadHandler::isIndexFabricated() { return fFakeIndex; } bool AVIReadHandler::getSegmentHint(const char **ppszPath) { if (!pSegmentHint) { if (ppszPath) *ppszPath = NULL; return false; } if (ppszPath) *ppszPath = pSegmentHint+1; return !!pSegmentHint[0]; } /////////////////////////////////////////////////////////////////////////// void AVIReadHandler::EnableStreaming(int stream) { if (!fStreamsActive) { if (!(streamBuffer = new char[STREAM_SIZE])) throw MyMemoryError(); i64StreamPosition = -1; sbPosition = sbSize = 0; } fStreamsActive |= (1< >48)) _SelectFile((int)(i64StreamPosition>>48)); if (sbPosition >= sbSize) { if (nRealTime || (((i64StreamPosition&0x0000FFFFFFFFFFFFi64)+sbSize) & -STREAM_BLOCK_SIZE)+STREAM_SIZE > i64Size) { i64StreamPosition += sbSize; sbPosition = 0; _seekFile(i64StreamPosition & 0x0000FFFFFFFFFFFFi64); sbSize = _readFile(streamBuffer, STREAM_RT_SIZE); if (sbSize < 0) { sbSize = 0; throw MyWin32Error("Failure streaming AVI file: %%s.",GetLastError()); } } else { i64StreamPosition += sbSize; sbPosition = i64StreamPosition & (STREAM_BLOCK_SIZE-1); i64StreamPosition &= -STREAM_BLOCK_SIZE; _seekFileUnbuffered(i64StreamPosition & 0x0000FFFFFFFFFFFFi64); sbSize = _readFileUnbuffered(streamBuffer, STREAM_SIZE); if (sbSize < 0) { sbSize = 0; throw MyWin32Error("Failure streaming AVI file: %%s.",GetLastError()); } } } if (sbPosition >= sbSize) return NULL; if (bytes > sbSize - sbPosition) bytes = sbSize - sbPosition; sbPosition += bytes; return streamBuffer + sbPosition - bytes; } bool AVIReadHandler::Stream(AVIStreamNode *pusher, __int64 pos) { bool read_something = false; if (!streamBuffer) return false; if (i64StreamPosition == -1) { i64StreamPosition = pos; sbPosition = 0; } if (pos < i64StreamPosition+sbPosition) return false; // >4Mb past current position!? if (pos > i64StreamPosition+sbPosition+4194304) { // OutputDebugString("Resetting streaming position!\n"); i64StreamPosition = pos; sbSize = sbPosition = 0; } /* if (pusher->hdr.fccType == 'sdiv') OutputDebugString("pushed by video\n"); else OutputDebugString("pushed by audio\n");*/ ++pusher->stream_pushes; pusher->stream_push_pos = pos; while(pos >= i64StreamPosition+sbPosition) { long actual, left; char *src; FOURCC hdr[2]; int stream; // read next header left = 8; while(left > 0) { actual = left; src = _StreamRead(actual); if (!src) return read_something; memcpy((char *)hdr + (8-left), src, actual); left -= actual; } stream = StreamFromFOURCC(hdr[0]); if (isxdigit(hdr[0]&0xff) && isxdigit((hdr[0]>>8)&0xff) && stream<32 && ((1L< NextFromHead()) { if (streamno == stream) { long left = hdr[1] + (hdr[1]&1); bool fWrite = pasn->cache->WriteBegin(i64StreamPosition + sbPosition, left); char *dst; while(left > 0) { actual = left; dst = _StreamRead(actual); if (!dst) { if (fWrite) pasn->cache->WriteEnd(); return read_something; } if (fWrite) pasn->cache->Write(dst, actual); left -= actual; } if (fWrite) pasn->cache->WriteEnd(); read_something = true; break; } pasn = pasn_next; ++streamno; } } else { if (hdr[0] != FOURCC_LIST && hdr[0] != FOURCC_RIFF) { long actual; // skip chunk left = hdr[1] + (hdr[1] & 1); while(left > 0) { actual = left; if (!_StreamRead(actual)) return read_something; left -= actual; } } else { left = 4; while(left > 0) { actual = left; if (!_StreamRead(actual)) return read_something; left -= actual; } } } } return true; } __int64 AVIReadHandler::getStreamPtr() { return i64StreamPosition + sbPosition; } void AVIReadHandler::FixCacheProblems(AVIReadStream *arse) { AVIStreamNode *pasn, *pasn_next; // The simplest fix is simply to disable caching on the stream that's // cache-missing. However, this is a bad idea if the problem is a low-bandwidth // audio stream that's outrunning a high-bandwidth video stream behind it. // Check the streaming leader, and if the streaming leader is comparatively // low bandwidth and running at least 512K ahead of the cache-missing stream, // disable its cache. AVIStreamNode *stream_leader = NULL; int stream_leader_no; int streamno=0; pasn = listStreams.AtHead(); while(pasn_next = pasn->NextFromHead()) { if (pasn->cache) if (!stream_leader || pasn->stream_pushes > stream_leader->stream_pushes) { stream_leader = pasn; stream_leader_no = streamno; } pasn = pasn_next; ++streamno; } if (stream_leader && stream_leader->stream_bytes*2 < arse->psnData->stream_bytes && stream_leader->stream_push_pos >= arse->psnData->stream_push_pos+524288) { #ifdef STREAMING_DEBUG OutputDebugString("caching disabled on fast puny leader\n"); #endif delete stream_leader->cache; stream_leader->cache = NULL; DisableStreaming(stream_leader_no); i64StreamPosition = -1; sbPosition = sbSize = 0; } else { #ifdef STREAMING_DEBUG OutputDebugString("disabling caching at request of client\n"); #endif arse->EndStreaming(); if (arse->psnData == stream_leader) { i64StreamPosition = -1; sbPosition = sbSize = 0; } } // Reset cache statistics on all caches. pasn = listStreams.AtHead(); while(pasn_next = pasn->NextFromHead()) { if (pasn->cache) pasn->cache->ResetStatistics(); pasn = pasn_next; } } long AVIReadHandler::ReadData(int stream, void *buffer, __int64 position, long len) { if (nCurrentFile<0 || nCurrentFile != (int)(position>>48)) _SelectFile((int)(position>>48)); // _RPT3(0,"Reading from file %d, position %I64x, size %d\n", nCurrentFile, position, len); if (!_seekFile2(position & 0x0000FFFFFFFFFFFFi64)) return -1; return _readFile(buffer, len); } void AVIReadHandler::_SelectFile(int file) { AVIFileDesc *pDesc, *pDesc_next; nCurrentFile = file; pDesc = listFiles.AtHead(); while((pDesc_next = pDesc->NextFromHead()) && file--) pDesc = pDesc_next; hFile = pDesc->hFile; hFileUnbuffered = pDesc->hFileUnbuffered; i64Size = pDesc->i64Size; }