/****************************************************************************** Copyright (C) Microsoft Corporation 1985-1995. 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) // from wownt32.h VOID (WINAPI * pWOWYield16)(VOID); #ifdef DEBUG #define AssertUserThread(npMCI) \ { \ DWORD thread = GetCurrentThreadId(); \ Assert((npMCI)->hTask != (HTASK)thread); \ Assert(!((npMCI)->hwndDefault) || ((DWORD_PTR)GetWindowTask((npMCI)->hwndDefault) != thread));\ } #else #define AssertUserThread(npMCI) #endif /* * send a request to the worker thread, and wait for it to complete, * then return the result * * We must hold the CmdCritSec to stop other threads from making requests. * * if bDelayedComplete is true, the request is one that has two phases: * * phase 1: initiating the operation (eg starting play). No other * requests are permitted during this phase, so we hold the * critical section and wait. No yielding of any sort is safe * at this point, since re-entry on the same thread is not * something we can handle well. This means that the worker * thread must not do anything before setting hEventResponse * that could block on our processing a sendmessage * * phase 2: while the play is taking place, we must process messages, * yield to the app and allow other requests (eg stop). * For this, we wait on a second event, timing out and yielding * to the driver 10 times a second. * */ DWORD mciaviTaskRequest( NPMCIGRAPHIC npMCI, UINT message, DWORD dwFlags, LPARAM lParam, LPARAM dwCallback, BOOL bDelayedComplete ) { DWORD dwRet; MSG msg; #ifdef _WIN32 // the gdi request queue is per-thread. We must flush the // app thread q here, or updates done to the window at the apps // request may appear before updates done by the app itself beforehand GdiFlush(); #endif // get the critsec that controls sending requests EnterCriticalSection(&npMCI->CmdCritSec); if (IsBadReadPtr(npMCI, sizeof(MCIGRAPHIC))) { // device has been closed beneath us! DPF(("help - npMCI has gone away")); // not safe to leave critsec or dec count return MCIERR_DEVICE_NOT_READY; } if (npMCI->EntryCount++ > 0) { DPF(("re-entering requestor on same thread (SendMessage?)")); //DebugBreak(); npMCI->EntryCount--; LeaveCriticalSection(&npMCI->CmdCritSec); return MCIERR_DEVICE_NOT_READY; //return 0; } if (!IsTask(npMCI->hTask)) { // worker thread has gone away (previous close ?) npMCI->EntryCount--; LeaveCriticalSection(&npMCI->CmdCritSec); DPF(("worker thread has gone away")); return MCIERR_DEVICE_NOT_READY; } // the response event should not be set yet! Assert(WaitForSingleObject(npMCI->hEventResponse, 0) == WAIT_TIMEOUT); // write the params npMCI->message = message; npMCI->dwParamFlags = dwFlags; npMCI->lParam = lParam; npMCI->dwReqCallback = dwCallback; npMCI->bDelayedComplete = bDelayedComplete; // we are the requesting task (we will be thrown out if this is // bDelayedComplete and there is an outstanding bDelayedComplete // from someone else) npMCI->hRequestor = GetCurrentTask(); // signal that there is a request SetEvent(npMCI->hEventSend); // and wait for the response. // // in the play-wait case, this wait will complete once the play // has started. So at this point, no yields. // send-message processing needed for RealizePalette on worker thread #if 1 // this could cause re-entry on this thread, and the critical section // will not prevent that. Hence the EntryCount checks. while (MsgWaitForMultipleObjects(1, &npMCI->hEventResponse, FALSE, INFINITE, QS_SENDMESSAGE) != WAIT_OBJECT_0) { DPF2(("rec'd sendmessage during wait\n")); // this peekmessage allows an inter-thread sendmessage to complete. // no message needs to be removed or processed- the range filtering is // essentially irrelevant for this. PeekMessage(&msg, NULL, WM_QUERYNEWPALETTE, WM_QUERYNEWPALETTE, PM_NOREMOVE); } #else WaitForSingleObject(npMCI->hEventResponse, INFINITE); #endif // pick up the return value dwRet = npMCI->dwReturn; DPF2(("Task returns %d\n", dwRet)); // release the critsec now that request is all done if (--npMCI->EntryCount != 0) { DPF(("EntryCount not 0 on exit")); } LeaveCriticalSection(&npMCI->CmdCritSec); // if this is a two-phased operation such as play + wait // we must do the yielding wait here if (!dwRet && bDelayedComplete) { DWORD dw; UINT nYieldInterval = 300; #ifdef DEBUG nYieldInterval = mmGetProfileInt(szIni, TEXT("YieldInterval"), nYieldInterval); #endif do { if (mciDriverYield(npMCI->wDevID)) { // app says we must stop now. do this by issuing a stop // request and carry on waiting for the play+wait to finish mciaviTaskRequest(npMCI, AVI_STOP, 0, 0, 0, FALSE); } dw = WaitForSingleObject(npMCI->hEventAllDone, nYieldInterval); // this peekmessage allows an inter-thread sendmessage to complete. // no message needs to be removed or processed- the range filtering is // essentially irrelevant for this. PeekMessage(&msg, NULL, WM_QUERYNEWPALETTE, WM_QUERYNEWPALETTE, PM_NOREMOVE); } while(dw != WAIT_OBJECT_0); // until this is cleared, no other task can issue delayed requests npMCI->hWaiter = 0; } return dwRet; } /*************************************************************************** * * @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; AssertUserThread(npMCI); // init the yield proc we will need for wow yielding if (IsNTWOW()) { if (pWOWYield16 == 0) { HMODULE hmod; hmod = GetModuleHandle(TEXT("wow32.dll")); if (hmod != NULL) { (FARPROC)pWOWYield16 = GetProcAddress(hmod, "WOWYield16"); } } } // note that DeviceClose *will* be called anyway, even if DeviceOpen // fails, so make sure that allocations and events can be cleaned up // correctly. npMCI->uErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); // must open the file on this app thread for ole reasons if (!mciaviOpenFile(npMCI)) { SetErrorMode(npMCI->uErrorMode); return npMCI->dwTaskError; } // OpenFileInit() on worker thread completes this open later. // create the communication channel to the worker thread, and // then start the thread // do this first, so that whenever we call DeviceClose we can always // safely do the Delete.. InitializeCriticalSection(&npMCI->CmdCritSec); SetNTFlags(npMCI, NTF_DELETECMDCRITSEC); // Remember to do the delete npMCI->EntryCount = 0; // must be manual-reset to allow polling during play. npMCI->hEventSend = CreateEvent(NULL, TRUE, FALSE, NULL); npMCI->hEventResponse = CreateEvent(NULL, FALSE, FALSE, NULL); npMCI->hEventAllDone = CreateEvent(NULL, FALSE, FALSE, NULL); if (!npMCI->hEventSend || !npMCI->hEventResponse || !npMCI->hEventAllDone) { // cleanup of events actually allocated will be done in DeviceClose return MCIERR_OUT_OF_MEMORY; } // create the worker thread #if 0 if (mmTaskCreate(mciaviTask, &npMCI->hThreadTermination, (DWORD)(UINT)npMCI) == 0) #else // We do not want the thread id, but CreateThread blows up if we pass // a null parameter. Hence overload dwRet... if (npMCI->hThreadTermination = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)mciaviTask, (LPVOID)npMCI, 0, &dwRet)) #endif { // check that the thread is actually created // either hEventResponse will be set, indicating that // the thread completed, or hThreadTermination will be // set, indicating that the thread aborted. #if 0 if (WaitForMultipleObjects(2, &npMCI->hEventResponse, FALSE, INFINITE) == WAIT_OBJECT_0) { // task completed ok Assert(IsTask(npMCI->hTask)); } #else // We must process messages during this phase... IF messages // must be processed by this thread before the AVI window can // be created. The most likely case is when a parent window // is passed and the parent window belongs to this (the UI) // thread. If no messages need to be processed (i.e. the AVI // window being created has no parent) then we could use the // simpler code above. DO THIS LATER. UINT n; while (WAIT_OBJECT_0+2 <= (n = MsgWaitForMultipleObjects(2, &npMCI->hEventResponse, FALSE, INFINITE, QS_SENDMESSAGE))) { MSG msg; if (n!=WAIT_OBJECT_0+2) { DPF0(("MsgWaitForMultipleObjects gave an unexpected return of %d\n", n)); } PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); // PeekMessage with PM_NOREMOVE causes the inter thread // sent messages to be processed } dwRet = 0; if (n == WAIT_OBJECT_0) { // task completed ok Assert(IsTask(npMCI->hTask)); } #endif else { // hThreadTermination has been signalled - abort CloseHandle(npMCI->hThreadTermination); npMCI->hThreadTermination = 0; dwRet = npMCI->dwTaskError; Assert(dwRet); } } else { npMCI->hTask = 0; dwRet = MCIERR_OUT_OF_MEMORY; npMCI->dwTaskError = GetLastError(); } SetErrorMode(npMCI->uErrorMode); if (dwRet != 0) { // open failed - the necessary cleanup will be done in DeviceClose // which will be called after a bad return from DeviceOpen. In // fact graphic.c (which calls DeviceOpen) will call GraphicClose // when DeviceOpen fails. GraphicClose will then call DeviceClose // which will delete the cmdCritSec } return dwRet; } /*************************************************************************** * * @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)) { AssertUserThread(npMCI); // tell the worker to close and wait for it to happen mciaviTaskRequest(npMCI, AVI_CLOSE, 0, 0, 0, FALSE); } // must wait for thread to exit if (npMCI->hThreadTermination != 0) { /* ** Wait for the thread to complete so the DLL doesn't get unloaded ** while it's still executing code in that thread */ // we must allow sendmessage at this point since the winproc thread // will block until it can send messages to our thread, and we are // waiting for the winproc thread to exit. // do not do this between setting hEventSend and receiving hEventResponse // though or we could re-enter the Request block and get confused // about whether we have seen hEventResponse. // we also need to yield in case we are on a wow thread - any // interthread sendmessage to another wow thread will block until // we yield here allowing other wow threads to run DWORD dw; do { if (pWOWYield16) { pWOWYield16(); } dw = MsgWaitForMultipleObjects( 1, &npMCI->hThreadTermination, FALSE, 100, QS_SENDMESSAGE); if (dw == WAIT_OBJECT_0 + 1) { MSG msg; DPF2(("rec'd sendmessage during shutdown wait\n")); // 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 (dw != WAIT_OBJECT_0); CloseHandle(npMCI->hThreadTermination); npMCI->hThreadTermination = 0; } if (TestNTFlags(npMCI, NTF_DELETECMDCRITSEC)) { DeleteCriticalSection(&npMCI->CmdCritSec); } if (npMCI->hEventSend) { CloseHandle(npMCI->hEventSend); } if (npMCI->hEventAllDone) { CloseHandle(npMCI->hEventAllDone); } if (npMCI->hEventResponse) { CloseHandle(npMCI->hEventResponse); } // uninitialize AVIFile and hence OLE - must be done on app thread #ifdef USEAVIFILE // // we must do this so COMPOBJ will shut down right. // FreeAVIFile(npMCI); #endif 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. * * @parm LPMCI_DGV_PLAY_PARMS | lpPlay | Parameters for the play message. * * @rdesc 0 means OK, otherwise mci error * ***************************************************************************/ DWORD PASCAL DevicePlay( NPMCIGRAPHIC npMCI, DWORD dwFlags, LPMCI_DGV_PLAY_PARMS lpPlay, LPARAM dwCallback ) { BOOL bWait = FALSE; DWORD dwErr; if (!IsTask(npMCI->hTask)) return MCIERR_DEVICE_NOT_READY; // all handled by the worker thread AssertUserThread(npMCI); if (dwFlags & MCI_WAIT) { bWait = TRUE; } dwErr = mciaviTaskRequest(npMCI, AVI_PLAY, dwFlags, (LPARAM) lpPlay, dwCallback, bWait); if (dwFlags & (MCI_MCIAVI_PLAY_FULLSCREEN | MCI_MCIAVI_PLAY_FULLBY2)) { MSG msg; DPF(("DevicePlay, removing stray messages\n")); /* 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)) ; } return dwErr; } /*************************************************************************** * * @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 bWait = FALSE; if (!IsTask(npMCI->hTask)) return MCIERR_DEVICE_NOT_READY; // all handled by the worker thread AssertUserThread(npMCI); return mciaviTaskRequest(npMCI, AVI_REALIZE, 0, 0, 0, FALSE); } /*************************************************************************** * * @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)) { DPF0(("DeviceStop called on a dead task, npMCI=%8x\n", npMCI)); return MCIERR_DEVICE_NOT_READY; } AssertUserThread(npMCI); return mciaviTaskRequest(npMCI, AVI_STOP, 0, 0, 0, FALSE); } /*************************************************************************** * * @doc INTERNAL MCIAVI * * @api DWORD | DeviceUpdate | Updates the frame into the given DC * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data block. * * @rdesc 0 means OK, otherwise mci error * ***************************************************************************/ DWORD PASCAL DeviceUpdate( NPMCIGRAPHIC npMCI, DWORD dwFlags, LPMCI_DGV_UPDATE_PARMS lpParms) { if (!IsTask(npMCI->hTask)) return MCIERR_DEVICE_NOT_READY; AssertUserThread(npMCI); return mciaviTaskRequest(npMCI, AVI_UPDATE, dwFlags, (LPARAM) lpParms, 0, FALSE); } /*************************************************************************** * * @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, LPARAM dwCallback) { if (!IsTask(npMCI->hTask)) return MCIERR_DEVICE_NOT_READY; AssertUserThread(npMCI); return mciaviTaskRequest(npMCI, AVI_PAUSE, dwFlags, 0, dwCallback, (dwFlags & MCI_WAIT)); } /*************************************************************************** * * @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

. * * @parm DWORD | dwFlags | MCI flags from command. * * @rdesc 0 means OK, otherwise mci error * ***************************************************************************/ DWORD PASCAL DeviceCue(NPMCIGRAPHIC npMCI, LONG lTo, DWORD dwFlags, LPARAM dwCallback) { if (!IsTask(npMCI->hTask)) return MCIERR_DEVICE_NOT_READY; AssertUserThread(npMCI); return mciaviTaskRequest(npMCI, AVI_CUE, dwFlags, lTo, dwCallback, (dwFlags & MCI_WAIT)); } /*************************************************************************** * * @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, LPARAM dwCallback) { DWORD dw = 0L; BOOL bWait = FALSE; if (!IsTask(npMCI->hTask)) return MCIERR_DEVICE_NOT_READY; // all handled by the worker thread AssertUserThread(npMCI); if (dwFlags & MCI_WAIT) { bWait = TRUE; } return mciaviTaskRequest(npMCI, AVI_RESUME, dwFlags, 0, dwCallback, bWait); } /*************************************************************************** * * @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, LPARAM dwCallback) { if (!IsTask(npMCI->hTask)) return MCIERR_DEVICE_NOT_READY; AssertUserThread(npMCI); return mciaviTaskRequest(npMCI, AVI_SEEK, dwFlags, lTo, dwCallback, (dwFlags & MCI_WAIT)); } /*************************************************************************** * * @doc INTERNAL MCIAVI * * @api DWORD | DeviceSetActive | 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) { // We cannot call AssertUserThread(npMCI); // This routine is called on the winproc thread, as well as the user // thread. if (fActive) // We must explicitly request a unicode string. %s will not // work as dprintf uses wvsprintfA DPF(("**** '%ls' is active.\n", (LPTSTR)npMCI->szFilename)); return 0; } /*************************************************************************** * * @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; } // there is no point in synchronizing with the worker thread for // this since the task state will be transient anyway. // just grab a snapshot and return that. AssertUserThread(npMCI); switch (npMCI->wTaskState) { case TASKIDLE: return MCI_MODE_STOP; case TASKCUEING: // problem: some apps (notably mplayer) will be surprised to // get MCI_MODE_SEEK immediately after issuing a PLAY command. // on win-16 the yielding model meant that the app would not // normally get control back until after the play proper had // started and so would never see the cueing state. // to avoid this confusion (and the bugs that arise from it), we // never return MCI_MODE_SEEK: we report this mode as playing. // this is often what would be seen on win-16 anyway (even in the // case of a PLAY command ?). // Except... for apps that really do seek this can fool them into // thinking that they are playing. So... we modify the algorithm // to return MODE_SEEK if lTo==lFrom (why is obvious) OR if // lRealStart==lTo. This latter is because if you seek in mplayer // by dragging the thumb the image is only updated every key frame. // lRealStart is updated to this key frame while seeking //DPF0(("F: %8x, To=%d, From=%d lReal=%d lDrawn=%d Current=%d\n", // npMCI->dwFlags, npMCI->lTo, npMCI->lFrom, npMCI->lRealStart, npMCI->lFrameDrawn, npMCI->lCurrentFrame)); if ((npMCI->lTo == npMCI->lFrom) || (npMCI->lTo == npMCI->lRealStart)) { return(MCI_MODE_SEEK); } return MCI_MODE_PLAY; case TASKSTARTING: // ready? of course we're ready case TASKPLAYING: return MCI_MODE_PLAY; case TASKPAUSED: return MCI_MODE_PAUSE; default: DPF(("Unexpected state %d in DeviceMode()\n", npMCI->wTaskState)); // fall through to the known states //case TASKBEINGCREATED: //case TASKINIT: case TASKCLOSE: //case TASKREADINDEX: 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) { // read a snapshot of the current state without // synchronising with the worker thread! AssertUserThread(npMCI); return InternalGetPosition(npMCI, lpl); } /*************************************************************************** * * @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) { if (!IsTask(npMCI->hTask)) return MCIERR_DEVICE_NOT_READY; AssertUserThread(npMCI); return mciaviTaskRequest(npMCI, AVI_WINDOW, 0, (LPARAM) hwnd, 0, FALSE); } /*************************************************************************** * * @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? * ** It is if you paused to change the speed then try and resume ** * ***************************************************************************/ DWORD PASCAL DeviceSetSpeed(NPMCIGRAPHIC npMCI, DWORD dwNewSpeed) { if (!IsTask(npMCI->hTask)) return MCIERR_DEVICE_NOT_READY; AssertUserThread(npMCI); return mciaviTaskRequest(npMCI, AVI_SETSPEED, 0, (LPARAM) dwNewSpeed, 0, FALSE); } /*************************************************************************** * * @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) { if (!IsTask(npMCI->hTask)) return MCIERR_DEVICE_NOT_READY; AssertUserThread(npMCI); return mciaviTaskRequest(npMCI, AVI_MUTE, 0, (LPARAM) fMute, 0, FALSE); } /*************************************************************************** * * @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; // switch audio off completely if setting volume level to 0, and back on // again if not. dw = DeviceMute(npMCI, (dwVolume == 0)); if (dw != 0) { return dw; } AssertUserThread(npMCI); return mciaviTaskRequest(npMCI, AVI_SETVOLUME, 0, (LPARAM) dwVolume, 0, FALSE); } /*************************************************************************** * * @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) { // all reference to the hWave *must* be done on the worker thread AssertUserThread(npMCI); return mciaviTaskRequest(npMCI, AVI_GETVOLUME, 0, 0, 0, FALSE); } /*************************************************************************** * * @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) { AssertUserThread(npMCI); return mciaviTaskRequest(npMCI, AVI_AUDIOSTREAM, wAudioStream, 0, 0, FALSE); } /*************************************************************************** * * @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) { AssertUserThread(npMCI); return mciaviTaskRequest(npMCI, AVI_AUDIOSTREAM, uStream, (BOOL)fOn, 0, FALSE); } /*************************************************************************** * * @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) { AssertUserThread(npMCI); return mciaviTaskRequest(npMCI, AVI_PUT, dwFlags, (LPARAM)lprc, 0, FALSE); } /*************************************************************************** * * @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) { AssertUserThread(npMCI); return mciaviTaskRequest(npMCI, AVI_PALETTE, 0, (LPARAM) hpal, 0, FALSE); } /*************************************************************************** * * @doc INTERNAL MCIAVI * * @api DWORD | DeviceSetPaletteColor | Changes the a single color * in the movie's palette. * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data. * * @parm DWORD | index | color index to change * * @parm DWORD | color | color value to use * * @rdesc 0 means OK, otherwise mci error * ***************************************************************************/ DWORD FAR PASCAL DeviceSetPaletteColor(NPMCIGRAPHIC npMCI, DWORD index, DWORD color) { AssertUserThread(npMCI); return mciaviTaskRequest(npMCI, AVI_PALETTECOLOR, color, (LPARAM) index, 0, FALSE); } // // user-thread version of ResetDestRect - note that there is a similar // winproc-thread-only version in window.c // void FAR PASCAL ResetDestRect(NPMCIGRAPHIC npMCI, BOOL fUseDefaultSizing) { RECT rc; /* WM_SIZE messages (on NT at least) are sometimes sent * during CreateWindow processing (eg if the initial window size * is not CW_DEFAULT). Some fields in npMCI are only filled in * after CreateWindow has returned. So there is a danger that at this * point some fields are not valid. */ if (npMCI->hwndPlayback && npMCI->hwndPlayback == npMCI->hwndDefault && (npMCI->dwOptionFlags & MCIAVIO_STRETCHTOWINDOW)) { GetClientRect(npMCI->hwndPlayback, &rc); } // Only allow ZOOMBY2 and fixed % defaults for our default playback window else if ((npMCI->streams > 0) && (npMCI->hwndPlayback == npMCI->hwndDefault)) { rc = npMCI->rcMovie; if (fUseDefaultSizing) AlterRectUsingDefaults(npMCI, &rc); } else { return; } if (!IsRectEmpty(&rc)) { DevicePut(npMCI, &rc, MCI_DGV_PUT_DESTINATION); } }