[PATCH v10 2/5] wmp: Add media completion notifications

Anton Romanov theli.ua at gmail.com
Fri Apr 6 14:22:46 CDT 2018


Signed-off-by: Anton Romanov <theli.ua at gmail.com>
---
Moved pumping messages to a helper function
 dlls/wmp/oleobj.c         |  26 ++++++-----
 dlls/wmp/player.c         |  94 +++++++++++++++++++++++++++++++++++++-
 dlls/wmp/tests/media.c    |  89 ++++++++++++++++++++++++++++++++++++
 dlls/wmp/tests/rsrc.rc    |   3 ++
 dlls/wmp/tests/test1s.mp3 | Bin 0 -> 4365 bytes
 dlls/wmp/wmp_main.c       |   1 +
 dlls/wmp/wmp_private.h    |   7 ++-
 7 files changed, 205 insertions(+), 15 deletions(-)
 create mode 100644 dlls/wmp/tests/test1s.mp3

diff --git a/dlls/wmp/oleobj.c b/dlls/wmp/oleobj.c
index cbf183c1f6..a90a0c2c6c 100644
--- a/dlls/wmp/oleobj.c
+++ b/dlls/wmp/oleobj.c
@@ -899,18 +899,20 @@ HRESULT WINAPI WMPFactory_CreateInstance(IClassFactory *iface, IUnknown *outer,
 
     wmp->ref = 1;
 
-    init_player(wmp);
-
-    ConnectionPointContainer_Init(wmp);
-    hdc = GetDC(0);
-    dpi_x = GetDeviceCaps(hdc, LOGPIXELSX);
-    dpi_y = GetDeviceCaps(hdc, LOGPIXELSY);
-    ReleaseDC(0, hdc);
-
-    wmp->extent.cx = MulDiv(192, 2540, dpi_x);
-    wmp->extent.cy = MulDiv(192, 2540, dpi_y);
-
-    hres = IOleObject_QueryInterface(&wmp->IOleObject_iface, riid, ppv);
+    if (init_player(wmp)) {
+        ConnectionPointContainer_Init(wmp);
+        hdc = GetDC(0);
+        dpi_x = GetDeviceCaps(hdc, LOGPIXELSX);
+        dpi_y = GetDeviceCaps(hdc, LOGPIXELSY);
+        ReleaseDC(0, hdc);
+
+        wmp->extent.cx = MulDiv(192, 2540, dpi_x);
+        wmp->extent.cy = MulDiv(192, 2540, dpi_y);
+
+        hres = IOleObject_QueryInterface(&wmp->IOleObject_iface, riid, ppv);
+    } else {
+        hres = E_FAIL;
+    }
     IOleObject_Release(&wmp->IOleObject_iface);
     return hres;
 }
diff --git a/dlls/wmp/player.c b/dlls/wmp/player.c
index b9237b097c..848b90c0c5 100644
--- a/dlls/wmp/player.c
+++ b/dlls/wmp/player.c
@@ -24,6 +24,12 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(wmp);
 
+static ATOM player_msg_class;
+static INIT_ONCE class_init_once;
+static UINT WM_WMPEVENT;
+static const WCHAR WMPmessageW[] = {'_', 'W', 'M', 'P', 'M','e','s','s','a','g','e',0};
+
+
 static void update_state(WindowsMediaPlayer *wmp, LONG type, LONG state)
 {
     DISPPARAMS dispparams;
@@ -213,18 +219,20 @@ static HRESULT WINAPI WMPPlayer4_put_currentMedia(IWMPPlayer4 *iface, IWMPMedia
 {
     WindowsMediaPlayer *This = impl_from_IWMPPlayer4(iface);
     TRACE("(%p)->(%p)\n", This, pMedia);
+
     if(pMedia == NULL) {
         return E_POINTER;
     }
     update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposPlaylistChanging);
     if(This->wmpmedia != NULL) {
+        IWMPControls_stop(&This->IWMPControls_iface);
         IWMPMedia_Release(This->wmpmedia);
     }
     update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposPlaylistChanged);
     update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposPlaylistOpenNoMedia);
 
+    IWMPMedia_AddRef(pMedia);
     This->wmpmedia = pMedia;
-    IWMPMedia_AddRef(This->wmpmedia);
     return S_OK;
 }
 
@@ -1426,6 +1434,20 @@ static HRESULT WINAPI WMPControls_play(IWMPControls *iface)
                     (void**)&This->media_control);
         if (SUCCEEDED(hres))
             update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposMediaOpen);
+        if (SUCCEEDED(hres))
+            hres = IGraphBuilder_QueryInterface(This->filter_graph, &IID_IMediaEvent,
+                    (void**)&This->media_event);
+        if (SUCCEEDED(hres))
+        {
+            IMediaEventEx *media_event_ex = NULL;
+            hres = IGraphBuilder_QueryInterface(This->filter_graph, &IID_IMediaEventEx,
+                    (void**)&media_event_ex);
+            if (SUCCEEDED(hres)) {
+                hres = IMediaEventEx_SetNotifyWindow(media_event_ex, (OAHWND)This->msg_window,
+                        WM_WMPEVENT, (LONG_PTR)This);
+                IMediaEventEx_Release(media_event_ex);
+            }
+        }
     }
 
     update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsTransitioning);
@@ -1458,9 +1480,15 @@ static HRESULT WINAPI WMPControls_stop(IWMPControls *iface)
         hres = IMediaControl_Stop(This->media_control);
         IMediaControl_Release(This->media_control);
     }
+    if (This->media_event) {
+        IMediaEvent_Release(This->media_event);
+    }
+
     IGraphBuilder_Release(This->filter_graph);
     This->filter_graph = NULL;
     This->media_control = NULL;
+    This->media_event = NULL;
+
     update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposPlaylistOpenNoMedia);
     update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsStopped);
     return hres;
@@ -1824,8 +1852,68 @@ static const IWMPMediaVtbl WMPMediaVtbl = {
     WMPMedia_isReadOnlyItem
 };
 
-void init_player(WindowsMediaPlayer *wmp)
+static LRESULT WINAPI player_wnd_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    if (msg == WM_WMPEVENT && wParam == 0) {
+        WindowsMediaPlayer *wmp = (WindowsMediaPlayer*)lParam;
+        LONG event_code;
+        LONG_PTR p1, p2;
+        HRESULT hr;
+        if (wmp->media_event) {
+            do {
+                hr = IMediaEvent_GetEvent(wmp->media_event, &event_code, &p1, &p2, 0);
+                if (SUCCEEDED(hr)) {
+                    TRACE("got event_code = 0x%02x\n", event_code);
+                    IMediaEvent_FreeEventParams(wmp->media_event, event_code, p1, p2);
+                    /* For now we only handle EC_COMPLETE */
+                    if (event_code == EC_COMPLETE) {
+                        update_state(wmp, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsMediaEnded);
+                        update_state(wmp, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsTransitioning);
+                        update_state(wmp, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsStopped);
+                    }
+                }
+            } while (hr == S_OK);
+        } else {
+            FIXME("Got event from quartz when interfaces are already released\n");
+        }
+    }
+    return DefWindowProcW(hwnd, msg, wParam, lParam);
+}
+
+static BOOL WINAPI register_player_msg_class(INIT_ONCE *once, void *param, void **context) {
+    static WNDCLASSEXW wndclass = {
+        sizeof(wndclass), CS_DBLCLKS, player_wnd_proc, 0, 0,
+        NULL, NULL, NULL, NULL, NULL,
+        WMPmessageW, NULL
+    };
+
+    wndclass.hInstance = wmp_instance;
+    player_msg_class = RegisterClassExW(&wndclass);
+    return TRUE;
+}
+
+void unregister_player_msg_class(void) {
+    if(player_msg_class)
+        UnregisterClassW(MAKEINTRESOURCEW(player_msg_class), wmp_instance);
+}
+
+BOOL init_player(WindowsMediaPlayer *wmp)
 {
+    InitOnceExecuteOnce(&class_init_once, register_player_msg_class, NULL, NULL);
+    wmp->msg_window = CreateWindowW( MAKEINTRESOURCEW(player_msg_class), NULL, 0, 0,
+            0, 0, 0, HWND_MESSAGE, 0, wmp_instance, wmp );
+    if (!wmp->msg_window) {
+        ERR("Failed to create message window, GetLastError: %d\n", GetLastError());
+        return FALSE;
+    }
+    if (!WM_WMPEVENT) {
+        WM_WMPEVENT= RegisterWindowMessageW(WMPmessageW);
+    }
+    if (!WM_WMPEVENT) {
+        ERR("Failed to register window message, GetLastError: %d\n", GetLastError());
+        return FALSE;
+    }
+
     wmp->IWMPPlayer4_iface.lpVtbl = &WMPPlayer4Vtbl;
     wmp->IWMPPlayer_iface.lpVtbl = &WMPPlayerVtbl;
     wmp->IWMPSettings_iface.lpVtbl = &WMPSettingsVtbl;
@@ -1834,6 +1922,7 @@ void init_player(WindowsMediaPlayer *wmp)
 
     wmp->invoke_urls = VARIANT_TRUE;
     wmp->auto_start = VARIANT_TRUE;
+    return TRUE;
 }
 
 void destroy_player(WindowsMediaPlayer *wmp)
@@ -1841,6 +1930,7 @@ void destroy_player(WindowsMediaPlayer *wmp)
     IWMPControls_stop(&wmp->IWMPControls_iface);
     if(wmp->wmpmedia)
         IWMPMedia_Release(wmp->wmpmedia);
+    DestroyWindow(wmp->msg_window);
 }
 
 WMPMedia *unsafe_impl_from_IWMPMedia(IWMPMedia *iface)
diff --git a/dlls/wmp/tests/media.c b/dlls/wmp/tests/media.c
index b33be4fa20..3774d475fb 100644
--- a/dlls/wmp/tests/media.c
+++ b/dlls/wmp/tests/media.c
@@ -65,9 +65,11 @@ DEFINE_EXPECT(PLAYSTATE);
 DEFINE_EXPECT(OPENSTATE);
 
 static HANDLE playing_event;
+static HANDLE completed_event;
 static DWORD main_thread_id;
 
 static const WCHAR mp3file[] = {'t','e','s','t','.','m','p','3',0};
+static const WCHAR mp3file1s[] = {'t','e','s','t','1','s','.','m','p','3',0};
 static inline WCHAR *load_resource(const WCHAR *name)
 {
     static WCHAR pathW[MAX_PATH];
@@ -151,6 +153,8 @@ static HRESULT WINAPI WMPOCXEvents_Invoke(IDispatch *iface, DISPID dispIdMember,
             CHECK_EXPECT(PLAYSTATE, V_UI4(pDispParams->rgvarg));
             if (V_UI4(pDispParams->rgvarg) == wmppsPlaying) {
                 SetEvent(playing_event);
+            } else if (V_UI4(pDispParams->rgvarg) == wmppsMediaEnded) {
+                SetEvent(completed_event);
             }
             if (winetest_debug > 1)
                 trace("DISPID_WMPCOREEVENT_PLAYSTATECHANGE, %d\n", V_UI4(pDispParams->rgvarg));
@@ -208,6 +212,88 @@ static HRESULT pump_messages(DWORD dwTimeout, DWORD nCount, const HANDLE *pHandl
     return res;
 }
 
+static void test_completion_event(void)
+{
+    DWORD res = 0;
+    IWMPPlayer4 *player4;
+    HRESULT hres;
+    BSTR filename;
+    IConnectionPointContainer *container;
+    IConnectionPoint *point;
+    IOleObject *oleobj;
+    static DWORD dw = 100;
+
+    hres = CoCreateInstance(&CLSID_WindowsMediaPlayer, NULL, CLSCTX_INPROC_SERVER, &IID_IOleObject, (void**)&oleobj);
+    if(hres == REGDB_E_CLASSNOTREG) {
+        win_skip("CLSID_WindowsMediaPlayer not registered\n");
+        return;
+    }
+    ok(hres == S_OK, "Could not create CLSID_WindowsMediaPlayer instance: %08x\n", hres);
+
+    hres = IOleObject_QueryInterface(oleobj, &IID_IConnectionPointContainer, (void**)&container);
+    ok(hres == S_OK, "QueryInterface(IID_IConnectionPointContainer) failed: %08x\n", hres);
+    if(FAILED(hres))
+        return;
+
+    hres = IConnectionPointContainer_FindConnectionPoint(container, &IID__WMPOCXEvents, &point);
+    IConnectionPointContainer_Release(container);
+    ok(hres == S_OK, "FindConnectionPoint failed: %08x\n", hres);
+
+    hres = IConnectionPoint_Advise(point, (IUnknown*)&WMPOCXEvents, &dw);
+    ok(hres == S_OK, "Advise failed: %08x\n", hres);
+
+    hres = IOleObject_QueryInterface(oleobj, &IID_IWMPPlayer4, (void**)&player4);
+    ok(hres == S_OK, "Could not get IWMPPlayer4 iface: %08x\n", hres);
+
+    filename = SysAllocString(load_resource(mp3file1s));
+
+    SET_EXPECT(OPENSTATE, wmposPlaylistChanging);
+    SET_EXPECT(OPENSTATE, wmposPlaylistOpenNoMedia);
+    SET_EXPECT(OPENSTATE, wmposPlaylistChanged);
+    SET_EXPECT(OPENSTATE, wmposOpeningUnknownURL);
+    SET_EXPECT(OPENSTATE, wmposMediaOpen);
+    SET_EXPECT(OPENSTATE, wmposMediaOpening);
+    SET_EXPECT(PLAYSTATE, wmppsPlaying);
+    SET_EXPECT(PLAYSTATE, wmppsMediaEnded);
+    SET_EXPECT(PLAYSTATE, wmppsStopped);
+    SET_EXPECT(PLAYSTATE, wmppsTransitioning);
+    /* following two are sent on vistau64 vms only */
+    SET_EXPECT(OPENSTATE, wmposMediaChanging);
+    SET_EXPECT(PLAYSTATE, wmppsReady);
+    hres = IWMPPlayer4_put_URL(player4, filename);
+    ok(hres == S_OK, "IWMPPlayer4_put_URL failed: %08x\n", hres);
+    res = pump_messages(3000, 1, &completed_event);
+    ok(res == WAIT_OBJECT_0 || broken(res == WAIT_TIMEOUT), "Timed out while waiting for media to complete\n");
+    
+    if (res == WAIT_TIMEOUT) {
+        /* This happens on Vista Ultimate 64 vms
+         * I have been unable to find out source of this behaviour */
+        win_skip("Failed to play media\n");
+        goto playback_skip;
+    }
+    CHECK_CALLED(OPENSTATE, wmposPlaylistChanging);
+    CHECK_CALLED(OPENSTATE, wmposPlaylistChanged);
+    CHECK_CALLED(OPENSTATE, wmposPlaylistOpenNoMedia);
+    CHECK_CALLED(PLAYSTATE, wmppsTransitioning);
+    CHECK_CALLED(OPENSTATE, wmposOpeningUnknownURL);
+    CHECK_CALLED(OPENSTATE, wmposMediaOpen);
+    /* MediaOpening happens only on xp, 2003 */
+    todo_wine CHECK_CALLED_OR_BROKEN(OPENSTATE, wmposMediaOpening);
+    CHECK_CALLED(PLAYSTATE, wmppsPlaying);
+    CHECK_CALLED(PLAYSTATE, wmppsMediaEnded);
+    CHECK_CALLED(PLAYSTATE, wmppsStopped);
+
+playback_skip:
+    hres = IConnectionPoint_Unadvise(point, dw);
+    ok(hres == S_OK, "Unadvise failed: %08x\n", hres);
+
+    IConnectionPoint_Release(point);
+    IWMPPlayer4_Release(player4);
+    IOleObject_Release(oleobj);
+    DeleteFileW(filename);
+    SysFreeString(filename);
+}
+
 static void test_wmp(void)
 {
     DWORD res = 0;
@@ -339,9 +425,12 @@ START_TEST(media)
 
     main_thread_id = GetCurrentThreadId();
     playing_event = CreateEventW(NULL, FALSE, FALSE, NULL);
+    completed_event = CreateEventW(NULL, FALSE, FALSE, NULL);
     test_wmp();
+    test_completion_event();
 
     CloseHandle(playing_event);
+    CloseHandle(completed_event);
 
     CoUninitialize();
 }
diff --git a/dlls/wmp/tests/rsrc.rc b/dlls/wmp/tests/rsrc.rc
index f33acc1256..ea25a2dea1 100644
--- a/dlls/wmp/tests/rsrc.rc
+++ b/dlls/wmp/tests/rsrc.rc
@@ -21,3 +21,6 @@
 /* ffmpeg -ar 48000 -t 60 -f s16le -acodec pcm_s16le -ac 2 -i /dev/zero -acodec libmp3lame -aq 4 output.mp3 */
 /* @makedep: test.mp3 */
 test.mp3 RCDATA "test.mp3"
+/* ffmpeg -ar 48000 -t 1 -f s16le -acodec pcm_s16le -ac 2 -i /dev/zero -acodec libmp3lame -aq 4 dlls/wmp/tests/test1s.mp3 */
+/* @makedep: test1s.mp3 */
+test1s.mp3 RCDATA "test1s.mp3"
diff --git a/dlls/wmp/tests/test1s.mp3 b/dlls/wmp/tests/test1s.mp3
new file mode 100644
index 0000000000000000000000000000000000000000..3e0b407e3fb8b292bf47fdb383a42b66d06d5568
GIT binary patch
literal 4365
zcmeZtF=k-^0p*b3U{@f`&%nU!lUSB!YOZHttY>Io0G5Ri|9^)d at vt*J^V0HxGC*S(
zv>6x#9xw<Biiyd{C at CqatLy3-8yj0#*x9+bxOjT{`-g^xMn at +kq@|_h<P;W`mzUSp
zHZ^s0boBL2o;-8r%=z<|ELpW`)rJk*w(Z`%`{2Rj$IqNObLrBJ8+Y&Cef;?4%XjbI
zefje1*Z=<@mjK<I40N+0vU?d+m=A#DK<=10|Dfvs-y$gt{2v$?GJ!%I3=F&q3=E7w
zv;@cnGEEv77+8E9eO<x+209J{;FSyw1(`2Lz<dldkp+k_--gEKN72~)3L2X~jmGA0
zp|SawXl(w&e*EblIXPmmiAbNL=7S1NjF1BhfXau_ at EHvs?5P7NI-0*&aE1*?Vl;e4
m!)LVq8m(V&6%wQ6!@!mgxZ(vWPI3EZ)O`H$I~qO|g%1GKzMp&m

literal 0
HcmV?d00001

diff --git a/dlls/wmp/wmp_main.c b/dlls/wmp/wmp_main.c
index 9a33b2762b..273d193e58 100644
--- a/dlls/wmp/wmp_main.c
+++ b/dlls/wmp/wmp_main.c
@@ -92,6 +92,7 @@ BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv)
         break;
     case DLL_PROCESS_DETACH:
         unregister_wmp_class();
+        unregister_player_msg_class();
         break;
     }
 
diff --git a/dlls/wmp/wmp_private.h b/dlls/wmp/wmp_private.h
index 9e84d56ea8..3792e52abd 100644
--- a/dlls/wmp/wmp_private.h
+++ b/dlls/wmp/wmp_private.h
@@ -75,9 +75,13 @@ struct WindowsMediaPlayer {
     /* DirectShow stuff */
     IGraphBuilder* filter_graph;
     IMediaControl* media_control;
+    IMediaEvent* media_event;
+
+    /* Async event notification */
+    HWND msg_window;
 };
 
-void init_player(WindowsMediaPlayer*) DECLSPEC_HIDDEN;
+BOOL init_player(WindowsMediaPlayer*) DECLSPEC_HIDDEN;
 void destroy_player(WindowsMediaPlayer*) DECLSPEC_HIDDEN;
 WMPMedia *unsafe_impl_from_IWMPMedia(IWMPMedia *iface) DECLSPEC_HIDDEN;
 HRESULT create_media_from_url(BSTR url, IWMPMedia **ppMedia) DECLSPEC_HIDDEN;
@@ -88,6 +92,7 @@ void call_sink(ConnectionPoint *This, DISPID dispid, DISPPARAMS *dispparams) DEC
 HRESULT WINAPI WMPFactory_CreateInstance(IClassFactory*,IUnknown*,REFIID,void**) DECLSPEC_HIDDEN;
 
 void unregister_wmp_class(void) DECLSPEC_HIDDEN;
+void unregister_player_msg_class(void) DECLSPEC_HIDDEN;
 
 extern HINSTANCE wmp_instance DECLSPEC_HIDDEN;
 
-- 
2.17.0




More information about the wine-devel mailing list