From a072899794919ffc72b5531a9ec10866e287e9ed Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Thu, 12 Apr 2018 22:55:00 -0700 Subject: [PATCH] wmp: Add media completion notifications. Signed-off-by: Anton Romanov Signed-off-by: Jacek Caban Signed-off-by: Alexandre Julliard --- dlls/wmp/oleobj.c | 22 ++++---- dlls/wmp/player.c | 92 ++++++++++++++++++++++++++++++- dlls/wmp/tests/media.c | 113 ++++++++++++++++++++++++++++++++++---- 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, 214 insertions(+), 24 deletions(-) create mode 100644 dlls/wmp/tests/test1s.mp3 diff --git a/dlls/wmp/oleobj.c b/dlls/wmp/oleobj.c index cbf183c1f6d..a90a0c2c6c3 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); + if (init_player(wmp)) { + ConnectionPointContainer_Init(wmp); + hdc = GetDC(0); + dpi_x = GetDeviceCaps(hdc, LOGPIXELSX); + dpi_y = GetDeviceCaps(hdc, LOGPIXELSY); + ReleaseDC(0, hdc); - 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); - wmp->extent.cx = MulDiv(192, 2540, dpi_x); - wmp->extent.cy = MulDiv(192, 2540, dpi_y); - - hres = IOleObject_QueryInterface(&wmp->IOleObject_iface, riid, ppv); + 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 8e012007883..f4ce62a4479 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; @@ -212,18 +218,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; } @@ -1425,6 +1433,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); @@ -1457,9 +1479,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; @@ -1823,8 +1851,66 @@ 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); + WM_WMPEVENT= RegisterWindowMessageW(WMPmessageW); + 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) { + 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; @@ -1833,6 +1919,7 @@ void init_player(WindowsMediaPlayer *wmp) wmp->invoke_urls = VARIANT_TRUE; wmp->auto_start = VARIANT_TRUE; + return TRUE; } void destroy_player(WindowsMediaPlayer *wmp) @@ -1840,6 +1927,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 a7f611c8263..c2cd1cf9412 100644 --- a/dlls/wmp/tests/media.c +++ b/dlls/wmp/tests/media.c @@ -40,16 +40,15 @@ called_ ## kind |= (1 << index); \ }while(0) -#define CHECK_CALLED(kind, index) \ +#define CLEAR_CALLED(kind, index) \ do { \ - ok(called_ ## kind & (1 << index), "expected " #kind ", %d\n", index); \ expect_ ## kind &= ~(1 << index); \ called_ ## kind &= ~(1 << index); \ }while(0) -#define CHECK_CALLED_OR_BROKEN(kind, index) \ +#define CHECK_CALLED(kind, index) \ do { \ - ok(called_ ## kind & (1 << index) || broken(1), "expected " #kind ", %d\n", index); \ + ok(called_ ## kind & (1 << index), "expected " #kind ", %d\n", index); \ expect_ ## kind &= ~(1 << index); \ called_ ## kind &= ~(1 << index); \ }while(0) @@ -65,9 +64,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 +152,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,18 +211,16 @@ static HRESULT pump_messages(DWORD timeout, DWORD count, const HANDLE *handles) return res; } -static void test_wmp(void) +static void test_completion_event(void) { DWORD res = 0; IWMPPlayer4 *player4; - IWMPControls *controls; HRESULT hres; BSTR filename; IConnectionPointContainer *container; IConnectionPoint *point; IOleObject *oleobj; static DWORD dw = 100; - IWMPSettings *settings; hres = CoCreateInstance(&CLSID_WindowsMediaPlayer, NULL, CLSCTX_INPROC_SERVER, &IID_IOleObject, (void**)&oleobj); if(hres == REGDB_E_CLASSNOTREG) { @@ -243,6 +244,86 @@ static void test_wmp(void) 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, "Timed out while waiting for media to complete\n"); + + /* following two are sent on vistau64 vms only */ + CLEAR_CALLED(OPENSTATE, wmposMediaChanging); + CLEAR_CALLED(PLAYSTATE, wmppsReady); + + CHECK_CALLED(OPENSTATE, wmposPlaylistChanging); + CHECK_CALLED(OPENSTATE, wmposPlaylistChanged); + CHECK_CALLED(OPENSTATE, wmposPlaylistOpenNoMedia); + CHECK_CALLED(PLAYSTATE, wmppsTransitioning); + CHECK_CALLED(OPENSTATE, wmposOpeningUnknownURL); + CHECK_CALLED(OPENSTATE, wmposMediaOpen); + CHECK_CALLED(PLAYSTATE, wmppsPlaying); + CHECK_CALLED(PLAYSTATE, wmppsMediaEnded); + CHECK_CALLED(PLAYSTATE, wmppsStopped); + /* MediaOpening happens only on xp, 2003 */ + CLEAR_CALLED(OPENSTATE, wmposMediaOpening); + + 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 BOOL test_wmp(void) +{ + DWORD res = 0; + IWMPPlayer4 *player4; + IWMPControls *controls; + HRESULT hres; + BSTR filename; + IConnectionPointContainer *container; + IConnectionPoint *point; + IOleObject *oleobj; + static DWORD dw = 100; + IWMPSettings *settings; + BOOL test_ran = TRUE; + + 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 FALSE; + } + 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); + + 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); + settings = NULL; hres = IWMPPlayer4_get_settings(player4, &settings); ok(hres == S_OK, "get_settings failed: %08x\n", hres); @@ -277,9 +358,10 @@ static void test_wmp(void) SET_EXPECT(OPENSTATE, wmposOpeningUnknownURL); SET_EXPECT(OPENSTATE, wmposMediaOpen); - SET_EXPECT(OPENSTATE, wmposMediaOpening); SET_EXPECT(PLAYSTATE, wmppsPlaying); SET_EXPECT(PLAYSTATE, wmppsTransitioning); + /* MediaOpening happens only on xp, 2003 */ + SET_EXPECT(OPENSTATE, wmposMediaOpening); hres = IWMPControls_play(controls); ok(hres == S_OK, "IWMPControls_play failed: %08x\n", hres); res = pump_messages(5000, 1, &playing_event); @@ -288,14 +370,15 @@ static void test_wmp(void) /* This happens on Vista Ultimate 64 vms * I have been unable to find out source of this behaviour */ win_skip("Failed to transition media to playing state.\n"); + test_ran = FALSE; goto playback_skip; } 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, wmppsTransitioning); + /* MediaOpening happens only on xp, 2003 */ + CLEAR_CALLED(OPENSTATE, wmposMediaOpening); SET_EXPECT(PLAYSTATE, wmppsStopped); /* The following happens on wine only since we close media on stop */ @@ -331,6 +414,8 @@ playback_skip: IOleObject_Release(oleobj); DeleteFileW(filename); SysFreeString(filename); + + return test_ran; } START_TEST(media) @@ -339,9 +424,15 @@ START_TEST(media) main_thread_id = GetCurrentThreadId(); playing_event = CreateEventW(NULL, FALSE, FALSE, NULL); - test_wmp(); + completed_event = CreateEventW(NULL, FALSE, FALSE, NULL); + if (test_wmp()) { + test_completion_event(); + } else { + win_skip("Failed to play media\n"); + } CloseHandle(playing_event); + CloseHandle(completed_event); CoUninitialize(); } diff --git a/dlls/wmp/tests/rsrc.rc b/dlls/wmp/tests/rsrc.rc index f33acc12563..a92079e798b 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 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@vt*J^V0HxGC*S( zv>6x#9xw