/* * Copyright 2019 Nikolay Sivov for CodeWeavers * * 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 */ #define COBJMACROS #include #include #include "windef.h" #include "winbase.h" #include "mfapi.h" #include "mfplay.h" #include "mferror.h" #include "wine/debug.h" #include "initguid.h" WINE_DEFAULT_DEBUG_CHANNEL(mfplat); DEFINE_GUID(_MF_TOPO_MEDIA_ITEM, 0x6c1bb4df, 0x59ba, 0x4020, 0x85, 0x0c, 0x35, 0x79, 0xa2, 0x7a, 0xe2, 0x51); DEFINE_GUID(_MF_CUSTOM_SINK, 0x7c1bb4df, 0x59ba, 0x4020, 0x85, 0x0c, 0x35, 0x79, 0xa2, 0x7a, 0xe2, 0x51); static const WCHAR eventclassW[] = L"MediaPlayerEventCallbackClass"; static LONG startup_refcount; static HINSTANCE mfplay_instance; static void platform_startup(void) { if (InterlockedIncrement(&startup_refcount) == 1) MFStartup(MF_VERSION, MFSTARTUP_FULL); } static void platform_shutdown(void) { if (InterlockedDecrement(&startup_refcount) == 0) MFShutdown(); } static inline const char *debugstr_normalized_rect(const MFVideoNormalizedRect *rect) { if (!rect) return "(null)"; return wine_dbg_sprintf("(%.8e,%.8e)-(%.8e,%.8e)", rect->left, rect->top, rect->right, rect->bottom); } struct media_player; struct media_item { IMFPMediaItem IMFPMediaItem_iface; LONG refcount; struct media_player *player; IMFMediaSource *source; IMFPresentationDescriptor *pd; DWORD_PTR user_data; WCHAR *url; IUnknown *object; LONGLONG start_position; LONGLONG stop_position; }; struct media_player { IMFPMediaPlayer IMFPMediaPlayer_iface; IPropertyStore IPropertyStore_iface; IMFAsyncCallback resolver_callback; IMFAsyncCallback events_callback; IMFAsyncCallback session_events_callback; LONG refcount; IMFPMediaPlayerCallback *callback; IPropertyStore *propstore; IMFSourceResolver *resolver; IMFMediaSession *session; IMFPMediaItem *item; MFP_CREATION_OPTIONS options; MFP_MEDIAPLAYER_STATE state; HWND event_window; HWND output_window; CRITICAL_SECTION cs; }; struct generic_event { MFP_EVENT_HEADER header; IMFPMediaItem *item; }; struct media_event { IUnknown IUnknown_iface; LONG refcount; union { MFP_EVENT_HEADER header; struct generic_event generic; MFP_PLAY_EVENT play; MFP_PAUSE_EVENT pause; MFP_STOP_EVENT stop; MFP_POSITION_SET_EVENT position_set; MFP_RATE_SET_EVENT rate_set; MFP_MEDIAITEM_CREATED_EVENT item_created; MFP_MEDIAITEM_SET_EVENT item_set; MFP_MEDIAITEM_CLEARED_EVENT item_cleared; MFP_MF_EVENT event; MFP_ERROR_EVENT error; MFP_PLAYBACK_ENDED_EVENT ended; MFP_ACQUIRE_USER_CREDENTIAL_EVENT acquire_creds; } u; }; static struct media_player *impl_from_IMFPMediaPlayer(IMFPMediaPlayer *iface) { return CONTAINING_RECORD(iface, struct media_player, IMFPMediaPlayer_iface); } static struct media_player *impl_from_IPropertyStore(IPropertyStore *iface) { return CONTAINING_RECORD(iface, struct media_player, IPropertyStore_iface); } static struct media_player *impl_from_resolver_IMFAsyncCallback(IMFAsyncCallback *iface) { return CONTAINING_RECORD(iface, struct media_player, resolver_callback); } static struct media_player *impl_from_events_IMFAsyncCallback(IMFAsyncCallback *iface) { return CONTAINING_RECORD(iface, struct media_player, events_callback); } static struct media_player *impl_from_session_events_IMFAsyncCallback(IMFAsyncCallback *iface) { return CONTAINING_RECORD(iface, struct media_player, session_events_callback); } static struct media_item *impl_from_IMFPMediaItem(IMFPMediaItem *iface) { return CONTAINING_RECORD(iface, struct media_item, IMFPMediaItem_iface); } static struct media_event *impl_event_from_IUnknown(IUnknown *iface) { return CONTAINING_RECORD(iface, struct media_event, IUnknown_iface); } static HRESULT WINAPI media_event_QueryInterface(IUnknown *iface, REFIID riid, void **obj) { if (IsEqualIID(riid, &IID_IUnknown)) { *obj = iface; IUnknown_AddRef(iface); return S_OK; } *obj = NULL; return E_NOINTERFACE; } static ULONG WINAPI media_event_AddRef(IUnknown *iface) { struct media_event *event = impl_event_from_IUnknown(iface); ULONG refcount = InterlockedIncrement(&event->refcount); TRACE("%p, refcount %lu.\n", iface, refcount); return refcount; } static ULONG WINAPI media_event_Release(IUnknown *iface) { struct media_event *event = impl_event_from_IUnknown(iface); ULONG refcount = InterlockedDecrement(&event->refcount); TRACE("%p, refcount %lu.\n", iface, refcount); if (!refcount) { if (event->u.header.pMediaPlayer) IMFPMediaPlayer_Release(event->u.header.pMediaPlayer); if (event->u.header.pPropertyStore) IPropertyStore_Release(event->u.header.pPropertyStore); switch (event->u.header.eEventType) { /* Most types share same layout. */ case MFP_EVENT_TYPE_PLAY: case MFP_EVENT_TYPE_PAUSE: case MFP_EVENT_TYPE_STOP: case MFP_EVENT_TYPE_POSITION_SET: case MFP_EVENT_TYPE_RATE_SET: case MFP_EVENT_TYPE_MEDIAITEM_CREATED: case MFP_EVENT_TYPE_MEDIAITEM_SET: case MFP_EVENT_TYPE_FRAME_STEP: case MFP_EVENT_TYPE_MEDIAITEM_CLEARED: case MFP_EVENT_TYPE_PLAYBACK_ENDED: if (event->u.generic.item) IMFPMediaItem_Release(event->u.generic.item); break; case MFP_EVENT_TYPE_MF: if (event->u.event.pMFMediaEvent) IMFMediaEvent_Release(event->u.event.pMFMediaEvent); if (event->u.event.pMediaItem) IMFPMediaItem_Release(event->u.event.pMediaItem); break; default: FIXME("Unsupported event %u.\n", event->u.header.eEventType); break; } free(event); } return refcount; } static const IUnknownVtbl media_event_vtbl = { media_event_QueryInterface, media_event_AddRef, media_event_Release, }; static HRESULT media_event_create(struct media_player *player, MFP_EVENT_TYPE event_type, HRESULT hr, IMFPMediaItem *item, struct media_event **event) { struct media_event *object; if (!(object = calloc(1, sizeof(*object)))) return E_OUTOFMEMORY; object->IUnknown_iface.lpVtbl = &media_event_vtbl; object->refcount = 1; object->u.header.eEventType = event_type; object->u.header.hrEvent = hr; object->u.header.pMediaPlayer = &player->IMFPMediaPlayer_iface; IMFPMediaPlayer_AddRef(object->u.header.pMediaPlayer); object->u.header.eState = player->state; switch (event_type) { case MFP_EVENT_TYPE_PLAY: case MFP_EVENT_TYPE_PAUSE: case MFP_EVENT_TYPE_STOP: case MFP_EVENT_TYPE_POSITION_SET: case MFP_EVENT_TYPE_RATE_SET: case MFP_EVENT_TYPE_MEDIAITEM_CREATED: case MFP_EVENT_TYPE_MEDIAITEM_SET: case MFP_EVENT_TYPE_FRAME_STEP: case MFP_EVENT_TYPE_MEDIAITEM_CLEARED: case MFP_EVENT_TYPE_PLAYBACK_ENDED: object->u.generic.item = item; if (object->u.generic.item) IMFPMediaItem_AddRef(object->u.generic.item); break; case MFP_EVENT_TYPE_MF: object->u.event.pMediaItem = item; if (object->u.event.pMediaItem) IMFPMediaItem_AddRef(object->u.event.pMediaItem); break; default: ; } /* FIXME: set properties for some events? */ *event = object; return S_OK; } static LRESULT WINAPI media_player_event_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { struct media_event *event = (void *)lparam; struct media_player *player; if (msg == WM_USER) { player = impl_from_IMFPMediaPlayer(event->u.header.pMediaPlayer); if (player->callback) IMFPMediaPlayerCallback_OnMediaPlayerEvent(player->callback, &event->u.header); IUnknown_Release(&event->IUnknown_iface); return 0; } return DefWindowProcW(hwnd, msg, wparam, lparam); } static void media_player_set_state(struct media_player *player, MFP_MEDIAPLAYER_STATE state) { if (player->state != MFP_MEDIAPLAYER_STATE_SHUTDOWN) { if (state == MFP_MEDIAPLAYER_STATE_SHUTDOWN) IMFMediaSession_Shutdown(player->session); player->state = state; } } static HRESULT media_item_get_pd(const struct media_item *item, IMFPresentationDescriptor **pd) { if (item->player->state == MFP_MEDIAPLAYER_STATE_SHUTDOWN) return MF_E_SHUTDOWN; *pd = item->pd; IMFPresentationDescriptor_AddRef(*pd); return S_OK; } static HRESULT WINAPI media_item_QueryInterface(IMFPMediaItem *iface, REFIID riid, void **obj) { TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); if (IsEqualIID(riid, &IID_IMFPMediaItem) || IsEqualIID(riid, &IID_IUnknown)) { *obj = iface; IMFPMediaItem_AddRef(iface); return S_OK; } WARN("Unsupported interface %s.\n", debugstr_guid(riid)); *obj = NULL; return E_NOINTERFACE; } static ULONG WINAPI media_item_AddRef(IMFPMediaItem *iface) { struct media_item *item = impl_from_IMFPMediaItem(iface); ULONG refcount = InterlockedIncrement(&item->refcount); TRACE("%p, refcount %lu.\n", iface, refcount); return refcount; } static ULONG WINAPI media_item_Release(IMFPMediaItem *iface) { struct media_item *item = impl_from_IMFPMediaItem(iface); ULONG refcount = InterlockedDecrement(&item->refcount); TRACE("%p, refcount %lu.\n", iface, refcount); if (!refcount) { if (item->player) IMFPMediaPlayer_Release(&item->player->IMFPMediaPlayer_iface); if (item->source) IMFMediaSource_Release(item->source); if (item->pd) IMFPresentationDescriptor_Release(item->pd); if (item->object) IUnknown_Release(item->object); free(item->url); free(item); } return refcount; } static HRESULT WINAPI media_item_GetMediaPlayer(IMFPMediaItem *iface, IMFPMediaPlayer **player) { struct media_item *item = impl_from_IMFPMediaItem(iface); HRESULT hr = S_OK; TRACE("%p, %p.\n", iface, player); EnterCriticalSection(&item->player->cs); if (item->player->state == MFP_MEDIAPLAYER_STATE_SHUTDOWN) { hr = MF_E_SHUTDOWN; *player = NULL; } else { *player = &item->player->IMFPMediaPlayer_iface; IMFPMediaPlayer_AddRef(*player); } LeaveCriticalSection(&item->player->cs); return hr; } static HRESULT WINAPI media_item_GetURL(IMFPMediaItem *iface, LPWSTR *url) { struct media_item *item = impl_from_IMFPMediaItem(iface); HRESULT hr = S_OK; TRACE("%p, %p.\n", iface, url); EnterCriticalSection(&item->player->cs); if (item->player->state == MFP_MEDIAPLAYER_STATE_SHUTDOWN) hr = MF_E_SHUTDOWN; else if (!item->url) hr = MF_E_NOT_FOUND; else { if (!(*url = CoTaskMemAlloc((wcslen(item->url) + 1) * sizeof(*item->url)))) hr = E_OUTOFMEMORY; if (*url) wcscpy(*url, item->url); } LeaveCriticalSection(&item->player->cs); return hr; } static HRESULT WINAPI media_item_GetObject(IMFPMediaItem *iface, IUnknown **object) { struct media_item *item = impl_from_IMFPMediaItem(iface); HRESULT hr = S_OK; TRACE("%p, %p.\n", iface, object); EnterCriticalSection(&item->player->cs); if (item->player->state == MFP_MEDIAPLAYER_STATE_SHUTDOWN) hr = MF_E_SHUTDOWN; else if (!item->object) hr = MF_E_NOT_FOUND; else { *object = item->object; IUnknown_AddRef(*object); } LeaveCriticalSection(&item->player->cs); return hr; } static HRESULT WINAPI media_item_GetUserData(IMFPMediaItem *iface, DWORD_PTR *user_data) { struct media_item *item = impl_from_IMFPMediaItem(iface); TRACE("%p, %p.\n", iface, user_data); *user_data = item->user_data; return S_OK; } static HRESULT WINAPI media_item_SetUserData(IMFPMediaItem *iface, DWORD_PTR user_data) { struct media_item *item = impl_from_IMFPMediaItem(iface); TRACE("%p, %Ix.\n", iface, user_data); item->user_data = user_data; return S_OK; } static HRESULT media_item_set_position(const GUID *format, const PROPVARIANT *position, LARGE_INTEGER *ret) { ret->QuadPart = 0; if (format && !IsEqualGUID(format, &MFP_POSITIONTYPE_100NS)) return E_INVALIDARG; if ((format != NULL) ^ (position != NULL)) return E_POINTER; if (position && position->vt != VT_EMPTY && position->vt != VT_I8) return E_INVALIDARG; if ((!format && !position) || position->vt == VT_EMPTY) return S_OK; if (position->hVal.QuadPart == 0) return MF_E_OUT_OF_RANGE; ret->QuadPart = position->hVal.QuadPart; return S_OK; } static void media_item_get_position(LONGLONG value, GUID *format, PROPVARIANT *position) { if (!format) return; memcpy(format, &MFP_POSITIONTYPE_100NS, sizeof(*format)); if (value) { position->vt = VT_I8; position->hVal.QuadPart = value; } } static HRESULT WINAPI media_item_GetStartStopPosition(IMFPMediaItem *iface, GUID *start_format, PROPVARIANT *start_position, GUID *stop_format, PROPVARIANT *stop_position) { struct media_item *item = impl_from_IMFPMediaItem(iface); TRACE("%p, %p, %p, %p, %p.\n", iface, start_format, start_position, stop_format, stop_position); if (start_position) start_position->vt = VT_EMPTY; if (stop_position) stop_position->vt = VT_EMPTY; if (((start_format != NULL) ^ (start_position != NULL)) || ((stop_format != NULL) ^ (stop_position != NULL))) { return E_POINTER; } media_item_get_position(item->start_position, start_format, start_position); media_item_get_position(item->stop_position, stop_format, stop_position); return S_OK; } static HRESULT WINAPI media_item_SetStartStopPosition(IMFPMediaItem *iface, const GUID *start_format, const PROPVARIANT *start_position, const GUID *stop_format, const PROPVARIANT *stop_position) { struct media_item *item = impl_from_IMFPMediaItem(iface); LARGE_INTEGER start, stop; HRESULT hr; TRACE("%p, %s, %p, %s, %p.\n", iface, debugstr_guid(start_format), start_position, debugstr_guid(stop_format), stop_position); hr = media_item_set_position(start_format, start_position, &start); if (SUCCEEDED(hr)) hr = media_item_set_position(stop_format, stop_position, &stop); if (FAILED(hr)) return hr; if (start.QuadPart > stop.QuadPart) return MF_E_OUT_OF_RANGE; item->start_position = start.QuadPart; item->stop_position = stop.QuadPart; return hr; } static HRESULT media_item_get_stream_type(IMFStreamDescriptor *sd, GUID *major) { IMFMediaTypeHandler *handler; HRESULT hr; if (SUCCEEDED(hr = IMFStreamDescriptor_GetMediaTypeHandler(sd, &handler))) { hr = IMFMediaTypeHandler_GetMajorType(handler, major); IMFMediaTypeHandler_Release(handler); } return hr; } static HRESULT media_item_has_stream(struct media_item *item, const GUID *major, BOOL *has_stream, BOOL *is_selected) { IMFPresentationDescriptor *pd; IMFStreamDescriptor *sd; unsigned int idx = 0; BOOL selected; HRESULT hr; GUID guid; EnterCriticalSection(&item->player->cs); if (SUCCEEDED(hr = media_item_get_pd(item, &pd))) { *has_stream = *is_selected = FALSE; while (SUCCEEDED(IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, idx++, &selected, &sd))) { if (SUCCEEDED(media_item_get_stream_type(sd, &guid)) && IsEqualGUID(&guid, major)) { *has_stream = TRUE; *is_selected = selected; } IMFStreamDescriptor_Release(sd); if (*has_stream && *is_selected) break; } IMFPresentationDescriptor_Release(pd); } LeaveCriticalSection(&item->player->cs); return hr; } static HRESULT WINAPI media_item_HasVideo(IMFPMediaItem *iface, BOOL *has_video, BOOL *selected) { struct media_item *item = impl_from_IMFPMediaItem(iface); TRACE("%p, %p, %p.\n", iface, has_video, selected); return media_item_has_stream(item, &MFMediaType_Video, has_video, selected); } static HRESULT WINAPI media_item_HasAudio(IMFPMediaItem *iface, BOOL *has_audio, BOOL *selected) { struct media_item *item = impl_from_IMFPMediaItem(iface); TRACE("%p, %p, %p.\n", iface, has_audio, selected); return media_item_has_stream(item, &MFMediaType_Audio, has_audio, selected); } static HRESULT WINAPI media_item_IsProtected(IMFPMediaItem *iface, BOOL *protected) { struct media_item *item = impl_from_IMFPMediaItem(iface); IMFPresentationDescriptor *pd; HRESULT hr; TRACE("%p, %p.\n", iface, protected); if (!protected) return E_POINTER; EnterCriticalSection(&item->player->cs); if (SUCCEEDED(hr = media_item_get_pd(item, &pd))) { *protected = (hr = MFRequireProtectedEnvironment(pd)) == S_OK; IMFPresentationDescriptor_Release(pd); } LeaveCriticalSection(&item->player->cs); return hr; } static HRESULT WINAPI media_item_GetDuration(IMFPMediaItem *iface, REFGUID format, PROPVARIANT *value) { struct media_item *item = impl_from_IMFPMediaItem(iface); IMFPresentationDescriptor *pd; HRESULT hr; TRACE("%p, %s, %p.\n", iface, debugstr_guid(format), value); EnterCriticalSection(&item->player->cs); if (SUCCEEDED(hr = media_item_get_pd(item, &pd))) { hr = IMFPresentationDescriptor_GetItem(pd, &MF_PD_DURATION, value); IMFPresentationDescriptor_Release(pd); } LeaveCriticalSection(&item->player->cs); return hr; } static HRESULT WINAPI media_item_GetNumberOfStreams(IMFPMediaItem *iface, DWORD *count) { struct media_item *item = impl_from_IMFPMediaItem(iface); IMFPresentationDescriptor *pd; HRESULT hr; TRACE("%p, %p.\n", iface, count); EnterCriticalSection(&item->player->cs); if (SUCCEEDED(hr = media_item_get_pd(item, &pd))) { hr = IMFPresentationDescriptor_GetStreamDescriptorCount(pd, count); IMFPresentationDescriptor_Release(pd); } LeaveCriticalSection(&item->player->cs); return hr; } static HRESULT WINAPI media_item_GetStreamSelection(IMFPMediaItem *iface, DWORD index, BOOL *selected) { struct media_item *item = impl_from_IMFPMediaItem(iface); IMFPresentationDescriptor *pd; IMFStreamDescriptor *sd; HRESULT hr; TRACE("%p, %lu, %p.\n", iface, index, selected); EnterCriticalSection(&item->player->cs); if (SUCCEEDED(hr = media_item_get_pd(item, &pd))) { if (SUCCEEDED(hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, index, selected, &sd))) IMFStreamDescriptor_Release(sd); IMFPresentationDescriptor_Release(pd); } LeaveCriticalSection(&item->player->cs); return hr; } static HRESULT WINAPI media_item_SetStreamSelection(IMFPMediaItem *iface, DWORD index, BOOL select) { struct media_item *item = impl_from_IMFPMediaItem(iface); IMFPresentationDescriptor *pd; HRESULT hr; TRACE("%p, %lu, %d.\n", iface, index, select); EnterCriticalSection(&item->player->cs); if (SUCCEEDED(hr = media_item_get_pd(item, &pd))) { hr = select ? IMFPresentationDescriptor_SelectStream(pd, index) : IMFPresentationDescriptor_DeselectStream(pd, index); IMFPresentationDescriptor_Release(pd); } LeaveCriticalSection(&item->player->cs); return hr; } static HRESULT WINAPI media_item_GetStreamAttribute(IMFPMediaItem *iface, DWORD index, REFGUID key, PROPVARIANT *value) { struct media_item *item = impl_from_IMFPMediaItem(iface); IMFPresentationDescriptor *pd; IMFStreamDescriptor *sd; BOOL selected; HRESULT hr; TRACE("%p, %lu, %s, %p.\n", iface, index, debugstr_guid(key), value); EnterCriticalSection(&item->player->cs); if (SUCCEEDED(hr = media_item_get_pd(item, &pd))) { if (SUCCEEDED(hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, index, &selected, &sd))) { hr = IMFStreamDescriptor_GetItem(sd, key, value); IMFStreamDescriptor_Release(sd); } IMFPresentationDescriptor_Release(pd); } LeaveCriticalSection(&item->player->cs); return hr; } static HRESULT WINAPI media_item_GetPresentationAttribute(IMFPMediaItem *iface, REFGUID key, PROPVARIANT *value) { struct media_item *item = impl_from_IMFPMediaItem(iface); IMFPresentationDescriptor *pd; HRESULT hr; TRACE("%p, %s, %p.\n", iface, debugstr_guid(key), value); EnterCriticalSection(&item->player->cs); if (SUCCEEDED(hr = media_item_get_pd(item, &pd))) { hr = IMFPresentationDescriptor_GetItem(pd, key, value); IMFPresentationDescriptor_Release(pd); } LeaveCriticalSection(&item->player->cs); return hr; } static HRESULT WINAPI media_item_GetCharacteristics(IMFPMediaItem *iface, MFP_MEDIAITEM_CHARACTERISTICS *flags) { struct media_item *item = impl_from_IMFPMediaItem(iface); DWORD value = 0; HRESULT hr; TRACE("%p, %p.\n", iface, flags); *flags = 0; EnterCriticalSection(&item->player->cs); if (item->player->state == MFP_MEDIAPLAYER_STATE_SHUTDOWN) hr = MF_E_SHUTDOWN; else { if (SUCCEEDED(hr = IMFMediaSource_GetCharacteristics(item->source, &value))) { *flags = value & (MFP_MEDIAITEM_IS_LIVE | MFP_MEDIAITEM_CAN_SEEK | MFP_MEDIAITEM_CAN_PAUSE | MFP_MEDIAITEM_HAS_SLOW_SEEK); } } LeaveCriticalSection(&item->player->cs); return hr; } static HRESULT WINAPI media_item_SetStreamSink(IMFPMediaItem *iface, DWORD index, IUnknown *sink) { struct media_item *item = impl_from_IMFPMediaItem(iface); IMFStreamDescriptor *sd; IUnknown *sink_object; BOOL selected; HRESULT hr; TRACE("%p, %lu, %p.\n", iface, index, sink); if (FAILED(hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(item->pd, index, &selected, &sd))) return hr; if (sink) { if (FAILED(hr = IUnknown_QueryInterface(sink, &IID_IMFStreamSink, (void **)&sink_object))) hr = IUnknown_QueryInterface(sink, &IID_IMFActivate, (void **)&sink_object); if (sink_object) { hr = IMFStreamDescriptor_SetUnknown(sd, &_MF_CUSTOM_SINK, sink_object); IUnknown_Release(sink_object); } } else IMFStreamDescriptor_DeleteItem(sd, &_MF_CUSTOM_SINK); IMFStreamDescriptor_Release(sd); return hr; } static HRESULT WINAPI media_item_GetMetadata(IMFPMediaItem *iface, IPropertyStore **metadata) { struct media_item *item = impl_from_IMFPMediaItem(iface); TRACE("%p, %p.\n", iface, metadata); return MFGetService((IUnknown *)item->source, &MF_PROPERTY_HANDLER_SERVICE, &IID_IPropertyStore, (void **)metadata); } static const IMFPMediaItemVtbl media_item_vtbl = { media_item_QueryInterface, media_item_AddRef, media_item_Release, media_item_GetMediaPlayer, media_item_GetURL, media_item_GetObject, media_item_GetUserData, media_item_SetUserData, media_item_GetStartStopPosition, media_item_SetStartStopPosition, media_item_HasVideo, media_item_HasAudio, media_item_IsProtected, media_item_GetDuration, media_item_GetNumberOfStreams, media_item_GetStreamSelection, media_item_SetStreamSelection, media_item_GetStreamAttribute, media_item_GetPresentationAttribute, media_item_GetCharacteristics, media_item_SetStreamSink, media_item_GetMetadata, }; static struct media_item *unsafe_impl_from_IMFPMediaItem(IMFPMediaItem *iface) { if (!iface) return NULL; assert(iface->lpVtbl == (IMFPMediaItemVtbl *)&media_item_vtbl); return CONTAINING_RECORD(iface, struct media_item, IMFPMediaItem_iface); } static HRESULT create_media_item(struct media_player *player, DWORD_PTR user_data, struct media_item **item) { struct media_item *object; if (!(object = calloc(1, sizeof(*object)))) return E_OUTOFMEMORY; object->IMFPMediaItem_iface.lpVtbl = &media_item_vtbl; object->refcount = 1; object->user_data = user_data; object->player = player; IMFPMediaPlayer_AddRef(&object->player->IMFPMediaPlayer_iface); *item = object; return S_OK; } static HRESULT media_item_set_source(struct media_item *item, IUnknown *object) { IMFPresentationDescriptor *pd; IMFMediaSource *source; HRESULT hr; if (FAILED(hr = IUnknown_QueryInterface(object, &IID_IMFMediaSource, (void **)&source))) return hr; if (FAILED(hr = IMFMediaSource_CreatePresentationDescriptor(source, &pd))) { WARN("Failed to get presentation descriptor, hr %#lx.\n", hr); IMFMediaSource_Release(source); return hr; } item->source = source; item->pd = pd; return hr; } static void media_player_queue_event(struct media_player *player, struct media_event *event) { if (player->options & MFP_OPTION_FREE_THREADED_CALLBACK) { MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_MULTITHREADED, &player->events_callback, &event->IUnknown_iface); } else { IUnknown_AddRef(&event->IUnknown_iface); PostMessageW(player->event_window, WM_USER, 0, (LPARAM)event); } } static HRESULT WINAPI media_player_QueryInterface(IMFPMediaPlayer *iface, REFIID riid, void **obj) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); if (IsEqualIID(riid, &IID_IMFPMediaPlayer) || IsEqualIID(riid, &IID_IUnknown)) { *obj = &player->IMFPMediaPlayer_iface; } else if (IsEqualIID(riid, &IID_IPropertyStore)) { *obj = &player->IPropertyStore_iface; } else { WARN("Unsupported interface %s.\n", debugstr_guid(riid)); *obj = NULL; return E_NOINTERFACE; } IUnknown_AddRef((IUnknown *)*obj); return S_OK; } static ULONG WINAPI media_player_AddRef(IMFPMediaPlayer *iface) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); ULONG refcount = InterlockedIncrement(&player->refcount); TRACE("%p, refcount %lu.\n", iface, refcount); return refcount; } static ULONG WINAPI media_player_Release(IMFPMediaPlayer *iface) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); ULONG refcount = InterlockedDecrement(&player->refcount); TRACE("%p, refcount %lu.\n", iface, refcount); if (!refcount) { if (player->callback) IMFPMediaPlayerCallback_Release(player->callback); if (player->propstore) IPropertyStore_Release(player->propstore); if (player->resolver) IMFSourceResolver_Release(player->resolver); if (player->session) IMFMediaSession_Release(player->session); DestroyWindow(player->event_window); DeleteCriticalSection(&player->cs); free(player); platform_shutdown(); } return refcount; } static HRESULT WINAPI media_player_Play(IMFPMediaPlayer *iface) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); PROPVARIANT pos; TRACE("%p.\n", iface); pos.vt = VT_EMPTY; return IMFMediaSession_Start(player->session, &GUID_NULL, &pos); } static HRESULT WINAPI media_player_Pause(IMFPMediaPlayer *iface) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); TRACE("%p.\n", iface); return IMFMediaSession_Pause(player->session); } static HRESULT WINAPI media_player_Stop(IMFPMediaPlayer *iface) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); TRACE("%p.\n", iface); return IMFMediaSession_Stop(player->session); } static HRESULT WINAPI media_player_FrameStep(IMFPMediaPlayer *iface) { FIXME("%p.\n", iface); return E_NOTIMPL; } static HRESULT WINAPI media_player_SetPosition(IMFPMediaPlayer *iface, REFGUID postype, const PROPVARIANT *position) { FIXME("%p, %s, %p.\n", iface, debugstr_guid(postype), position); return E_NOTIMPL; } static HRESULT WINAPI media_player_GetPosition(IMFPMediaPlayer *iface, REFGUID postype, PROPVARIANT *position) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); IMFPresentationClock *presentation_clock; IMFClock *clock; HRESULT hr; TRACE("%p, %s, %p.\n", iface, debugstr_guid(postype), position); if (!position) return E_POINTER; if (!IsEqualGUID(postype, &MFP_POSITIONTYPE_100NS)) return E_INVALIDARG; EnterCriticalSection(&player->cs); if (player->state == MFP_MEDIAPLAYER_STATE_SHUTDOWN) hr = MF_E_SHUTDOWN; else if (!player->item) hr = MF_E_INVALIDREQUEST; else { if (SUCCEEDED(hr = IMFMediaSession_GetClock(player->session, &clock))) { if (SUCCEEDED(hr = IMFClock_QueryInterface(clock, &IID_IMFPresentationClock, (void **)&presentation_clock))) { position->vt = VT_UI8; hr = IMFPresentationClock_GetTime(presentation_clock, (MFTIME *)&position->uhVal.QuadPart); IMFPresentationClock_Release(presentation_clock); } IMFClock_Release(clock); } } LeaveCriticalSection(&player->cs); return hr; } static HRESULT WINAPI media_player_GetDuration(IMFPMediaPlayer *iface, REFGUID postype, PROPVARIANT *duration) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); HRESULT hr; TRACE("%p, %s, %p.\n", iface, debugstr_guid(postype), duration); if (!duration) return E_POINTER; if (!IsEqualGUID(postype, &MFP_POSITIONTYPE_100NS)) return E_INVALIDARG; EnterCriticalSection(&player->cs); if (player->state == MFP_MEDIAPLAYER_STATE_SHUTDOWN) hr = MF_E_SHUTDOWN; else if (!player->item) hr = MF_E_INVALIDREQUEST; else /* FIXME: use start/stop markers for resulting duration */ hr = IMFPMediaItem_GetDuration(player->item, postype, duration); LeaveCriticalSection(&player->cs); return hr; } static HRESULT WINAPI media_player_SetRate(IMFPMediaPlayer *iface, float rate) { FIXME("%p, %f.\n", iface, rate); return E_NOTIMPL; } static HRESULT WINAPI media_player_GetRate(IMFPMediaPlayer *iface, float *rate) { FIXME("%p, %p.\n", iface, rate); return E_NOTIMPL; } static HRESULT WINAPI media_player_GetSupportedRates(IMFPMediaPlayer *iface, BOOL forward, float *slowest_rate, float *fastest_rate) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); IMFRateSupport *rs; HRESULT hr; TRACE("%p, %d, %p, %p.\n", iface, forward, slowest_rate, fastest_rate); if (SUCCEEDED(hr = MFGetService((IUnknown *)player->session, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateSupport, (void **)&rs))) { if (SUCCEEDED(hr = IMFRateSupport_GetSlowestRate(rs, forward ? MFRATE_FORWARD : MFRATE_REVERSE, FALSE, slowest_rate))) hr = IMFRateSupport_GetFastestRate(rs, forward ? MFRATE_FORWARD : MFRATE_REVERSE, FALSE, fastest_rate); IMFRateSupport_Release(rs); } return hr; } static HRESULT WINAPI media_player_GetState(IMFPMediaPlayer *iface, MFP_MEDIAPLAYER_STATE *state) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); TRACE("%p, %p.\n", iface, state); *state = player->state; return S_OK; } static HRESULT media_player_create_item_from_url(struct media_player *player, const WCHAR *url, BOOL sync, DWORD_PTR user_data, IMFPMediaItem **ret) { struct media_item *item; MF_OBJECT_TYPE obj_type; IUnknown *object; HRESULT hr; if (sync && !ret) return E_POINTER; if (!sync && !player->callback) { WARN("Asynchronous item creation is not supported without user callback.\n"); return MF_E_INVALIDREQUEST; } if (FAILED(hr = create_media_item(player, user_data, &item))) return hr; if (url && !(item->url = wcsdup(url))) { IMFPMediaItem_Release(&item->IMFPMediaItem_iface); return E_OUTOFMEMORY; } if (sync) { *ret = NULL; if (SUCCEEDED(hr = IMFSourceResolver_CreateObjectFromURL(player->resolver, url, MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE, player->propstore, &obj_type, &object))) { hr = media_item_set_source(item, object); IUnknown_Release(object); } if (SUCCEEDED(hr)) { *ret = &item->IMFPMediaItem_iface; IMFPMediaItem_AddRef(*ret); } IMFPMediaItem_Release(&item->IMFPMediaItem_iface); } else { if (ret) *ret = NULL; hr = IMFSourceResolver_BeginCreateObjectFromURL(player->resolver, url, MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE, player->propstore, NULL, &player->resolver_callback, (IUnknown *)&item->IMFPMediaItem_iface); IMFPMediaItem_Release(&item->IMFPMediaItem_iface); } return hr; } static HRESULT WINAPI media_player_CreateMediaItemFromURL(IMFPMediaPlayer *iface, const WCHAR *url, BOOL sync, DWORD_PTR user_data, IMFPMediaItem **item) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); HRESULT hr; TRACE("%p, %s, %d, %Ix, %p.\n", iface, debugstr_w(url), sync, user_data, item); EnterCriticalSection(&player->cs); if (player->state == MFP_MEDIAPLAYER_STATE_SHUTDOWN) hr = MF_E_SHUTDOWN; else hr = media_player_create_item_from_url(player, url, sync, user_data, item); LeaveCriticalSection(&player->cs); return hr; } static HRESULT media_player_create_item_from_object(struct media_player *player, IUnknown *object, BOOL sync, DWORD_PTR user_data, IMFPMediaItem **ret) { struct media_item *item; MF_OBJECT_TYPE obj_type; HRESULT hr; IMFByteStream *stream = NULL; IMFMediaSource *source = NULL; *ret = NULL; if (FAILED(hr = create_media_item(player, user_data, &item))) return hr; item->object = object; IUnknown_AddRef(item->object); if (FAILED(IUnknown_QueryInterface(object, &IID_IMFMediaSource, (void **)&source))) IUnknown_QueryInterface(object, &IID_IMFByteStream, (void **)&stream); if (!source && !stream) { WARN("Unsupported object type.\n"); IMFPMediaItem_Release(&item->IMFPMediaItem_iface); return E_UNEXPECTED; } if (sync) { if (stream) hr = IMFSourceResolver_CreateObjectFromByteStream(player->resolver, stream, NULL, MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE, player->propstore, &obj_type, &object); else IUnknown_AddRef(object); if (SUCCEEDED(hr)) hr = media_item_set_source(item, object); IUnknown_Release(object); if (SUCCEEDED(hr)) { *ret = &item->IMFPMediaItem_iface; IMFPMediaItem_AddRef(*ret); } IMFPMediaItem_Release(&item->IMFPMediaItem_iface); } else { if (stream) { hr = IMFSourceResolver_BeginCreateObjectFromByteStream(player->resolver, stream, NULL, MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE, player->propstore, NULL, &player->resolver_callback, (IUnknown *)&item->IMFPMediaItem_iface); } else { /* Resolver callback will check again if item's object is a source. */ hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_MULTITHREADED, &player->resolver_callback, (IUnknown *)&item->IMFPMediaItem_iface); } IMFPMediaItem_Release(&item->IMFPMediaItem_iface); } if (source) IMFMediaSource_Release(source); if (stream) IMFByteStream_Release(stream); return hr; } static HRESULT WINAPI media_player_CreateMediaItemFromObject(IMFPMediaPlayer *iface, IUnknown *object, BOOL sync, DWORD_PTR user_data, IMFPMediaItem **item) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); HRESULT hr; TRACE("%p, %p, %d, %Ix, %p.\n", iface, object, sync, user_data, item); EnterCriticalSection(&player->cs); if (player->state == MFP_MEDIAPLAYER_STATE_SHUTDOWN) hr = MF_E_SHUTDOWN; else hr = media_player_create_item_from_object(player, object, sync, user_data, item); LeaveCriticalSection(&player->cs); return hr; } static HRESULT media_item_create_source_node(struct media_item *item, IMFStreamDescriptor *sd, IMFTopologyNode **node) { HRESULT hr; if (SUCCEEDED(hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, node))) { IMFTopologyNode_SetUnknown(*node, &MF_TOPONODE_SOURCE, (IUnknown *)item->source); IMFTopologyNode_SetUnknown(*node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR, (IUnknown *)item->pd); IMFTopologyNode_SetUnknown(*node, &MF_TOPONODE_STREAM_DESCRIPTOR, (IUnknown *)sd); if (item->start_position) IMFTopologyNode_SetUINT64(*node, &MF_TOPONODE_MEDIASTART, item->start_position); if (item->stop_position) IMFTopologyNode_SetUINT64(*node, &MF_TOPONODE_MEDIASTOP, item->stop_position); } return hr; } static HRESULT media_item_create_sink_node(IUnknown *sink, IMFTopologyNode **node) { HRESULT hr; if (SUCCEEDED(hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, node))) IMFTopologyNode_SetObject(*node, sink); return hr; } static HRESULT media_item_create_topology(struct media_player *player, struct media_item *item, IMFTopology **out) { IMFTopologyNode *src_node, *sink_node; BOOL selected, video_added = FALSE; IMFStreamDescriptor *sd; IMFTopology *topology; unsigned int idx; IUnknown *sink; HRESULT hr; GUID major; if (FAILED(hr = MFCreateTopology(&topology))) return hr; /* Set up branches for all selected streams. */ idx = 0; while (SUCCEEDED(IMFPresentationDescriptor_GetStreamDescriptorByIndex(item->pd, idx++, &selected, &sd))) { if (!selected || FAILED(media_item_get_stream_type(sd, &major))) { IMFStreamDescriptor_Release(sd); continue; } sink = NULL; if (SUCCEEDED(IMFStreamDescriptor_GetUnknown(sd, &_MF_CUSTOM_SINK, &IID_IUnknown, (void **)&sink))) { /* User sink is attached as-is. */ } else if (IsEqualGUID(&major, &MFMediaType_Audio)) { if (FAILED(hr = MFCreateAudioRendererActivate((IMFActivate **)&sink))) WARN("Failed to create SAR activation object, hr %#lx.\n", hr); } else if (IsEqualGUID(&major, &MFMediaType_Video) && player->output_window && !video_added) { if (FAILED(hr = MFCreateVideoRendererActivate(player->output_window, (IMFActivate **)&sink))) WARN("Failed to create EVR activation object, hr %#lx.\n", hr); video_added = SUCCEEDED(hr); } if (sink) { hr = media_item_create_source_node(item, sd, &src_node); if (SUCCEEDED(hr)) hr = media_item_create_sink_node(sink, &sink_node); if (SUCCEEDED(hr)) { IMFTopology_AddNode(topology, src_node); IMFTopology_AddNode(topology, sink_node); IMFTopologyNode_ConnectOutput(src_node, 0, sink_node, 0); } if (src_node) IMFTopologyNode_Release(src_node); if (sink_node) IMFTopologyNode_Release(sink_node); IUnknown_Release(sink); } IMFStreamDescriptor_Release(sd); } IMFTopology_SetUINT32(topology, &MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES, TRUE); *out = topology; return S_OK; } static HRESULT WINAPI media_player_SetMediaItem(IMFPMediaPlayer *iface, IMFPMediaItem *item_iface) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); struct media_item *item; IMFTopology *topology; HRESULT hr; TRACE("%p, %p.\n", iface, item_iface); if (!item_iface) return E_POINTER; item = unsafe_impl_from_IMFPMediaItem(item_iface); if (item->player != player) return E_INVALIDARG; if (FAILED(hr = media_item_create_topology(player, item, &topology))) return hr; IMFTopology_SetUnknown(topology, &_MF_TOPO_MEDIA_ITEM, (IUnknown *)item_iface); hr = IMFMediaSession_SetTopology(player->session, MFSESSION_SETTOPOLOGY_IMMEDIATE, topology); IMFTopology_Release(topology); return hr; } static HRESULT WINAPI media_player_ClearMediaItem(IMFPMediaPlayer *iface) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); TRACE("%p.\n", iface); return IMFMediaSession_SetTopology(player->session, MFSESSION_SETTOPOLOGY_CLEAR_CURRENT, NULL); } static HRESULT WINAPI media_player_GetMediaItem(IMFPMediaPlayer *iface, IMFPMediaItem **item) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); HRESULT hr = S_OK; TRACE("%p, %p.\n", iface, item); if (!item) return E_POINTER; EnterCriticalSection(&player->cs); if (player->state == MFP_MEDIAPLAYER_STATE_SHUTDOWN) hr = MF_E_SHUTDOWN; else if (!player->item) hr = MF_E_NOT_FOUND; else { *item = player->item; IMFPMediaItem_AddRef(player->item); } LeaveCriticalSection(&player->cs); return hr; } static HRESULT WINAPI media_player_GetVolume(IMFPMediaPlayer *iface, float *volume) { FIXME("%p, %p.\n", iface, volume); return E_NOTIMPL; } static HRESULT WINAPI media_player_SetVolume(IMFPMediaPlayer *iface, float volume) { FIXME("%p, %.8e.\n", iface, volume); return E_NOTIMPL; } static HRESULT WINAPI media_player_GetBalance(IMFPMediaPlayer *iface, float *balance) { FIXME("%p, %p.\n", iface, balance); return E_NOTIMPL; } static HRESULT WINAPI media_player_SetBalance(IMFPMediaPlayer *iface, float balance) { FIXME("%p, %.8e.\n", iface, balance); return E_NOTIMPL; } static HRESULT WINAPI media_player_GetMute(IMFPMediaPlayer *iface, BOOL *mute) { FIXME("%p, %p.\n", iface, mute); return E_NOTIMPL; } static HRESULT WINAPI media_player_SetMute(IMFPMediaPlayer *iface, BOOL mute) { FIXME("%p, %d.\n", iface, mute); return E_NOTIMPL; } static HRESULT media_player_get_display_control(const struct media_player *player, IMFVideoDisplayControl **display_control) { HRESULT hr = MFGetService((IUnknown *)player->session, &MR_VIDEO_RENDER_SERVICE, &IID_IMFVideoDisplayControl, (void **)display_control); if (SUCCEEDED(hr)) return hr; return hr == MF_E_SHUTDOWN ? hr : MF_E_INVALIDREQUEST; } static HRESULT WINAPI media_player_GetNativeVideoSize(IMFPMediaPlayer *iface, SIZE *video, SIZE *arvideo) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); IMFVideoDisplayControl *display_control; HRESULT hr; TRACE("%p, %p, %p.\n", iface, video, arvideo); if (SUCCEEDED(hr = media_player_get_display_control(player, &display_control))) { hr = IMFVideoDisplayControl_GetNativeVideoSize(display_control, video, arvideo); IMFVideoDisplayControl_Release(display_control); } return hr; } static HRESULT WINAPI media_player_GetIdealVideoSize(IMFPMediaPlayer *iface, SIZE *min_size, SIZE *max_size) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); IMFVideoDisplayControl *display_control; HRESULT hr; TRACE("%p, %p, %p.\n", iface, min_size, max_size); if (SUCCEEDED(hr = media_player_get_display_control(player, &display_control))) { hr = IMFVideoDisplayControl_GetIdealVideoSize(display_control, min_size, max_size); IMFVideoDisplayControl_Release(display_control); } return hr; } static HRESULT WINAPI media_player_SetVideoSourceRect(IMFPMediaPlayer *iface, MFVideoNormalizedRect const *rect) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); IMFVideoDisplayControl *display_control; RECT dst_rect; HRESULT hr; TRACE("%p, %s.\n", iface, debugstr_normalized_rect(rect)); if (!GetClientRect(player->output_window, &dst_rect)) hr = HRESULT_FROM_WIN32(GetLastError()); else if (SUCCEEDED(hr = media_player_get_display_control(player, &display_control))) { hr = IMFVideoDisplayControl_SetVideoPosition(display_control, rect, &dst_rect); IMFVideoDisplayControl_Release(display_control); } return hr; } static HRESULT WINAPI media_player_GetVideoSourceRect(IMFPMediaPlayer *iface, MFVideoNormalizedRect *rect) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); IMFVideoDisplayControl *display_control; HRESULT hr; RECT dest; TRACE("%p, %p.\n", iface, rect); if (SUCCEEDED(hr = media_player_get_display_control(player, &display_control))) { hr = IMFVideoDisplayControl_GetVideoPosition(display_control, rect, &dest); IMFVideoDisplayControl_Release(display_control); } return hr; } static HRESULT WINAPI media_player_SetAspectRatioMode(IMFPMediaPlayer *iface, DWORD mode) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); IMFVideoDisplayControl *display_control; HRESULT hr; TRACE("%p, %lu.\n", iface, mode); if (SUCCEEDED(hr = media_player_get_display_control(player, &display_control))) { hr = IMFVideoDisplayControl_SetAspectRatioMode(display_control, mode); IMFVideoDisplayControl_Release(display_control); } return hr; } static HRESULT WINAPI media_player_GetAspectRatioMode(IMFPMediaPlayer *iface, DWORD *mode) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); IMFVideoDisplayControl *display_control; HRESULT hr; TRACE("%p, %p.\n", iface, mode); if (SUCCEEDED(hr = media_player_get_display_control(player, &display_control))) { hr = IMFVideoDisplayControl_GetAspectRatioMode(display_control, mode); IMFVideoDisplayControl_Release(display_control); } return hr; } static HRESULT WINAPI media_player_GetVideoWindow(IMFPMediaPlayer *iface, HWND *window) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); TRACE("%p, %p.\n", iface, window); *window = player->output_window; return S_OK; } static HRESULT WINAPI media_player_UpdateVideo(IMFPMediaPlayer *iface) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); IMFVideoDisplayControl *display_control; HRESULT hr; RECT rect; TRACE("%p.\n", iface); if (SUCCEEDED(hr = media_player_get_display_control(player, &display_control))) { if (GetClientRect(player->output_window, &rect)) hr = IMFVideoDisplayControl_SetVideoPosition(display_control, NULL, &rect); if (SUCCEEDED(hr)) hr = IMFVideoDisplayControl_RepaintVideo(display_control); IMFVideoDisplayControl_Release(display_control); } return hr; } static HRESULT WINAPI media_player_SetBorderColor(IMFPMediaPlayer *iface, COLORREF color) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); IMFVideoDisplayControl *display_control; HRESULT hr; TRACE("%p, %#lx.\n", iface, color); if (SUCCEEDED(hr = media_player_get_display_control(player, &display_control))) { hr = IMFVideoDisplayControl_SetBorderColor(display_control, color); IMFVideoDisplayControl_Release(display_control); } return hr; } static HRESULT WINAPI media_player_GetBorderColor(IMFPMediaPlayer *iface, COLORREF *color) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); IMFVideoDisplayControl *display_control; HRESULT hr; TRACE("%p, %p.\n", iface, color); if (SUCCEEDED(hr = media_player_get_display_control(player, &display_control))) { hr = IMFVideoDisplayControl_GetBorderColor(display_control, color); IMFVideoDisplayControl_Release(display_control); } return hr; } static HRESULT WINAPI media_player_InsertEffect(IMFPMediaPlayer *iface, IUnknown *effect, BOOL optional) { FIXME("%p, %p, %d.\n", iface, effect, optional); return E_NOTIMPL; } static HRESULT WINAPI media_player_RemoveEffect(IMFPMediaPlayer *iface, IUnknown *effect) { FIXME("%p, %p.\n", iface, effect); return E_NOTIMPL; } static HRESULT WINAPI media_player_RemoveAllEffects(IMFPMediaPlayer *iface) { FIXME("%p.\n", iface); return E_NOTIMPL; } static HRESULT WINAPI media_player_Shutdown(IMFPMediaPlayer *iface) { struct media_player *player = impl_from_IMFPMediaPlayer(iface); TRACE("%p.\n", iface); EnterCriticalSection(&player->cs); media_player_set_state(player, MFP_MEDIAPLAYER_STATE_SHUTDOWN); if (player->item) { IMFPMediaItem_Release(player->item); player->item = NULL; } LeaveCriticalSection(&player->cs); return S_OK; } static const IMFPMediaPlayerVtbl media_player_vtbl = { media_player_QueryInterface, media_player_AddRef, media_player_Release, media_player_Play, media_player_Pause, media_player_Stop, media_player_FrameStep, media_player_SetPosition, media_player_GetPosition, media_player_GetDuration, media_player_SetRate, media_player_GetRate, media_player_GetSupportedRates, media_player_GetState, media_player_CreateMediaItemFromURL, media_player_CreateMediaItemFromObject, media_player_SetMediaItem, media_player_ClearMediaItem, media_player_GetMediaItem, media_player_GetVolume, media_player_SetVolume, media_player_GetBalance, media_player_SetBalance, media_player_GetMute, media_player_SetMute, media_player_GetNativeVideoSize, media_player_GetIdealVideoSize, media_player_SetVideoSourceRect, media_player_GetVideoSourceRect, media_player_SetAspectRatioMode, media_player_GetAspectRatioMode, media_player_GetVideoWindow, media_player_UpdateVideo, media_player_SetBorderColor, media_player_GetBorderColor, media_player_InsertEffect, media_player_RemoveEffect, media_player_RemoveAllEffects, media_player_Shutdown, }; static HRESULT WINAPI media_player_propstore_QueryInterface(IPropertyStore *iface, REFIID riid, void **obj) { struct media_player *player = impl_from_IPropertyStore(iface); return IMFPMediaPlayer_QueryInterface(&player->IMFPMediaPlayer_iface, riid, obj); } static ULONG WINAPI media_player_propstore_AddRef(IPropertyStore *iface) { struct media_player *player = impl_from_IPropertyStore(iface); return IMFPMediaPlayer_AddRef(&player->IMFPMediaPlayer_iface); } static ULONG WINAPI media_player_propstore_Release(IPropertyStore *iface) { struct media_player *player = impl_from_IPropertyStore(iface); return IMFPMediaPlayer_Release(&player->IMFPMediaPlayer_iface); } static HRESULT WINAPI media_player_propstore_GetCount(IPropertyStore *iface, DWORD *count) { struct media_player *player = impl_from_IPropertyStore(iface); TRACE("%p, %p.\n", iface, count); return IPropertyStore_GetCount(player->propstore, count); } static HRESULT WINAPI media_player_propstore_GetAt(IPropertyStore *iface, DWORD prop, PROPERTYKEY *key) { struct media_player *player = impl_from_IPropertyStore(iface); TRACE("%p, %lu, %p.\n", iface, prop, key); return IPropertyStore_GetAt(player->propstore, prop, key); } static HRESULT WINAPI media_player_propstore_GetValue(IPropertyStore *iface, REFPROPERTYKEY key, PROPVARIANT *value) { struct media_player *player = impl_from_IPropertyStore(iface); TRACE("%p, %p, %p.\n", iface, key, value); return IPropertyStore_GetValue(player->propstore, key, value); } static HRESULT WINAPI media_player_propstore_SetValue(IPropertyStore *iface, REFPROPERTYKEY key, REFPROPVARIANT value) { struct media_player *player = impl_from_IPropertyStore(iface); TRACE("%p, %p, %p.\n", iface, key, value); return IPropertyStore_SetValue(player->propstore, key, value); } static HRESULT WINAPI media_player_propstore_Commit(IPropertyStore *iface) { struct media_player *player = impl_from_IPropertyStore(iface); TRACE("%p.\n", iface); return IPropertyStore_Commit(player->propstore); } static const IPropertyStoreVtbl media_player_propstore_vtbl = { media_player_propstore_QueryInterface, media_player_propstore_AddRef, media_player_propstore_Release, media_player_propstore_GetCount, media_player_propstore_GetAt, media_player_propstore_GetValue, media_player_propstore_SetValue, media_player_propstore_Commit, }; static HRESULT WINAPI media_player_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) { if (IsEqualIID(riid, &IID_IMFAsyncCallback) || IsEqualIID(riid, &IID_IUnknown)) { *obj = iface; IMFAsyncCallback_AddRef(iface); return S_OK; } *obj = NULL; return E_NOINTERFACE; } static ULONG WINAPI media_player_resolver_callback_AddRef(IMFAsyncCallback *iface) { struct media_player *player = impl_from_resolver_IMFAsyncCallback(iface); return IMFPMediaPlayer_AddRef(&player->IMFPMediaPlayer_iface); } static ULONG WINAPI media_player_resolver_callback_Release(IMFAsyncCallback *iface) { struct media_player *player = impl_from_resolver_IMFAsyncCallback(iface); return IMFPMediaPlayer_Release(&player->IMFPMediaPlayer_iface); } static HRESULT WINAPI media_player_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) { return E_NOTIMPL; } static HRESULT WINAPI media_player_resolver_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) { struct media_player *player = impl_from_resolver_IMFAsyncCallback(iface); struct media_event *event; IUnknown *object, *state; MF_OBJECT_TYPE obj_type; struct media_item *item; HRESULT hr; if (FAILED(IMFAsyncResult_GetState(result, &state))) return S_OK; item = impl_from_IMFPMediaItem((IMFPMediaItem *)state); if (item->object) { if (FAILED(hr = IUnknown_QueryInterface(item->object, &IID_IMFMediaSource, (void **)&object))) hr = IMFSourceResolver_EndCreateObjectFromByteStream(player->resolver, result, &obj_type, &object); } else hr = IMFSourceResolver_EndCreateObjectFromURL(player->resolver, result, &obj_type, &object); if (SUCCEEDED(hr)) { hr = media_item_set_source(item, object); IUnknown_Release(object); } if (FAILED(hr)) WARN("Failed to set media source, hr %#lx.\n", hr); if (FAILED(media_event_create(player, MFP_EVENT_TYPE_MEDIAITEM_CREATED, hr, &item->IMFPMediaItem_iface, &event))) { WARN("Failed to create event object.\n"); IUnknown_Release(state); return S_OK; } event->u.item_created.dwUserData = item->user_data; media_player_queue_event(player, event); IUnknown_Release(&event->IUnknown_iface); IUnknown_Release(state); return S_OK; } static const IMFAsyncCallbackVtbl media_player_resolver_callback_vtbl = { media_player_callback_QueryInterface, media_player_resolver_callback_AddRef, media_player_resolver_callback_Release, media_player_callback_GetParameters, media_player_resolver_callback_Invoke, }; static ULONG WINAPI media_player_events_callback_AddRef(IMFAsyncCallback *iface) { struct media_player *player = impl_from_events_IMFAsyncCallback(iface); return IMFPMediaPlayer_AddRef(&player->IMFPMediaPlayer_iface); } static ULONG WINAPI media_player_events_callback_Release(IMFAsyncCallback *iface) { struct media_player *player = impl_from_events_IMFAsyncCallback(iface); return IMFPMediaPlayer_Release(&player->IMFPMediaPlayer_iface); } static HRESULT WINAPI media_player_events_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) { struct media_player *player = impl_from_events_IMFAsyncCallback(iface); struct media_event *event; IUnknown *state; if (FAILED(IMFAsyncResult_GetState(result, &state))) return S_OK; event = impl_event_from_IUnknown(state); if (player->callback) IMFPMediaPlayerCallback_OnMediaPlayerEvent(player->callback, &event->u.header); IUnknown_Release(state); return S_OK; } static const IMFAsyncCallbackVtbl media_player_events_callback_vtbl = { media_player_callback_QueryInterface, media_player_events_callback_AddRef, media_player_events_callback_Release, media_player_callback_GetParameters, media_player_events_callback_Invoke, }; static ULONG WINAPI media_player_session_events_callback_AddRef(IMFAsyncCallback *iface) { struct media_player *player = impl_from_session_events_IMFAsyncCallback(iface); return IMFPMediaPlayer_AddRef(&player->IMFPMediaPlayer_iface); } static ULONG WINAPI media_player_session_events_callback_Release(IMFAsyncCallback *iface) { struct media_player *player = impl_from_session_events_IMFAsyncCallback(iface); return IMFPMediaPlayer_Release(&player->IMFPMediaPlayer_iface); } static void media_player_change_state(struct media_player *player, MFP_MEDIAPLAYER_STATE state, HRESULT event_status, struct media_event **event) { MFP_EVENT_TYPE event_type; EnterCriticalSection(&player->cs); if (state == MFP_MEDIAPLAYER_STATE_PLAYING) event_type = MFP_EVENT_TYPE_PLAY; else if (state == MFP_MEDIAPLAYER_STATE_PAUSED) event_type = MFP_EVENT_TYPE_PAUSE; else event_type = MFP_EVENT_TYPE_STOP; media_player_set_state(player, state); media_event_create(player, event_type, event_status, player->item, event); LeaveCriticalSection(&player->cs); } static void media_player_set_item(struct media_player *player, IMFTopology *topology, HRESULT event_status, struct media_event **event) { IMFPMediaItem *item; if (FAILED(IMFTopology_GetUnknown(topology, &_MF_TOPO_MEDIA_ITEM, &IID_IMFPMediaItem, (void **)&item))) return; EnterCriticalSection(&player->cs); if (player->item) IMFPMediaItem_Release(player->item); player->item = item; IMFPMediaItem_AddRef(player->item); media_event_create(player, MFP_EVENT_TYPE_MEDIAITEM_SET, event_status, item, event); LeaveCriticalSection(&player->cs); IMFPMediaItem_Release(item); } static void media_player_clear_item(struct media_player *player, HRESULT event_status, struct media_event **event) { IMFPMediaItem *item; EnterCriticalSection(&player->cs); item = player->item; player->item = NULL; media_event_create(player, MFP_EVENT_TYPE_MEDIAITEM_SET, event_status, item, event); LeaveCriticalSection(&player->cs); } static void media_player_create_forward_event(struct media_player *player, HRESULT event_status, IMFMediaEvent *session_event, struct media_event **event) { EnterCriticalSection(&player->cs); if (SUCCEEDED(media_event_create(player, MFP_EVENT_TYPE_MF, event_status, player->item, event))) { IMFMediaEvent_GetType(session_event, &(*event)->u.event.MFEventType); (*event)->u.event.pMFMediaEvent = session_event; IMFMediaEvent_AddRef((*event)->u.event.pMFMediaEvent); } LeaveCriticalSection(&player->cs); } static void media_player_playback_ended(struct media_player *player, HRESULT event_status, struct media_event **event) { EnterCriticalSection(&player->cs); media_player_set_state(player, MFP_MEDIAPLAYER_STATE_STOPPED); media_event_create(player, MFP_EVENT_TYPE_PLAYBACK_ENDED, event_status, player->item, event); LeaveCriticalSection(&player->cs); } static HRESULT WINAPI media_player_session_events_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) { struct media_player *player = impl_from_session_events_IMFAsyncCallback(iface); MediaEventType session_event_type = MEUnknown; struct media_event *event = NULL; IMFMediaEvent *session_event; MFP_MEDIAPLAYER_STATE state; HRESULT hr, event_status; IMFTopology *topology; unsigned int status; PROPVARIANT value; if (FAILED(hr = IMFMediaSession_EndGetEvent(player->session, result, &session_event))) return S_OK; IMFMediaEvent_GetType(session_event, &session_event_type); IMFMediaEvent_GetStatus(session_event, &event_status); switch (session_event_type) { case MESessionStarted: case MESessionStopped: case MESessionPaused: if (session_event_type == MESessionStarted) state = MFP_MEDIAPLAYER_STATE_PLAYING; else if (session_event_type == MESessionPaused) state = MFP_MEDIAPLAYER_STATE_PAUSED; else state = MFP_MEDIAPLAYER_STATE_STOPPED; media_player_change_state(player, state, event_status, &event); break; case MESessionTopologySet: value.vt = VT_EMPTY; if (SUCCEEDED(IMFMediaEvent_GetValue(session_event, &value))) { if (value.vt == VT_EMPTY) { media_player_clear_item(player, event_status, &event); } else if (value.vt == VT_UNKNOWN && value.punkVal && SUCCEEDED(IUnknown_QueryInterface(value.punkVal, &IID_IMFTopology, (void **)&topology))) { media_player_set_item(player, topology, event_status, &event); IMFTopology_Release(topology); } PropVariantClear(&value); } break; case MESessionTopologyStatus: if (SUCCEEDED(IMFMediaEvent_GetUINT32(session_event, &MF_EVENT_TOPOLOGY_STATUS, &status)) && status == MF_TOPOSTATUS_ENDED) { media_player_playback_ended(player, event_status, &event); } break; case MEBufferingStarted: case MEBufferingStopped: case MEExtendedType: case MEReconnectStart: case MEReconnectEnd: case MERendererEvent: case MEStreamSinkFormatChanged: media_player_create_forward_event(player, event_status, session_event, &event); break; case MEError: media_event_create(player, MFP_EVENT_TYPE_ERROR, event_status, NULL, &event); break; default: ; } if (event) { media_player_queue_event(player, event); IUnknown_Release(&event->IUnknown_iface); } IMFMediaSession_BeginGetEvent(player->session, &player->session_events_callback, NULL); IMFMediaEvent_Release(session_event); return S_OK; } static const IMFAsyncCallbackVtbl media_player_session_events_callback_vtbl = { media_player_callback_QueryInterface, media_player_session_events_callback_AddRef, media_player_session_events_callback_Release, media_player_callback_GetParameters, media_player_session_events_callback_Invoke, }; /*********************************************************************** * MFPCreateMediaPlayer (mfplay.@) */ HRESULT WINAPI MFPCreateMediaPlayer(const WCHAR *url, BOOL start_playback, MFP_CREATION_OPTIONS options, IMFPMediaPlayerCallback *callback, HWND window, IMFPMediaPlayer **player) { struct media_player *object; IMFPMediaItem *item; HRESULT hr; TRACE("%s, %d, %#x, %p, %p, %p.\n", debugstr_w(url), start_playback, options, callback, window, player); if (!player) return E_POINTER; *player = NULL; if (!url && start_playback) return E_INVALIDARG; if (!(object = calloc(1, sizeof(*object)))) return E_OUTOFMEMORY; platform_startup(); object->IMFPMediaPlayer_iface.lpVtbl = &media_player_vtbl; object->IPropertyStore_iface.lpVtbl = &media_player_propstore_vtbl; object->resolver_callback.lpVtbl = &media_player_resolver_callback_vtbl; object->events_callback.lpVtbl = &media_player_events_callback_vtbl; object->session_events_callback.lpVtbl = &media_player_session_events_callback_vtbl; object->refcount = 1; object->callback = callback; if (object->callback) IMFPMediaPlayerCallback_AddRef(object->callback); object->options = options; object->output_window = window; InitializeCriticalSection(&object->cs); if (FAILED(hr = CreatePropertyStore(&object->propstore))) goto failed; if (FAILED(hr = MFCreateSourceResolver(&object->resolver))) goto failed; if (FAILED(hr = MFCreateMediaSession(NULL, &object->session))) goto failed; if (FAILED(hr = IMFMediaSession_BeginGetEvent(object->session, &object->session_events_callback, NULL))) goto failed; if (!(object->options & MFP_OPTION_FREE_THREADED_CALLBACK)) { object->event_window = CreateWindowW(eventclassW, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, mfplay_instance, NULL); } if (url) { if (FAILED(hr = media_player_create_item_from_url(object, url, TRUE, 0, &item))) { WARN("Failed to create media item, hr %#lx.\n", hr); goto failed; } hr = IMFPMediaPlayer_SetMediaItem(&object->IMFPMediaPlayer_iface, item); IMFPMediaItem_Release(item); if (FAILED(hr)) { WARN("Failed to set media item, hr %#lx.\n", hr); goto failed; } if (start_playback) IMFPMediaPlayer_Play(&object->IMFPMediaPlayer_iface); } *player = &object->IMFPMediaPlayer_iface; return S_OK; failed: IMFPMediaPlayer_Release(&object->IMFPMediaPlayer_iface); return hr; } static void media_player_register_window_class(void) { WNDCLASSW cls = { 0 }; cls.lpfnWndProc = media_player_event_proc; cls.hInstance = mfplay_instance; cls.lpszClassName = eventclassW; RegisterClassW(&cls); } BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved) { switch (reason) { case DLL_PROCESS_ATTACH: mfplay_instance = instance; DisableThreadLibraryCalls(instance); media_player_register_window_class(); break; case DLL_PROCESS_DETACH: if (reserved) break; UnregisterClassW(eventclassW, instance); break; } return TRUE; }