windows-nt/Source/XPSP1/NT/multimedia/media/avi/mciavi32/avidraw.c
2020-09-26 16:20:57 +08:00

1334 lines
36 KiB
C

/******************************************************************************
Copyright (C) Microsoft Corporation 1991-1995. All rights reserved.
Title: avidraw.c - Functions that actually draw video for AVI.
*****************************************************************************/
#include "graphic.h"
//
// if the average key frame spacing is greater than this value, always
// force a buffer.
//
#define KEYFRAME_PANIC_SPACE 2500
#define YIELDATFUNNYTIMES
#define ALIGNULONG(i) ((i+3)&(~3)) /* ULONG aligned ! */
#define WIDTHBYTES(i) ((unsigned)((i+31)&(~31))/8) /* ULONG aligned ! */
#define DIBWIDTHBYTES(bi) (DWORD)WIDTHBYTES((int)(bi).biWidth * (int)(bi).biBitCount)
#ifdef _WIN32
#define LockCurrentTask(x) (x)
#else
extern FAR PASCAL LockCurrentTask(BOOL);
#endif
BOOL NEAR PASCAL DrawBits(NPMCIGRAPHIC npMCI, DWORD ckid, DWORD cksize, BOOL fHurryUp);
void NEAR PASCAL UpdateDisplayDibPalette(NPMCIGRAPHIC npMCI);
BOOL NEAR PASCAL ProcessPaletteChange(NPMCIGRAPHIC npMCI, DWORD cksize)
{
UINT wStartIndex;
UINT wNumEntries;
UINT w;
LPPALETTEENTRY ppe;
npMCI->dwFlags |= MCIAVI_PALCHANGED;
DPF2(("Setting PALCHANGED\n"));
while (cksize > 4) {
wStartIndex = GET_BYTE();
wNumEntries = GET_BYTE();
/* Skip filler word */
GET_WORD();
/* Zero is used as a shorthand for 256 */
if (wNumEntries == 0)
wNumEntries = 256;
ppe = (LPVOID)npMCI->lp;
for (w=0; w<wNumEntries; w++)
{
npMCI->argb[wStartIndex+w].rgbRed = ppe[w].peRed;
npMCI->argb[wStartIndex+w].rgbGreen = ppe[w].peGreen;
npMCI->argb[wStartIndex+w].rgbBlue = ppe[w].peBlue;
}
SKIP_BYTES(wNumEntries * sizeof(PALETTEENTRY));
cksize -= 4 + wNumEntries * sizeof(PALETTEENTRY);
}
if (npMCI->pbiFormat->biBitCount == 8) {
hmemcpy((LPBYTE) npMCI->pbiFormat + npMCI->pbiFormat->biSize,
(LPBYTE) npMCI->argb,
sizeof(RGBQUAD) * npMCI->pbiFormat->biClrUsed);
}
#ifdef DEBUG
/* Make sure we've used up the entire chunk... */
if (cksize != 0) {
DPF(("Problem with palc chunk\n"));
}
#endif
return TRUE;
}
/* Display the video from the current record.
*/
BOOL NEAR PASCAL DisplayVideoFrame(NPMCIGRAPHIC npMCI, BOOL fHurryUp)
{
DWORD ckid;
DWORD cksize;
BOOL fRet;
int stream;
DWORD dwRet;
LONG len;
DWORD dwDrawStart;
LPVOID lpSave;
LPVOID lpChunk;
/* If we're allowed to skip frames, apply some relatively
** bogus heuristics to decide if we should do it, and
** pass the appropriate flag on to the driver.
*/
if ((npMCI->lCurrentFrame & 0x0f) == 0) {
fHurryUp = FALSE;
}
/* Even if SKIPFRAMES is off, count how many frames we _would_ have
** skipped if we could.
*/
if (fHurryUp)
++npMCI->dwSkippedFrames;
if (!(npMCI->dwOptionFlags & MCIAVIO_SKIPFRAMES))
fHurryUp = FALSE;
/* Keep track of what we've drawn. */
npMCI->lFrameDrawn = npMCI->lCurrentFrame;
len = (LONG)npMCI->dwThisRecordSize;
lpSave = npMCI->lp;
/* If it's interleaved, adjust for the next record header.... */
// !!! Only if not last frame?
if (npMCI->wPlaybackAlg == MCIAVI_ALG_INTERLEAVED)
len -= 3 * sizeof(DWORD);
while (len >= 2 * sizeof(DWORD)) {
/* Look at the next chunk */
ckid = GET_DWORD();
cksize = GET_DWORD();
DPF3(("'Chunk %.4s': %lu bytes\n", (LPSTR) &ckid, cksize));
if ((LONG) cksize > len) {
AssertSz(FALSE, "Chunk obviously too big!");
break;
}
len -= ((cksize+1)&~1) + 8;
if (len < -1) {
AssertSz(FALSE, "Chunk overflowed what was read in!");
break;
}
lpChunk = npMCI->lp;
stream = StreamFromFOURCC(ckid);
if (stream == npMCI->nVideoStream) {
if ((npMCI->lCurrentFrame < npMCI->lVideoStart) &&
!(npMCI->dwFlags & MCIAVI_REVERSE))
goto skip;
switch(TWOCCFromFOURCC(ckid)) {
case cktypePALchange:
ProcessPaletteChange(npMCI, cksize);
npMCI->lLastPaletteChange = npMCI->lCurrentFrame;
break;
default:
/* Some other chunk... */
if (!fHurryUp && ckid) {
dwDrawStart = timeGetTime();
}
//!!! we need to handle half frames!!!
fRet = DrawBits(npMCI, ckid, cksize, fHurryUp);
if (!fRet)
return FALSE;
if (npMCI->dwBufferedVideo)
npMCI->dwLastDrawTime = 0;
else
if (!fHurryUp && ckid) {
npMCI->dwLastDrawTime = timeGetTime() - dwDrawStart;
}
break;
}
} else if (stream >= 0 && stream < npMCI->streams &&
SI(stream)->hicDraw) {
dwRet = ICDraw(SI(stream)->hicDraw, (fHurryUp ? ICDRAW_HURRYUP : 0L),
SI(stream)->lpFormat,
(ckid == 0) ? 0L : npMCI->lp, cksize, npMCI->lCurrentFrame);
// !!! Error check?
}
skip:
/* If not interleaved, we're done. */
if (npMCI->wPlaybackAlg != MCIAVI_ALG_INTERLEAVED)
return TRUE;
/* Skip to the next chunk */
npMCI->lp = (HPSTR) lpChunk + ((cksize+1)&~1);
}
npMCI->lp = lpSave;
return TRUE;
}
//
// mark all streams in the passed RECT as dirty
//
void NEAR PASCAL StreamInvalidate(NPMCIGRAPHIC npMCI, LPRECT prc)
{
int i;
int n;
STREAMINFO *psi;
RECT rc;
if (prc)
DPF2(("StreamInvalidate: [%d, %d, %d, %d]\n", *prc));
else
DPF2(("StreamInvalidate: NULL\n", *prc));
for (n=i=0; i<npMCI->streams; i++) {
psi = SI(i);
// we always update any visible error streams
if (!(psi->dwFlags & STREAM_ERROR) &&
!(psi->dwFlags & STREAM_ENABLED))
continue;
if (IsRectEmpty(&psi->rcDest))
continue;
if (prc && !IntersectRect(&rc, prc, &psi->rcDest))
continue;
n++;
psi->dwFlags |= STREAM_NEEDUPDATE;
}
//
// !!!is this right? or should we always dirty the movie?
//
if (n > 0)
npMCI->dwFlags |= MCIAVI_NEEDUPDATE;
else
npMCI->dwFlags &= ~MCIAVI_NEEDUPDATE;
}
//
// update all dirty streams
//
// if fPaint is set paint the area even if the stream handler does not
//
BOOL NEAR PASCAL DoStreamUpdate(NPMCIGRAPHIC npMCI, BOOL fPaint)
{
int i;
BOOL f=TRUE;
STREAMINFO *psi;
// This routine is called on both the winproc and worker threads
EnterHDCCrit(npMCI); // Protect hdc use/changes
Assert(npMCI->hdc);
SaveDC(npMCI->hdc);
for (i=0; i<npMCI->streams; i++) {
psi = SI(i);
//
// this stream is clean, dont paint it.
//
if (!(psi->dwFlags & (STREAM_DIRTY|STREAM_NEEDUPDATE))) {
ExcludeClipRect(npMCI->hdc,
DEST(i).left,DEST(i).top,DEST(i).right,DEST(i).bottom);
continue;
}
psi->dwFlags &= ~STREAM_NEEDUPDATE;
psi->dwFlags &= ~STREAM_DIRTY;
if (psi->dwFlags & STREAM_ERROR) {
UINT u, cb;
TCHAR ach[80];
TCHAR szMessage[80];
HBRUSH hbr = CreateHatchBrush(HS_BDIAGONAL, RGB(128,0,0));
if (psi->sh.fccType == streamtypeVIDEO)
LoadString(ghModule, MCIAVI_CANT_DRAW_VIDEO, ach, NUMELMS(ach));
else
LoadString(ghModule, MCIAVI_CANT_DRAW_STREAM, ach, NUMELMS(ach));
FillRect(npMCI->hdc, &DEST(i), hbr);
u = SetBkMode(npMCI->hdc, OPAQUE);
cb = wsprintf(szMessage, ach,
(LPVOID)&psi->sh.fccType,
(LPVOID)&psi->sh.fccHandler);
DrawText(npMCI->hdc, szMessage, cb, &DEST(i),
DT_NOPREFIX|DT_WORDBREAK|DT_VCENTER|DT_CENTER);
SetBkMode(npMCI->hdc, u);
DeleteObject(hbr);
FrameRect(npMCI->hdc, &DEST(i), GetStockObject(BLACK_BRUSH));
}
else if (!(psi->dwFlags & STREAM_ENABLED)) {
FillRect(npMCI->hdc, &DEST(i), GetStockObject(DKGRAY_BRUSH));
}
else if (psi->sh.fccType == streamtypeVIDEO &&
!(npMCI->dwFlags & MCIAVI_SHOWVIDEO)) {
continue; // we will paint black here.
}
else if (npMCI->nVideoStreams > 0 && i == npMCI->nVideoStream) {
BOOL fDraw;
try {
fDraw = DrawBits(npMCI, 0L, 0L, FALSE);
} except (EXCEPTION_EXECUTE_HANDLER) {
fDraw = FALSE;
}
if (!fDraw) {
psi->dwFlags |= STREAM_NEEDUPDATE;
f = FALSE;
if (fPaint) // will paint back if told to.
continue;
}
}
else if (psi->hicDraw == NULL) {
FillRect(npMCI->hdc, &DEST(i), GetStockObject(DKGRAY_BRUSH));
}
else if (ICDraw(psi->hicDraw,ICDRAW_UPDATE,psi->lpFormat,NULL,0,0) != 0) {
psi->dwFlags |= STREAM_NEEDUPDATE;
f = FALSE;
// should other streams work like this?
if (fPaint) // will paint back if told to.
continue;
}
//
// we painted so clean this area
//
ExcludeClipRect(npMCI->hdc,
DEST(i).left,DEST(i).top,DEST(i).right,DEST(i).bottom);
}
// now paint black every where else
FillRect(npMCI->hdc,&npMCI->rcDest,GetStockObject(BLACK_BRUSH));
RestoreDC(npMCI->hdc, -1);
LeaveHDCCrit(npMCI);
//
// do we still still need a update?
//
if (f) {
npMCI->dwFlags &= ~MCIAVI_NEEDUPDATE;
}
else {
DPF2(("StreamUpdate: update failed\n"));
npMCI->dwFlags |= MCIAVI_NEEDUPDATE;
}
return f;
}
#ifdef _WIN32
#define DWORG POINT
#define GETDCORG(hdc, dwOrg) GetDCOrgEx(hdc, &dwOrg)
#else
#define DWORG DWORD
#define GETDCORG(hdc, dwOrg) dwOrg = GetDCOrg(hdc)
#endif
#ifdef DAYTONA
#define AlignPlaybackWindow(npMCI)
#else
STATICFN void NEAR PASCAL AlignPlaybackWindow(NPMCIGRAPHIC npMCI)
{
DWORG dw;
int x,y;
HWND hwnd; // the window we will move.
RECT rc;
// if (npMCI->hicDraw != npMCI->hicDrawInternal)
// return; !!! only align if using the default draw guy?
#pragma message("**** move this into the draw handler and/or DrawDib")
#pragma message("**** we need to query the alignment from the codec????")
#define X_ALIGN 4
#define Y_ALIGN 4
// the MCIAVI_RELEASEDC flags means the DC came from a GetDC(npMCI->hwnd)
if (!(npMCI->dwFlags & MCIAVI_RELEASEDC)) {
DPF0(("Align: MCIAVI_RELEASEDC\n"));
return;
}
//
// dont align if the dest rect is not at 0,0
//
if (npMCI->rcMovie.left != 0 || npMCI->rcMovie.top != 0) {
DPF0(("Align: not at 0,0\n"));
return;
}
GETDCORG(npMCI->hdc, dw);
#ifdef _WIN32
x = dw.x + npMCI->rcMovie.left;
y = dw.y + npMCI->rcMovie.top;
#else
x = LOWORD(dw) + npMCI->rcMovie.left;
y = HIWORD(dw) + npMCI->rcMovie.top;
#endif
if ((x & (X_ALIGN-1)) || (y & (Y_ALIGN-1)))
{
DPF0(("*** warning movie is not aligned! (%d,%d)***\n",x,y));
//
// find the first moveable window walking up the tree.
//
for (hwnd = npMCI->hwndPlayback; hwnd; hwnd = GetParent(hwnd))
{
LONG l = GetWindowLong(hwnd, GWL_STYLE);
// this window is toplevel stop
if (!(l & WS_CHILD))
break;
// this window is sizeable (should be movable too)
if (l & WS_THICKFRAME)
break;
// this window has a caption (is moveable)
if ((l & WS_CAPTION) == WS_CAPTION)
break;
}
//
// dont move the window if it does not want to be moved.
//
if (IsWindowVisible(hwnd) &&
!IsZoomed(hwnd) &&
!IsIconic(hwnd) &&
IsWindowEnabled(hwnd))
{
GetClientRect(hwnd, &rc);
ClientToScreen(hwnd, (LPPOINT)&rc);
//
// if the movie is not in the upper corner of the window
// don't align
//
if (x < rc.left || x-rc.left > 16 ||
y < rc.top || y-rc.top > 16)
return;
GetWindowRect(hwnd, &rc);
OffsetRect(&rc, -(x & (X_ALIGN-1)), -(y & (Y_ALIGN-1)));
if (GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD)
ScreenToClient(GetParent(hwnd), (LPPOINT)&rc);
// dont move window off the screen.
if (rc.left < 0 || rc.top < 0) {
DPF0(("Align: not off the screen\n"));
return;
}
DPF0(("*** moving window to [%d,%d,%d,%d]\n",rc));
// We must relinquish the critical section before moving the
// window otherwise the WinProc thread will not run
LeaveWinCrit(npMCI);
SetWindowPos(hwnd,NULL,rc.left,rc.top,0,0,
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);
EnterWinCrit(npMCI);
DPF0(("Align: window moved\n"));
#ifdef _WIN32
// with fulldrag the window may move again between setting the
// position and checking here that it is aligned. Do NOT assert...
return;
#endif
#ifdef DEBUG
GETDCORG(npMCI->hdc, dw);
#ifdef _WIN32
x = dw.x + npMCI->rcMovie.left;
y = dw.y + npMCI->rcMovie.top;
#else
x = LOWORD(dw) + npMCI->rcMovie.left;
y = HIWORD(dw) + npMCI->rcMovie.top;
#endif
Assert(!(x & (X_ALIGN-1)) && !(y & (Y_ALIGN-1)));
#endif
}
} else {
DPF0(("Aligning playback window - no movement\n"));
}
}
#endif
UINT NEAR PASCAL PrepareDC(NPMCIGRAPHIC npMCI)
{
UINT u;
int i;
STREAMINFO *psi;
HDCCritCheckIn(npMCI);
DPF2(("*** PrepareDC(%04X)\n",npMCI->hdc));
// If we simply
// Assert(npMCI->hdc != NULL);
// and the assertion fails a message box appears. Message boxes allow
// processing to continue. In all likelihood the avi will need repainting, and
// a WM_PAINT (or a palette change) will cause GraphicWndProc to wake up while the
// assertion is being displayed. This will only add to the confusion.
if (npMCI->hdc == NULL) {
DPF0(("** NULL hdc from PrepareDC **\n"));
return 0;
}
if (!(npMCI->dwFlags & MCIAVI_FULLSCREEN) &&
!(npMCI->dwFlags & MCIAVI_SEEKING) &&
!(npMCI->dwFlags & MCIAVI_UPDATING) &&
(npMCI->dwFlags & MCIAVI_SHOWVIDEO) ) {
AlignPlaybackWindow(npMCI);
}
if (npMCI->hicDraw) {
DPF2(("Calling ICDrawRealize\n"));
u = (UINT)ICDrawRealize(npMCI->hicDraw, npMCI->hdc, npMCI->fForceBackground);
} else {
u = 0;
}
//
// realize the other strems, but force them into the background.
//
for (i=0; i<npMCI->streams; i++) {
psi = SI(i);
if (!(psi->dwFlags & STREAM_ENABLED))
continue;
if (psi->dwFlags & STREAM_ERROR)
continue;
if (psi == npMCI->psiVideo)
continue;
if (psi->hicDraw == NULL)
continue;
if (psi->hicDraw == npMCI->hicDraw)
continue;
ICDrawRealize(psi->hicDraw, npMCI->hdc, TRUE);
}
//
// return "master" stream realize value.
//
return u;
}
void NEAR PASCAL UnprepareDC(NPMCIGRAPHIC npMCI)
{
Assert(npMCI->hdc);
DPF2(("*** UnprepareDC(%04X)\n",npMCI->hdc));
SelectPalette(npMCI->hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
RealizePalette(npMCI->hdc);
//RestoreDC(npMCI->hdc, -1);
}
/* This function is called to actually handle drawing.
**
** ckid and cksize specify the type and size of the data to be drawn;
** it's located at npMCI->lp.
**
** If the fHurryUp flag is set, that means that we're behind and we
** shouldn't draw now. all we do it update the current buffered image
** and return...
*/
BOOL NEAR PASCAL DrawBits(NPMCIGRAPHIC npMCI, DWORD ckid, DWORD cksize, BOOL fHurryUp)
{
LPVOID lp = npMCI->lp;
LPBITMAPINFOHEADER lpFormat = npMCI->pbiFormat;
DWORD dwRet;
DWORD dwFlags;
STREAMINFO *psi;
if (!npMCI->pbiFormat)
return TRUE;
if (npMCI->fNoDrawing || !(npMCI->dwFlags & MCIAVI_SHOWVIDEO))
return TRUE;
psi = SI(npMCI->nVideoStream);
//
// let's compute the flags we need to pass to ICDecompress() and
// to ICDraw()
//
// ICDRAW_HURRYUP - we are behind
// ICDRAW_PREROLL - we are seeking (before a play)
// ICDRAW_UPDATE - update of frame (repaint, ...)
// ICDRAW_NOTKEYFRAME - this frame data is not a key.
//
dwFlags = 0;
if (psi->dwFlags & STREAM_NEEDUPDATE)
dwFlags |= ICDRAW_UPDATE;
if (cksize == 0)
dwFlags |= ICDRAW_NULLFRAME;
if (ckid == 0) {
dwFlags |= ICDRAW_UPDATE;
lp = 0;
}
else if (fHurryUp) {
dwFlags |= ICDRAW_HURRYUP;
psi->dwFlags |= STREAM_DIRTY;
}
else if (!(npMCI->dwFlags & MCIAVI_REVERSE) &&
(npMCI->lCurrentFrame < npMCI->lRealStart)) {
dwFlags |= ICDRAW_PREROLL;
psi->dwFlags |= STREAM_DIRTY;
}
if (npMCI->hpFrameIndex) {
if ((ckid == 0L || cksize == 0) ||
FramePrevKey(npMCI->lCurrentFrame) != npMCI->lCurrentFrame)
dwFlags |= ICDRAW_NOTKEYFRAME;
}
//
// now draw the frame, decompress first if needed.
//
if (npMCI->hic) {
if (ckid != 0L && cksize != 0) {
TIMESTART(timeDecompress);
npMCI->pbiFormat->biSizeImage = cksize; // !!! Is this safe?
dwRet = ICDecompress(npMCI->hic,
dwFlags,
npMCI->pbiFormat,
npMCI->lp,
&npMCI->bih,
npMCI->hpDecompress);
TIMEEND(timeDecompress);
if (dwRet == ICERR_DONTDRAW) {
return TRUE; // !!!???
}
// ICERR_NEWPALETTE?
dwFlags &= (~ICDRAW_NOTKEYFRAME); // It's a key frame now....
}
if (dwFlags & (ICDRAW_HURRYUP|ICDRAW_PREROLL))
return TRUE;
lpFormat = &npMCI->bih;
lp = npMCI->hpDecompress;
cksize = npMCI->bih.biSizeImage;
}
TIMESTART(timeDraw);
if ((npMCI->dwFlags & MCIAVI_PALCHANGED) &&
!(dwFlags & (ICDRAW_HURRYUP|ICDRAW_PREROLL))) {
#ifdef USEAVIFILE
if (psi->ps) {
if (npMCI->hic) {
//!!! should be psi->lpFormat *not* npMCI->pbiFormat
ICDecompressGetPalette(npMCI->hic, npMCI->pbiFormat, &npMCI->bih);
ICDrawChangePalette(npMCI->hicDraw, &npMCI->bih);
}
else {
ICDrawChangePalette(npMCI->hicDraw, npMCI->pbiFormat);
}
}
else
#endif
{
DPF2(("Calling ICDrawChangePalette\n"));
ICDrawChangePalette(npMCI->hicDraw, &npMCI->bih);
}
npMCI->dwFlags &= ~(MCIAVI_PALCHANGED);
dwFlags &= ~ICDRAW_HURRYUP; // should realy draw this!
}
if ((npMCI->dwFlags & MCIAVI_SEEKING) &&
!(dwFlags & ICDRAW_PREROLL))
PrepareDC(npMCI);
lpFormat->biSizeImage = cksize; // !!! ??? Is this safe?
//
// !!!do we realy realy want to do this here?
// or just relay on the MPlay(er) status function
//
////if (npMCI->dwFlags & MCIAVI_WANTMOVE)
//// CheckWindowMoveFast(npMCI);
DPF3(("Calling ICDraw on frame %ld (%08lx)\n", npMCI->lCurrentFrame, dwFlags));
dwRet = ICDraw(npMCI->hicDraw, dwFlags, lpFormat, lp, cksize,
npMCI->lCurrentFrame - npMCI->lFramePlayStart);
TIMEEND(timeDraw);
if ((LONG) dwRet < ICERR_OK) {
DPF(("Driver failed ICM_DRAW message err=%ld\n", dwRet));
return FALSE;
}
else {
psi->dwFlags &= ~STREAM_NEEDUPDATE;
if (!(dwFlags & (ICDRAW_HURRYUP|ICDRAW_PREROLL)))
psi->dwFlags &= ~STREAM_DIRTY;
}
if (ICERR_STOPDRAWING == dwRet) {
npMCI->dwFlags |= MCIAVI_STOP;
}
#ifdef INTERVAL_TIMES
// grab the interframe interval as soon as the video is shown
if (npMCI->wTaskState == TASKPLAYING) {
DWORD dwTime = timeGetTime();
if (npMCI->nFrames > 0) {
long lMsecs = (LONG) dwTime - npMCI->dwStartTime;
if (lMsecs < npMCI->msFrameMin) {
npMCI->msFrameMin = lMsecs;
}
if (lMsecs > npMCI->msFrameMax) {
npMCI->msFrameMax = lMsecs;
}
npMCI->msFrameTotal += lMsecs;
npMCI->msSquares += (lMsecs * lMsecs);
if (lMsecs < NBUCKETS * BUCKETSIZE)
npMCI->buckets[lMsecs/BUCKETSIZE]++;
else
npMCI->buckets[NBUCKETS]++;
if (npMCI->nFrames < npMCI->cIntervals)
*(npMCI->paIntervals+npMCI->nFrames) = lMsecs;
}
npMCI->nFrames++;
npMCI->dwStartTime = dwTime;
}
#endif
return TRUE;
}
/***************************************************************************
***************************************************************************/
#if 0
static void FreeDecompressBuffer(NPMCIGRAPHIC npMCI)
{
if (npMCI->hpDecompress)
GlobalFreePtr(npMCI->hpDecompress);
npMCI->hpDecompress = NULL;
npMCI->cbDecompress = 0;
}
#endif
/***************************************************************************
***************************************************************************/
static BOOL GetDecompressBuffer(NPMCIGRAPHIC npMCI)
{
int n = npMCI->nVideoStream;
int dxDest = RCW(DEST(n));
int dyDest = RCH(DEST(n));
HPSTR hp;
npMCI->bih.biSizeImage = npMCI->bih.biHeight * DIBWIDTHBYTES(npMCI->bih);
if ((LONG) npMCI->bih.biSizeImage <= npMCI->cbDecompress)
return TRUE;
if (!npMCI->hpDecompress)
hp = GlobalAllocPtr(GHND|GMEM_SHARE, npMCI->bih.biSizeImage);
else
hp = GlobalReAllocPtr(npMCI->hpDecompress,
npMCI->bih.biSizeImage,
GMEM_MOVEABLE | GMEM_SHARE);
if (hp == NULL) {
npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
return FALSE;
}
npMCI->hpDecompress = hp;
npMCI->cbDecompress = npMCI->bih.biSizeImage;
return TRUE;
}
/*
Possibilities:
1. We're starting to play.
We may need to switch into fullscreen mode.
We need a DrawBegin.
2. We're updating the screen.
Do we send a new DrawBegin?
Has anything changed since we last updated? Perhaps we can use
a flag to say whether something has changed, and set it when we leave
fullscreen mode or when the window is stretched.
What if we're updating to memory?
3. We're playing, and the user has stretched the window.
The Draw device may need us to go back to a key frame.
If we have a separate decompressor, it may need us to go back to a key frame.
*/
#if 0
RestartCompressor()
{
DWORD dwDrawFlags;
dwDrawFlags = (npMCI->dwFlags & MCIAVI_FULLSCREEN) ?
ICDRAW_FULLSCREEN : ICDRAW_HDC;
if (pfRestart)
dwDrawFlags |= ICDRAW_CONTINUE;
if (npMCI->dwFlags & MCIAVI_UPDATETOMEMORY)
dwDrawFlags |= ICDRAW_MEMORYDC;
if (npMCI->hic) {
static struct {
BITMAPINFOHEADER bi;
RGBQUAD rgbq[256];
} dib;
}
}
#endif
STATICFN BOOL TryDrawDevice(NPMCIGRAPHIC npMCI, HIC hicDraw, DWORD dwDrawFlags, BOOL fTryDecompress)
{
LRESULT dw;
int n = npMCI->nVideoStream;
STREAMINFO *psi = SI(n);
Assert(psi);
if (hicDraw == NULL)
return FALSE;
// See if the standard draw device can handle the format
dw = ICDrawBegin(hicDraw,
dwDrawFlags,
npMCI->hpal, // palette to draw with
npMCI->hwndPlayback, // window to draw to
npMCI->hdc, // HDC to draw to
RCX(DEST(n)),
RCY(DEST(n)),
RCW(DEST(n)),
RCH(DEST(n)),
npMCI->pbiFormat,
RCX(SOURCE(n)),
RCY(SOURCE(n)),
RCW(SOURCE(n)),
RCH(SOURCE(n)),
// !!! First of all, these two are backwards.
// !!! Secondly, what if PlayuSec == 0?
npMCI->dwPlayMicroSecPerFrame,
1000000L);
if (dw == ICERR_OK) {
npMCI->hic = 0;
npMCI->hicDraw = hicDraw;
return TRUE;
}
if (npMCI->hicDecompress && fTryDecompress) {
RECT rc;
// Ask the draw device to suggest a format, then try to get our
// decompressor to make that format.
dw = ICDrawSuggestFormat(hicDraw,
npMCI->pbiFormat,
&npMCI->bih,
RCW(SOURCE(n)),
RCH(SOURCE(n)),
RCW(DEST(n)),
RCH(DEST(n)),
npMCI->hicDecompress);
if ((LONG)dw >= 0)
dw = ICDecompressQuery(npMCI->hicDecompress,
npMCI->pbiFormat,&npMCI->bih);
if ((LONG)dw < 0) {
//
// default to the right format for the screen, in case the draw guy
// fails the draw suggest.
//
ICGetDisplayFormat(npMCI->hicDecompress,
npMCI->pbiFormat,&npMCI->bih, 0,
MulDiv((int)npMCI->pbiFormat->biWidth, RCW(psi->rcDest),RCW(psi->rcSource)),
MulDiv((int)npMCI->pbiFormat->biHeight,RCH(psi->rcDest),RCH(psi->rcSource)));
dw = ICDecompressQuery(npMCI->hicDecompress,
npMCI->pbiFormat,&npMCI->bih);
if (dw != ICERR_OK) {
npMCI->dwTaskError = MCIERR_INTERNAL;
return FALSE;
}
}
if (npMCI->bih.biBitCount <= 8) {
ICDecompressGetPalette(npMCI->hicDecompress,
npMCI->pbiFormat, &npMCI->bih);
}
#ifdef DEBUG
DPF(("InitDecompress: Decompressing %dx%dx%d '%4.4ls' to %dx%dx%d\n",
(int)npMCI->pbiFormat->biWidth,
(int)npMCI->pbiFormat->biHeight,
(int)npMCI->pbiFormat->biBitCount,
(LPSTR)(
npMCI->pbiFormat->biCompression == BI_RGB ? "None" :
npMCI->pbiFormat->biCompression == BI_RLE8 ? "Rle8" :
npMCI->pbiFormat->biCompression == BI_RLE4 ? "Rle4" :
(LPSTR)&npMCI->pbiFormat->biCompression),
(int)npMCI->bih.biWidth,
(int)npMCI->bih.biHeight,
(int)npMCI->bih.biBitCount));
#endif
if (!GetDecompressBuffer(npMCI))
return FALSE;
//
// setup the "real" source rect we will draw with.
//
#if 0
rc.left = (int) ((SOURCE(n).left * npMCI->bih.biWidth) / npMCI->pbiFormat->biWidth);
rc.right = (int) ((SOURCE(n).right * npMCI->bih.biWidth) / npMCI->pbiFormat->biWidth);
rc.top = (int) ((SOURCE(n).top * npMCI->bih.biHeight) / npMCI->pbiFormat->biHeight);
rc.bottom = (int) ((SOURCE(n).bottom * npMCI->bih.biHeight) / npMCI->pbiFormat->biHeight);
#else
rc = SOURCE(n);
rc.left = (int) ((rc.left * npMCI->bih.biWidth) / npMCI->pbiFormat->biWidth);
rc.right = (int) ((rc.right * npMCI->bih.biWidth) / npMCI->pbiFormat->biWidth);
rc.top = (int) ((rc.top * npMCI->bih.biHeight) / npMCI->pbiFormat->biHeight);
rc.bottom = (int) ((rc.bottom * npMCI->bih.biHeight) / npMCI->pbiFormat->biHeight);
#endif
dw = ICDrawBegin(hicDraw,
dwDrawFlags,
npMCI->hpal, // palette to draw with
npMCI->hwndPlayback, // window to draw to
npMCI->hdc, // HDC to draw to
RCX(DEST(n)),
RCY(DEST(n)),
RCW(DEST(n)),
RCH(DEST(n)),
&npMCI->bih,
rc.left, rc.top,
rc.right - rc.left,
rc.bottom - rc.top,
// !!! First of all, these two are backwards.
// !!! Secondly, what if PlayuSec == 0?
npMCI->dwPlayMicroSecPerFrame,
1000000L);
if (dw == ICERR_OK) {
npMCI->hic = npMCI->hicDecompress;
npMCI->hicDraw = hicDraw;
// Now, we have the format we'd like the decompressor to decompress to...
dw = ICDecompressBegin(npMCI->hicDecompress,
npMCI->pbiFormat,
&npMCI->bih);
if (dw != ICERR_OK) {
DPF(("DrawBegin: decompressor succeeded query, failed begin!\n"));
ICDrawEnd(npMCI->hicDraw);
return FALSE;
}
return TRUE;
}
if (npMCI->dwFlags & MCIAVI_FULLSCREEN) {
npMCI->dwTaskError = MCIERR_AVI_NODISPDIB;
}
}
return FALSE;
}
#ifndef DEBUG
INLINE
#endif
STATICFN BOOL FindDrawDevice(NPMCIGRAPHIC npMCI, DWORD dwDrawFlags)
{
if (npMCI->dwFlags & MCIAVI_USERDRAWPROC) {
// If the user has set a draw procedure, try it.
if (TryDrawDevice(npMCI, npMCI->hicDrawDefault, dwDrawFlags, TRUE)) {
if (npMCI->hic) {
DPF2(("Using decompressor, then application's draw device...\n"));
} else {
DPF2(("Using application's draw device...\n"));
}
return TRUE;
}
// If it fails, it fails.
DPF(("Can't use application's draw device!\n"));
return FALSE;
}
// First, try a pure draw device we've found.
if (TryDrawDevice(npMCI, SI(npMCI->nVideoStream)->hicDraw, dwDrawFlags, FALSE)) {
DPF2(("Draw device is drawing to the screen...\n"));
return TRUE;
}
// Next, try see if the decompressor we found can draw too.
// Should this even get asked before the guy above?!!!!
if (TryDrawDevice(npMCI, npMCI->hicDecompress, dwDrawFlags, FALSE)) {
DPF2(("Decompressor is drawing to the screen...\n"));
return TRUE;
}
// No? Then, get the standard draw device, for fullscreen or not.
if (npMCI->dwFlags & MCIAVI_FULLSCREEN) {
// !!! If it's fullscreen, should we force a re-begin?
// !!! Assume fullscreen only happens when play is starting?
if (npMCI->hicDrawFull == NULL) {
DPF2(("Opening default fullscreen codec...\n"));
npMCI->hicDrawFull = ICOpen(streamtypeVIDEO,
FOURCC_AVIFull,ICMODE_DRAW);
if (!npMCI->hicDrawFull)
npMCI->hicDrawFull = (HIC) -1;
}
npMCI->hicDraw = npMCI->hicDrawFull;
} else {
if (npMCI->hicDrawDefault == NULL) {
DPF2(("Opening default draw codec...\n"));
npMCI->hicDrawDefault = ICOpen(streamtypeVIDEO,
FOURCC_AVIDraw,ICMODE_DRAW);
if (!npMCI->hicDrawDefault)
npMCI->hicDrawDefault = (HIC) -1;
}
npMCI->hicDraw = npMCI->hicDrawDefault;
}
// If there's an installed draw device, try it.
if (npMCI->hicDraw && npMCI->hicDraw != (HIC) -1) {
if (TryDrawDevice(npMCI, npMCI->hicDraw, dwDrawFlags, TRUE)) {
if (npMCI->hic) {
DPF2(("Using decompressor, then default draw device...\n"));
} else {
DPF2(("Using default draw device...\n"));
}
return TRUE;
}
}
if (npMCI->dwFlags & MCIAVI_FULLSCREEN) {
if (!npMCI->hicInternalFull)
npMCI->hicInternalFull = ICOpenFunction(streamtypeVIDEO,
FOURCC_AVIFull,ICMODE_DRAW,(FARPROC)ICAVIFullProc);
npMCI->hicDraw = npMCI->hicInternalFull;
} else {
if (!npMCI->hicInternal) {
npMCI->hicInternal = ICOpenFunction(streamtypeVIDEO,
FOURCC_AVIDraw,ICMODE_DRAW,(FARPROC)ICAVIDrawProc);
#ifdef DEBUG
{
// This is a hack to get the hdd back from AVIDrawOpen
extern HDRAWDIB ghdd;
npMCI->hdd = ghdd;
ghdd = NULL;
}
#endif
}
npMCI->hicDraw = npMCI->hicInternal;
}
// As a last resort, try the built-in draw device.
if (TryDrawDevice(npMCI, npMCI->hicDraw, dwDrawFlags, TRUE)) {
if (npMCI->hic) {
DPF2(("Using decompressor, then built-in draw device...\n"));
} else {
DPF2(("Using built-in draw device...\n"));
}
return TRUE;
}
// if we are failing because fullscreen, then return an error
// indicating this
if (npMCI->dwFlags & MCIAVI_FULLSCREEN) {
npMCI->dwTaskError = MCIERR_AVI_NODISPDIB;
}
return FALSE;
}
/**************************************************************************
* @doc INTERNAL DRAWDIB
*
* @api BOOL | DibEq | This function compares two dibs.
*
* @parm LPBITMAPINFOHEADER lpbi1 | Pointer to one bitmap.
* this DIB is assumed to have the colors after the BITMAPINFOHEADER
*
* @parm LPBITMAPINFOHEADER | lpbi2 | Pointer to second bitmap.
* this DIB is assumed to have the colors after biSize bytes.
*
* @rdesc Returns TRUE if bitmaps are identical, FALSE otherwise.
*
**************************************************************************/
INLINE BOOL DibEq(LPBITMAPINFOHEADER lpbi1, LPBITMAPINFOHEADER lpbi2)
{
return
lpbi1->biCompression == lpbi2->biCompression &&
lpbi1->biSize == lpbi2->biSize &&
lpbi1->biWidth == lpbi2->biWidth &&
lpbi1->biHeight == lpbi2->biHeight &&
lpbi1->biBitCount == lpbi2->biBitCount;
}
/***************************************************************************
*
* @doc INTERNAL MCIAVI
*
* @api void | DrawBegin
*
*
***************************************************************************/
BOOL FAR PASCAL DrawBegin(NPMCIGRAPHIC npMCI, BOOL FAR *pfRestart)
{
DWORD dwDrawFlags;
HIC hicLast = npMCI->hic;
HIC hicLastDraw = npMCI->hicDraw;
BITMAPINFOHEADER bihDecompLast = npMCI->bih;
if (npMCI->nVideoStreams == 0)
return TRUE;
if (!npMCI->pbiFormat)
return TRUE;
npMCI->fNoDrawing = FALSE;
// if fullscreen, make sure we re-initialize....
if (npMCI->dwFlags & MCIAVI_FULLSCREEN) {
npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;
}
dwDrawFlags = (npMCI->dwFlags & MCIAVI_FULLSCREEN) ?
ICDRAW_FULLSCREEN : ICDRAW_HDC;
if (pfRestart) {
dwDrawFlags |= ICDRAW_CONTINUE;
*pfRestart = TRUE;
}
if (npMCI->dwFlags & MCIAVI_UPDATETOMEMORY)
dwDrawFlags |= ICDRAW_MEMORYDC;
// !!! What about "stupid mode"?
//
// if the file has no keyframes force a buffer
//
if (npMCI->dwKeyFrameInfo == 0)
dwDrawFlags |= ICDRAW_BUFFER;
//
// if the file has few keyframes also force a buffer.
//
if (MovieToTime(npMCI->dwKeyFrameInfo) > KEYFRAME_PANIC_SPACE)
dwDrawFlags |= ICDRAW_BUFFER;
if (dwDrawFlags & ICDRAW_BUFFER)
DPF(("Forcing a decompress buffer because too few key frames\n"));
if (npMCI->wTaskState > TASKIDLE &&
!(npMCI->dwFlags & MCIAVI_SEEKING) &&
!(npMCI->dwFlags & MCIAVI_FULLSCREEN) &&
(npMCI->dwFlags & MCIAVI_ANIMATEPALETTE)) {
dwDrawFlags |= ICDRAW_ANIMATE;
#if 0
//
// I moved all this into ShowStage() where you could claim it realy belongs.
//
if (npMCI->hwnd == npMCI->hwndDefault &&
!(GetWindowLong(npMCI->hwnd, GWL_STYLE) & WS_CHILD))
SetActiveWindow(npMCI->hwnd);
#endif
}
if (npMCI->hdc == NULL) {
DPF2(("DrawBegin() with NULL hdc!\n"));
}
if (FindDrawDevice(npMCI, dwDrawFlags)) {
if (npMCI->hicDraw != hicLastDraw || (npMCI->hic != hicLast) ||
(npMCI->hic && !DibEq(&npMCI->bih, &bihDecompLast))) {
// !!! This obviously shouldn't always be invalidated!
//
// make sure the current image buffer is invalidated
//
DPF2(("Draw device is different; restarting....\n"));
npMCI->lFrameDrawn = (- (LONG) npMCI->wEarlyRecords) - 1;
npMCI->dwFlags |= MCIAVI_WANTMOVE;
if (pfRestart)
*pfRestart = TRUE;
}
if (npMCI->dwFlags & MCIAVI_WANTMOVE)
CheckWindowMove(npMCI, TRUE);
// if (pfRestart)
// *pfRestart = (dw == ICERR_GOTOKEYFRAME);
npMCI->dwFlags &= ~(MCIAVI_NEEDDRAWBEGIN);
#if 0
//
// tell the compressor some interesting info.
//
if (npMCI->hicDraw) { // !!! Does npMCI->hic need to know this?
ICSendMessage(npMCI->hic, ICM_SET, ICM_FRAMERATE, npMCI->dwPlayMicroSecPerFrame);
ICSendMessage(npMCI->hic, ICM_SET, ICM_KEYFRAMERATE, npMCI->dwKeyFrameInfo);
}
#endif
return TRUE;
}
return FALSE;
}
/***************************************************************************
*
* @doc INTERNAL MCIAVI
*
* @api void | DrawEnd
*
* @parm NPMCIGRAPHIC | npMCI | pointer to instance data block.
*
***************************************************************************/
void NEAR PASCAL DrawEnd(NPMCIGRAPHIC npMCI)
{
if (!npMCI->pbiFormat)
return;
ICDrawEnd(npMCI->hicDraw);
// if we were fullscreen, we now need to repaint and things....
if (npMCI->dwFlags & MCIAVI_FULLSCREEN) {
npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;
}
/*
** let DrawDib clean up if we're animating the palette.
*/
if (npMCI->wTaskState > TASKIDLE &&
!(npMCI->dwFlags & MCIAVI_SEEKING) &&
!(npMCI->dwFlags & MCIAVI_FULLSCREEN) &&
!(npMCI->dwFlags & MCIAVI_UPDATING) &&
(npMCI->dwFlags & MCIAVI_ANIMATEPALETTE)) {
npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;
InvalidateRect(npMCI->hwndPlayback, NULL, FALSE);
}
}