#include #define _LARGEFILE64_SOURCE #include #include #include #include "AVIReadHandler2.h" #include "AVIIndex2.h" #include "FastReadStream.h" #include #include #include //#include "ProgressDialog.h" #include "AVIIndex.h" //#include "Error.h" #include #include "List.h" #include using namespace std; //#include "gui.h" #define __MODULE__ "AVIReadHandler2" #ifdef _DEBUG #define DEBUG_DISPLAY_SCAN_OBJECTS //#define DEBUG_DISPLAY_READ_OBJECTS #endif typedef long long __int64; //#define STREAMING_DEBUG // HACK!!!! extern pthread_mutex_t g_diskcs; extern bool g_disklockinited; /////////////////////////////////////////////////////////////////////////// static const char g_szASFCheckWarning[]="ASF validity check warning"; /////////////////////////////////////////////////////////////////////////// // I hate GUIDs. struct GUID { long v1; short v2; short v3; char v4[8]; int operator==(const GUID& guid) const{return !memcmp(this, &guid, sizeof(GUID));} static int LookupChunkType(GUID *pGuid); ostream& operator<<(ostream& e) { return e< min_fr) min_fr = fr; } diff = paie[i+1].pres_time - paie[i].pres_time; // printf("diff: %d ms\n", diff); #ifdef _DEBUG if (diff < 0) printf( "WARNING: Anachronistic presentation time step (%d)!\n", diff); #endif // Ignore 0ms differences... this can be due to grouping. This // means we also ignore the first difference after a zero. if (diff) { if (!last_was_zero) { if (diff <= 500) { ++histo[diff-1]; ++histo_cnt; } } last_was_zero = false; } else last_was_zero = true; } printf("minimum frame rate = %.3f fps\n", min_fr); #if 0 // Pick out the most occurring frame difference. If there's // a tie, pick the lower one. int top_val = 0; int top_histo = histo[0]; for(i=1; i<500; i++) { if (histo[i]>top_histo) { top_histo = histo[i]; top_val = i+1; } } #else // Pick out the first frame difference that occurs for at least // 10% of the video. Ignore any timings below 10ms (100fps). int threshold = len/10; int top_val; for(i=10; i<500; i++) if (histo[i] >= threshold) break; top_val = i; #endif // Rescan the array, summing up differences +/-2 ms from // the mode. __int64 diff_sum = 0; int diff_cnt = 0; for(i=0; i=top_val-2 && diff<=top_val+2) { diff_sum += diff; diff_cnt++; } last_was_zero = false; } else last_was_zero = true; } double msperframe = (double)diff_sum / diff_cnt; printf("estimated ms per frame = %.3f ms (%.3f fps)\n", msperframe, 1000.0 / msperframe); if (1000.0/min_fr < msperframe) msperframe = 1000.0/min_fr; // create new index ASFIndex index2; double ms_low; ms_low = lTimebaseBias-msperframe/2.0; #ifdef _DEBUG double ms_high = ms_low + msperframe; #endif for(i=0; i0 && paie[i].pres_time != paie[i-1].pres_time) { while(paie[i].pres_time > ms_low) { index2.add(paie[i].pos, 0, false, i, paie[i].seq); ms_low += msperframe; #ifdef _DEBUG ms_high += msperframe; #endif } } index2.add(paie[i].pos, paie[i].size&0x7FFFFFFF, !(paie[i].size&0x80000000), i, paie[i].seq); ms_low += msperframe; #ifdef _DEBUG ms_high += msperframe; #endif } index2.makeIndex(); index.Transfer(&index2); fResequenced = true; // Reset stream parameters. hdr.dwRate = 1000000; hdr.dwScale = (DWORD)(0.5 + 1000.0 * msperframe); hdr.dwLength = index.indexLen(); } long MulDiv(long v1, long v2, long v3) { if(v3==0)return 0; long long t=(long long)v1*(long long)v2; return (long)(t/v3); } void ASFStreamNode::AdjustFrameTimingAudio() { ASFIndexEntry *paie = index.indexPtr(); if (paie->pres_time > lTimebaseBias) { const WAVEFORMATEX *pwfex = (const WAVEFORMATEX *)pFormat; // convert ms bias into blocks // // ms / 1000 = sec // (ms / 1000) * avgbytes/sec = bytes // (ms / 1000) * avgbytes/sec / nBlockAlign = blocks lAudioRepeat = MulDiv(paie[0].pres_time - lTimebaseBias, pwfex->nAvgBytesPerSec, pwfex->nBlockAlign*1000); } } void ASFStreamNode::AdjustFrameTiming() { if (hdr.fccType == streamtypeVIDEO) AdjustFrameTimingVideo(); else if (hdr.fccType == streamtypeAUDIO) AdjustFrameTimingAudio(); } /////////////////////////////////////////////////////////////////////////// class AVIReadHandler2 : public IvAVIReadHandler { public: bool fDisableFastIO; bool fQuiet; bool fAcceptPartial; AVIReadHandler2(const char *, bool, bool); ~AVIReadHandler2(); void AddRef(); void Release(); IvAVIReadStream *GetStream(DWORD fccType, LONG lParam); void EnableFastIO(bool); bool isOptimizedForRealtime(); bool isStreaming(); bool isIndexFabricated(); bool AppendFile(const char *pszFile); bool getSegmentHint(const char **ppszPath); HRESULT Header(MainAVIHeader* mh){ #warning FIXME return -1; } long ReadData(int streamno, void *lpBuffer, __int64 pktpos, int offset, long bytes, ASFIndexEntry *paie); private: // enum { STREAM_SIZE = 65536 }; enum { STREAM_SIZE = 1048576 }; enum { STREAM_RT_SIZE = 65536 }; enum { STREAM_BLOCK_SIZE = 4096 }; int ref_count; int hFile, hFileUnbuffered; __int64 i64FilePosition; __int64 i64MovieLimit; long pktsize; void *pktbuf; List listStreams; void _construct(const char *pszFile); void _parseFileRoot(); void _parseFileHeader(__int64 len, __int64 filesize); void _parseStreamHeader(__int64 len); void _parseMovieChunk(__int64 pos, __int64 len); void _destruct(); long _readFile(void *data, long len); void _readFile2(void *data, long len); void _seekFile(__int64 i64NewPos); bool _seekFile2(__int64 i64NewPos); long _readFileUnbuffered(void *data, long len); void _seekFileUnbuffered(__int64 i64NewPos); void _skipFile(__int64 bytes); bool _skipFile2(__int64 bytes); __int64 _posFile(); }; IvAVIReadHandler *CreateAVIReadHandler2(const char *s, bool fQuiet, bool fAcceptPartial) { return new AVIReadHandler2(s, fQuiet, fAcceptPartial); } /////////////////////////////////////////////////////////////////////////// class AVIReadStream2 : public IvAVIReadStream { friend class AVIReadHandler2; public: AVIReadStream2(AVIReadHandler2 *, ASFStreamNode *, int); ~AVIReadStream2(); HRESULT BeginStreaming(long lStart, long lEnd, long lRate); HRESULT EndStreaming(); HRESULT Info(AVISTREAMINFO *pasi, long lSize); bool IsKeyFrame(long lFrame) const; 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); HRESULT FormatSize(long lFrame, long *plSize); HRESULT ReadFormat(long lFrame, void *pFormat, long *plSize); bool isStreaming(); void ClearCache(){return;} HRESULT Header(AVIStreamHeader* ash){ if(ash==0)return -1; AVIStreamHeader& asi=*ash; 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; return 0; } long Length() const {return length;} private: AVIReadHandler2 *parent; ASFStreamNode *psnData; ASFIndexEntry *pIndex; long length; long frames; long sampsize; int streamno; __int64 i64CachedPosition; ASFIndexEntry *pCachedEntry; }; /////////////////////////////////////////////////////////////////////////// AVIReadStream2::AVIReadStream2(AVIReadHandler2 *parent, ASFStreamNode *psnData, int streamno) { this->parent = parent; this->psnData = psnData; this->streamno = streamno; parent->AddRef(); pIndex = psnData->index.indexPtr(); sampsize = psnData->hdr.dwSampleSize; frames = psnData->index.indexLen(); length = psnData->lLength; if (sampsize) { i64CachedPosition = 0; pCachedEntry = pIndex; } } AVIReadStream2::~AVIReadStream2() { parent->Release(); } HRESULT AVIReadStream2::BeginStreaming(long lStart, long lEnd, long lRate) { return 0; } HRESULT AVIReadStream2::EndStreaming() { return 0; } HRESULT AVIReadStream2::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 AVIReadStream2::IsKeyFrame(long lFrame) const { if (sampsize) return true; else { if (lFrame < 0 || lFrame >= length) return false; return !(pIndex[lFrame].size & 0x80000000); } } HRESULT AVIReadStream2::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) { // Check for repeating. if (lStart < psnData->lAudioRepeat) { lStart = 0; lSamples = 1; } else lStart -= psnData->lAudioRepeat; // Check for audio scrambling. if (psnData->lAudioScrambleCount > 1) { // Audio is scrambled (sh*t!). // // ASF1 seems to scramble data differently than described in the // ASF2 specs; it's a simple index reversal. Assuming each // scrambling block is composed of sample[a][b], where // b=0...(scramble_chunk_size-1), the unscrambling method is // simply sample[b][a] (with index dimensions reversed as well, // of course). // // For now, we'll be lazy, and simply decode one sample at a time, // reducing the read to a single index transform. This REALLY // should be changed! long scramble_base; long scramble_offset; int i,j; // Break down sample number into (start of scrambled block) and // (offset within block). scramble_offset = lStart % (psnData->lAudioScrambleCount*psnData->lAudioScrambleChunk); scramble_base = lStart - scramble_offset; // Check to see if this is a partial block and abort descramble if so if (scramble_base + psnData->lAudioScrambleCount*psnData->lAudioScrambleChunk <= psnData->lLength) { // Descramble offset within block. i = scramble_offset / psnData->lAudioScrambleCount; j = scramble_offset % psnData->lAudioScrambleCount; scramble_offset = i + j*psnData->lAudioScrambleChunk; // Combine to new sample number. lStart = scramble_base + scramble_offset; lSamples = 1; } printf("new lStart = %d, length = %d, count = %d\n", lStart, psnData->lLength, lSamples); } // This code works for NGE_01.ASF with SVAL=3, SVAL2=21 // // lSamples = 1; // lStart = ((lStart / SVAL2) % SVAL) + (lStart%SVAL2)*SVAL + (lStart/(SVAL*SVAL2))*SVAL*SVAL2; // Proceed. ASFIndexEntry *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) 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) { if (psnData->fResequenced) actual_bytes = parent->ReadData(streamno, lpBuffer, avie2->pos, (int)byte_off, (long)bytecnt, avie2); else actual_bytes = parent->ReadData(streamno, lpBuffer, avie2->pos, (int)byte_off, (long)bytecnt, avie2); 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; } else { if (plBytes) *plBytes = (long)bytecnt; if (plSamples) *plSamples = lSamples; } } else { ASFIndexEntry *avie2 = &pIndex[lStart]; if (lpBuffer && (avie2->size & 0x7FFFFFFF) > cbBuffer) { return AVIERR_BUFFERTOOSMALL; } if (lpBuffer) { // read data if (psnData->fResequenced) lActual = parent->ReadData(streamno, lpBuffer, avie2->pos, 0, avie2->size & 0x7FFFFFFF, avie2); else lActual = parent->ReadData(streamno, lpBuffer, avie2->pos, 0, avie2->size & 0x7FFFFFFF, avie2); 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; } return 0; } long AVIReadStream2::Start() { return 0; } long AVIReadStream2::End() { return length; } long AVIReadStream2::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 AVIReadStream2::NextKeyFrame(long lFrame) { if (sampsize) return lFrame= length) return -1; while(++lFrame < length) if (!(pIndex[lFrame].size & 0x80000000)) return lFrame; return -1; } long AVIReadStream2::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; } HRESULT AVIReadStream2::FormatSize(long lFrame, long *plSize) { *plSize = psnData->lFormatLen; return 0; } HRESULT AVIReadStream2::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 AVIReadStream2::isStreaming() { return false; } /////////////////////////////////////////////////////////////////////////// AVIReadHandler2::AVIReadHandler2(const char *s, bool _fQuiet, bool _fAcceptPartial) : fQuiet (_fQuiet) , fAcceptPartial(_fAcceptPartial) { if (!g_disklockinited) { g_disklockinited=true; pthread_mutex_init(&g_diskcs, 0); } _construct(s); } AVIReadHandler2::~AVIReadHandler2() { _destruct(); } void AVIReadHandler2::_construct(const char *pszFile) { hFile = -1; hFileUnbuffered = -1; pktbuf = NULL; ref_count = 1; try { // open file // hFile = CreateFile(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); hFile=open(pszFile, O_RDONLY); // if (INVALID_HANDLE_VALUE == hFile) if(hFile<0) throw FATAL("Couldn't open file"); i64FilePosition = 0; hFileUnbuffered=open(pszFile, O_RDONLY); // recursively parse file _parseFileRoot(); } catch(...) { _destruct(); throw; } } void AVIReadHandler2::_parseFileRoot() { struct { GUID guid; __int64 len; } hdr; __int64 i64MoviePos=0, i64MovieLen; __int64 filesize = 0; // Can we get the file size? { DWORD dwLow, dwHigh; // dwLow = GetFileSize(hFile, &dwHigh); long pos, size; pos=lseek(hFile, 0, SEEK_CUR); size=lseek(hFile, 0, SEEK_END); lseek(hFile, pos, SEEK_SET); dwLow=size; dwHigh=0; if (GetLastError() == NO_ERROR) filesize = ((__int64)dwHigh << 32) | (unsigned long)dwLow; } pktsize = 0; while(_readFile(&hdr, 24)>0 && hdr.len) { int cktype = GUID::LookupChunkType(&hdr.guid); #ifdef _DEBUG char msg[256]; cerr << hex << _posFile << ": " << hdr.guid << "," hdr.len << "bytes ( chunk type" << dec << hdr.guid.LookupChunkType() << ")" << endl; /* sprintf(msg, "%16Lx: %08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x %Ld bytes (chunk type %d)\n", _posFile(), hdr.guid.Data1, hdr.guid.Data2, hdr.guid.Data3, hdr.guid.Data4[0], hdr.guid.Data4[1], hdr.guid.Data4[2], hdr.guid.Data4[3], hdr.guid.Data4[4], hdr.guid.Data4[5], hdr.guid.Data4[6], hdr.guid.Data4[7], hdr.len, LookupChunkType(&hdr.guid) ); printf(msg); */ #endif // Is this some ridiculous chunk information, or a chunk that's incomplete? if (hdr.len < 0){ break; } if (filesize && hdr.len+_posFile()-24>filesize){ printf("ASF Warning: truncated data chunk\n"); hdr.len = filesize - _posFile() + 24; } // Parse the chunk. switch(cktype) { case CHUNK_HEADER2: throw FATAL("ASF v2.0 (public spec 1.0) files are not supported"); case CHUNK_HEADER: _readFile(&hdr, 6); break; case CHUNK_HEADER_STREAMHDR: _parseStreamHeader(hdr.len - 24); break; case CHUNK_HEADER_FILEHDR: _parseFileHeader(hdr.len - 24, filesize); break; case CHUNK_MOVIE: if (!pktsize) throw FATAL("File header not found"); i64MoviePos = _posFile(); i64MovieLen = hdr.len - 24; default: _seekFile(_posFile() + hdr.len - 24); break; } } if (!i64MoviePos) throw FATAL("Data chunk not found"); i64MovieLimit = i64MoviePos + i64MovieLen; // Allocate packet buffer pktbuf = new char[pktsize + 16]; _parseMovieChunk(i64MoviePos, i64MovieLen); // Glue all indices together and deduce frame speeds { ASFStreamNode *pasn, *pasn_next; long lEarliestTime = 0x7FFFFFFF; pasn = (ASFStreamNode *)listStreams.AtHead(); while(pasn_next = (ASFStreamNode *)pasn->NextFromHead()) { if (!pasn->index.makeIndex()) throw MEMORY_FATAL; pasn->lLength = pasn->index.indexLen(); ASFIndexEntry *avie2 = pasn->index.indexPtr(); pasn->bytes = 0; for(int i=pasn->index.indexLen()-1; i>=0; i--) pasn->bytes += avie2[i].size & 0x7FFFFFFF; if (pasn->hdr.dwSampleSize) pasn->hdr.dwLength = (DWORD)(pasn->bytes / pasn->hdr.dwSampleSize); else pasn->hdr.dwLength = pasn->index.indexLen(); if (pasn->index.indexPtr()[0].pres_time < lEarliestTime) lEarliestTime = pasn->index.indexPtr()[0].pres_time; pasn = pasn_next; } pasn = (ASFStreamNode *)listStreams.AtHead(); while(pasn_next = (ASFStreamNode *)pasn->NextFromHead()) { pasn->lTimebaseBias = lEarliestTime; pasn->AdjustFrameTiming(); if (pasn->hdr.dwSampleSize) { pasn->lLength = (long)(pasn->bytes / pasn->hdr.dwSampleSize); } else pasn->lLength = pasn->index.indexLen(); pasn = pasn_next; } } } void AVIReadHandler2::_parseFileHeader(__int64 len, __int64 filesize) { struct { GUID uid; // generated by client computer __int64 file_size; // total size of ASF file FILETIME file_time; // time of creation __int64 x2; __int64 stream_len[2]; // length of streams in microseconds long x7; long x8; long x9; long pktsize; // size of a data packet long x10; long frame_size; // uncompressed frame size } hdr; if (len > sizeof hdr) { char string[100]; sprintf(string, "ASF: file header too long (%d > %d)", len, sizeof hdr); throw FATAL(string); } _readFile2(&hdr, (long)len); pktsize = hdr.pktsize; // Check file size and warn user if appropriate if (!fQuiet) { if (filesize && filesize != hdr.file_size) { /* if (IDYES != guiMessageBoxF(NULL, g_szASFCheckWarning, MB_YESNO, "The ASF file is %Ld bytes long, but was originally %Ld bytes when encoded. " "It may have been truncated or corrupted. Open file anyway?" ,filesize ,hdr.file_size )) throw MyUserAbortError(); */ fAcceptPartial = true; } } } void AVIReadHandler2::_parseStreamHeader(__int64 len) { #pragma pack(1) union { char buf[1024]; struct { GUID uid_type; GUID uid_conceal; // AUDIO: describes concealment mode __int64 x0; long data_len; long edata_len; // follows data short stream; // stream number (1, 2, ...) long x2; // data starts at this point; union { struct { long width; long height; char c; short biSize; BITMAPINFOHEADER bih; } vid; struct { WAVEFORMATEX wfex; // audio scramble data follows } aud; }; } hdr; }; struct ASFAudioScrambleDef { unsigned char block_size; // total number of audio blocks in each scramble group unsigned short chunk_size; // byte size of each scrambling chunk unsigned short block_align_1; // usually = nBlockAlign unsigned short block_align_2; // usually = nBlockAlign } *pScrambleDef; // Read in the stream chunk if (len > sizeof buf) throw FATAL("ASF: stream header is too long"); _readFile2(&hdr, (long)len); // is it a type we know? ASFStreamNode *pasn; if (!(pasn = new ASFStreamNode())) throw MEMORY_FATAL; memset(&pasn->hdr, 0, sizeof(AVISTREAMINFO)); pasn->hdr.dwQuality = -1; pasn->nSeq = 1; if (hdr.uid_type == guid_audio_stream) { // _RPT0(0,"ASF: audio stream detected\n"); pasn->hdr.fccType = streamtypeAUDIO; pasn->hdr.fccHandler = 0; pasn->hdr.dwRate = hdr.aud.wfex.nAvgBytesPerSec; pasn->hdr.dwScale = hdr.aud.wfex.nBlockAlign; pasn->hdr.dwSampleSize = hdr.aud.wfex.nBlockAlign; // If it's Microsoft Audio Codec, manually patch the format if (hdr.aud.wfex.wFormatTag == 0x161) { // Windows Media Audio v2 pasn->lFormatLen = sizeof(WAVEFORMATEX)+47; pasn->pFormat = new char[sizeof(WAVEFORMATEX)+47]; memcpy(pasn->pFormat, &hdr.aud.wfex, sizeof(WAVEFORMATEX)+10); strcpy((char *)pasn->pFormat + 0x1c, "1A0F78F0-EC8A-11d2-BBBE-006008320064"); ((WAVEFORMATEX *)pasn->pFormat)->cbSize = 0x2f; } else if (hdr.aud.wfex.wFormatTag == 0x160) { // Windows Media Audio v1 pasn->lFormatLen = sizeof(WAVEFORMATEX)+41; pasn->pFormat = new char[sizeof(WAVEFORMATEX)+41]; memcpy(pasn->pFormat, &hdr.aud.wfex, sizeof(WAVEFORMATEX)+4); strcpy((char *)pasn->pFormat + 0x16, "1A0F78F0-EC8A-11d2-BBBE-006008320064"); ((WAVEFORMATEX *)pasn->pFormat)->cbSize = 0x29; } else { pasn->lFormatLen = hdr.data_len; if (!(pasn->pFormat = new char[pasn->lFormatLen])) throw MEMORY_FATAL; memcpy(pasn->pFormat, &hdr.aud.wfex, pasn->lFormatLen); } // Find the audio descrambling data, and store it. pScrambleDef = (ASFAudioScrambleDef *)((char *)&hdr.aud.wfex + hdr.data_len); if (hdr.uid_conceal == guid_audio_conceal_interleave) { pasn->lAudioScrambleCount = pScrambleDef->block_size; pasn->lAudioScrambleChunk = pScrambleDef->chunk_size / pScrambleDef->block_align_1; } } else if (hdr.uid_type == guid_video_stream) { printf("ASF: video stream detected\n"); pasn->hdr.fccType = streamtypeVIDEO; pasn->hdr.fccHandler = hdr.vid.bih.biCompression; pasn->hdr.dwRate = 15; pasn->hdr.dwScale = 1; pasn->hdr.dwSampleSize = 0; pasn->lFormatLen = hdr.vid.biSize; if (!(pasn->pFormat = new char[pasn->lFormatLen])) throw MEMORY_FATAL; memcpy(pasn->pFormat, &hdr.vid.bih, pasn->lFormatLen); } else { printf("ASF Warning: unknown stream detected\n"); delete pasn; return; } listStreams.AddTail(pasn); } void AVIReadHandler2::_parseMovieChunk(__int64 pos, __int64 len) { char buf[26]; // ProgressDialog pd(NULL, "ASF Import Filter", "Reconstructing ASF index block", (long)((len+1023)/1024), true); long last_objlen = 0; long objlens[128]; // pd.setValueFormat("%ldK of %ldK"); _seekFile(pos); _readFile2(buf, 26); len -= 26; while(len >= pktsize) { int pktleft; unsigned char *pp = (unsigned char *)pktbuf; unsigned long packet_send_time; char pktflags; int padsize = 0; int segments_expected = 1; int segment_count=0; bool fSY = false; #ifdef DEBUG_DISPLAY_SCAN_OBJECTS printf("processing packet at %8Lx\n", _posFile()); #endif _readFile2(pp, pktsize); len -= pktsize; if (*(short *)pp != 'YS') { // packet header type 1: // // byte 0x82 (?) // word always zero (?) // byte flags // 0x10 16-bit padding size specified // 0x08 8-bit padding size specified // 0x01 multiple packets exist // byte 0x5d (?) // // [word] [0x10] end of packet padding size // [byte] [0x08] end of packet padding size // // long send time for this packet (ms) // word duration of this packet (ms) // [byte] [0x01] number of segments in this packet + 0x80 pktflags = pp[3]; pp+=5; if (pktflags & 0x40) { padsize += pktsize - *(unsigned short*)pp; pp+=2; } if (pktflags & 0x10) { // 0x10: padding flag padsize += *(unsigned short *)pp; pp+=2; } if (pktflags & 0x08) { padsize += *pp++; } packet_send_time = *(long *)pp; pp+=6; if (pktflags & 0x01) { segments_expected = *pp++ & 0x7f; } } else { // packet header type 2: // // word "SY" // word unknown // byte unknown // byte sequence number // word pad length // long unknown (send time?) // word unknown // byte segments + 0x80 pktflags = 0x01; packet_send_time = *(long *)(pp + 7); segments_expected = pp[13] - 0x80; padsize = *(short *)(pp + 5); pp += 14; fSY = true; } pktleft = pktsize - (pp - (unsigned char *)pktbuf) - padsize; // parse down segments/subpackets/whatever... while(pktleft > 6) { int frag_start; int objlen; bool is_key = !!(pp[0] & 0x80); int stream = pp[0] & 0x7f; int seq = pp[1]; int fraglen; char flags; long pres_time; long first_value; if (++segment_count > segments_expected) if (fAcceptPartial) return; else { char buf[64]="buf"; // ticks_to_str(buf, packet_send_time); throw FATAL( "ASF: parse error -- too many segments in packet"); // "ASF: parse error -- too many segments in packet at %Lx (%d expected)\n" // "packet size = %lx, send time: %s" // , _posFile()-pktsize, segment_count, pktsize, buf); } // if flags == 0x08: // +0 byte stream number (hi bit = keyframe) // +1 byte sequence number // +2 long fragment offset (word for SY format) // +6 byte flag // +7 long object length // +11 long object time (ms) // +15 [word] fragment length // // if flags == 0x01 (grouping): // +0 byte stream number (hi bit = keyframe) // +1 byte sequence number // +2 long object time (ms) // +6 byte flag // +7 byte (unknown) // +8 word [block length] // repeat: // +0 byte object length // +1 bytes object // +n byte [object length]... // // if flags == 0x00 (SY): // +0 byte stream number (hi bit = keyframe) // +1 byte sequence number // +2 word fragment offset // +4 byte flag // +5 word fragment length if (fSY) { first_value = *(short *)(pp + 2); pp += 4; pktleft -= 4; } else { first_value = *(long *)(pp + 2); pp += 6; pktleft -= 6; } flags = pp[0]; if (flags == 0x01) { unsigned char *pplimit; pres_time = first_value; frag_start = 0; if (pktflags & 0x01) { // or absence of 0x10? fraglen = *(unsigned short *)(pp+2); pp+=4; pktleft -= 4+fraglen; } else { fraglen = pktleft-2; pp+=2; pktleft = 0; } pplimit = pp + fraglen; while(pp < pplimit) { objlen = *pp++; pp += objlen; #ifdef DEBUG_DISPLAY_SCAN_OBJECTS { char mbuf[128]; sprintf(mbuf,"stream %d/seq %02x (%c) - len %04x \n" ,stream ,seq ,is_key ? 'K' : '.' ,objlen ,pplimit-pp ); printf(mbuf); Sleep(50); } #endif if (stream) { ASFStreamNode *pasn = (ASFStreamNode *)listStreams.AtHead(), *pasn_next; int i = stream; while((pasn_next = (ASFStreamNode *)pasn->NextFromHead()) && --i) pasn = pasn_next; if (!i && pasn_next) { signed char diff = seq - pasn->nSeq; pasn->nSeq = seq + 1; // pasn->index.add(fcc + (stream<<8), _posFile()-pktsize, objlen, is_key); pasn->index.add(_posFile()-pktsize, objlen, is_key, pres_time, seq); } } ++seq; } } else if (flags == 0x08 || flags == 0x00) { if (flags == 0x00) { objlen = objlens[stream]; frag_start = first_value; pres_time = 0; fraglen = *(short *)(pp + 1); pp += 3; pktleft -= 3; } else { frag_start = first_value; objlen = *(long *)(pp+1); pres_time = *(long *)(pp+5); objlens[stream] = objlen; pp+=9; pktleft -= 9; fraglen = pktleft; // The real tricky part. Is there a length field!? // // There is no length field if flag 0x08 is set. if (pktflags & 0x01) { fraglen = *(unsigned short *)pp; pp+=2; pktleft-=2; } } pp += fraglen; pktleft -= fraglen; #ifdef DEBUG_DISPLAY_SCAN_OBJECTS { char mbuf[128]; sprintf(mbuf,"stream %d/seq %02x (%c) - len %04x" ,stream ,seq ,is_key ? 'K' : '.' ,objlen ); if (frag_start || fraglenNextFromHead()) && --i) pasn = pasn_next; if (!i && pasn_next) { signed char diff = seq - pasn->nSeq; pasn->nSeq = seq+1; // pasn->index.add(fcc + (stream<<8), _posFile()-pktsize, objlen, is_key); pasn->index.add(_posFile()-pktsize, objlen, is_key, pres_time, seq); } } } else { if (fAcceptPartial) return; else { char buf[64]="buf"; // ticks_to_str(buf, packet_send_time); throw FATAL( "ASF: illegal object fragment flags" ); // "ASF: illegal object fragment flags %02x in packet at %Lx\n" // "packet size = %lx, pp = %lx, send time: %s" // , (unsigned char)flags, _posFile()-pktsize, pktsize, pp-(unsigned char *)pktbuf, buf); } } } if (segment_count < segments_expected) { char buf[64]="qwe"; // ticks_to_str(buf, packet_send_time); throw FATAL( "ASF: parse error -- too few segments in packet"); // "ASF: parse error -- too few segments in packet at %Lx (%d expected, %d found)\n" // "packet size = %lx, send time: %s" // , _posFile()-pktsize, segments_expected, segment_count, pktsize, buf); } // pd.advance((long)((_posFile() - pos)/1024)); // pd.check(); } } void AVIReadHandler2::_destruct() { ASFStreamNode *pasn; while(pasn = (ASFStreamNode *)listStreams.RemoveTail()) delete pasn; if (hFile != -1) ::close(hFile); if (hFileUnbuffered != -1) ::close(hFileUnbuffered); delete (char*)pktbuf; pktbuf = NULL; } /////////////////////////////////////////////////////////////////////////// void AVIReadHandler2::Release() { if (!--ref_count) delete this; } void AVIReadHandler2::AddRef() { ++ref_count; } IvAVIReadStream *AVIReadHandler2::GetStream(DWORD fccType, LONG lParam) { ASFStreamNode *pasn, *pasn_next; int streamno = 0; pasn = (ASFStreamNode *)listStreams.AtHead(); while(pasn_next = (ASFStreamNode *)pasn->NextFromHead()) { if (pasn->hdr.fccType == fccType && !lParam--) break; pasn = pasn_next; ++streamno; } if (pasn_next) { return new AVIReadStream2(this, pasn, streamno); } return NULL; } void AVIReadHandler2::EnableFastIO(bool f) { fDisableFastIO = !f; } bool AVIReadHandler2::isOptimizedForRealtime() { return false; } bool AVIReadHandler2::isStreaming() { return false; } bool AVIReadHandler2::isIndexFabricated() { return false; } bool AVIReadHandler2::AppendFile(const char *pszFile) { return false; } bool AVIReadHandler2::getSegmentHint(const char **ppszPath) { if (ppszPath) *ppszPath = NULL; return false; } /////////////////////////////////////////////////////////////////////////// long AVIReadHandler2::ReadData(int streamno, void *lpBuffer, __int64 pktpos, int offset, long bytes, ASFIndexEntry *paie) { unsigned char current_seq = paie++->seq; long fragment_point = 0; char *lpData = (char *)lpBuffer; #ifdef DEBUG_DISPLAY_READ_OBJECTS printf("Read request: stream %d, start packet at %Lx, %d bytes\n", streamno+1, pktpos, bytes); #endif if (!bytes) return 0; _seekFile(pktpos); ++streamno; // printf("start>> %d, %Lx, %ld\n", bytes, _posFile(), offset); while(bytes > 0 && _posFile() < i64MovieLimit) { int pktleft; unsigned char *pp = (unsigned char *)pktbuf; char pktflags; int padsize = 0; #ifdef DEBUG_DISPLAY_READ_OBJECTS printf("processing packet at %8Lx\n", _posFile()); #endif _readFile2(pktbuf, pktsize); pktflags = pp[3]; pp+=5; if (pktflags & 0x10) { // 0x10: padding flag padsize += *(unsigned short *)pp; pp+=2; } if (pktflags & 0x08) padsize += *pp++; pp+=6; if (pktflags & 0x01) ++pp; pktleft = pktsize - (pp - (unsigned char *)pktbuf) - padsize; // parse down subpackets while(pktleft > 10 && bytes>0) { int frag_start; int objlen; bool is_key = !!(pp[0] & 0x80); int stream = pp[0] & 0x7f; int seq = pp[1]; int fraglen; char flags = pp[6]; unsigned char *ppData; if (flags == 0x01) { // Since this is a grouped set, if we are still in the middle of fragment // assembly we've missed a fragment somewhere. if (pktflags & 0x01) { // either that or absence of 0x10 fraglen = *(unsigned short *)(pp+8); frag_start = 0; pp+=10; pktleft -= 10; } else { fraglen = pktleft - 8; frag_start = 0; pp+=8; pktleft -= 8; } ppData = pp; pp += fraglen; pktleft -= fraglen; if (stream == streamno) { if (fragment_point) throw FATAL("missing fragment"); // throw FATAL("ASF: missing fragment (group, fragpt=%lx, current_seq=%x, pkt=%Lx)", fragment_point, current_seq, _posFile()-pktsize); while(ppData < pp && bytes>0) { objlen = *ppData++; #ifdef DEBUG_DISPLAY_READ_OBJECTS { char mbuf[128]; sprintf(mbuf,"stream %d/seq %02x (%c) - len %04x \n" ,stream ,seq ,is_key ? 'K' : '.' ,objlen ); printf(mbuf); } #endif if ((signed char)(seq - current_seq) > 0) { throw FATAL("missing fragment"); // throw FATAL("ASF: missing fragment (seq=%02x, expecting %02x)", seq, current_seq); } else if (seq == current_seq) { int frag_off = 0; int tc; current_seq = paie++->seq; // handle the object if (offset > 0) { if (offset >= objlen) { offset -= objlen; goto object_swallowed; } frag_off = offset; offset = 0; } tc = objlen - offset; if (tc > bytes) tc = bytes; memcpy(lpData, ppData + offset, tc); lpData += tc; bytes -= tc; } object_swallowed: ++seq; ppData+=objlen; } } #ifdef DEBUG_DISPLAY_READ_OBJECTS else { char mbuf[128]; sprintf(mbuf,"stream %d/seq %02x (%c) - len %04x \n" ,stream ,seq ,is_key ? 'K' : '.' ,fraglen ); printf(mbuf); } #endif } else if (flags == 0x08) { objlen = *(long *)(pp+7); frag_start = *(long *)(pp+2); pp+=15; pktleft -= 15; fraglen = pktleft; if (pktflags & 0x01) { fraglen = *(unsigned short *)pp; pp+=2; pktleft-=2; } ppData = pp; pp += fraglen; pktleft -= fraglen; #ifdef DEBUG_DISPLAY_READ_OBJECTS { char mbuf[128]; sprintf(mbuf,"stream %d/seq %02x (%c) - len %04x" ,stream ,seq ,is_key ? 'K' : '.' ,objlen ); if (frag_start || fraglen00 rollover. // // Watch out, #'s may not be sequential; EVA2.ASF is missing the 0x65 // sequence at 0x39796-0x39f16... if ((signed char)(seq - current_seq) > 0) { throw FATAL("missing fragment"); // throw FATAL("ASF: missing fragment (seq=%02x, expecting %02x, pkt=%Lx)", seq, current_seq, _posFile()-pktsize); } /* if ((signed char)(seq - current_seq) > 0) { current_seq = seq; printf("WARNING: sequence jump!\n"); }*/ if (seq == current_seq) { int frag_off = 0; int tc; // If we are reassembling fragments, make sure we haven't missed one. if (frag_start != fragment_point) throw FATAL("fragment reassembly error"); // throw FATAL("ASF: fragment reassembly error (fragpt=%08lx, should be %08lx)", frag_start, fragment_point); // Update fragment point. fragment_point += fraglen; if (fragment_point >= objlen) { fragment_point = 0; current_seq = paie++->seq; } // handle the fragment. if (offset > 0) { if (offset >= fraglen) { offset -= fraglen; continue; } frag_off = offset; offset = 0; } tc = fraglen - frag_off; if (tc > bytes) tc = bytes; memcpy(lpData, ppData + frag_off, tc); lpData += tc; bytes -= tc; } } } else throw FATAL("illegal object fragment"); // throw FATAL("ASF: illegal object fragment flags %02x in packet at %Llx", (unsigned char)flags, _posFile()-pktsize); } } // printf("end>> %d, %Lx, %ld\n", bytes, _posFile(), offset); return lpData - (char *)lpBuffer; } /////////////////////////////////////////////////////////////////////////// long AVIReadHandler2::_readFile(void *data, long len) { DWORD dwActual; #ifdef STREAMING_DEBUG OutputDebugString("readfile()\n"); #endif if (!ReadFile(hFile, data, len, &dwActual, NULL)) return -1; i64FilePosition += dwActual; return (long)dwActual; } void AVIReadHandler2::_readFile2(void *data, long len) { long lActual = _readFile(data, len); if (lActual < 0) throw FATAL("Failure reading file"); if (lActual != len) throw FATAL("Failure reading file: Unexpected end of file"); } void AVIReadHandler2::_seekFile(__int64 i64NewPos) { LONG lHi = (LONG)(i64NewPos>>32); DWORD dwError; #ifndef QUIET // printf("Seeking to %Ld\n", i64NewPos); #endif if (0xFFFFFFFF == SetFilePointer(hFile, (LONG)i64NewPos, &lHi, FILE_BEGIN)) if ((dwError = GetLastError()) != NO_ERROR) throw FATAL("AVIReadHandler2: seek error"); i64FilePosition = i64NewPos; } bool AVIReadHandler2::_seekFile2(__int64 i64NewPos) { LONG lHi = (LONG)(i64NewPos>>32); DWORD dwError; printf("Seeking to %Ld\n", i64NewPos); if (0xFFFFFFFF == SetFilePointer(hFile, (LONG)i64NewPos, &lHi, FILE_BEGIN)) if ((dwError = GetLastError()) != NO_ERROR) return false; i64FilePosition = i64NewPos; return true; } void AVIReadHandler2::_skipFile(__int64 bytes) { LONG lHi = (LONG)(bytes>>32); DWORD dwError; LONG lNewLow; if (0xFFFFFFFF == (lNewLow = SetFilePointer(hFile, (LONG)bytes, &lHi, FILE_CURRENT))) if ((dwError = GetLastError()) != NO_ERROR) throw FATAL("AVIReadHandler2: seek error"); i64FilePosition = (unsigned long)lNewLow | (((__int64)(unsigned long)lHi)<<32); } bool AVIReadHandler2::_skipFile2(__int64 bytes) { LONG lHi = (LONG)(bytes>>32); DWORD dwError; LONG lNewLow; if (0xFFFFFFFF == (lNewLow = SetFilePointer(hFile, (LONG)bytes, &lHi, FILE_CURRENT))) if ((dwError = GetLastError()) != NO_ERROR) return false; i64FilePosition = (unsigned long)lNewLow | (((__int64)(unsigned long)lHi)<<32); return true; } long AVIReadHandler2::_readFileUnbuffered(void *data, long len) { DWORD dwActual; pthread_mutex_lock(&g_diskcs); if (!ReadFile(hFileUnbuffered, data, len, &dwActual, NULL)) { pthread_mutex_unlock(&g_diskcs); return -1; } pthread_mutex_unlock(&g_diskcs); return (long)dwActual; } void AVIReadHandler2::_seekFileUnbuffered(__int64 i64NewPos) { LONG lHi = (LONG)(i64NewPos>>32); DWORD dwError; #ifdef STREAMING_DEBUG { char buf[100]; sprintf(buf, "Seeking to %16Lx\n", i64NewPos); OutputDebugString(buf); } #endif printf("Seeking to %Ld\n", i64NewPos); if (0xFFFFFFFF == SetFilePointer(hFileUnbuffered, (LONG)i64NewPos, &lHi, FILE_BEGIN)) if ((dwError = GetLastError()) != NO_ERROR) throw FATAL("AVIReadHandler2: seek error"); } __int64 AVIReadHandler2::_posFile() { return i64FilePosition; }