/**************************************************************************** * * AVIFILE.C * * routines for reading Standard AVI files * * Copyright (c) 1992 - 1995 Microsoft Corporation. All Rights Reserved. * * You have a royalty-free right to use, modify, reproduce and * distribute the Sample Files (and/or any modified version) in * any way you find useful, provided that you agree that * Microsoft has no warranty obligations or liability for any * Sample Application Files which are modified. * ***************************************************************************/ #include #include #include #include "avifilei.h" #include "avifile.rc" #include #include "debug.h" #if !defined NUMELMS #define NUMELMS(aa) (sizeof(aa)/sizeof((aa)[0])) #endif #ifdef _WIN32 // We need a structure to read in the AVIStreamHeader from persistent // storage. This structure contains a RECT element, which in 16 bit land // contains 4 16-bit values. Hence when 16 bit AVIFILE.DLL writes the // file out, it uses a RECT composed of 4 16-bit values. On WIN32 we have // to map this "short rect" to a WIN32 RECT of 4 32-bit elements. In order // that 16 bit code can continue to read/write files interchangeably with // 32 bit code we map the two structures on read/write. // The AVIStreamHeader structure exists on disk. Hence the RECT struct // is the same size for all systems, i.e. SHORT, 16 bit values. The code // that reads/writes files, and returns INFO will map these 16 bit values // to the 32 bit RECT structure as appropriate. typedef struct tagSRECT { SHORT left; SHORT top; SHORT right; SHORT bottom; } SRECT; typedef struct { FOURCC fccType; FOURCC fccHandler; DWORD dwFlags; /* Contains AVITF_* flags */ WORD wPriority; WORD wLanguage; DWORD dwInitialFrames; DWORD dwScale; DWORD dwRate; /* dwRate / dwScale == samples/second */ DWORD dwStart; DWORD dwLength; /* In units above... */ // new.... DWORD dwSuggestedBufferSize; DWORD dwQuality; DWORD dwSampleSize; SRECT rcFrame; /* does each frame need this? */ /* additional type-specific data goes in StreamInfo chunk */ /* For video: position within rectangle... */ /* For audio: volume? stereo channel? */ } AVIStreamHeaderShort; #else // Map one name to the other for 16 bit code... #define AVIStreamHeaderShort AVIStreamHeader #endif #if defined _WIN32 && !defined UNICODE // // This is Win 95 code. Explicit unicode stuff is needed. // int LoadUnicodeString(HINSTANCE hinst, UINT wID, LPWSTR lpBuffer, int cchBuffer) { char ach[256]; int i; i = LoadString(hinst, wID, ach, NUMELMS(ach)); if (i > 0) MultiByteToWideChar(CP_ACP, 0, ach, -1, lpBuffer, cchBuffer); return i; } #define lstrlenW lstrlenUnicode int WINAPI lstrlenUnicode( LPCWSTR lpString ) { int count = 0; while (*lpString++) count++; return count; } #else #define LoadUnicodeString LoadString #endif extern "C" { LPSTR FAR lstrzcpyA (LPSTR pszTarget, LPCSTR pszSource, size_t cchMax) { lstrcpynA (pszTarget, pszSource, cchMax -1); pszTarget[ cchMax -1 ] = TEXT('\0'); return pszTarget; } LPWSTR FAR lstrzcpyW (LPWSTR pszTarget, LPCWSTR pszSource, size_t cchMax) { lstrcpynW (pszTarget, pszSource, cchMax -1); pszTarget[ cchMax -1 ] = TEXT('\0'); return pszTarget; } LPTSTR FAR lstrzcpy (LPTSTR pszTarget, LPCTSTR pszSource, size_t cchMax) { lstrcpyn (pszTarget, pszSource, cchMax -1); pszTarget[ cchMax -1 ] = TEXT('\0'); return pszTarget; } LPWSTR FAR lstrzcpyAtoW (LPWSTR pszTarget, LPCSTR pszSourceA, size_t cchMax) { LPWSTR pszSourceW; pszSourceW = (LPWSTR)GlobalAllocPtr(GMEM_MOVEABLE,sizeof(WCHAR)*cchMax); if (pszSourceW != NULL) { mbstowcs(pszSourceW, pszSourceA, cchMax); lstrcpynW (pszTarget, pszSourceW, cchMax -1); pszTarget[ cchMax -1 ] = TEXT('\0'); GlobalFreePtr(pszSourceW); } return pszTarget; } LPSTR FAR lstrzcpyWtoA (LPSTR pszTarget, LPCWSTR pszSourceW, size_t cchMax) { LPSTR pszSourceA; pszSourceA = (LPSTR)GlobalAllocPtr (GMEM_MOVEABLE, cchMax); if (pszSourceA != NULL) { wcstombs(pszSourceA, pszSourceW, cchMax); lstrcpynA (pszTarget, pszSourceA, cchMax -1); pszTarget[ cchMax -1 ] = TEXT('\0'); GlobalFreePtr(pszSourceA); } return pszTarget; } } // extern "C" extern "C" STDAPI CalculateFileDataRate(PAVIFILE pf, LONG FAR *plMaxBytesPerSec); //#undef StreamFromFOURCC //#define StreamFromFOURCC(fcc) (UINT)(HIBYTE(LOWORD(fcc)) - (BYTE)'0') BOOL AddToIndex(CAVIFile FAR * pfile, DWORD ckid, DWORD cksize, LONG off, DWORD dwFlags); /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// EXTERN_C void DecodeRle(LPBITMAPINFOHEADER lpbi, BYTE _huge *pb, BYTE _huge *prle, DWORD dwInSize); EXTERN_C HINSTANCE ghMod; /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// #define comptypeNONE mmioFOURCC('N','O','N','E') #define comptypeRLE0 mmioFOURCC('R','L','E','0') #define comptypeRLE mmioFOURCC('R','L','E',' ') #define WIDTHBYTES(i) ((UINT)((i+31)&(~31))/8) #define DIBWIDTHBYTES(lpbi) (UINT)WIDTHBYTES((UINT)(lpbi)->biWidth * (UINT)(lpbi)->biBitCount) LONG lBufferSize = 0; int nBuffers = 0; #define ckidSTREAMNAME mmioFOURCC('s', 't', 'r', 'n') /*************************************************************************** ***************************************************************************/ /*************************************************************************** ***************************************************************************/ EXTERN_C LONG FAR PASCAL shfileReadProc(HANDLE hsf, LONG lSeek, LONG lRead, LPVOID lpBuffer) { if (shfileSeek((HSHFILE)hsf, lSeek, SEEK_SET) == -1) return -1; if (shfileRead((HSHFILE)hsf, (HPSTR)lpBuffer, lRead) != lRead) return -1; return lRead; } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// #define INDEX_WRITE_SIZE 32l*1024 #define INDEX_READ_SIZE 32l*1024 static BOOL WriteOutIndex(CAVIFile FAR *pfile, DWORD dwOffsetMovie) { MMCKINFO ck; PAVIINDEXENTRY pIndex; LONG cnt = INDEX_WRITE_SIZE / sizeof(*pIndex); LONG l; BOOL f=FALSE; #ifdef DEBUG DWORD time; #endif /* ** Now write index out! */ ck.ckid = ckidAVINEWINDEX; ck.cksize = sizeof(AVIINDEXENTRY) * pfile->px->nIndex; if (shfileCreateChunk(pfile->hshfile, &ck, 0)) return FALSE; DPF("Writing Index", time=timeGetTime()); pIndex = (PAVIINDEXENTRY)GlobalAllocPtr(GHND,INDEX_WRITE_SIZE); if (pIndex) { for (l=0; l < pfile->px->nIndex; ) { cnt = IndexGetFileIndex(pfile->px, l, cnt, pIndex, -(LONG)dwOffsetMovie); if (cnt == 0) break; //l = cnt * sizeof(AVIINDEXENTRY); if (shfileWrite(pfile->hshfile, (HPSTR)pIndex, cnt * sizeof(AVIINDEXENTRY)) != cnt * (LONG)sizeof(AVIINDEXENTRY)) goto exit; l += cnt; DPF("!."); } DPF("!Done (%ldms)\n", timeGetTime()-time); if (!shfileAscend(pfile->hshfile, &ck, 0)) f = TRUE; exit: GlobalFreePtr(pIndex); } return f; } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// static BOOL ReadInIndex(CAVIFile FAR *pfile, DWORD size, DWORD dwOffsetMovie, BOOL fRle) { PAVIINDEXENTRY pIndex; LONG cnt; LONG lIndexAdjust; BOOL f = FALSE; #ifdef DEBUG DWORD time; #endif pIndex = (PAVIINDEXENTRY)GlobalAllocPtr(GHND,INDEX_READ_SIZE); if (pIndex == NULL) goto exit; pfile->px = IndexCreate(); if (pfile->px == 0) goto exit; DPF("Reading index.", time = timeGetTime()); if (pfile->avihdr.dwFlags & AVIF_MUSTUSEINDEX) lIndexAdjust = dwOffsetMovie; else lIndexAdjust = -1; // set when we read first index entry. while (size > 0) { cnt = min(INDEX_READ_SIZE, size); if (shfileRead(pfile->hshfile,(HPSTR)pIndex,cnt) != cnt) goto exit; size -= cnt; cnt /= sizeof(AVIINDEXENTRY); // // fix up the index // if (lIndexAdjust == -1) { lIndexAdjust = (LONG)(dwOffsetMovie + sizeof(DWORD)) - (LONG)pIndex->dwChunkOffset; } pfile->px = IndexAddFileIndex(pfile->px, pIndex, cnt, lIndexAdjust, fRle); if (pfile->px == NULL) goto exit; DPF("!."); } DPF("!Done (%ldms)\n", timeGetTime() - time); f = TRUE; exit: if (pIndex) GlobalFreePtr(pIndex); return f; } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// HRESULT SaveChanges(CAVIFile FAR * pfile, BOOL fRelease) { CAVIStream FAR * pavi; int stream; MMCKINFO ck; MMCKINFO ckRIFF; MMCKINFO ckLIST; MMCKINFO ckStream; LONG lCur; HRESULT hr = AVIERR_OK; AVIStreamHeaderShort strhdr; // Clean up interleaving if (pfile->fInRecord) { if (pfile->px->nIndex > pfile->lRecordIndex + 1) { AVIFileEndRecord((PAVIFILE) pfile); } // back out of last record.... --pfile->px->nIndex; pfile->lWriteLoc -= 3 * sizeof(DWORD); shfileSeek(pfile->hshfile, pfile->lWriteLoc, SEEK_SET); pfile->fInRecord = FALSE; } // Go back and write out the header lCur = shfileSeek(pfile->hshfile, 0, SEEK_CUR); shfileSeek(pfile->hshfile, 0, SEEK_SET); /* Create RIFF chunk */ ckRIFF.cksize = 0; ckRIFF.fccType = formtypeAVI; if (shfileCreateChunk(pfile->hshfile, &ckRIFF, MMIO_CREATERIFF)) { goto FileError; } /* Create header list */ ckLIST.cksize = 0; ckLIST.fccType = listtypeAVIHEADER; if (shfileCreateChunk(pfile->hshfile, &ckLIST, MMIO_CREATELIST)) { goto FileError; } /* Create AVI header chunk */ ck.cksize = sizeof(pfile->avihdr); ck.ckid = ckidAVIMAINHDR; if (shfileCreateChunk(pfile->hshfile, &ck, 0)) { goto FileError; } CalculateFileDataRate(&pfile->m_AVIFile, (LONG FAR *) &pfile->avihdr.dwMaxBytesPerSec); // !!! CalculateFileDataRate may have seeked us to the wrong place.... shfileSeek(pfile->hshfile, ck.dwDataOffset, SEEK_SET); /* Write AVI header info */ if (shfileWrite(pfile->hshfile, (HPSTR)&pfile->avihdr, sizeof(pfile->avihdr)) != sizeof(pfile->avihdr)) { goto FileError; } if (shfileAscend(pfile->hshfile, &ck, 0)) { goto FileError; } #if 0 for (l = 0; l < muldiv32(pfile->avihdr.dwTotalFrames, pfile->avihdr.dwMicroSecPerFrame, 1000000L); l++) { for (stream = 0; stream < (int) pfile->avihdr.dwStreams; stream++) { } } #endif for (stream = 0; stream < (int) pfile->avihdr.dwStreams; stream++) { pavi = pfile->ps[stream]; /* Create stream header list */ ckStream.cksize = 0; ckStream.fccType = listtypeSTREAMHEADER; if (shfileCreateChunk(pfile->hshfile,&ckStream,MMIO_CREATELIST)) { goto FileError; } ck.ckid = ckidSTREAMHEADER; if (shfileCreateChunk(pfile->hshfile, &ck, 0)) { goto FileError; } // Make an AVIStreamHeader from the AVISTREAMINFO strhdr.fccType = pavi->avistream.fccType; strhdr.fccHandler = pavi->avistream.fccHandler; strhdr.dwFlags = pavi->avistream.dwFlags; strhdr.wPriority = pavi->avistream.wPriority; strhdr.wLanguage = pavi->avistream.wLanguage; strhdr.dwRate = pavi->avistream.dwRate; strhdr.dwScale = pavi->avistream.dwScale; strhdr.dwStart = pavi->avistream.dwStart; strhdr.dwLength = pavi->avistream.dwLength; strhdr.dwSuggestedBufferSize = pavi->avistream.dwSuggestedBufferSize; strhdr.dwQuality = pavi->avistream.dwQuality; strhdr.dwSampleSize = pavi->avistream.dwSampleSize; #ifdef _WIN32 // Write out the Short rectangle format strhdr.rcFrame.left = (SHORT)pavi->avistream.rcFrame.left ; strhdr.rcFrame.right = (SHORT)pavi->avistream.rcFrame.right ; strhdr.rcFrame.top = (SHORT)pavi->avistream.rcFrame.top ; strhdr.rcFrame.bottom = (SHORT)pavi->avistream.rcFrame.bottom ; #else strhdr.rcFrame = pavi->avistream.rcFrame; #endif strhdr.dwInitialFrames = pavi->avistream.dwInitialFrames; if (shfileWrite(pfile->hshfile, (HPSTR) &strhdr, sizeof(strhdr)) != sizeof(strhdr)) { goto FileError; } if (shfileAscend(pfile->hshfile, &ck, 0)) { goto FileError; } ck.cksize = pavi->cbFormat; ck.ckid = ckidSTREAMFORMAT; if (shfileCreateChunk(pfile->hshfile, &ck, 0)) goto FileError; if (shfileWrite(pfile->hshfile, (HPSTR) pavi->lpFormat, ck.cksize) != (LONG) ck.cksize) goto FileError; if (shfileAscend(pfile->hshfile, &ck, 0)) goto FileError; if (pavi->avistream.szName[0]) { long sz = lstrlenW(pavi->avistream.szName)+1; ck.cksize = sz; ck.ckid = ckidSTREAMNAME; if (shfileCreateChunk(pfile->hshfile, &ck, 0)) goto FileError; #ifdef _WIN32 // the file format expects ANSI names! LPSTR pA = (LPSTR) GlobalAllocPtr(GPTR, sz); if (pA == 0) { DPF(("memory allocation failed for Unicode conversion")); goto FileError; } WideCharToMultiByte(CP_ACP, 0, pavi->avistream.szName, -1, pA, sz, NULL, NULL); sz = shfileWrite(pfile->hshfile, (HPSTR) pA, ck.cksize); GlobalFreePtr(pA); if (sz != (LONG) ck.cksize) goto FileError; #else if (shfileWrite(pfile->hshfile, (HPSTR) pavi->avistream.szName, ck.cksize) != (LONG) ck.cksize) goto FileError; #endif if (shfileAscend(pfile->hshfile, &ck, 0)) goto FileError; } if (pavi->extra.cb) { DPF2("Writing %ld bytes of extra stream data.\n", pavi->extra.cb); if (shfileWrite(pfile->hshfile, (HPSTR) pavi->extra.lp, pavi->extra.cb) != (LONG) pavi->extra.cb) goto FileError; } /* Ascend out of stream's header */ if (shfileAscend(pfile->hshfile, &ckStream, 0)) { goto FileError; } } /* ascend from the Header list */ if (shfileAscend(pfile->hshfile, &ckLIST, 0)) { goto FileError; } lCur = shfileSeek(pfile->hshfile, 0, SEEK_CUR); DPF("Data list start = %ld, current pos = %ld\n", pfile->lDataListStart, lCur); if (lCur + 8 > pfile->lDataListStart) { // !!! Ack: we didn't leave enough space for the header. // !!! How can we avoid this? DPF("Header was too big! Failing!\n"); goto FileError; } /* Pad this header out so that the real data will start on a 2K ** boundary by writing a JUNK chunk. */ ck.ckid = ckidAVIPADDING; ck.cksize = pfile->lDataListStart - lCur - 8; if (shfileCreateChunk(pfile->hshfile,&ck,0)) { goto FileError; } shfileZero(pfile->hshfile, pfile->lDataListStart - lCur - 8); if (shfileAscend(pfile->hshfile, &ck, 0)) { goto FileError; } /* Start the 'movi' list, where all of the actual data will be. */ ckLIST.cksize = pfile->lWriteLoc - pfile->lDataListStart - 8; ckLIST.fccType = listtypeAVIMOVIE; if (shfileCreateChunk(pfile->hshfile, &ckLIST, MMIO_CREATELIST)) { goto FileError; } shfileSeek(pfile->hshfile, pfile->lWriteLoc, SEEK_SET); if (shfileAscend(pfile->hshfile, &ckLIST, 0)) goto FileError; if (!WriteOutIndex(pfile, ckLIST.dwDataOffset)) goto FileError; // // Write out any extra data around // if (pfile->extra.cb) { DPF2("Writing %ld bytes of extra file data.\n", pfile->extra.cb); if (shfileWrite(pfile->hshfile, (HPSTR) pfile->extra.lp, pfile->extra.cb) != (LONG) pfile->extra.cb) goto FileError; } FinishUp: if (shfileAscend(pfile->hshfile, &ckRIFF, 0)) { hr = ResultFromScode(AVIERR_FILEWRITE); } // // Always flush to be sure that the data really made it to the disk.... // if (shfileFlush(pfile->hshfile, 0)) { hr = ResultFromScode(AVIERR_FILEWRITE); } return hr; FileError: DPF("SaveChanges returning Error! This error will be ignored!\n"); hr = ResultFromScode(AVIERR_FILEWRITE); goto FinishUp; } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP_(ULONG) CAVIFile::CUnknownImpl::Release() { CAVIFile FAR * pfile = m_pAVIFile; CAVIStream FAR * pavi; int iStream; CLock tlock(pfile); DPF2("File %p: Usage--=%lx\n", (DWORD_PTR) (LPVOID) this, m_refs - 1); uUseCount--; if (!--m_refs) { if (pfile->fDirty) { ++m_refs; SaveChanges(pfile, TRUE); --m_refs; // Unfortunately, it's too late to tell about any errors.... } for (iStream = 0; iStream < (int)pfile->avihdr.dwStreams; iStream++) { pavi = pfile->ps[iStream]; if (!pavi) continue; delete pavi; } if (pfile->hshfile) { shfileRelease(pfile->hshfile); shfileClose(pfile->hshfile, 0); } if (pfile->px) FreeIndex(pfile->px); if (pfile->extra.lp) { DPF2("Freeing %ld bytes of extra file data.\n", pfile->extra.cb); GlobalFreePtr(pfile->extra.lp); } if (pfile->pb) EndBuffered(pfile->pb); pfile->hshfile = NULL; pfile->px = NULL; // done with critsec now - no-one else has any refs to it tlock.Exit(); #ifdef _WIN32 DeleteCriticalSection(&pfile->m_critsec); #endif delete pfile; return 0; } else { if (pfile->hshfile) shfileRelease(pfile->hshfile); } return m_refs; } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// #ifndef _WIN32 STDMETHODIMP CAVIFile::CAVIFileImpl::Save( LPCTSTR szFile, AVICOMPRESSOPTIONS FAR *lpOptions, AVISAVECALLBACK lpfnCallback) { CAVIFile FAR * pfile = m_pAVIFile; HRESULT hr = ResultFromScode(AVIERR_OK); CLock tlock(pfile); if (pfile->fDirty) { hr = SaveChanges(pfile, FALSE); } return hr; } #endif #define SLASH(c) ((c) == TEXT('/') || (c) == TEXT('\\')) /*--------------------------------------------------------------+ | FileName - return a pointer to the filename part of szPath | | with no preceding path. | +--------------------------------------------------------------*/ LPTSTR FAR FileName(LPCTSTR lszPath) { LPCTSTR lszCur; #ifdef _WIN32 // We really should be using GetFileTitle API as this will provide // better validation on the input name. #endif for (lszCur = lszPath + lstrlen(lszPath); lszCur > lszPath && !SLASH(*lszCur) && *lszCur != ':';) { #ifdef _WIN32 lszCur = CharPrev(lszPath, lszCur); #else lszCur = AnsiPrev(lszPath, lszCur); #endif } if (lszCur == lszPath) return (LPTSTR)lszCur; else return (LPTSTR)(lszCur + 1); } // We do not currently use the last defined parameter for IsRectBogus // Use a macro to remove it. (It can be quickly restored.) #define ISRECTBOGUS(lprc, dwW, dwH, lpbi) IsRectBogus(lprc, dwW, dwH) INLINE BOOL IsRectBogus(LPRECT lprc, DWORD dwFrameWidth, DWORD dwFrameHeight) // unused LPBITMAPINFOHEADER lpbi) { if (IsRectEmpty(lprc)) return TRUE; if (lprc->right - lprc->left > (int) dwFrameWidth) return TRUE; if (lprc->bottom - lprc->top > (int) dwFrameHeight) return TRUE; // !!!! Check that rectangle matches lpbi? // We've run out of things to check, so it's OK.... return FALSE; } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIFile::OpenInternal(DWORD mode) { CAVIFile FAR * pfile = this; CAVIStream FAR * pavi; MMCKINFO ck; MMCKINFO ckRIFF; MMCKINFO ckLIST; MMCKINFO ckStream; DWORD dwSize; BOOL fRle=FALSE; LONG l; int iStream; int i; HRESULT hr = ResultFromScode(AVIERR_OK); IUnknown FAR * pUnk; AVIStreamHeaderShort strhdr; TCHAR ach[20]; TCHAR ach2[20]; int iStreamNumber; #ifdef DEBUG DWORD time; #endif CLock tlock(pfile); if (!pfile->hshfile) { hr = ResultFromScode(AVIERR_FILEOPEN); goto error; } if (mode & OF_CREATE) { // make a empty index. pfile->px = IndexCreate(); if (pfile->px == 0) goto memerror; pfile->lWriteLoc = 0; pfile->lHeaderSize = sizeof(MainAVIHeader) + 11 * sizeof(DWORD); #ifndef AVIF_TRUSTCKTYPE #define AVIF_TRUSTCKTYPE 0x00000800 // Use CKType to find key frames? #endif pfile->avihdr.dwFlags = AVIF_HASINDEX | AVIF_TRUSTCKTYPE; } else { /* Read RIFF chunk */ if (shfileDescend(pfile->hshfile, &ckRIFF, NULL, 0) != 0) goto readerror; /* * check for a 'QuickTime AVI' file, a QuickTime AVI file is a * QuickTime public movie with a AVI file in the 'mdat' atom. */ if (ckRIFF.cksize == mmioFOURCC('m','d','a','t')) { /* * now the 'mdat' atom better be a RIFF/AVI or we cant handle it. */ if (shfileDescend(pfile->hshfile, &ckRIFF, NULL, 0) != 0) goto formaterror; } if (ckRIFF.ckid != FOURCC_RIFF) goto formaterror; if (ckRIFF.fccType != formtypeAVI) goto formaterror; /* Read header list */ ckLIST.fccType = listtypeAVIHEADER; if (FindChunkAndKeepExtras(&pfile->extra, pfile->hshfile, &ckLIST, &ckRIFF, MMIO_FINDLIST)) goto error; pfile->lHeaderSize = ckLIST.cksize + 8 * sizeof(DWORD); /* Read AVI header chunk */ ck.ckid = ckidAVIMAINHDR; if (FindChunkAndKeepExtras(&pfile->extra, pfile->hshfile, &ck, &ckLIST, MMIO_FINDCHUNK)) goto error; dwSize = min(ck.cksize, sizeof(MainAVIHeader)); /* Read AVI header info */ if (shfileRead(pfile->hshfile, (HPSTR)&pfile->avihdr, dwSize) != (LONG)dwSize) goto readerror; if (shfileAscend(pfile->hshfile, &ck, 0)) goto readerror; /* Check there aren't more streams than we can handle */ if (pfile->avihdr.dwStreams > MAXSTREAMS) { /* Make sure we don't crash deleteing non-existent streams */ pfile->avihdr.dwStreams = 0; goto error; } for (iStream = 0; iStream < (int)pfile->avihdr.dwStreams; iStream++) { pfile->ps[iStream] = NULL; } /* Allocate stream data stuff, read streams */ for (iStream = 0; iStream < (int)pfile->avihdr.dwStreams; ) { if (shfileDescend(pfile->hshfile, &ckStream, &ckLIST, 0) != 0) goto readerror; // // found a non-stream header skip // if (ckStream.fccType != listtypeSTREAMHEADER || ckStream.ckid != FOURCC_LIST) { if ((hr = ReadIntoExtra(&pfile->extra, pfile->hshfile, &ckStream)) != ResultFromScode(AVIERR_OK)) goto error; if (shfileAscend(pfile->hshfile, &ckStream, 0) != 0) goto readerror; continue; } pfile->ps[iStream] = new FAR CAVIStream(NULL, &pUnk); if (!pfile->ps[iStream]) goto memerror; pavi = pfile->ps[iStream]; pavi->pfile = pfile; pavi->iStream = iStream; // // walk every chunk in this stream header, until we are done. // while (shfileDescend(pfile->hshfile, &ck, &ckStream, 0) == 0) { switch (ck.ckid) { case ckidSTREAMHEADER: // // set these to sane default's incase the file // header is not big enough // // NOTE the stream rectangle is set to NULL, if // this is a video stream it will be corrected // when we process the format. // strhdr.dwQuality = (DWORD) ICQUALITY_DEFAULT; #ifdef _WIN32 // Set the 16 bit rectangle values to 0 strhdr.rcFrame.left = strhdr.rcFrame.right= strhdr.rcFrame.top = strhdr.rcFrame.bottom = 0; #else SetRectEmpty(&strhdr.rcFrame); #endif l = min(ck.cksize, sizeof(strhdr)); if (shfileRead(pfile->hshfile, (HPSTR)&strhdr, l) != l) goto readerror; // Copy fields from strhdr into StreamInfo pavi->avistream.fccType = strhdr.fccType; pavi->avistream.fccHandler = strhdr.fccHandler; pavi->avistream.dwFlags = strhdr.dwFlags; //!!! pavi->avistream.dwCaps = 0; // !!! pavi->avistream.wPriority = strhdr.wPriority; pavi->avistream.wLanguage = strhdr.wLanguage; pavi->avistream.dwRate = strhdr.dwRate; pavi->avistream.dwScale = strhdr.dwScale; pavi->avistream.dwStart = strhdr.dwStart; pavi->avistream.dwLength = strhdr.dwLength; pavi->avistream.dwSuggestedBufferSize = strhdr.dwSuggestedBufferSize; pavi->avistream.dwInitialFrames = strhdr.dwInitialFrames; pavi->avistream.dwQuality = strhdr.dwQuality; pavi->avistream.dwSampleSize = strhdr.dwSampleSize; #ifdef _WIN32 // Copy the 16 bit rectangle we have read from // persistent storage into a WIN32 RECT (32 bit) // structure. There is no operator= defined for // this "short rect" to RECT assignment. pavi->avistream.rcFrame.left = strhdr.rcFrame.left; pavi->avistream.rcFrame.top = strhdr.rcFrame.top; pavi->avistream.rcFrame.right = strhdr.rcFrame.right; pavi->avistream.rcFrame.bottom= strhdr.rcFrame.bottom; #else pavi->avistream.rcFrame = strhdr.rcFrame; #endif pavi->avistream.dwEditCount = 0; pavi->avistream.dwFormatChangeCount = 0; // Make up a stream name out of the filename, stream // type, and stream number. if (pavi->avistream.fccType == streamtypeVIDEO) LoadString(ghMod, IDS_VIDEO, ach, NUMELMS(ach)); else if (pavi->avistream.fccType == streamtypeAUDIO) LoadString(ghMod, IDS_AUDIO, ach, NUMELMS(ach)); else wsprintf(ach, TEXT("'%4.4hs'"), (LPSTR)&(pavi->avistream.fccType)); // figure out what # stream of this type this is.... iStreamNumber = 1; for (i = 0; i < iStream; i++) { if (pfile->ps[i]->avistream.fccType == pavi->avistream.fccType) ++iStreamNumber; } LoadString(ghMod, IDS_SSSTREAMD, ach2, NUMELMS(ach2)); { TCHAR achTemp[MAX_PATH]; wsprintf(achTemp, (LPTSTR)ach2, (LPTSTR)FileName(pfile->achFile), (LPTSTR)ach, iStreamNumber); #if defined _WIN32 && !defined UNICODE lstrzcpyAtoW (pavi->avistream.szName, achTemp, NUMELMS (pavi->avistream.szName)); #else lstrzcpy (pavi->avistream.szName, achTemp, NUMELMS (pavi->avistream.szName)); #endif } break; case ckidSTREAMFORMAT: if (pavi->lpFormat == NULL) { pavi->cbFormat = ck.cksize; pavi->lpFormat = GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, ck.cksize); if (pavi->lpFormat == NULL) goto memerror; if (shfileRead(pfile->hshfile, (HPSTR) pavi->lpFormat, (LONG)ck.cksize) != (LONG)ck.cksize) goto readerror; #define lpbi ((LPBITMAPINFOHEADER)pavi->lpFormat) if (pavi->avistream.fccType != streamtypeVIDEO) break; if (!ValidateBitmapInfoHeader(lpbi, ck.cksize)) { break; } // // make sure this is set // if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8) lpbi->biClrUsed = (1 << (int)lpbi->biBitCount); // // fix up bogus stream rectangles. // if (ISRECTBOGUS(&pavi->avistream.rcFrame, pfile->avihdr.dwWidth, pfile->avihdr.dwHeight, lpbi)) { SetRect(&pavi->avistream.rcFrame, 0, 0, (int)lpbi->biWidth, (int)lpbi->biHeight); } // // make sure the biCompression is right for // RLE files. // if (lpbi->biCompression == 0 && lpbi->biBitCount == 8) { if (pavi->avistream.fccHandler == comptypeRLE0 || pavi->avistream.fccHandler == comptypeRLE) lpbi->biCompression = BI_RLE8; } if (pavi->avistream.fccHandler == comptypeNONE && lpbi->biCompression == 0) pavi->avistream.fccHandler = comptypeDIB; if (pavi->avistream.fccHandler == 0 && lpbi->biCompression == 0) pavi->avistream.fccHandler = comptypeDIB; // Assuming a DIB handler has RGB data will blow up // if it has RLE data, and VidEdit et. al write out // confusing files like this. //if (pavi->avistream.fccHandler == comptypeDIB) // lpbi->biCompression = BI_RGB; if (lpbi->biCompression <= BI_RLE8) fRle = TRUE; #undef lpbi } break; case ckidSTREAMHANDLERDATA: if (pavi->lpData == NULL) { pavi->cbData = ck.cksize; pavi->lpData = GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, ck.cksize); if (pavi->lpData == NULL) goto memerror; if (shfileRead(pfile->hshfile, (HPSTR)pavi->lpData, (LONG)ck.cksize) != (LONG)ck.cksize) goto readerror; } break; case ckidSTREAMNAME: { #ifdef _WIN32 char achTemp[MAX_PATH]; l = (LONG)min((LONG) ck.cksize, NUMELMS(achTemp)); if (shfileRead(pfile->hshfile, (LPSTR)achTemp, l) != l) { goto readerror; } MultiByteToWideChar(CP_ACP, 0, achTemp, -1, pavi->avistream.szName, NUMELMS(pavi->avistream.szName)); #else l = min((LONG) ck.cksize, NUMELMS(pavi->avistream.szName)); if (shfileRead(pfile->hshfile, (HPSTR)pavi->avistream.szName, l) != l) goto readerror; #endif } break; case ckidAVIPADDING: case mmioFOURCC('p','a','d','d'): break; default: if ((hr = ReadIntoExtra(&pavi->extra, pfile->hshfile, &ck)) != ResultFromScode(AVIERR_OK)) goto error; break; } if (shfileAscend(pfile->hshfile, &ck, 0) != 0) goto readerror; } /* Ascend out of stream header */ if (shfileAscend(pfile->hshfile, &ckStream, 0) != 0) goto readerror; if (pavi->avistream.fccType == 0) goto formaterror; if (pavi->lpFormat == NULL) goto formaterror; // // make sure the sample size is set right // switch(pavi->avistream.fccType) { case streamtypeAUDIO: /* Hack for backward compatibility with audio */ pavi->avistream.dwSampleSize = ((LPWAVEFORMAT)pavi->lpFormat)->nBlockAlign; // For audio, this number isn't useful when reading. // !!!pavi->avistream.dwInitialFrames = 0; // !!! We should let people read what the header says.... break; case streamtypeVIDEO: // !!! But what if the samples are all the right size? pavi->avistream.dwSampleSize = 0; break; default: // !!! ??? pavi->avistream.dwInitialFrames = 0; // !!! ??? pavi->avistream.dwSampleSize = 0; break; } l = NUMELMS(pavi->avistream.szName) - 1; pavi->avistream.szName[l] = TEXT('\0'); // next stream iStream++; } // Read extra data at end of header list.... FindChunkAndKeepExtras(&pfile->extra, pfile->hshfile, &ck, &ckLIST, 0); if (shfileAscend(pfile->hshfile, &ckLIST, 0)) goto readerror; /* Find big data chunk */ ckLIST.fccType = listtypeAVIMOVIE; if (FindChunkAndKeepExtras(&pfile->extra, pfile->hshfile, &ckLIST, &ckRIFF, MMIO_FINDLIST)) goto error; pfile->lDataListStart = ckLIST.dwDataOffset - 2 * sizeof(DWORD); if (shfileAscend(pfile->hshfile, &ckLIST, 0)) goto readerror; // Keep track of where data can be written pfile->lWriteLoc = ckLIST.dwDataOffset + ckLIST.cksize; // // read in or create a index, we only want the index entries for the // stream we are interested in! // ck.ckid = ckidAVINEWINDEX; if (FindChunkAndKeepExtras(&pfile->extra, pfile->hshfile, &ck, &ckRIFF, MMIO_FINDCHUNK) == 0 && ck.cksize != 0) { if (!ReadInIndex(pfile, ck.cksize, ckLIST.dwDataOffset, fRle)) goto formaterror; } else { /* Seek back to beginning of list, so we can scan */ shfileSeek(pfile->hshfile, ckLIST.dwDataOffset + sizeof(DWORD), SEEK_SET); //!!! should we really scan big files, or give a error? pfile->px = IndexCreate(); if (pfile->px == 0) goto formaterror; DPF("Scanning index", time = timeGetTime()); /* Scan through chunks... */ while (shfileDescend(pfile->hshfile, &ck, &ckLIST, 0) == 0) { AddToIndex(pfile,ck.ckid,ck.cksize,ck.dwDataOffset-8,0); /* Hack: don't ascend from LISTs */ if (ck.ckid != FOURCC_LIST) { if (shfileAscend(pfile->hshfile, &ck, 0) != 0) goto readerror; } if (pfile->px->nIndex % 512 == 0) { DPF("!."); } } DPF("!Done (%ldms)\n", timeGetTime() - time); } if (pfile->px->nIndex == 0) goto formaterror; // Read extra data at end of file.... FindChunkAndKeepExtras(&pfile->extra, pfile->hshfile, &ck, &ckRIFF, 0); // shfileSetBuffer(pfile->hshfile, NULL, 0L, 0); // // compute dwSuggestedBufferSize // if (pfile->avihdr.dwFlags & AVIF_ISINTERLEAVED) { LONG l; LONG lLen; pfile->avihdr.dwSuggestedBufferSize = 0; for (l=IndexFirst(pfile->px, STREAM_REC); l != -1; l = IndexNext(pfile->px, l, 0)) { lLen = IndexLength(pfile->px, l); if (pfile->avihdr.dwSuggestedBufferSize < (DWORD)lLen) pfile->avihdr.dwSuggestedBufferSize = (DWORD)lLen; } } #ifdef USE_DIRECTIO // don't use additional buffering if we're using direct io if (shfileIsDirect(pfile->hshfile)) { pfile->pb = NULL; } else #endif { if ((pfile->avihdr.dwFlags & AVIF_ISINTERLEAVED) && pfile->avihdr.dwInitialFrames) { pfile->pb = InitBuffered((int) pfile->avihdr.dwInitialFrames * 2, pfile->avihdr.dwSuggestedBufferSize, pfile->hshfile, pfile->px); } else /* if (pfile->avihdr.dwSuggestedBufferSize > 0 && pfile->avihdr.dwSuggestedBufferSize < 32l*1024) */ { int nBuffers = GetProfileIntA("avifile", "buffers", 5); pfile->pb = InitBuffered(nBuffers, min(pfile->avihdr.dwSuggestedBufferSize * 2, 32768L), pfile->hshfile, pfile->px); } } } return ResultFromScode(AVIERR_OK); readerror: return ResultFromScode(AVIERR_FILEREAD); memerror: return ResultFromScode(AVIERR_MEMORY); formaterror: return ResultFromScode(AVIERR_BADFORMAT); error: if (hr == ResultFromScode(AVIERR_OK)) return ResultFromScode(AVIERR_ERROR); return hr; } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// #ifndef _WIN32 STDMETHODIMP CAVIFile::CAVIFileImpl::Open(LPCTSTR szFile, UINT mode) { CAVIFile FAR * pfile = m_pAVIFile; UINT ui; CLock tlock(pfile); if (pfile->achFile[0]) return ResultFromScode(-1); pfile->mode = mode; lstrcpy(pfile->achFile, szFile); // Assumptions about avilib.cpp: // We're assuming that if CREATE is set, WRITE is set too. // We're assuming that we'll always see READWRITE instead of just WRITE. // If it ain't broke, don't fix it - who do I look like, the share flag // standards enforcing committee? #if 0 // force the share flags to the 'correct' values if (mode & OF_READWRITE) { pfile->mode = (mode & ~(MMIO_SHAREMODE)) | OF_SHARE_EXCLUSIVE; } else { pfile->mode = (mode & ~(MMIO_SHAREMODE)) | OF_SHARE_DENY_WRITE; } #endif // try to open the actual file // If the first attempt fails, no system error box, please. ui = SetErrorMode(SEM_NOOPENFILEERRORBOX); pfile->hshfile = shfileOpen(pfile->achFile, NULL, pfile->mode); if (!pfile->hshfile && ((mode & MMIO_RWMODE) == OF_READ)) { // if the open fails, try again without the share flags. pfile->mode &= ~(MMIO_SHAREMODE); pfile->hshfile = shfileOpen(pfile->achFile, NULL, pfile->mode); } if (pfile->hshfile) shfileAddRef(pfile->hshfile); // compensate for later rel of IUnknown SetErrorMode(ui); return pfile->OpenInternal(mode); } #endif /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIFile::CAVIFileImpl::GetStream(PAVISTREAM FAR *ppavi, DWORD fccType, LONG lParam) { CAVIFile FAR * pfile = m_pAVIFile; CAVIStream FAR *pavi; int iStreamCur; int iStreamWant; int iStream; LONG lLength; // thread locking CLock tlock(pfile); *ppavi = NULL; iStreamWant = (int)lParam; if (iStreamWant < 0 || iStreamWant >= (int)pfile->avihdr.dwStreams) return ResultFromScode(AVIERR_NODATA); /* Allocate stream data stuff, read streams */ for (iStreamCur = -1, iStream = 0; iStream < (int)pfile->avihdr.dwStreams; iStream++) { if (fccType == 0 || pfile->ps[iStream]->avistream.fccType == fccType) iStreamCur++; if (iStreamCur == iStreamWant) break; } if (iStreamCur != iStreamWant) return ResultFromScode(AVIERR_NODATA); pavi = pfile->ps[iStream]; if (pavi->fInit) goto returnnow; pavi->fInit = TRUE; #if 0 if ((pf->mode & (OF_WRITE | OF_READWRITE)) == 0) { pavi->hshfile = shfileOpen(pfile->achFile, NULL, MMIO_ALLOCBUF | pfile->mode); if (!pavi->hshfile) goto error; } else #endif pavi->hshfile = pfile->hshfile; pavi->lPal = -4242; pavi->psx = MakeStreamIndex(pfile->px, iStream, (LONG)pavi->avistream.dwStart - pavi->avistream.dwInitialFrames, (LONG)pavi->avistream.dwSampleSize, pfile->hshfile, shfileReadProc, NULL); if (pavi->psx == NULL) { pavi->fInit = FALSE; // sigh; failed. return ResultFromScode(AVIERR_MEMORY); } AddRef(); // Now safe. We only ever return AVIERR_OK after this. pavi->avistream.dwSuggestedBufferSize = pavi->psx->lMaxSampleSize; if (pavi->psx->lPalFrames == 0) pavi->avistream.dwFlags &= ~AVISF_VIDEO_PALCHANGES; else pavi->avistream.dwFlags |= AVISF_VIDEO_PALCHANGES; pavi->pb = pavi->pfile->pb; if (!pavi->pb) { #ifdef USE_DIRECTIO if (!shfileIsDirect(pavi->hshfile)) #endif { lBufferSize = GetProfileIntA("avifile", "buffersize", 0) * 1024L; nBuffers = GetProfileIntA("avifile", "buffers", 0); if (lBufferSize && nBuffers && !(pavi->pfile->mode & OF_CREATE)) { pavi->pb = InitBuffered(nBuffers, lBufferSize, pavi->hshfile, NULL); } } } // // use ReadBuffered() to read data! // if (pavi->pb) { pavi->psx->hFile = (HANDLE)pavi->pb; pavi->psx->Read = (STREAMIOPROC)BufferedRead; } lLength = pavi->psx->lEnd - pavi->psx->lStart; if (lLength != (LONG)pavi->avistream.dwLength + (LONG)pavi->avistream.dwInitialFrames) { #ifdef DEBUG DPF("Stream %d: Length is %ld, header says %ld.\n", iStream, lLength, pavi->avistream.dwLength + pavi->avistream.dwInitialFrames); #endif //!!! should we correct the header!!! } returnnow: pavi->m_AVIStream.QueryInterface(IID_IAVIStream, (LPVOID FAR *) ppavi); Assert(*ppavi); // We had better return an interface pointer // // all done return success. // return ResultFromScode(AVIERR_OK); // success } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIFile::CAVIFileImpl::CreateStream( PAVISTREAM FAR *ppavi, AVISTREAMINFOW FAR *psi) { CAVIFile FAR * pf = m_pAVIFile; CAVIStream FAR * pavi; int iStream = (int) pf->avihdr.dwStreams; IUnknown FAR * pUnk; CLock tlock(m_pAVIFile); // !!! If we are writing to an existing file, and not to a new file, we have // a limitation where we cannot grow the size of the header. // Check to see if the header will take up too much room! if (pf->lWriteLoc > 0) { LONG lHeader = sizeof(AVIStreamHeader) + pf->lHeaderSize + 8 * sizeof(DWORD) + lstrlenW(psi->szName); if (lHeader > pf->lDataListStart) { DPF("Header will be too big with this new stream!\n"); return ResultFromScode(AVIERR_UNSUPPORTED); } } pf->lHeaderSize += sizeof(AVIStreamHeader) + 8 * sizeof(DWORD) + lstrlenW(psi->szName); if (iStream >= MAXSTREAMS) { DPF("Ack: Too many streams: we only support %ld.\n", (LONG) MAXSTREAMS); return ResultFromScode(AVIERR_UNSUPPORTED); } if ((pf->mode & (OF_WRITE | OF_READWRITE)) == 0) return ResultFromScode(AVIERR_READONLY); pf->ps[iStream] = new FAR CAVIStream(NULL, &pUnk); if (!pf->ps[iStream]) return ResultFromScode(AVIERR_MEMORY); pavi = pf->ps[iStream]; pavi->iStream = iStream; pavi->pfile = pf; pavi->avistream = *psi; pavi->avistream.dwLength = 0; // no data initially pavi->avistream.dwSuggestedBufferSize = 0; pavi->hshfile = pf->hshfile; pavi->m_AVIStream.AddRef(); AddRef(); pavi->lpFormat = NULL; // This will be set leater with a SetFormat pavi->cbFormat = 0; if (pavi->avistream.fccType == streamtypeAUDIO) { SetRectEmpty(&pavi->avistream.rcFrame); } pf->avihdr.dwStreams++; if (pavi->iStream == 0) { pavi->pfile->avihdr.dwMicroSecPerFrame = max(1000L, muldiv32(1000000L, pavi->avistream.dwScale, pavi->avistream.dwRate)); } /* Make sure the width and height of the created file are right.... */ pf->avihdr.dwWidth = max(pf->avihdr.dwWidth, (DWORD) pavi->avistream.rcFrame.right); pf->avihdr.dwHeight = max(pf->avihdr.dwHeight, (DWORD) pavi->avistream.rcFrame.bottom); // Only if interleaved? pf->avihdr.dwInitialFrames = max(pf->avihdr.dwInitialFrames, pavi->avistream.dwInitialFrames); *ppavi = &pavi->m_AVIStream; return ResultFromScode(AVIERR_OK); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// #if 0 STDMETHODIMP CAVIFile::CAVIFileImpl::AddStream( PAVISTREAM pavi, PAVISTREAM FAR *ppaviNew) { CAVIFile FAR * pf = m_pAVIFile; CAVIStream FAR * paviNew; int iStream = (int) pf->avihdr.dwStreams; HRESULT hr; IUnknown FAR * pUnk; if ((pf->mode & (OF_WRITE | OF_READWRITE)) == 0) return ResultFromScode(AVIERR_READONLY); pfile->ps[iStream] = new FAR CAVIStream(NULL, &pUnk); if (!pfile->ps[iStream]) return ResultFromScode(AVIERR_MEMORY); paviNew = pf->ps[iStream]; paviNew->iStream = iStream; paviNew->pfile = pf; AVIStreamInfo(pavi, &paviNew->avistream, sizeof(paviNew->avistream)); paviNew->hshfile = pf->hshfile; paviNew->m_AVIStream.AddRef(); paviNew->paviBase = pavi; AVIStreamAddRef(pavi); paviNew->cbFormat = AVIStreamFormatSize(pavi, 0); paviNew->lpFormat = GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, paviNew->cbFormat); if (!paviNew->lpFormat) { AVIStreamClose((PAVISTREAM) pf->ps[iStream]); return ResultFromScode(AVIERR_MEMORY); } AVIStreamReadFormat(pavi, 0, paviNew->lpFormat, &paviNew->cbFormat); pf->avihdr.dwStreams++; AddRef(); if (paviNew->iStream == 0) { pf->avihdr.dwMicroSecPerFrame = muldiv32(1000000L, paviNew->avistream.dwScale, paviNew->avistream.dwRate); } *ppaviNew = (PAVISTREAM) paviNew; return ResultFromScode(AVIERR_OK); } #endif /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIFile::CAVIFileImpl::WriteData( DWORD ckid, LPVOID lpData, LONG cbData) { CAVIFile FAR * pf = m_pAVIFile; CLock tlock(m_pAVIFile); // !!! Anything else we can check? if (lpData == NULL || cbData == 0) return ResultFromScode(AVIERR_BADPARAM); if ((pf->mode & (OF_WRITE | OF_READWRITE)) == 0) return ResultFromScode(AVIERR_READONLY); pf->fDirty = TRUE; return WriteExtra(&pf->extra, ckid, lpData, cbData); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIFile::CAVIFileImpl::ReadData( DWORD ckid, LPVOID lpData, LONG FAR *lpcbData) { CAVIFile FAR * pf = m_pAVIFile; CLock tlock(m_pAVIFile); return ReadExtra(&pf->extra, ckid, lpData, lpcbData); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIFile::CAVIFileImpl::EndRecord() { CAVIFile FAR * pf = m_pAVIFile; CLock tlock(m_pAVIFile); if ((pf->mode & (OF_WRITE | OF_READWRITE)) == 0) return ResultFromScode(AVIERR_READONLY); pf->fDirty = TRUE; pf->avihdr.dwFlags |= AVIF_ISINTERLEAVED; if (pf->lWriteLoc == 0) { pf->lWriteLoc = (pf->lHeaderSize + 1024 + 2047) & ~(2047); // Leave room for start of first 'rec' chunk.... pf->lWriteLoc -= 3 * sizeof(DWORD); pf->lDataListStart = pf->lWriteLoc - 3 * sizeof(DWORD); DPF("Writing first chunk at position %lu\n", pf->lWriteLoc); } shfileSeek(pf->hshfile, pf->lWriteLoc, SEEK_SET); if (pf->fInRecord) { #ifndef NOPADDING { DWORD dwCurOffset; DWORD dwPadNeeded; MMCKINFO ck; dwCurOffset = shfileSeek(pf->hshfile, 0, SEEK_CUR); // want to start next record at 2K-12 byte boundary dwCurOffset = (dwCurOffset + 12) % 2048; if (dwCurOffset != 0) { // we need to pad.... dwPadNeeded = 4096 - dwCurOffset - 8; if (dwPadNeeded >= 2048) dwPadNeeded -= 2048; ck.ckid = mmioFOURCC('J','U','N','K'); ck.cksize = dwPadNeeded; if (shfileCreateChunk(pf->hshfile, &ck, 0)) { return ResultFromScode(AVIERR_FILEWRITE); } shfileZero(pf->hshfile, dwPadNeeded); if (shfileAscend(pf->hshfile, &ck, 0)) return ResultFromScode(AVIERR_FILEWRITE); } } #endif if (shfileAscend(pf->hshfile, (MMCKINFO FAR *) &pf->ckRecord, 0)) return ResultFromScode(AVIERR_FILEWRITE); IndexSetLength(pf->px, pf->lRecordIndex, pf->ckRecord.cksize); // // Keep the main suggested buffer size as big as the biggest // record.... // if (pf->ckRecord.cksize + 3 * sizeof(DWORD) > pf->avihdr.dwSuggestedBufferSize) pf->avihdr.dwSuggestedBufferSize = pf->ckRecord.cksize + 3 * sizeof(DWORD); } /* Start the next 'rec' list */ pf->ckRecord.cksize = 0; pf->ckRecord.fccType = listtypeAVIRECORD; pf->fInRecord = TRUE; if (shfileCreateChunk(pf->hshfile, (MMCKINFO FAR *) &pf->ckRecord, MMIO_CREATELIST)) { return ResultFromScode(AVIERR_FILEWRITE); } pf->lWriteLoc = shfileSeek(pf->hshfile, 0, SEEK_CUR); pf->lRecordIndex = pf->px->nIndex; if (!AddToIndex(pf, pf->ckRecord.fccType, 0, pf->ckRecord.dwDataOffset - 2 * sizeof(DWORD), AVIIF_LIST)) { return ResultFromScode(AVIERR_MEMORY); } return ResultFromScode(AVIERR_OK); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIFile::CAVIFileImpl::Info( AVIFILEINFOW FAR * pfi, LONG lSize) { CAVIFile FAR * pf = m_pAVIFile; if (pfi == NULL) return ResultFromScode(AVIERR_BADPARAM); if (lSize < sizeof(AVIFILEINFOW)) return ResultFromScode(AVIERR_BUFFERTOOSMALL); CLock tlock(m_pAVIFile); pfi->dwMaxBytesPerSec = pf->avihdr.dwMaxBytesPerSec; pfi->dwFlags = (pf->avihdr.dwFlags & AVIF_ISINTERLEAVED); pfi->dwCaps = AVIFILECAPS_CANREAD | AVIFILECAPS_CANWRITE; pfi->dwStreams = pf->avihdr.dwStreams; pfi->dwSuggestedBufferSize = pf->avihdr.dwSuggestedBufferSize; pfi->dwWidth = pf->avihdr.dwWidth; pfi->dwHeight = pf->avihdr.dwHeight; pfi->dwScale = pf->avihdr.dwMicroSecPerFrame; pfi->dwRate = 1000000L; pfi->dwLength = pf->avihdr.dwTotalFrames; pfi->dwEditCount = 0; LoadUnicodeString(ghMod, IDS_AVIFILE, pfi->szFileType, NUMELMS(pfi->szFileType)); return AVIERR_OK; } /////////////////////////////////////////////////////////////////////////// // // AVIFileClose() // // close a AVIFile stream // /////////////////////////////////////////////////////////////////////////// STDMETHODIMP_(ULONG) CAVIStream::CUnknownImpl::Release() { CAVIStream FAR * pavi = m_pAVIStream; CLock tlock(pavi->pfile); uUseCount--; if (m_refs < 20) { DPF2("Stream %p: Usage--=%lx\n", (DWORD_PTR) (LPVOID) this, m_refs - 1); } shfileRelease(pavi->hshfile); if (!--m_refs) { if (pavi->hshfile != pavi->pfile->hshfile) { shfileClose(pavi->hshfile, 0); pavi->hshfile = 0; } if (pavi->pb && pavi->pb != pavi->pfile->pb) { EndBuffered(pavi->pb); pavi->pb = 0; } if (pavi->psx) { FreeStreamIndex(pavi->psx); pavi->psx = NULL; } pavi->fInit = FALSE; // this call can cause the AVIFile object to be deleted, and // thus we must release the critical section first. There is no // danger in this, as nothing unsafe can happen to us between // releasing it here and getting it again when we enter // the file Release() call. tlock.Exit(); pavi->pfile->m_AVIFile.Release(); return 0; } return m_refs; } /* - - - - - - - - */ /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// void CAVIStream::CAVIStreamImpl::ReadPalette(LONG lPos, LONG lPal, LPRGBQUAD prgb) { CAVIStream FAR * pavi = m_pAVIStream; CLock tlock(pavi->pfile); LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER) pavi->lpFormat; LONG l; int i,n; static struct { BYTE bFirstEntry; /* first entry to change */ BYTE bNumEntries; /* # entries to change (0 if 256) */ WORD wFlags; /* Mostly to preserve alignment... */ PALETTEENTRY peNew[256]; /* New color specifications */ } pc; DPF("Reading palette: lPos = %ld, lPal = %ld\n", lPos, lPal); if (lPal > lPos) lPal = 0; // // get the palette colors in the initial format header // if (lPal <= 0) { hmemcpy(prgb,(LPBYTE)lpbi+(int)lpbi->biSize, lpbi->biClrUsed * sizeof(RGBQUAD)); lPal = -1; } for (;;) { // // search index forward for next palette change // l = StreamFindSample(pavi->psx, lPal+1, FIND_FORMAT|FIND_NEXT); if (l < 0 || l > lPos || l == lPal) break; lPal = l; if (l <= (LONG) pavi->avistream.dwStart) continue; LONG off = StreamFindSample(pavi->psx, lPal, FIND_FORMAT|FIND_OFFSET); LONG len = StreamFindSample(pavi->psx, lPal, FIND_FORMAT|FIND_LENGTH); #ifdef DEBUG DWORD adw[2]; shfileSeek(pavi->hshfile, off-8, SEEK_SET); shfileRead(pavi->hshfile, (HPSTR)adw, sizeof(adw)); Assert(TWOCCFromFOURCC(adw[0]) == cktypePALchange); Assert(adw[1] == (DWORD) len); #endif if (len > (LONG)(sizeof(AVIPALCHANGE) + (LONG)lpbi->biClrUsed * sizeof(PALETTEENTRY) * 2)) { DPF("Palette chunk obviously too large!\n"); break; } // // read palchange from file and apply it // shfileSeek(pavi->hshfile, off, SEEK_SET); while (len >= (LONG)sizeof(AVIPALCHANGE)) { if (shfileRead(pavi->hshfile, (HPSTR)&pc, sizeof(AVIPALCHANGE)) != sizeof(AVIPALCHANGE)) { DPF("Error reading palette change\n"); break; } n = pc.bNumEntries == 0 ? 256 : (int)pc.bNumEntries; if ((DWORD) n > lpbi->biClrUsed) { DPF("%d colors in palette change, only %lu in movie!\n", n, lpbi->biClrUsed); break; } if (pc.bFirstEntry + n > (int)lpbi->biClrUsed) { DPF("%d colors in palette change, only %lu in movie!\n", n, lpbi->biClrUsed); break; } if (shfileRead(pavi->hshfile, (HPSTR)&pc.peNew, n * sizeof(PALETTEENTRY)) != (LONG) (n * sizeof(PALETTEENTRY))) { DPF("Error reading palette change entries\n"); break; } for (i=0; iargbq[pc.bFirstEntry+i].rgbRed = pc.peNew[i].peRed; pavi->argbq[pc.bFirstEntry+i].rgbGreen = pc.peNew[i].peGreen; pavi->argbq[pc.bFirstEntry+i].rgbBlue = pc.peNew[i].peBlue; pavi->argbq[pc.bFirstEntry+i].rgbReserved = 0; } len -= n * sizeof(PALETTEENTRY) + sizeof(AVIPALCHANGE); } } } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIStream::CAVIStreamImpl::ReadFormat(LONG lPos, LPVOID lpFormat, LONG FAR *lpcbFormat) { CAVIStream FAR * pavi = m_pAVIStream; CLock tlock(pavi->pfile); LONG lPal; LPBITMAPINFOHEADER lpbi; if (lpcbFormat == NULL) return ResultFromScode(AVIERR_BADPARAM); if (lpFormat == NULL || *lpcbFormat == 0) { *lpcbFormat = pavi->cbFormat; return AVIERR_OK; } if (pavi->avistream.dwFlags & AVISF_VIDEO_PALCHANGES) { Assert(pavi->psx); // // now go find the nearest palette change // lPal = StreamFindSample(pavi->psx, lPos, FIND_FORMAT|FIND_PREV); if (lPal < 0) lPal = 0; if (lPal != pavi->lPal) { ReadPalette(lPal, pavi->lPal, pavi->argbq); pavi->lPal = lPal; } lpbi = (LPBITMAPINFOHEADER) pavi->lpFormat; hmemcpy(lpFormat, lpbi, min((LONG) lpbi->biSize, *lpcbFormat)); if (*lpcbFormat > (LONG) lpbi->biSize) { hmemcpy((LPBYTE)lpFormat + (int)lpbi->biSize, pavi->argbq, min(lpbi->biClrUsed * sizeof(RGBQUAD), *lpcbFormat - lpbi->biSize)); } } else { hmemcpy(lpFormat, pavi->lpFormat, min(*lpcbFormat, pavi->cbFormat)); } if (*lpcbFormat < pavi->cbFormat) { *lpcbFormat = pavi->cbFormat; return ResultFromScode(AVIERR_BUFFERTOOSMALL); } *lpcbFormat = pavi->cbFormat; return AVIERR_OK; } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIStream::CAVIStreamImpl::Create(LPARAM lParam1, LPARAM lParam2) { return ResultFromScode(AVIERR_UNSUPPORTED); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIStream::CAVIStreamImpl::Info(AVISTREAMINFOW FAR * psi, LONG lSize) { CAVIStream FAR * pavi = m_pAVIStream; CLock tlock(pavi->pfile); if (psi == NULL) return ResultFromScode(AVIERR_BADPARAM); if (lSize < sizeof(pavi->avistream)) return ResultFromScode(AVIERR_BUFFERTOOSMALL); hmemcpy(psi, &pavi->avistream, sizeof(pavi->avistream)); return AVIERR_OK; } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP_(LONG) CAVIStream::CAVIStreamImpl::FindSample(LONG lPos, LONG lFlags) { CAVIStream FAR * pavi = m_pAVIStream; CLock tlock(pavi->pfile); if (pavi->paviBase) { // If we haven't copied over the data yet, delegate. return AVIStreamFindSample(pavi->paviBase, lPos, lFlags); } #ifdef _WIN32 if (!lPos && (lFlags & FIND_FROM_START)) { lPos = pavi->avistream.dwStart; } else #endif if (lPos < (LONG)pavi->avistream.dwStart) return -1; if (lPos >= (LONG)(pavi->avistream.dwStart + pavi->avistream.dwLength)) return -1; lPos = StreamFindSample(pavi->psx, lPos, (UINT)lFlags); return lPos < 0 ? -1 : lPos; } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIStream::CAVIStreamImpl::Read( LONG lStart, LONG lSamples, LPVOID lpBuffer, LONG cbBuffer, LONG FAR * plBytes, LONG FAR * plSamples) { CAVIStream FAR * pavi = m_pAVIStream; LONG lBytes; CLock tlock(pavi->pfile); if (pavi->paviBase) { // If we haven't copied over the data yet, delegate. return AVIStreamRead(pavi->paviBase, lStart, lSamples, lpBuffer, cbBuffer, plBytes, plSamples); } Assert(pavi->psx); // !!! What if start too big? Length too long? if (lStart < (LONG) pavi->avistream.dwStart) { DPF("Read before start!\n"); return ResultFromScode(AVIERR_BADPARAM); } // Handle one of the sillier aspects of AVI files: // Certain RLE-encoded files have their first frames split // up into lots of small pieces. This code puts all of those // pieces back together again if necessary. if ((lStart == (LONG) pavi->avistream.dwStart) && (pavi->avistream.fccType == streamtypeVIDEO) && (pavi->avistream.dwInitialFrames > 0)) { LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER) pavi->lpFormat; LPVOID lp; lStart -= (LONG) pavi->avistream.dwInitialFrames; lBytes = (DWORD)(WORD)DIBWIDTHBYTES(lpbi) * (DWORD)(WORD)lpbi->biHeight; // // a NULL buffer means return the size buffer needed to read // the given sample. // if (lpBuffer == NULL || cbBuffer == 0) { if (plBytes) *plBytes = lBytes; return AVIERR_OK; } if (cbBuffer < lBytes) { if (plBytes) *plBytes = lBytes; DPF("ReadFirst: Buffer is %ld bytes, needed %ld\n", cbBuffer, lBytes); return ResultFromScode(AVIERR_BUFFERTOOSMALL); } lp = GlobalAllocPtr(GMEM_MOVEABLE, lBytes); if (!lp) return ResultFromScode(AVIERR_MEMORY); while (lStart <= (LONG)pavi->avistream.dwStart) { if (StreamRead(pavi->psx, lStart, 1, lp, lBytes) < 0) { GlobalFreePtr(lp); return ResultFromScode(AVIERR_FILEREAD); } // We probably shouldn't assume RLE here.... DecodeRle(lpbi, (BYTE _huge *) lpBuffer, (BYTE _huge *) lp, lBytes); lStart++; } GlobalFreePtr(lp); goto done; } // // do the read // lBytes = StreamRead(pavi->psx,lStart,lSamples,lpBuffer,cbBuffer); // // check for error // if (lBytes < 0) { if (plBytes) *plBytes = 0; if (plSamples) *plSamples = 0; if (cbBuffer == 0) return ResultFromScode(AVIERR_ERROR); // // the error may have been buffer too small, check this. // if (cbBuffer < pavi->psx->lSampleSize) return ResultFromScode(AVIERR_BUFFERTOOSMALL); lBytes = StreamFindSample(pavi->psx,lStart,FIND_PREV|FIND_LENGTH); if (cbBuffer < lBytes) { if (plBytes) *plBytes = lBytes; return ResultFromScode(AVIERR_BUFFERTOOSMALL); } else return ResultFromScode(AVIERR_FILEREAD); } done: if (plBytes) *plBytes = lBytes; if (plSamples) { LONG lSampleSize = pavi->psx->lSampleSize; if (lSampleSize) *plSamples = lBytes / lSampleSize; else *plSamples = 1; } return AVIERR_OK; } /************************************************************************** * @doc INTERNAL DRAWDIB * * @api BOOL | DibEq | This function compares two dibs. * * @parm LPBITMAPINFOHEADER lpbi1 | Pointer to one bitmap. * this DIB is assumed to have the colors after the BITMAPINFOHEADER * * @parm LPBITMAPINFOHEADER | lpbi2 | Pointer to second bitmap. * this DIB is assumed to have the colors after biSize bytes. * * @rdesc Returns TRUE if bitmaps are identical, FALSE otherwise. * **************************************************************************/ inline BOOL DibEq(LPBITMAPINFOHEADER lpbi1, LPBITMAPINFOHEADER lpbi2) { return lpbi1->biCompression == lpbi2->biCompression && lpbi1->biSize == lpbi2->biSize && lpbi1->biWidth == lpbi2->biWidth && lpbi1->biHeight == lpbi2->biHeight && lpbi1->biBitCount == lpbi2->biBitCount; } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIStream::CAVIStreamImpl::SetFormat(LONG lPos,LPVOID lpFormat,LONG cbFormat) { CAVIStream FAR * pavi = m_pAVIStream; CLock tlock(pavi->pfile); LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER) lpFormat; int i; RGBQUAD FAR * lprgb; struct { BYTE bFirstEntry; /* first entry to change */ BYTE bNumEntries; /* # entries to change (0 if 256) */ WORD wFlags; /* Mostly to preserve alignment... */ PALETTEENTRY pe[256]; } s; // // Make sure the stream isn't read-only // if ((pavi->pfile->mode & (OF_WRITE | OF_READWRITE)) == 0) return ResultFromScode(AVIERR_READONLY); if (pavi->lpFormat == NULL) { // !!! If we are writing to an existing file, and not to a new file, we // have a limitation where we cannot grow the size of the header. // Check to see if the header will take up too much room! if (pavi->pfile->lWriteLoc > 0) { LONG lHeader = pavi->cbFormat + pavi->pfile->lHeaderSize + 2 * sizeof(DWORD); if (lHeader > pavi->pfile->lDataListStart) { DPF("Header will be too big with this format!\n"); return ResultFromScode(AVIERR_UNSUPPORTED); } } pavi->pfile->lHeaderSize += cbFormat + 2 * sizeof(DWORD); // This is a new stream, whose format hasn't been set. pavi->lpFormat = GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, cbFormat); if (!pavi->lpFormat) { return ResultFromScode(AVIERR_MEMORY); } hmemcpy(pavi->lpFormat, lpFormat, cbFormat); pavi->cbFormat = cbFormat; if (pavi->avistream.fccType == streamtypeVIDEO) { if (ISRECTBOGUS(&pavi->avistream.rcFrame, pavi->pfile->avihdr.dwWidth, pavi->pfile->avihdr.dwHeight, lpbi)) { DPF("Resetting stream rectangle....\n"); SetRect(&pavi->avistream.rcFrame, 0, 0, (int)lpbi->biWidth, (int)lpbi->biHeight); } if (lpbi->biClrUsed > 0) { // Get the right colors, so that we can detect palette changes hmemcpy(pavi->argbq, (LPBYTE) lpbi + lpbi->biSize, lpbi->biClrUsed * sizeof(RGBQUAD)); } /* Make sure the width and height of the created file are right.... */ pavi->pfile->avihdr.dwWidth = max(pavi->pfile->avihdr.dwWidth, (DWORD) pavi->avistream.rcFrame.right); pavi->pfile->avihdr.dwHeight = max(pavi->pfile->avihdr.dwHeight, (DWORD) pavi->avistream.rcFrame.bottom); } return ResultFromScode(AVIERR_OK); } // // First, check if the format is actually different.... // if (cbFormat == pavi->cbFormat && (_fmemcmp(pavi->lpFormat, lpFormat, (int) cbFormat) == 0)) return ResultFromScode(AVIERR_OK); // // We really only support format changes if they're palette changes... // if (pavi->avistream.fccType != streamtypeVIDEO) { return ResultFromScode(AVIERR_UNSUPPORTED); } // // Can only currently set the palette at the end of the file // if (lPos < (LONG) (pavi->avistream.dwStart + pavi->avistream.dwLength)) return ResultFromScode(AVIERR_UNSUPPORTED); // // We can only change the palette for things with palettes.... // if (lpbi->biBitCount > 8 || lpbi->biClrUsed == 0) return ResultFromScode(AVIERR_UNSUPPORTED); // // Be sure only the palette is changing, nothing else.... // if (cbFormat != pavi->cbFormat) return ResultFromScode(AVIERR_UNSUPPORTED); if (!DibEq((LPBITMAPINFOHEADER) lpFormat, (LPBITMAPINFOHEADER) pavi->lpFormat)) return ResultFromScode(AVIERR_UNSUPPORTED); // !!! Need to do here: // Get the correct palette for this point in the file, and check // that the new palette is in fact different. lprgb = (RGBQUAD FAR *) ((LPBYTE) lpbi + lpbi->biSize); if (_fmemcmp(pavi->argbq, lprgb, (UINT) lpbi->biClrUsed * sizeof(RGBQUAD)) == 0) return ResultFromScode(AVIERR_OK); // // Make the new format the current one.... // hmemcpy(pavi->argbq, lprgb, lpbi->biClrUsed * sizeof(RGBQUAD)); pavi->lPal = lPos; // // And be sure the stream is marked as having changes... // pavi->avistream.dwFlags |= AVISF_VIDEO_PALCHANGES; s.bFirstEntry = 0; s.bNumEntries = (BYTE) lpbi->biClrUsed; s.wFlags = 0; for (i = 0; i < (int) lpbi->biClrUsed; i++, lprgb++) { s.pe[i].peRed = lprgb->rgbRed; s.pe[i].peGreen = lprgb->rgbGreen; s.pe[i].peBlue = lprgb->rgbBlue; } // !!! Hack: use Write to write the palette change.... return Write(lPos, 0, &s, sizeof(AVIPALCHANGE) + lpbi->biClrUsed * sizeof(PALETTEENTRY), AVIIF_NOTIME, NULL, NULL); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIStream::CAVIStreamImpl::Write(LONG lStart, LONG lSamples, LPVOID lpData, LONG cbData, DWORD dwFlags, LONG FAR *plSampWritten, LONG FAR *plBytesWritten) { CAVIStream FAR * pavi = m_pAVIStream; CLock tlock(pavi->pfile); MMCKINFO ck; WORD cktype; HRESULT hr; DWORD dwmsec; // !!! Idea: if it's audio-like data, and everything else matches the // last chunk written out, then merge the new data in with the old // data, rather than making a new chunk.... if ((pavi->pfile->mode & (OF_WRITE | OF_READWRITE)) == 0) return ResultFromScode(AVIERR_READONLY); if (!pavi->lpFormat) { // The format must be set before any write calls // are made.... return ResultFromScode(E_UNEXPECTED); } if (pavi->avistream.fccType == streamtypeAUDIO) cktype = aviTWOCC('w', 'b'); else if (pavi->avistream.fccType == streamtypeVIDEO) { if (dwFlags & AVIIF_NOTIME) cktype = aviTWOCC('p', 'c'); else { LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER) pavi->lpFormat; if ((dwFlags & AVIIF_KEYFRAME) || (lpbi->biCompression <= BI_RLE8 && cbData == (LONG) lpbi->biSizeImage)) cktype = aviTWOCC('d', 'b'); else cktype = aviTWOCC('d', 'c'); // !!! 00dx ack! } } else { cktype = aviTWOCC('d', 'c'); } ck.ckid = MAKEAVICKID(cktype, pavi->iStream); ck.cksize = cbData; if (lStart < 0) lStart = pavi->avistream.dwStart + pavi->avistream.dwLength; if (lStart > (LONG) (pavi->avistream.dwStart + pavi->avistream.dwLength)) { if (pavi->avistream.dwSampleSize == 0) { // !!! hack--insert lots of blank index entries.... while (lStart > (LONG) (pavi->avistream.dwStart + pavi->avistream.dwLength)) { #if 1 hr = Write(pavi->avistream.dwStart + pavi->avistream.dwLength, 1, NULL, 0, 0, NULL, NULL); if (FAILED(hr)) return hr; #else if (!AddToIndex(pavi->pfile, ck.ckid, 0, 0, 0)) return ResultFromScode(AVIERR_MEMORY); ++pavi->avistream.dwLength; pavi->pfile->avihdr.dwFlags |= AVIF_MUSTUSEINDEX; #endif } } else return ResultFromScode(AVIERR_BADPARAM); } if (lStart < (LONG) (pavi->avistream.dwStart + pavi->avistream.dwLength)) return ResultFromScode(AVIERR_UNSUPPORTED); pavi->pfile->fDirty = TRUE; if (pavi->pfile->lWriteLoc == 0) { pavi->pfile->lWriteLoc = (pavi->pfile->lHeaderSize + 1024 + 2047) & ~(2047); pavi->pfile->lDataListStart = pavi->pfile->lWriteLoc - 3 * sizeof(DWORD); DPF("Writing first chunk at position %lu\n", pavi->pfile->lWriteLoc); } #if 0 if ((lStart == (LONG) (pavi->avistream.dwStart + pavi->avistream.dwLength)) && (pavi->avistream.fccType == streamtypeAUDIO) && (pavi->pfile->lIndex > 0)) { AVIINDEXENTRY idx = pavi->pfile->pIndex[pavi->pfile->lIndex - 1]; if ((idx.ckid == ckid) && (idx.dwChunkOffset + 2 * sizeof(DWORD) + idx.dwChunkLength == lWriteLoc)) { // We could append to the previous chunk here.... } } #endif #ifdef DONTWRITEZEROLENGTH if (cbData == 0) { ck.dwDataOffset = 0; pavi->pfile->avihdr.dwFlags |= AVIF_MUSTUSEINDEX; } else #endif { shfileSeek(pavi->hshfile, pavi->pfile->lWriteLoc, SEEK_SET); shfileCreateChunk(pavi->hshfile, &ck, 0); if (cbData) { if (shfileWrite(pavi->hshfile, (HPSTR) lpData, cbData) != cbData) return ResultFromScode(AVIERR_FILEWRITE); } if (shfileAscend(pavi->hshfile, &ck, 0) != 0) return ResultFromScode(AVIERR_FILEWRITE); pavi->pfile->lWriteLoc = shfileSeek(pavi->hshfile, 0, SEEK_CUR); } if (!AddToIndex(pavi->pfile, ck.ckid, cbData, ck.dwDataOffset - 2 * sizeof(DWORD), dwFlags)) return ResultFromScode(AVIERR_MEMORY); // // if we dont have a stream index now is a good time to make one. // if (pavi->psx == NULL) { pavi->psx = MakeStreamIndex(pavi->pfile->px, pavi->iStream, (LONG)pavi->avistream.dwStart - pavi->avistream.dwInitialFrames, (LONG)pavi->avistream.dwSampleSize, pavi->pfile->hshfile, shfileReadProc, NULL); //!!! what about pavi->pb if (!(dwFlags & AVIIF_NOTIME)) pavi->psx->lEnd -= lSamples; // correct for the decrement below } if (pavi->psx == NULL) { DPF("CAVIStream::Write no stream index!\n"); return ResultFromScode(AVIERR_MEMORY); } if (!(dwFlags & AVIIF_NOTIME)) { pavi->avistream.dwLength += lSamples; if (pavi->psx) pavi->psx->lEnd += lSamples; } if (cbData > (LONG) pavi->avistream.dwSuggestedBufferSize) pavi->avistream.dwSuggestedBufferSize = cbData; if (cbData > (LONG) pavi->pfile->avihdr.dwSuggestedBufferSize) pavi->pfile->avihdr.dwSuggestedBufferSize = cbData; // Recalculate the overall file length.... dwmsec = muldiv32(pavi->avistream.dwLength, pavi->avistream.dwScale * 1000L, pavi->avistream.dwRate); pavi->pfile->avihdr.dwTotalFrames = max(pavi->pfile->avihdr.dwTotalFrames, (DWORD) muldiv32(dwmsec, 1000L, pavi->pfile->avihdr.dwMicroSecPerFrame)); // !!! The above calculation could easily overflow. // !!! NEEDS TO BE REORGANIZED! if (plBytesWritten) *plBytesWritten = cbData; if (plSampWritten) *plSampWritten = lSamples; return ResultFromScode(AVIERR_OK); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIStream::CAVIStreamImpl::Delete(LONG lStart,LONG lSamples) { CAVIStream FAR * pavi = m_pAVIStream; CLock tlock(pavi->pfile); if ((pavi->pfile->mode & (OF_WRITE | OF_READWRITE)) == 0) return ResultFromScode(AVIERR_READONLY); // go through and kill things from the index? // !!! what about keyframe boundaries? return ResultFromScode(AVIERR_UNSUPPORTED); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIStream::CAVIStreamImpl::ReadData(DWORD ckid, LPVOID lp, LONG FAR *lpcb) { CAVIStream FAR * pavi = m_pAVIStream; CLock tlock(pavi->pfile); return ReadExtra(&pavi->extra, ckid, lp, lpcb); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIStream::CAVIStreamImpl::WriteData(DWORD ckid, LPVOID lp, LONG cb) { CAVIStream FAR * pavi = m_pAVIStream; CLock tlock(pavi->pfile); DPF("WriteData asked to write %ld bytes\n", cb); if ((pavi->pfile->mode & (OF_WRITE | OF_READWRITE)) == 0) return ResultFromScode(AVIERR_READONLY); // !!! If we are writing to an existing file, and not to a new file, we have // a limitation where we cannot grow the size of the header. // Check to see if the header will take up too much room. if (pavi->pfile->lWriteLoc > 0) { LONG lHeader = cb + pavi->pfile->lHeaderSize + 2 * sizeof(DWORD); if (lHeader > pavi->pfile->lDataListStart) { DPF("Header will be too big with this extra data!\n"); return ResultFromScode(AVIERR_UNSUPPORTED); } } pavi->pfile->lHeaderSize += cb + 3 * sizeof(DWORD); pavi->pfile->fDirty = TRUE; return WriteExtra(&pavi->extra, ckid, lp, cb); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// #if 0 STDMETHODIMP CAVIStream::CAVIStreamImpl::Clone(PAVISTREAM FAR * ppaviNew) { CAVIStream FAR * pavi = m_pAVIStream; return ResultFromScode(AVIERR_UNSUPPORTED); } #endif STDMETHODIMP CAVIStream::CStreamingImpl::Begin(LONG lStart, LONG lEnd, LONG lRate) { CAVIStream FAR * pavi = m_pAVIStream; CLock tlock(pavi->pfile); #ifdef USE_DIRECTIO if (shfileIsDirect(pavi->hshfile)) { shfileStreamStart(pavi->hshfile); } else #endif if (pavi->pb) BeginBufferedStreaming(pavi->pb, lRate > 0); return ResultFromScode(AVIERR_OK); } STDMETHODIMP CAVIStream::CStreamingImpl::End() { CAVIStream FAR * pavi = m_pAVIStream; CLock tlock(pavi->pfile); #ifdef USE_DIRECTIO if (shfileIsDirect(pavi->hshfile)) { shfileStreamStop(pavi->hshfile); } else #endif if (pavi->pb) EndBufferedStreaming(pavi->pb); return ResultFromScode(AVIERR_OK); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// static BOOL AddToIndex(CAVIFile FAR * pfile, DWORD ckid, DWORD cksize, LONG off, DWORD dwFlags) { PAVIINDEX px; AVIINDEXENTRY idx; idx.ckid = ckid; idx.dwChunkOffset = off; idx.dwChunkLength = cksize; idx.dwFlags = dwFlags; px = IndexAddFileIndex(pfile->px, &idx, 1, 0, FALSE); if (px == NULL) return FALSE; // // GlobalReAlloc may have moved our pointer, we need to patch all // places we use it! // if (px != pfile->px) { DPF("Index pointer has changed!\n"); pfile->px = px; for (int i=0; i<(int)pfile->avihdr.dwStreams; i++) { CAVIStream FAR *ps = pfile->ps[i]; if (ps->psx) ps->psx->px = px; } if (pfile->pb) pfile->pb->px = px; } return TRUE; } #ifdef _WIN32 STDMETHODIMP CAVIStream::CAVIStreamImpl::SetInfo(AVISTREAMINFOW FAR *lpInfo, LONG cbInfo) { CAVIStream FAR * pavi = m_pAVIStream; CLock tlock(pavi->pfile); if ((pavi->pfile->mode & (OF_WRITE | OF_READWRITE)) == 0) return ResultFromScode(AVIERR_READONLY); if ((cbInfo < sizeof(AVISTREAMINFOW)) || (IsBadReadPtr(lpInfo, sizeof(AVISTREAMINFOW)))) return ResultFromScode(AVIERR_BADPARAM); // Things we don't copy: // fccType // fccHandler // dwFlags // dwCaps // dwLength // dwInitialFrames // dwSuggestedBufferSize // dwSampleSize // dwEditCount // dwFormatChangeCount pavi->avistream.wPriority = lpInfo->wPriority; pavi->avistream.wLanguage = lpInfo->wLanguage; pavi->avistream.dwScale = lpInfo->dwScale; pavi->avistream.dwRate = lpInfo->dwRate; pavi->avistream.dwStart = lpInfo->dwStart; // !!! ??? pavi->avistream.dwQuality = lpInfo->dwQuality; pavi->avistream.rcFrame = lpInfo->rcFrame; if (lpInfo->szName[0]) _fmemcpy(pavi->avistream.szName, lpInfo->szName, sizeof(pavi->avistream.szName)); pavi->pfile->fDirty = TRUE; return NOERROR; } STDMETHODIMP CAVIFile::CAVIFileImpl::DeleteStream(DWORD fccType, LONG lParam) { CAVIFile FAR * pfile = m_pAVIFile; CAVIStream FAR *pavi; int iStreamCur; int iStreamWant; int iStream; // thread locking CLock tlock(pfile); iStreamWant = (int)lParam; if (iStreamWant < 0 || iStreamWant >= (int)pfile->avihdr.dwStreams) return ResultFromScode(AVIERR_NODATA); /* Allocate stream data stuff, read streams */ for (iStreamCur = -1, iStream = 0; iStream < (int)pfile->avihdr.dwStreams; iStream++) { if (fccType == 0 || pfile->ps[iStream]->avistream.fccType == fccType) iStreamCur++; if (iStreamCur == iStreamWant) break; } if (iStreamCur != iStreamWant) return ResultFromScode(AVIERR_NODATA); pavi = pfile->ps[iStream]; // Is somebody using this stream? if (pavi->fInit) return ResultFromScode(AVIERR_UNSUPPORTED); pfile->avihdr.dwStreams++; pfile->lHeaderSize -= sizeof(AVIStreamHeader) + 8 * sizeof(DWORD) + lstrlenW(pavi->avistream.szName); while (iStream < (int) pfile->avihdr.dwStreams) { pfile->ps[iStream] = pfile->ps[iStream + 1]; iStream++; } delete pavi; return ResultFromScode(AVIERR_UNSUPPORTED); } #else /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIFile::CAVIFileImpl::Reserved1(void) { return ResultFromScode(AVIERR_UNSUPPORTED); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIFile::CAVIFileImpl::Reserved2(void) { return ResultFromScode(AVIERR_UNSUPPORTED); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIFile::CAVIFileImpl::Reserved3(void) { return ResultFromScode(AVIERR_UNSUPPORTED); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIFile::CAVIFileImpl::Reserved4(void) { return ResultFromScode(AVIERR_UNSUPPORTED); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIFile::CAVIFileImpl::Reserved5(void) { return ResultFromScode(AVIERR_UNSUPPORTED); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIStream::CAVIStreamImpl::Reserved1(void) { return ResultFromScode(AVIERR_UNSUPPORTED); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIStream::CAVIStreamImpl::Reserved2(void) { return ResultFromScode(AVIERR_UNSUPPORTED); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIStream::CAVIStreamImpl::Reserved3(void) { return ResultFromScode(AVIERR_UNSUPPORTED); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIStream::CAVIStreamImpl::Reserved4(void) { return ResultFromScode(AVIERR_UNSUPPORTED); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// STDMETHODIMP CAVIStream::CAVIStreamImpl::Reserved5(void) { return ResultFromScode(AVIERR_UNSUPPORTED); } #endif #ifdef CUSTOMMARSHAL CAVIFile::CMarshalImpl::CMarshalImpl( CAVIFile FAR* pAVIFile) { m_pAVIFile = pAVIFile; } STDMETHODIMP CAVIFile::CMarshalImpl::QueryInterface( const IID FAR& iid, void FAR* FAR* ppv) { return m_pAVIFile->m_pUnknownOuter->QueryInterface(iid, ppv); } /* - - - - - - - - */ STDMETHODIMP_(ULONG) CAVIFile::CMarshalImpl::AddRef() { return m_pAVIFile->m_pUnknownOuter->AddRef(); } /* - - - - - - - - */ STDMETHODIMP_(ULONG) CAVIFile::CMarshalImpl::Release() { return m_pAVIFile->m_pUnknownOuter->Release(); } /* - - - - - - - - */ // *** IMarshal methods *** STDMETHODIMP CAVIFile::CMarshalImpl::GetUnmarshalClass (THIS_ REFIID riid, LPVOID pv, DWORD dwDestContext, LPVOID pvDestContext, DWORD mshlflags, LPCLSID pCid) { HRESULT hr = NOERROR; IUnknown FAR * pUnk = &m_pAVIFile->m_Unknown; DPF("(F) UnMarshalClass called (context = %lx)\n", dwDestContext); if (dwDestContext != MSHCTX_LOCAL) { LPMARSHAL pMarshal; DPF("Marshal context is %lu: delegating...\n", dwDestContext); hr = CoGetStandardMarshal(riid, NULL, dwDestContext, pvDestContext, mshlflags, &pMarshal); if (hr != NOERROR) return hr; hr = pMarshal->GetUnmarshalClass(riid, pv, dwDestContext, pvDestContext, mshlflags, pCid); pMarshal->Release(); return hr; } *pCid = CLSID_AVISimpleUnMarshal; return hr; } STDMETHODIMP CAVIFile::CMarshalImpl::GetMarshalSizeMax (THIS_ REFIID riid, LPVOID pv, DWORD dwDestContext, LPVOID pvDestContext, DWORD mshlflags, LPDWORD pSize) { HRESULT hr = NOERROR; IUnknown FAR * pUnk = &m_pAVIFile->m_Unknown; if (dwDestContext != MSHCTX_LOCAL) { LPMARSHAL pMarshal; hr = CoGetStandardMarshal(riid, NULL, dwDestContext, pvDestContext, mshlflags, &pMarshal); if (hr != NOERROR) return hr; hr = pMarshal->GetMarshalSizeMax(riid, pv, dwDestContext, pvDestContext, mshlflags, pSize); pMarshal->Release(); return hr; } *pSize = sizeof(pUnk); return hr; } STDMETHODIMP CAVIFile::CMarshalImpl::MarshalInterface (THIS_ LPSTREAM pStm, REFIID riid, LPVOID pv, DWORD dwDestContext, LPVOID pvDestContext, DWORD mshlflags) { HRESULT hr = NOERROR; IUnknown FAR * pUnk = &m_pAVIFile->m_Unknown; DPF("MarshalInterface (F) called\n"); if (dwDestContext != MSHCTX_LOCAL) { LPMARSHAL pMarshal; DPF("Marshal context is %lu: delegating...\n", dwDestContext); hr = CoGetStandardMarshal(riid, NULL, dwDestContext, pvDestContext, mshlflags, &pMarshal); if (hr != NOERROR) return hr; hr = pMarshal->MarshalInterface(pStm, riid, pv, dwDestContext, pvDestContext, mshlflags); pMarshal->Release(); return hr; } if ((riid != IID_IAVIStream && riid != IID_IAVIFile && riid != IID_IUnknown)) return ResultFromScode(E_INVALIDARG); if ((hr = pStm->Write(&pUnk, sizeof(pUnk), NULL)) == NOERROR) AddRef(); DPF("Returns %lx\n", (DWORD) (LPVOID) hr); return hr; } STDMETHODIMP CAVIFile::CMarshalImpl::UnmarshalInterface (THIS_ LPSTREAM pStm, REFIID riid, LPVOID FAR* ppv) { HRESULT hr = ResultFromScode(E_FAIL); DPF("(F) UnMarshalInterface called!!!\n"); return hr; } STDMETHODIMP CAVIFile::CMarshalImpl::ReleaseMarshalData (THIS_ LPSTREAM pStm) { HRESULT hr = NOERROR; IUnknown FAR * pUnk; hr = pStm->Read(&pUnk,sizeof(pUnk),NULL); DPF("(F) ReleaseMarshalData\n"); if (hr == NOERROR) pUnk->Release(); return hr; } STDMETHODIMP CAVIFile::CMarshalImpl::DisconnectObject (THIS_ DWORD dwReserved) { HRESULT hr = NOERROR; return hr; } CAVIStream::CMarshalImpl::CMarshalImpl( CAVIStream FAR* pAVIStream) { m_pAVIStream = pAVIStream; } STDMETHODIMP CAVIStream::CMarshalImpl::QueryInterface( const IID FAR& iid, void FAR* FAR* ppv) { return m_pAVIStream->m_pUnknownOuter->QueryInterface(iid, ppv); } /* - - - - - - - - */ STDMETHODIMP_(ULONG) CAVIStream::CMarshalImpl::AddRef() { return m_pAVIStream->m_pUnknownOuter->AddRef(); } /* - - - - - - - - */ STDMETHODIMP_(ULONG) CAVIStream::CMarshalImpl::Release() { return m_pAVIStream->m_pUnknownOuter->Release(); } /* - - - - - - - - */ // *** IMarshal methods *** STDMETHODIMP CAVIStream::CMarshalImpl::GetUnmarshalClass (THIS_ REFIID riid, LPVOID pv, DWORD dwDestContext, LPVOID pvDestContext, DWORD mshlflags, LPCLSID pCid) { HRESULT hr = NOERROR; IUnknown FAR * pUnk = &m_pAVIStream->m_Unknown; DPF("(S) UnMarshalClass called (context = %lx)\n", dwDestContext); *pCid = CLSID_AVISimpleUnMarshal; return hr; } STDMETHODIMP CAVIStream::CMarshalImpl::GetMarshalSizeMax (THIS_ REFIID riid, LPVOID pv, DWORD dwDestContext, LPVOID pvDestContext, DWORD mshlflags, LPDWORD pSize) { HRESULT hr = NOERROR; IUnknown FAR * pUnk = &m_pAVIStream->m_Unknown; *pSize = sizeof(pUnk); return hr; } STDMETHODIMP CAVIStream::CMarshalImpl::MarshalInterface (THIS_ LPSTREAM pStm, REFIID riid, LPVOID pv, DWORD dwDestContext, LPVOID pvDestContext, DWORD mshlflags) { HRESULT hr = NOERROR; IUnknown FAR * pUnk = &m_pAVIStream->m_Unknown; DPF("MarshalInterface (S) called\n"); if ((riid != IID_IAVIStream && riid != IID_IAVIStream && riid != IID_IUnknown)) return ResultFromScode(E_INVALIDARG); if ((hr = pStm->Write(&pUnk, sizeof(pUnk), NULL)) == NOERROR) AddRef(); DPF("Returns %lx\n", (DWORD) (LPVOID) hr); return hr; } STDMETHODIMP CAVIStream::CMarshalImpl::UnmarshalInterface (THIS_ LPSTREAM pStm, REFIID riid, LPVOID FAR* ppv) { HRESULT hr = ResultFromScode(E_FAIL); DPF("(S) UnMarshalInterface called!!!\n"); return hr; } STDMETHODIMP CAVIStream::CMarshalImpl::ReleaseMarshalData (THIS_ LPSTREAM pStm) { HRESULT hr = NOERROR; IUnknown FAR * pUnk; hr = pStm->Read(&pUnk,sizeof(pUnk),NULL); DPF("(S) ReleaseMarshalData\n"); if (hr == NOERROR) pUnk->Release(); return hr; } STDMETHODIMP CAVIStream::CMarshalImpl::DisconnectObject (THIS_ DWORD dwReserved) { HRESULT hr = NOERROR; return hr; } #endif // CUSTOMMARSHAL only /*************************************************************************** DecodeRle - 'C' version Play back a RLE buffer into a DIB buffer returns none ***************************************************************************/ void DecodeRle(LPBITMAPINFOHEADER lpbi, BYTE _huge *pb, BYTE _huge *prle, DWORD dwInSize) { BYTE cnt; BYTE b; WORD x; WORD dx,dy; DWORD wWidthBytes; DWORD dwOutSize; DWORD dwJump; #define RLE_ESCAPE 0 #define RLE_EOL 0 #define RLE_EOF 1 #define RLE_JMP 2 #define RLE_RUN 3 #if 0 #ifndef _WIN32 // // this uses ASM code found in RLEA.ASM // if (!(WinFlags & WF_CPU286)) DecodeRle386(lpbi, pb, prle); else if (lpbi->biSizeImage < 65536l) DecodeRle286(lpbi, pb, prle); else #endif #endif #define EatOutput(_x_) \ { \ if (dwOutSize < (_x_)) { \ return; \ } \ dwOutSize -= (_x_); \ } #define EatInput(_x_) \ { \ if (dwInSize < (_x_)) { \ return; \ } \ dwInSize -= (_x_); \ } if (lpbi->biHeight <= 0) { return; } { wWidthBytes = (WORD)lpbi->biWidth+3 & ~3; dwOutSize = wWidthBytes * (DWORD)lpbi->biHeight; x = 0; for(;;) { EatInput(2); cnt = *prle++; b = *prle++; if (cnt == RLE_ESCAPE) { switch (b) { case RLE_EOF: return; case RLE_EOL: EatOutput(wWidthBytes - x); pb += wWidthBytes - x; x = 0; break; case RLE_JMP: EatInput(2); dx = (WORD)*prle++; dy = (WORD)*prle++; dwJump = (DWORD)wWidthBytes * dy + dx; EatOutput(dwJump); pb += dwJump; x += dx; break; default: cnt = b; EatOutput(cnt); EatInput(cnt); x += cnt; while (cnt-- > 0) *pb++ = *prle++; if (b & 1) { EatInput(1); prle++; } break; } } else { x += cnt; EatOutput(cnt); while (cnt-- > 0) *pb++ = b; } } } } CAVIFile::CPersistStorageImpl::CPersistStorageImpl( CAVIFile FAR* pAVIFile) { m_pAVIFile = pAVIFile; } STDMETHODIMP CAVIFile::CPersistStorageImpl::QueryInterface( const IID FAR& iid, void FAR* FAR* ppv) { return m_pAVIFile->m_pUnknownOuter->QueryInterface(iid, ppv); } /* - - - - - - - - */ STDMETHODIMP_(ULONG) CAVIFile::CPersistStorageImpl::AddRef() { return m_pAVIFile->m_pUnknownOuter->AddRef(); } /* - - - - - - - - */ STDMETHODIMP_(ULONG) CAVIFile::CPersistStorageImpl::Release() { return m_pAVIFile->m_pUnknownOuter->Release(); } /* - - - - - - - - */ // *** IPersist methods *** STDMETHODIMP CAVIFile::CPersistStorageImpl::GetClassID (LPCLSID lpClassID) { CAVIFile FAR * pfile = m_pAVIFile; return NOERROR; } // *** IPersistStorage methods *** STDMETHODIMP CAVIFile::CPersistStorageImpl::IsDirty () { CAVIFile FAR * pfile = m_pAVIFile; if (pfile->fDirty) return NOERROR; else return ResultFromScode(S_FALSE); } STDMETHODIMP CAVIFile::CPersistStorageImpl::InitNew (LPSTORAGE pStg) { CAVIFile FAR * pfile = m_pAVIFile; return NOERROR; } STDMETHODIMP CAVIFile::CPersistStorageImpl::Load (LPSTORAGE pStg) { CAVIFile FAR * pfile = m_pAVIFile; return NOERROR; } STDMETHODIMP CAVIFile::CPersistStorageImpl::Save (LPSTORAGE pStgSave, BOOL fSameAsLoad) { CAVIFile FAR * pfile = m_pAVIFile; return NOERROR; } STDMETHODIMP CAVIFile::CPersistStorageImpl::SaveCompleted (LPSTORAGE pStgNew) { CAVIFile FAR * pfile = m_pAVIFile; return NOERROR; } STDMETHODIMP CAVIFile::CPersistStorageImpl::HandsOffStorage () { CAVIFile FAR * pfile = m_pAVIFile; return NOERROR; } CAVIFile::CPersistFileImpl::CPersistFileImpl( CAVIFile FAR* pAVIFile) { m_pAVIFile = pAVIFile; } STDMETHODIMP CAVIFile::CPersistFileImpl::QueryInterface( const IID FAR& iid, void FAR* FAR* ppv) { return m_pAVIFile->m_pUnknownOuter->QueryInterface(iid, ppv); } /* - - - - - - - - */ STDMETHODIMP_(ULONG) CAVIFile::CPersistFileImpl::AddRef() { return m_pAVIFile->m_pUnknownOuter->AddRef(); } /* - - - - - - - - */ STDMETHODIMP_(ULONG) CAVIFile::CPersistFileImpl::Release() { return m_pAVIFile->m_pUnknownOuter->Release(); } /* - - - - - - - - */ // *** IPersist methods *** STDMETHODIMP CAVIFile::CPersistFileImpl::GetClassID (LPCLSID lpClassID) { CAVIFile FAR * pfile = m_pAVIFile; return NOERROR; } // *** IPersistFile methods *** STDMETHODIMP CAVIFile::CPersistFileImpl::IsDirty () { CAVIFile FAR * pfile = m_pAVIFile; if (pfile->fDirty) return NOERROR; else return ResultFromScode(S_FALSE); } STDMETHODIMP CAVIFile::CPersistFileImpl::Load (LPCOLESTR lpszFileName, DWORD grfMode) { CAVIFile FAR * pfile = m_pAVIFile; UINT ui; CLock tlock(pfile); if (pfile->achFile[0]) return ResultFromScode(-1); pfile->mode = grfMode; #if defined _WIN32 && !defined UNICODE WideCharToMultiByte(CP_ACP, 0, lpszFileName, -1, pfile->achFile, NUMELMS(pfile->achFile), NULL, NULL); #else lstrcpy(pfile->achFile, lpszFileName); #endif // Assumptions about avilib.cpp: // We're assuming that if CREATE is set, WRITE is set too. // We're assuming that we'll always see READWRITE instead of just WRITE. // If it ain't broke, don't fix it - who do I look like, the share flag // standards enforcing committee? #if 0 // force the share flags to the 'correct' values if (grfMode & OF_READWRITE) { pfile->mode = (grfMode & ~(MMIO_SHAREMODE)) | OF_SHARE_EXCLUSIVE; } else { pfile->mode = (grfMode & ~(MMIO_SHAREMODE)) | OF_SHARE_DENY_WRITE; } #endif // try to open the actual file // If the first attempt fails, no system error box, please. ui = SetErrorMode(SEM_NOOPENFILEERRORBOX); pfile->hshfile = shfileOpen(pfile->achFile, NULL, pfile->mode); if (!pfile->hshfile && ((grfMode & MMIO_RWMODE) == OF_READ)) { // if the open fails, try again without the share flags. pfile->mode &= ~(MMIO_SHAREMODE); pfile->hshfile = shfileOpen(pfile->achFile, NULL, pfile->mode); } SetErrorMode(ui); if (pfile->hshfile) { shfileAddRef(pfile->hshfile); // compensate for later release of IPersistFile shfileAddRef(pfile->hshfile); // compensate for later release of IPersistFile } return pfile->OpenInternal(grfMode); } STDMETHODIMP CAVIFile::CPersistFileImpl::Save (LPCOLESTR lpszFileName, BOOL fRemember) { CAVIFile FAR * pfile = m_pAVIFile; return NOERROR; } STDMETHODIMP CAVIFile::CPersistFileImpl::SaveCompleted (LPCOLESTR lpszFileName) { CAVIFile FAR * pfile = m_pAVIFile; return NOERROR; } STDMETHODIMP CAVIFile::CPersistFileImpl::GetCurFile (LPOLESTR FAR * lplpszFileName) { CAVIFile FAR * pfile = m_pAVIFile; return NOERROR; }