Signed-off-by: Anton Romanov theli.ua@gmail.com --- dlls/wmp/events.c | 11 +++ dlls/wmp/oleobj.c | 2 +- dlls/wmp/player.c | 50 ++++++++++- dlls/wmp/tests/media.c | 229 ++++++++++++++++++++++++++++++++++++++++++++++++- dlls/wmp/wmp_main.c | 1 + dlls/wmp/wmp_private.h | 1 + include/wmpids.h | 24 ++++++ 7 files changed, 313 insertions(+), 5 deletions(-) create mode 100644 include/wmpids.h
diff --git a/dlls/wmp/events.c b/dlls/wmp/events.c index a908834010..412eb307d1 100644 --- a/dlls/wmp/events.c +++ b/dlls/wmp/events.c @@ -400,3 +400,14 @@ void ConnectionPointContainer_Destroy(WindowsMediaPlayer *wmp) { ConnectionPoint_Destroy(wmp->wmpocx); } + +void call_sink(ConnectionPoint *This, DISPID dispid, DISPPARAMS *dispparams) +{ + DWORD i; + + for(i=0; i<This->sinks_size; i++) { + if(This->sinks[i]) + IDispatch_Invoke(This->sinks[i], dispid, &IID_NULL, LOCALE_SYSTEM_DEFAULT, + DISPATCH_METHOD, dispparams, NULL, NULL, NULL); + } +} diff --git a/dlls/wmp/oleobj.c b/dlls/wmp/oleobj.c index cc0e9a9d98..cbf183c1f6 100644 --- a/dlls/wmp/oleobj.c +++ b/dlls/wmp/oleobj.c @@ -307,8 +307,8 @@ static ULONG WINAPI OleObject_Release(IOleObject *iface)
if(!ref) { release_client_site(This); - ConnectionPointContainer_Destroy(This); destroy_player(This); + ConnectionPointContainer_Destroy(This); heap_free(This); }
diff --git a/dlls/wmp/player.c b/dlls/wmp/player.c index 3d0df96bd8..b9237b097c 100644 --- a/dlls/wmp/player.c +++ b/dlls/wmp/player.c @@ -20,9 +20,27 @@
#include "wine/debug.h" #include <nserror.h> +#include "wmpids.h"
WINE_DEFAULT_DEBUG_CHANNEL(wmp);
+static void update_state(WindowsMediaPlayer *wmp, LONG type, LONG state) +{ + DISPPARAMS dispparams; + VARIANTARG params[1]; + + dispparams.cArgs = 1; + dispparams.cNamedArgs = 0; + dispparams.rgdispidNamedArgs = NULL; + dispparams.rgvarg = params; + + V_VT(params) = VT_UI4; + V_UI4(params) = state; + + call_sink(wmp->wmpocx, type, + &dispparams); +} + static inline WMPMedia *impl_from_IWMPMedia(IWMPMedia *iface) { return CONTAINING_RECORD(iface, WMPMedia, IWMPMedia_iface); @@ -125,14 +143,21 @@ static HRESULT WINAPI WMPPlayer4_put_URL(IWMPPlayer4 *iface, BSTR url) if(url == NULL) { return E_POINTER; } + hres = create_media_from_url(url, &media); + if (SUCCEEDED(hres)) { + update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsTransitioning); hres = IWMPPlayer4_put_currentMedia(iface, media); - IWMPMedia_Release(media); /* put will addref */ } - if (SUCCEEDED(hres) && This->auto_start) { - hres = IWMPControls_play(&This->IWMPControls_iface); + if (SUCCEEDED(hres)) { + IWMPMedia_Release(media); /* put will addref */ + update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsReady); + if (This->auto_start == VARIANT_TRUE) { + hres = IWMPControls_play(&This->IWMPControls_iface); + } } + return hres; }
@@ -191,9 +216,13 @@ static HRESULT WINAPI WMPPlayer4_put_currentMedia(IWMPPlayer4 *iface, IWMPMedia if(pMedia == NULL) { return E_POINTER; } + update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposPlaylistChanging); if(This->wmpmedia != NULL) { IWMPMedia_Release(This->wmpmedia); } + update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposPlaylistChanged); + update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposPlaylistOpenNoMedia); + This->wmpmedia = pMedia; IWMPMedia_AddRef(This->wmpmedia); return S_OK; @@ -1388,19 +1417,32 @@ static HRESULT WINAPI WMPControls_play(IWMPControls *iface) CLSCTX_INPROC_SERVER, &IID_IGraphBuilder, (void **)&This->filter_graph); + update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposOpeningUnknownURL); + if (SUCCEEDED(hres)) hres = IGraphBuilder_RenderFile(This->filter_graph, media->url, NULL); if (SUCCEEDED(hres)) hres = IGraphBuilder_QueryInterface(This->filter_graph, &IID_IMediaControl, (void**)&This->media_control); + if (SUCCEEDED(hres)) + update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposMediaOpen); }
+ update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsTransitioning); + if (SUCCEEDED(hres)) hres = IMediaControl_Run(This->media_control);
if (hres == S_FALSE) { hres = S_OK; /* S_FALSE will mean that graph is transitioning and that is fine */ } + + if (SUCCEEDED(hres)) { + update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsPlaying); + } else { + update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsUndefined); + } + return hres; }
@@ -1419,6 +1461,8 @@ static HRESULT WINAPI WMPControls_stop(IWMPControls *iface) IGraphBuilder_Release(This->filter_graph); This->filter_graph = NULL; This->media_control = NULL; + update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposPlaylistOpenNoMedia); + update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsStopped); return hres; }
diff --git a/dlls/wmp/tests/media.c b/dlls/wmp/tests/media.c index 6e4a0c170f..633e8ebbf4 100644 --- a/dlls/wmp/tests/media.c +++ b/dlls/wmp/tests/media.c @@ -19,9 +19,54 @@ #include <wmp.h> #include <olectl.h> #include <nserror.h> +#include <wmpids.h> +#include <math.h> +#include <assert.h>
#include "wine/test.h"
+#define DEFINE_EXPECT(kind) \ + static DWORD expect_ ## kind = 0, called_ ## kind = 0 + +#define SET_EXPECT(kind, index) \ + do { \ + assert(index < 8 * sizeof(expect_ ## kind)); \ + expect_ ## kind |= (1 << index); \ + }while(0) + +#define CHECK_EXPECT(kind, index) \ + do { \ + ok(expect_ ##kind & (1 << index), "unexpected event for " #kind ", index:%d\n", index); \ + called_ ## kind |= (1 << index); \ + }while(0) + +#define CHECK_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) \ + do { \ + ok(called_ ## kind & (1 << index) || broken(1), "expected " #kind ", %d\n", index); \ + expect_ ## kind &= ~(1 << index); \ + called_ ## kind &= ~(1 << index); \ + }while(0) + +#define CHECK_NOT_CALLED(kind, index) \ + do { \ + ok(!(called_ ## kind & (1 << index)), "not expected " #kind ", %d\n", index); \ + expect_ ## kind &= ~(1 << index); \ + called_ ## kind &= ~(1 << index); \ + }while(0) + +DEFINE_EXPECT(PLAYSTATE); +DEFINE_EXPECT(OPENSTATE); + +static HANDLE playing_event; +static DWORD main_thread_id; + static const WCHAR mp3file[] = {'t','e','s','t','.','m','p','3',0}; static inline WCHAR *load_resource(const WCHAR *name) { @@ -48,14 +93,112 @@ static inline WCHAR *load_resource(const WCHAR *name) return pathW; }
+static ULONG WINAPI Dispatch_AddRef(IDispatch *iface) +{ + return 2; +} + +static ULONG WINAPI Dispatch_Release(IDispatch *iface) +{ + return 1; +} + +static HRESULT WINAPI Dispatch_GetTypeInfoCount(IDispatch *iface, UINT *pctinfo) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Dispatch_GetTypeInfo(IDispatch *iface, UINT iTInfo, LCID lcid, + ITypeInfo **ppTInfo) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Dispatch_GetIDsOfNames(IDispatch *iface, REFIID riid, LPOLESTR *rgszNames, + UINT cNames, LCID lcid, DISPID *rgDispId) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI WMPOCXEvents_QueryInterface(IDispatch *iface, REFIID riid, void **ppv) +{ + *ppv = NULL; + + if(IsEqualGUID(&IID__WMPOCXEvents, riid) || IsEqualGUID(&IID_IDispatch, riid)) { + *ppv = iface; + return S_OK; + } + + ok(0, "unexpected riid %s\n", wine_dbgstr_guid(riid)); + return E_NOINTERFACE; +} + +static HRESULT WINAPI WMPOCXEvents_Invoke(IDispatch *iface, DISPID dispIdMember, REFIID riid, + LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, + EXCEPINFO *pExcepInfo, UINT *puArgErr) +{ + ok(main_thread_id == GetCurrentThreadId(), "Got notification outside of main thread!\n"); + switch(dispIdMember) { + case DISPID_WMPCOREEVENT_OPENSTATECHANGE: + CHECK_EXPECT(OPENSTATE, V_UI4(pDispParams->rgvarg)); + if (winetest_debug > 1) + trace("DISPID_WMPCOREEVENT_OPENSTATECHANGE, %d\n", V_UI4(pDispParams->rgvarg)); + break; + case DISPID_WMPCOREEVENT_PLAYSTATECHANGE: + CHECK_EXPECT(PLAYSTATE, V_UI4(pDispParams->rgvarg)); + if (V_UI4(pDispParams->rgvarg) == wmppsPlaying) { + SetEvent(playing_event); + } + if (winetest_debug > 1) + trace("DISPID_WMPCOREEVENT_PLAYSTATECHANGE, %d\n", V_UI4(pDispParams->rgvarg)); + break; + case DISPID_WMPCOREEVENT_MEDIACHANGE: + if (winetest_debug > 1) + trace("DISPID_WMPCOREEVENT_MEDIACHANGE\n"); + break; + case DISPID_WMPCOREEVENT_CURRENTITEMCHANGE: + if (winetest_debug > 1) + trace("DISPID_WMPCOREEVENT_CURRENTITEMCHANGE\n"); + break; + case DISPID_WMPCOREEVENT_STATUSCHANGE: + if (winetest_debug > 1) + trace("DISPID_WMPCOREEVENT_STATUSCHANGE\n"); + break; + default: + if (winetest_debug > 1) + trace("event: %d\n", dispIdMember); + break; + } + + return E_NOTIMPL; +} + +static IDispatchVtbl WMPOcxEventsVtbl = { + WMPOCXEvents_QueryInterface, + Dispatch_AddRef, + Dispatch_Release, + Dispatch_GetTypeInfoCount, + Dispatch_GetTypeInfo, + Dispatch_GetIDsOfNames, + WMPOCXEvents_Invoke, +}; + +static IDispatch WMPOCXEvents = { &WMPOcxEventsVtbl }; + static void 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;
hres = CoCreateInstance(&CLSID_WindowsMediaPlayer, NULL, CLSCTX_INPROC_SERVER, &IID_IOleObject, (void**)&oleobj); @@ -65,6 +208,18 @@ static void test_wmp(void) } 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);
@@ -87,22 +242,90 @@ static void test_wmp(void)
filename = SysAllocString(load_resource(mp3file));
+ SET_EXPECT(OPENSTATE, wmposPlaylistChanging); + SET_EXPECT(OPENSTATE, wmposPlaylistOpenNoMedia); + SET_EXPECT(OPENSTATE, wmposPlaylistChanged); + SET_EXPECT(PLAYSTATE, wmppsTransitioning); + SET_EXPECT(PLAYSTATE, wmppsReady); hres = IWMPPlayer4_put_URL(player4, filename); ok(hres == S_OK, "IWMPPlayer4_put_URL failed: %08x\n", hres); + CHECK_CALLED(OPENSTATE, wmposPlaylistChanging); + CHECK_CALLED(OPENSTATE, wmposPlaylistChanged); + CHECK_CALLED(OPENSTATE, wmposPlaylistOpenNoMedia); + CHECK_CALLED(PLAYSTATE, wmppsTransitioning); + CHECK_CALLED(PLAYSTATE, wmppsReady);
+ SET_EXPECT(OPENSTATE, wmposOpeningUnknownURL); + SET_EXPECT(OPENSTATE, wmposMediaOpen); + SET_EXPECT(OPENSTATE, wmposMediaOpening); + SET_EXPECT(PLAYSTATE, wmppsPlaying); + SET_EXPECT(PLAYSTATE, wmppsTransitioning); hres = IWMPControls_play(controls); + ok(hres == S_OK, "IWMPControls_play failed: %08x\n", hres); + { + MSG msg; + DWORD start_time = GetTickCount(); + DWORD dwTimeout = 5000; + HANDLE handles[1]; + handles[0] = playing_event; + do { + DWORD now = GetTickCount(); + res = MsgWaitForMultipleObjectsEx(1, handles, start_time + dwTimeout - now, + QS_ALLINPUT ,MWMO_ALERTABLE | MWMO_INPUTAVAILABLE); + if (res == WAIT_OBJECT_0 + 1) { + GetMessageW(&msg, 0, 0, 0); + if (winetest_debug > 1) + trace("Dispatching %d\n", msg.message); + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + while (res == WAIT_OBJECT_0 + 1); + ok(res == WAIT_OBJECT_0 || broken(res == WAIT_TIMEOUT), "Timed out while waiting for media to become ready\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 transition media to playing state.\n"); + 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);
+ SET_EXPECT(PLAYSTATE, wmppsStopped); + /* The following happens on wine only since we close media on stop */ + SET_EXPECT(OPENSTATE, wmposPlaylistOpenNoMedia); hres = IWMPControls_stop(controls); ok(hres == S_OK, "IWMPControls_stop failed: %08x\n", hres); + CHECK_CALLED(PLAYSTATE, wmppsStopped); + todo_wine CHECK_NOT_CALLED(OPENSTATE, wmposPlaylistOpenNoMedia);
/* Already Stopped */ hres = IWMPControls_stop(controls); ok(hres == NS_S_WMPCORE_COMMAND_NOT_AVAILABLE, "IWMPControls_stop is available: %08x\n", hres);
+ SET_EXPECT(PLAYSTATE, wmppsPlaying); + /* The following happens on wine only since we close media on stop */ + SET_EXPECT(OPENSTATE, wmposOpeningUnknownURL); + SET_EXPECT(OPENSTATE, wmposMediaOpen); + SET_EXPECT(PLAYSTATE, wmppsTransitioning); hres = IWMPControls_play(controls); ok(hres == S_OK, "IWMPControls_play failed: %08x\n", hres); + CHECK_CALLED(PLAYSTATE, wmppsPlaying); + todo_wine CHECK_NOT_CALLED(OPENSTATE, wmposOpeningUnknownURL); + todo_wine CHECK_NOT_CALLED(OPENSTATE, wmposMediaOpen); + todo_wine CHECK_NOT_CALLED(PLAYSTATE, wmppsTransitioning);
+playback_skip: + hres = IConnectionPoint_Unadvise(point, dw); + ok(hres == S_OK, "Unadvise failed: %08x\n", hres); + + IConnectionPoint_Release(point); IWMPControls_Release(controls); IWMPPlayer4_Release(player4); IOleObject_Release(oleobj); @@ -114,7 +337,11 @@ START_TEST(media) { CoInitialize(NULL);
+ main_thread_id = GetCurrentThreadId(); + playing_event = CreateEventW(NULL, FALSE, FALSE, NULL); test_wmp();
+ CloseHandle(playing_event); + CoUninitialize(); } diff --git a/dlls/wmp/wmp_main.c b/dlls/wmp/wmp_main.c index 29b096f7fd..9a33b2762b 100644 --- a/dlls/wmp/wmp_main.c +++ b/dlls/wmp/wmp_main.c @@ -25,6 +25,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(wmp);
HINSTANCE wmp_instance; +DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID riid, void **ppv) { diff --git a/dlls/wmp/wmp_private.h b/dlls/wmp/wmp_private.h index 05ad5889e2..9e84d56ea8 100644 --- a/dlls/wmp/wmp_private.h +++ b/dlls/wmp/wmp_private.h @@ -83,6 +83,7 @@ WMPMedia *unsafe_impl_from_IWMPMedia(IWMPMedia *iface) DECLSPEC_HIDDEN; HRESULT create_media_from_url(BSTR url, IWMPMedia **ppMedia) DECLSPEC_HIDDEN; void ConnectionPointContainer_Init(WindowsMediaPlayer *wmp) DECLSPEC_HIDDEN; void ConnectionPointContainer_Destroy(WindowsMediaPlayer *wmp) DECLSPEC_HIDDEN; +void call_sink(ConnectionPoint *This, DISPID dispid, DISPPARAMS *dispparams) DECLSPEC_HIDDEN;
HRESULT WINAPI WMPFactory_CreateInstance(IClassFactory*,IUnknown*,REFIID,void**) DECLSPEC_HIDDEN;
diff --git a/include/wmpids.h b/include/wmpids.h new file mode 100644 index 0000000000..a8500409ac --- /dev/null +++ b/include/wmpids.h @@ -0,0 +1,24 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* WMPCoreEvents */ +#define DISPID_WMPCOREEVENT_OPENSTATECHANGE 5001 +#define DISPID_WMPCOREEVENT_STATUSCHANGE 5002 + +#define DISPID_WMPCOREEVENT_PLAYSTATECHANGE 5101 + +#define DISPID_WMPCOREEVENT_MEDIACHANGE 5802 +#define DISPID_WMPCOREEVENT_CURRENTITEMCHANGE 5806
Signed-off-by: Anton Romanov theli.ua@gmail.com --- dlls/wmp/oleobj.c | 26 ++++++----- dlls/wmp/player.c | 86 +++++++++++++++++++++++++++++++++++- dlls/wmp/tests/media.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++ 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 | 9 +++- 7 files changed, 219 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..8c36d5e9e7 100644 --- a/dlls/wmp/player.c +++ b/dlls/wmp/player.c @@ -213,18 +213,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 +1428,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 +1474,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 +1846,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) +{ + switch(msg) { + case WM_WMPEVENT: + if (wParam == 0) { + WindowsMediaPlayer *wmp = (WindowsMediaPlayer*)lParam; + LONG event_code; + LONG_PTR p1, p2; + HRESULT hr; + 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); + IWMPControls_stop(&wmp->IWMPControls_iface); + break; + } + } + } while (hr == S_OK); + break; + } + } + return DefWindowProcW(hwnd, msg, wParam, lParam); +} + +static ATOM player_msg_class; +static INIT_ONCE class_init_once; + +static BOOL WINAPI register_player_msg_class(INIT_ONCE *once, void *param, void **context) { + static const WCHAR messageW[] = {'_', 'W', 'M', 'P', 'M','e','s','s','a','g','e',0}; + + static WNDCLASSEXW wndclass = { + sizeof(wndclass), CS_DBLCLKS, player_wnd_proc, 0, 0, + NULL, NULL, NULL, NULL, NULL, + messageW, 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\n"); + return FALSE; + } + wmp->IWMPPlayer4_iface.lpVtbl = &WMPPlayer4Vtbl; wmp->IWMPPlayer_iface.lpVtbl = &WMPPlayerVtbl; wmp->IWMPSettings_iface.lpVtbl = &WMPSettingsVtbl; @@ -1834,6 +1914,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 +1922,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 633e8ebbf4..2d1222e28a 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)); @@ -188,6 +192,108 @@ static IDispatchVtbl WMPOcxEventsVtbl = {
static IDispatch WMPOCXEvents = { &WMPOcxEventsVtbl };
+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); + + { + MSG msg; + DWORD start_time = GetTickCount(); + DWORD dwTimeout = 3000; + HANDLE handles[1]; + handles[0] = completed_event; + do { + DWORD now = GetTickCount(); + res = MsgWaitForMultipleObjectsEx(1, handles, start_time + dwTimeout - now, + QS_ALLINPUT ,MWMO_ALERTABLE | MWMO_INPUTAVAILABLE); + if (res == WAIT_OBJECT_0 + 1) { + GetMessageW(&msg, 0, 0, 0); + if (winetest_debug > 1) + trace("Dispatching %d\n", msg.message); + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + while (res == WAIT_OBJECT_0 + 1 || res == WAIT_IO_COMPLETION); + 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 +445,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@vt*J^V0HxGC*S( zv>6x#9xw<Biiyd{C@CqatLy3-8yj0#*x9+bxOjT{`-g^xMn@+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_@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..df778f1025 100644 --- a/dlls/wmp/wmp_private.h +++ b/dlls/wmp/wmp_private.h @@ -75,9 +75,15 @@ 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; +#define WM_WMPEVENT (WM_USER+0x300) + +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 +94,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;
Signed-off-by: Anton Romanov theli.ua@gmail.com --- dlls/wmp/player.c | 66 ++++++++++++++++++++++++++++++++++++++++++-------- dlls/wmp/tests/media.c | 33 +++++++++++++++++++++++++ dlls/wmp/wmp_private.h | 3 +++ 3 files changed, 92 insertions(+), 10 deletions(-)
diff --git a/dlls/wmp/player.c b/dlls/wmp/player.c index 8c36d5e9e7..b405ff8c74 100644 --- a/dlls/wmp/player.c +++ b/dlls/wmp/player.c @@ -1391,8 +1391,22 @@ static HRESULT WINAPI WMPControls_Invoke(IWMPControls *iface, DISPID dispIdMembe static HRESULT WINAPI WMPControls_get_isAvailable(IWMPControls *iface, BSTR bstrItem, VARIANT_BOOL *pIsAvailable) { WindowsMediaPlayer *This = impl_from_IWMPControls(iface); - FIXME("(%p)->(%s)\n", This, debugstr_w(bstrItem)); - return E_NOTIMPL; + static const WCHAR currentPosition[] = {'c','u','r','r','e','n','t','P','o','s','i','t','i','o','n',0}; + TRACE("(%p)->(%s %p)\n", This, debugstr_w(bstrItem), pIsAvailable); + *pIsAvailable = VARIANT_FALSE; + if (!This->filter_graph) { + *pIsAvailable = VARIANT_FALSE; + } else if (strcmpW(currentPosition, bstrItem) == 0) { + DWORD capabilities; + IMediaSeeking_GetCapabilities(This->media_seeking, &capabilities); + *pIsAvailable = (capabilities & AM_SEEKING_CanSeekAbsolute) ? + VARIANT_TRUE : VARIANT_FALSE; + } else { + FIXME("%s not implemented\n", debugstr_w(bstrItem)); + return E_NOTIMPL; + } + + return S_OK; }
static HRESULT WINAPI WMPControls_play(IWMPControls *iface) @@ -1423,11 +1437,16 @@ static HRESULT WINAPI WMPControls_play(IWMPControls *iface)
if (SUCCEEDED(hres)) hres = IGraphBuilder_RenderFile(This->filter_graph, media->url, NULL); + if (SUCCEEDED(hres)) + update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposMediaOpen); if (SUCCEEDED(hres)) hres = IGraphBuilder_QueryInterface(This->filter_graph, &IID_IMediaControl, (void**)&This->media_control); if (SUCCEEDED(hres)) - update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposMediaOpen); + hres = IGraphBuilder_QueryInterface(This->filter_graph, &IID_IMediaSeeking, + (void**)&This->media_seeking); + if (SUCCEEDED(hres)) + hres = IMediaSeeking_SetTimeFormat(This->media_seeking, &TIME_FORMAT_MEDIA_TIME); if (SUCCEEDED(hres)) hres = IGraphBuilder_QueryInterface(This->filter_graph, &IID_IMediaEvent, (void**)&This->media_event); @@ -1454,7 +1473,10 @@ static HRESULT WINAPI WMPControls_play(IWMPControls *iface) }
if (SUCCEEDED(hres)) { + LONGLONG duration; update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsPlaying); + if (SUCCEEDED(IMediaSeeking_GetDuration(This->media_seeking, &duration))) + media->duration = (DOUBLE)duration / 10000000.0f; } else { update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsUndefined); } @@ -1477,11 +1499,14 @@ static HRESULT WINAPI WMPControls_stop(IWMPControls *iface) if (This->media_event) { IMediaEvent_Release(This->media_event); } - + if (This->media_seeking) { + IMediaSeeking_Release(This->media_seeking); + } IGraphBuilder_Release(This->filter_graph); This->filter_graph = NULL; This->media_control = NULL; This->media_event = NULL; + This->media_seeking = NULL;
update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposPlaylistOpenNoMedia); update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsStopped); @@ -1512,15 +1537,33 @@ static HRESULT WINAPI WMPControls_fastReverse(IWMPControls *iface) static HRESULT WINAPI WMPControls_get_currentPosition(IWMPControls *iface, DOUBLE *pdCurrentPosition) { WindowsMediaPlayer *This = impl_from_IWMPControls(iface); - FIXME("(%p)->(%p)\n", This, pdCurrentPosition); - return E_NOTIMPL; + HRESULT hres; + LONGLONG currentPosition; + + TRACE("(%p)->(%p)\n", This, pdCurrentPosition); + if (!This->media_seeking) + return S_FALSE; + + hres = IMediaSeeking_GetCurrentPosition(This->media_seeking, ¤tPosition); + *pdCurrentPosition = (DOUBLE) currentPosition / 10000000.0f; + TRACE("hres: %d, pos: %f\n", hres, *pdCurrentPosition); + return hres; }
static HRESULT WINAPI WMPControls_put_currentPosition(IWMPControls *iface, DOUBLE dCurrentPosition) { + LONGLONG Current; + HRESULT hres; WindowsMediaPlayer *This = impl_from_IWMPControls(iface); - FIXME("(%p)->(%f)\n", This, dCurrentPosition); - return E_NOTIMPL; + TRACE("(%p)->(%f)\n", This, dCurrentPosition); + if (!This->media_seeking) + return S_FALSE; + + Current = 10000000 * dCurrentPosition; + hres = IMediaSeeking_SetPositions(This->media_seeking, &Current, + AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning); + + return hres; }
static HRESULT WINAPI WMPControls_get_currentPositionString(IWMPControls *iface, BSTR *pbstrCurrentPosition) @@ -1757,9 +1800,12 @@ static HRESULT WINAPI WMPMedia_getMarkerName(IWMPMedia *iface, LONG MarkerNum, B
static HRESULT WINAPI WMPMedia_get_duration(IWMPMedia *iface, DOUBLE *pDuration) { + /* MSDN: If this property is used with a media item other than the one + * specified in Player.currentMedia, it may not contain a valid value. */ WMPMedia *This = impl_from_IWMPMedia(iface); - FIXME("(%p)->(%p)\n", This, pDuration); - return E_NOTIMPL; + TRACE("(%p)->(%p)\n", This, pDuration); + *pDuration = This->duration; + return S_OK; }
static HRESULT WINAPI WMPMedia_get_durationString(IWMPMedia *iface, BSTR *pbstrDuration) diff --git a/dlls/wmp/tests/media.c b/dlls/wmp/tests/media.c index 2d1222e28a..6f5eb21b3e 100644 --- a/dlls/wmp/tests/media.c +++ b/dlls/wmp/tests/media.c @@ -306,6 +306,11 @@ static void test_wmp(void) IOleObject *oleobj; static DWORD dw = 100; IWMPSettings *settings; + DOUBLE duration; + VARIANT_BOOL vbool; + IWMPMedia *media; + static const WCHAR currentPosition[] = {'c','u','r','r','e','n','t','P','o','s','i','t','i','o','n',0}; + BSTR bstrcurrentPosition = SysAllocString(currentPosition);
hres = CoCreateInstance(&CLSID_WindowsMediaPlayer, NULL, CLSCTX_INPROC_SERVER, &IID_IOleObject, (void**)&oleobj); if(hres == REGDB_E_CLASSNOTREG) { @@ -343,6 +348,10 @@ static void test_wmp(void) ok(hres == S_OK, "get_controls failed: %08x\n", hres); ok(controls != NULL, "controls = NULL\n");
+ hres = IWMPControls_get_isAvailable(controls, bstrcurrentPosition, &vbool); + ok(hres == S_OK, "IWMPControls_get_isAvailable failed: %08x\n", hres); + ok(vbool == VARIANT_FALSE, "unexpected value\n"); + hres = IWMPControls_play(controls); ok(hres == NS_S_WMPCORE_COMMAND_NOT_AVAILABLE, "IWMPControls_play is available: %08x\n", hres);
@@ -403,6 +412,29 @@ static void test_wmp(void) CHECK_CALLED(PLAYSTATE, wmppsPlaying); CHECK_CALLED(PLAYSTATE, wmppsTransitioning);
+ hres = IWMPControls_get_isAvailable(controls, bstrcurrentPosition, &vbool); + ok(hres == S_OK, "IWMPControls_get_isAvailable failed: %08x\n", hres); + ok(vbool == VARIANT_TRUE, "unexpected value\n"); + + duration = 0.0; + hres = IWMPControls_get_currentPosition(controls, &duration); + ok(hres == S_OK, "IWMPControls_get_currentPosition failed: %08x\n", hres); + ok((int)duration == 0, "unexpected value %f\n", duration); + + hres = IWMPControls_put_currentPosition(controls, duration); + ok(hres == S_OK, "IWMPControls_put_currentPosition failed: %08x\n", hres); + + hres = IWMPPlayer4_get_currentMedia(player4, &media); + ok(hres == S_OK, "IWMPPlayer4_get_currentMedia failed: %08x\n", hres); + hres = IWMPMedia_get_duration(media, &duration); + ok(hres == S_OK, "IWMPMedia_get_duration failed: %08x\n", hres); + ok( + round(duration) == 60 || + broken(round(duration) == 30) || + broken(round(duration) == 57) + , "unexpected value: %f\n", duration); + IWMPMedia_Release(media); + SET_EXPECT(PLAYSTATE, wmppsStopped); /* The following happens on wine only since we close media on stop */ SET_EXPECT(OPENSTATE, wmposPlaylistOpenNoMedia); @@ -437,6 +469,7 @@ playback_skip: IOleObject_Release(oleobj); DeleteFileW(filename); SysFreeString(filename); + SysFreeString(bstrcurrentPosition); }
START_TEST(media) diff --git a/dlls/wmp/wmp_private.h b/dlls/wmp/wmp_private.h index df778f1025..c5ea0a93d2 100644 --- a/dlls/wmp/wmp_private.h +++ b/dlls/wmp/wmp_private.h @@ -42,6 +42,8 @@ typedef struct { LONG ref;
WCHAR *url; + + DOUBLE duration; } WMPMedia;
struct WindowsMediaPlayer { @@ -76,6 +78,7 @@ struct WindowsMediaPlayer { IGraphBuilder* filter_graph; IMediaControl* media_control; IMediaEvent* media_event; + IMediaSeeking* media_seeking;
/* Async event notification */ HWND msg_window;
Signed-off-by: Anton Romanov theli.ua@gmail.com --- dlls/wmp/player.c | 27 +++++++++++++++++++++++---- dlls/wmp/tests/media.c | 16 ++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-)
diff --git a/dlls/wmp/player.c b/dlls/wmp/player.c index b405ff8c74..08a78ab044 100644 --- a/dlls/wmp/player.c +++ b/dlls/wmp/player.c @@ -1145,8 +1145,17 @@ static HRESULT WINAPI WMPNetwork_get_bufferingCount(IWMPNetwork *iface, LONG *pl static HRESULT WINAPI WMPNetwork_get_bufferingProgress(IWMPNetwork *iface, LONG *plBufferingProgress) { WindowsMediaPlayer *This = impl_from_IWMPNetwork(iface); - FIXME("(%p)->(%p)\n", This, plBufferingProgress); - return E_NOTIMPL; + TRACE("(%p)->(%p)\n", This, plBufferingProgress); + if (!This->filter_graph) { + return S_FALSE; + } + /* Ideally we would use IAMOpenProgress for URL reader but we don't have it in wine (yet) + * For file sources FileAsyncReader->Length should work + * */ + FIXME("stub: Returning buffering progress 100\n"); + *plBufferingProgress = 100; + + return S_OK; }
static HRESULT WINAPI WMPNetwork_get_bufferingTime(IWMPNetwork *iface, LONG *plBufferingTime) @@ -1271,8 +1280,18 @@ static HRESULT WINAPI WMPNetwork_put_maxBandwidth(IWMPNetwork *iface, LONG lMaxB static HRESULT WINAPI WMPNetwork_get_downloadProgress(IWMPNetwork *iface, LONG *plDownloadProgress) { WindowsMediaPlayer *This = impl_from_IWMPNetwork(iface); - FIXME("(%p)->(%p)\n", This, plDownloadProgress); - return E_NOTIMPL; + TRACE("(%p)->(%p)\n", This, plDownloadProgress); + if (!This->filter_graph) { + return S_FALSE; + } + /* Ideally we would use IAMOpenProgress for URL reader but we don't have it in wine (yet) + * For file sources FileAsyncReader->Length could work or it should just be + * 100 + * */ + FIXME("stub: Returning download progress 100\n"); + *plDownloadProgress = 100; + + return S_OK; }
static HRESULT WINAPI WMPNetwork_get_encodedFrameRate(IWMPNetwork *iface, LONG *plFrameRate) diff --git a/dlls/wmp/tests/media.c b/dlls/wmp/tests/media.c index 6f5eb21b3e..5c03b12db8 100644 --- a/dlls/wmp/tests/media.c +++ b/dlls/wmp/tests/media.c @@ -306,8 +306,10 @@ static void test_wmp(void) IOleObject *oleobj; static DWORD dw = 100; IWMPSettings *settings; + IWMPNetwork *network; DOUBLE duration; VARIANT_BOOL vbool; + LONG progress; IWMPMedia *media; static const WCHAR currentPosition[] = {'c','u','r','r','e','n','t','P','o','s','i','t','i','o','n',0}; BSTR bstrcurrentPosition = SysAllocString(currentPosition); @@ -435,6 +437,20 @@ static void test_wmp(void) , "unexpected value: %f\n", duration); IWMPMedia_Release(media);
+ network = NULL; + hres = IWMPPlayer4_get_network(player4, &network); + ok(hres == S_OK, "get_network failed: %08x\n", hres); + ok(network != NULL, "network = NULL\n"); + progress = 0; + hres = IWMPNetwork_get_bufferingProgress(network, &progress); + ok(hres == S_OK || broken(hres == S_FALSE), "IWMPNetwork_get_bufferingProgress failed: %08x\n", hres); + ok(progress == 100, "unexpected value: %d\n", progress); + progress = 0; + hres = IWMPNetwork_get_downloadProgress(network, &progress); + ok(hres == S_OK, "IWMPNetwork_get_downloadProgress failed: %08x\n", hres); + ok(progress == 100, "unexpected value: %d\n", progress); + IWMPNetwork_Release(network); + SET_EXPECT(PLAYSTATE, wmppsStopped); /* The following happens on wine only since we close media on stop */ SET_EXPECT(OPENSTATE, wmposPlaylistOpenNoMedia);
Signed-off-by: Anton Romanov theli.ua@gmail.com --- dlls/wmp/player.c | 30 ++++++++++++++++++++++++++---- dlls/wmp/tests/media.c | 8 +++++++- dlls/wmp/wmp_private.h | 1 + 3 files changed, 34 insertions(+), 5 deletions(-)
diff --git a/dlls/wmp/player.c b/dlls/wmp/player.c index 08a78ab044..36ac0eacb3 100644 --- a/dlls/wmp/player.c +++ b/dlls/wmp/player.c @@ -952,16 +952,32 @@ static HRESULT WINAPI WMPSettings_put_balance(IWMPSettings *iface, LONG v)
static HRESULT WINAPI WMPSettings_get_volume(IWMPSettings *iface, LONG *p) { + HRESULT hres; WindowsMediaPlayer *This = impl_from_IWMPSettings(iface); - FIXME("(%p)->(%p)\n", This, p); - return E_NOTIMPL; + TRACE("(%p)->(%p)\n", This, p); + if (!This->filter_graph) { + return S_FALSE; + } + hres = IBasicAudio_get_Volume(This->basic_audio, p); + /* IBasicAudio - [-10000, 0], wmp - [0, 100] */ + if (SUCCEEDED(hres)) + *p = (*p + 10000) * 100 / 10000; + return hres; }
static HRESULT WINAPI WMPSettings_put_volume(IWMPSettings *iface, LONG v) { + HRESULT hres; WindowsMediaPlayer *This = impl_from_IWMPSettings(iface); - FIXME("(%p)->(%d)\n", This, v); - return E_NOTIMPL; + TRACE("(%p)->(%d)\n", This, v); + if (!This->filter_graph) { + return S_FALSE; + } + /* IBasicAudio - [-10000, 0], wmp - [0, 100] */ + v = 10000 * v / 100 - 10000; + hres = IBasicAudio_put_Volume(This->basic_audio, v); + TRACE("ret: %d", hres); + return hres; }
static HRESULT WINAPI WMPSettings_getMode(IWMPSettings *iface, BSTR mode, VARIANT_BOOL *p) @@ -1480,6 +1496,8 @@ static HRESULT WINAPI WMPControls_play(IWMPControls *iface) IMediaEventEx_Release(media_event_ex); } } + if (SUCCEEDED(hres)) + hres = IGraphBuilder_QueryInterface(This->filter_graph, &IID_IBasicAudio, (void**)&This->basic_audio); }
update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsTransitioning); @@ -1521,11 +1539,15 @@ static HRESULT WINAPI WMPControls_stop(IWMPControls *iface) if (This->media_seeking) { IMediaSeeking_Release(This->media_seeking); } + if (This->basic_audio) { + IBasicAudio_Release(This->basic_audio); + } IGraphBuilder_Release(This->filter_graph); This->filter_graph = NULL; This->media_control = NULL; This->media_event = NULL; This->media_seeking = NULL; + This->basic_audio = NULL;
update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposPlaylistOpenNoMedia); update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsStopped); diff --git a/dlls/wmp/tests/media.c b/dlls/wmp/tests/media.c index 5c03b12db8..5343f2fef3 100644 --- a/dlls/wmp/tests/media.c +++ b/dlls/wmp/tests/media.c @@ -343,7 +343,6 @@ static void test_wmp(void)
hres = IWMPSettings_put_autoStart(settings, VARIANT_FALSE); ok(hres == S_OK, "Could not put autoStart in IWMPSettings: %08x\n", hres); - IWMPSettings_Release(settings);
controls = NULL; hres = IWMPPlayer4_get_controls(player4, &controls); @@ -479,7 +478,14 @@ playback_skip: hres = IConnectionPoint_Unadvise(point, dw); ok(hres == S_OK, "Unadvise failed: %08x\n", hres);
+ hres = IWMPSettings_put_volume(settings, 36); + ok(hres == S_OK, "IWMPSettings_put_volume failed: %08x\n", hres); + hres = IWMPSettings_get_volume(settings, &progress); + ok(hres == S_OK, "IWMPSettings_get_volume failed: %08x\n", hres); + ok(progress == 36, "unexpected value: %d\n", progress); + IConnectionPoint_Release(point); + IWMPSettings_Release(settings); IWMPControls_Release(controls); IWMPPlayer4_Release(player4); IOleObject_Release(oleobj); diff --git a/dlls/wmp/wmp_private.h b/dlls/wmp/wmp_private.h index c5ea0a93d2..5e7dda55ba 100644 --- a/dlls/wmp/wmp_private.h +++ b/dlls/wmp/wmp_private.h @@ -79,6 +79,7 @@ struct WindowsMediaPlayer { IMediaControl* media_control; IMediaEvent* media_event; IMediaSeeking* media_seeking; + IBasicAudio* basic_audio;
/* Async event notification */ HWND msg_window;
Hi Anton,
Note that we'll need to store states in WMP object at some point, at least so that we can implement get_openState() and get_playState(), but I'm fine with the patch for now. Please just address one comment bellow.
On 04/03/2018 07:36 AM, Anton Romanov wrote:
ok(hres == S_OK, "IWMPControls_play failed: %08x\n", hres);
- {
MSG msg;
DWORD start_time = GetTickCount();
DWORD dwTimeout = 5000;
HANDLE handles[1];
handles[0] = playing_event;
do {
DWORD now = GetTickCount();
res = MsgWaitForMultipleObjectsEx(1, handles, start_time + dwTimeout - now,
QS_ALLINPUT ,MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
if (res == WAIT_OBJECT_0 + 1) {
GetMessageW(&msg, 0, 0, 0);
if (winetest_debug > 1)
trace("Dispatching %d\n", msg.message);
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
In general, adding a new block purely for variable declarations is not encouraged. In this case, I'd suggest a helper pump_messages() function here.
Thanks,
Jacek
On Fri, Apr 6, 2018 at 10:28 AM, Jacek Caban jacek@codeweavers.com wrote:
Hi Anton,
Note that we'll need to store states in WMP object at some point, at least so that we can implement get_openState() and get_playState(), but I'm fine with the patch for now. Please just address one comment bellow.
We probably will eventually. For now I was implementing the bare minimum my test game needs. And adding saving of states would be a fairly easy change as all state updates now go through the same function anyway. (And frankly, only because it crashes with native WMP)
On 04/03/2018 07:36 AM, Anton Romanov wrote:
ok(hres == S_OK, "IWMPControls_play failed: %08x\n", hres);
- {
MSG msg;
DWORD start_time = GetTickCount();
DWORD dwTimeout = 5000;
HANDLE handles[1];
handles[0] = playing_event;
do {
DWORD now = GetTickCount();
res = MsgWaitForMultipleObjectsEx(1, handles, start_time + dwTimeout - now,
QS_ALLINPUT ,MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
if (res == WAIT_OBJECT_0 + 1) {
GetMessageW(&msg, 0, 0, 0);
if (winetest_debug > 1)
trace("Dispatching %d\n", msg.message);
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
In general, adding a new block purely for variable declarations is not encouraged. In this case, I'd suggest a helper pump_messages() function here.
Sure, will do. I left it inline initially only because there was just one occurence of it. Now that I use it for both completion and playing events it makes even more sense to factor it out.
Thanks,
Jacek
Signed-off-by: Anton Romanov theli.ua@gmail.com --- Moved pumping messages to a function dlls/wmp/events.c | 11 ++ dlls/wmp/oleobj.c | 2 +- dlls/wmp/player.c | 50 ++++++++- dlls/wmp/tests/media.c | 229 ++++++++++++++++++++++++++++++++++++++++- dlls/wmp/wmp_main.c | 1 + dlls/wmp/wmp_private.h | 1 + include/wmpids.h | 24 +++++ 7 files changed, 313 insertions(+), 5 deletions(-) create mode 100644 include/wmpids.h
diff --git a/dlls/wmp/events.c b/dlls/wmp/events.c index a908834010..412eb307d1 100644 --- a/dlls/wmp/events.c +++ b/dlls/wmp/events.c @@ -400,3 +400,14 @@ void ConnectionPointContainer_Destroy(WindowsMediaPlayer *wmp) { ConnectionPoint_Destroy(wmp->wmpocx); } + +void call_sink(ConnectionPoint *This, DISPID dispid, DISPPARAMS *dispparams) +{ + DWORD i; + + for(i=0; i<This->sinks_size; i++) { + if(This->sinks[i]) + IDispatch_Invoke(This->sinks[i], dispid, &IID_NULL, LOCALE_SYSTEM_DEFAULT, + DISPATCH_METHOD, dispparams, NULL, NULL, NULL); + } +} diff --git a/dlls/wmp/oleobj.c b/dlls/wmp/oleobj.c index cc0e9a9d98..cbf183c1f6 100644 --- a/dlls/wmp/oleobj.c +++ b/dlls/wmp/oleobj.c @@ -307,8 +307,8 @@ static ULONG WINAPI OleObject_Release(IOleObject *iface)
if(!ref) { release_client_site(This); - ConnectionPointContainer_Destroy(This); destroy_player(This); + ConnectionPointContainer_Destroy(This); heap_free(This); }
diff --git a/dlls/wmp/player.c b/dlls/wmp/player.c index 3d0df96bd8..b9237b097c 100644 --- a/dlls/wmp/player.c +++ b/dlls/wmp/player.c @@ -20,9 +20,27 @@
#include "wine/debug.h" #include <nserror.h> +#include "wmpids.h"
WINE_DEFAULT_DEBUG_CHANNEL(wmp);
+static void update_state(WindowsMediaPlayer *wmp, LONG type, LONG state) +{ + DISPPARAMS dispparams; + VARIANTARG params[1]; + + dispparams.cArgs = 1; + dispparams.cNamedArgs = 0; + dispparams.rgdispidNamedArgs = NULL; + dispparams.rgvarg = params; + + V_VT(params) = VT_UI4; + V_UI4(params) = state; + + call_sink(wmp->wmpocx, type, + &dispparams); +} + static inline WMPMedia *impl_from_IWMPMedia(IWMPMedia *iface) { return CONTAINING_RECORD(iface, WMPMedia, IWMPMedia_iface); @@ -125,14 +143,21 @@ static HRESULT WINAPI WMPPlayer4_put_URL(IWMPPlayer4 *iface, BSTR url) if(url == NULL) { return E_POINTER; } + hres = create_media_from_url(url, &media); + if (SUCCEEDED(hres)) { + update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsTransitioning); hres = IWMPPlayer4_put_currentMedia(iface, media); - IWMPMedia_Release(media); /* put will addref */ } - if (SUCCEEDED(hres) && This->auto_start) { - hres = IWMPControls_play(&This->IWMPControls_iface); + if (SUCCEEDED(hres)) { + IWMPMedia_Release(media); /* put will addref */ + update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsReady); + if (This->auto_start == VARIANT_TRUE) { + hres = IWMPControls_play(&This->IWMPControls_iface); + } } + return hres; }
@@ -191,9 +216,13 @@ static HRESULT WINAPI WMPPlayer4_put_currentMedia(IWMPPlayer4 *iface, IWMPMedia if(pMedia == NULL) { return E_POINTER; } + update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposPlaylistChanging); if(This->wmpmedia != NULL) { IWMPMedia_Release(This->wmpmedia); } + update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposPlaylistChanged); + update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposPlaylistOpenNoMedia); + This->wmpmedia = pMedia; IWMPMedia_AddRef(This->wmpmedia); return S_OK; @@ -1388,19 +1417,32 @@ static HRESULT WINAPI WMPControls_play(IWMPControls *iface) CLSCTX_INPROC_SERVER, &IID_IGraphBuilder, (void **)&This->filter_graph); + update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposOpeningUnknownURL); + if (SUCCEEDED(hres)) hres = IGraphBuilder_RenderFile(This->filter_graph, media->url, NULL); if (SUCCEEDED(hres)) hres = IGraphBuilder_QueryInterface(This->filter_graph, &IID_IMediaControl, (void**)&This->media_control); + if (SUCCEEDED(hres)) + update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposMediaOpen); }
+ update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsTransitioning); + if (SUCCEEDED(hres)) hres = IMediaControl_Run(This->media_control);
if (hres == S_FALSE) { hres = S_OK; /* S_FALSE will mean that graph is transitioning and that is fine */ } + + if (SUCCEEDED(hres)) { + update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsPlaying); + } else { + update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsUndefined); + } + return hres; }
@@ -1419,6 +1461,8 @@ static HRESULT WINAPI WMPControls_stop(IWMPControls *iface) IGraphBuilder_Release(This->filter_graph); This->filter_graph = NULL; This->media_control = NULL; + update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposPlaylistOpenNoMedia); + update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsStopped); return hres; }
diff --git a/dlls/wmp/tests/media.c b/dlls/wmp/tests/media.c index 6e4a0c170f..b33be4fa20 100644 --- a/dlls/wmp/tests/media.c +++ b/dlls/wmp/tests/media.c @@ -19,9 +19,54 @@ #include <wmp.h> #include <olectl.h> #include <nserror.h> +#include <wmpids.h> +#include <math.h> +#include <assert.h>
#include "wine/test.h"
+#define DEFINE_EXPECT(kind) \ + static DWORD expect_ ## kind = 0, called_ ## kind = 0 + +#define SET_EXPECT(kind, index) \ + do { \ + assert(index < 8 * sizeof(expect_ ## kind)); \ + expect_ ## kind |= (1 << index); \ + }while(0) + +#define CHECK_EXPECT(kind, index) \ + do { \ + ok(expect_ ##kind & (1 << index), "unexpected event for " #kind ", index:%d\n", index); \ + called_ ## kind |= (1 << index); \ + }while(0) + +#define CHECK_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) \ + do { \ + ok(called_ ## kind & (1 << index) || broken(1), "expected " #kind ", %d\n", index); \ + expect_ ## kind &= ~(1 << index); \ + called_ ## kind &= ~(1 << index); \ + }while(0) + +#define CHECK_NOT_CALLED(kind, index) \ + do { \ + ok(!(called_ ## kind & (1 << index)), "not expected " #kind ", %d\n", index); \ + expect_ ## kind &= ~(1 << index); \ + called_ ## kind &= ~(1 << index); \ + }while(0) + +DEFINE_EXPECT(PLAYSTATE); +DEFINE_EXPECT(OPENSTATE); + +static HANDLE playing_event; +static DWORD main_thread_id; + static const WCHAR mp3file[] = {'t','e','s','t','.','m','p','3',0}; static inline WCHAR *load_resource(const WCHAR *name) { @@ -48,14 +93,132 @@ static inline WCHAR *load_resource(const WCHAR *name) return pathW; }
+static ULONG WINAPI Dispatch_AddRef(IDispatch *iface) +{ + return 2; +} + +static ULONG WINAPI Dispatch_Release(IDispatch *iface) +{ + return 1; +} + +static HRESULT WINAPI Dispatch_GetTypeInfoCount(IDispatch *iface, UINT *pctinfo) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Dispatch_GetTypeInfo(IDispatch *iface, UINT iTInfo, LCID lcid, + ITypeInfo **ppTInfo) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Dispatch_GetIDsOfNames(IDispatch *iface, REFIID riid, LPOLESTR *rgszNames, + UINT cNames, LCID lcid, DISPID *rgDispId) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI WMPOCXEvents_QueryInterface(IDispatch *iface, REFIID riid, void **ppv) +{ + *ppv = NULL; + + if(IsEqualGUID(&IID__WMPOCXEvents, riid) || IsEqualGUID(&IID_IDispatch, riid)) { + *ppv = iface; + return S_OK; + } + + ok(0, "unexpected riid %s\n", wine_dbgstr_guid(riid)); + return E_NOINTERFACE; +} + +static HRESULT WINAPI WMPOCXEvents_Invoke(IDispatch *iface, DISPID dispIdMember, REFIID riid, + LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, + EXCEPINFO *pExcepInfo, UINT *puArgErr) +{ + ok(main_thread_id == GetCurrentThreadId(), "Got notification outside of main thread!\n"); + switch(dispIdMember) { + case DISPID_WMPCOREEVENT_OPENSTATECHANGE: + CHECK_EXPECT(OPENSTATE, V_UI4(pDispParams->rgvarg)); + if (winetest_debug > 1) + trace("DISPID_WMPCOREEVENT_OPENSTATECHANGE, %d\n", V_UI4(pDispParams->rgvarg)); + break; + case DISPID_WMPCOREEVENT_PLAYSTATECHANGE: + CHECK_EXPECT(PLAYSTATE, V_UI4(pDispParams->rgvarg)); + if (V_UI4(pDispParams->rgvarg) == wmppsPlaying) { + SetEvent(playing_event); + } + if (winetest_debug > 1) + trace("DISPID_WMPCOREEVENT_PLAYSTATECHANGE, %d\n", V_UI4(pDispParams->rgvarg)); + break; + case DISPID_WMPCOREEVENT_MEDIACHANGE: + if (winetest_debug > 1) + trace("DISPID_WMPCOREEVENT_MEDIACHANGE\n"); + break; + case DISPID_WMPCOREEVENT_CURRENTITEMCHANGE: + if (winetest_debug > 1) + trace("DISPID_WMPCOREEVENT_CURRENTITEMCHANGE\n"); + break; + case DISPID_WMPCOREEVENT_STATUSCHANGE: + if (winetest_debug > 1) + trace("DISPID_WMPCOREEVENT_STATUSCHANGE\n"); + break; + default: + if (winetest_debug > 1) + trace("event: %d\n", dispIdMember); + break; + } + + return E_NOTIMPL; +} + +static IDispatchVtbl WMPOcxEventsVtbl = { + WMPOCXEvents_QueryInterface, + Dispatch_AddRef, + Dispatch_Release, + Dispatch_GetTypeInfoCount, + Dispatch_GetTypeInfo, + Dispatch_GetIDsOfNames, + WMPOCXEvents_Invoke, +}; + +static IDispatch WMPOCXEvents = { &WMPOcxEventsVtbl }; + +static HRESULT pump_messages(DWORD dwTimeout, DWORD nCount, const HANDLE *pHandles) { + MSG msg; + HRESULT res; + DWORD start_time = GetTickCount(); + do { + DWORD now = GetTickCount(); + res = MsgWaitForMultipleObjectsEx(nCount, pHandles, start_time + dwTimeout - now, + QS_ALLINPUT ,MWMO_ALERTABLE | MWMO_INPUTAVAILABLE); + if (res == WAIT_OBJECT_0 + 1) { + GetMessageW(&msg, 0, 0, 0); + if (winetest_debug > 1) + trace("Dispatching %d\n", msg.message); + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + while (res == WAIT_OBJECT_0 + 1); + return res; +} + static void 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;
hres = CoCreateInstance(&CLSID_WindowsMediaPlayer, NULL, CLSCTX_INPROC_SERVER, &IID_IOleObject, (void**)&oleobj); @@ -65,6 +228,18 @@ static void test_wmp(void) } 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);
@@ -87,22 +262,70 @@ static void test_wmp(void)
filename = SysAllocString(load_resource(mp3file));
+ SET_EXPECT(OPENSTATE, wmposPlaylistChanging); + SET_EXPECT(OPENSTATE, wmposPlaylistOpenNoMedia); + SET_EXPECT(OPENSTATE, wmposPlaylistChanged); + SET_EXPECT(PLAYSTATE, wmppsTransitioning); + SET_EXPECT(PLAYSTATE, wmppsReady); hres = IWMPPlayer4_put_URL(player4, filename); ok(hres == S_OK, "IWMPPlayer4_put_URL failed: %08x\n", hres); + CHECK_CALLED(OPENSTATE, wmposPlaylistChanging); + CHECK_CALLED(OPENSTATE, wmposPlaylistChanged); + CHECK_CALLED(OPENSTATE, wmposPlaylistOpenNoMedia); + CHECK_CALLED(PLAYSTATE, wmppsTransitioning); + CHECK_CALLED(PLAYSTATE, wmppsReady);
+ SET_EXPECT(OPENSTATE, wmposOpeningUnknownURL); + SET_EXPECT(OPENSTATE, wmposMediaOpen); + SET_EXPECT(OPENSTATE, wmposMediaOpening); + SET_EXPECT(PLAYSTATE, wmppsPlaying); + SET_EXPECT(PLAYSTATE, wmppsTransitioning); hres = IWMPControls_play(controls); ok(hres == S_OK, "IWMPControls_play failed: %08x\n", hres); + res = pump_messages(5000, 1, &playing_event); + ok(res == WAIT_OBJECT_0 || broken(res == WAIT_TIMEOUT), "Timed out while waiting for media to become ready\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 transition media to playing state.\n"); + 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);
+ SET_EXPECT(PLAYSTATE, wmppsStopped); + /* The following happens on wine only since we close media on stop */ + SET_EXPECT(OPENSTATE, wmposPlaylistOpenNoMedia); hres = IWMPControls_stop(controls); ok(hres == S_OK, "IWMPControls_stop failed: %08x\n", hres); + CHECK_CALLED(PLAYSTATE, wmppsStopped); + todo_wine CHECK_NOT_CALLED(OPENSTATE, wmposPlaylistOpenNoMedia);
/* Already Stopped */ hres = IWMPControls_stop(controls); ok(hres == NS_S_WMPCORE_COMMAND_NOT_AVAILABLE, "IWMPControls_stop is available: %08x\n", hres);
+ SET_EXPECT(PLAYSTATE, wmppsPlaying); + /* The following happens on wine only since we close media on stop */ + SET_EXPECT(OPENSTATE, wmposOpeningUnknownURL); + SET_EXPECT(OPENSTATE, wmposMediaOpen); + SET_EXPECT(PLAYSTATE, wmppsTransitioning); hres = IWMPControls_play(controls); ok(hres == S_OK, "IWMPControls_play failed: %08x\n", hres); + CHECK_CALLED(PLAYSTATE, wmppsPlaying); + todo_wine CHECK_NOT_CALLED(OPENSTATE, wmposOpeningUnknownURL); + todo_wine CHECK_NOT_CALLED(OPENSTATE, wmposMediaOpen); + todo_wine CHECK_NOT_CALLED(PLAYSTATE, wmppsTransitioning);
+playback_skip: + hres = IConnectionPoint_Unadvise(point, dw); + ok(hres == S_OK, "Unadvise failed: %08x\n", hres); + + IConnectionPoint_Release(point); IWMPControls_Release(controls); IWMPPlayer4_Release(player4); IOleObject_Release(oleobj); @@ -114,7 +337,11 @@ START_TEST(media) { CoInitialize(NULL);
+ main_thread_id = GetCurrentThreadId(); + playing_event = CreateEventW(NULL, FALSE, FALSE, NULL); test_wmp();
+ CloseHandle(playing_event); + CoUninitialize(); } diff --git a/dlls/wmp/wmp_main.c b/dlls/wmp/wmp_main.c index 29b096f7fd..9a33b2762b 100644 --- a/dlls/wmp/wmp_main.c +++ b/dlls/wmp/wmp_main.c @@ -25,6 +25,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(wmp);
HINSTANCE wmp_instance; +DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID riid, void **ppv) { diff --git a/dlls/wmp/wmp_private.h b/dlls/wmp/wmp_private.h index 05ad5889e2..9e84d56ea8 100644 --- a/dlls/wmp/wmp_private.h +++ b/dlls/wmp/wmp_private.h @@ -83,6 +83,7 @@ WMPMedia *unsafe_impl_from_IWMPMedia(IWMPMedia *iface) DECLSPEC_HIDDEN; HRESULT create_media_from_url(BSTR url, IWMPMedia **ppMedia) DECLSPEC_HIDDEN; void ConnectionPointContainer_Init(WindowsMediaPlayer *wmp) DECLSPEC_HIDDEN; void ConnectionPointContainer_Destroy(WindowsMediaPlayer *wmp) DECLSPEC_HIDDEN; +void call_sink(ConnectionPoint *This, DISPID dispid, DISPPARAMS *dispparams) DECLSPEC_HIDDEN;
HRESULT WINAPI WMPFactory_CreateInstance(IClassFactory*,IUnknown*,REFIID,void**) DECLSPEC_HIDDEN;
diff --git a/include/wmpids.h b/include/wmpids.h new file mode 100644 index 0000000000..a8500409ac --- /dev/null +++ b/include/wmpids.h @@ -0,0 +1,24 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* WMPCoreEvents */ +#define DISPID_WMPCOREEVENT_OPENSTATECHANGE 5001 +#define DISPID_WMPCOREEVENT_STATUSCHANGE 5002 + +#define DISPID_WMPCOREEVENT_PLAYSTATECHANGE 5101 + +#define DISPID_WMPCOREEVENT_MEDIACHANGE 5802 +#define DISPID_WMPCOREEVENT_CURRENTITEMCHANGE 5806