2239 lines
61 KiB
C
2239 lines
61 KiB
C
/******************************************************************************
|
|
|
|
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
|