2774 lines
74 KiB
C++
2774 lines
74 KiB
C++
/****************************************************************************
|
|
*
|
|
* AVIFILE.C
|
|
*
|
|
* routines for reading Standard AVI files
|
|
*
|
|
* Copyright (c) 1992 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 <win32.h>
|
|
#include <storage.h>
|
|
#include <compman.h>
|
|
#include <compobj.h>
|
|
#include <avifmt.h>
|
|
#include "avifile.h"
|
|
#include "avifilei.h"
|
|
#include "avifile.rc"
|
|
|
|
#include "debug.h"
|
|
|
|
#undef GlobalFreePtr // fix for C6
|
|
#define GlobalFreePtr(p) GlobalFree(GlobalPtrHandle(p))
|
|
|
|
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);
|
|
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))
|
|
goto exit;
|
|
|
|
DPF("Writing Index", time=timeGetTime());
|
|
|
|
pIndex = (PAVIINDEXENTRY)GlobalAllocPtr(GHND,INDEX_WRITE_SIZE);
|
|
|
|
if (pIndex == NULL)
|
|
goto exit;
|
|
|
|
for (l=0; l < pfile->px->nIndex; ) {
|
|
|
|
cnt = IndexGetFileIndex(pfile->px, l, cnt, pIndex, -(LONG)dwOffsetMovie);
|
|
|
|
if (cnt == 0)
|
|
break;
|
|
|
|
if (shfileWrite(pfile->hshfile, (HPSTR)pIndex,
|
|
cnt * sizeof(AVIINDEXENTRY)) != cnt * sizeof(AVIINDEXENTRY))
|
|
goto exit;
|
|
|
|
l += cnt;
|
|
|
|
DPF("!.");
|
|
}
|
|
|
|
DPF("!Done (%ldms)\n", timeGetTime()-time);
|
|
|
|
if (shfileAscend(pfile->hshfile, &ck, 0))
|
|
goto exit;
|
|
|
|
f = TRUE;
|
|
|
|
exit:
|
|
if (pIndex)
|
|
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;
|
|
AVIStreamHeader 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);
|
|
|
|
/* Write AVI header info */
|
|
if (shfileWrite(pfile->hshfile,
|
|
(LPSTR)&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;
|
|
strhdr.rcFrame = pavi->avistream.rcFrame;
|
|
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]) {
|
|
ck.cksize = lstrlen(pavi->avistream.szName) + 1;
|
|
ck.ckid = ckidSTREAMNAME;
|
|
|
|
if (shfileCreateChunk(pfile->hshfile, &ck, 0))
|
|
goto FileError;
|
|
|
|
if (shfileWrite(pfile->hshfile, (HPSTR) pavi->avistream.szName, ck.cksize) !=
|
|
(LONG) ck.cksize)
|
|
goto FileError;
|
|
|
|
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;
|
|
}
|
|
|
|
/* Pad this header out so that the real data will start on a 2K
|
|
** boundary by writing a JUNK chunk.
|
|
*/
|
|
ck.ckid = ckidAVIPADDING;
|
|
if (shfileCreateChunk(pfile->hshfile,&ck,0)) {
|
|
goto FileError;
|
|
}
|
|
|
|
DPF("Data list start = %ld, current pos = %ld\n", pfile->lDataListStart, shfileSeek(pfile->hshfile, 0, SEEK_CUR));
|
|
|
|
if (shfileSeek(pfile->hshfile, 0, SEEK_CUR) > pfile->lDataListStart) {
|
|
// !!! Ack: we didn't leave enough space for the header.
|
|
// !!! How can we avoid this?
|
|
goto FileError;
|
|
}
|
|
|
|
shfileSeek(pfile->hshfile, pfile->lDataListStart, SEEK_SET);
|
|
|
|
if (shfileAscend(pfile->hshfile, &ck, 0)) {
|
|
goto FileError;
|
|
}
|
|
|
|
/* Start the 'movi' list, where all of the actual data will be. */
|
|
ckLIST.cksize = 0;
|
|
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))
|
|
goto FileError;
|
|
|
|
//
|
|
// Always flush to be sure that the data really made it to the disk....
|
|
//
|
|
if (shfileFlush(pfile->hshfile, 0))
|
|
goto FileError;
|
|
|
|
return ResultFromScode(AVIERR_OK);
|
|
|
|
FileError:
|
|
hr = ResultFromScode(AVIERR_FILEWRITE);
|
|
goto FinishUp;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
STDMETHODIMP_(ULONG) CAVIFile::CUnknownImpl::Release()
|
|
{
|
|
CAVIFile FAR * pfile = m_pAVIFile;
|
|
CAVIStream FAR * pavi;
|
|
int iStream;
|
|
|
|
DPF2("File %lx: Usage--=%lx\n", (DWORD) (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;
|
|
|
|
if (pavi->lpFormat)
|
|
GlobalFreePtr(pavi->lpFormat);
|
|
|
|
if (pavi->lpData)
|
|
GlobalFreePtr(pavi->lpData);
|
|
|
|
if (pavi->extra.lp) {
|
|
DPF2("Freeing %ld bytes of extra stream data.\n", pavi->extra.cb);
|
|
GlobalFreePtr(pavi->extra.lp);
|
|
}
|
|
|
|
if (pavi->paviBase)
|
|
AVIStreamClose(pavi->paviBase);
|
|
|
|
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;
|
|
delete pfile;
|
|
return 0;
|
|
} else {
|
|
if (pfile->hshfile)
|
|
shfileRelease(pfile->hshfile);
|
|
}
|
|
|
|
return m_refs;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
STDMETHODIMP CAVIFile::CAVIFileImpl::Save(
|
|
LPCSTR szFile,
|
|
AVICOMPRESSOPTIONS FAR *lpOptions,
|
|
AVISAVECALLBACK lpfnCallback)
|
|
{
|
|
CAVIFile FAR * pfile = m_pAVIFile;
|
|
HRESULT hr = ResultFromScode(AVIERR_OK);
|
|
|
|
if (pfile->fDirty) {
|
|
hr = SaveChanges(pfile, FALSE);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
#define SLASH(c) ((c) == '/' || (c) == '\\')
|
|
|
|
/*--------------------------------------------------------------+
|
|
| FileName - return a pointer to the filename part of szPath |
|
|
| with no preceding path. |
|
|
+--------------------------------------------------------------*/
|
|
LPSTR FAR FileName(LPCSTR lszPath)
|
|
{
|
|
LPCSTR lszCur;
|
|
|
|
for (lszCur = lszPath + lstrlen(lszPath); lszCur > lszPath && !SLASH(*lszCur) && *lszCur != ':';)
|
|
lszCur = AnsiPrev(lszPath, lszCur);
|
|
if (lszCur == lszPath)
|
|
return (LPSTR)lszCur;
|
|
else
|
|
return (LPSTR)(lszCur + 1);
|
|
}
|
|
|
|
BOOL IsRectBogus(LPRECT lprc, DWORD dwFrameWidth, DWORD dwFrameHeight,
|
|
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::CAVIFileImpl::Open(LPCSTR szFile, UINT mode)
|
|
{
|
|
CAVIFile FAR * pfile = m_pAVIFile;
|
|
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;
|
|
UINT ui;
|
|
AVIStreamHeader strhdr;
|
|
char ach[20];
|
|
char ach2[20];
|
|
int iStreamNumber;
|
|
#ifdef DEBUG
|
|
DWORD time;
|
|
#endif
|
|
|
|
if (pfile->achFile[0])
|
|
return ResultFromScode(-1);
|
|
|
|
pfile->mode = mode;
|
|
_fstrcpy(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.
|
|
|
|
// 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;
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
SetErrorMode(ui);
|
|
|
|
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);
|
|
pfile->avihdr.dwFlags = AVIF_HASINDEX;
|
|
} 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;
|
|
|
|
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 proccess the format.
|
|
//
|
|
strhdr.dwQuality = (DWORD) ICQUALITY_DEFAULT;
|
|
SetRectEmpty(&strhdr.rcFrame);
|
|
|
|
l = min(ck.cksize, sizeof(AVIStreamHeader));
|
|
|
|
if (shfileRead(pfile->hshfile, (HPSTR)&strhdr, l) != l)
|
|
goto readerror;
|
|
|
|
// !!! Copy fields from strhdr into
|
|
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;
|
|
pavi->avistream.rcFrame = strhdr.rcFrame;
|
|
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, sizeof(ach));
|
|
else if (pavi->avistream.fccType == streamtypeAUDIO)
|
|
LoadString(ghMod, IDS_AUDIO, ach, sizeof(ach));
|
|
else
|
|
wsprintf(ach, "'%4.4s'",
|
|
(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, sizeof(ach2));
|
|
wsprintf(pavi->avistream.szName,
|
|
(LPSTR)ach2,
|
|
(LPSTR)FileName(pfile->achFile),
|
|
(LPSTR)ach,
|
|
iStreamNumber);
|
|
|
|
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;
|
|
|
|
//
|
|
// 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:
|
|
l = min((LONG) ck.cksize, sizeof(pavi->avistream.szName));
|
|
|
|
if (shfileRead(pfile->hshfile,
|
|
(HPSTR)pavi->avistream.szName, l) != l)
|
|
goto readerror;
|
|
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 = sizeof(pavi->avistream.szName) - 1;
|
|
pavi->avistream.szName[l] = '\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 realy 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;
|
|
}
|
|
}
|
|
|
|
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 = GetProfileInt("avifile", "buffers", 5);
|
|
|
|
pfile->pb = InitBuffered(nBuffers,
|
|
min(pfile->avihdr.dwSuggestedBufferSize * 2, 32768L),
|
|
pfile->hshfile,
|
|
pfile->px);
|
|
}
|
|
}
|
|
|
|
return ResultFromScode(AVIERR_OK);
|
|
|
|
readerror:
|
|
hr = ResultFromScode(AVIERR_FILEREAD);
|
|
goto error;
|
|
|
|
memerror:
|
|
hr = ResultFromScode(AVIERR_MEMORY);
|
|
goto error;
|
|
|
|
formaterror:
|
|
hr = ResultFromScode(AVIERR_BADFORMAT);
|
|
|
|
error:
|
|
if (hr == ResultFromScode(AVIERR_OK))
|
|
hr = ResultFromScode(AVIERR_ERROR);
|
|
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
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;
|
|
|
|
*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;
|
|
|
|
AddRef();
|
|
|
|
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)
|
|
return ResultFromScode(AVIERR_MEMORY);
|
|
|
|
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) {
|
|
|
|
lBufferSize = GetProfileInt("avifile", "buffersize", 0) * 1024L;
|
|
nBuffers = GetProfileInt("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);
|
|
|
|
//
|
|
// all done return success.
|
|
//
|
|
return ResultFromScode(AVIERR_OK); // success
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
STDMETHODIMP CAVIFile::CAVIFileImpl::CreateStream(
|
|
PAVISTREAM FAR *ppavi,
|
|
AVISTREAMINFO FAR *psi)
|
|
{
|
|
CAVIFile FAR * pf = m_pAVIFile;
|
|
CAVIStream FAR * pavi;
|
|
int iStream = (int) pf->avihdr.dwStreams;
|
|
IUnknown FAR * pUnk;
|
|
|
|
// Check to see if the header will take up too much room!
|
|
if (pf->lWriteLoc > 0) {
|
|
if ((sizeof(AVIStreamHeader) + pf->lHeaderSize +
|
|
lstrlen(psi->szName) + 8 * sizeof(DWORD)) >
|
|
pf->lWriteLoc) {
|
|
DPF("Header will be too big with this new stream!\n");
|
|
return ResultFromScode(AVIERR_UNSUPPORTED);
|
|
}
|
|
}
|
|
pf->lHeaderSize += sizeof(AVIStreamHeader) + 8 * sizeof(DWORD) +
|
|
lstrlen(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);
|
|
|
|
// !!! error check?
|
|
|
|
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;
|
|
|
|
// !!! 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;
|
|
|
|
return ReadExtra(&pf->extra, ckid, lpData, lpcbData);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
STDMETHODIMP CAVIFile::CAVIFileImpl::EndRecord()
|
|
{
|
|
CAVIFile FAR * pf = 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);
|
|
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) {
|
|
// !!! Support padding here?
|
|
|
|
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(
|
|
AVIFILEINFO FAR * pfi,
|
|
LONG lSize)
|
|
{
|
|
CAVIFile FAR * pf = m_pAVIFile;
|
|
AVIFILEINFO fi;
|
|
char ach[30];
|
|
|
|
if (pfi == NULL)
|
|
return ResultFromScode(AVIERR_BADPARAM);
|
|
|
|
if (lSize < sizeof(fi))
|
|
return ResultFromScode(AVIERR_BUFFERTOOSMALL);
|
|
|
|
fi.dwMaxBytesPerSec = pf->avihdr.dwMaxBytesPerSec;
|
|
fi.dwFlags = 0;
|
|
fi.dwStreams = pf->avihdr.dwStreams;
|
|
fi.dwSuggestedBufferSize = pf->avihdr.dwSuggestedBufferSize;
|
|
fi.dwWidth = pf->avihdr.dwWidth;
|
|
fi.dwHeight = pf->avihdr.dwHeight;
|
|
fi.dwScale = pf->avihdr.dwMicroSecPerFrame;
|
|
fi.dwRate = 1000000L;
|
|
fi.dwLength = pf->avihdr.dwTotalFrames;
|
|
fi.dwEditCount = 0;
|
|
|
|
LoadString(ghMod, IDS_AVIFILE, ach, sizeof(ach));
|
|
wsprintf(fi.szFileType, (LPSTR)ach);
|
|
|
|
hmemcpy(pfi, &fi, sizeof(fi));
|
|
|
|
return AVIERR_OK;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// AVIFileClose()
|
|
//
|
|
// close a AVIFile stream
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
STDMETHODIMP_(ULONG) CAVIStream::CUnknownImpl::Release()
|
|
{
|
|
CAVIStream FAR * pavi = m_pAVIStream;
|
|
|
|
uUseCount--;
|
|
|
|
if (m_refs < 20) {
|
|
DPF2("Stream %lx: Usage--=%lx\n", (DWORD) (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;
|
|
|
|
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;
|
|
|
|
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 >= 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; i<n; i++) {
|
|
pavi->argbq[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;
|
|
|
|
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(LONG lParam1, LONG lParam2)
|
|
{
|
|
return ResultFromScode(AVIERR_UNSUPPORTED);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
STDMETHODIMP CAVIStream::CAVIStreamImpl::Info(AVISTREAMINFO FAR * psi, LONG lSize)
|
|
{
|
|
CAVIStream FAR * pavi = m_pAVIStream;
|
|
|
|
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;
|
|
|
|
if (pavi->paviBase) {
|
|
// If we haven't copied over the data yet, delegate.
|
|
return AVIStreamFindSample(pavi->paviBase, lPos, lFlags);
|
|
}
|
|
|
|
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;
|
|
|
|
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);
|
|
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;
|
|
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) {
|
|
// Check to see if the header will take up too much room!
|
|
if (pavi->pfile->lWriteLoc > 0) {
|
|
if ((pavi->cbFormat + pavi->pfile->lHeaderSize + 2 * sizeof(DWORD)) >
|
|
pavi->pfile->lWriteLoc) {
|
|
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 0;
|
|
}
|
|
|
|
//
|
|
// First, check if the format is actually different....
|
|
//
|
|
if (cbFormat == pavi->cbFormat &&
|
|
(_fmemcmp(pavi->lpFormat, lpFormat, (int) cbFormat) == 0))
|
|
return 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 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;
|
|
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->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) {
|
|
// !!! bad 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;
|
|
|
|
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;
|
|
|
|
return ReadExtra(&pavi->extra, ckid, lp, lpcb);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
STDMETHODIMP CAVIStream::CAVIStreamImpl::WriteData(DWORD ckid, LPVOID lp, LONG cb)
|
|
{
|
|
CAVIStream FAR * pavi = m_pAVIStream;
|
|
|
|
// Check to see if the header will take up too much room!
|
|
if (pavi->pfile->lWriteLoc > 0) {
|
|
if ((cb + pavi->pfile->lHeaderSize + 2 * sizeof(DWORD)) >
|
|
pavi->pfile->lWriteLoc) {
|
|
DPF("Header will be too big with this extra data!\n");
|
|
return ResultFromScode(AVIERR_UNSUPPORTED);
|
|
}
|
|
}
|
|
|
|
pavi->pfile->lHeaderSize += cb + 3 * sizeof(DWORD);
|
|
|
|
if ((pavi->pfile->mode & (OF_WRITE | OF_READWRITE)) == 0)
|
|
return ResultFromScode(AVIERR_READONLY);
|
|
|
|
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;
|
|
|
|
if (pavi->pb)
|
|
BeginBufferedStreaming(pavi->pb, lRate > 0);
|
|
|
|
return ResultFromScode(AVIERR_OK);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CAVIStream::CStreamingImpl::End()
|
|
{
|
|
CAVIStream FAR * pavi = m_pAVIStream;
|
|
|
|
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;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
DecodeRle - 'C' version
|
|
|
|
Play back a RLE buffer into a DIB buffer
|
|
|
|
returns
|
|
none
|
|
|
|
***************************************************************************/
|
|
|
|
void DecodeRle(LPBITMAPINFOHEADER lpbi, BYTE _huge *pb, BYTE _huge *prle)
|
|
{
|
|
BYTE cnt;
|
|
BYTE b;
|
|
WORD x;
|
|
WORD dx,dy;
|
|
WORD wWidthBytes;
|
|
|
|
#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
|
|
{
|
|
wWidthBytes = (WORD)lpbi->biWidth+3 & ~3;
|
|
|
|
x = 0;
|
|
|
|
for(;;)
|
|
{
|
|
cnt = *prle++;
|
|
b = *prle++;
|
|
|
|
if (cnt == RLE_ESCAPE)
|
|
{
|
|
switch (b)
|
|
{
|
|
case RLE_EOF:
|
|
return;
|
|
|
|
case RLE_EOL:
|
|
pb += wWidthBytes - x;
|
|
x = 0;
|
|
break;
|
|
|
|
case RLE_JMP:
|
|
dx = (WORD)*prle++;
|
|
dy = (WORD)*prle++;
|
|
|
|
pb += (DWORD)wWidthBytes * dy + dx;
|
|
x += dx;
|
|
|
|
break;
|
|
|
|
default:
|
|
cnt = b;
|
|
x += cnt;
|
|
while (cnt-- > 0)
|
|
*pb++ = *prle++;
|
|
|
|
if (b & 1)
|
|
prle++;
|
|
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
x += cnt;
|
|
|
|
while (cnt-- > 0)
|
|
*pb++ = b;
|
|
}
|
|
}
|
|
}
|
|
}
|