From: Akihiro Sagawa Subject: [3/4] mciqtz32: Fix MCI_NOTIFY behaviour when playing. Message-Id: <20150310005003.DBFD.375B48EC@gmail.com> Date: Tue, 10 Mar 2015 00:50:57 +0900 Should fix bug 31535. --- dlls/mciqtz32/mciqtz.c | 120 +++++++++++++++++++++++++++++++++-------- dlls/mciqtz32/mciqtz_private.h | 4 ++ 2 files changed, 103 insertions(+), 21 deletions(-) diff --git a/dlls/mciqtz32/mciqtz.c b/dlls/mciqtz32/mciqtz.c index 798ff8e..5e81e98 100644 --- a/dlls/mciqtz32/mciqtz.c +++ b/dlls/mciqtz32/mciqtz.c @@ -113,6 +113,8 @@ static DWORD MCIQTZ_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp) if (!wma) return 0; + wma->obs_stop = CreateEventW(NULL, FALSE, FALSE, NULL); + if (!wma->obs_stop) goto err; wma->task.start = CreateEventW(NULL, FALSE, FALSE, NULL); if (!wma->task.start) goto err; wma->task.sync = CreateEventW(NULL, FALSE, FALSE, NULL); @@ -128,6 +130,7 @@ static DWORD MCIQTZ_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp) return modp->wDeviceID; err: + if (wma->obs_stop) CloseHandle(wma->obs_stop); if (wma->task.start) CloseHandle(wma->task.start); if (wma->task.sync) CloseHandle(wma->task.sync); HeapFree(GetProcessHeap(), 0, wma); @@ -157,6 +160,7 @@ static DWORD MCIQTZ_drvClose(DWORD dwDevID) CloseHandle(wma->task.thread); DeleteCriticalSection(&wma->cs); mciSetDriverData(dwDevID, 0); + CloseHandle(wma->obs_stop); HeapFree(GetProcessHeap(), 0, wma); return 1; } @@ -184,6 +188,20 @@ static DWORD MCIQTZ_drvConfigure(DWORD dwDevID) return 1; } +/************************************************************************** + * MCIQTZ_mciNotify [internal] + * + * Notifications in MCI work like a 1-element queue. + * Each new notification request supersedes the previous one. + */ +static void MCIQTZ_mciNotify(DWORD_PTR hWndCallBack, WINE_MCIQTZ* wma, UINT wStatus) +{ + MCIDEVICEID wDevID = wma->notify_devid; + HANDLE old = InterlockedExchangePointer(&wma->callback, NULL); + if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_SUPERSEDED); + mciDriverNotify(HWND_32(LOWORD(hWndCallBack)), wDevID, wStatus); +} + /*************************************************************************** * MCIQTZ_mciOpen [internal] */ @@ -342,6 +360,64 @@ static DWORD MCIQTZ_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpP } /*************************************************************************** + * MCIQTZ_observerThread [internal] + */ +static DWORD CALLBACK MCIQTZ_observerThread(LPVOID arg) +{ + WINE_MCIQTZ* wma = (WINE_MCIQTZ *)arg; + HRESULT hr; + HANDLE handle[2]; + DWORD ret = 0; + + handle[0] = wma->obs_stop; + IMediaEvent_GetEventHandle(wma->mevent, (OAEVENT *)&handle[1]); + TRACE("observerThread (%p,%p)\n", handle[0], handle[1]); + + for (;;) { + DWORD r; + HANDLE old; + + r = WaitForMultipleObjects(2, handle, FALSE, INFINITE); + TRACE(">>> r = %d\n", r); + if (r == WAIT_OBJECT_0) { + old = InterlockedExchangePointer(&wma->callback, NULL); + if (old) + mciDriverNotify(old, wma->notify_devid, MCI_NOTIFY_ABORTED); + break; + } + else if (r == WAIT_OBJECT_0+1) { + LONG event_code; + LONG_PTR p1, p2; + do { + hr = IMediaEvent_GetEvent(wma->mevent, &event_code, &p1, &p2, 0); + if (SUCCEEDED(hr)) { + TRACE("event_code = 0x%02x\n", event_code); + IMediaEvent_FreeEventParams(wma->mevent, event_code, p1, p2); + } + } while (hr == S_OK && event_code != EC_COMPLETE); + if (hr == S_OK && event_code == EC_COMPLETE) { + old = InterlockedExchangePointer(&wma->callback, NULL); + if (old) + mciDriverNotify(old, wma->notify_devid, MCI_NOTIFY_SUCCESSFUL); + break; + } + } + else { + TRACE("Unknown error (%d)\n", (int)r); + break; + } + } + + hr = IMediaControl_Stop(wma->pmctrl); + if (FAILED(hr)) { + TRACE("Cannot stop filtergraph (hr = %x)\n", hr); + ret = MCIERR_INTERNAL; + } + + return ret; +} + +/*************************************************************************** * MCIQTZ_mciPlay [internal] */ static DWORD MCIQTZ_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms) @@ -358,6 +434,15 @@ static DWORD MCIQTZ_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms if (!wma) return MCIERR_INVALID_DEVICE_ID; + ResetEvent(wma->obs_stop); + if (dwFlags & MCI_NOTIFY) { + HANDLE old; + old = InterlockedExchangePointer(&wma->callback, HWND_32(LOWORD(lpParms->dwCallback))); + TRACE("old=%p\n", old); + if (old) + mciDriverNotify(old, wma->notify_devid, MCI_NOTIFY_ABORTED); + } + IMediaSeeking_GetTimeFormat(wma->seek, &format); if (dwFlags & MCI_FROM) { if (IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME)) @@ -384,8 +469,11 @@ static DWORD MCIQTZ_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms IVideoWindow_put_Visible(wma->vidwin, OATRUE); - if (dwFlags & MCI_NOTIFY) - mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)), wDevID, MCI_NOTIFY_SUCCESSFUL); + wma->observer = CreateThread(NULL, 0, MCIQTZ_observerThread, wma, 0, NULL); + if (!wma->observer) { + TRACE("Can't create observer thread\n"); + return MCIERR_INTERNAL; + } return 0; } @@ -426,7 +514,7 @@ static DWORD MCIQTZ_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms } if (dwFlags & MCI_NOTIFY) - mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)), wDevID, MCI_NOTIFY_SUCCESSFUL); + MCIQTZ_mciNotify(lpParms->dwCallback, wma, MCI_NOTIFY_SUCCESSFUL); return 0; } @@ -437,7 +525,6 @@ static DWORD MCIQTZ_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms static DWORD MCIQTZ_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) { WINE_MCIQTZ* wma; - HRESULT hr; TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms); @@ -448,10 +535,11 @@ static DWORD MCIQTZ_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpPa if (!wma->opened) return 0; - hr = IMediaControl_Stop(wma->pmctrl); - if (FAILED(hr)) { - TRACE("Cannot stop filtergraph (hr = %x)\n", hr); - return MCIERR_INTERNAL; + if (wma->observer) { + SetEvent(wma->obs_stop); + WaitForSingleObject(wma->observer, INFINITE); + CloseHandle(wma->observer); + wma->observer = NULL; } if (!wma->parent) @@ -696,19 +784,9 @@ static DWORD MCIQTZ_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_DGV_STATUS_PARMS if (state == State_Stopped) lpParms->dwReturn = MAKEMCIRESOURCE(MCI_MODE_STOP, MCI_MODE_STOP); else if (state == State_Running) { - LONG code; - LONG_PTR p1, p2; - lpParms->dwReturn = MAKEMCIRESOURCE(MCI_MODE_PLAY, MCI_MODE_PLAY); - - do { - hr = IMediaEvent_GetEvent(wma->mevent, &code, &p1, &p2, 0); - if (hr == S_OK && code == EC_COMPLETE){ - lpParms->dwReturn = MAKEMCIRESOURCE(MCI_MODE_STOP, MCI_MODE_STOP); - IMediaControl_Stop(wma->pmctrl); - } - } while (hr == S_OK); - + if (!wma->observer || WaitForSingleObject(wma->observer, 0) == WAIT_OBJECT_0) + lpParms->dwReturn = MAKEMCIRESOURCE(MCI_MODE_STOP, MCI_MODE_STOP); } else if (state == State_Paused) lpParms->dwReturn = MAKEMCIRESOURCE(MCI_MODE_PAUSE, MCI_MODE_PAUSE); ret = MCI_RESOURCE_RETURNED; @@ -734,7 +812,7 @@ static DWORD MCIQTZ_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_DGV_STATUS_PARMS } if (dwFlags & MCI_NOTIFY) - mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)), wDevID, MCI_NOTIFY_SUCCESSFUL); + MCIQTZ_mciNotify(lpParms->dwCallback, wma, MCI_NOTIFY_SUCCESSFUL); return ret; } diff --git a/dlls/mciqtz32/mciqtz_private.h b/dlls/mciqtz32/mciqtz_private.h index 939133f..63306b0 100644 --- a/dlls/mciqtz32/mciqtz_private.h +++ b/dlls/mciqtz32/mciqtz_private.h @@ -51,6 +51,10 @@ typedef struct { HWND parent; CRITICAL_SECTION cs; WINE_MCIQTZ_TASK task; + MCIDEVICEID notify_devid; + HANDLE callback; + HANDLE observer; + HANDLE obs_stop; } WINE_MCIQTZ; #endif /* __WINE_PRIVATE_MCIQTZ_H */