mciavi32: Rewrite asynchronous MCI_PLAY command handling.

Rewrite asynchronous MCI_PLAY command handling in MCIAVI driver, make
it more responsive to commands in the MCI_MODE_PLAY state by checking
hStopEvent even if the time frame between frames has expired.
This commit is contained in:
Dmitry Timoshkov 2005-12-22 17:15:33 +01:00 committed by Alexandre Julliard
parent 707fa2c3a2
commit 43cbb44f4f
2 changed files with 75 additions and 81 deletions

View File

@ -44,74 +44,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(mciavi);
static DWORD MCIAVI_mciStop(UINT, DWORD, LPMCI_GENERIC_PARMS); static DWORD MCIAVI_mciStop(UINT, DWORD, LPMCI_GENERIC_PARMS);
/* ===================================================================
* ===================================================================
* FIXME: should be using the new mmThreadXXXX functions from WINMM
* instead of those
* it would require to add a wine internal flag to mmThreadCreate
* in order to pass a 32 bit function instead of a 16 bit one
* ===================================================================
* =================================================================== */
struct SCA {
MCIDEVICEID wDevID;
UINT wMsg;
DWORD_PTR dwParam1;
DWORD_PTR dwParam2;
};
/**************************************************************************
* MCI_SCAStarter [internal]
*/
static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
{
struct SCA* sca = (struct SCA*)arg;
DWORD ret;
TRACE("In thread before async command (%08x,%04x,%08lx,%08lx)\n",
sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2);
TRACE("In thread after async command (%08x,%04x,%08lx,%08lx)\n",
sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
HeapFree(GetProcessHeap(), 0, sca);
return ret;
}
/**************************************************************************
* MCI_SendCommandAsync [internal]
*/
static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD dwParam1,
DWORD_PTR dwParam2, UINT size)
{
HANDLE handle;
struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
if (sca == 0)
return MCIERR_OUT_OF_MEMORY;
sca->wDevID = wDevID;
sca->wMsg = wMsg;
sca->dwParam1 = dwParam1;
if (size && dwParam2) {
sca->dwParam2 = (DWORD_PTR)sca + sizeof(struct SCA);
/* copy structure passed by program in dwParam2 to be sure
* we can still use it whatever the program does
*/
memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
} else {
sca->dwParam2 = dwParam2;
}
if ((handle = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) {
WARN("Couldn't allocate thread for async command handling, sending synchronously\n");
return MCI_SCAStarter(&sca);
}
SetThreadPriority(handle, THREAD_PRIORITY_TIME_CRITICAL);
CloseHandle(handle);
return 0;
}
/*======================================================================* /*======================================================================*
* MCI AVI implemantation * * MCI AVI implemantation *
*======================================================================*/ *======================================================================*/
@ -152,6 +84,7 @@ static DWORD MCIAVI_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
return 0; return 0;
InitializeCriticalSection(&wma->cs); InitializeCriticalSection(&wma->cs);
wma->ack_event = CreateEventW(NULL, FALSE, FALSE, NULL);
wma->hStopEvent = CreateEventW(NULL, FALSE, FALSE, NULL); wma->hStopEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
wma->wDevID = modp->wDeviceID; wma->wDevID = modp->wDeviceID;
wma->wCommandTable = mciLoadCommandResource(MCIAVI_hInstance, mciAviWStr, 0); wma->wCommandTable = mciLoadCommandResource(MCIAVI_hInstance, mciAviWStr, 0);
@ -184,6 +117,7 @@ static DWORD MCIAVI_drvClose(DWORD dwDevID)
mciSetDriverData(dwDevID, 0); mciSetDriverData(dwDevID, 0);
mciFreeCommandResource(wma->wCommandTable); mciFreeCommandResource(wma->wCommandTable);
CloseHandle(wma->ack_event);
CloseHandle(wma->hStopEvent); CloseHandle(wma->hStopEvent);
LeaveCriticalSection(&wma->cs); LeaveCriticalSection(&wma->cs);
@ -393,6 +327,61 @@ DWORD MCIAVI_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
return dwRet; return dwRet;
} }
static DWORD MCIAVI_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms);
struct MCIAVI_play_data
{
MCIDEVICEID wDevID;
DWORD flags;
MCI_PLAY_PARMS params;
};
/*
* MCIAVI_mciPlay_thread
*
* FIXME: probably should use a common worker thread created at the driver
* load time and queue all async commands to it.
*/
static DWORD WINAPI MCIAVI_mciPlay_thread(LPVOID arg)
{
struct MCIAVI_play_data *data = (struct MCIAVI_play_data *)arg;
DWORD ret;
TRACE("In thread before async play command (id %08x, flags %08lx)\n", data->wDevID, data->flags);
ret = MCIAVI_mciPlay(data->wDevID, data->flags | MCI_WAIT, &data->params);
TRACE("In thread after async play command (id %08x, flags %08lx)\n", data->wDevID, data->flags);
HeapFree(GetProcessHeap(), 0, data);
return ret;
}
/*
* MCIAVI_mciPlay_async
*/
static DWORD MCIAVI_mciPlay_async(WINE_MCIAVI *wma, DWORD dwFlags, LPMCI_PLAY_PARMS lpParams)
{
HANDLE handle, ack_event = wma->ack_event;
struct MCIAVI_play_data *data = HeapAlloc(GetProcessHeap(), 0, sizeof(struct MCIAVI_play_data));
if (!data) return MCIERR_OUT_OF_MEMORY;
data->wDevID = wma->wDevID;
data->flags = dwFlags;
memcpy(&data->params, lpParams, sizeof(MCI_PLAY_PARMS));
if (!(handle = CreateThread(NULL, 0, MCIAVI_mciPlay_thread, data, 0, NULL)))
{
WARN("Couldn't create thread for async play, playing synchronously\n");
return MCIAVI_mciPlay_thread(data);
}
SetThreadPriority(handle, THREAD_PRIORITY_TIME_CRITICAL);
CloseHandle(handle);
/* wait until the thread starts up, so the app could see a changed status */
WaitForSingleObject(ack_event, INFINITE);
TRACE("Async play has started\n");
return 0;
}
/*************************************************************************** /***************************************************************************
* MCIAVI_mciPlay [internal] * MCIAVI_mciPlay [internal]
*/ */
@ -429,10 +418,8 @@ static DWORD MCIAVI_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms
LeaveCriticalSection(&wma->cs); LeaveCriticalSection(&wma->cs);
if (!(dwFlags & MCI_WAIT)) { if (!(dwFlags & MCI_WAIT))
return MCI_SendCommandAsync(wDevID, MCI_PLAY, dwFlags, return MCIAVI_mciPlay_async(wma, dwFlags, lpParms);
(DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
}
if (!(GetWindowLongW(wma->hWndPaint, GWL_STYLE) & WS_VISIBLE)) if (!(GetWindowLongW(wma->hWndPaint, GWL_STYLE) & WS_VISIBLE))
ShowWindow(wma->hWndPaint, SW_SHOWNA); ShowWindow(wma->hWndPaint, SW_SHOWNA);
@ -460,16 +447,20 @@ static DWORD MCIAVI_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms
if (wma->dwStatus == MCI_MODE_PLAY) if (wma->dwStatus == MCI_MODE_PLAY)
{ {
LeaveCriticalSection(&wma->cs); LeaveCriticalSection(&wma->cs);
SetEvent(wma->ack_event);
return 0; return 0;
} }
if (wma->dwToVideoFrame <= wma->dwCurrVideoFrame) if (wma->dwToVideoFrame <= wma->dwCurrVideoFrame)
{ {
dwRet = 0; dwRet = 0;
SetEvent(wma->ack_event);
goto mci_play_done; goto mci_play_done;
} }
wma->dwStatus = MCI_MODE_PLAY; wma->dwStatus = MCI_MODE_PLAY;
/* signal the state change */
SetEvent(wma->ack_event);
if (dwFlags & (MCI_DGV_PLAY_REPEAT|MCI_DGV_PLAY_REVERSE|MCI_MCIAVI_PLAY_WINDOW|MCI_MCIAVI_PLAY_FULLSCREEN)) if (dwFlags & (MCI_DGV_PLAY_REPEAT|MCI_DGV_PLAY_REVERSE|MCI_MCIAVI_PLAY_WINDOW|MCI_MCIAVI_PLAY_FULLSCREEN))
FIXME("Unsupported flag %08lx\n", dwFlags); FIXME("Unsupported flag %08lx\n", dwFlags);
@ -492,6 +483,7 @@ static DWORD MCIAVI_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms
while (wma->dwStatus == MCI_MODE_PLAY) while (wma->dwStatus == MCI_MODE_PLAY)
{ {
HDC hDC; HDC hDC;
DWORD ret;
tc = GetTickCount(); tc = GetTickCount();
@ -504,7 +496,6 @@ static DWORD MCIAVI_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms
if (wma->lpWaveFormat) { if (wma->lpWaveFormat) {
HANDLE events[2]; HANDLE events[2];
DWORD ret;
events[0] = wma->hStopEvent; events[0] = wma->hStopEvent;
events[1] = wma->hEvent; events[1] = wma->hEvent;
@ -522,15 +513,15 @@ static DWORD MCIAVI_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms
delta = GetTickCount() - tc; delta = GetTickCount() - tc;
if (delta < frameTime) if (delta < frameTime)
{ delta = frameTime - delta;
DWORD ret; else
delta = 0;
LeaveCriticalSection(&wma->cs); LeaveCriticalSection(&wma->cs);
ret = MsgWaitForMultipleObjectsEx(1, &wma->hStopEvent, frameTime - delta, ret = MsgWaitForMultipleObjectsEx(1, &wma->hStopEvent, delta,
QS_ALLINPUT, MWMO_INPUTAVAILABLE); QS_ALLINPUT, MWMO_INPUTAVAILABLE);
EnterCriticalSection(&wma->cs); EnterCriticalSection(&wma->cs);
if (ret == WAIT_OBJECT_0) break; if (ret == WAIT_OBJECT_0) break;
}
if (wma->dwCurrVideoFrame < dwToFrame) if (wma->dwCurrVideoFrame < dwToFrame)
wma->dwCurrVideoFrame++; wma->dwCurrVideoFrame++;
@ -618,6 +609,8 @@ static DWORD MCIAVI_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpPa
EnterCriticalSection(&wma->cs); EnterCriticalSection(&wma->cs);
TRACE("current status %04lx\n", wma->dwStatus);
switch (wma->dwStatus) { switch (wma->dwStatus) {
case MCI_MODE_PLAY: case MCI_MODE_PLAY:
case MCI_MODE_RECORD: case MCI_MODE_RECORD:

View File

@ -80,6 +80,7 @@ typedef struct {
/* data for the background mechanism */ /* data for the background mechanism */
CRITICAL_SECTION cs; CRITICAL_SECTION cs;
HANDLE hStopEvent; HANDLE hStopEvent;
HANDLE ack_event; /* acknowledge that an async command has started */
} WINE_MCIAVI; } WINE_MCIAVI;
extern HINSTANCE MCIAVI_hInstance; extern HINSTANCE MCIAVI_hInstance;