/****************************************************************************
*
* DSEQF.CPP
*
* routines for reading DIB sequences
*
***************************************************************************/
/**************************************************************************
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
* KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
* PURPOSE.
*
* Copyright 1992 - 1998 Microsoft Corporation. All Rights Reserved.
*
**************************************************************************/
#define INC_OLE2
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
#include <ctype.h>
#include <vfw.h>
#include "handler.h"
#include "handler.rc"
static DWORD NEAR PASCAL dseqParseFileName(
LPSTR lpszFileName,
LPSTR lpszTemplate,
DWORD FAR * lpdwMaxValue);
#ifdef DEBUG
static void CDECL dprintf(LPSTR, ...);
#define DPF dprintf
#else
#define DPF ; / ## /
#endif
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
/* - - - - - - - - */
UINT uUseCount;
BOOL fLocked;
/* - - - - - - - - */
//
// External function called by the Class Factory to create an instance of
// the DIB sequence reader/writer
//
HRESULT CAVIFile::Create(
IUnknown FAR* pUnknownOuter,
const IID FAR& riid,
void FAR* FAR* ppv)
{
IUnknown FAR* pUnknown;
CAVIFile FAR* pAVIFile;
HRESULT hresult;
pAVIFile = new FAR CAVIFile(pUnknownOuter, &pUnknown);
if (!pAVIFile)
return ResultFromScode(E_OUTOFMEMORY);
hresult = pUnknown->QueryInterface(riid, ppv);
if (FAILED(GetScode(hresult)))
delete pAVIFile;
return hresult;
}
/* - - - - - - - - */
//
// Random C++ stuff: constructors & such...
//
CAVIFile::CAVIFile(
IUnknown FAR* pUnknownOuter,
IUnknown FAR* FAR* ppUnknown) :
m_Unknown(this),
m_AVIFile(this),
m_Persist(this),
#ifdef CUSTOMMARSHAL
m_Marshal(this),
#endif
m_AVIStream(this)
{
if (pUnknownOuter)
m_pUnknownOuter = pUnknownOuter;
else
m_pUnknownOuter = &m_Unknown;
*ppUnknown = &m_Unknown;
}
/* - - - - - - - - */
CAVIFile::CUnknownImpl::CUnknownImpl(
CAVIFile FAR* pAVIFile)
{
m_pAVIFile = pAVIFile;
m_refs = 0;
}
/* - - - - - - - - */
//
// This QueryInterface function allows a caller to move between the various
// interfaces the object presents
//
STDMETHODIMP CAVIFile::CUnknownImpl::QueryInterface(
const IID FAR& iid,
void FAR* FAR* ppv)
{
if (iid == IID_IUnknown)
*ppv = &m_pAVIFile->m_Unknown;
else if (iid == IID_IAVIFile)
*ppv = &m_pAVIFile->m_AVIFile;
else if (iid == IID_IAVIStream)
*ppv = &m_pAVIFile->m_AVIStream;
else if (iid == IID_IPersistFile)
*ppv = &m_pAVIFile->m_Persist;
#ifdef CUSTOMMARSHAL
else if (iid == IID_IMarshal)
*ppv = &m_pAVIFile->m_Marshal;
#endif
else
return ResultFromScode(E_NOINTERFACE);
AddRef();
return NULL;
}
/* - - - - - - - - */
STDMETHODIMP_(ULONG) CAVIFile::CUnknownImpl::AddRef()
{
uUseCount++;
return ++m_refs;
}
/* - - - - - - - - */
//
// All calls to AddRef, Release, QueryInterface for the file or stream
// functions are redirected to the Unknown implementation...
//
CAVIFile::CAVIFileImpl::CAVIFileImpl(
CAVIFile FAR* pAVIFile)
{
m_pAVIFile = pAVIFile;
}
/* - - - - - - - - */
CAVIFile::CAVIFileImpl::~CAVIFileImpl()
{
}
/* - - - - - - - - */
STDMETHODIMP CAVIFile::CAVIFileImpl::QueryInterface(
const IID FAR& iid,
void FAR* FAR* ppv)
{
return m_pAVIFile->m_pUnknownOuter->QueryInterface(iid, ppv);
}
/* - - - - - - - - */
STDMETHODIMP_(ULONG) CAVIFile::CAVIFileImpl::AddRef()
{
return m_pAVIFile->m_pUnknownOuter->AddRef();
}
/* - - - - - - - - */
STDMETHODIMP_(ULONG) CAVIFile::CAVIFileImpl::Release()
{
return m_pAVIFile->m_pUnknownOuter->Release();
}
/* - - - - - - - - */
CAVIFile::CAVIStreamImpl::CAVIStreamImpl(
CAVIFile FAR* pAVIFile)
{
m_pAVIFile = pAVIFile;
}
/* - - - - - - - - */
CAVIFile::CAVIStreamImpl::~CAVIStreamImpl()
{
}
/* - - - - - - - - */
STDMETHODIMP CAVIFile::CAVIStreamImpl::QueryInterface(
const IID FAR& iid,
void FAR* FAR* ppv)
{
return m_pAVIFile->m_pUnknownOuter->QueryInterface(iid, ppv);
}
/* - - - - - - - - */
STDMETHODIMP_(ULONG) CAVIFile::CAVIStreamImpl::AddRef()
{
return m_pAVIFile->m_pUnknownOuter->AddRef();
}
/* - - - - - - - - */
STDMETHODIMP_(ULONG) CAVIFile::CAVIStreamImpl::Release()
{
return m_pAVIFile->m_pUnknownOuter->Release();
}
// --- IPersistFile implementation --------------------------------------
CAVIFile::CPersistFileImpl::CPersistFileImpl(CAVIFile FAR* pAVIFile)
{
m_pAVIFile = pAVIFile;
}
STDMETHODIMP
CAVIFile::CPersistFileImpl::QueryInterface(REFIID riid, LPVOID FAR* ppv)
{
return m_pAVIFile->m_pUnknownOuter->QueryInterface(riid, 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)
{
*lpClassID = CLSID_DIBSEQFileReader;
return NOERROR;
}
// *** IPersistFile methods ***
STDMETHODIMP
CAVIFile::CPersistFileImpl::IsDirty ()
{
if (m_pAVIFile->fDirty) {
return NOERROR;
} else {
return ResultFromScode(S_FALSE);
}
}
/* - - - - - - - - */
//
// This function takes the name of the first file in a DIB sequence, and
// returns a printf() specifier which can be used to create the names in
// the sequence, along with minimum and maximum values that can be used.
//
//
// Examples:
// lpszFileName = "FOO0047.DIB"
// -> lpszTemplate = "FOO%04d.DIB", dwMaxValue = 9999, return = 47
//
// lpszFileName = "TEST01.DIB"
// -> lpszTemplate = "TEST%01d.DIB", dwMaxValue = 9, return = 1
//
// lpszFileName = "TEST1.DIB"
// -> lpszTemplate = "TEST%d.DIB", dwMaxValue = 9999, return = 1
//
// lpszFileName = "SINGLE.DIB"
// -> lpszTemplate = "SINGLE.DIB", dwMaxValue = 0, return = 0
//
static DWORD NEAR PASCAL dseqParseFileName(
LPSTR lpszFileName,
LPSTR lpszTemplate,
DWORD FAR * lpdwMaxValue)
{
char achTemp[_MAX_PATH];
DWORD dwFirst;
WORD wFieldWidth;
DWORD dwMult;
BOOL fLeadingZero = FALSE;
LPSTR lp;
LPSTR lp2;
LPSTR lpExt;
/* Find end of string */
lp2 = lpszFileName;
lp = achTemp;
while (*lp2)
*lp++ = *lp2++;
*lp = '\0';
/* Make lp2 point at last character of base filename (w/o extension) */
/* Make lpExt point at the extension (without the dot) */
for (lp2 = lp; *lp2 != '.'; lp2--)
{
lpExt = lp2;
if ((lp2 == achTemp) || (*lp2 == '\\')
|| (*lp2 == ':') || (*lp2 == '!'))
{
/* There is no extension */
lp2 = lp;
lpExt = lp;
break;
}
}
lp2--;
// Count the number of numeric characters here....
dwFirst = 0;
wFieldWidth = 0;
dwMult = 1;
while (lp2 >= achTemp && (*lp2 >= '0') && (*lp2 <= '9')) {
fLeadingZero = (*lp2 == '0');
dwFirst += dwMult * (*(lp2--) - '0');
dwMult *= 10;
wFieldWidth++;
}
*lpdwMaxValue = dwMult - 1;
lp2++;
*lp2 = '\0';
// Make the format specifier....
if (wFieldWidth) {
if (fLeadingZero) {
wsprintf((LPSTR) lpszTemplate,"%s%%0%ulu.%s",
(LPSTR) achTemp, wFieldWidth,(LPSTR) lpExt);
} else {
wsprintf((LPSTR) lpszTemplate,"%s%%lu.%s",
(LPSTR) achTemp, (LPSTR) lpExt);
*lpdwMaxValue = 999999L;
// !!! This should really be based on the number of
// characters left after the base name....
}
} else
wsprintf((LPSTR) lpszTemplate,"%s.%s",
(LPSTR) achTemp, (LPSTR) lpExt);
DPF("First = %lu, Width = %u, Template = '%s'\n",dwFirst, wFieldWidth, lpszTemplate);
return dwFirst;
}
/* - - - - - - - - */
#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);
}
/* - - - - - - - - */
//
// "Open" a DIB sequence, by parsing the filename and counting the number
// of frames actually present....
//
STDMETHODIMP
CAVIFile::CPersistFileImpl::Load (LPCOLESTR szFile, DWORD mode)
{
CAVIFile FAR *p = m_pAVIFile;
UINT ui;
char ach[80];
char szFileA[MAX_PATH];
p->mode = mode;
//
// Parse the filename
//
wsprintf(szFileA, "%ls", szFile);
p->dwFirstFrame = dseqParseFileName(szFileA,
p->achFilenameTemplate,
&p->dwMaxValue);
//
// Initialize the variables that keep track of what frame is cached
//
p->lCurFrame = -1;
p->lpFrame = NULL;
p->cbFrame = 0;
p->cbFrameBuffer = 0;
p->lpFormat = NULL;
p->cbFormat = 0;
p->cbFormatBuffer = 0;
//
// Build a stream header....
//
p->sinfo.fccType = streamtypeVIDEO;
p->sinfo.fccHandler = 0;
p->sinfo.dwFlags = 0;
p->sinfo.wPriority = 0;
p->sinfo.wLanguage = 0;
p->sinfo.dwInitialFrames = 0;
p->sinfo.dwScale = 1;
p->sinfo.dwRate = 15;
p->sinfo.dwStart = 0;
p->sinfo.dwLength = 0;
p->sinfo.dwSuggestedBufferSize = 0;
p->sinfo.dwSampleSize = 0;
LoadString(ghModule, IDS_STREAMNAME, ach, sizeof(ach));
{
char TempFileName[80];
char TempName[80];
wsprintf(TempFileName, "%ls", szFile);
wsprintf(TempName, ach, FileName(TempFileName));
wsprintfW(p->sinfo.szName, L"%hs", TempName);
}
//
// ... and a file header.
//
_fmemset(&p->finfo, 0, sizeof(p->finfo));
p->finfo.dwRate = 15;
p->finfo.dwScale = 1;
p->finfo.dwStreams = 1;
p->finfo.dwWidth = 0;
p->finfo.dwHeight = 0;
LoadString(ghModule, IDS_FILETYPE,
p->finfo.szFileType,
sizeof(p->finfo.szFileType));
p->finfo.dwCaps = AVIFILECAPS_CANREAD |
AVIFILECAPS_CANWRITE |
AVIFILECAPS_ALLKEYFRAMES;
if (mode & OF_CREATE) {
//
// They're creating a "new" sequence
//
p->fStreamPresent = FALSE;
} else {
char ach[_MAX_PATH];
OFSTRUCT of;
DWORD dwFrame;
HRESULT hr;
//
// They're opening an existing sequence, so we have to actually
// count how many files are present
//
p->fStreamPresent = TRUE;
ui = SetErrorMode(SEM_NOOPENFILEERRORBOX);
for (dwFrame = 0; TRUE; dwFrame++) {
if (dwFrame > p->dwMaxValue)
break;
wsprintf(ach,p->achFilenameTemplate, dwFrame + p->dwFirstFrame);
// DPF("DIBSEQ: Checking frame %lu from '%s'\n",dwFrame,(LPSTR) ach);
/****************************************************************************/
/* DOS share has a bug. If the file we're testing for existence is open */
/* already by someone else, we have to give it the same flag for SHARE as */
/* the other person is using. So we have to try both on and off. Only one */
/* of these will return TRUE but if one of them does, the file exists. Also*/
/* we have to turn off the system model error box for share violations. */
/****************************************************************************/
if (OpenFile((LPSTR)ach, &of, OF_EXIST) == HFILE_ERROR &&
OpenFile((LPSTR)ach, &of, OF_EXIST | OF_SHARE_DENY_NONE) ==
HFILE_ERROR)
break;
}
SetErrorMode(ui);
if (dwFrame == 0)
goto error;
//
// Fix up the length in the header structures
//
p->sinfo.dwLength = dwFrame;
p->finfo.dwLength = dwFrame;
//
// Load the first frame, so we'll be ready...
//
hr = p->LoadFrame(0);
if (FAILED(GetScode(hr)))
return hr;
p->finfo.dwSuggestedBufferSize = p->cbFrame;
p->sinfo.dwSuggestedBufferSize = p->cbFrame;
p->finfo.dwWidth = ((LPBITMAPINFOHEADER) p->lpFormat)->biWidth;
p->finfo.dwHeight = ((LPBITMAPINFOHEADER) p->lpFormat)->biHeight;
p->sinfo.dwFlags = AVISTREAMINFO_FORMATCHANGES;
SetRect(&p->sinfo.rcFrame,
0, 0, (int) p->finfo.dwWidth, (int) p->finfo.dwHeight);
}
//
// all done return success.
//
return ResultFromScode(0); // success
error:
return ResultFromScode(AVIERR_FILEREAD);
}
STDMETHODIMP
CAVIFile::CPersistFileImpl::Save (LPCOLESTR lpszFileName, BOOL fRemember)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}
STDMETHODIMP
CAVIFile::CPersistFileImpl::SaveCompleted (LPCOLESTR lpszFileName)
{
return NOERROR;
}
STDMETHODIMP
CAVIFile::CPersistFileImpl::GetCurFile (LPOLESTR FAR * lplpszFileName)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}
// -------------------- IAVIFile Implementation-----------------------
//
// The GetStream method returns an interface pointer to the video stream,
// assuming one exists.
//
STDMETHODIMP CAVIFile::CAVIFileImpl::GetStream(
PAVISTREAM FAR * ppavi,
DWORD fccType,
LONG lParam)
{
CAVIFile FAR * p = m_pAVIFile;
int iStreamWant;
iStreamWant = (int)lParam;
if (!p->fStreamPresent)
return ResultFromScode(-1);
// We only support one stream
if (lParam != 0)
return ResultFromScode(-1);
// We only support a video stream
if (fccType && fccType != streamtypeVIDEO)
return ResultFromScode(-1);
//
// Be sure to keep the reference count up to date...
//
AddRef();
*ppavi = (PAVISTREAM) &(p->m_AVIStream);
return ResultFromScode(AVIERR_OK);
}
//
// If they opened the file with the OF_CREATE flag, they will use this
// method to create the video stream.
//
STDMETHODIMP CAVIFile::CAVIFileImpl::CreateStream(
PAVISTREAM FAR *ppstream,
AVISTREAMINFOW FAR *psi
)
{
CAVIFile FAR * p = m_pAVIFile;
// If the stream was already there, we fail.
if (p->fStreamPresent)
return ResultFromScode(AVIERR_UNSUPPORTED);
p->sinfo = *psi;
p->sinfo.dwLength = 0;
*ppstream = (PAVISTREAM) &(p->m_AVIStream);
p->fStreamPresent = TRUE;
// Keep the reference count correct
AddRef();
return ResultFromScode(AVIERR_OK);
}
STDMETHODIMP CAVIFile::CAVIFileImpl::WriteData(
DWORD ckid,
LPVOID lpData,
LONG cbData)
{
CAVIFile FAR * p = m_pAVIFile;
return ResultFromScode(AVIERR_UNSUPPORTED);
}
STDMETHODIMP CAVIFile::CAVIFileImpl::ReadData(
DWORD ckid,
LPVOID lpData,
LONG FAR *lpcbData)
{
CAVIFile FAR * p = m_pAVIFile;
return ResultFromScode(AVIERR_UNSUPPORTED);
}
STDMETHODIMP CAVIFile::CAVIFileImpl::EndRecord(void)
{
return ResultFromScode(AVIERR_OK);
}
STDMETHODIMP CAVIFile::CAVIFileImpl::Info(AVIFILEINFOW FAR * pfi, LONG lSize)
{
CAVIFile FAR * p = m_pAVIFile;
hmemcpy(pfi, &p->finfo, min(lSize,sizeof(p->finfo)));
return 0;
}
STDMETHODIMP CAVIFile::CAVIStreamImpl::Create(
LONG lParam1,
LONG lParam2)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}
//
// Returns where the last key frame before the given frame is.
//
// For now, we assume each DIB is a key frame.
//
STDMETHODIMP_(LONG) CAVIFile::CAVIStreamImpl::FindSample(
LONG lPos,
LONG lFlags)
{
CAVIFile FAR * p = m_pAVIFile;
// some minimal error checking....
if (lPos < 0 || lPos >= (LONG) p->sinfo.dwLength)
return -1;
// !!! Can we really assume every frame is non-empty and a key frame?
// !!! Who knows where format changes are? Let's assume everywhere!
return lPos;
}
#define WIDTHBYTES(i) ((unsigned)((i+31)&(~31))/8) /* ULONG aligned ! */
#define DIBWIDTHBYTES(bi) (int)WIDTHBYTES((int)(bi).biWidth * (int)(bi).biBitCount)
#define BFT_BITMAP 0x4d42 /* 'BM' */
//
// Helper function to load a given frame into our cache.
//
// This is where the actual work is done; all other functions just return
// the current format or frame out of the cache.
//
HRESULT NEAR PASCAL CAVIFile::LoadFrame(
LONG lPos)
{
char ach[_MAX_PATH];
HMMIO hmmio;
BITMAPFILEHEADER bfh;
BITMAPINFOHEADER bih;
SCODE sc = 0;
UINT ui;
//
// Check if we've already loaded this frame...
//
if (lPos == lCurFrame)
return 0;
//
// Build the filename by printing using our template
//
wsprintf(ach, achFilenameTemplate, dwFirstFrame + lPos);
// No system error box, please.
ui = SetErrorMode(SEM_NOOPENFILEERRORBOX);
//
// Go try to read the frame... Because of SHARE we have to try
// opening it two different ways.
//
hmmio = mmioOpen(ach, NULL, MMIO_READ | OF_SHARE_DENY_WRITE);
if (!hmmio) {
hmmio = mmioOpen(ach, NULL, MMIO_READ);
if (!hmmio)
return ResultFromScode(AVIERR_FILEOPEN);
}
SetErrorMode(ui);
//
// Read the BitmapFileHeader...
//
if (mmioRead(hmmio, (LPSTR) &bfh, sizeof(bfh)) != sizeof(bfh)) {
sc = AVIERR_FILEREAD;
goto error;
}
if (bfh.bfType != BFT_BITMAP) {
sc = AVIERR_BADFORMAT;
goto error;
}
//
// Read the BitmapInfoHeader...
//
if (mmioRead(hmmio, (LPSTR) &bih, sizeof(bih)) != sizeof(bih)) {
sc = AVIERR_FILEREAD;
goto error;
}
if (bih.biSize < sizeof(bih)) {
sc = AVIERR_BADFORMAT;
goto error;
}
// Check that the width and height match....
if ((finfo.dwWidth && finfo.dwWidth != (DWORD) bih.biWidth) ||
(finfo.dwHeight && finfo.dwHeight != (DWORD) bih.biHeight)) {
sc = AVIERR_BADFORMAT;
goto error;
}
// Fix up some fields in the header...
if (bih.biSizeImage == 0) {
bih.biSizeImage = DIBWIDTHBYTES(bih) * bih.biHeight;
}
if (bih.biClrUsed == 0 && bih.biBitCount <= 8 && bih.biCompression <= BI_RLE8)
bih.biClrUsed = 1 << bih.biBitCount;
cbFormat = bih.biSize + bih.biClrUsed * sizeof(RGBQUAD);
// Allocate space for the format
if (cbFormat > cbFormatBuffer) {
if (lpFormat) {
GlobalFreePtr(lpFormat);
lpFormat = 0;
cbFormatBuffer = 0;
}
lpFormat = GlobalAllocPtr(GMEM_MOVEABLE | GMEM_DDESHARE, cbFormat);
if (!lpFormat) {
sc = AVIERR_MEMORY;
goto error;
}
cbFormatBuffer = cbFormat;
}
*((LPBITMAPINFOHEADER) lpFormat) = bih;
// If the format is bigger than a BITMAPINFOHEADER, read the rest....
if (cbFormat > sizeof(bih)) {
if (mmioRead(hmmio, (LPSTR) lpFormat + sizeof(bih),
cbFormat - (LONG)sizeof(bih))
!= cbFormat - (LONG)sizeof(bih))
{
sc = AVIERR_FILEREAD;
goto error;
}
}
//
// Allocate enough space to read the frame in...
//
if (bih.biSizeImage > (DWORD) cbFrameBuffer) {
if (lpFrame) {
GlobalFreePtr(lpFrame);
lpFrame = 0;
cbFrameBuffer = 0;
}
lpFrame = GlobalAllocPtr(GMEM_MOVEABLE | GMEM_DDESHARE, bih.biSizeImage);
if (!lpFrame) {
sc = AVIERR_MEMORY;
goto error;
}
cbFrameBuffer = bih.biSizeImage;
}
cbFrame = bih.biSizeImage;
//
// and actually read the frame....
//
if (mmioRead(hmmio, (LPSTR) lpFrame, cbFrame) != cbFrame) {
sc = AVIERR_FILEREAD;
goto error;
}
lCurFrame = lPos;
error:
mmioClose(hmmio, 0);
return ResultFromScode(sc);
}
//
// The ReadFormat method returns the format of the specified frame....
//
STDMETHODIMP CAVIFile::CAVIStreamImpl::ReadFormat(
LONG lPos,
LPVOID lpFormat,
LONG FAR *lpcbFormat)
{
CAVIFile FAR * p = m_pAVIFile;
HRESULT hr;
//
// Try to get the correct frame
//
hr = p->LoadFrame(lPos);
if (hr != 0)
return hr;
// No buffer to fill in, this means return the size needed.
if (lpFormat == NULL || *lpcbFormat == 0) {
*lpcbFormat = p->cbFormat;
return 0;
}
//
// and return as much of the format as will fit.
//
hmemcpy(lpFormat, p->lpFormat, min(*lpcbFormat, p->cbFormat));
*lpcbFormat = p->cbFormat;
return 0;
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
STDMETHODIMP CAVIFile::CAVIStreamImpl::Info(AVISTREAMINFOW FAR * psi, LONG lSize)
{
CAVIFile FAR * p = m_pAVIFile;
hmemcpy(psi,&p->sinfo, min(lSize,sizeof(p->sinfo)));
// return sizeof(p->sinfo);
return 0;
}
STDMETHODIMP_(ULONG) CAVIFile::CUnknownImpl::Release()
{
CAVIFile FAR * p = m_pAVIFile;
uUseCount--;
if (!--m_refs) {
LONG lRet = AVIERR_OK;
if (p->fDirty) {
}
goto success;
success:
if (p->lpFormat)
GlobalFreePtr(p->lpFormat);
p->lpFormat = NULL;
p->cbFormat = 0;
if (p->lpFrame)
GlobalFreePtr(p->lpFrame);
p->lpFrame = NULL;
p->cbFrame = 0;
delete this;
return 0;
}
return m_refs;
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
STDMETHODIMP CAVIFile::CAVIStreamImpl::Read(
LONG lStart,
LONG lSamples,
LPVOID lpBuffer,
LONG cbBuffer,
LONG FAR * plBytes,
LONG FAR * plSamples)
{
CAVIFile FAR * p = m_pAVIFile;
HRESULT hr;
if (lStart < 0 || lStart >= (LONG) p->sinfo.dwLength) {
if (plBytes)
*plBytes = 0;
if (plSamples)
*plSamples = 0;
return 0;
}
// We always read one frame at a time...
lSamples = 1;
// Load it into the cache....
hr = p->LoadFrame(lStart);
if (hr != 0)
return hr;
//
// a NULL buffer means return the size buffer needed to read
// the given sample.
//
if (lpBuffer == NULL || cbBuffer == 0) {
if (plBytes)
*plBytes = p->cbFrame;
if (plSamples)
*plSamples = lSamples;
return 0;
}
//
// They didn't give us enough space for the frame, so complain
//
if (cbBuffer < p->cbFrame) {
if (plBytes)
*plBytes = p->cbFrame;
return ResultFromScode(AVIERR_BUFFERTOOSMALL);
}
//
// Copy the frame into the caller's buffer
//
hmemcpy(lpBuffer, p->lpFrame, p->cbFrame);
//
// success return number of bytes and number of samples read
//
if (plBytes)
*plBytes = p->cbFrame;
if (plSamples)
*plSamples = lSamples;
return ResultFromScode(AVIERR_OK);
}
STDMETHODIMP CAVIFile::CAVIStreamImpl::SetFormat(
LONG lPos,
LPVOID lpFormat,
LONG cbFormat)
{
CAVIFile FAR * p = m_pAVIFile;
// Keep track of the format....
p->cbFormat = cbFormat;
p->lpFormat = (LPVOID) GlobalAllocPtr(GMEM_MOVEABLE | GMEM_DDESHARE, cbFormat);
if (p->lpFormat == NULL)
return ResultFromScode(AVIERR_MEMORY);
hmemcpy(p->lpFormat, lpFormat, cbFormat);
p->finfo.dwWidth = ((LPBITMAPINFOHEADER) p->lpFormat)->biWidth;
p->finfo.dwHeight = ((LPBITMAPINFOHEADER) p->lpFormat)->biHeight;
SetRect(&p->sinfo.rcFrame,
0, 0, (int) p->finfo.dwWidth, (int) p->finfo.dwHeight);
return 0L;
}
//
// Helper function to save a single frame
//
HRESULT NEAR PASCAL CAVIFile::WriteFrame(
LONG lPos,
LPVOID lp,
LONG cb)
{
char ach[_MAX_PATH];
HMMIO hmmio;
BITMAPFILEHEADER bfh;
//
// If they're overwriting the cached frame, invalidate the cache
//
if (lPos == lCurFrame)
lCurFrame = -1;
//
// Build the filename to write to
//
wsprintf(ach, achFilenameTemplate, dwFirstFrame + lPos);
// and write it.
hmmio = mmioOpen(ach, NULL, MMIO_WRITE | MMIO_CREATE | OF_SHARE_EXCLUSIVE);
if (!hmmio)
return ResultFromScode(AVIERR_FILEOPEN);
//
// Write the BitmapFileHeader
//
bfh.bfType = BFT_BITMAP;
bfh.bfOffBits = sizeof(bfh) + cbFormat;
bfh.bfSize = bfh.bfOffBits + cb;
if (mmioWrite(hmmio, (LPSTR) &bfh, sizeof(bfh)) != sizeof(bfh)) {
error:
mmioClose(hmmio, 0);
return ResultFromScode(AVIERR_FILEWRITE);
}
((LPBITMAPINFOHEADER) lpFormat)->biSizeImage = cb;
//
// Write the DIB format
//
if (mmioWrite(hmmio, (LPSTR) lpFormat, cbFormat) != cbFormat)
goto error;
//
// Write the data
//
if (mmioWrite(hmmio, (LPSTR) lp, cb) != cb)
goto error;
//
// Flush things so that we can be sure everything is written out
//
if (mmioFlush(hmmio, 0) != 0)
goto error;
mmioClose(hmmio, 0);
return 0;
}
STDMETHODIMP CAVIFile::CAVIStreamImpl::Write(
LONG lStart,
LONG lSamples,
LPVOID lpData,
LONG cbData,
DWORD dwFlags,
LONG FAR *plSampWritten,
LONG FAR *plBytesWritten)
{
CAVIFile FAR * p = m_pAVIFile;
HRESULT hr;
if ((p->mode & (OF_WRITE | OF_READWRITE)) == 0)
return ResultFromScode(AVIERR_READONLY);
if (p->lpFormat == NULL)
return ResultFromScode(AVIERR_UNSUPPORTED);
// < 0 means "at end"
if (lStart < 0)
lStart = p->sinfo.dwStart + p->sinfo.dwLength;
if (lStart > (LONG) (p->sinfo.dwStart + p->sinfo.dwLength))
return ResultFromScode(AVIERR_BADPARAM);
// !!! Die if we've reached the limit of our numbers....
if ((DWORD) lStart + p->dwFirstFrame > p->dwMaxValue)
return ResultFromScode(AVIERR_FILEWRITE);
if (lSamples != 1)
return ResultFromScode(AVIERR_BADPARAM);
// only allow key frames!
if (!(dwFlags & AVIIF_KEYFRAME)) {
DPF("Tried to write a non-key frame to a DIB sequence!\n");
return ResultFromScode(AVIERR_UNSUPPORTED);
}
hr = p->WriteFrame(lStart, lpData, cbData);
if (hr != AVIERR_OK)
return hr;
p->fDirty = TRUE;
p->sinfo.dwLength =
max((LONG) p->sinfo.dwLength,
lStart + lSamples);
p->finfo.dwLength = p->sinfo.dwLength;
p->finfo.dwSuggestedBufferSize =
max(p->finfo.dwSuggestedBufferSize, (DWORD) cbData);
p->sinfo.dwSuggestedBufferSize =
p->finfo.dwSuggestedBufferSize;
if (plSampWritten)
*plSampWritten = lSamples;
if (plBytesWritten)
*plBytesWritten = cbData;
return ResultFromScode(AVIERR_OK);
}
// these both are for saving. we don't support saving
STDMETHODIMP
CAVIFile::CAVIFileImpl::DeleteStream(DWORD fccType, LONG lParam)
{
return ResultFromScode(E_FAIL);
}
STDMETHODIMP
CAVIFile::CAVIStreamImpl::SetInfo(
AVISTREAMINFOW FAR * lpInfo,
LONG cbInfo
)
{
return ResultFromScode(E_FAIL);
}
STDMETHODIMP CAVIFile::CAVIStreamImpl::Delete(
LONG lStart,
LONG lSamples)
{
CAVIFile FAR * p = m_pAVIFile;
return ResultFromScode(AVIERR_UNSUPPORTED);
}
// Should these just map to Read/WriteData? !!!
STDMETHODIMP CAVIFile::CAVIStreamImpl::ReadData(
DWORD fcc,
LPVOID lp,
LONG FAR *lpcb)
{
CAVIFile FAR * p = m_pAVIFile;
return ResultFromScode(AVIERR_UNSUPPORTED);
}
STDMETHODIMP CAVIFile::CAVIStreamImpl::WriteData(
DWORD fcc,
LPVOID lp,
LONG cb)
{
CAVIFile FAR * p = m_pAVIFile;
return ResultFromScode(AVIERR_UNSUPPORTED);
}
#ifdef CUSTOMMARSHAL
// The code below supports custom marshalling.
// !!! Need good explanation here!
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;
DPF("UnMarshalClass 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->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 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("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);
if (hr == NOERROR)
pUnk->Release();
return hr;
}
STDMETHODIMP CAVIFile::CMarshalImpl::DisconnectObject (
THIS_ DWORD dwReserved)
{
HRESULT hr = NOERROR;
return hr;
}
#endif
/*****************************************************************************
*
* dprintf() is called by the DPF macro if DEBUG is defined at compile time.
*
* The messages will be send to COM1: like any debug message. To
* enable debug output, add the following to WIN.INI :
*
* [debug]
* ICSAMPLE=1
*
****************************************************************************/
#ifdef DEBUG
#define MODNAME "DSEQFILE"
static BOOL fDebug = -1;
static void cdecl dprintf(
LPSTR szFormat, ...)
{
char ach[128];
va_list va;
if (fDebug == -1)
fDebug = GetProfileIntA("Debug",MODNAME, FALSE);
if (!fDebug)
return;
va_start(va, szFormat);
if (szFormat[0] == '!')
ach[0]=0, szFormat++;
else
lstrcpyA(ach, MODNAME ": ");
wvsprintfA(ach+lstrlenA(ach),szFormat,(LPSTR)va);
va_end(va);
// lstrcatA(ach, "\r\r\n");
OutputDebugStringA(ach);
}
#endif