From: Akihiro Sagawa Subject: [2/2] mciqtz32: Fix notify flag behavior. Message-Id: <20150318000306.19A2.375B48EC@gmail.com> Date: Wed, 18 Mar 2015 00:04:59 +0900 Fixes bug https://bugs.winehq.org/show_bug.cgi?id=14563 . Thanks Bruno for testing with Picasa. --- dlls/mciqtz32/mciqtz.c | 116 +++++++++++++++++++++++++++++++++-------- dlls/mciqtz32/mciqtz_private.h | 6 ++- dlls/winmm/tests/mci.c | 6 +-- 3 files changed, 103 insertions(+), 25 deletions(-) diff --git a/dlls/mciqtz32/mciqtz.c b/dlls/mciqtz32/mciqtz.c index a72f1af..a1c3450 100644 --- a/dlls/mciqtz32/mciqtz.c +++ b/dlls/mciqtz32/mciqtz.c @@ -85,6 +85,7 @@ static DWORD MCIQTZ_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp) if (!wma) return 0; + wma->stop_event = CreateEventW(NULL, FALSE, FALSE, NULL); modp->wType = MCI_DEVTYPE_DIGITAL_VIDEO; wma->wDevID = modp->wDeviceID; modp->wCustomCommandTable = wma->command_table = mciLoadCommandResource(MCIQTZ_hInstance, mciAviWStr, 0); @@ -110,6 +111,7 @@ static DWORD MCIQTZ_drvClose(DWORD dwDevID) mciFreeCommandResource(wma->command_table); mciSetDriverData(dwDevID, 0); + CloseHandle(wma->stop_event); HeapFree(GetProcessHeap(), 0, wma); return 1; } @@ -137,6 +139,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] */ @@ -307,6 +323,63 @@ static DWORD MCIQTZ_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpP } /*************************************************************************** + * MCIQTZ_notifyThread [internal] + */ +static DWORD CALLBACK MCIQTZ_notifyThread(LPVOID parm) +{ + WINE_MCIQTZ* wma = (WINE_MCIQTZ *)parm; + HRESULT hr; + HANDLE handle[2]; + DWORD n = 0, ret = 0; + + handle[n++] = wma->stop_event; + IMediaEvent_GetEventHandle(wma->mevent, (OAEVENT *)&handle[n++]); + + for (;;) { + DWORD r; + HANDLE old; + + r = WaitForMultipleObjects(n, handle, FALSE, INFINITE); + if (r == WAIT_OBJECT_0) { + TRACE("got stop event\n"); + 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("got 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) @@ -326,6 +399,14 @@ static DWORD MCIQTZ_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms if (!wma) return MCIERR_INVALID_DEVICE_ID; + ResetEvent(wma->stop_event); + if (dwFlags & MCI_NOTIFY) { + HANDLE old; + old = InterlockedExchangePointer(&wma->callback, HWND_32(LOWORD(lpParms->dwCallback))); + 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)) @@ -352,8 +433,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->thread = CreateThread(NULL, 0, MCIQTZ_notifyThread, wma, 0, NULL); + if (!wma->thread) { + TRACE("Can't create thread\n"); + return MCIERR_INTERNAL; + } return 0; } @@ -397,7 +481,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; } @@ -408,7 +492,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); @@ -419,10 +502,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->thread) { + SetEvent(wma->stop_event); + WaitForSingleObject(wma->thread, INFINITE); + CloseHandle(wma->thread); + wma->thread = NULL; } if (!wma->parent) @@ -676,19 +760,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->thread || WaitForSingleObject(wma->thread, 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; @@ -714,7 +788,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 7263a26..27939aa 100644 --- a/dlls/mciqtz32/mciqtz_private.h +++ b/dlls/mciqtz32/mciqtz_private.h @@ -38,7 +38,11 @@ typedef struct { IBasicAudio* audio; DWORD time_format; UINT command_table; - HWND parent; + HWND parent; + MCIDEVICEID notify_devid; + HANDLE callback; + HANDLE thread; + HANDLE stop_event; } WINE_MCIQTZ; #endif /* __WINE_PRIVATE_MCIQTZ_H */ diff --git a/dlls/winmm/tests/mci.c b/dlls/winmm/tests/mci.c index 423fceb..7470ed1 100644 --- a/dlls/winmm/tests/mci.c +++ b/dlls/winmm/tests/mci.c @@ -1376,7 +1376,7 @@ static void test_asyncWaveTypeMpegvideo(HWND hwnd) err = mciSendStringA("status mysound mode notify", buf, sizeof(buf), hwnd); ok(!err,"mci status mode returned %s\n", dbg_mcierr(err)); if(!err) ok(!strcmp(buf,"paused"), "mci status mode: %s\n", buf); - todo_wine test_notification(hwnd,"play (superseded)",MCI_NOTIFY_SUPERSEDED); + test_notification(hwnd,"play (superseded)",MCI_NOTIFY_SUPERSEDED); test_notification(hwnd,"status",MCI_NOTIFY_SUCCESSFUL); err = mciSendStringA("seek mysound to start wait", NULL, 0, NULL); @@ -1388,11 +1388,11 @@ static void test_asyncWaveTypeMpegvideo(HWND hwnd) err = mciSendStringA("play mysound to 1500 notify", NULL, 0, hwnd); ok(!err,"mci play returned %s\n", dbg_mcierr(err)); Sleep(200); - todo_wine test_notification(hwnd,"play",0); + test_notification(hwnd,"play",0); err = mciSendStringA("close mysound wait", NULL, 0, NULL); ok(!err,"mci close wait returned %s\n", dbg_mcierr(err)); - todo_wine test_notification(hwnd,"play (aborted by close)",MCI_NOTIFY_ABORTED); + test_notification(hwnd,"play (aborted by close)",MCI_NOTIFY_ABORTED); } START_TEST(mci)