|
|
/******************************************************************************
Copyright (C) Microsoft Corporation 1985-1995. All rights reserved.
Title: avitask.c - Background task that actually manipulates AVI files.
*****************************************************************************/ #include "graphic.h"
STATICFN BOOL OnTask_ProcessRequest(NPMCIGRAPHIC npMCI); STATICFN void OnTask_WinProcRequests(NPMCIGRAPHIC npMCI, BOOL bPlaying); STATICFN void OnTask_StopTemporarily(NPMCIGRAPHIC npMCI); STATICFN void OnTask_RestartAgain(NPMCIGRAPHIC npMCI, BOOL bSetEvent);
STATICFN DWORD InternalPlayStart( NPMCIGRAPHIC npMCI, DWORD dwFlags, long lReqTo, long lReqFrom, DWORD_PTR dwCallback );
BOOL TryStreamUpdate( NPMCIGRAPHIC npMCI, DWORD dwFlags, HDC hdc, LPRECT lprc );
/*
* design overview under construction * * this file contains the core code for the worker thread that manages * playback on request from the user's thread. The worker thread also * creates a wndproc thread that owns the default playback window. */
// set the error flag and signal completion of request
void TaskReturns(NPMCIGRAPHIC npMCI, DWORD dwErr) { npMCI->dwReturn = dwErr;
// clear the hEventSend manual-reset event now that we
// have processed it
ResetEvent(npMCI->hEventSend);
#ifdef DEBUG
// make the message invalid
npMCI->message = 0; #endif
// Wake up the thread that made the request
DPF2(("...[%x] ok", npMCI->hRequestor)); SetEvent(npMCI->hEventResponse); }
/*
* KillWinProcThread: * * If the winproc thread exists, send a message to the window to cause * the thread to destroy the window and terminate. */ STATICFN void KillWinProcThread(NPMCIGRAPHIC npMCI) { // kill the winproc thread and wait for it to die
if (npMCI->hThreadWinproc) {
INT_PTR bOK = TRUE;
if (npMCI->hwndDefault) { // must destroy on creating thread
bOK = SendMessage(npMCI->hwndDefault, AVIM_DESTROY, 0, 0); if (!bOK) { DPF1(("failed to destroy window: %d", GetLastError())); } else { Assert(!IsWindow(npMCI->hwndDefault)); } }
// wait for winproc thread to destroy itself when the window goes
if (bOK) { WaitForSingleObject(npMCI->hThreadWinproc, INFINITE); } CloseHandle(npMCI->hThreadWinproc); npMCI->hThreadWinproc = 0;
} }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api void | mciaviTask | This function is the background task which plays * AVI files. It is called as a result of the call to mmTaskCreate() * in DeviceOpen(). When this function returns, the task is destroyed. * * @parm DWORD | dwInst | instance data passed to mmCreateTask - contains * a pointer to an instance data block. * ***************************************************************************/
void FAR PASCAL _LOADDS mciaviTask(DWORD_PTR dwInst) { NPMCIGRAPHIC npMCI; DWORD dwThreadId; BOOL bExit = FALSE;
npMCI = (NPMCIGRAPHIC) dwInst;
// Set this task's error mode to the same as the parent's.
SetErrorMode(npMCI->uErrorMode);
DPF1(("Bkgd Task hTask=%04X\n", GetCurrentTask()));
/* We must set hTask up before changing the TaskState as the UI */ /* thread can abort as soon as wTaskState is altered */ /* NB: this comment is no longer true. Since the rewrite of */ /* mciavi the UI thread will create the task thread and wait */ /* until it is explicitly signalled. */ npMCI->hTask = GetCurrentTask(); npMCI->wTaskState = TASKIDLE; npMCI->dwTaskError = 0;
// create a critical section to protect window-update code between
// the worker and the winproc thread
InitializeCriticalSection(&npMCI->HDCCritSec); SetNTFlags(npMCI, NTF_DELETEHDCCRITSEC);
// create a critical section to protect window-request code between
// the worker and the winproc thread
InitializeCriticalSection(&npMCI->WinCritSec); SetNTFlags(npMCI, NTF_DELETEWINCRITSEC);
// create an event to wait on for the winproc thread to tell us that
// init is ok.
npMCI->hEventWinProcOK = CreateEvent(NULL, FALSE, FALSE, NULL);
// also a second event that the winproc signals when it has
// requests for us
npMCI->heWinProcRequest = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!npMCI->hEventWinProcOK || !npMCI->heWinProcRequest) {
npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY; mciaviTaskCleanup(npMCI); // Abort this thread. Our waiter will wake up when our thread
// handle is signalled.
return; }
// create a second background task to create the default window and
// own the winproc.
#if 0
if (mmTaskCreate((LPTASKCALLBACK) aviWinProcTask, &npMCI->hThreadWinproc, (DWORD)(UINT)npMCI) == 0) #else
if (npMCI->hThreadWinproc = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)aviWinProcTask, (LPVOID)npMCI, 0, &dwThreadId )) #endif
{ // wait to be sure that window can be created
// hThreadWinproc will be signalled if the thread exits.
if (WaitForMultipleObjects(2, &npMCI->hThreadWinproc, FALSE, INFINITE) == WAIT_OBJECT_0) {
// thread aborted
npMCI->dwTaskError = MCIERR_CREATEWINDOW;
// dont wait for this thread in cleanup phase
CloseHandle(npMCI->hThreadWinproc); npMCI->hThreadWinproc = 0; mciaviTaskCleanup(npMCI); // Abort this thread. Our waiter will wake up when our thread
// handle is signalled.
return; } } else { // could not create winproc thread
npMCI->dwTaskError = MCIERR_CREATEWINDOW; mciaviTaskCleanup(npMCI); // Abort this thread. Our waiter will wake up when our thread
// handle is signalled.
return; }
/* Open the file */
// open has now been done on app thread -complete init here.
if (!OpenFileInit(npMCI)) { DPF1(("Failed to complete open of AVI file\n")); mciaviTaskCleanup(npMCI); // Abort this thread. Our waiter will wake up when our thread
// handle is signalled.
return; }
// signal that the open is complete
TaskReturns(npMCI, 0);
// now loop waiting for requests and processing them
// ProcessRequest returns TRUE when it is time to exit
// hEventSend is manual-reset so we can poll it during play.
// it is reset in TaskReturns just before we signal the response
// event.
// hEventAllDone is set here for bDelayedComplete requests
// (eg play+wait) when the entire request is satisfied and
// the worker thread is back to idle. hEventResponse is set in
// ProcessMessage when the request itself is complete - eg for play, once
// play has started the event will be set.
// we can't handle more than one thread potentially blocking on
// hEventAllDone at one time, so while one thread has made a request
// that could block on hEventAllDone, no other such request is permitted
// from other threads. In other words, while one (user) thread has
// requested a play+wait, other threads can request stop, but not
// play + wait.
while (!bExit) { DWORD dwObject;
npMCI->wTaskState = TASKIDLE;
#ifdef DEBUG
// A complex assertion. If we have stopped temporarily, then we
// do not want to go back to sleep.
if ((npMCI->dwFlags & MCIAVI_UPDATING) && (WAIT_TIMEOUT == WaitForMultipleObjects(IDLEWAITFOR, &npMCI->hEventSend, FALSE, 0)) ) { Assert(!"About to go to sleep when we should be restarting!"); } #endif
// the OLE guys are kind enough to create windows on this thread.
// so we need to handle sent messages here to avoid deadlocks.
// same is true of the similar loop in BePaused()
do { #ifdef DEBUG
if (npMCI->hWaiter) { DPF(("About to call MsgWaitForMultipleObjects while hWaiter=%x\n", npMCI->hWaiter)); } #endif
dwObject = MsgWaitForMultipleObjects(IDLEWAITFOR, &npMCI->hEventSend, FALSE, INFINITE, QS_SENDMESSAGE);
DPF2(("Task woken up, dwObject=%x, hWaiter=%x\n", dwObject, npMCI->hWaiter));
if (dwObject == WAIT_OBJECT_0 + IDLEWAITFOR) { MSG msg;
// just a single peekmessage with NOREMOVE will
// process the inter-thread send and not affect the queue
PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); } } while (dwObject == WAIT_OBJECT_0 + IDLEWAITFOR);
switch (dwObject) { case WAIT_OBJECT_0: // check that the message has actually been set
Assert(npMCI->message != 0);
if (npMCI->bDelayedComplete) { if (npMCI->hWaiter && (npMCI->hWaiter != npMCI->hRequestor)) { TaskReturns(npMCI, MCIERR_DEVICE_NOT_READY); continue; } else { DPF2(("Setting hWaiter to %x\n", npMCI->hRequestor)); npMCI->hWaiter = npMCI->hRequestor; } } DPF2(("get %d [%x]...", npMCI->message, npMCI->hRequestor));
// we must reset this event here, or OnTask_PeekRequest will
// think it is a new request and will process and
// potentially complete it!!
ResetEvent(npMCI->hEventSend);
bExit = OnTask_ProcessRequest(npMCI);
break;
case WAIT_OBJECT_0+1: default: //
// winproc request to do something to the task - while idle
//
#ifdef DEBUG
if (dwObject != WAIT_OBJECT_0+1) { DPF2(("dwObject == %d\n", dwObject)); } #endif
Assert(dwObject == WAIT_OBJECT_0+1); OnTask_WinProcRequests(npMCI, FALSE);
// this request may have resulted in a temporary stop - so we
// need to restart
if (npMCI->dwFlags & MCIAVI_UPDATING) { OnTask_RestartAgain(npMCI, FALSE); }
}
// if we have stopped temporarily to restart with new params,
// then don't signal completion. However if we did restart
// and now everything is quiescent, signal any waiter that happens
// to be around. This code is common to both the winproc request
// and user request legs, as it is possible to stop and restart
// from both winproc and user requests.
if (npMCI->hWaiter && (!(npMCI->dwFlags & MCIAVI_UPDATING))) { SetEvent(npMCI->hEventAllDone); } else { if (npMCI->hWaiter) { DPF2(("Would have Set hEventAllDone, but am updating\n")); } }
// QUERY: if we had processed all the requests, and therefore the
// two events on which we were waiting had been reset, AND
// MCIAVI_UPDATING is set (due to a temporary stop) then perhaps
// we ought to call OnTask_RestartAgain here. This would mean that
// all the ugly RestartAgain calls within OnTask_ProcessRequest
// could be removed.
}
// be sure to wake him up before cleanup just in case
if (npMCI->hWaiter) { DPF2(("Setting hEventAllDone before closing\n")); SetEvent(npMCI->hEventAllDone); }
// close the window and destroy the winproc thread before any other
// cleanup, so that paints or realizes don't happen during
// a partially closed state
KillWinProcThread(npMCI);
mciaviCloseFile(npMCI);
mciaviTaskCleanup(npMCI); return;
}
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api void | mciaviTaskCleanup | called when the background task * is being destroyed. This is where critical cleanup goes. * ***************************************************************************/
void FAR PASCAL mciaviTaskCleanup(NPMCIGRAPHIC npMCI) { npMCI->hTask = 0;
/* Closing the driver causes any currently saved notifications */ /* to abort. */
GraphicDelayedNotify(npMCI, MCI_NOTIFY_ABORTED);
GdiFlush();
// if still alive, kill the winproc thread and wait for it to die
KillWinProcThread(npMCI);
// free winproc <-> worker thread communication resources
if (npMCI->hEventWinProcOK) { CloseHandle(npMCI->hEventWinProcOK); } if (npMCI->heWinProcRequest) { CloseHandle(npMCI->heWinProcRequest); }
if (TestNTFlags(npMCI, NTF_DELETEWINCRITSEC)) { DeleteCriticalSection(&npMCI->WinCritSec); }
if (TestNTFlags(npMCI, NTF_DELETEHDCCRITSEC)) { DeleteCriticalSection(&npMCI->HDCCritSec); }
//
// call a MSVideo shutdown routine.
//
}
// task message functions
// utility functions called on worker thread
/***************************************************************************
* * @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)) return;
// don't show stage if we are working in response to a winproc
// update request, as this could cause deadlock and is any case
// pointless - if the window is now hidden, we can't possibly need
// to paint it!
if (npMCI->bDoingWinUpdate) { return; }
if ((npMCI->dwFlags & MCIAVI_SHOWVIDEO) && npMCI->hwndPlayback == npMCI->hwndDefault && ////////////!(GetWindowLong(npMCI->hwnd, GWL_STYLE) & WS_CHILD) &&
(!IsWindowVisible (npMCI->hwndPlayback) || npMCI->hwndPlayback != GetActiveWindow ())) {
// if this is our window, then we need to show it
// ourselves
if (npMCI->hwndDefault == npMCI->hwndPlayback) { WinCritCheckOut(npMCI); PostMessage(npMCI->hwndPlayback, AVIM_SHOWSTAGE, 0, 0); } else { SetWindowPos(npMCI->hwndPlayback, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | (IsWindowVisible(npMCI->hwndPlayback) ? SWP_NOACTIVATE : 0)); } }
//
// 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->hwndPlayback == npMCI->hwndDefault && !(GetWindowLong(npMCI->hwndPlayback, GWL_STYLE) & WS_CHILD)) { // if this is our window, then we need to show it
// ourselves
if (npMCI->hwndDefault == npMCI->hwndPlayback) {
// force activation even if visible
WinCritCheckOut(npMCI); PostMessage(npMCI->hwndPlayback, AVIM_SHOWSTAGE, TRUE, 0); } else { SetActiveWindow(npMCI->hwndPlayback); } }
npMCI->dwFlags &= ~(MCIAVI_NEEDTOSHOW); }
//
// called to save state if we stop temporarily to change something.
// we will restart with OnTask_RestartAgain. Called on worker thread from
// somewhere in aviTaskCheckRequests
STATICFN void OnTask_StopTemporarily(NPMCIGRAPHIC npMCI) { // save old state and flags
npMCI->oldState = npMCI->wTaskState; npMCI->oldTo = npMCI->lTo; npMCI->oldFrom = npMCI->lFrom; npMCI->oldFlags = npMCI->dwFlags; npMCI->oldCallback = (DWORD_PTR) npMCI->hCallback;
npMCI->dwFlags |= (MCIAVI_UPDATING | MCIAVI_STOP); DPF(("StopTemp: OldState=%d, oldTo=%d, oldFrom=%d, oldFlags=%8x\n", npMCI->oldState, npMCI->oldTo, npMCI->oldFrom, npMCI->oldFlags)); }
// called from worker thread on completion of a (idle-time) request
// to restart a suspended play function
//
// responsible for signalling hEventResponse once the restart is complete
// (or failed).
STATICFN void OnTask_RestartAgain(NPMCIGRAPHIC npMCI, BOOL bSetEvent) { DWORD dwErr; DWORD dwFlags = 0; long lFrom; UINT wNotify;
// we're restarting after a temporary stop- so clear the flag.
// also turn off REPEATING - we might reset this in a moment
npMCI->dwFlags &= ~(MCIAVI_UPDATING | MCIAVI_REPEATING);
// Turn on the repeating flag if it was originally set
npMCI->dwFlags |= (npMCI->oldFlags & MCIAVI_REPEATING);
if (npMCI->oldFlags & MCIAVI_REVERSE) { dwFlags |= MCI_DGV_PLAY_REVERSE; }
switch (npMCI->oldState) { case TASKPAUSED: // get to the old From frame and pause when you get there
npMCI->dwFlags |= MCIAVI_PAUSE; // Make sure we end up paused
// NOTE: InternalPlayStart no longer clears the PAUSE flag
lFrom = npMCI->oldFrom; break;
case TASKCUEING:
// npMCI->dwFlags should still say whether to pause at
// end of cueing or play
lFrom = npMCI->oldFrom; dwFlags |= MCI_TO; // Stop when we get to the right frame
break;
case TASKPLAYING:
lFrom = npMCI->lCurrentFrame; dwFlags |= MCI_TO; break;
default:
DPF(("asked to restart to idle (%d) state", npMCI->oldState)); if (bSetEvent) { TaskReturns(npMCI, 0); } return; }
DPF2(("RestartAgain calling InternalPlayStart, flags=%8x\n",dwFlags)); dwErr = InternalPlayStart(npMCI, dwFlags, npMCI->oldTo, lFrom, npMCI->oldCallback);
if (bSetEvent && dwErr) { TaskReturns(npMCI, dwErr); }
if (!dwErr) { wNotify = mciaviPlayFile(npMCI, bSetEvent);
// if we stopped to pick up new params without actually completing the
// the play (OnTask_StopTemporarily) then MCIAVI_UPDATING will be set
if (! (npMCI->dwFlags & MCIAVI_UPDATING)) { // perform any notification
if (wNotify != MCI_NOTIFY_FAILURE) { GraphicDelayedNotify(npMCI, wNotify); } } } }
/***************************************************************************
* * 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) { return (WindowFromDC(hdc) != NULL); } #endif
// called from several task side functions to initiate play
// when stopped. All you need to do is call mciaviPlayFile
// once this function returns
STATICFN DWORD InternalPlayStart( NPMCIGRAPHIC npMCI, DWORD dwFlags, long lReqTo, long lReqFrom, DWORD_PTR dwCallback ) { long lTo, lFrom; DWORD dwRet;
if (dwFlags & (MCI_MCIAVI_PLAY_FULLSCREEN | MCI_MCIAVI_PLAY_FULLBY2)) { // do nothing here - handled in fullproc
} else { if (!IsWindow(npMCI->hwndPlayback)) { return MCIERR_NO_WINDOW; }
npMCI->dwFlags |= MCIAVI_NEEDTOSHOW; }
/* Range checks : 0 < 'from' <= 'to' <= last frame */
if (dwFlags & MCI_TO) { lTo = lReqTo;
if (lTo < 0L || lTo > npMCI->lFrames) { return MCIERR_OUTOFRANGE; } } else { if (dwFlags & MCI_DGV_PLAY_REVERSE) lTo = 0; else lTo = npMCI->lFrames;
dwFlags |= MCI_TO; }
// if no from setting, then get current position
if (dwFlags & MCI_FROM) { lFrom = lReqFrom;
if (lFrom < 0L || lFrom > npMCI->lFrames) { return MCIERR_OUTOFRANGE; } } else if (dwRet = InternalGetPosition(npMCI, &lFrom)) { return dwRet; }
/* check 'to' and 'from' relationship. */ if (lTo < lFrom) dwFlags |= MCI_DGV_PLAY_REVERSE;
if ((lFrom < lTo) && (dwFlags & MCI_DGV_PLAY_REVERSE)) { return MCIERR_OUTOFRANGE; }
/* If the test flag is set, return without doing anything. */ /* Question: do we have to check for more possible errors? */ if (dwFlags & MCI_TEST) { return 0; }
npMCI->lFrom = lFrom;
if (dwFlags & MCI_DGV_PLAY_REPEAT) { /* If from position isn't given, repeat from either the beginning or
** end of file as appropriate. */ npMCI->lRepeatFrom = (dwFlags & MCI_FROM) ? lFrom : ((dwFlags & MCI_DGV_PLAY_REVERSE) ? npMCI->lFrames : 0); }
/* if not already playing, start the task up. */
/* 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); }
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); }
// Make sure flags are cleared if they should be
//npMCI->dwFlags &= ~(MCIAVI_PAUSE | MCIAVI_CUEING | MCIAVI_REVERSE);
// PAUSE is NOT turned off, otherwise we cannot RestartAgain to a
// paused state.
npMCI->dwFlags &= ~(MCIAVI_CUEING | MCIAVI_REVERSE);
if (dwFlags & MCI_DGV_PLAY_REPEAT) { npMCI->dwFlags |= MCIAVI_REPEATING; }
/* Don't set up notify until here, so that the seek won't make it happen*/ // idle so no current notify
if (dwFlags & MCI_NOTIFY) { GraphicSaveCallback(npMCI, (HANDLE) (UINT_PTR)dwCallback); }
if (lTo > npMCI->lFrames) lTo = npMCI->lFrames;
if (lTo < 0) lTo = 0;
if (dwFlags & MCI_TO) npMCI->lTo = lTo;
DPF2(("InternalPlayStart Flags=%8x, ReqTo=%d ReqFrom=%d To=%d\n", dwFlags, lReqTo, lReqFrom, lTo));
if (dwFlags & MCI_DGV_PLAY_REVERSE) npMCI->dwFlags |= MCIAVI_REVERSE;
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 an internal 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; }
return 0;
}
// called at task idle time to initiate a play request -
// the worker thread is NOT busy playing, seeking, cueing or paused
// at this point.
//
// responsible for calling TaskReturns() appropriately.
void OnTask_Play(NPMCIGRAPHIC npMCI) {
DWORD dwRet; DWORD dwMCIFlags = npMCI->dwParamFlags; LPMCI_DGV_PLAY_PARMS lpPlay = (LPMCI_DGV_PLAY_PARMS)npMCI->lParam; long lTo, lFrom; UINT wNotify;
if (lpPlay != NULL) { lTo = lpPlay->dwTo; lFrom = lpPlay->dwFrom; } else { dwMCIFlags &= ~(MCI_TO | MCI_FROM); }
npMCI->dwFlags &= ~MCIAVI_REPEATING;
// need to convert to frames before calling InternalPlayStart
if (dwMCIFlags & MCI_TO) { lTo = ConvertToFrames(npMCI, lTo); } if (dwMCIFlags & MCI_FROM) { lFrom = ConvertToFrames(npMCI, lFrom); }
dwRet = InternalPlayStart(npMCI, dwMCIFlags, lTo, lFrom, npMCI->dwReqCallback);
if (dwRet || (dwMCIFlags & MCI_TEST)) { TaskReturns(npMCI, dwRet); return; }
// actually play the file
wNotify = mciaviPlayFile(npMCI, TRUE);
// if we stopped to pick up new params without actually completing the
// the play (OnTask_StopTemporarily) then MCIAVI_UPDATING will be set
if (! (npMCI->dwFlags & MCIAVI_UPDATING)) { // perform any notification
if (wNotify != MCI_NOTIFY_FAILURE) { GraphicDelayedNotify(npMCI, wNotify); } }
return; }
//
// called to process a play request when play is actually happening.
// if parameters can be adjusted without stopping the current play,
// returns FALSE. Also if the request is rejected (and hEventResponse
// signalled) because of some error, returns FALSE indicating no need to
// stop. Otherwise returns TRUE, so that OnTask_Play() will
// be called after stopping the current play.
BOOL OnTask_PlayDuringPlay(NPMCIGRAPHIC npMCI) {
DWORD dwFlags = npMCI->dwParamFlags; LPMCI_DGV_PLAY_PARMS lpPlay = (LPMCI_DGV_PLAY_PARMS)npMCI->lParam; long lTo, lFrom; DWORD dwRet;
// since this is a real play request coming from the user we need
// to show the stage window
if (dwFlags & (MCI_MCIAVI_PLAY_FULLSCREEN | MCI_MCIAVI_PLAY_FULLBY2)) { // do nothing here - handled in fullproc
} else { npMCI->dwFlags |= MCIAVI_NEEDTOSHOW; }
// can be called with null lpPlay (in the resume case)
// in this case, to and from will be left unchanged
// if you pass lpPlay, then to and from will be set to defaults even
// if you don't set MCI_TO and MCI_FROM
if (lpPlay == NULL) { dwFlags &= ~(MCI_TO | MCI_FROM); }
/* Range checks : 0 < 'from' <= 'to' <= last frame */
if (dwFlags & MCI_TO) { lTo = ConvertToFrames(npMCI, lpPlay->dwTo);
if (lTo < 0L || lTo > npMCI->lFrames) { TaskReturns(npMCI, MCIERR_OUTOFRANGE); return FALSE; } } else { // don't touch to and from for resume
if (lpPlay) { if (dwFlags & MCI_DGV_PLAY_REVERSE) lTo = 0; else lTo = npMCI->lFrames;
dwFlags |= MCI_TO; } else { lTo = npMCI->lTo; } }
// if no from setting, then get current position
if (dwFlags & MCI_FROM) { lFrom = ConvertToFrames(npMCI, lpPlay->dwFrom);
if (lFrom < 0L || lFrom > npMCI->lFrames) { TaskReturns(npMCI, MCIERR_OUTOFRANGE); return FALSE; } } else if (dwRet = InternalGetPosition(npMCI, &lFrom)) { TaskReturns(npMCI, dwRet); return FALSE; }
/* check 'to' and 'from' relationship. */ if (lTo < lFrom) dwFlags |= MCI_DGV_PLAY_REVERSE;
if ((lFrom < lTo) && (dwFlags & MCI_DGV_PLAY_REVERSE)) { TaskReturns(npMCI, MCIERR_OUTOFRANGE); return FALSE; }
/* If the test flag is set, return without doing anything. */ /* Question: do we have to check for more possible errors? */ if (dwFlags & MCI_TEST) { TaskReturns(npMCI, 0L); return FALSE; }
/* We want any previous playing to be aborted if and only if a 'from'
** parameter is specified. If only a new 'to' parameter is specified, ** we can just change the 'to' value, and play will stop at the ** proper time. ** ** Also abort the play if we have lost the audio. An explicit play ** command has been issued and we should try and get the audio again. */
// if it's new from position or we are seeking to the wrong stop,
// or we are reversing the direction of play,
// or we had lost the audio
// then we need to stop.
if ( (dwFlags & MCI_FROM) || (dwFlags & (MCI_MCIAVI_PLAY_FULLSCREEN | MCI_MCIAVI_PLAY_FULLBY2)) || ((npMCI->dwFlags & MCIAVI_SEEKING) && (npMCI->lTo != lFrom)) || (npMCI->wTaskState == TASKCUEING) || (npMCI->dwFlags & MCIAVI_LOSTAUDIO) || (((npMCI->dwFlags & MCIAVI_REVERSE) != 0) != ((dwFlags & MCI_DGV_PLAY_REVERSE) != 0)) ) {
// we can't continue the play - we have to stop, and then pick up
// this request again in OnTask_Play().
// this will abort the current notify
return TRUE; }
// ok to continue the current play with revised params.
// set the from position correctly
npMCI->lFrom = lFrom;
/* If we're changing the "to" position, abort any pending notify. */ if (lTo != npMCI->lTo) { GraphicDelayedNotify (npMCI, MCI_NOTIFY_ABORTED); } /* Don't set up notify until here, so that the seek won't make it happen*/ if (dwFlags & MCI_NOTIFY) { GraphicSaveCallback(npMCI, (HANDLE) (UINT_PTR)npMCI->dwReqCallback); }
// Make sure flags are cleared if they should be
npMCI->dwFlags &= ~(MCIAVI_PAUSE | MCIAVI_CUEING | MCIAVI_REVERSE | MCIAVI_LOSEAUDIO);
/* Set up the 'repeat' flags */ npMCI->dwFlags &= ~(MCIAVI_REPEATING);
if (dwFlags & MCI_DGV_PLAY_REPEAT) { /* If from position isn't given, repeat from either the beginning or
** end of file as appropriate. ** ** if no lpPlay is given, then don't change repeatfrom */
if (lpPlay) { npMCI->lRepeatFrom = (dwFlags & MCI_FROM) ? lFrom : ((dwFlags & MCI_DGV_PLAY_REVERSE) ? npMCI->lFrames : 0); } npMCI->dwFlags |= MCIAVI_REPEATING; }
/* if not already playing, start the task up. */
if (lTo > npMCI->lFrames) lTo = npMCI->lFrames;
if (lTo < 0) lTo = 0;
if (dwFlags & MCI_TO) npMCI->lTo = lTo;
if (dwFlags & MCI_DGV_PLAY_REVERSE) npMCI->dwFlags |= MCIAVI_REVERSE;
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 an internal 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; }
//everything adjusted - tell user ok and return to playing
TaskReturns(npMCI, 0); return FALSE;
}
void OnTask_Realize(NPMCIGRAPHIC npMCI) { DWORD dw;
EnterHDCCrit(npMCI); dw = InternalRealize(npMCI); LeaveHDCCrit(npMCI); TaskReturns(npMCI, dw);
}
DWORD InternalRealize(NPMCIGRAPHIC npMCI) { BOOL fGetDC; BOOL fPalChanged; #ifndef _WIN32
BOOL fAlreadyDoneThat; #endif
HDCCritCheckIn(npMCI); if (npMCI->dwFlags & MCIAVI_WANTMOVE) CheckWindowMove(npMCI, TRUE);
#ifndef _WIN32
if (fAlreadyDoneThat = (BOOL)(npMCI->dwFlags & MCIAVI_UPDATING)) { DPF(("Re-entering InternalRealize - but we don't care, npMCI=%8x\n",npMCI)); } #endif
if (!IsTask(npMCI->hTask)) return(0L);
if (fGetDC = (npMCI->hdc == NULL)) { npMCI->hdc = GetDC(npMCI->hwndPlayback); Assert(npMCI->hdc != NULL); }
#ifndef _WIN32
// this only prevents playback window alignment - which is not done
// for NT anyway
npMCI->dwFlags |= MCIAVI_UPDATING; #endif
fPalChanged = PrepareDC(npMCI) > 0;
#ifndef _WIN32
if (!fAlreadyDoneThat) npMCI->dwFlags &= ~MCIAVI_UPDATING; #endif
if (fGetDC) { UnprepareDC(npMCI); ReleaseDC(npMCI->hwndPlayback, npMCI->hdc); npMCI->hdc = NULL; HDCCritCheckIn(npMCI); }
if (fPalChanged) InvalidateRect(npMCI->hwndPlayback, &npMCI->rcDest, TRUE);
CheckIfActive(npMCI);
return 0L; }
void OnTask_Update(NPMCIGRAPHIC npMCI) { RECT rc; LPMCI_DGV_UPDATE_PARMS lpParms = (LPMCI_DGV_UPDATE_PARMS) npMCI->lParam; DWORD dwFlags = npMCI->dwFlags; DWORD dwErr;
rc.left = lpParms->ptOffset.x; rc.top = lpParms->ptOffset.y; rc.right = lpParms->ptOffset.x + lpParms->ptExtent.x; rc.bottom = lpParms->ptOffset.y + lpParms->ptExtent.y;
dwErr = Internal_Update (npMCI, dwFlags, lpParms->hDC, (dwFlags & MCI_DGV_RECT) ? &rc : NULL);
//now, where were we ?
if (!dwErr && (npMCI->dwFlags & MCIAVI_UPDATING)) { OnTask_RestartAgain(npMCI, TRUE); } else { TaskReturns(npMCI, dwErr); } }
BOOL OnTask_UpdateDuringPlay(NPMCIGRAPHIC npMCI) { RECT userrc, rc; LPMCI_DGV_UPDATE_PARMS lpParms = (LPMCI_DGV_UPDATE_PARMS) npMCI->lParam; DWORD dwFlags = npMCI->dwFlags; HDC hdc = lpParms->hDC;
userrc.left = lpParms->ptOffset.x; userrc.top = lpParms->ptOffset.y; userrc.right = lpParms->ptOffset.x + lpParms->ptExtent.x; userrc.bottom = lpParms->ptOffset.y + lpParms->ptExtent.y;
//
// mark the proper streams dirty, this will set the proper update flags
//
if (hdc) GetClipBox(hdc, &rc); else rc = npMCI->rcDest;
if (dwFlags & MCI_DGV_RECT) IntersectRect(&rc, &rc, &userrc);
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;
// we are playing now (we have a dc). just realize
// the palette and set the update flag
// unless we are painting to a memory dc.
//
// 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);
EnterHDCCrit(npMCI); UnprepareDC(npMCI); PrepareDC(npMCI); // re-prepare
LeaveHDCCrit(npMCI);
// all ok - no need for stop.
TaskReturns(npMCI, 0); return FALSE; }
// try to use DoStreamUpdate - if this fails, we need to stop
if (TryStreamUpdate(npMCI, dwFlags, hdc, (dwFlags & MCI_DGV_RECT) ? &userrc : NULL)) {
// we are playing and so have an hdc. However, we have just
// done a update to another hdc. switching back to the original
// hdc without this will fail
PrepareDC(npMCI);
TaskReturns(npMCI, 0); return FALSE; }
// otherwise we need to stop to do this
// indicate that we should restart after doing this, and
// save enough state to do this
OnTask_StopTemporarily(npMCI);
return TRUE; }
// attempt repaint using DoStreamUpdate - if this fails (eg wrong frame)
// then you need to use mciaviPlayFile to do it (to/from same frame)
BOOL TryStreamUpdate( NPMCIGRAPHIC npMCI, DWORD dwFlags, HDC hdc, LPRECT lprc ) { HDC hdcSave; BOOL f;
//
// 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 stupid 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) { Assert(hdc); 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 need to re-draw using play.
//
// 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.
//
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. */
// worker thread must hold critsec round all drawing
EnterHDCCrit(npMCI); 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; HDCCritCheckIn(npMCI); npMCI->lFrameDrawn = (- (LONG) npMCI->wEarlyRecords) - 1; LeaveHDCCrit(npMCI); return FALSE; // need to use play
} }
PrepareDC(npMCI); // make sure the palette is in there
f = DoStreamUpdate(npMCI, FALSE);
UnprepareDC(npMCI); // be sure to put things back....
Assert(hdc == npMCI->hdc); HDCCritCheckIn(npMCI); npMCI->hdc = hdcSave; LeaveHDCCrit(npMCI);
if (!f) { DPF(("DeviceUpdate failed! invalidating lFrameDrawn\n")); npMCI->lFrameDrawn = (- (LONG) npMCI->wEarlyRecords) - 1; Assert(!lprc); } else if (npMCI->lFrameDrawn >= npMCI->lCurrentFrame-1) { 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 TRUE; } //return FALSE; Drop through
}
return FALSE;
}
// called in stopped case to paint from OnTask_Update, and
// also on winproc thread (when stopped). Not called during play.
DWORD Internal_Update(NPMCIGRAPHIC npMCI, DWORD dwFlags, HDC hdc, LPRECT lprc) { DWORD dwErr = 0L; HWND hCallback; HCURSOR hcurPrev; RECT rc; LONG lFrameDrawn;
if (npMCI->dwFlags & MCIAVI_WANTMOVE) CheckWindowMove(npMCI, TRUE);
//
// see if we are the active movie now.
//
CheckIfActive(npMCI);
//
// 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;
lFrameDrawn = npMCI->lFrameDrawn; // save this for compare
// try to use DoStreamUpdate
if (TryStreamUpdate(npMCI, dwFlags, hdc, lprc)) { return 0; }
// no - need to use Play
// note we are already stopped at this point.
//
// 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. */ EnterHDCCrit(npMCI); npMCI->hdc = hdc; PrepareDC(npMCI); // make sure the palette is in there
LeaveHDCCrit(npMCI);
hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
/* Hide any notification, so it won't get sent... */ hCallback = npMCI->hCallback; npMCI->hCallback = NULL;
mciaviPlayFile(npMCI, FALSE);
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); }
//HDCCritCheckIn(npMCI) ??? This is an atomic operation - and
// why are we setting it to NULL here ??
npMCI->hdc = NULL;
if (lprc) { RestoreDC(hdc, -1); } npMCI->dwFlags &= ~(MCIAVI_UPDATETOMEMORY);
if (npMCI->dwFlags & MCIAVI_NEEDUPDATE) { DPF(("**** we did a DeviceUpdate but still dirty?\n")); }
return dwErr; }
void OnTask_PauseDuringPlay(NPMCIGRAPHIC npMCI) { DWORD dwFlags = npMCI->dwParamFlags; DPF3(("Pause during play\n"));
// no pause during cueing
if (npMCI->wTaskState == TASKCUEING) { // leave event sent - wait till later
return; }
// save the notify
if (dwFlags & MCI_NOTIFY) { GraphicSaveCallback(npMCI, (HANDLE) (UINT_PTR)npMCI->dwReqCallback); }
// what about delayed completion pause ?
// especially "pause" followed by "pause wait"
if (dwFlags & MCI_WAIT) { // indicate hEventAllDone should be set on Pause, not
// on idle (ie at final stop)
npMCI->dwFlags |= MCIAVI_WAITING; }
if (npMCI->wTaskState == TASKPAUSED) { // all done already
if (dwFlags & MCI_NOTIFY) { GraphicDelayedNotify(npMCI, MCI_NOTIFY_SUCCESSFUL); }
} else if (npMCI->wTaskState == TASKPLAYING) {
// remember to pause
npMCI->dwFlags |= MCIAVI_PAUSE;
if (dwFlags & MCI_NOTIFY) { // remember to send a notify when we pause
npMCI->dwFlags |= MCIAVI_CUEING; } }
TaskReturns(npMCI, 0); }
void OnTask_Cue(NPMCIGRAPHIC npMCI, DWORD dwFlags, long lTo) { UINT wNotify;
DPF3(("OnTask_Cue: dwFlags=%8x, To=%d\n", dwFlags, lTo));
GraphicDelayedNotify(npMCI, MCI_NOTIFY_ABORTED);
if (dwFlags & MCI_NOTIFY) { GraphicSaveCallback(npMCI, (HANDLE) (UINT_PTR)npMCI->dwReqCallback); }
/* Clear the 'repeat' flags */ npMCI->dwFlags &= ~(MCIAVI_REPEATING);
if (dwFlags & MCI_TO) { npMCI->lFrom = lTo; } else if (npMCI->wTaskState == TASKIDLE) { npMCI->lFrom = npMCI->lCurrentFrame; }
/* If we're ever resumed, we want to go to the end of the file. */ npMCI->lTo = npMCI->lFrames;
npMCI->dwFlags |= MCIAVI_PAUSE | MCIAVI_CUEING;
if (dwFlags & MCI_WAIT) { npMCI->dwFlags |= MCIAVI_WAITING; }
wNotify = mciaviPlayFile(npMCI, TRUE);
// if we stopped to pick up new params without actually completing the
// the play (OnTask_StopTemporarily) then MCIAVI_UPDATING will be set
if (! (npMCI->dwFlags & MCIAVI_UPDATING)) { // perform any notification
if (wNotify != MCI_NOTIFY_FAILURE) { GraphicDelayedNotify(npMCI, wNotify); } } }
BOOL OnTask_CueDuringPlay(NPMCIGRAPHIC npMCI) { DWORD dw = 0L; DWORD dwFlags = npMCI->dwParamFlags; long lTo = (LONG) npMCI->lParam;
DPF3(("OnTask_CueDuringPlay\n"));
if (npMCI->dwFlags & MCIAVI_SEEKING) { /* We're currently seeking, so we have to start again to get audio
** to work. */ return TRUE; }
if (dwFlags & MCI_TO) { return TRUE; }
/* Clear the 'repeat' flags */ npMCI->dwFlags &= ~(MCIAVI_REPEATING);
GraphicDelayedNotify(npMCI, MCI_NOTIFY_ABORTED);
if (dwFlags & MCI_NOTIFY) { GraphicSaveCallback(npMCI, (HANDLE) (UINT_PTR)npMCI->dwReqCallback); }
/* 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);
// complete completed
TaskReturns(npMCI, 0);
// delayed completion is also done!
if (dwFlags & MCI_WAIT) { SetEvent(npMCI->hEventAllDone); }
// don't drop through to the second TaskReturns() below!
return FALSE;
} else if ((npMCI->wTaskState == TASKCUEING) || (npMCI->wTaskState == TASKPLAYING)) {
// ask for pause on completion of cueing/playing,
// and for notify and hEventAllDone when pause reached
npMCI->dwFlags |= MCIAVI_PAUSE | MCIAVI_CUEING;
if (dwFlags & MCI_WAIT) { npMCI->dwFlags |= MCIAVI_WAITING; }
} else { TaskReturns (npMCI, MCIERR_NONAPPLICABLE_FUNCTION); return FALSE; }
TaskReturns(npMCI, 0); return FALSE; }
void OnTask_Seek(NPMCIGRAPHIC npMCI) { UINT wNotify; DWORD dwFlags = npMCI->dwParamFlags; long lTo = (long) npMCI->lParam;
DPF3(("DeviceSeek - to frame %d (CurrentFrame==%d) Current State is %d\n", lTo, npMCI->lCurrentFrame, npMCI->wTaskState)); /* The window will be shown by the play code. */
// task state is now TASKIDLE and blocked
if (dwFlags & MCI_NOTIFY) { GraphicSaveCallback(npMCI, (HANDLE) (UINT_PTR)npMCI->dwReqCallback); }
/* Clear the 'repeat' flags */ npMCI->dwFlags &= ~(MCIAVI_REPEATING);
if (npMCI->lCurrentFrame != lTo) {
/* 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;
wNotify = mciaviPlayFile(npMCI, TRUE);
// if we stopped to pick up new params without actually completing the
// the play (OnTask_StopTemporarily) then MCIAVI_UPDATING will be set
if (! (npMCI->dwFlags & MCIAVI_UPDATING)) { // perform any notification
if (wNotify != MCI_NOTIFY_FAILURE) { GraphicDelayedNotify(npMCI, wNotify); } }
} else { // task complete
TaskReturns(npMCI, 0);
/* 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); } }
OnTask_SeekDuringPlay(NPMCIGRAPHIC npMCI) { long lTo = (long) npMCI->lParam; DWORD dwFlags = npMCI->dwParamFlags;
DPF3(("DeviceSeek - to frame %d (CurrentFrame==%d) Current State is %d\n", lTo, npMCI->lCurrentFrame, npMCI->wTaskState)); /* 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)) {
npMCI->lTo = lTo;
/* Clear the 'repeat' flags */ npMCI->dwFlags &= ~(MCIAVI_REPEATING);
GraphicDelayedNotify (npMCI, MCI_NOTIFY_ABORTED);
if (dwFlags & MCI_NOTIFY) { GraphicSaveCallback(npMCI, (HANDLE) (UINT_PTR)npMCI->dwReqCallback); }
TaskReturns(npMCI, 0); return FALSE; }
// we have to stop to do this seek
return TRUE; }
void OnTask_SetWindow(NPMCIGRAPHIC npMCI) {
npMCI->hwndPlayback = (HWND) npMCI->lParam;
npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN; InvalidateRect(npMCI->hwndPlayback, &npMCI->rcDest, FALSE);
/* Should we update the window here? */
/* Start playing again in the new window (if we had to stop) */
//now, where were we ?
if (npMCI->dwFlags & MCIAVI_UPDATING) { OnTask_RestartAgain(npMCI, TRUE); } else { TaskReturns(npMCI, 0); } }
void OnTask_SetSpeed(NPMCIGRAPHIC npMCI) { npMCI->dwSpeedFactor = (DWORD)npMCI->lParam;
// if we stopped to do this, then restart whatever we were doing
if (npMCI->dwFlags & MCIAVI_UPDATING) { OnTask_RestartAgain(npMCI, TRUE); } else { TaskReturns(npMCI, 0); } }
BOOL OnTask_SetSpeedDuringPlay(NPMCIGRAPHIC npMCI) { /* If new speed is the same as the old speed, done. */ if ((DWORD)npMCI->lParam == npMCI->dwSpeedFactor) { TaskReturns(npMCI, 0); return FALSE; }
// otherwise we have to stop and restart
OnTask_StopTemporarily(npMCI); return TRUE; }
void OnTask_WaveSteal(NPMCIGRAPHIC npMCI) {
DPF2(("OnTask_WaveSteal, '%ls' hTask=%04X\n", (LPSTR)npMCI->szFilename, npMCI->hTask));
// if we stopped to do this, then restart whatever we were doing
if (npMCI->dwFlags & MCIAVI_UPDATING) { // We stopped to do this...
EnterWinCrit(npMCI);
// Turn the lose audio flag on so that when we restart we do not
// try and pick up the wave device. The flag will be reset in
// SetUpAudio.
npMCI->dwFlags |= MCIAVI_LOSEAUDIO;
// Hint that we would like sound again
npMCI->dwFlags |= MCIAVI_LOSTAUDIO;
LeaveWinCrit(npMCI); OnTask_RestartAgain(npMCI, TRUE);
Assert(!(npMCI->dwFlags & MCIAVI_LOSEAUDIO)); // The flag has been reset by SetUpAudio
// By using MCIAVI_LOSEAUDIO we do not have to change the state of
// the MCIAVI_PLAYAUDIO flag. This is goodness as that flag controls
// the mute state - and that is independent of the availability of a
// wave device, activation and/or deactivation.
} else { TaskReturns(npMCI, 0); } }
void OnTask_WaveReturn(NPMCIGRAPHIC npMCI) {
// Turn off the flag that caused us to get called.
// Note: if the audio device is still unavailable, this flag will get
// turned on again when we fail to open the device.
npMCI->dwFlags &= ~MCIAVI_LOSTAUDIO;
DPF2(("OnTask_WaveReturn... pick up the audio\n")); // if we stopped to do this, then restart whatever we were doing
if (npMCI->dwFlags & MCIAVI_UPDATING) { OnTask_RestartAgain(npMCI, TRUE); } else { TaskReturns(npMCI, 0); } }
BOOL OnTask_WaveStealDuringPlay(NPMCIGRAPHIC npMCI) {
DPF2(("OnTask_WaveStealDuringPlay, '%ls' hTask=%04X\n", (LPSTR)npMCI->szFilename, npMCI->hTask)); /* If we do not have the audio, just return. */ if (npMCI->hWave == 0) { TaskReturns(npMCI, 0); return FALSE; }
/* Stop before changing sound status */ OnTask_StopTemporarily(npMCI); return(TRUE); }
/*
* A wave device may have become available. Stop and try to pick it up. */
BOOL OnTask_WaveReturnDuringPlay(NPMCIGRAPHIC npMCI) {
/* If there's no audio, just return. */ if (npMCI->nAudioStreams == 0) { npMCI->dwFlags &= ~MCIAVI_LOSTAUDIO; TaskReturns(npMCI, 0); return FALSE; }
/* Stop before changing sound status */ OnTask_StopTemporarily(npMCI); return(TRUE); }
BOOL OnTask_MuteDuringPlay(NPMCIGRAPHIC npMCI) { // If npMCI->lParam is TRUE, this means that we are to mute the
// device - hence turn off the PLAYAUDIO flag.
DWORD fPlayAudio = (DWORD)((BOOL) npMCI->lParam ? 0 : MCIAVI_PLAYAUDIO);
/* If there's no audio, just return. Should this be an error? */ if (npMCI->nAudioStreams == 0) { TaskReturns(npMCI, 0); return FALSE; }
/* If the mute state isn't changing, don't do anything. */ if ( (npMCI->dwFlags & MCIAVI_PLAYAUDIO) == fPlayAudio) { TaskReturns(npMCI, 0); return FALSE; }
DPF2(("DeviceMute, fPlayAudio = %x, npMCI=%8x\n", fPlayAudio, npMCI));
/* Stop before changing mute */ OnTask_StopTemporarily(npMCI);
return TRUE; }
void OnTask_Mute(NPMCIGRAPHIC npMCI) {
// If npMCI->lParam is TRUE, this means that we are to mute the
// device - hence turn off the PLAYAUDIO flag.
// We do not bother to check a change in state. That is only
// relevant if we are already playing when we only want to stop
// for a change in state.
BOOL fMute = (BOOL)npMCI->lParam;
/* If there's no audio, just return. Should this be an error? */ if (npMCI->nAudioStreams != 0) {
EnterWinCrit(npMCI); if (fMute) npMCI->dwFlags &= ~MCIAVI_PLAYAUDIO; else npMCI->dwFlags |= MCIAVI_PLAYAUDIO; LeaveWinCrit(npMCI); }
// if we stopped to do this, then restart whatever we were doing
if (npMCI->dwFlags & MCIAVI_UPDATING) { OnTask_RestartAgain(npMCI, TRUE); } else { TaskReturns(npMCI, 0); } }
// all access to the hWave *must* be restricted to the thread that created
// the wave device. So even getting the volume must be done on the
// worker thread only
//
// this function gets the current volume setting and stores it in
// npMCI->dwVolume
DWORD InternalGetVolume(NPMCIGRAPHIC npMCI) { DWORD dw = 0; DWORD dwVolume = 0;
if (npMCI->hWave) { // Get the current audio volume....
dw = waveOutMessage(npMCI->hWave, WODM_GETVOLUME, (DWORD_PTR) (DWORD FAR *)&dwVolume, 0);
} 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 really doubt zero is the current volume, try to work
// with broken cards like the windows sound system.
//
dw = waveOutGetVolume((HWAVEOUT)(UINT)WAVE_MAPPER, &dwVolume);
if ((dw != 0) || (dwVolume != 0)) {
dw = waveOutGetVolume((HWAVEOUT)0, &dwVolume); }
// don't accept default volume of 0
if ((dwVolume == 0) && (dw == 0)) { dw = MCIERR_NONAPPLICABLE_FUNCTION; }
} if (dw == 0) { npMCI->dwVolume = MAKELONG((UINT)muldiv32(LOWORD(dwVolume), 500L, 32768L), (UINT)muldiv32(HIWORD(dwVolume), 500L, 32768L)); } return dw;
}
DWORD InternalSetVolume(NPMCIGRAPHIC npMCI, DWORD dwVolume) { DWORD dw = 0;
npMCI->dwVolume = dwVolume;
EnterWinCrit(npMCI); npMCI->dwFlags |= MCIAVI_VOLUMESET; LeaveWinCrit(npMCI);
/* clear flag to emulate volume */; npMCI->fEmulatingVolume = FALSE;
/* If there's no audio, just return. Should this be an error? */ if (npMCI->nAudioStreams != 0) {
if (npMCI->hWave) { 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; }
INLINE void OnTask_SetVolume(NPMCIGRAPHIC npMCI) { DWORD dwVolume = (DWORD) npMCI->lParam;
TaskReturns(npMCI, InternalSetVolume(npMCI, dwVolume)); }
void OnTask_SetAudioStream(NPMCIGRAPHIC npMCI) { UINT wAudioStream = npMCI->dwParamFlags; int stream;
/* If there's no audio, we're done. Should this be an error? */
if (npMCI->nAudioStreams != 0) {
for (stream = 0; stream < npMCI->streams; stream++) { if (SH(stream).fccType == streamtypeAUDIO) { --wAudioStream;
if (wAudioStream == 0) break; } }
Assert(stream < npMCI->streams);
npMCI->psiAudio = SI(stream); npMCI->nAudioStream = stream; }
// if we stopped to do this, then restart whatever we were doing
if (npMCI->dwFlags & MCIAVI_UPDATING) { OnTask_RestartAgain(npMCI, TRUE); } else { TaskReturns(npMCI, 0); } }
void OnTask_SetVideoStream(NPMCIGRAPHIC npMCI) { UINT uStream = npMCI->dwParamFlags; BOOL fOn = (BOOL) npMCI->lParam; DWORD dw = 0L; 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) { dw = MCIERR_OUTOFRANGE; } else {
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"); }
// if we stopped to do this, then restart whatever we were doing
if ( (dw == 0) && (npMCI->dwFlags & MCIAVI_UPDATING)) { OnTask_RestartAgain(npMCI, TRUE); } else { TaskReturns(npMCI, dw); }
}
/***************************************************************************
* ***************************************************************************/
static void MapRect(RECT *prc, RECT*prcIn, RECT *prcFrom, RECT *prcTo) { if (IsRectEmpty(prcFrom)) { SetRectEmpty(prc); } else { 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); } }
/***************************************************************************
* ***************************************************************************/
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
//
IntersectRect(&SI(i)->rcSource, &SH(i).rcFrame, &npMCI->rcSource);
//
// now map the stream source rect onto the destination
//
MapRect(&SI(i)->rcDest, &SI(i)->rcSource, &npMCI->rcSource, &npMCI->rcDest); //
// make the stream source RECT (rcSource) relative to the
// stream rect (rcFrame)
//
OffsetRect(&SI(i)->rcSource,-SH(i).rcFrame.left,-SH(i).rcFrame.top); } }
//
// try to set the dest or source rect without stopping play.
// called both at stop time and at play time
//
// returns TRUE if stop needed, or else FALSE if all handled.
// lpdwErr is set to a non-zero error if any error occured (in which case
// FALSE will be returned.
//
BOOL TryPutRect(NPMCIGRAPHIC npMCI, DWORD dwFlags, LPRECT lprc, LPDWORD lpdwErr) {
RECT rc; PRECT prcPut; DWORD dw = 0;
// assume no error
*lpdwErr = 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 rectangle???
//
// ?? Why do we use an intermediate structure?
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) { // this is fine if there are no video streams
if (npMCI->nVideoStreams <= 0) { // no video so all ok
return FALSE; }
DPF2(("DevicePut: invalid rectangle [%d, %d, %d, %d]\n", *lprc)); *lpdwErr = MCIERR_OUTOFRANGE; return FALSE; }
/* make sure the rect changed */ if (EqualRect(prcPut,lprc)) { return FALSE; }
InvalidateRect(npMCI->hwndPlayback, &npMCI->rcDest, TRUE); rc = *prcPut; /* save it */ *prcPut = *lprc; /* change it */ InvalidateRect(npMCI->hwndPlayback, &npMCI->rcDest, FALSE);
/* have both the dest and source been set? */ if (IsRectEmpty(&npMCI->rcDest) || IsRectEmpty(&npMCI->rcSource)) { return FALSE; }
MapStreamRects(npMCI); StreamInvalidate(npMCI, NULL); // invalidate the world
if (npMCI->wTaskState == TASKIDLE) { DPF2(("TryPutRect: Idle, force DrawBegin on update\n")); npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN; } else { BOOL fRestart = FALSE; //
// we dont need to start/stop just begin again.
//
DPF2(("TryPutRect: Calling DrawBegin()\n")); if (!DrawBegin(npMCI, &fRestart)) { *lpdwErr = npMCI->dwTaskError; return FALSE; }
if (!DoStreamUpdate(npMCI, FALSE)) { DPF(("TryPutRect: Failed update, forcing restart....\n")); npMCI->lFrameDrawn = (- (LONG) npMCI->wEarlyRecords) - 1; fRestart = TRUE; } if (fRestart) { // restart needed
return TRUE; } }
// all ok
return FALSE; }
void OnTask_Put(NPMCIGRAPHIC npMCI) {
DWORD dwFlags = npMCI->dwParamFlags; LPRECT lprc = (LPRECT) npMCI->lParam; DWORD dw = 0; //If the user is doing an MCI_PUT to set the rectangle we should
//stop any previous requests to set the rectangle.
npMCI->dwWinProcRequests &= ~WINPROC_RESETDEST;
if (TryPutRect(npMCI, dwFlags, lprc, &dw)) {
// what to do now? It says we need to stop, but we
// are stopped.
TaskReturns(npMCI, MCIERR_DEVICE_NOT_READY); return; }
// if we stopped to do this, then restart whatever we were doing
if ((dw == 0) && (npMCI->dwFlags & MCIAVI_UPDATING)) {
// !!! We used to call InitDecompress here...
npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;
OnTask_RestartAgain(npMCI, TRUE); } else { TaskReturns(npMCI, dw); } }
BOOL OnTask_PutDuringPlay(NPMCIGRAPHIC npMCI) { DWORD dwFlags = npMCI->dwParamFlags; LPRECT lprc = (LPRECT) npMCI->lParam; DWORD dw = 0;
if (TryPutRect(npMCI, dwFlags, lprc, &dw)) {
// need to stop to handle this one.
// !!! Set a flag here to prevent any more drawing
npMCI->fNoDrawing = TRUE;
OnTask_StopTemporarily(npMCI); return TRUE; }
// handled ok or error - no stop needed
TaskReturns(npMCI, dw); return FALSE; }
void OnTask_Palette(NPMCIGRAPHIC npMCI) { HPALETTE hpal = (HPALETTE)npMCI->lParam;
// Remember this for later.
npMCI->hpal = hpal;
npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN; InvalidateRect(npMCI->hwndPlayback, NULL, TRUE);
// !!! Do we need to stop and restart here?
// Answer: probably not, because if they care about the palette not being
// messed up, they probably never allowed us to be shown at all.
TaskReturns(npMCI, 0); return; }
void OnTask_PaletteColor(NPMCIGRAPHIC npMCI) { DWORD index = (DWORD)npMCI->lParam; DWORD color = (DWORD)npMCI->dwParamFlags;
// !!! Do we need to stop and restart here?
// Answer: probably not, because if they care about the palette not being
// messed up, they probably never allowed us to be shown at all.
// Note: chicago code does stop... but they stop for most things.
// (it would be cleaner to stop and restart...)
// Pound the new color into the old format.
((DWORD FAR *) ((BYTE FAR *) npMCI->pbiFormat + npMCI->pbiFormat->biSize))[index] = color;
((DWORD FAR *) npMCI->argb)[index] = color;
npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN; InvalidateRect(npMCI->hwndPlayback, NULL, TRUE); TaskReturns(npMCI, 0); return; }
/*
* OnTask_ProcessRequest * * Process a request on the worker thread. Set hEventResponse if completed * in error, or once the request has been completed (in the case of async * requests such as play, set the event once play has begun ok * * return TRUE if it is time for the thread to exit, or else false. * */ BOOL OnTask_ProcessRequest(NPMCIGRAPHIC npMCI) { switch(npMCI->message) {
case AVI_CLOSE: // release the requesting thread so he can go and wait on the
// worker thread exit
TaskReturns(npMCI, 0);
// now go and exit
return TRUE;
case AVI_RESUME: // same as play, except that repeat and reverse flags need
// to be set on worker thread
npMCI->dwParamFlags |= ((npMCI->dwFlags & MCIAVI_REVERSE)? MCI_DGV_PLAY_REVERSE : 0); // fall through
case AVI_PLAY: OnTask_Play(npMCI); break;
case AVI_STOP: npMCI->dwFlags &= ~MCIAVI_REPEATING; // Give the wave device away
TaskReturns(npMCI, 0); break;
case AVI_REALIZE: OnTask_Realize(npMCI); break;
case AVI_UPDATE: OnTask_Update(npMCI); break;
case AVI_PAUSE: // not playing, so same as cue to current frame
OnTask_Cue(npMCI, npMCI->dwParamFlags | MCI_TO, npMCI->lCurrentFrame); break;
case AVI_CUE: OnTask_Cue(npMCI, npMCI->dwParamFlags, (LONG) npMCI->lParam); break;
case AVI_SEEK: OnTask_Seek(npMCI); break;
case AVI_WINDOW: OnTask_SetWindow(npMCI); break;
case AVI_MUTE: OnTask_Mute(npMCI); break;
case AVI_SETSPEED: OnTask_SetSpeed(npMCI); break;
case AVI_SETVOLUME: OnTask_SetVolume(npMCI); break;
case AVI_GETVOLUME: TaskReturns(npMCI, InternalGetVolume(npMCI)); break;
case AVI_AUDIOSTREAM: OnTask_SetAudioStream(npMCI); break;
case AVI_VIDEOSTREAM: OnTask_SetVideoStream(npMCI); break;
case AVI_PUT: OnTask_Put(npMCI); break;
case AVI_PALETTE: OnTask_Palette(npMCI); break;
case AVI_PALETTECOLOR: OnTask_PaletteColor(npMCI); break;
case AVI_WAVESTEAL: OnTask_WaveSteal(npMCI); break;
case AVI_WAVERETURN: OnTask_WaveReturn(npMCI); break;
default: TaskReturns(npMCI, MCIERR_UNSUPPORTED_FUNCTION); break; } return FALSE; }
// OnTask_PeekRequest
//
// called from aviTaskCheckRequests() to process a message at play time.
// if the message requires a stop, then this function returns TRUE and
// leaves the message unprocessed.
//
// otherwise the message is fully processed. This must include resetting
// hEventSend
//
INLINE STATICFN BOOL OnTask_PeekRequest(NPMCIGRAPHIC npMCI) { switch(npMCI->message) {
// always need to stop
case AVI_CLOSE: case AVI_STOP: npMCI->dwFlags &= ~MCIAVI_REPEATING; // Give the wave device away
return TRUE;
// may need to stop
case AVI_RESUME: // same as play, except that repeat and reverse flags need
// to be set on worker thread
npMCI->dwParamFlags |= ((npMCI->dwFlags & MCIAVI_REPEATING)? MCI_DGV_PLAY_REPEAT : 0) | ((npMCI->dwFlags & MCIAVI_REVERSE)? MCI_DGV_PLAY_REVERSE : 0); // fall through
case AVI_PLAY: return OnTask_PlayDuringPlay(npMCI);
case AVI_UPDATE: return OnTask_UpdateDuringPlay(npMCI);
case AVI_SEEK: return OnTask_SeekDuringPlay(npMCI);
case AVI_CUE: return OnTask_CueDuringPlay(npMCI);
case AVI_MUTE: return OnTask_MuteDuringPlay(npMCI);
case AVI_WAVESTEAL: return OnTask_WaveStealDuringPlay(npMCI);
case AVI_WAVERETURN: return OnTask_WaveReturnDuringPlay(npMCI);
case AVI_SETSPEED: return OnTask_SetSpeedDuringPlay(npMCI);
case AVI_PUT: return OnTask_PutDuringPlay(npMCI);
// need temporary stop
case AVI_WINDOW: case AVI_AUDIOSTREAM: case AVI_VIDEOSTREAM: OnTask_StopTemporarily(npMCI); return TRUE;
// never need to stop
case AVI_REALIZE: OnTask_Realize(npMCI); break;
case AVI_PAUSE: OnTask_PauseDuringPlay(npMCI); break;
case AVI_SETVOLUME: OnTask_SetVolume(npMCI); break;
case AVI_GETVOLUME: TaskReturns(npMCI, InternalGetVolume(npMCI)); break;
case AVI_PALETTE: OnTask_Palette(npMCI); break;
case AVI_PALETTECOLOR: OnTask_PaletteColor(npMCI); break;
default: TaskReturns(npMCI, MCIERR_UNSUPPORTED_FUNCTION); break; } return FALSE; }
/*
* This routine is called from the IDLE loop and at key points while * playing. If it is possible to service the request, the state of the * device is updated and the request flag is cleared. * * If the request cannot be handled now (e.g. while playing) the flag * is not set and we will be called again (e.g. when idle). * * If we need to stop to service the request (i.e. to regain the sound * device) we return TRUE. In all other cases we return FALSE. The * return value is only checked if we are actually playing. */
STATICFN void OnTask_WinProcRequests(NPMCIGRAPHIC npMCI, BOOL bPlaying) {
DWORD requests; EnterWinCrit(npMCI);
// grab the request bits now, so we don't need to hold the
// critsec while servicing them.
// any that are not cleared will be or-ed back in at the end
requests = npMCI->dwWinProcRequests; npMCI->dwWinProcRequests = 0; LeaveWinCrit(npMCI);
if (requests & WINPROC_STOP) { requests &= ~WINPROC_STOP; npMCI->dwFlags |= MCIAVI_STOP; }
if (requests & WINPROC_MUTE) { if (bPlaying) { OnTask_StopTemporarily(npMCI); } else { // toggle audio flag
npMCI->dwFlags ^= MCIAVI_PLAYAUDIO; requests &= ~WINPROC_MUTE; } }
if (requests & WINPROC_SOUND) { // We might be able to pick up the sound. This is only of interest
// if we are currently playing, do not have a sound device, and want
// the audio.
if (bPlaying && (NULL == npMCI->hWave) && (MCIAVI_PLAYAUDIO & npMCI->dwFlags)) { OnTask_StopTemporarily(npMCI); } else { // We have finished this request. Make sure we try and
// get sound when we restart
requests &= ~WINPROC_SOUND; npMCI->dwFlags &= ~MCIAVI_LOSEAUDIO; } }
#ifdef REMOTESTEAL
if (requests & WINPROC_SILENT) { extern HWND hwndWantAudio; DPF2(("WINPROC_SILENT request made, bPlaying=%x\n", bPlaying)); // If we are playing, and we have a wave device, stop.
// When we are recalled, we will start again without the wave device.
if (bPlaying && npMCI->hWave) { OnTask_StopTemporarily(npMCI); // Stopping will cause the wave device to be released, which
// means a message will be posted to whoever wanted the wave
// device
} else { // If we are playing, we do not have a wave device, and we
// do not want to stop.
// Otherwise we want to lose our wave device.
// Either way, we will finish with WINPROC_SILENT on this pass
requests &= ~WINPROC_SILENT; hwndWantAudio = 0; // In case we did not have to stop
if (!bPlaying) { // Remember we lost audio, and start again without audio
npMCI->dwFlags |= MCIAVI_LOSTAUDIO; npMCI->dwFlags |= MCIAVI_LOSEAUDIO; } } }
#endif
if (requests & WINPROC_RESETDEST) {
RECT rc; DWORD dw;
if (npMCI->hwndPlayback && npMCI->hwndPlayback == npMCI->hwndDefault && (npMCI->dwOptionFlags & MCIAVIO_STRETCHTOWINDOW)) { GetClientRect(npMCI->hwndPlayback, &rc); } else if (npMCI->streams > 0) { rc = npMCI->rcMovie;
if (npMCI->dwOptionFlags & MCIAVIO_ZOOMBY2) { rc.right *= 2; rc.bottom *= 2; } }
if (TryPutRect(npMCI, MCI_DGV_PUT_DESTINATION, &rc, &dw) && bPlaying) { OnTask_StopTemporarily(npMCI); } else { requests &= ~WINPROC_RESETDEST; } }
if (requests & WINPROC_ACTIVE) {
// We are being made active. The only extra work we must do
// is grab the wave device - if we have ever lost it.
// If we are playing, and we do not have the audio, and we want
// the audio...
if (bPlaying && (npMCI->hWave == 0) && (npMCI->dwFlags & MCIAVI_PLAYAUDIO)) {
// Let's try and make the sound active by stealing the wave device
// Must stop before trying to reset the sound
if (StealWaveDevice(npMCI)) {
OnTask_StopTemporarily(npMCI); // Force ourselves to be called again. Doing it this way
// means that we will be recalled. We cannot rely on
// WINPROC_ACTIVE staying around. A deactivation could
// cause the flag to be cleared
requests |= WINPROC_SOUND; } } else { // We had not lost the wave device...
// OR we are playing silently, and so there is no point
// in trying to steal it.
// We are finished.
}
// Clear WINPROC_ACTIVE - all processing done.
// Note: we might have set WINPROC_SOUND, which will cause this
// routine to be recalled. Once recalled, then playing can restart
requests &= ~ WINPROC_ACTIVE;
} else { // We never have both INACTIVE and ACTIVE at the same time
if (requests & WINPROC_INACTIVE) { //!!!need to support this
requests &= ~WINPROC_INACTIVE; } }
EnterWinCrit(npMCI); // Do we really need this one here??
if (requests & WINPROC_UPDATE) { if (bPlaying) { npMCI->dwFlags |= MCIAVI_NEEDUPDATE; } else {
HDC hdc;
// don't do this if the window is now hidden
// or showstage will be called with the critsec and deadlock
if (IsWindowVisible(npMCI->hwndPlayback)) { EnterHDCCrit(npMCI); npMCI->bDoingWinUpdate = TRUE;
hdc = GetDC(npMCI->hwndPlayback); Assert(hdc); Internal_Update(npMCI, MCI_DGV_UPDATE_PAINT, hdc, NULL); ReleaseDC(npMCI->hwndPlayback, hdc);
npMCI->bDoingWinUpdate = FALSE; LeaveHDCCrit(npMCI); } } requests &= ~WINPROC_UPDATE; }
if (requests & WINPROC_REALIZE) { EnterHDCCrit(npMCI); InternalRealize(npMCI); LeaveHDCCrit(npMCI); requests &= ~ WINPROC_REALIZE; }
// or back the bits we didn't clear
npMCI->dwWinProcRequests |= requests;
// if we processed all the bits (and no new bits were set)
if (! npMCI->dwWinProcRequests) { ResetEvent(npMCI->heWinProcRequest); }
LeaveWinCrit(npMCI); }
/***************************************************************************
* * @doc INTERNAL MCIAVI * * @api void| aviTaskCheckRequests | called on the worker thread at least once per * * frame. We use this to check for requests from the user thread. * * ***************************************************************************/
void NEAR PASCAL aviTaskCheckRequests(NPMCIGRAPHIC npMCI) { HANDLE hWaiter;
if (WaitForSingleObject(npMCI->hEventSend, 0) == WAIT_OBJECT_0) {
// there is a request
Assert(npMCI->message != 0);
// check the waiter
// if this is an async request with wait, we need to set hWaiter
// so that hEventAllDone is set correctly. If we stop
// and process this message in the idle loop, then we don't want to
// set hWaiter here, or EventAllDone could be signalled when we
// stop - before we've even started this request.
// so we need to check the validity (no waiting if another thread is
// waiting) and pick up the waiter and bDelayed while the critsec
// is still held, but only set hWaiter if the request was processed
// here.
// no - that leaves a timing window when hWaiter is not set and the
// critsec is not held. Set hWaiter, but be prepared to unset it
// if we postpone processing during the idle loop (in which case,
// the waiter will hold the critsec until we have stopped).
hWaiter = npMCI->hWaiter;
if (npMCI->bDelayedComplete) {
if (npMCI->hWaiter && (npMCI->hWaiter != npMCI->hRequestor)) { TaskReturns(npMCI, MCIERR_DEVICE_NOT_READY); return; } else { DPF2(("Replacing hWaiter in aviTaskCheckRequests... was %x, now %x\n", npMCI->hWaiter, npMCI->hRequestor)); npMCI->hWaiter = npMCI->hRequestor; } }
DPF2(("peek %d [%x] ...", npMCI->message, npMCI->hRequestor));
if (OnTask_PeekRequest(npMCI)) { // we need to stop
// must be set on WORKER THREAD ONLY
npMCI->dwFlags |= MCIAVI_STOP; DPF2(("Need to stop - replacing hWaiter (was %x, now %x)\n", npMCI->hWaiter, hWaiter));
// replace hWaiter so idle loop does not set hEventAllDone for
// a request he has not yet started.
npMCI->hWaiter = hWaiter; } // else the request has already been dealt with
}
// did the winproc have any requests
if (WaitForSingleObject(npMCI->heWinProcRequest, 0) == WAIT_OBJECT_0) {
//
// We have a request from the window thread. Go process it
//
OnTask_WinProcRequests(npMCI, TRUE); } }
/***************************************************************************
* * @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 * ***************************************************************************/
void CheckIfActive(NPMCIGRAPHIC npMCI) { BOOL fActive; HWND hwndA;
if (!IsTask(npMCI->hTask)) return;
//
// 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
#ifndef _WIN32
hwndA = GetActiveWindow();
fActive = (hwndA == npMCI->hwndPlayback) || (GetFocus() == npMCI->hwndPlayback) || (IsWindow(hwndA) && IsChild(hwndA, npMCI->hwndPlayback) && !npMCI->fForceBackground); #else
hwndA = GetForegroundWindow();
fActive = (hwndA == npMCI->hwndPlayback) || (IsWindow(hwndA) && IsChild(hwndA, npMCI->hwndPlayback) && !npMCI->fForceBackground); #endif
DeviceSetActive(npMCI, fActive); }
|