|
|
/******************************************************************************
Copyright (C) Microsoft Corporation 1985-1991. All rights reserved.
Title: device.c - Multimedia Systems Media Control Interface driver for AVI.
*****************************************************************************/ #include "graphic.h"
#include "avitask.h"
#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
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api void | TaskWaitComplete | wait for a task to complete * ***************************************************************************/
void TaskWaitComplete(NPMCIGRAPHIC npMCI) { LONG lCount;
/*
** Release the critical section so that the task can complete! */
lCount = npMCI->lCritRefCount; npMCI->lCritRefCount = 0; LeaveCriticalSection(&npMCI->CritSec);
/*
** Use the handle given to us when we created the task to wait ** for the thread to complete */
WaitForSingleObject(npMCI->hThreadTermination, INFINITE); CloseHandle(npMCI->hThreadTermination);
/*
** Restore our critical section state */
EnterCriticalSection(&npMCI->CritSec); npMCI->lCritRefCount = lCount; } #endif
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api void | TaskWait | wait for a task state * background task. * ***************************************************************************/
DWORD mciaviTaskWait(NPMCIGRAPHIC npMCI, int state, BOOL fMciWait) { #ifdef WIN32
long lCount; MSG msg; #endif
DWORD dwStartWaitTime = timeGetTime(); // #define TIMEOUT_VALUE 10000L
#ifndef WIN32
Assert(npMCI->hTask != GetCurrentTask()); #endif
/*
** either wait for a state (state > 0) or wait for not state (state < 0) ** ** !!! if we want to timeout this is the place to do it. ** ** !!! Should we put up a wait cursor here? */ while (state < 0 ? (int)npMCI->wTaskState == -state : (int)npMCI->wTaskState != state) { if (!IsTask(npMCI->hTask)) { npMCI->dwTaskError = MCIERR_DEVICE_NOT_READY; return MCIERR_DEVICE_NOT_READY; }
#ifdef WIN32
/*
* Critical sections: * * We hold a critical section around the whole of the * WinProc (among other things). The owning thread can re-enter * a critical section, but needs to Leave the same number of times. * * When yielding here, we need to release the critical section. To avoid * the problem of multiple entries, EnterCrit is a macro that * increments an entry count (protected by the critical section), and * only goes one level into the critsec. Correspondingly, * LeaveCrit decrements the count and only actually leaves if * the count reaches 0. * * Here, however, we need to actually Enter and Leave regardless * of the count, so that we release the critical section * to other threads during the yield. Thus we don't use the macro, * and we also save/restore the critsec count so someone else * getting the critsec while we are yielding will behave correctly. */
lCount = npMCI->lCritRefCount; npMCI->lCritRefCount = 0; LeaveCriticalSection(&npMCI->CritSec);
/*
* Sleep for > 0 because this thread may be at a higher priority than * then thread actually playing the AVI because of the use of * SetThreadPriorityBackground and Sleep(0) only relinquishes * the remainder of the time slice if another thread of the SAME * PRIORITY is waiting to run. */
Sleep(10); #else
// DirectedYield(npMCI->hTask);
Yield(); #endif
#ifdef WM_AVISWP
if (TRUE) #else
if (fMciWait) #endif
{ #ifdef WIN32
/*
* if it's safe to yield, it's safe to poll * messages fully. This way, we will be absolutely * sure of getting the async size messages etc */ //aviTaskYield();
// it clearly is not safe at this point, since this
// yield can cause mci to close the driver, leaving us
// with nowhere to return to.
// handling messages for our own window is safe and should have the
// desired effect.
#ifdef WM_AVISWP
if (npMCI->hwnd) { if (PeekMessage(&msg, npMCI->hwnd, WM_AVISWP, WM_AVISWP, PM_REMOVE)) DispatchMessage(&msg); } #endif
#endif
if (fMciWait && mciDriverYield(npMCI->wDevID)) { #ifdef WIN32
EnterCriticalSection(&npMCI->CritSec); npMCI->lCritRefCount = lCount; #endif
break; } } else { #ifdef TIMEOUT_VALUE
if (timeGetTime() > dwStartWaitTime + TIMEOUT_VALUE) { Assert(0); npMCI->dwTaskError = MCIERR_DEVICE_NOT_READY; #ifdef WIN32
EnterCriticalSection(&npMCI->CritSec); npMCI->lCritRefCount = lCount; #endif
return MCIERR_DEVICE_NOT_READY; } #endif
} #ifdef WIN32
EnterCriticalSection(&npMCI->CritSec); npMCI->lCritRefCount = lCount; #endif
} return 0L; }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | mciaviTaskMessage | this function sends a message to the * background task. * ***************************************************************************/
DWORD mciaviTaskMessage(NPMCIGRAPHIC npMCI, int msg) { if (!IsTask(npMCI->hTask)) { npMCI->dwTaskError = MCIERR_DEVICE_NOT_READY; return npMCI->dwTaskError; }
if (GetCurrentTask() == npMCI->hTask) { mciaviMessage(npMCI, msg); return npMCI->dwTaskError; }
if (npMCI->wTaskState == TASKPAUSED) { DPF(("Ack! message while PAUSED!\n")); return 1; // !!! Real error?
}
#ifdef DEBUG
if (npMCI->wTaskState != TASKIDLE) { DPF0(("Unknown task state (mciaviTaskMessage) %d\n", npMCI->wTaskState)); } Assert(npMCI->wTaskState == TASKIDLE); #endif
if (mciaviTaskWait(npMCI, TASKIDLE, FALSE) != 0) { DPF(("Error waiting for TASKIDLE in mciaviTaskMessage\n")); return npMCI->dwTaskError; }
npMCI->dwTaskError = 0L; npMCI->wTaskState = msg; mmTaskSignal(npMCI->hTask);
/*
** wait for the message to kick in */ mciaviTaskWait(npMCI, -msg, FALSE);
return npMCI->dwTaskError; }
DWORD NEAR PASCAL StopTemporarily(NPMCIGRAPHIC npMCI, TEMPORARYSTATE FAR * ps) { DWORD dw; HWND hCallback;
DPF2(("StopTemporarily: stopping from state %u.\n", npMCI->wTaskState));
Assert(ps);
ps->wOldTaskState = npMCI->wTaskState; ps->dwFlags = npMCI->dwFlags; ps->lTo = npMCI->lTo - (LONG)npMCI->dwBufferedVideo; ps->lFrom = npMCI->lFrom;
//
// setting MCIAVI_UPDATING will make sure we dont yield or do
// other wierd things unless we need to. it is a bad name for the
// flag I know I am sorry.
//
// it means we are stoping temporarily and will be restarted
// the code will not do things like give our wave device
// away or become the active window.
//
npMCI->dwFlags |= MCIAVI_UPDATING;
/* Hide the delayed notification, if any, so it doesn't happen now. */ hCallback = npMCI->hCallback; npMCI->hCallback = NULL;
dw = DeviceStop(npMCI, MCI_WAIT);
/* Restore the notification */ npMCI->hCallback = hCallback;
if (dw != 0 ) { if (ps->dwFlags & MCIAVI_UPDATING) npMCI->dwFlags |= MCIAVI_UPDATING; else npMCI->dwFlags &= ~MCIAVI_UPDATING; }
DPF2(("StopTemporarily: stopped.\n")); return dw; }
DWORD NEAR PASCAL RestartAgain(NPMCIGRAPHIC npMCI, TEMPORARYSTATE FAR * ps) { DWORD dw = 0; DWORD dwFlags = 0;
DPF2(("Restart Again: restarting.\n"));
Assert(ps);
if (ps->dwFlags & MCIAVI_REVERSE) dwFlags = MCI_DGV_PLAY_REVERSE;
// !!! Make sure that this will actually cause a repeat in all cases....
if (ps->dwFlags & MCIAVI_REPEATING) npMCI->dwFlags |= MCIAVI_REPEATING; else npMCI->dwFlags &= ~MCIAVI_REPEATING;
if (ps->wOldTaskState == TASKPLAYING) { /* The only flags that matter at this point are the
** VGA flags and the wait flag. If we managed to ** get a new command, neither is in effect, so it's ** okay to pass zero for these flags. */ npMCI->lFrom = npMCI->lCurrentFrame; dw = DevicePlay(npMCI, ps->lTo, dwFlags | MCI_TO); } else if (ps->wOldTaskState == TASKCUEING) { /* Continue whatever we were doing */ npMCI->lFrom = ps->lFrom; dw = DevicePlay(npMCI, ps->lTo, dwFlags | MCI_TO); } else if (ps->wOldTaskState == TASKPAUSED) { dw = DeviceCue(npMCI, 0, MCI_WAIT); npMCI->lTo = ps->lTo; } else if (ps->wOldTaskState == TASKIDLE) { } else { DPF(("Trying to restart: task state %u...\n", ps->wOldTaskState)); Assert(0); }
//
// restore this flag so we can yield again.
//
if (ps->dwFlags & MCIAVI_UPDATING) npMCI->dwFlags |= MCIAVI_UPDATING; else npMCI->dwFlags &= ~MCIAVI_UPDATING;
DPF2(("Restart Again: restarted.\n")); return dw; }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api void | ShowStage | This utility function brings the default stage * window to the foreground on play, seek, step and pause commands. It * does nothing if the stage window is not the default window * * @parm NPMCIGRAPHIC | npMCI | near ptr to the instance data * ***************************************************************************/
void NEAR PASCAL ShowStage(NPMCIGRAPHIC npMCI) { if (!(npMCI->dwFlags & MCIAVI_NEEDTOSHOW)) { DPF0(("ShowStage returning NEEDTOSHOW is OFF\n")); return; }
if ((npMCI->dwFlags & MCIAVI_SHOWVIDEO) && npMCI->hwnd == npMCI->hwndDefault && ////////////!(GetWindowLong(npMCI->hwnd, GWL_STYLE) & WS_CHILD) &&
(!IsWindowVisible (npMCI->hwnd) || npMCI->hwnd != GetActiveWindow ())) { #ifdef WM_AVISWP
// Get the UI thread to do the window positioning
// This routine can be called on the background task while the main
// routine is waiting in mciaviTaskWait
SendMessage(npMCI->hwnd, WM_AVISWP, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | (IsWindowVisible(npMCI->hwnd) ? SWP_NOACTIVATE : 0)); #else
SetWindowPos(npMCI->hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | (IsWindowVisible(npMCI->hwnd) ? SWP_NOACTIVATE : 0)); #endif
}
//
// if the movie has palette changes we need to make it the active
// window otherwise the palette animation will not work right
//
if ((npMCI->dwFlags & MCIAVI_ANIMATEPALETTE) && !(npMCI->dwFlags & MCIAVI_SEEKING) && !(npMCI->dwFlags & MCIAVI_FULLSCREEN) && !(npMCI->dwFlags & MCIAVI_UPDATING) && npMCI->hwnd == npMCI->hwndDefault && !(GetWindowLong(npMCI->hwnd, GWL_STYLE) & WS_CHILD)) { SetActiveWindow(npMCI->hwnd); }
npMCI->dwFlags &= ~(MCIAVI_NEEDTOSHOW); }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | DeviceOpen | Open an AVI file. * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data. * * @parm LPSTR | lpName | file name. * * @parm DWORD | dwFlags | Open flags. * * @rdesc 0 means OK, otherwise mci error * ***************************************************************************/
DWORD PASCAL DeviceOpen(NPMCIGRAPHIC npMCI, DWORD dwFlags) { DWORD dwRet = 0L;
npMCI->wTaskState = TASKBEINGCREATED;
npMCI->uErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
#ifndef WIN32
// give our PSP to the task
npMCI->pspParent = GetCurrentPDB(); #endif
switch (mmTaskCreate(mciaviTask, &npMCI->hThreadTermination, (DWORD)(UINT)npMCI)) { case 0:
// Yield to the newly created task until it has
// had a chance to initialize or fail to initialize
while (npMCI->wTaskState <= TASKINIT) {
#ifndef WIN32
Yield(); #else
/* we have to peekmsg here since the threads are
* synchronised. but we don't need to actually * pick up any messages - so limit ourselves to the * avi window ones */ Sleep(1); if (npMCI->hwnd) { MSG msg;
if (PeekMessage(&msg, npMCI->hwnd, 0, 0, PM_REMOVE)) { DispatchMessage(&msg); } } #endif
if (npMCI->wTaskState != TASKBEINGCREATED && !IsTask(npMCI->hTask)) break; }
/*
* we need to do this peek message again. We may have never * entered the body of the loop above, or if this thread * gets very little cpu during the above loop, we might fail to * execute the PeekMessage above AFTER the SetWindowPos happens * in mciaviOpen. In that case, the swp resizing will not happen * until the next getmessage or peekmessage - in that case, * it could come after the ShowWindow (bad) or after * another size request (much worse). * * First check the thread opened the device successfully */
if (!IsTask(npMCI->hTask)) { // Task thread failed its initialisation. Wait for the
// task to terminate before returning to the user.
DPF2(("Waiting for task thread to terminate\n")); #ifdef WIN32
// On Win32 we must explicitly wait. On Win16, because this
// "thread" does not get control back until the task thread
// releases control the wait is irrelevant and is not used.
TaskWaitComplete(npMCI); #endif
dwRet = npMCI->dwTaskError; } else {
if (npMCI->hwnd) { MSG msg; if (PeekMessage(&msg, npMCI->hwnd, 0, 0, PM_REMOVE)) { DispatchMessage(&msg); } } }
break; case TASKERR_NOTASKSUPPORT: case TASKERR_OUTOFMEMORY: default: npMCI->hTask = 0; dwRet = MCIERR_OUT_OF_MEMORY; break; }
SetErrorMode(npMCI->uErrorMode);
return dwRet; }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | DeviceStop | Stop an AVI movie. * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data. * * @parm DWORD | dwFlags | Flags. * * @rdesc 0 means OK, otherwise mci error * ***************************************************************************/
DWORD PASCAL DeviceStop(NPMCIGRAPHIC npMCI, DWORD dwFlags) { DWORD dw = 0L;
/* Stop the record or playback if the task is currently playing */
if (!IsTask(npMCI->hTask)) return MCIERR_DEVICE_NOT_READY; if (npMCI->wTaskState == TASKPLAYING || npMCI->wTaskState == TASKPAUSED || npMCI->wTaskState == TASKCUEING || npMCI->wTaskState == TASKSTARTING) { /* Set STOP flag - the task watches for this flag to be set. The
** STOP flag is cleared by the task when playback has stopped. */ // Assert(!(npMCI->dwFlags & MCIAVI_STOP));
npMCI->dwFlags |= MCIAVI_STOP;
/* Send an extra signal to the task in case it is still
** blocked. This will be true if we are paused or if play ** has just completed. */
mmTaskSignal(npMCI->hTask);
/* Yield until playback is finished and we've really stopped. */ mciaviTaskWait(npMCI, TASKIDLE, FALSE); } else { #ifdef DEBUG
if (npMCI->wTaskState != TASKIDLE) { DPF0(("Unknown task state (DeviceStop) %d\n", npMCI->wTaskState)); } Assert(npMCI->wTaskState == TASKIDLE); // ??? Why ???
#endif
}
return dw; }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | DevicePause | Pause an AVI movie. * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data. * * @parm DWORD | dwFlags | Flags. * * @rdesc 0 means OK, otherwise mci error * ***************************************************************************/
DWORD PASCAL DevicePause(NPMCIGRAPHIC npMCI, DWORD dwFlags) { DWORD dw = 0L;
// If we're currently seeking, allow that to finish before
// pausing. This could potentially lock up the machine for
// a while, but the alternatives are ugly.
mciaviTaskWait(npMCI, -TASKCUEING, FALSE);
// Pause the record or playback if the task is currently playing
// or recording (BUSY)
if (npMCI->wTaskState == TASKPAUSED) { /* We're already paused at the right place, so
** that means we did it. Reset the flag, though, just in ** case we were about to restart. */ npMCI->dwFlags |= MCIAVI_PAUSE; if (dwFlags & MCI_NOTIFY) GraphicDelayedNotify(npMCI, MCI_NOTIFY_SUCCESSFUL); } else if (npMCI->wTaskState == TASKPLAYING) { npMCI->dwFlags |= MCIAVI_PAUSE | MCIAVI_WAITING; /* If the notify flag is set, set a flag which will tell us to
** send a notification when we actually pause. */ if (dwFlags & MCI_NOTIFY) npMCI->dwFlags |= MCIAVI_CUEING; if (dwFlags & MCI_WAIT) { /* We have to wait to actually pause. */ mciaviTaskWait(npMCI, -TASKPLAYING, TRUE); } npMCI->dwFlags &= ~(MCIAVI_WAITING); } else if (npMCI->wTaskState == TASKIDLE) { /* We're stopped. Put us in paused mode by cueing. */ npMCI->lTo = npMCI->lCurrentFrame; DeviceCue(npMCI, 0, dwFlags); } else { dw = MCIERR_NONAPPLICABLE_FUNCTION; }
return dw; }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | DeviceClose | Close an AVI file. * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data. * * @rdesc 0 means OK, otherwise mci error * ***************************************************************************/
DWORD PASCAL DeviceClose (NPMCIGRAPHIC npMCI) { DWORD dw = 0L;
if (npMCI && IsTask(npMCI->hTask)) { /* Be sure to stop playing, one way or another... */ DeviceStop(npMCI, MCI_WAIT);
// task state is now TASKIDLE and blocked
#ifdef DEBUG
if (npMCI->wTaskState != TASKIDLE) { DPF0(("Unknown task state (DeviceClose) %d\n", npMCI->wTaskState)); } Assert(npMCI->wTaskState == TASKIDLE); #endif
// Set task state to TASKCLOSE - this informs the task that it is
// time to die.
mciaviTaskMessage(npMCI, TASKCLOSE); mciaviTaskWait(npMCI, TASKCLOSED, FALSE);
#ifdef WIN32
/*
** Wait for the thread to complete so the DLL doesn't get unloaded ** while it's still executing code in that thread */
TaskWaitComplete(npMCI);
#endif // WIN32
}
return dw; }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | DevicePlay | Play an AVI movie. * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data. * * @parm DWORD | dwFlags | MCI flags from command. * * @rdesc 0 means OK, otherwise mci error * ***************************************************************************/
DWORD PASCAL DevicePlay (NPMCIGRAPHIC npMCI, LONG lPlayTo, DWORD dwFlags) { HWND hCallback; DWORD dw = 0L;
if (!IsTask(npMCI->hTask)) return MCIERR_DEVICE_NOT_READY; /* if not already playing, start the task up. */
if (dwFlags & MCI_NOTIFY) { /* Hide the delayed notification, if any, so it doesn't happen now. */ hCallback = npMCI->hCallback; npMCI->hCallback = NULL; }
/* First, figure out what mode to use. */ if (dwFlags & (MCI_MCIAVI_PLAY_FULLSCREEN | MCI_MCIAVI_PLAY_FULLBY2)) { if (npMCI->rcDest.right - npMCI->rcDest.left > npMCI->rcMovie.right - npMCI->rcMovie.left) dwFlags |= MCI_MCIAVI_PLAY_FULLBY2; if ((dwFlags & MCI_MCIAVI_PLAY_FULLBY2) && (npMCI->rcMovie.right <= 160) && (npMCI->rcMovie.bottom <= 120)) { npMCI->dwFlags |= MCIAVI_ZOOMBY2; } else { npMCI->dwFlags &= ~(MCIAVI_ZOOMBY2); }
// !!! This check is dumb: Some DISPDIB's may support > 320x240....
if ((npMCI->rcMovie.right > 320) || (npMCI->rcMovie.bottom > 240)) { dw = MCIERR_AVI_TOOBIGFORVGA; goto Exit; }
/* If playing, we have to stop so that we'll get put into DispDib
mode correctly. */ dw = DeviceStop(npMCI, MCI_WAIT); if (dw) goto Exit;
if ((dwFlags & MCI_WAIT) && !(npMCI->dwFlags & MCIAVI_REPEATING)) npMCI->dwFlags |= MCIAVI_NOBREAK; else npMCI->dwFlags &= ~(MCIAVI_NOBREAK); npMCI->dwFlags |= MCIAVI_FULLSCREEN; } else { npMCI->dwFlags &= ~(MCIAVI_FULLSCREEN); }
if ((npMCI->dwFlags & MCIAVI_SEEKING) && (npMCI->lTo != npMCI->lFrom)) { /* We're currently seeking, so we have to restart to get audio
** to work. */ DeviceStop(npMCI, MCI_WAIT); }
/* If we're currently seeking, stop so play can begin immediately. */ if (npMCI->wTaskState == TASKCUEING) { DeviceStop(npMCI, MCI_WAIT); }
if (npMCI->wTaskState == TASKPLAYING || npMCI->wTaskState == TASKPAUSED) { if (((npMCI->dwFlags & MCIAVI_REVERSE) != 0) != ((dwFlags & MCI_DGV_PLAY_REVERSE) != 0)) DeviceStop(npMCI, MCI_WAIT); }
// Make sure flags are cleared if they should be
npMCI->dwFlags &= ~(MCIAVI_PAUSE | MCIAVI_CUEING | MCIAVI_REVERSE);
if (dwFlags & MCI_DGV_PLAY_REPEAT) { npMCI->dwFlags |= MCIAVI_REPEATING; }
if (dwFlags & MCI_NOTIFY) { /* Restore the notification */ npMCI->hCallback = hCallback; }
if (lPlayTo > npMCI->lFrames) lPlayTo = npMCI->lFrames;
if (lPlayTo < 0) lPlayTo = 0;
if (dwFlags & MCI_TO) npMCI->lTo = lPlayTo;
if (dwFlags & MCI_DGV_PLAY_REVERSE) npMCI->dwFlags |= MCIAVI_REVERSE;
npMCI->dwFlags |= MCIAVI_WAITING;
if (npMCI->dwFlags & MCIAVI_NEEDTOSHOW) { ShowStage(npMCI); //
// leave this set so the play code knows this is a "real" play
// coming from the user, not a interal play/stop
//
// if the window needs shown we want to do it here if we can
// not in the background task.
//
npMCI->dwFlags |= MCIAVI_NEEDTOSHOW; }
if (npMCI->wTaskState == TASKPAUSED) { /* Wake the task up from pausing */ mmTaskSignal(npMCI->hTask); } else if (npMCI->wTaskState == TASKCUEING || npMCI->wTaskState == TASKPLAYING) { } else { /* Tell the task what to do when it wakes up */
mciaviTaskMessage(npMCI, TASKSTARTING);
dw = npMCI->dwTaskError; }
if (dwFlags & MCI_WAIT) { // yield to playback task until playback completes but don't
// yield to application - apps must use driveryield to get
// out of waits.
mciaviTaskWait(npMCI, TASKIDLE, TRUE); dw = npMCI->dwTaskError; }
if (dwFlags & (MCI_MCIAVI_PLAY_FULLSCREEN | MCI_MCIAVI_PLAY_FULLBY2)) { MSG msg; /* Remove stray mouse and keyboard events after DispDib. */ while (PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_NOYIELD | PM_REMOVE) || PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOYIELD | PM_REMOVE)) ; }
npMCI->dwFlags &= ~(MCIAVI_WAITING); Exit: return dw; }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | DeviceResume | Play an AVI movie. * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data. * * @parm DWORD | dwFlags | MCI flags from command. * * @rdesc 0 means OK, otherwise mci error * ***************************************************************************/
DWORD PASCAL DeviceResume(NPMCIGRAPHIC npMCI, DWORD dwFlags) { DWORD dw = 0L;
if (!IsTask(npMCI->hTask)) return MCIERR_DEVICE_NOT_READY; dw = DevicePlay(npMCI, 0, dwFlags | ((npMCI->dwFlags & MCIAVI_REVERSE) ? MCI_DGV_PLAY_REVERSE : 0));
return dw; } /***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | DeviceCue | Cue an AVI movie for playing. * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data. * * @parm LONG | lTo | Frame to seek to, if MCI_TO set in <p dwFlags>. * * @parm DWORD | dwFlags | MCI flags from command. * * @rdesc 0 means OK, otherwise mci error * ***************************************************************************/
DWORD PASCAL DeviceCue(NPMCIGRAPHIC npMCI, LONG lTo, DWORD dwFlags) { DWORD dw = 0L; HWND hCallback;
/* if not already playing, start animation and set timer */
if (!IsTask(npMCI->hTask)) return MCIERR_DEVICE_NOT_READY; if (dwFlags & MCI_NOTIFY) { /* Hide the delayed notification, if any, so it doesn't happen now. */ hCallback = npMCI->hCallback; npMCI->hCallback = NULL; }
if (npMCI->dwFlags & MCIAVI_SEEKING) { /* We're currently seeking, so we have to start again to get audio
** to work. */ DeviceStop(npMCI, MCI_WAIT); }
if (dwFlags & MCI_TO) { DeviceStop(npMCI, MCI_WAIT); npMCI->lFrom = lTo; } else if (npMCI->wTaskState == TASKIDLE) { npMCI->lFrom = npMCI->lCurrentFrame; }
if (dwFlags & MCI_NOTIFY) { /* Restore the notification */ npMCI->hCallback = hCallback; }
/* If we're ever resumed, we want to go to the end of the file. */ npMCI->lTo = npMCI->lFrames;
if (npMCI->wTaskState == TASKPAUSED) { /* We're already paused at the right place, so
** that means we did it. */ if (dwFlags & MCI_NOTIFY) GraphicDelayedNotify(npMCI, MCI_NOTIFY_SUCCESSFUL); } else if (npMCI->wTaskState == TASKIDLE) { // !!! Is this the only condition we can do this in?
npMCI->dwFlags |= MCIAVI_PAUSE | MCIAVI_CUEING | MCIAVI_WAITING;
mciaviTaskMessage(npMCI, TASKSTARTING);
if (dwFlags & MCI_WAIT) { mciaviTaskWait(npMCI, -TASKCUEING, TRUE); }
npMCI->dwFlags &= ~(MCIAVI_WAITING); dw = npMCI->dwTaskError; } else if (npMCI->wTaskState == TASKCUEING) { npMCI->dwFlags |= MCIAVI_PAUSE | MCIAVI_CUEING | MCIAVI_WAITING;
if (dwFlags & MCI_WAIT) { mciaviTaskWait(npMCI, -TASKCUEING, TRUE); }
npMCI->dwFlags &= ~(MCIAVI_WAITING); dw = npMCI->dwTaskError; } else if (npMCI->wTaskState == TASKPLAYING) { npMCI->dwFlags |= MCIAVI_PAUSE | MCIAVI_CUEING | MCIAVI_WAITING;
if (dwFlags & MCI_WAIT) { mciaviTaskWait(npMCI, -TASKPLAYING, TRUE); }
npMCI->dwFlags &= ~(MCIAVI_WAITING); dw = npMCI->dwTaskError; } else { dw = MCIERR_NONAPPLICABLE_FUNCTION; }
return dw; }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | DeviceSeek | Seek to a position in an AVI movie. * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data. * * @parm LONG | lTo | Frame to seek to. * * @parm DWORD | dwFlags | MCI flags from command. * * @rdesc 0 means OK, otherwise mci error * ***************************************************************************/
DWORD PASCAL DeviceSeek(NPMCIGRAPHIC npMCI, LONG lTo, DWORD dwFlags) { DWORD dw = 0; HWND hCallback;
DPF3(("DeviceSeek\n")); /* The window will be shown by the play code. */
/* If we can just shorten a previous seek, do it. */ if ((npMCI->wTaskState == TASKCUEING) && (npMCI->dwFlags & MCIAVI_SEEKING) && (npMCI->lCurrentFrame <= lTo) && (npMCI->lTo >= lTo)) { if (lTo != npMCI->lTo) { DPF3(("Seeking to %ld instead.\n", lTo)); } npMCI->lTo = lTo; return 0L; }
if (dwFlags & MCI_NOTIFY) { /* Hide the delayed notification, if any, so it doesn't happen now. */ hCallback = npMCI->hCallback; npMCI->hCallback = NULL; }
/* If playing, stop, so we can seek. */ dw = DeviceStop(npMCI, MCI_WAIT);
if (dwFlags & MCI_NOTIFY) { /* Restore the notification */ npMCI->hCallback = hCallback; }
// task state is now TASKIDLE and blocked
if (npMCI->lCurrentFrame != lTo) { npMCI->dwFlags |= MCIAVI_WAITING;
/* Essentially, we are telling the task: play just frame <lTo>.
** When it gets there, it will update the screen for us. */ npMCI->lFrom = npMCI->lTo = lTo; mciaviTaskMessage(npMCI, TASKSTARTING); if (dwFlags & MCI_WAIT) { mciaviTaskWait(npMCI, -TASKCUEING, TRUE); } npMCI->dwFlags &= ~(MCIAVI_WAITING); } else { /* Be sure the window gets shown and the notify gets sent,
** even though we don't have to do anything. */ if (npMCI->dwFlags & MCIAVI_NEEDTOSHOW) ShowStage(npMCI);
if (dwFlags & MCI_NOTIFY) GraphicDelayedNotify(npMCI, MCI_NOTIFY_SUCCESSFUL); } return dw; }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | CheckIfActive | check to see if we are the active movie * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data block. * * @rdesc 0 means OK, otherwise mci error * ***************************************************************************/
static void CheckIfActive(NPMCIGRAPHIC npMCI) { BOOL fActive; HWND hwndA;
//
// are we the foreground window?
//
// ??? should the value of <npMCI->fForceBackground> matter?
//
// IMPORTANT: This does NOT work under NT. The best that can
// be done is to check GetForegroundWindow
hwndA = GetActiveWindow();
fActive = (hwndA == npMCI->hwnd) || (GetFocus() == npMCI->hwnd) || (IsChild(hwndA, npMCI->hwnd) && !npMCI->fForceBackground);
DeviceSetActive(npMCI, fActive); }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | DeviceRealize | Updates the frame into the given DC * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data block. * * @parm BOOL | fForceBackground | Realize as background palette? * * @rdesc 0 means OK, otherwise mci error * ***************************************************************************/
DWORD PASCAL DeviceRealize(NPMCIGRAPHIC npMCI) { BOOL fGetDC; BOOL fPalChanged; BOOL fAlreadyDoneThat;
if (npMCI->dwFlags & MCIAVI_WANTMOVE) CheckWindowMove(npMCI, TRUE);
if (fAlreadyDoneThat = (BOOL)(npMCI->dwFlags & MCIAVI_UPDATING)) { DPF(("Re-entering DeviceRealize - but we don't care")); }
if (fGetDC = (npMCI->hdc == NULL)) { npMCI->hdc = GetDC(npMCI->hwnd); }
npMCI->dwFlags |= MCIAVI_UPDATING;
fPalChanged = PrepareDC(npMCI) > 0;
if (!fAlreadyDoneThat) npMCI->dwFlags &= ~MCIAVI_UPDATING;
if (fGetDC) { UnprepareDC(npMCI); ReleaseDC(npMCI->hwnd, npMCI->hdc); npMCI->hdc = NULL; }
if (fPalChanged) InvalidateRect(npMCI->hwnd, &npMCI->rcDest, TRUE);
CheckIfActive(npMCI);
return 0L; }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | DeviceActivate | is the movie active? * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data block. * * @rdesc 0 means OK, otherwise mci error * ***************************************************************************/
DWORD PASCAL DeviceSetActive(NPMCIGRAPHIC npMCI, BOOL fActive) { if (fActive) #ifdef WIN32
// We must explicitly request a unicode string. %s will not
// work as dprintf uses wvsprintfA
DPF(("**** '%hs' is active.\n", (LPTSTR)npMCI->szFilename)); #else
DPF(("**** '%s' is active.\n", (LPTSTR)npMCI->szFilename)); #endif
//
// if we are now the foreground "window" try to get the wave
// device back (iff it was stolen from us)
//
if (fActive && (npMCI->dwFlags & MCIAVI_LOSTAUDIO)) {
if (StealWaveDevice(npMCI)) { Assert(npMCI->dwFlags & MCIAVI_PLAYAUDIO); Assert(npMCI->hWave == NULL);
npMCI->dwFlags &= ~MCIAVI_PLAYAUDIO; DeviceMute(npMCI, FALSE); } }
return 0; }
/***************************************************************************
* * IsScreenDC() - returns true if the passed DC is a DC to the screen. * NOTE this checks for a DCOrg != 0, bitmaps always have * a origin of (0,0) This will give the wrong info a * fullscreen DC. * ***************************************************************************/
#ifndef WIN32
#define IsScreenDC(hdc) (GetDCOrg(hdc) != 0L)
#else
INLINE BOOL IsScreenDC(HDC hdc) { POINT pt;
GetDCOrgEx(hdc, &pt); return pt.x != 0 && pt.y != 0; } #endif
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | DeviceUpdate | Updates the frame into the given DC * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data block. * * @parm HDC | hDC | DC to draw frame into. * * @parm LPRECT | lprc | Update rect. * * @rdesc 0 means OK, otherwise mci error * ***************************************************************************/
DWORD PASCAL DeviceUpdate(NPMCIGRAPHIC npMCI, DWORD dwFlags, HDC hdc, LPRECT lprc) { DWORD dwErr = 0L; BOOL f; HDC hdcSave; TEMPORARYSTATE ts; HWND hCallback; HCURSOR hcurPrev; RECT rc; LONG lFrameDrawn;
if (!IsTask(npMCI->hTask)) { DPF0(("Returning DEVICE_NOT_READY from DeviceUpdate\n")); return MCIERR_DEVICE_NOT_READY; }
if (npMCI->dwFlags & MCIAVI_UPDATING) { DPF(("DeviceUpdate has been reentered.\n")); Assert(0); return MCIERR_DEVICE_NOT_READY; }
if (npMCI->dwFlags & MCIAVI_WANTMOVE) CheckWindowMove(npMCI, TRUE);
//
// see if we are the active movie now.
//
CheckIfActive(npMCI);
/* Setting this flag insures that the background task doesn't
** yield while we're trying to update. */ npMCI->dwFlags |= MCIAVI_UPDATING;
//
// mark the proper streams dirty, this will set the proper update flags
//
if (hdc) GetClipBox(hdc, &rc); else rc = npMCI->rcDest;
if (lprc) IntersectRect(&rc, &rc, lprc);
StreamInvalidate(npMCI, &rc);
//
// if they are drawing to the screen *assume* they wanted to set
// the MCI_DGV_UPDATE_PAINT flag
//
if (IsScreenDC(hdc)) dwFlags |= MCI_DGV_UPDATE_PAINT;
//
// if we are playing/seeking... (ie have a DC)
// then realize the palette now. and set the update flag if we just need to
// paint
//
// if we are paused, fall through so we can handle the case where
// a update fails
//
// !!!mabey we should rework this code to do this even if playing?
//
if (npMCI->hdc && (dwFlags & MCI_DGV_UPDATE_PAINT) && (npMCI->wTaskState != TASKPAUSED) &&
//!!! what is this?
((npMCI->wTaskState != TASKCUEING) || (npMCI->lCurrentFrame <= 1) || (npMCI->lCurrentFrame > npMCI->lRealStart - 30)) ) {
Assert(npMCI->wTaskState == TASKPLAYING || npMCI->wTaskState == TASKCUEING);
UnprepareDC(npMCI); PrepareDC(npMCI); // re-prepare
////////
//////// a update can fail when paused so we may have to stop/restart the task
////////
////////if (npMCI->wTaskState == TASKPAUSED)
//////// mmTaskSignal(npMCI->hTask);
npMCI->dwFlags &= ~MCIAVI_UPDATING; return 0L; }
//////////////////////////////////////////////////////////////////////
//
// when we get here one of the follow applies
//
// 1. we aren't playing/seeking/...
//
// 2. we need to draw into a memory bitmap (not the screen)
//
//////////////////////////////////////////////////////////////////////
//
// are we updating to a memory bitmap?
//
if (!(dwFlags & MCI_DGV_UPDATE_PAINT)) npMCI->dwFlags |= MCIAVI_UPDATETOMEMORY;
//
// if we are using a draw device (or are in easy mode) make sure we seek
// to the frame we want and dont use the current decompress buffer that
// may not be correct.
//
if ((npMCI->dwFlags & MCIAVI_UPDATETOMEMORY) || (npMCI->dwFlags & MCIAVI_STUPIDMODE)) { DPF(("DeviceUpdate: decompress buffer may be bad, ignoring it....\n")); npMCI->lFrameDrawn = (- (LONG) npMCI->wEarlyRecords) - 1; }
//
// honor the passed rect
//
if (lprc) { SaveDC(hdc); IntersectClipRect(hdc, lprc->left, lprc->top, lprc->right, lprc->bottom); }
//
// Always do an Update, if the update succeeds and we are at the right
// frame keep it.
//
// if it fails or the frame is wrong re-draw
//
// we need to do this because even though lFrameDrawn is a valid
// frame the draw handler may fail a update anyway (for example
// when decompressing to screen) so lFrameDrawn can be bogus and
// we do not know it until we try it.
//
lFrameDrawn = npMCI->lFrameDrawn; // save this for compare
if (npMCI->lFrameDrawn <= npMCI->lCurrentFrame && npMCI->lFrameDrawn >= 0) {
DPF2(("Update: redrawing frame %ld, current = %ld.\n", npMCI->lFrameDrawn, npMCI->lCurrentFrame));
/* Save the DC, in case we're playing, but need to update
** to a memory bitmap. */ hdcSave = npMCI->hdc; npMCI->hdc = hdc;
/* Realize the palette here, because it will cause strange
** things to happen if we do it in the task. */ if (npMCI->dwFlags & MCIAVI_NEEDDRAWBEGIN) { DrawBegin(npMCI, NULL);
if (npMCI->lFrameDrawn < npMCI->lVideoStart) { npMCI->hdc = hdcSave; goto SlowUpdate; } }
PrepareDC(npMCI); // make sure the palette is in there
// worker thread must hold critsec round all drawing
EnterCrit(npMCI); f = DoStreamUpdate(npMCI, FALSE); LeaveCrit(npMCI);
UnprepareDC(npMCI); // be sure to put things back....
npMCI->hdc = hdcSave;
if (!f) { SlowUpdate: DPF(("DeviceUpdate failed! invalidating lFrameDrawn\n")); npMCI->lFrameDrawn = (- (LONG) npMCI->wEarlyRecords) - 1; } else if (npMCI->lFrameDrawn >= npMCI->lCurrentFrame-1) { goto Exit; } }
DPF(("Update: drawn = %ld, current = %ld.\n", npMCI->lFrameDrawn, npMCI->lCurrentFrame));
//
// stop everything.
//
StopTemporarily(npMCI, &ts); Assert(npMCI->hdc == NULL); Assert(npMCI->wTaskState == TASKIDLE);
//
// the problem this tries to fix is the following:
// sometimes we are at N+1 but frame N is on the
// screen, if we now play to N+1 a mismatch will occur
//
if (lFrameDrawn >= 0 && lFrameDrawn == npMCI->lCurrentFrame-1) npMCI->lFrom = npMCI->lTo = lFrameDrawn; else npMCI->lFrom = npMCI->lTo = npMCI->lCurrentFrame;
/* Realize the palette here, because it will cause strange
** things to happen if we do it in the task. */ npMCI->hdc = hdc; PrepareDC(npMCI); // make sure the palette is in there
hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
/* Hide any notification, so it won't get sent... */ hCallback = npMCI->hCallback; npMCI->hCallback = NULL;
/* Wake the task up, and wait until it quiets down. */ mciaviTaskMessage(npMCI, TASKSTARTING); mciaviTaskWait(npMCI, TASKIDLE, FALSE); dwErr = npMCI->dwTaskError;
npMCI->hCallback = hCallback;
// We may have just yielded.. so only set the cursor back if we
// are still the wait cursor.
if (hcurPrev) { hcurPrev = SetCursor(hcurPrev); if (hcurPrev != LoadCursor(NULL, IDC_WAIT)) SetCursor(hcurPrev); }
npMCI->hdc = NULL;
if (dwErr == 0) dwErr = RestartAgain(npMCI,&ts); Exit: if (lprc) { RestoreDC(hdc, -1); }
npMCI->dwFlags &= ~(MCIAVI_UPDATING|MCIAVI_UPDATETOMEMORY);
if (npMCI->dwFlags & MCIAVI_NEEDUPDATE) { DPF(("**** we did a DeviceUpdate but still dirty?\n")); }
return dwErr; }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | DeviceStatus | Returns the current status * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data block. * * @rdesc Returns value for MCI's return value * ***************************************************************************/
UINT PASCAL DeviceMode(NPMCIGRAPHIC npMCI) { if (!IsTask(npMCI->hTask)) { return MCI_MODE_NOT_READY; }
switch (npMCI->wTaskState) { case TASKIDLE: return MCI_MODE_STOP; case TASKCUEING: return MCI_MODE_SEEK; case TASKPLAYING: return MCI_MODE_PLAY; case TASKPAUSED: return MCI_MODE_PAUSE; case TASKBEINGCREATED: case TASKINIT: case TASKCLOSE: case TASKSTARTING: case TASKREADINDEX: default: DPF(("Unexpected state %d in DeviceMode()\n", npMCI->wTaskState)); return MCI_MODE_NOT_READY; } }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | DevicePosition | Returns the current frame * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data block. * * @parm LPLONG | lpl | returns current frame * * @rdesc 0 means OK, otherwise mci error * ***************************************************************************/
DWORD PASCAL DevicePosition(NPMCIGRAPHIC npMCI, LPLONG lpl) { LONG NEAR PASCAL WhatFrameIsItTimeFor(NPMCIGRAPHIC npMCI); LONG l;
l = npMCI->lCurrentFrame - npMCI->dwBufferedVideo;
#if 0
if (npMCI->wTaskState == TASKPLAYING && npMCI->wPlaybackAlg != MCIAVI_ALG_INTERLEAVED) l = WhatFrameIsItTimeFor(npMCI); #endif
if ((npMCI->wTaskState == TASKCUEING) && !(npMCI->dwFlags & MCIAVI_SEEKING) && l < npMCI->lRealStart) l = npMCI->lRealStart;
if (l < 0) l = 0;
*lpl = l;
return 0L; }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | DeviceSetWindow | Set window for display * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data. * * @parm HWND | hwnd | Window to display into. * * @rdesc 0 means OK, otherwise mci error * * @comm Should this only take effect at time of next play? * ***************************************************************************/
DWORD PASCAL DeviceSetWindow(NPMCIGRAPHIC npMCI, HWND hwnd) { DWORD dw = 0L; TEMPORARYSTATE ts;
/* Stop play before changing windows. */ dw = StopTemporarily(npMCI, &ts);
if (!dw) { npMCI->hwnd = hwnd;
if (ts.wOldTaskState == TASKIDLE) { #if 0
DrawBegin(npMCI); DrawEnd(npMCI); #else
npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN; InvalidateRect(hwnd, &npMCI->rcDest, FALSE); #endif
}
/* Should we update the window here? */
/* Start playing again in the new window */ dw = RestartAgain(npMCI, &ts); }
return dw; }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | DeviceSpeed | Adjust the playback speed of an AVI movie. * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data. * * @parm DWORD | dwNewSpeed | New speed, where 1000 is 'normal' speed. * * @rdesc 0 means OK, otherwise mci error * * @comm If we are currently playing, we stop the device, set our flag, * and start playing again where we left off. If we were paused, * we end up stopped. Is this bad? * ***************************************************************************/
DWORD PASCAL DeviceSetSpeed(NPMCIGRAPHIC npMCI, DWORD dwNewSpeed) { DWORD dw = 0L; TEMPORARYSTATE ts;
/* If new speed is the same as the old speed, return. */ if (dwNewSpeed == npMCI->dwSpeedFactor) return 0L;
// !!! What if we were cueing or paused?
npMCI->dwSpeedFactor = dwNewSpeed;
if (npMCI->wTaskState == TASKIDLE) return 0L;
/* We're playing, so we have to adjust the playback rate in
** midstream. If we don't have sound going, this is pretty ** easy. If we do have sound, we either need to speed it up ** or slow it down or stop and start over. */
// This code doesn't work, since there are internal variables that
// need to be updated. Therefore, just stop and restart, even if there
// isn't any sound.
#if 0
/* Figure out how fast we're playing.... */ npMCI->dwPlayMicroSecPerFrame = muldiv32(npMCI->dwMicroSecPerFrame, 1000L, npMCI->dwSpeedFactor);
/* If there's no sound, we're done. */ if ((npMCI->nAudioStreams == 0) || !(npMCI->dwFlags & MCIAVI_PLAYAUDIO)) return 0L;
if (npMCI->hWave) { /* We could potentially try to do a waveOutSetPlaybackRate() here. */ } #endif
dw = StopTemporarily(npMCI, &ts);
if (!dw) { dw = RestartAgain(npMCI, &ts); }
return dw; }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | DeviceMute | Turn AVI sound on/off. * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data. * * @parm BOOL | fMute | TRUE If sound should be turned off, FALSE * if sound should stay on. * * @rdesc 0 means OK, otherwise mci error * * @comm If we are currently playing, we stop the device, set our flag, * and start playing again where we left off. If we were paused, * we end up stopped. Is this bad? * ***************************************************************************/
DWORD PASCAL DeviceMute(NPMCIGRAPHIC npMCI, BOOL fMute) { DWORD dw = 0L; TEMPORARYSTATE ts;
/* If there's no audio, just return. Should this be an error? */ if (npMCI->nAudioStreams == 0) return 0L;
/* If the mute state isn't changing, don't do anything. */ if (npMCI->dwFlags & MCIAVI_PLAYAUDIO) { if (!fMute) return 0L; } else { if (fMute) return 0L; }
/* Stop before changing mute */
dw = StopTemporarily(npMCI, &ts);
if (!dw) {
if (fMute) npMCI->dwFlags &= ~MCIAVI_PLAYAUDIO; else npMCI->dwFlags |= MCIAVI_PLAYAUDIO;
dw = RestartAgain(npMCI, &ts); }
return dw; }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | DeviceSetVolume | Set AVI volume. * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data. * * @parm DWORD | dwVolume | ranges from 0 to 1000. * * @rdesc 0 means OK, otherwise mci error * * @comm If we are currently playing, we try to change the volume of the * wave out device. * ***************************************************************************/
DWORD PASCAL DeviceSetVolume(NPMCIGRAPHIC npMCI, DWORD dwVolume) { DWORD dw = 0L; npMCI->dwVolume = dwVolume;
npMCI->dwFlags |= MCIAVI_VOLUMESET;
/* clear flag to emulate volume */; npMCI->fEmulatingVolume = FALSE;
/* If there's no audio, just return. Should this be an error? */ if (npMCI->nAudioStreams == 0) return 0L;
dw = DeviceMute(npMCI, dwVolume == 0);
if (npMCI->hWave && dw == 0L) { WORD wLeft; WORD wRight;
if (LOWORD(dwVolume) >= 1000) wLeft = 0xFFFF; else wLeft = (WORD) muldiv32(LOWORD(dwVolume), 32768L, 500L);
if (HIWORD(dwVolume) >= 1000) wRight = 0xFFFF; else wRight = (WORD) muldiv32(HIWORD(dwVolume), 32768L, 500L);
// !!! Support left and right volume?
dw = waveOutMessage(npMCI->hWave, WODM_SETVOLUME, MAKELONG(wLeft, wRight), 0);
if (dw != MMSYSERR_NOERROR && LOWORD(dwVolume) != 500) { npMCI->fEmulatingVolume = TRUE; BuildVolumeTable(npMCI); }
dw = 0; }
return dw; }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | DeviceGetVolume | Check the wave output device's current * volume. * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data. * * @rdesc 0 means OK, otherwise mci error * * @comm The volume is left in npMCI->dwVolume * * Issue: On devices with global volume control, like an SBPro, how should * things work? * ***************************************************************************/ DWORD PASCAL DeviceGetVolume(NPMCIGRAPHIC npMCI) { DWORD dw; DWORD dwVolume;
if (npMCI->hWave) { // Get the current audio volume....
dw = waveOutMessage(npMCI->hWave, WODM_GETVOLUME, (DWORD) (DWORD FAR *)&dwVolume, 0);
if (dw == 0) { returnvolume: npMCI->dwVolume = MAKELONG((UINT)muldiv32(LOWORD(dwVolume), 500L, 32768L), (UINT)muldiv32(HIWORD(dwVolume), 500L, 32768L)); } } else if (!(npMCI->dwFlags & MCIAVI_VOLUMESET)) { // We have no device open, and the user hasn't chosen a
// volume yet.
//
// Try to find out what the current "default" volume is.
//
// I realy doubt zero is the current volume, try to work
// with broken cards like the windows sound system.
//
dw = waveOutGetVolume((UINT)WAVE_MAPPER, &dwVolume);
if (dw == 0 && dwVolume != 0) goto returnvolume;
dw = waveOutGetVolume(0, &dwVolume);
if (dw == 0 && dwVolume != 0) goto returnvolume;
return MCIERR_NONAPPLICABLE_FUNCTION; }
return 0; }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | DeviceSetAudioStream | Choose which audio stream to use. * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data. * * @parm WORD | wStream | ranges from 1 to the number of streams. * * @rdesc 0 means OK, otherwise mci error * ***************************************************************************/
DWORD PASCAL DeviceSetAudioStream(NPMCIGRAPHIC npMCI, UINT wAudioStream) { DWORD dw = 0L; TEMPORARYSTATE ts; int stream;
/* If there's no audio, just return. Should this be an error? */
if (npMCI->nAudioStreams == 0) return 0;
for (stream = 0; stream < npMCI->streams; stream++) { if (SH(stream).fccType == streamtypeAUDIO) { --wAudioStream;
if (wAudioStream == 0) break; } }
if (stream == npMCI->nAudioStream) return 0;
Assert(stream < npMCI->streams);
/* Stop before changing mute */
dw = StopTemporarily(npMCI, &ts);
if (!dw) { npMCI->psiAudio = SI(stream); npMCI->nAudioStream = stream; dw = RestartAgain(npMCI, &ts); }
return dw; }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | DeviceSetVideoStream | Choose which video stream is the * "default". Also can enable/disable a stream. this works for both * video and "other" streams. * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data. * * @parm WORD | wStream | ranges from 1 to the number of streams. * * @rdesc 0 means OK, otherwise mci error * ***************************************************************************/
DWORD PASCAL DeviceSetVideoStream(NPMCIGRAPHIC npMCI, UINT uStream, BOOL fOn) { DWORD dw = 0L; TEMPORARYSTATE ts; int stream; STREAMINFO *psi;
//
// find the Nth non-audio, non-error stream
//
for (stream = 0; stream < npMCI->streams; stream++) {
psi = SI(stream);
if (psi->sh.fccType == streamtypeAUDIO) continue;
if (psi->dwFlags & STREAM_ERROR) continue;
if (--uStream == 0) break; }
if (stream == npMCI->streams) return MCIERR_OUTOFRANGE;
/* Stop before changing */
dw = StopTemporarily(npMCI, &ts);
if (!dw) {
if (fOn) psi->dwFlags |= STREAM_ENABLED; else psi->dwFlags &= ~STREAM_ENABLED;
if (fOn && psi->sh.fccType == streamtypeVIDEO) { //!!! should we change the master timebase?
DOUT("Setting main video stream\n"); #if 0
//
// the master video stream is too special cased to change!
//
npMCI->psiVideo = psi; npMCI->nVideoStream = stream; #endif
}
if (!fOn && npMCI->nVideoStream == stream) { DOUT("Turning off main video stream\n"); npMCI->dwFlags &= ~MCIAVI_SHOWVIDEO; }
//
// now we turn MCIAVI_SHOWVIDEO off if no video/other streams
// are enabled.
//
npMCI->dwFlags &= ~MCIAVI_SHOWVIDEO; // assume off.
for (stream = 0; stream < npMCI->streams; stream++) {
psi = SI(stream);
if (psi->sh.fccType == streamtypeAUDIO) continue;
if (psi->dwFlags & STREAM_ERROR) continue;
if (!(psi->dwFlags & STREAM_ENABLED)) continue;
// at least one stream is enabled show "video"
npMCI->dwFlags |= MCIAVI_SHOWVIDEO; }
if (!(npMCI->dwFlags & MCIAVI_SHOWVIDEO)) DOUT("All streams off\n");
dw = RestartAgain(npMCI, &ts); }
return dw; }
/***************************************************************************
* ***************************************************************************/
static void MapRect(RECT *prc, RECT*prcIn, RECT *prcFrom, RECT *prcTo) { if (IsRectEmpty(prcFrom)) { SetRectEmpty(prc); } else { DPF0(("MapRect: In [%d %d %d %d]\n", *prcIn)); DPF0(("MapRect: From [%d %d %d %d]\n", *prcFrom)); DPF0(("MapRect: To [%d %d %d %d]\n", *prcTo)); prc->left = prcTo->left + MulDiv(prcIn->left - prcFrom->left, prcTo->right - prcTo->left, prcFrom->right - prcFrom->left); prc->top = prcTo->top + MulDiv(prcIn->top - prcFrom->top, prcTo->bottom - prcTo->top, prcFrom->bottom - prcFrom->top); prc->right = prcTo->left + MulDiv(prcIn->right - prcFrom->left, prcTo->right - prcTo->left, prcFrom->right - prcFrom->left); prc->bottom= prcTo->top + MulDiv(prcIn->bottom- prcFrom->top, prcTo->bottom - prcTo->top, prcFrom->bottom - prcFrom->top); DPF0(("MapRect: OUT [%d %d %d %d]\n", *prc)); } }
/***************************************************************************
* ***************************************************************************/
static void MapStreamRects(NPMCIGRAPHIC npMCI) { int i;
//
// now set the source and dest rects for each stream.
//
for (i=0; i<npMCI->streams; i++) {
//
// make sure the stream rect is in bounds
//
DPF0(("SH(%d) rcFrame [%d %d %d %d]\n", i, SH(i).rcFrame)); DPF0(("SI(%d) rcSource [%d %d %d %d]\n", i, SI(i)->rcSource)); DPF0(("SI(%d) rcDest [%d %d %d %d]\n", i, SI(i)->rcDest)); DPF0(("np(%d) rcSource [%d %d %d %d]\n", i, npMCI->rcSource)); DPF0(("np(%d) rcDest [%d %d %d %d]\n", i, npMCI->rcDest )); DPF0(("\n")); IntersectRect(&SI(i)->rcSource, &SH(i).rcFrame, &npMCI->rcSource); DPF0(("SI(%d) rcSource [%d %d %d %d]\n", i, SI(i)->rcSource)); DPF0(("\n"));
//
// now map the stream source rect onto the destination
//
MapRect(&SI(i)->rcDest, &SI(i)->rcSource, &npMCI->rcSource, &npMCI->rcDest); DPF0(("SI(%d) rcSource [%d %d %d %d]\n", i, SI(i)->rcSource)); DPF0(("SI(%d) rcDest [%d %d %d %d]\n", i, SI(i)->rcDest)); DPF0(("np(%d) rcSource [%d %d %d %d]\n", i, npMCI->rcSource)); DPF0(("np(%d) rcDest [%d %d %d %d]\n", i, npMCI->rcDest )); DPF0(("\n"));
//
// make the stream source RECT (rcSource) relative to the
// stream rect (rcFrame)
//
OffsetRect(&SI(i)->rcSource,-SH(i).rcFrame.left,-SH(i).rcFrame.top); DPF0(("SI(%d) rcSource [%d %d %d %d]\n", i, SI(i)->rcSource)); DPF0(("SI(%d) rcDest [%d %d %d %d]\n", i, SI(i)->rcDest)); DPF0(("np(%d) rcSource [%d %d %d %d]\n", i, npMCI->rcSource)); DPF0(("np(%d) rcDest [%d %d %d %d]\n", i, npMCI->rcDest )); DPF0(("\n")); } }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | DevicePut | Change source or destination rectangle * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data. * * @parm LPRECT | lprc | Pointer to new rectangle to use. * * @parm DWORD | dwFlags | Flags: will be either MCI_DGV_PUT_DESTINATION * or MCI_DGV_PUT_SOURCE. * * @rdesc 0 means OK, otherwise mci error * * @comm * If we end up using a custom stretch buffer, it would go here. * ***************************************************************************/ DWORD FAR PASCAL DevicePut(NPMCIGRAPHIC npMCI, LPRECT lprc, DWORD dwFlags) { RECT rc; PRECT prcPut; DWORD dw = 0;
if (dwFlags & MCI_DGV_PUT_DESTINATION) { DPF2(("DevicePut: destination [%d, %d, %d, %d]\n", *lprc)); prcPut = &npMCI->rcDest; } else { DPF2(("DevicePut: source [%d, %d, %d, %d]\n", *lprc)); prcPut = &npMCI->rcSource;
//
// make sure source rectangle is in range.
//
// !!!should we return a error, or just fix the rectange???
//
rc = npMCI->rcMovie; IntersectRect(lprc, &rc, lprc); // fix up the passed rect.
}
//
// check for a bogus rect. either a NULL or inverted rect is considered
// invalid.
//
// !!!NOTE we should handle a inverted rect (mirrored stretch)
//
if (lprc->left >= lprc->right || lprc->top >= lprc->bottom) { DPF2(("DevicePut: invalid rectangle [%d, %d, %d, %d]\n", *lprc)); return MCIERR_OUTOFRANGE; }
/* make sure the rect changed */ if (EqualRect(prcPut,lprc)) return 0L;
InvalidateRect(npMCI->hwnd, &npMCI->rcDest, TRUE); rc = *prcPut; /* save it */ *prcPut = *lprc; /* change it */ InvalidateRect(npMCI->hwnd, &npMCI->rcDest, FALSE);
/* has both the dest and source been set? */ if (IsRectEmpty(&npMCI->rcDest) || IsRectEmpty(&npMCI->rcSource)) return 0L;
MapStreamRects(npMCI); StreamInvalidate(npMCI, NULL); // invalidate the world
if (npMCI->wTaskState <= TASKIDLE) { DPF2(("DevicePut: Idle, force DrawBegin on update\n")); npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN; } else { BOOL fRestart; //
// we dont need to start/stop just begin again.
//
DPF2(("DevicePut: Calling DrawBegin()\n")); if (!DrawBegin(npMCI, &fRestart)) { return npMCI->dwTaskError; }
if (!DoStreamUpdate(npMCI, FALSE)) { DPF(("Put: Failed update, forcing restart....\n")); npMCI->lFrameDrawn = (- (LONG) npMCI->wEarlyRecords) - 1; fRestart = TRUE; } if (fRestart) { TEMPORARYSTATE ts;
DPF2(("DevicePut: Stopping temporarily()\n"));
// !!! Set a flag here to prevent any more drawing
npMCI->fNoDrawing = TRUE;
if (StopTemporarily(npMCI, &ts) != 0) return npMCI->dwTaskError;
// !!! We used to call InitDecompress here...
npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;
RestartAgain(npMCI, &ts);
dw = npMCI->dwTaskError; } }
return dw; }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | DeviceSetPalette | Changes the override palette. * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data. * * @parm HPALETTE | hpal | New palette to use. * * @rdesc 0 means OK, otherwise mci error * ***************************************************************************/ DWORD FAR PASCAL DeviceSetPalette(NPMCIGRAPHIC npMCI, HPALETTE hpal) { //
// You might think it is a good idea to allow the app to change the
// the palette while playing; think again. This will break
// MagicSchoolBus, and cause us to get into a infinite palette fight.
//
#if 0
DWORD dw = 0L; TEMPORARYSTATE ts;
dw = StopTemporarily(npMCI, &ts);
// Remember this for later.
npMCI->hpal = hpal;
if (!dw) { npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN; dw = RestartAgain(npMCI, &ts); }
return dw; #else
if (npMCI->hpal != hpal) { // Remember this for later.
npMCI->hpal = hpal; // This won't happen until we restart the movie, so effectively, this
// request for a palette change will be ignored for now.
npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN; InvalidateRect(npMCI->hwnd, NULL, TRUE); } return 0; #endif
}
#ifndef LOADACTUALLYWORKS
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api DWORD | DeviceLoad | Load a new AVI movie. * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data. * * @parm DWORD | dwFlags | MCI flags from command. * * @rdesc 0 means OK, otherwise mci error * ***************************************************************************/
DWORD PASCAL DeviceLoad(NPMCIGRAPHIC npMCI) { DWORD dw = 0L;
if (!IsTask(npMCI->hTask)) return MCIERR_DEVICE_NOT_READY; dw = DeviceStop(npMCI, MCI_WAIT);
// Kill the current file and open a new file...
mciaviTaskMessage(npMCI, TASKRELOAD);
dw = npMCI->dwTaskError;
return dw; } #endif
|