You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2664 lines
69 KiB
2664 lines
69 KiB
/******************************************************************************
|
|
|
|
Copyright (C) Microsoft Corporation 1985-1991. All rights reserved.
|
|
|
|
Title: aviopen.c - open a AVI file
|
|
|
|
*****************************************************************************/
|
|
#include "graphic.h"
|
|
#ifdef _WIN32
|
|
#include <wchar.h>
|
|
#endif
|
|
|
|
#ifdef USEAVIFILE
|
|
#include <initguid.h>
|
|
#include <scode.h>
|
|
|
|
DEFINE_AVIGUID(IID_IAVIFile, 0x00020020, 0, 0);
|
|
DEFINE_AVIGUID(IID_IAVIStream, 0x00020021, 0, 0);
|
|
#endif
|
|
|
|
#define comptypeNONE mmioFOURCC('N','O','N','E')
|
|
|
|
//
|
|
// special error to force using AVIFile to try and open this file.
|
|
//
|
|
#define AVIERR_NOT_AVIFILE 4242
|
|
|
|
//
|
|
// if this is defined we will always use AVIFILE.DLL, except for
|
|
// 1:1 interleaved files.
|
|
//
|
|
#ifdef USEAVIFILE
|
|
#define USE_AVIFILE_FOR_NON_INT
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
BOOL runningInWow = 0; // Assume we are not in WOW
|
|
#endif
|
|
|
|
|
|
/***************************************************************************
|
|
* Local function declarations
|
|
***************************************************************************/
|
|
|
|
STATICFN BOOL NEAR PASCAL InitStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi);
|
|
STATICFN BOOL NEAR PASCAL InitVideoStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi);
|
|
STATICFN BOOL NEAR PASCAL InitAudioStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi);
|
|
STATICFN BOOL NEAR PASCAL InitOtherStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi);
|
|
STATICFN void NEAR PASCAL CloseStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi);
|
|
|
|
|
|
STATICFN BOOL NEAR PASCAL ParseNewHeader(NPMCIGRAPHIC npMCI);
|
|
|
|
STATICFN BOOL NEAR PASCAL OpenRiffAVIFile(NPMCIGRAPHIC npMCI, BOOL bForce);
|
|
STATICFN BOOL NEAR PASCAL OpenWithAVIFile(NPMCIGRAPHIC npMCI);
|
|
STATICFN BOOL NEAR PASCAL OpenInterface(NPMCIGRAPHIC npMCI);
|
|
#ifdef USEAVIFILE
|
|
STATICFN BOOL NEAR PASCAL OpenAVIFile(NPMCIGRAPHIC npMCI, IAVIFile FAR *pf);
|
|
STATICFN BOOL NEAR PASCAL OpenAVIStream(NPMCIGRAPHIC npMCI, int stream, IAVIStream FAR *pf);
|
|
#endif
|
|
|
|
INLINE static BOOL NEAR PASCAL IsRectBogus(RECT *prc);
|
|
#ifndef _WIN32
|
|
static LONG NEAR PASCAL atol(char *sz);
|
|
#endif // !_WIN32
|
|
|
|
#ifdef _WIN32XXXX
|
|
#define GetFileDriveType GetDriveType
|
|
#else
|
|
static UINT NEAR PASCAL GetFileDriveType(LPTSTR szPath);
|
|
#endif
|
|
|
|
|
|
#ifndef _WIN32
|
|
SZCODE szOLENLSDLL[] = "OLE2NLS.DLL";
|
|
SZCODE szOLENLSAPI[] = "GetUserDefaultLangID";
|
|
#endif
|
|
|
|
//
|
|
// due to OLE, all ole calls must be single-threaded and thus on
|
|
// the app thread. so we moved the Open call to the app thread
|
|
|
|
|
|
|
|
//
|
|
// -- called on app thread -------------------------------------------------
|
|
//
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api BOOL | mciaviOpenFile | Open an AVI file.
|
|
* the filename we are to open is passed to npMCI->szFileName.
|
|
*
|
|
* @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
|
|
*
|
|
* @rdesc TRUE means OK, otherwise mci error in dwTaskError
|
|
*
|
|
***************************************************************************/
|
|
|
|
BOOL FAR PASCAL mciaviOpenFile(NPMCIGRAPHIC npMCI)
|
|
{
|
|
//
|
|
// mciaviOpenFile should not be called with a file open!
|
|
//
|
|
DPF(("mciaviOpenFile, name = >>%ls<<\n", npMCI->szFilename));
|
|
Assert(npMCI->streams == 0);
|
|
Assert(npMCI->hmmio == NULL);
|
|
Assert(npMCI->hpIndex == NULL);
|
|
Assert(!(npMCI->dwFlags & (
|
|
MCIAVI_NOTINTERLEAVED |
|
|
MCIAVI_ANIMATEPALETTE |
|
|
MCIAVI_CANDRAW |
|
|
MCIAVI_HASINDEX)));
|
|
//
|
|
// !!!support open new
|
|
//
|
|
if (npMCI->szFilename[0] == '\0') {
|
|
}
|
|
|
|
//
|
|
// what media is this file coming from, will be important later
|
|
// when we play.
|
|
//
|
|
if (npMCI->szFilename[0] == TEXT('@')) {
|
|
npMCI->uDriveType = DRIVE_INTERFACE;
|
|
if (IsNTWOW()) {
|
|
DPF2(("Rejecting open from WOW because of an interface pointer\n"));
|
|
npMCI->dwTaskError=MCIERR_UNSUPPORTED_FUNCTION;
|
|
return(FALSE);
|
|
// Cannot support a 16 bit interface pointer from 32 bit code
|
|
}
|
|
}
|
|
else
|
|
npMCI->uDriveType = GetFileDriveType(npMCI->szFilename);
|
|
|
|
#ifdef DEBUG
|
|
switch (npMCI->uDriveType) {
|
|
case DRIVE_CDROM:
|
|
DOUT2("Media is a CD-ROM\n");
|
|
break;
|
|
|
|
case DRIVE_REMOTE:
|
|
DOUT2("Media is a Network\n");
|
|
break;
|
|
|
|
case DRIVE_FIXED:
|
|
DOUT2("Media is a Hard disk\n");
|
|
break;
|
|
|
|
case DRIVE_REMOVABLE:
|
|
DOUT2("Media is a floppy disk\n");
|
|
break;
|
|
|
|
case DRIVE_INTERFACE:
|
|
DOUT2("Media is OLE COM Interface\n");
|
|
break;
|
|
|
|
default:
|
|
DPF(("Unknown Media type %d\n", npMCI->uDriveType));
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#ifdef USEAVIFILE
|
|
//
|
|
// if the "filename" is of the form: '@########' then we assume we
|
|
// have been pased a interface pointer of some sort.
|
|
//
|
|
if (npMCI->szFilename[0] == TEXT('@') &&
|
|
OpenInterface(npMCI))
|
|
goto DoneOpening;
|
|
|
|
// !!! This will open even AVI files this way!
|
|
if ((npMCI->dwOptionFlags & MCIAVIO_USEAVIFILE) &&
|
|
OpenWithAVIFile(npMCI))
|
|
goto DoneOpening;
|
|
#endif
|
|
|
|
if (!OpenRiffAVIFile(npMCI, FALSE)) {
|
|
|
|
|
|
#ifdef USEAVIFILE
|
|
//
|
|
// unable to open RIFF file, if it was because it was
|
|
// not a AVI file, then give AVIFile a try.
|
|
//
|
|
if (npMCI->dwTaskError == AVIERR_NOT_AVIFILE) {
|
|
|
|
npMCI->dwTaskError = 0;
|
|
|
|
if (OpenWithAVIFile(npMCI))
|
|
goto DoneOpening;
|
|
|
|
// if this is a riff file that we could open but chose
|
|
// to pass to avifile, and avifile is not present, then we need
|
|
// to pass it back to OpenRiffAVIFile and force it to try
|
|
// and open it properly.
|
|
|
|
npMCI->dwTaskError = 0;
|
|
if (OpenRiffAVIFile(npMCI, TRUE))
|
|
goto DoneOpening;
|
|
|
|
if (npMCI->dwTaskError == AVIERR_NOT_AVIFILE) {
|
|
npMCI->dwTaskError = MCIERR_INVALID_FILE;
|
|
// We cannot return an AVI specific error as there is
|
|
// no way for the UI to get access to the error text.
|
|
// Hence we use a generic mci error.
|
|
}
|
|
}
|
|
#endif
|
|
goto error;
|
|
}
|
|
|
|
DoneOpening:
|
|
npMCI->dwTaskError = 0;
|
|
return TRUE;
|
|
|
|
error:
|
|
mciaviCloseFile(npMCI);
|
|
|
|
if (npMCI->dwTaskError == 0)
|
|
npMCI->dwTaskError = MCIERR_INVALID_FILE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef USEAVIFILE
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api BOOL | OpenWithAVIFile | Open an file using AVIFile
|
|
*
|
|
* @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
|
|
*
|
|
* @rdesc TRUE means OK, otherwise mci error in dwTaskError
|
|
*
|
|
***************************************************************************/
|
|
|
|
STATICFN BOOL NEAR PASCAL OpenWithAVIFile(NPMCIGRAPHIC npMCI)
|
|
{
|
|
IAVIFile FAR *pf = NULL;
|
|
|
|
if (!InitAVIFile(npMCI))
|
|
return FALSE;
|
|
|
|
AVIFileOpen(&pf, npMCI->szFilename, MMIO_READ, 0);
|
|
|
|
if (pf == NULL) {
|
|
npMCI->dwTaskError = MCIERR_INVALID_FILE;
|
|
return FALSE;
|
|
}
|
|
|
|
if (!OpenAVIFile(npMCI, pf)) {
|
|
mciaviCloseFile(npMCI);
|
|
pf->lpVtbl->Release(pf);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api BOOL | OpenInterface | Open an interface pointer
|
|
*
|
|
* @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
|
|
*
|
|
* @rdesc TRUE means OK, otherwise mci error in dwTaskError
|
|
*
|
|
***************************************************************************/
|
|
|
|
STATICFN BOOL NEAR PASCAL OpenInterface(NPMCIGRAPHIC npMCI)
|
|
{
|
|
IUnknown FAR *p;
|
|
IAVIFile FAR *pf=NULL;
|
|
IAVIStream FAR *ps=NULL;
|
|
|
|
if (!InitAVIFile(npMCI))
|
|
return FALSE;
|
|
|
|
if (npMCI->szFilename[0] != TEXT('@')) {
|
|
DPF1(("Failing to open interface for file %ls\n",npMCI->szFilename));
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
p = (IUnknown FAR *)wcstol(npMCI->szFilename+1, NULL, 10);
|
|
#else
|
|
p = (IUnknown FAR *)atol(npMCI->szFilename+1);
|
|
#endif
|
|
|
|
#ifdef USE_ISVALIDINTERFACE
|
|
if (!IsValidInterface(p))
|
|
return FALSE;
|
|
#endif
|
|
|
|
#ifndef _WIN32
|
|
//!!!we need to do the PSP stuff? or will the TASK stuff in
|
|
//!!!COMPOBJ mess us up?
|
|
{
|
|
extern void FAR SetPSP(UINT psp);
|
|
SetPSP(npMCI->pspParent);
|
|
}
|
|
#endif
|
|
|
|
p->lpVtbl->QueryInterface(p, &IID_IAVIFile, (LPVOID FAR *)&pf);
|
|
|
|
if (pf != NULL)
|
|
{
|
|
if (OpenAVIFile(npMCI, pf))
|
|
return TRUE;
|
|
|
|
pf->lpVtbl->Release(pf);
|
|
pf = NULL; // AVIMakeFileFromStreams need not set pf to NULL
|
|
}
|
|
|
|
p->lpVtbl->QueryInterface(p, &IID_IAVIStream, (LPVOID FAR *)&ps);
|
|
|
|
if (ps != NULL)
|
|
{
|
|
AVIMakeFileFromStreams(&pf, 1, &ps);
|
|
ps->lpVtbl->Release(ps);
|
|
|
|
if (pf == NULL)
|
|
return FALSE;
|
|
|
|
if (OpenAVIFile(npMCI, pf))
|
|
return TRUE;
|
|
|
|
pf->lpVtbl->Release(pf);
|
|
//return FALSE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api BOOL | OpenAVIFile | Open an a AVIFile object
|
|
*
|
|
* NOTE we do not do call AddRef() we assume we dont need to.
|
|
* neither do we Release(). If we fail, our caller will Release(),
|
|
*
|
|
* @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
|
|
*
|
|
* @rdesc TRUE means OK, otherwise mci error in dwTaskError
|
|
* On a successful return npMCI->pf will be loaded with the
|
|
* input IAVIFILE interface pointer.
|
|
*
|
|
***************************************************************************/
|
|
|
|
STATICFN BOOL NEAR PASCAL OpenAVIFile(NPMCIGRAPHIC npMCI, IAVIFile FAR *pf)
|
|
{
|
|
AVIFILEINFOW info;
|
|
HRESULT hr;
|
|
IAVIStream FAR *ps;
|
|
STREAMINFO *psi;
|
|
int i;
|
|
|
|
Assert(npMCI->pf == NULL);
|
|
|
|
_fmemset(&info, 0, sizeof(info));
|
|
hr = pf->lpVtbl->Info(pf, &info, sizeof(info));
|
|
|
|
if (FAILED(GetScode(hr))) {
|
|
npMCI->dwTaskError = MCIERR_INVALID_FILE;
|
|
return FALSE;
|
|
}
|
|
|
|
DPF1(("OpenAVIFile: %ls\n\t#Streams=%d, Width=%d Height=%d\n",
|
|
info.szFileType, info.dwStreams, info.dwWidth, info.dwHeight));
|
|
|
|
//
|
|
// get rid of bad files
|
|
//
|
|
if (info.dwStreams == 0 || info.dwStreams > 255 || info.dwLength == 0) {
|
|
npMCI->dwTaskError = MCIERR_INVALID_FILE;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// save the interface pointer
|
|
// make a copy of the VTable, for later use
|
|
//
|
|
npMCI->pf = pf;
|
|
////npMCI->vt = *pf->lpVtbl;
|
|
|
|
npMCI->dwFlags |= MCIAVI_HASINDEX;
|
|
|
|
npMCI->dwMicroSecPerFrame = muldiv32(info.dwScale, 1000000, info.dwRate);
|
|
|
|
npMCI->lFrames = (LONG)info.dwLength;
|
|
npMCI->dwRate = info.dwRate;
|
|
npMCI->dwScale = info.dwScale;
|
|
|
|
npMCI->streams = (int)info.dwStreams;
|
|
|
|
npMCI->dwBytesPerSec = info.dwMaxBytesPerSec;
|
|
npMCI->dwSuggestedBufferSize = info.dwSuggestedBufferSize + 2*sizeof(DWORD);
|
|
|
|
SetRect(&npMCI->rcMovie,0,0,(int)info.dwWidth,(int)info.dwHeight);
|
|
|
|
npMCI->paStreamInfo = (STREAMINFO*)
|
|
LocalAlloc(LPTR,npMCI->streams * sizeof(STREAMINFO));
|
|
|
|
if (npMCI->paStreamInfo == NULL) {
|
|
npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
|
|
npMCI->pf = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < npMCI->streams; i++) {
|
|
|
|
ps = NULL;
|
|
AVIFileGetStream(pf, &ps, 0, i);
|
|
|
|
if (ps == NULL) {
|
|
npMCI->dwTaskError = MCIERR_INVALID_FILE;
|
|
npMCI->pf = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
if (!OpenAVIStream(npMCI, i, ps))
|
|
DPF(("Error opening stream %d!\n", i));
|
|
|
|
if (npMCI->dwTaskError) {
|
|
npMCI->pf = NULL;
|
|
ps->lpVtbl->Release(ps);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// compute the key frames every value
|
|
//
|
|
// do this by finding the key frame average over the first few frames.
|
|
//
|
|
#define NFRAMES 250
|
|
|
|
if (psi = npMCI->psiVideo) {
|
|
LONG l;
|
|
int nKeyFrames=0;
|
|
int nFrames = (int) min(npMCI->lFrames, NFRAMES);
|
|
|
|
for (l=0; l<nFrames; l++) {
|
|
if (AVIStreamFindSample(psi->ps, psi->sh.dwStart+l, FIND_PREV|FIND_KEY) == l)
|
|
nKeyFrames++;
|
|
}
|
|
|
|
if (nKeyFrames > 1)
|
|
npMCI->dwKeyFrameInfo = (DWORD)((nFrames + nKeyFrames/2)/nKeyFrames);
|
|
else
|
|
npMCI->dwKeyFrameInfo = 0;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api BOOL | OpenAVIStream | Open an a AVIStream object
|
|
*
|
|
* @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
|
|
*
|
|
* @rdesc TRUE means OK, otherwise mci error in dwTaskError
|
|
*
|
|
***************************************************************************/
|
|
|
|
STATICFN BOOL NEAR PASCAL OpenAVIStream(NPMCIGRAPHIC npMCI, int stream, IAVIStream FAR *ps)
|
|
{
|
|
STREAMINFO* psi;
|
|
AVISTREAMINFOW info;
|
|
HRESULT hr;
|
|
|
|
_fmemset(&info, 0, sizeof(info));
|
|
hr = ps->lpVtbl->Info(ps, &info, sizeof(info));
|
|
|
|
if (FAILED(GetScode(hr))) {
|
|
npMCI->dwTaskError = MCIERR_INVALID_FILE;
|
|
return FALSE;
|
|
}
|
|
|
|
DPF(("OpenAVIStream(%d) %4.4hs:%4.4hs %ls\n", stream, (LPSTR)&info.fccType, (LPSTR)&info.fccHandler, info.szName));
|
|
|
|
//
|
|
// init the STREAMINFO from the IAVIStream
|
|
//
|
|
psi = SI(stream);
|
|
|
|
psi->ps = ps; // save interface
|
|
////psi->vt = *ps->lpVtbl; // save VTable !!!needed?
|
|
|
|
psi->sh.fccType = info.fccType;
|
|
psi->sh.fccHandler = info.fccHandler;
|
|
psi->sh.dwFlags = info.dwFlags;
|
|
psi->sh.wPriority = info.wPriority;
|
|
psi->sh.wLanguage = info.wLanguage;
|
|
psi->sh.dwInitialFrames = 0; // info.dwInitialFrames;
|
|
psi->sh.dwScale = info.dwScale;
|
|
psi->sh.dwRate = info.dwRate;
|
|
psi->sh.dwStart = info.dwStart;
|
|
psi->sh.dwLength = info.dwLength;
|
|
psi->sh.dwSuggestedBufferSize = info.dwSuggestedBufferSize;
|
|
psi->sh.dwQuality = info.dwQuality;
|
|
psi->sh.dwSampleSize = info.dwSampleSize;
|
|
psi->sh.rcFrame = info.rcFrame;
|
|
|
|
//
|
|
// get the format of the stream.
|
|
//
|
|
AVIStreamFormatSize(ps, 0, &psi->cbFormat);
|
|
psi->lpFormat = GlobalAllocPtr(GMEM_MOVEABLE, psi->cbFormat);
|
|
|
|
if (!psi->lpFormat) {
|
|
npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
|
|
return FALSE;
|
|
}
|
|
|
|
AVIStreamReadFormat(psi->ps, 0, psi->lpFormat, &psi->cbFormat);
|
|
|
|
//
|
|
// get the extra data for the stream.
|
|
//
|
|
AVIStreamReadData(psi->ps,ckidSTREAMHANDLERDATA, NULL, &psi->cbData);
|
|
|
|
if (psi->cbData > 0) {
|
|
psi->lpData = GlobalAllocPtr(GMEM_MOVEABLE, psi->cbData);
|
|
|
|
if (!psi->lpData) {
|
|
npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
|
|
return FALSE;
|
|
}
|
|
|
|
AVIStreamReadData(psi->ps, ckidSTREAMHANDLERDATA, NULL, &psi->cbData);
|
|
}
|
|
|
|
return InitStream(npMCI, psi);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api BOOL | InitAVIFile | called to RTL to AVIFILE.DLL
|
|
*
|
|
* @rdesc TRUE means OK, otherwise error
|
|
*
|
|
***************************************************************************/
|
|
|
|
#ifdef _WIN32
|
|
SZCODE szAVIFILE[] = TEXT("AVIFIL32.DLL");
|
|
|
|
#ifdef USE_ISVALIDINTERFACE
|
|
SZCODE szCOMPOBJ[] = TEXT("COMPOB32");
|
|
#endif
|
|
#else
|
|
SZCODE szAVIFILE[] = "AVIFILE.DLL";
|
|
#ifdef USE_ISVALIDINTERFACE
|
|
SZCODE szCOMPOBJ[] = "COMPOBJ";
|
|
#endif
|
|
#endif
|
|
|
|
// On NT the entry points will NOT be unicode strings, as there is
|
|
// no unicode version of GetProcAddress. BUT SZCODE generate UNICODE...
|
|
|
|
// on the other hand, the entry points will all be unicode entrypoints 'W'
|
|
|
|
#ifdef UNICODE
|
|
#define UNICODE_APPEND "W"
|
|
#else
|
|
#define UNICODE_APPEND
|
|
#endif
|
|
|
|
SZCODEA szAVIFileInit[] = "AVIFileInit";
|
|
SZCODEA szAVIFileExit[] = "AVIFileExit";
|
|
|
|
#ifdef USE_ISVALIDINTERFACE
|
|
SZCODEA szIsValidInterface[] = "IsValidInterface";
|
|
#endif
|
|
|
|
SZCODEA szAVIMakeFileFromStreams[] = "AVIMakeFileFromStreams";
|
|
SZCODEA szAVIStreamBeginStreaming[] = "AVIStreamBeginStreaming";
|
|
SZCODEA szAVIStreamEndStreaming[] = "AVIStreamEndStreaming";
|
|
SZCODEA szAVIFileOpen[] = "AVIFileOpen" UNICODE_APPEND;
|
|
|
|
BOOL FAR InitAVIFile(NPMCIGRAPHIC npMCI)
|
|
{
|
|
UINT u;
|
|
|
|
if (hdllAVIFILE == (HMODULE)-1)
|
|
// We have already tried to load AVIFILE and failed
|
|
return FALSE;
|
|
|
|
if (hdllAVIFILE == NULL) {
|
|
|
|
u = SetErrorMode(SEM_NOOPENFILEERRORBOX);
|
|
#ifdef _WIN32
|
|
// The load and free of szAVIFILE is safe as it is protected by
|
|
// the npMCI device critical section. This means that open/close
|
|
// events will be synchronised.
|
|
#endif
|
|
hdllAVIFILE = LoadLibrary(szAVIFILE);
|
|
SetErrorMode(u);
|
|
|
|
#ifndef _WIN32
|
|
if ((UINT)hdllAVIFILE <= (UINT)HINSTANCE_ERROR)
|
|
hdllAVIFILE = NULL;
|
|
#endif
|
|
|
|
if (hdllAVIFILE == NULL) {
|
|
hdllAVIFILE = (HMODULE)-1;
|
|
DPF(("Failed to load AVIFILE dll - error %d\n", GetLastError()));
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef USE_ISVALIDINTERFACE
|
|
hdllCOMPOBJ = GetModuleHandle(szCOMPOBJ);
|
|
Assert(hdllCOMPOBJ != NULL);
|
|
|
|
(FARPROC)XIsValidInterface = GetProcAddress(hdllCOMPOBJ, szIsValidInterface);
|
|
|
|
Assert(XIsValidInterface != NULL);
|
|
#endif
|
|
|
|
(FARPROC)XAVIFileInit = GetProcAddress(hdllAVIFILE, szAVIFileInit);
|
|
(FARPROC)XAVIFileExit = GetProcAddress(hdllAVIFILE, szAVIFileExit);
|
|
(FARPROC)XAVIFileOpen = GetProcAddress(hdllAVIFILE, szAVIFileOpen);
|
|
(FARPROC)XAVIMakeFileFromStreams = GetProcAddress(hdllAVIFILE, szAVIMakeFileFromStreams);
|
|
(FARPROC)XAVIStreamBeginStreaming = GetProcAddress(hdllAVIFILE, szAVIStreamBeginStreaming);
|
|
(FARPROC)XAVIStreamEndStreaming = GetProcAddress(hdllAVIFILE, szAVIStreamEndStreaming);
|
|
|
|
Assert(XAVIFileInit != NULL);
|
|
Assert(XAVIFileExit != NULL);
|
|
Assert(XAVIFileOpen != NULL);
|
|
Assert(XAVIMakeFileFromStreams != NULL);
|
|
}
|
|
|
|
//
|
|
// we need to call AVIFileInit() and AVIFileExit() for each task that
|
|
// is using AVIFile or things will not work right.
|
|
//
|
|
if (!(npMCI->dwFlags & MCIAVI_USING_AVIFILE)) {
|
|
|
|
npMCI->dwFlags |= MCIAVI_USING_AVIFILE;
|
|
|
|
AVIFileInit(); // Should we check the return code?
|
|
uAVIFILE++;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// must be called from app thread too!
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api BOOL | FreeAVIFile | called to un-RTL to AVIFILE.DLL
|
|
*
|
|
* @rdesc TRUE means OK, otherwise error
|
|
*
|
|
***************************************************************************/
|
|
|
|
BOOL FAR FreeAVIFile(NPMCIGRAPHIC npMCI)
|
|
{
|
|
if (!(npMCI->dwFlags & MCIAVI_USING_AVIFILE))
|
|
return FALSE;
|
|
|
|
Assert(hdllAVIFILE != (HMODULE)-1 && hdllAVIFILE != NULL);
|
|
|
|
// free this tasks use of AVIFile
|
|
AVIFileExit();
|
|
|
|
// if no more people using AVIFile let the DLLs go.
|
|
Assert(uAVIFILE > 0);
|
|
uAVIFILE--;
|
|
|
|
if (uAVIFILE == 0) {
|
|
FreeLibrary(hdllAVIFILE);
|
|
hdllAVIFILE = NULL;
|
|
#ifdef USE_ISVALIDINTERFACE
|
|
hdllCOMPOBJ = NULL;
|
|
#endif
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#endif /* USEAVIFILE */
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api BOOL | OpenRiffAVIFile | Open an RIFF AVI file
|
|
*
|
|
* @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
|
|
*
|
|
* @parm BOOL | bForce | if FALSE, return error for files that
|
|
* we think should be opened by AVIFILE instead.
|
|
*
|
|
* @rdesc TRUE means OK, otherwise mci error in dwTaskError
|
|
*
|
|
***************************************************************************/
|
|
|
|
STATICFN BOOL NEAR PASCAL OpenRiffAVIFile(NPMCIGRAPHIC npMCI, BOOL bForce)
|
|
{
|
|
HMMIO hmmio;
|
|
HANDLE h = NULL;
|
|
BOOL fRet = TRUE;
|
|
MMIOINFO mmioInfo;
|
|
MMCKINFO ckRIFF;
|
|
MMCKINFO ckLIST;
|
|
MMCKINFO ckRECORD;
|
|
|
|
_fmemset(&mmioInfo, 0, sizeof(MMIOINFO));
|
|
mmioInfo.htask = (HANDLE)npMCI->hCallingTask;
|
|
|
|
hmmio = mmioOpen(npMCI->szFilename, &mmioInfo, MMIO_READ | MMIO_DENYWRITE);
|
|
|
|
if (hmmio == NULL)
|
|
hmmio = mmioOpen(npMCI->szFilename, &mmioInfo, MMIO_READ);
|
|
|
|
if (!hmmio) {
|
|
switch (mmioInfo.wErrorRet) {
|
|
case MMIOERR_OUTOFMEMORY:
|
|
npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
|
|
break;
|
|
case MMIOERR_FILENOTFOUND:
|
|
case MMIOERR_CANNOTOPEN:
|
|
default:
|
|
npMCI->dwTaskError = MCIERR_FILE_NOT_FOUND;
|
|
break;
|
|
}
|
|
fRet = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
npMCI->hmmio = hmmio;
|
|
|
|
/*
|
|
** Descend into RIFF file
|
|
*/
|
|
if (mmioDescend(hmmio, &ckRIFF, NULL, 0) != 0) {
|
|
goto ERROR_BADFILE;
|
|
}
|
|
|
|
/*
|
|
* 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'))
|
|
{
|
|
DPF(("File is a QuickTime public movie\n"));
|
|
|
|
/*
|
|
* now the 'mdat' atom better be a RIFF/AVI or we cant handle it.
|
|
*/
|
|
if (mmioDescend(hmmio, &ckRIFF, NULL, 0) != 0) {
|
|
goto ERROR_BADFILE;
|
|
}
|
|
}
|
|
|
|
/* Make sure it's a RIFF file */
|
|
if (ckRIFF.ckid != FOURCC_RIFF) {
|
|
npMCI->dwTaskError = MCIERR_INVALID_FILE;
|
|
goto ERROR_NOT_AVIFILE;
|
|
}
|
|
|
|
/* Otherwise, it should be an AVI file */
|
|
if (ckRIFF.fccType != formtypeAVI) {
|
|
npMCI->dwTaskError = MCIERR_INVALID_FILE;
|
|
goto ERROR_NOT_AVIFILE;
|
|
}
|
|
|
|
/*
|
|
** Descend into header LIST
|
|
*/
|
|
ckLIST.fccType = listtypeAVIHEADER;
|
|
if (mmioDescend(hmmio, &ckLIST, &ckRIFF, MMIO_FINDLIST) != 0) {
|
|
goto ERROR_BADFILE;
|
|
}
|
|
|
|
/* Leave space at end of buffer for pad word */
|
|
h = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, ckLIST.cksize -
|
|
sizeof(DWORD) +
|
|
sizeof(DWORD));
|
|
|
|
if (!h) {
|
|
npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
|
|
return FALSE;
|
|
}
|
|
|
|
npMCI->lp = npMCI->lpBuffer = (LPSTR) GlobalLock(h);
|
|
|
|
DPF(("Reading header list: %lu bytes.\n", ckLIST.cksize - sizeof(DWORD)));
|
|
|
|
if (mmioRead(hmmio, npMCI->lp, ckLIST.cksize - sizeof(DWORD))
|
|
!= (LONG) (ckLIST.cksize - sizeof(DWORD))) {
|
|
npMCI->dwTaskError = MCIERR_FILE_READ;
|
|
goto ERROR_RETURN;
|
|
}
|
|
|
|
#ifdef USE_AVIFILE_FOR_NON_INT
|
|
//
|
|
// we check here for AVI RIFF files we dont want to handle with our
|
|
// built in code, and want to pass on to AVIFILE.DLL
|
|
//
|
|
// we handle the following files:
|
|
//
|
|
// interleaved
|
|
//
|
|
// we pass on the following files to AVIFILE.DLL
|
|
//
|
|
// non-interleaved
|
|
//
|
|
// pretty simple right now, just interleaved non-interlaved
|
|
// but could get as complex as you want.
|
|
//
|
|
// if there is a problem with avifile (eg dll not present) we will
|
|
// be called with bForce TRUE: this means we should open it if at
|
|
// all possible
|
|
//
|
|
if (!bForce) {
|
|
MainAVIHeader FAR * lpHdr;
|
|
|
|
lpHdr = (MainAVIHeader FAR *)((BYTE FAR *)npMCI->lp + 8);
|
|
|
|
if (!(lpHdr->dwFlags & AVIF_ISINTERLEAVED) ||
|
|
lpHdr->dwInitialFrames == 0) {
|
|
|
|
DOUT("File is not interleaved, giving it to AVIFILE.DLL\n");
|
|
goto ERROR_NOT_AVIFILE;
|
|
}
|
|
|
|
//
|
|
// ok now we have a 1:1 interleved file.
|
|
//
|
|
// always use our code on a CD-ROM, but on other media...
|
|
//
|
|
switch (npMCI->uDriveType) {
|
|
case DRIVE_CDROM:
|
|
break;
|
|
|
|
case DRIVE_REMOTE:
|
|
case DRIVE_FIXED:
|
|
case DRIVE_REMOVABLE:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (PEEK_DWORD() == ckidAVIMAINHDR) {
|
|
if (!ParseNewHeader(npMCI))
|
|
goto ERROR_RETURN;
|
|
} else {
|
|
goto ERROR_BADFILE;
|
|
}
|
|
|
|
/* Ascend out of header LIST */
|
|
if (mmioAscend(hmmio, &ckLIST, 0) != 0) {
|
|
npMCI->dwTaskError = MCIERR_FILE_READ;
|
|
goto ERROR_RETURN;
|
|
}
|
|
|
|
/* Initially, no frame has been drawn */
|
|
npMCI->lFrameDrawn = (- (LONG) npMCI->wEarlyRecords) - 1;
|
|
|
|
/*
|
|
** Descend into big 'Movie LIST'
|
|
*/
|
|
ckLIST.fccType = listtypeAVIMOVIE;
|
|
if (mmioDescend(hmmio, &ckLIST, &ckRIFF, MMIO_FINDLIST) != 0) {
|
|
goto ERROR_BADFILE;
|
|
}
|
|
|
|
npMCI->dwMovieListOffset = ckLIST.dwDataOffset;
|
|
|
|
/* Calculate end of 'movi' list, in case we need to read the index */
|
|
npMCI->dwBigListEnd = ckLIST.dwDataOffset + ckLIST.cksize +
|
|
(ckLIST.cksize & 1);
|
|
|
|
/*
|
|
** Descend into header of first chunk
|
|
*/
|
|
if (mmioDescend(hmmio, &ckRECORD, &ckLIST, 0) != 0) {
|
|
goto ERROR_BADFILE;
|
|
}
|
|
npMCI->dwFirstRecordType = ckRECORD.ckid;
|
|
npMCI->dwFirstRecordSize = ckRECORD.cksize + 2 * sizeof(DWORD);
|
|
npMCI->dwFirstRecordPosition = mmioSeek(hmmio, 0, SEEK_CUR);
|
|
|
|
if (mmioAscend(hmmio, &ckRECORD, 0) != 0) {
|
|
npMCI->dwTaskError = MCIERR_FILE_READ;
|
|
goto ERROR_RETURN;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
DPF2(("First record (%4.4s) 0x%lx bytes at position 0x%lx.\n",
|
|
(LPSTR)&npMCI->dwFirstRecordType,
|
|
npMCI->dwFirstRecordSize,
|
|
npMCI->dwFirstRecordPosition));
|
|
|
|
if (npMCI->dwFirstRecordPosition & 0x7ff) {
|
|
DPF(("!!\n"));
|
|
DPF(("!! This file is not properly aligned to a 2K boundary.\n"));
|
|
DPF(("!! It may not play well from CD-ROM.\n"));
|
|
DPF(("!!\n"));
|
|
}
|
|
#endif
|
|
|
|
exit:
|
|
if (!fRet)
|
|
mciaviCloseFile(npMCI);
|
|
|
|
if (h) {
|
|
npMCI->lpBuffer = NULL;
|
|
npMCI->dwBufferSize = 0L;
|
|
GlobalUnlock(h);
|
|
GlobalFree(h);
|
|
}
|
|
|
|
return fRet;
|
|
|
|
ERROR_NOT_AVIFILE:
|
|
#ifdef USEAVIFILE
|
|
npMCI->dwTaskError = AVIERR_NOT_AVIFILE; // mark as not a AVI file
|
|
goto ERROR_RETURN; // we will be called a second time
|
|
#endif
|
|
|
|
ERROR_BADFILE:
|
|
npMCI->dwTaskError = MCIERR_INVALID_FILE; // something wrong with the file
|
|
|
|
ERROR_RETURN:
|
|
fRet = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api BOOL | ParseNewHeader | 'nuf said
|
|
*
|
|
***************************************************************************/
|
|
|
|
STATICFN BOOL NEAR PASCAL ParseNewHeader(NPMCIGRAPHIC npMCI)
|
|
{
|
|
DWORD dwHeaderSize;
|
|
MainAVIHeader FAR * lpHdr;
|
|
int stream;
|
|
|
|
if (GET_DWORD() != ckidAVIMAINHDR) {
|
|
goto FileError;
|
|
}
|
|
|
|
dwHeaderSize = GET_DWORD(); /* Skip size */
|
|
|
|
/* Now, we're pointing at the actual header */
|
|
lpHdr = (MainAVIHeader FAR *) npMCI->lp;
|
|
|
|
npMCI->lFrames = (LONG)lpHdr->dwTotalFrames;
|
|
npMCI->dwMicroSecPerFrame = lpHdr->dwMicroSecPerFrame;
|
|
npMCI->dwRate = 1000000;
|
|
npMCI->dwScale = npMCI->dwMicroSecPerFrame;
|
|
|
|
/* Reject some bad values */
|
|
if (!lpHdr->dwStreams || lpHdr->dwStreams > 255 || !npMCI->lFrames) {
|
|
goto FileError;
|
|
}
|
|
|
|
npMCI->streams = (int) lpHdr->dwStreams;
|
|
npMCI->dwBytesPerSec = lpHdr->dwMaxBytesPerSec;
|
|
npMCI->wEarlyRecords = (UINT) lpHdr->dwInitialFrames;
|
|
npMCI->dwSuggestedBufferSize = lpHdr->dwSuggestedBufferSize;
|
|
|
|
SetRect(&npMCI->rcMovie,0,0,(int)lpHdr->dwWidth,(int)lpHdr->dwHeight);
|
|
|
|
npMCI->dwFlags |= MCIAVI_HASINDEX;
|
|
|
|
if (!(lpHdr->dwFlags & AVIF_ISINTERLEAVED)) {
|
|
DPF(("File is not interleaved.\n"));
|
|
npMCI->dwFlags |= MCIAVI_NOTINTERLEAVED;
|
|
}
|
|
|
|
SKIP_BYTES(dwHeaderSize); /* Skip rest of chunk */
|
|
|
|
npMCI->paStreamInfo = (STREAMINFO NEAR *)
|
|
LocalAlloc(LPTR, npMCI->streams * sizeof(STREAMINFO));
|
|
// !!! error check
|
|
|
|
for (stream = 0; stream < npMCI->streams; stream++) {
|
|
AVIStreamHeader FAR * lpStream;
|
|
HPSTR hpNextChunk;
|
|
STREAMINFO * psi = &npMCI->paStreamInfo[stream];
|
|
|
|
if (GET_DWORD() != FOURCC_LIST) {
|
|
goto FileError;
|
|
}
|
|
|
|
dwHeaderSize = GET_DWORD(); /* Skip size */
|
|
|
|
hpNextChunk = npMCI->lp + (dwHeaderSize + (dwHeaderSize & 1));
|
|
|
|
if (GET_DWORD() != listtypeSTREAMHEADER) {
|
|
goto FileError;
|
|
}
|
|
|
|
/* Now, we're at the begging of the stream's header chunks. */
|
|
|
|
if (GET_DWORD() != ckidSTREAMHEADER) {
|
|
goto FileError;
|
|
}
|
|
|
|
dwHeaderSize = GET_DWORD(); /* Skip size */
|
|
|
|
/* Now, we're pointing at the stream header */
|
|
lpStream = (AVIStreamHeader FAR *) npMCI->lp;
|
|
hmemcpy(&psi->sh, lpStream, min(dwHeaderSize, sizeof(psi->sh)));
|
|
|
|
//
|
|
// reject files with more than one video stream.
|
|
//
|
|
if (psi->sh.fccType == streamtypeVIDEO &&
|
|
npMCI->nVideoStreams >= 1) {
|
|
DPF(("File has multiple video streams, giving it to AVIFILE.DLL\n"));
|
|
goto DontHandleThisFile;
|
|
}
|
|
|
|
SKIP_BYTES(dwHeaderSize);
|
|
|
|
/* Read the format */
|
|
if (GET_DWORD() != ckidSTREAMFORMAT) {
|
|
goto FileError;
|
|
}
|
|
|
|
dwHeaderSize = GET_DWORD(); /* Skip size */
|
|
|
|
if (dwHeaderSize > 16384L) {
|
|
goto FileError;
|
|
}
|
|
|
|
psi->cbFormat = dwHeaderSize;
|
|
psi->lpFormat = GlobalAllocPtr(GHND,dwHeaderSize);
|
|
if (!psi->lpFormat) {
|
|
npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
|
|
return FALSE;
|
|
}
|
|
|
|
hmemcpy(psi->lpFormat, npMCI->lp, dwHeaderSize);
|
|
|
|
SKIP_BYTES(dwHeaderSize);
|
|
|
|
if (PEEK_DWORD() == ckidSTREAMHANDLERDATA) {
|
|
GET_DWORD();
|
|
dwHeaderSize = GET_DWORD(); /* Skip size */
|
|
|
|
psi->cbData = dwHeaderSize;
|
|
psi->lpData = GlobalAllocPtr(GHND,dwHeaderSize);
|
|
|
|
if (!psi->lpData) {
|
|
npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
|
|
return FALSE;
|
|
}
|
|
|
|
hmemcpy(psi->lpData, npMCI->lp, dwHeaderSize);
|
|
|
|
/* Skip to end of Data chunk */
|
|
SKIP_BYTES(dwHeaderSize);
|
|
} else {
|
|
psi->cbData = 0;
|
|
psi->lpData = NULL;
|
|
}
|
|
|
|
InitStream(npMCI, psi);
|
|
|
|
npMCI->lp = hpNextChunk;
|
|
}
|
|
|
|
if (npMCI->dwTaskError) {
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
|
|
FileError:
|
|
npMCI->dwTaskError = MCIERR_INVALID_FILE;
|
|
return FALSE;
|
|
|
|
DontHandleThisFile:
|
|
npMCI->dwTaskError = AVIERR_NOT_AVIFILE;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// --- called on worker thread ---------------------------------------------
|
|
//
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api BOOL | OpenFileInit | called after a file is opened to init things
|
|
*
|
|
* @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
|
|
*
|
|
* @rdesc TRUE means OK, otherwise mci error in dwTaskError
|
|
*
|
|
***************************************************************************/
|
|
|
|
BOOL NEAR PASCAL OpenFileInit(NPMCIGRAPHIC npMCI)
|
|
{
|
|
int i;
|
|
RECT rc;
|
|
|
|
//
|
|
// lets make sure we opened something.
|
|
//
|
|
if (npMCI->streams == 0)
|
|
return FALSE;
|
|
|
|
if (npMCI->nVideoStreams + npMCI->nAudioStreams + npMCI->nOtherStreams == 0)
|
|
return FALSE;
|
|
|
|
if (npMCI->nVideoStreams == 0)
|
|
npMCI->dwFlags &= ~MCIAVI_SHOWVIDEO;
|
|
|
|
if (npMCI->nAudioStreams == 0)
|
|
npMCI->dwFlags &= ~MCIAVI_PLAYAUDIO; // No audio streams means silence
|
|
|
|
if (IsNTWOW()) {
|
|
if (!npMCI->pbiFormat) {
|
|
// Although there are valid video streams we do not appear to have
|
|
// access to a decompressor that knows what to do with them. Hence
|
|
// if we are in NT/WOW we fail the open, the call will return to
|
|
// 16 bit land and there might be a codec there that can cope
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
if (npMCI->nAudioStreams > 1) {
|
|
UINT wLang;
|
|
int stream;
|
|
|
|
#ifndef _WIN32
|
|
UINT (WINAPI * GetUserDefaultLangID)(void);
|
|
UINT u;
|
|
HANDLE hdll;
|
|
|
|
u = SetErrorMode(SEM_NOOPENFILEERRORBOX);
|
|
hdll = LoadLibrary(szOLENLSDLL);
|
|
SetErrorMode(u);
|
|
|
|
wLang = 0;
|
|
if ((UINT)hdll > (UINT)HINSTANCE_ERROR)
|
|
{
|
|
if ((FARPROC) GetUserDefaultLangID = GetProcAddress(hdll, szOLENLSAPI)) {
|
|
wLang = GetUserDefaultLangID();
|
|
}
|
|
FreeLibrary(hdll);
|
|
}
|
|
#else
|
|
wLang = GetUserDefaultLangID();
|
|
#endif
|
|
DPF(("Current language: %x\n", wLang));
|
|
|
|
if (wLang > 0) {
|
|
for (stream = 0; stream < npMCI->streams; stream++) {
|
|
if (SH(stream).fccType == streamtypeAUDIO) {
|
|
|
|
if (!(SH(stream).dwFlags & STREAM_ENABLED))
|
|
continue;
|
|
|
|
if (SH(stream).wLanguage == wLang) {
|
|
npMCI->nAudioStream = stream;
|
|
npMCI->psiAudio = SI(stream);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (npMCI->dwFlags & MCIAVI_NOTINTERLEAVED) {
|
|
npMCI->wEarlyRecords = npMCI->wEarlyVideo;
|
|
}
|
|
else {
|
|
npMCI->wEarlyRecords = max(npMCI->wEarlyVideo, npMCI->wEarlyAudio);
|
|
}
|
|
|
|
if (npMCI->wEarlyRecords == 0 &&
|
|
!(npMCI->dwFlags & MCIAVI_NOTINTERLEAVED)) {
|
|
DPF(("Interleaved file with no audio skew?\n"));
|
|
npMCI->dwFlags |= MCIAVI_NOTINTERLEAVED;
|
|
}
|
|
|
|
if (npMCI->dwFlags & MCIAVI_ANIMATEPALETTE) {
|
|
DPF(("This AVI file has palette changes.\n"));
|
|
|
|
if (npMCI->nVideoStreams > 1) {
|
|
npMCI->dwFlags &= ~MCIAVI_ANIMATEPALETTE;
|
|
DPF(("...But we are going to ignore them?\n"));
|
|
}
|
|
}
|
|
|
|
//
|
|
// this must be set
|
|
//
|
|
if (npMCI->dwSuggestedBufferSize == 0) {
|
|
for (i=0; i<npMCI->streams; i++)
|
|
npMCI->dwSuggestedBufferSize =
|
|
max(SH(i).dwSuggestedBufferSize,npMCI->dwSuggestedBufferSize);
|
|
}
|
|
|
|
//
|
|
// check all fields in the main header
|
|
//
|
|
if (npMCI->dwScale == 0 ||
|
|
npMCI->dwRate == 0) {
|
|
}
|
|
|
|
////will be set when header parsed
|
|
////npMCI->dwMicroSecPerFrame = muldiv32(npMCI->dwScale, 1000000, npMCI->dwRate);
|
|
npMCI->dwPlayMicroSecPerFrame = npMCI->dwMicroSecPerFrame;
|
|
|
|
#define COMMON_SCALE 10000
|
|
//
|
|
// convert the rate/scale into something that is normalized to 1000
|
|
//
|
|
npMCI->dwRate = muldiv32(npMCI->dwRate, COMMON_SCALE, npMCI->dwScale);
|
|
npMCI->dwScale = COMMON_SCALE;
|
|
|
|
//
|
|
// walk all streams and fix them up.
|
|
//
|
|
for (i=0; i<npMCI->streams; i++) {
|
|
STREAMINFO *psi = SI(i);
|
|
LONG lStart;
|
|
LONG lEnd;
|
|
|
|
//
|
|
// convert the rate/scale into something that is normalized to 1000
|
|
//
|
|
psi->sh.dwRate = muldiv32(psi->sh.dwRate, COMMON_SCALE, psi->sh.dwScale);
|
|
psi->sh.dwScale = COMMON_SCALE;
|
|
|
|
//
|
|
// trim any streams that hang over the movie.
|
|
//
|
|
lStart = MovieToStream(psi, 0);
|
|
lEnd = MovieToStream(psi, npMCI->lFrames);
|
|
|
|
if ((LONG)(psi->sh.dwStart + psi->sh.dwLength) > lEnd) {
|
|
|
|
DPF(("Stream #%d is too long, was %ld now %ld\n", i,
|
|
psi->sh.dwLength, lEnd - psi->sh.dwStart));
|
|
|
|
psi->sh.dwLength = lEnd - psi->sh.dwStart;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this is WOW we had better check that there is an audio codec
|
|
// available. If not, we let the 16 bit side open the video.
|
|
//
|
|
if (IsNTWOW()) {
|
|
if (SetUpAudio(npMCI, FALSE)) return FALSE;
|
|
}
|
|
|
|
//
|
|
// fix up the movie rect
|
|
//
|
|
if (IsRectEmpty(&npMCI->rcMovie)) {
|
|
DPF2(("Movie rect is empty\n"));
|
|
|
|
SetRectEmpty(&rc);
|
|
|
|
for (i=0; i<npMCI->streams; i++) {
|
|
UnionRect(&rc,&rc,&SH(i).rcFrame);
|
|
}
|
|
|
|
npMCI->rcMovie = rc;
|
|
DPF2(("Movie rect was empty, now [%d, %d, %d, %d]\n", rc));
|
|
} else {
|
|
rc = npMCI->rcMovie;
|
|
}
|
|
|
|
|
|
// check we can access the interface ptr - the main reason for this
|
|
// is that if we have pasted this interface ptr, we cannot access it on
|
|
// the worker thread (which is where we are now). Clean up the error handling
|
|
// by actually detecting this case.
|
|
|
|
if (npMCI->pf) {
|
|
IAVIStream * ps;
|
|
long l;
|
|
if ((l = AVIFileGetStream(npMCI->pf, &ps, 0, 0)) != 0) {
|
|
DPF(("avifile err %d", l));
|
|
npMCI->dwTaskError = l;
|
|
return FALSE;
|
|
} else {
|
|
if (ps->lpVtbl->FindSample(ps, 0, FIND_FROM_START|FIND_NEXT|FIND_ANY) < 0) {
|
|
npMCI->dwTaskError = MCIERR_DEVICE_NOT_READY;
|
|
ps->lpVtbl->Release(ps);
|
|
return FALSE;
|
|
}
|
|
ps->lpVtbl->Release(ps);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// always read the index, so we can skip frames even on CD!
|
|
//
|
|
ReadIndex(npMCI);
|
|
|
|
DPF(("Key frames are every (on average): %ld frames (%ld ms)\n",npMCI->dwKeyFrameInfo, MovieToTime(npMCI->dwKeyFrameInfo)));
|
|
|
|
// force things to happen, in case we're re-loading
|
|
SetRectEmpty(&npMCI->rcSource);
|
|
SetRectEmpty(&npMCI->rcDest);
|
|
|
|
/* this will call DrawDibBegin() ... */
|
|
if (TryPutRect(npMCI, MCI_DGV_PUT_SOURCE, &rc, &npMCI->dwTaskError)) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* also set the dest rect. This should be done
|
|
* by the WM_SIZE message sent during SetWindowToDefaultSize.
|
|
* On NT, the WM_SIZE message is not sent synchronously since it
|
|
* is an inter-thread sendmessage (the winproc is on the original thread
|
|
* whereas we are currently running on the avi thread). The winproc
|
|
* thread may well not get the WM_SIZE message until much too late, so
|
|
* set the dest rect here. Note: don't use ResetDestRect since that
|
|
* also relies on the window size, which is not set yet.
|
|
*/
|
|
|
|
/* double frame size of destination if zoom by 2 */
|
|
|
|
if (npMCI->dwOptionFlags & MCIAVIO_ZOOMBY2)
|
|
SetRect(&rc, 0, 0, rc.right*2, rc.bottom*2);
|
|
|
|
if (TryPutRect(npMCI, MCI_DGV_PUT_DESTINATION, &rc, &npMCI->dwTaskError)) {
|
|
return FALSE;
|
|
}
|
|
if (npMCI->dwTaskError) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// size the window and things.
|
|
//
|
|
SetWindowToDefaultSize(npMCI, FALSE);
|
|
|
|
DrawBegin(npMCI, NULL);
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api BOOL | mciaviCloseFile | Close an AVI file.
|
|
*
|
|
* @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
|
|
*
|
|
* @rdesc TRUE means OK, otherwise mci error in dwTaskError
|
|
*
|
|
***************************************************************************/
|
|
|
|
BOOL FAR PASCAL mciaviCloseFile (NPMCIGRAPHIC npMCI)
|
|
{
|
|
if (!npMCI)
|
|
return FALSE;
|
|
|
|
if (npMCI->lpMMIOBuffer) {
|
|
GlobalFreePtr(npMCI->lpMMIOBuffer);
|
|
npMCI->lpMMIOBuffer = NULL;
|
|
}
|
|
|
|
npMCI->hicDraw = NULL;
|
|
|
|
if (npMCI->hicDrawDefault) {
|
|
if (npMCI->hicDrawDefault != (HIC) -1)
|
|
ICClose(npMCI->hicDrawDefault);
|
|
npMCI->hicDrawDefault = NULL;
|
|
}
|
|
|
|
if (npMCI->hicDrawFull) {
|
|
if (npMCI->hicDrawFull != (HIC) -1)
|
|
ICClose(npMCI->hicDrawFull);
|
|
npMCI->hicDrawFull = NULL;
|
|
}
|
|
|
|
if (npMCI->hicDecompress) {
|
|
// !!! What if we never began it?
|
|
ICDecompressEnd(npMCI->hicDecompress);
|
|
|
|
ICClose(npMCI->hicDecompress);
|
|
npMCI->hicDecompress = NULL;
|
|
}
|
|
|
|
if (npMCI->hicInternal) {
|
|
ICClose(npMCI->hicInternal);
|
|
npMCI->hicInternal = NULL;
|
|
}
|
|
|
|
if (npMCI->hicInternalFull) {
|
|
ICClose(npMCI->hicInternalFull);
|
|
npMCI->hicInternalFull = NULL;
|
|
}
|
|
|
|
if (npMCI->hmmio) {
|
|
mmioClose(npMCI->hmmio, 0);
|
|
npMCI->hmmio = NULL;
|
|
}
|
|
|
|
if (npMCI->hmmioAudio) {
|
|
mmioClose(npMCI->hmmioAudio, 0);
|
|
npMCI->hmmioAudio = NULL;
|
|
}
|
|
|
|
if (npMCI->pWF) {
|
|
LocalFree((HANDLE) npMCI->pWF);
|
|
npMCI->pWF = NULL;
|
|
}
|
|
|
|
if (npMCI->pbiFormat) {
|
|
GlobalFreePtr(npMCI->pbiFormat);
|
|
npMCI->pbiFormat = NULL;
|
|
}
|
|
|
|
// if (npMCI->hpal) {
|
|
// DeleteObject(npMCI->hpal);
|
|
// npMCI->hpal = NULL;
|
|
// }
|
|
|
|
if (npMCI->hpDecompress) {
|
|
GlobalFreePtr(npMCI->hpDecompress);
|
|
npMCI->hpDecompress = NULL;
|
|
}
|
|
|
|
if (npMCI->hpIndex) {
|
|
GlobalFreePtr(npMCI->hpIndex);
|
|
npMCI->hpIndex = NULL;
|
|
}
|
|
|
|
if (npMCI->hpFrameIndex) {
|
|
GlobalUnlock(npMCI->hgFrameIndex);
|
|
GlobalFree(npMCI->hgFrameIndex);
|
|
|
|
npMCI->hpFrameIndex = NULL;
|
|
npMCI->hgFrameIndex = 0;
|
|
}
|
|
|
|
if (npMCI->pVolumeTable) {
|
|
LocalFree((HLOCAL)npMCI->pVolumeTable);
|
|
npMCI->pVolumeTable = NULL;
|
|
}
|
|
|
|
#ifdef USEAVIFILE
|
|
if (npMCI->pf) {
|
|
AVIFileClose(npMCI->pf);
|
|
npMCI->pf = NULL;
|
|
}
|
|
#endif
|
|
|
|
if (npMCI->paStreamInfo) {
|
|
int i;
|
|
|
|
for (i = 0; i < npMCI->streams; i++) {
|
|
CloseStream(npMCI, &npMCI->paStreamInfo[i]);
|
|
}
|
|
|
|
LocalFree((HLOCAL)npMCI->paStreamInfo);
|
|
npMCI->paStreamInfo = NULL;
|
|
}
|
|
|
|
npMCI->streams = 0;
|
|
npMCI->nAudioStreams = 0;
|
|
npMCI->nVideoStreams = 0;
|
|
npMCI->nErrorStreams = 0;
|
|
npMCI->nOtherStreams = 0;
|
|
|
|
npMCI->wEarlyVideo = 0;
|
|
npMCI->wEarlyAudio = 0;
|
|
npMCI->wEarlyRecords = 0;
|
|
|
|
//!!! I bet we need to clear more
|
|
npMCI->dwFlags &= ~(MCIAVI_NOTINTERLEAVED |
|
|
MCIAVI_ANIMATEPALETTE |
|
|
MCIAVI_CANDRAW |
|
|
MCIAVI_HASINDEX);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api BOOL | CloseStream | Close an StreamAVI file.
|
|
*
|
|
***************************************************************************/
|
|
|
|
STATICFN void NEAR PASCAL CloseStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
|
|
{
|
|
psi->dwFlags &= ~STREAM_ENABLED;
|
|
////psi->sh.fccType = 0;
|
|
////psi->sh.fccHandler = 0;
|
|
|
|
if (psi->lpFormat) {
|
|
GlobalFreePtr(psi->lpFormat);
|
|
psi->lpFormat = NULL;
|
|
}
|
|
|
|
if (psi->lpData) {
|
|
GlobalFreePtr(psi->lpData);
|
|
psi->lpData = NULL;
|
|
}
|
|
|
|
if (psi->hicDraw) {
|
|
ICClose(psi->hicDraw);
|
|
psi->hicDraw = NULL;
|
|
}
|
|
|
|
#ifdef USEAVIFILE
|
|
if (psi->ps) {
|
|
AVIStreamClose(psi->ps);
|
|
psi->ps = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api BOOL | InitStream | initialize a stream
|
|
*
|
|
***************************************************************************/
|
|
|
|
STATICFN BOOL NEAR PASCAL InitStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
|
|
{
|
|
BOOL f;
|
|
|
|
//
|
|
// set flags
|
|
//
|
|
if (psi->sh.dwFlags & AVISF_VIDEO_PALCHANGES)
|
|
psi->dwFlags |= STREAM_PALCHANGES;
|
|
|
|
psi->lStart = (LONG)psi->sh.dwStart;
|
|
psi->lEnd = (LONG)psi->sh.dwStart + psi->sh.dwLength;
|
|
|
|
if (psi->sh.fccType == streamtypeVIDEO &&
|
|
!(npMCI->dwFlags & MCIAVI_NOTINTERLEAVED))
|
|
psi->lStart -= (LONG)psi->sh.dwInitialFrames;
|
|
|
|
switch(psi->sh.fccType) {
|
|
case streamtypeVIDEO:
|
|
f = InitVideoStream(npMCI, psi);
|
|
break;
|
|
|
|
case streamtypeAUDIO:
|
|
f = InitAudioStream(npMCI, psi);
|
|
break;
|
|
|
|
default:
|
|
f = InitOtherStream(npMCI, psi);
|
|
break;
|
|
}
|
|
|
|
if (!f) {
|
|
psi->dwFlags |= STREAM_ERROR;
|
|
npMCI->nErrorStreams++;
|
|
CloseStream(npMCI, psi);
|
|
}
|
|
|
|
//
|
|
// disable the stream if the file header says to
|
|
//
|
|
if (psi->sh.dwFlags & AVISF_DISABLED) {
|
|
psi->dwFlags &= ~STREAM_ENABLED;
|
|
}
|
|
|
|
return f;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api BOOL | InitVideoStream | initialize a video stream
|
|
*
|
|
***************************************************************************/
|
|
|
|
STATICFN BOOL NEAR PASCAL InitVideoStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
|
|
{
|
|
LPBITMAPINFOHEADER lpbi;
|
|
int stream = (int) (INT_PTR) (psi - npMCI->paStreamInfo);
|
|
|
|
npMCI->wEarlyVideo = (UINT)psi->sh.dwInitialFrames;
|
|
|
|
if (psi->sh.dwFlags & AVISF_VIDEO_PALCHANGES) {
|
|
//!!! is this right.
|
|
npMCI->dwFlags |= MCIAVI_ANIMATEPALETTE;
|
|
}
|
|
|
|
if (IsRectBogus(&psi->sh.rcFrame)) {
|
|
DPF(("BOGUS Stream rectangle [%d %d %d %d]\n", psi->sh.rcFrame));
|
|
SetRectEmpty(&psi->sh.rcFrame);
|
|
}
|
|
|
|
// In case the rectangle is totally wrong, chop it down to size....
|
|
// !!! What if the user _wants_ a zero-size RECT?
|
|
IntersectRect(&psi->sh.rcFrame, &psi->sh.rcFrame, &npMCI->rcMovie);
|
|
if (IsRectEmpty(&psi->sh.rcFrame)) {
|
|
DPF2(("Video stream rect is empty, correcting\n"));
|
|
SetRect(&psi->sh.rcFrame, 0, 0,
|
|
(int)((LPBITMAPINFOHEADER)psi->lpFormat)->biWidth,
|
|
(int)((LPBITMAPINFOHEADER)psi->lpFormat)->biHeight);
|
|
}
|
|
|
|
//
|
|
// make sure the biCompression is right for RLE files.
|
|
//
|
|
lpbi = (LPBITMAPINFOHEADER)psi->lpFormat;
|
|
|
|
if (psi->sh.fccHandler == 0) {
|
|
|
|
if (lpbi->biCompression == 0)
|
|
psi->sh.fccHandler = comptypeDIB;
|
|
|
|
if (lpbi->biCompression == BI_RLE8 && lpbi->biBitCount == 8)
|
|
psi->sh.fccHandler = comptypeRLE;
|
|
|
|
if (lpbi->biCompression > 256)
|
|
psi->sh.fccHandler = lpbi->biCompression;
|
|
}
|
|
|
|
if (lpbi->biCompression <= BI_RLE8 && lpbi->biBitCount == 8) {
|
|
|
|
if (psi->sh.fccHandler == comptypeRLE0 ||
|
|
psi->sh.fccHandler == comptypeRLE)
|
|
lpbi->biCompression = BI_RLE8;
|
|
|
|
// Assuming a DIB handler has RGB data will blow up files that have RLE data.
|
|
// Unfortunately, VidEdit writes out files like this.
|
|
// if (psi->sh.fccHandler == comptypeDIB)
|
|
// lpbi->biCompression = BI_RGB;
|
|
}
|
|
|
|
//
|
|
// make sure the color table is set to the right size
|
|
//
|
|
if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
|
|
lpbi->biClrUsed = (1 << (int)lpbi->biBitCount);
|
|
|
|
//
|
|
// try to open draw handler
|
|
//
|
|
if (psi->sh.fccHandler) {
|
|
psi->hicDraw = ICDrawOpen(psi->sh.fccType,psi->sh.fccHandler,psi->lpFormat);
|
|
|
|
if (psi->hicDraw)
|
|
DPF(("Opened draw handler %4.4hs:%4.4hs (Stream %d)\n", (LPSTR)&psi->sh.fccType,(LPSTR)&psi->sh.fccHandler, stream));
|
|
else
|
|
DPF(("Failed to open draw handler for %4.4hs:%4.4hs (Stream %d)\n", (LPSTR)&psi->sh.fccType,(LPSTR)&psi->sh.fccHandler, stream));
|
|
}
|
|
|
|
//
|
|
// one video stream is the master, he controls the palette etc
|
|
// for lack of a better default the first valid (i.e. one we can
|
|
// handle) video stream will
|
|
// become the master.
|
|
//
|
|
if (npMCI->pbiFormat == NULL) {
|
|
|
|
npMCI->nVideoStream = stream;
|
|
npMCI->psiVideo = psi;
|
|
|
|
npMCI->pbiFormat = (LPBITMAPINFOHEADER)
|
|
GlobalAllocPtr(GMEM_MOVEABLE, psi->cbFormat);
|
|
|
|
if (!npMCI->pbiFormat) {
|
|
npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// copy the entire format over
|
|
//
|
|
hmemcpy(npMCI->pbiFormat,psi->lpFormat,psi->cbFormat);
|
|
|
|
npMCI->bih = *npMCI->pbiFormat;
|
|
npMCI->bih.biSize = sizeof(BITMAPINFOHEADER);
|
|
npMCI->bih.biCompression = BI_RGB;
|
|
|
|
if (npMCI->bih.biClrUsed) {
|
|
/* save the original colors. */
|
|
hmemcpy(npMCI->argb, (LPBYTE)npMCI->pbiFormat + npMCI->pbiFormat->biSize,
|
|
(int)npMCI->bih.biClrUsed * sizeof(RGBQUAD));
|
|
hmemcpy(npMCI->argbOriginal, (LPSTR) npMCI->pbiFormat + npMCI->pbiFormat->biSize,
|
|
(int)npMCI->bih.biClrUsed * sizeof(RGBQUAD));
|
|
}
|
|
|
|
//
|
|
// now open the decompressor, try fastdecompress if it will do it.
|
|
//
|
|
npMCI->hicDecompress = ICLocate(ICTYPE_VIDEO,psi->sh.fccHandler,
|
|
psi->lpFormat,NULL,ICMODE_FASTDECOMPRESS);
|
|
|
|
// fast decompress may not be supported
|
|
if (npMCI->hicDecompress == NULL) {
|
|
npMCI->hicDecompress = ICDecompressOpen(ICTYPE_VIDEO,
|
|
psi->sh.fccHandler,psi->lpFormat,NULL);
|
|
}
|
|
|
|
//
|
|
// set any state data.
|
|
//
|
|
if (npMCI->hicDecompress && psi->cbData) {
|
|
ICSetState(npMCI->hicDecompress, psi->lpData, psi->cbData);
|
|
}
|
|
|
|
if (psi->hicDraw == NULL && npMCI->hicDecompress == NULL &&
|
|
psi->sh.fccHandler != comptypeRLE0 &&
|
|
psi->sh.fccHandler != comptypeNONE &&
|
|
psi->sh.fccHandler != comptypeDIB &&
|
|
psi->sh.fccHandler != comptypeRLE &&
|
|
psi->sh.fccHandler != 0) {
|
|
|
|
DPF(("Unable to open compressor '%4.4hs'!!!\n", (LPSTR) &psi->sh.fccHandler));
|
|
|
|
npMCI->nVideoStream = -1;
|
|
npMCI->psiVideo = NULL;
|
|
|
|
GlobalFreePtr(npMCI->pbiFormat);
|
|
npMCI->pbiFormat = NULL;
|
|
|
|
//
|
|
// we would like to return a custom error but by the time the
|
|
// user (application) comes to retrieve the error text MCI will
|
|
// have unloaded us (because the open failed) and therefore
|
|
// will not be able to get our specific error text.
|
|
// Hence we return a bogus generic error.
|
|
//
|
|
#ifdef _WIN32
|
|
SetLastError(MCIERR_AVI_NOCOMPRESSOR);
|
|
#endif
|
|
if (npMCI->streams == 1) // this is the only stream
|
|
npMCI->dwTaskError = MMSYSERR_NODRIVER; // MCIERR_AVI_NOCOMPRESSOR;
|
|
|
|
return FALSE; // cant load this video stream
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// this is not the default video stream find a draw handler that
|
|
// can deal with the stream.
|
|
//
|
|
|
|
//
|
|
// try VIDS.DRAW
|
|
//
|
|
// if that fails open a draw handler not-specific to the format
|
|
//
|
|
if (psi->hicDraw == NULL) {
|
|
|
|
psi->hicDraw = ICOpen(psi->sh.fccType,FOURCC_AVIDraw,ICMODE_DRAW);
|
|
|
|
if (psi->hicDraw)
|
|
DOUT("Opened draw handler VIDS.DRAW\n");
|
|
|
|
if (psi->hicDraw && ICDrawQuery(psi->hicDraw,psi->lpFormat) != ICERR_OK) {
|
|
DOUT("Closing VIDS.DRAW because it cant handle this format");
|
|
ICClose(psi->hicDraw);
|
|
psi->hicDraw = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// if that fails open our internal handler.
|
|
//
|
|
if (psi->hicDraw == NULL) {
|
|
|
|
psi->hicDraw = ICOpenFunction(psi->sh.fccType,
|
|
FOURCC_AVIDraw,ICMODE_DRAW,(FARPROC)ICAVIDrawProc);
|
|
|
|
if (psi->hicDraw)
|
|
DOUT("Opened Internal draw handler\n");
|
|
}
|
|
}
|
|
|
|
npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;
|
|
|
|
psi->dwFlags |= STREAM_VIDEO; // is a video stream
|
|
psi->dwFlags |= STREAM_ENABLED;
|
|
npMCI->nVideoStreams++;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api BOOL | InitAudioStream | initialize a audio stream
|
|
*
|
|
***************************************************************************/
|
|
|
|
STATICFN BOOL NEAR PASCAL InitAudioStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
|
|
{
|
|
int stream = (int) (INT_PTR) (psi - npMCI->paStreamInfo);
|
|
LPWAVEFORMAT pwf;
|
|
|
|
npMCI->wEarlyAudio = (UINT)psi->sh.dwInitialFrames;
|
|
|
|
pwf = (LPWAVEFORMAT)psi->lpFormat;
|
|
|
|
if (pwf->nChannels == 0 || pwf->nSamplesPerSec == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (pwf->wFormatTag == WAVE_FORMAT_PCM) {
|
|
pwf->nBlockAlign = pwf->nChannels *
|
|
((((LPPCMWAVEFORMAT)pwf)->wBitsPerSample + 7) / 8);
|
|
|
|
pwf->nAvgBytesPerSec = pwf->nBlockAlign * pwf->nSamplesPerSec;
|
|
}
|
|
|
|
psi->sh.dwSampleSize = pwf->nBlockAlign;
|
|
|
|
psi->dwFlags |= STREAM_AUDIO; // audio stream
|
|
psi->dwFlags |= STREAM_ENABLED; // enabled by default.
|
|
|
|
//
|
|
// make sure dwRate and dwScale are right
|
|
// dwRate/dwScale should be blocks/sec
|
|
//
|
|
Assert(muldiv32(pwf->nAvgBytesPerSec,1000,pwf->nBlockAlign) ==
|
|
muldiv32(psi->sh.dwRate, 1000, psi->sh.dwScale));
|
|
|
|
//
|
|
// just to be safe set these ourself to the right value.
|
|
//
|
|
psi->sh.dwRate = pwf->nAvgBytesPerSec;
|
|
psi->sh.dwScale = pwf->nBlockAlign;
|
|
|
|
//
|
|
// only one audio stream can be active at once
|
|
// for lack of a better default the first audio stream will
|
|
// become the active one.
|
|
//
|
|
if (npMCI->nAudioStreams == 0) {
|
|
npMCI->nAudioStream = stream;
|
|
npMCI->psiAudio = psi;
|
|
}
|
|
|
|
npMCI->nAudioStreams++;
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api BOOL | InitOtherStream | initialize a random stream
|
|
*
|
|
***************************************************************************/
|
|
|
|
STATICFN BOOL NEAR PASCAL InitOtherStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
|
|
{
|
|
int stream = (int) (INT_PTR) (psi - npMCI->paStreamInfo);
|
|
|
|
/* Open the specified video compressor */
|
|
psi->hicDraw = ICDrawOpen(psi->sh.fccType,psi->sh.fccHandler,psi->lpFormat);
|
|
|
|
if (psi->hicDraw == NULL) {
|
|
DPF(("Unable to play stream!\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
if (psi->cbData > 0) {
|
|
ICSetState(psi->hicDraw, psi->lpData, psi->cbData);
|
|
}
|
|
|
|
psi->dwFlags |= STREAM_ENABLED;
|
|
////psi->dwFlags |= STREAM_OTHER;
|
|
npMCI->nOtherStreams++;
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api BOOL | CleanIndex | This function cleans up the index loaded by
|
|
* ReadIndex() it does the following when cleaning up the index:
|
|
*
|
|
* converts all offsets to be absolute
|
|
*
|
|
* converts "alpha" format index's into new format.
|
|
*
|
|
* computes the max buffer size needed to read this file.
|
|
*
|
|
***************************************************************************/
|
|
|
|
INLINE static BOOL NEAR CleanIndex(NPMCIGRAPHIC npMCI)
|
|
{
|
|
LONG lScan;
|
|
AVIINDEXENTRY FAR * px;
|
|
AVIINDEXENTRY FAR * pxRec=NULL;
|
|
DWORD lIndexAdjust;
|
|
|
|
Assert(npMCI->hpIndex != NULL);
|
|
|
|
px = (AVIINDEXENTRY FAR *)npMCI->hpIndex;
|
|
|
|
#ifdef ALPHAFILES
|
|
if (npMCI->dwFlags & MCIAVI_USINGALPHAFORMAT)
|
|
lIndexAdjust = 0;
|
|
else
|
|
#endif
|
|
if (// (avihdr.dwFlags & AVIF_MUSTUSEINDEX) ||
|
|
(px->dwChunkOffset < 100))
|
|
lIndexAdjust = npMCI->dwMovieListOffset;
|
|
else
|
|
lIndexAdjust = (npMCI->dwMovieListOffset + sizeof(DWORD)) -
|
|
px->dwChunkOffset;
|
|
|
|
//!!! only compute this for the video stream! (or interleaved...)
|
|
npMCI->dwSuggestedBufferSize = 0; // lets get this exactly right
|
|
|
|
DPF(("Adjusting index by %ld bytes....\n", lIndexAdjust));
|
|
|
|
/* Can we do anything to see if the index is valid? */
|
|
for (lScan = 0; lScan < (LONG)npMCI->macIndex;
|
|
lScan++, ++((AVIINDEXENTRY _huge *)px)) {
|
|
DWORD ckid;
|
|
|
|
//
|
|
// adjust the offset to be absolute
|
|
//
|
|
px->dwChunkOffset += lIndexAdjust;
|
|
|
|
// get ckid
|
|
ckid = px->ckid;
|
|
|
|
//
|
|
// make sure the buffer size is right, ignore audio chunks because
|
|
// they are either in a 'rec' or we will be reading them into
|
|
// internal buffers not the main buffer.365
|
|
//
|
|
if (((npMCI->dwFlags & MCIAVI_NOTINTERLEAVED) ||
|
|
ckid == listtypeAVIRECORD) &&
|
|
TWOCCFromFOURCC(ckid) != cktypeWAVEbytes) {
|
|
|
|
if (px->dwChunkLength + 8 > npMCI->dwSuggestedBufferSize)
|
|
npMCI->dwSuggestedBufferSize = px->dwChunkLength + 12;
|
|
}
|
|
|
|
#ifdef ALPHAFILES
|
|
//
|
|
// convert a "old" index to a new index
|
|
//
|
|
if (npMCI->dwFlags & MCIAVI_USINGALPHAFORMAT) {
|
|
switch(ckid) {
|
|
case ckidDIBbits:
|
|
px->dwFlags |= AVIIF_KEYFRAME;
|
|
px->ckid = MAKEAVICKID(cktypeDIBbits, 0);
|
|
break;
|
|
|
|
case ckidDIBcompressed:
|
|
px->ckid = MAKEAVICKID(cktypeDIBcompressed, 0);
|
|
break;
|
|
|
|
case ckidDIBhalfframe:
|
|
px->ckid = MAKEAVICKID(cktypeDIBhalf, 0);
|
|
break;
|
|
|
|
case ckidPALchange:
|
|
px->ckid = MAKEAVICKID(cktypePALchange, 0);
|
|
break;
|
|
|
|
case ckidWAVEbytes:
|
|
px->ckid = MAKEAVICKID(cktypeWAVEbytes, 1);
|
|
break;
|
|
|
|
case ckidWAVEsilence:
|
|
px->ckid = MAKEAVICKID(cktypeWAVEsilence, 1);
|
|
break;
|
|
|
|
case ckidAVIPADDING:
|
|
case ckidOLDPADDING:
|
|
px->ckid = ckidAVIPADDING;
|
|
break;
|
|
}
|
|
|
|
ckid = px->ckid;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// do special things with the video stream.
|
|
//
|
|
|
|
if (StreamFromFOURCC(ckid) == (UINT)npMCI->nVideoStream) {
|
|
|
|
//
|
|
// fix up bogus index's by adding any missing AVIIF_KEYFRAME
|
|
// bits. ie this only applies for RLE files.
|
|
//
|
|
if (TWOCCFromFOURCC(ckid) == cktypeDIBbits &&
|
|
VIDFMT(npMCI->nVideoStream)->biCompression <= BI_RLE8)
|
|
|
|
px->dwFlags |= AVIIF_KEYFRAME;
|
|
|
|
//
|
|
// for video streams, make sure the palette changes are marked
|
|
// as a no time chunk
|
|
//
|
|
if (TWOCCFromFOURCC(ckid) == cktypePALchange)
|
|
px->dwFlags |= AVIIF_NOTIME/*|AVIIF_PALCHANGE*/;
|
|
|
|
//
|
|
// make sure the 'REC ' list has the right flags.
|
|
//
|
|
if (pxRec) {
|
|
if ((px->dwFlags & AVIIF_KEYFRAME) !=
|
|
(pxRec->dwFlags & AVIIF_KEYFRAME)) {
|
|
|
|
// Record list does not have correct flags
|
|
|
|
pxRec->dwFlags &= ~AVIIF_KEYFRAME;
|
|
pxRec->dwFlags |= (px->dwFlags & AVIIF_KEYFRAME);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ckid == listtypeAVIRECORD) {
|
|
|
|
pxRec = px;
|
|
|
|
if (npMCI->dwFlags & MCIAVI_NOTINTERLEAVED) {
|
|
DPF(("Non interleaved file with a 'REC ' in it?\n"));
|
|
npMCI->wEarlyRecords = max(npMCI->wEarlyVideo, npMCI->wEarlyAudio);
|
|
|
|
if (npMCI->wEarlyRecords > 0) {
|
|
DPF(("Interlaved file with bad header\n"));
|
|
npMCI->dwFlags &= ~MCIAVI_NOTINTERLEAVED;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api BOOL | MakeFrameIndex | makes the frame index
|
|
*
|
|
* the frame index is a array of AVIFRAMEINDEX entries, one for each
|
|
* frame in the movie. using the frame index we can easily find
|
|
* a given frame, along with it's keyframe and palette.
|
|
*
|
|
***************************************************************************/
|
|
|
|
static BOOL NEAR MakeFrameIndex(NPMCIGRAPHIC npMCI)
|
|
{
|
|
LONG nFrames;
|
|
LONG iFrameStart;
|
|
LONG iFrame;
|
|
LONG iKeyFrame;
|
|
LONG nKeyFrames;
|
|
LONG iScan;
|
|
LONG iNewIndex;
|
|
LONG iPalette;
|
|
BOOL fInterleaved;
|
|
DWORD ckid;
|
|
STREAMINFO *psi;
|
|
|
|
AVIINDEXENTRY _huge * pNewIndex;
|
|
AVIINDEXENTRY _huge * pIndexEntry;
|
|
AVIFRAMEINDEX _huge * pFrameIndex;
|
|
|
|
if (npMCI->nVideoStreams == 0)
|
|
return TRUE;
|
|
|
|
if (npMCI->hpFrameIndex != NULL)
|
|
return TRUE;
|
|
|
|
psi = npMCI->psiVideo;
|
|
Assert(psi != NULL);
|
|
|
|
fInterleaved = !(npMCI->dwFlags & MCIAVI_NOTINTERLEAVED);
|
|
|
|
if (fInterleaved &&
|
|
muldiv32(npMCI->dwRate, 1000, npMCI->dwScale) !=
|
|
muldiv32(psi->sh.dwRate, 1000, psi->sh.dwScale)) {
|
|
//
|
|
// master video stream should match the movie rate!
|
|
//
|
|
AssertSz(FALSE, "Video stream differnet rate than movie");
|
|
npMCI->dwRate = psi->sh.dwRate;
|
|
npMCI->dwScale = psi->sh.dwScale;
|
|
}
|
|
|
|
if (fInterleaved)
|
|
iFrameStart = -(LONG)npMCI->wEarlyRecords;
|
|
else
|
|
iFrameStart = -(LONG)npMCI->wEarlyVideo;
|
|
|
|
nFrames = npMCI->lFrames - iFrameStart;
|
|
|
|
// hpFrameIndex is modified to point partway into the block.
|
|
// therefore we need to remember the handle of the block so we
|
|
// can free it correctly (esp on NT)
|
|
if ( (npMCI->hgFrameIndex = GlobalAlloc(GMEM_SHARE|GHND, (DWORD)(nFrames+1) * sizeof(AVIFRAMEINDEX)))
|
|
&& (npMCI->hpFrameIndex = GlobalLock(npMCI->hgFrameIndex))) {
|
|
} else {
|
|
// We failed to allocate, or we failed to lock down. Clean up
|
|
// and return an error.
|
|
if (npMCI->hgFrameIndex) {
|
|
GlobalFree(npMCI->hgFrameIndex);
|
|
npMCI->hgFrameIndex = 0;
|
|
}
|
|
DPF(("Couldn't allocate memory for frame index!\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// do this so we can just index the array with the frame number
|
|
// (positive or neg)
|
|
//
|
|
npMCI->hpFrameIndex += (-iFrameStart);
|
|
|
|
pFrameIndex = npMCI->hpFrameIndex;
|
|
|
|
iFrame = iFrameStart;
|
|
iKeyFrame = -(LONG)npMCI->wEarlyVideo; // iFrameStart;
|
|
iNewIndex = 0;
|
|
iPalette = -1; // first palette
|
|
nKeyFrames= 0;
|
|
|
|
#ifdef USEAVIFILE
|
|
if (npMCI->pf) {
|
|
PAVISTREAM ps = SI(npMCI->nVideoStream)->ps;
|
|
|
|
for (iFrame = 0; iFrame < npMCI->lFrames; iFrame++) {
|
|
|
|
LONG iKey;
|
|
|
|
iKey = AVIStreamFindSample(ps,iFrame,FIND_PREV|FIND_KEY);
|
|
iPalette = AVIStreamFindSample(ps,iFrame,FIND_PREV|FIND_FORMAT);
|
|
|
|
if (iKey != -1)
|
|
iKeyFrame = iKey;
|
|
|
|
if (iPalette == -1)
|
|
iPalette = 0;
|
|
|
|
pFrameIndex[iFrame].iPrevKey = (UINT)(iFrame - iKeyFrame);
|
|
pFrameIndex[iFrame].iNextKey = 0;
|
|
pFrameIndex[iFrame].iPalette = (WORD)iPalette;
|
|
pFrameIndex[iFrame].dwOffset = 0;
|
|
pFrameIndex[iFrame].dwLength = 0;
|
|
|
|
Assert(iPalette <= 0xFFFF);
|
|
|
|
if (iFrame - iKeyFrame > 0xFFFF) {
|
|
//!!! we need to set a flag!
|
|
//!!! we need to throw out the index!
|
|
AssertSz(FALSE, "File has too few key frames");
|
|
pFrameIndex[iFrame].iPrevKey = 0;
|
|
}
|
|
}
|
|
|
|
goto ack;
|
|
}
|
|
#endif
|
|
|
|
Assert(npMCI->hpIndex != NULL);
|
|
Assert(npMCI->macIndex != 0L);
|
|
pNewIndex = npMCI->hpIndex;
|
|
pIndexEntry = npMCI->hpIndex;
|
|
|
|
for (iScan = 0; iScan < (LONG)npMCI->macIndex; iScan++, pIndexEntry++) {
|
|
|
|
ckid = pIndexEntry->ckid;
|
|
|
|
//
|
|
// check for palette changes.
|
|
//
|
|
if (StreamFromFOURCC(ckid) == (UINT)npMCI->nVideoStream &&
|
|
TWOCCFromFOURCC(ckid) == cktypePALchange) {
|
|
|
|
iPalette = iNewIndex;
|
|
|
|
pNewIndex[iNewIndex++] = *pIndexEntry;
|
|
|
|
if (fInterleaved)
|
|
pFrameIndex[iFrame-1].iPalette = (WORD)iPalette;
|
|
}
|
|
|
|
//
|
|
// remove the video stream from the master index
|
|
//
|
|
if ((ckid != listtypeAVIRECORD) &&
|
|
(StreamFromFOURCC(ckid) != (UINT)npMCI->nVideoStream)) {
|
|
pNewIndex[iNewIndex++] = *pIndexEntry;
|
|
}
|
|
|
|
//
|
|
// in interleaved files a "frame" happens every list record
|
|
//
|
|
// in non-interleaved files a "frame" happens every piece of
|
|
// data in the video stream (except no time chunks)
|
|
//
|
|
if (fInterleaved) {
|
|
|
|
if (ckid != listtypeAVIRECORD)
|
|
continue;
|
|
|
|
} else {
|
|
|
|
if ((StreamFromFOURCC(ckid) != (UINT)npMCI->nVideoStream) ||
|
|
(pIndexEntry->dwFlags & AVIIF_NOTIME))
|
|
|
|
continue;
|
|
}
|
|
|
|
AssertSz(iFrame < npMCI->lFrames,"Too many frames in index!");
|
|
|
|
if (iFrame >= npMCI->lFrames) {
|
|
break;
|
|
}
|
|
|
|
if (pIndexEntry->dwFlags & AVIIF_KEYFRAME) {
|
|
iKeyFrame = iFrame;
|
|
nKeyFrames++;
|
|
}
|
|
|
|
pFrameIndex[iFrame].iPrevKey = (UINT)(iFrame - iKeyFrame);
|
|
pFrameIndex[iFrame].iNextKey = 0;
|
|
pFrameIndex[iFrame].iPalette = (WORD)iPalette;
|
|
pFrameIndex[iFrame].dwOffset = pIndexEntry->dwChunkOffset;
|
|
pFrameIndex[iFrame].dwLength = pIndexEntry->dwChunkLength;
|
|
|
|
if (fInterleaved)
|
|
pFrameIndex[iFrame].dwOffset += 3 * sizeof(DWORD);
|
|
|
|
Assert(iPalette <= 0xFFFF);
|
|
|
|
if (iFrame - iKeyFrame > 0xFFFF) {
|
|
//!!! we need to set a flag!
|
|
//!!! we need to throw out the index!
|
|
AssertSz(FALSE, "File has too few key frames");
|
|
pFrameIndex[iFrame].iPrevKey = 0;
|
|
}
|
|
|
|
iFrame++;
|
|
}
|
|
ack:
|
|
//
|
|
// iFrame better equal npMCI->lFrames
|
|
//
|
|
Assert(iFrame == npMCI->lFrames);
|
|
|
|
if (iFrame < npMCI->lFrames)
|
|
npMCI->lFrames = iFrame;
|
|
|
|
//
|
|
// make a "dummy" last frame
|
|
//
|
|
pFrameIndex[iFrame].iPrevKey = (UINT)(iFrame - iKeyFrame);
|
|
pFrameIndex[iFrame].iNextKey = 0;
|
|
pFrameIndex[iFrame].iPalette = (WORD)iPalette;
|
|
pFrameIndex[iFrame].dwOffset = 0;
|
|
pFrameIndex[iFrame].dwLength = 0;
|
|
|
|
//
|
|
// compute the key frames every value
|
|
//
|
|
if (nKeyFrames) {
|
|
|
|
if (nKeyFrames > 1)
|
|
npMCI->dwKeyFrameInfo = (DWORD)((nFrames + nKeyFrames/2)/nKeyFrames);
|
|
else
|
|
npMCI->dwKeyFrameInfo = 0;
|
|
}
|
|
|
|
//
|
|
// now go through the index, and fix all the iNextKey fields
|
|
//
|
|
pFrameIndex = npMCI->hpFrameIndex;
|
|
////iKeyFrame = npMCI->lFrames; //!!! what should this be set to? zero?
|
|
|
|
for (iFrame = npMCI->lFrames; iFrame>=iFrameStart; iFrame--)
|
|
{
|
|
if (pFrameIndex[iFrame].iPrevKey == 0)
|
|
iKeyFrame = iFrame;
|
|
|
|
if (iKeyFrame >= iFrame)
|
|
pFrameIndex[iFrame].iNextKey = (UINT)(iKeyFrame - iFrame);
|
|
else
|
|
pFrameIndex[iFrame].iNextKey = 0xFFFF; // way far away
|
|
|
|
if (iKeyFrame - iFrame > 0xFFFF) {
|
|
//!!! we need to set a flag!
|
|
//!!! we need to throw out the index!
|
|
AssertSz(FALSE, "File has too few key frames");
|
|
pFrameIndex[iFrame].iNextKey = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// we dont need the index, if we are using AVIFile or
|
|
// we have a interleaved file. when the file is interleaved
|
|
// we never do random access (except for palette changes)
|
|
//
|
|
// !!!this is not true, we need the index iff we have a audio only
|
|
// file or we play a interleaved file real slow.
|
|
//
|
|
if (npMCI->pf /* ||
|
|
(fInterleaved && !(npMCI->dwFlags & MCIAVI_ANIMATEPALETTE))*/ ) {
|
|
DOUT("The Master index must go!\n");
|
|
iNewIndex = 0;
|
|
}
|
|
|
|
//
|
|
// now re-alloc the master index down to size.
|
|
//
|
|
// !!! do we even need the master index anymore, for interleaved files?
|
|
//
|
|
DPF(("Master index was %ld entries now %ld\n",npMCI->macIndex, iNewIndex));
|
|
|
|
npMCI->macIndex = iNewIndex;
|
|
|
|
if (iNewIndex > 0) {
|
|
npMCI->hpIndex = (AVIINDEXENTRY _huge *)
|
|
GlobalReAllocPtr(npMCI->hpIndex,
|
|
(LONG)iNewIndex * sizeof(AVIINDEXENTRY),
|
|
GMEM_MOVEABLE | GMEM_SHARE);
|
|
|
|
Assert(npMCI->hpIndex != NULL);
|
|
}
|
|
else {
|
|
if (npMCI->hpIndex)
|
|
GlobalFreePtr(npMCI->hpIndex);
|
|
npMCI->hpIndex = NULL;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api BOOL | ReadIndex | Read the index into npMCI->hpIndex. Should
|
|
* only be called if the HASINDEX flag is set.
|
|
*
|
|
* @parm NPMCIGRAPHIC | npMCI | Pointer to instance data
|
|
*
|
|
* @rdesc TRUE means no errors, false means unable to read index.
|
|
*
|
|
***************************************************************************/
|
|
BOOL FAR PASCAL ReadIndex(NPMCIGRAPHIC npMCI)
|
|
{
|
|
MMCKINFO ck;
|
|
DWORD dwOldPos;
|
|
|
|
if (npMCI->hpIndex || npMCI->hpFrameIndex)
|
|
return TRUE;
|
|
|
|
if (!(npMCI->dwFlags & MCIAVI_HASINDEX))
|
|
return FALSE;
|
|
|
|
if (npMCI->pf) {
|
|
MakeFrameIndex(npMCI);
|
|
return TRUE;
|
|
}
|
|
|
|
#if 0
|
|
if (GetCurrentTask() != npMCI->hTask) {
|
|
|
|
/* this function is called (from GraphicStatus) when
|
|
* possibly playing - so we have to suspend play while we read
|
|
* the index.
|
|
*/
|
|
// won't work with the current multi-thread design
|
|
// TEMPORARYSTATE ts;
|
|
|
|
// if (StopTemporarily(npMCI, &ts) == 0) {
|
|
// mciaviTaskMessage(npMCI, TASKREADINDEX);
|
|
// RestartAgain(npMCI, &ts);
|
|
// return (npMCI->hpIndex != NULL);
|
|
// }
|
|
return(FALSE);
|
|
}
|
|
#else
|
|
if (GetCurrentTask() != npMCI->hTask)
|
|
return FALSE;
|
|
#endif
|
|
|
|
dwOldPos = mmioSeek(npMCI->hmmio, 0, SEEK_CUR);
|
|
|
|
DPF(("Reading index: starting from %lx\n", npMCI->dwBigListEnd));
|
|
|
|
if (mmioSeek(npMCI->hmmio, npMCI->dwBigListEnd, SEEK_SET) == -1) {
|
|
IndexReadError:
|
|
DPF(("Error reading index!\n"));
|
|
npMCI->dwFlags &= ~(MCIAVI_HASINDEX);
|
|
mmioSeek(npMCI->hmmio, dwOldPos, SEEK_SET);
|
|
return FALSE;
|
|
}
|
|
|
|
ck.ckid = ckidAVINEWINDEX;
|
|
if (mmioDescend(npMCI->hmmio, &ck, NULL, MMIO_FINDCHUNK) != 0) {
|
|
goto IndexReadError;
|
|
}
|
|
|
|
/* A zero-size index isn't much good. */
|
|
if (ck.cksize == 0)
|
|
goto IndexReadError;
|
|
|
|
npMCI->macIndex = ck.cksize / sizeof(AVIINDEXENTRY);
|
|
npMCI->hpIndex = (AVIINDEXENTRY _huge *)
|
|
GlobalAllocPtr(GMEM_SHARE | GMEM_MOVEABLE, ck.cksize);
|
|
|
|
if (!npMCI->hpIndex) {
|
|
DPF(("Insufficient memory to read index.\n"));
|
|
goto IndexReadError;
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
Assert(OFFSETOF(npMCI->hpIndex) == 0);
|
|
#endif
|
|
|
|
if (mmioRead(npMCI->hmmio, (HPSTR) npMCI->hpIndex, ck.cksize) != (LONG) ck.cksize) {
|
|
Assert(0);
|
|
goto IndexReadError;
|
|
}
|
|
|
|
CleanIndex(npMCI);
|
|
MakeFrameIndex(npMCI);
|
|
|
|
////should we do this for audio? remove video data?
|
|
////MakeStreamIndex(npMCI, ???);
|
|
|
|
mmioSeek(npMCI->hmmio, dwOldPos, SEEK_SET);
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api BOOL | IsRectBogus | 'nuf said
|
|
*
|
|
***************************************************************************/
|
|
|
|
INLINE static BOOL NEAR PASCAL IsRectBogus(RECT* prc)
|
|
{
|
|
if (prc->right - prc->left <= 0 ||
|
|
prc->bottom - prc->top <= 0 ||
|
|
prc->bottom <= 0 ||
|
|
prc->right <= 0)
|
|
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
#ifndef _WIN32 // _WIN32 has one!
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api LONG | atol | local version of atol
|
|
*
|
|
***************************************************************************/
|
|
|
|
static LONG NEAR PASCAL atol(char *sz)
|
|
{
|
|
LONG l = 0;
|
|
|
|
while (*sz && *sz >= '0' && *sz <= '9')
|
|
l = l*10 + *sz++ - '0';
|
|
|
|
return l;
|
|
}
|
|
#endif
|
|
|
|
#ifndef _WIN32
|
|
|
|
/*--------------------------------------------------------------------------
|
|
*
|
|
* IsCDROMDrive() -
|
|
*
|
|
* Purpose: Return non-zero if a RAM drive
|
|
*
|
|
* wDrive drive index (0=A, 1=B, ...)
|
|
*
|
|
* return TRUE/FALSE
|
|
*-------------------------------------------------------------------------*/
|
|
|
|
#pragma optimize("", off)
|
|
static BOOL NEAR PASCAL IsCDROMDrive(UINT wDrive)
|
|
{
|
|
BOOL f;
|
|
|
|
_asm {
|
|
mov ax, 1500h /* first test for presence of MSCDEX */
|
|
xor bx, bx
|
|
int 2fh
|
|
mov ax, bx /* MSCDEX is not there if bx is still zero */
|
|
or ax, ax /* ...so return FALSE from this function */
|
|
jz no_mscdex
|
|
mov ax, 150bh /* MSCDEX driver check API */
|
|
mov cx, wDrive /* ...cx is drive index */
|
|
int 2fh
|
|
no_mscdex:
|
|
mov f,ax
|
|
}
|
|
return f;
|
|
}
|
|
#pragma optimize("", on)
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api BOOL | IsNetFile | is the passed file on a network drive?
|
|
*
|
|
***************************************************************************/
|
|
|
|
static BOOL NEAR PASCAL IsNetFile(LPTSTR szFile)
|
|
{
|
|
OFSTRUCT of;
|
|
|
|
if (OpenFile(szFile, &of, OF_PARSE) == -1)
|
|
return FALSE;
|
|
|
|
AnsiUpper(of.szPathName);
|
|
|
|
if (of.szPathName[0] == '\\' && of.szPathName[1] == '\\')
|
|
return TRUE;
|
|
|
|
if (of.szPathName[0] == '/' && of.szPathName[1] == '/')
|
|
return TRUE;
|
|
|
|
if (of.szPathName[1] == ':' &&
|
|
GetDriveType(of.szPathName[0] - 'A') == DRIVE_REMOTE &&
|
|
!IsCDROMDrive(of.szPathName[0] - 'A'))
|
|
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api BOOL | IsCDROMFile | is the passed file on a CD-ROM drive?
|
|
*
|
|
***************************************************************************/
|
|
|
|
static BOOL NEAR PASCAL IsCDROMFile(LPTSTR szFile)
|
|
{
|
|
OFSTRUCT of;
|
|
|
|
if (OpenFile(szFile, &of, OF_PARSE) == -1)
|
|
return FALSE;
|
|
|
|
AnsiUpper(of.szPathName);
|
|
|
|
if (of.szPathName[0] == '\\' && of.szPathName[1] == '\\')
|
|
return FALSE;
|
|
|
|
if (of.szPathName[0] == '/' && of.szPathName[1] == '/')
|
|
return FALSE;
|
|
|
|
if (of.szPathName[1] == ':' &&
|
|
GetDriveType(of.szPathName[0] - 'A') == DRIVE_REMOTE &&
|
|
IsCDROMDrive(of.szPathName[0] - 'A'))
|
|
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL MCIAVI
|
|
*
|
|
* @api UINT | GetFileDriveType | return drive type given a file
|
|
*
|
|
* DRIVE_CDROM
|
|
* DRIVE_REMOTE
|
|
* DRIVE_FIXED
|
|
* DRIVE_REMOVABLE
|
|
*
|
|
***************************************************************************/
|
|
|
|
static UINT NEAR PASCAL GetFileDriveType(LPSTR szPath)
|
|
{
|
|
if (IsCDROMFile(szPath))
|
|
return DRIVE_CDROM;
|
|
|
|
if (IsNetFile(szPath))
|
|
return DRIVE_REMOTE;
|
|
|
|
if (szPath[1] == ':')
|
|
return GetDriveType(szPath[0] - 'A');
|
|
|
|
return 0;
|
|
}
|
|
#else // _WIN32
|
|
/*
|
|
* We cannot pass the call directly to GetDriveType as we have a full
|
|
* pathname - and that gets rejected by GetFileDriveType. Using a
|
|
* static variable is ok - the open call is serialised. The assumption
|
|
* is that this routine is ONLY called with complete path information.
|
|
*/
|
|
|
|
TCHAR DriveType[4] = TEXT(".:\\");
|
|
static UINT NEAR PASCAL GetFileDriveType(LPTSTR szPath)
|
|
{
|
|
|
|
Assert(szPath != NULL);
|
|
if (*szPath == TEXT('\\')) {
|
|
Assert(szPath[1] == TEXT('\\'));
|
|
return(DRIVE_REMOTE);
|
|
}
|
|
|
|
if (szPath[1] == TEXT(':')) {
|
|
DriveType[0] = szPath[0];
|
|
return GetDriveType(DriveType);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|