Sweden-Number/dlls/mfplay/player.c

1410 lines
39 KiB
C

/*
* 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 <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "mfapi.h"
#include "mfplay.h"
#include "mferror.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
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();
}
struct media_item
{
IMFPMediaItem IMFPMediaItem_iface;
LONG refcount;
IMFPMediaPlayer *player;
IMFMediaSource *source;
IMFPresentationDescriptor *pd;
DWORD_PTR user_data;
WCHAR *url;
};
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;
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 %u.\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 %u.\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, 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;
/* 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 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 %u.\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 %u.\n", iface, refcount);
if (!refcount)
{
if (item->player)
IMFPMediaPlayer_Release(item->player);
if (item->source)
IMFMediaSource_Release(item->source);
if (item->pd)
IMFPresentationDescriptor_Release(item->pd);
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);
TRACE("%p, %p.\n", iface, player);
*player = item->player;
IMFPMediaPlayer_AddRef(*player);
return S_OK;
}
static HRESULT WINAPI media_item_GetURL(IMFPMediaItem *iface, LPWSTR *url)
{
struct media_item *item = impl_from_IMFPMediaItem(iface);
TRACE("%p, %p.\n", iface, url);
if (!item->url)
return MF_E_NOT_FOUND;
if (!(*url = CoTaskMemAlloc((wcslen(item->url) + 1) * sizeof(*item->url))))
return E_OUTOFMEMORY;
wcscpy(*url, item->url);
return S_OK;
}
static HRESULT WINAPI media_item_GetObject(IMFPMediaItem *iface, IUnknown **obj)
{
FIXME("%p, %p.\n", iface, obj);
return E_NOTIMPL;
}
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, %lx.\n", iface, user_data);
item->user_data = user_data;
return S_OK;
}
static HRESULT WINAPI media_item_GetStartStopPosition(IMFPMediaItem *iface, GUID *start_format,
PROPVARIANT *start_position, GUID *stop_format, PROPVARIANT *stop_position)
{
FIXME("%p, %p, %p, %p, %p.\n", iface, start_format, start_position, stop_format, stop_position);
return E_NOTIMPL;
}
static HRESULT WINAPI media_item_SetStartStopPosition(IMFPMediaItem *iface, const GUID *start_format,
const PROPVARIANT *start_position, const GUID *stop_format, const PROPVARIANT *stop_position)
{
FIXME("%p, %s, %p, %s, %p.\n", iface, debugstr_guid(start_format), start_position,
debugstr_guid(stop_format), stop_position);
return E_NOTIMPL;
}
static HRESULT WINAPI media_item_HasVideo(IMFPMediaItem *iface, BOOL *has_video, BOOL *selected)
{
FIXME("%p, %p, %p.\n", iface, has_video, selected);
return E_NOTIMPL;
}
static HRESULT WINAPI media_item_HasAudio(IMFPMediaItem *iface, BOOL *has_audio, BOOL *selected)
{
FIXME("%p, %p, %p.\n", iface, has_audio, selected);
return E_NOTIMPL;
}
static HRESULT WINAPI media_item_IsProtected(IMFPMediaItem *iface, BOOL *protected)
{
struct media_item *item = impl_from_IMFPMediaItem(iface);
TRACE("%p, %p.\n", iface, protected);
*protected = MFRequireProtectedEnvironment(item->pd) == S_OK;
return S_OK;
}
static HRESULT WINAPI media_item_GetDuration(IMFPMediaItem *iface, REFGUID format, PROPVARIANT *value)
{
struct media_item *item = impl_from_IMFPMediaItem(iface);
TRACE("%p, %s, %p.\n", iface, debugstr_guid(format), value);
return IMFPresentationDescriptor_GetItem(item->pd, &MF_PD_DURATION, value);
}
static HRESULT WINAPI media_item_GetNumberOfStreams(IMFPMediaItem *iface, DWORD *count)
{
struct media_item *item = impl_from_IMFPMediaItem(iface);
TRACE("%p, %p.\n", iface, count);
return IMFPresentationDescriptor_GetStreamDescriptorCount(item->pd, count);
}
static HRESULT WINAPI media_item_GetStreamSelection(IMFPMediaItem *iface, DWORD index, BOOL *selected)
{
struct media_item *item = impl_from_IMFPMediaItem(iface);
IMFStreamDescriptor *sd;
HRESULT hr;
TRACE("%p, %u, %p.\n", iface, index, selected);
if (SUCCEEDED(hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(item->pd, index, selected, &sd)))
IMFStreamDescriptor_Release(sd);
return hr;
}
static HRESULT WINAPI media_item_SetStreamSelection(IMFPMediaItem *iface, DWORD index, BOOL select)
{
struct media_item *item = impl_from_IMFPMediaItem(iface);
TRACE("%p, %u, %d.\n", iface, index, select);
return select ? IMFPresentationDescriptor_SelectStream(item->pd, index) :
IMFPresentationDescriptor_DeselectStream(item->pd, index);
}
static HRESULT WINAPI media_item_GetStreamAttribute(IMFPMediaItem *iface, DWORD index, REFGUID key,
PROPVARIANT *value)
{
struct media_item *item = impl_from_IMFPMediaItem(iface);
IMFStreamDescriptor *sd;
BOOL selected;
HRESULT hr;
TRACE("%p, %u, %s, %p.\n", iface, index, debugstr_guid(key), value);
if (SUCCEEDED(hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(item->pd, index, &selected, &sd)))
{
hr = IMFStreamDescriptor_GetItem(sd, key, value);
IMFStreamDescriptor_Release(sd);
}
return hr;
}
static HRESULT WINAPI media_item_GetPresentationAttribute(IMFPMediaItem *iface, REFGUID key,
PROPVARIANT *value)
{
struct media_item *item = impl_from_IMFPMediaItem(iface);
TRACE("%p, %s, %p.\n", iface, debugstr_guid(key), value);
return IMFPresentationDescriptor_GetItem(item->pd, key, value);
}
static HRESULT WINAPI media_item_GetCharacteristics(IMFPMediaItem *iface, MFP_MEDIAITEM_CHARACTERISTICS *flags)
{
struct media_item *item = impl_from_IMFPMediaItem(iface);
HRESULT hr;
TRACE("%p, %p.\n", iface, flags);
*flags = 0;
if (SUCCEEDED(hr = IMFMediaSource_GetCharacteristics(item->source, flags)))
{
*flags &= (MFP_MEDIAITEM_IS_LIVE | MFP_MEDIAITEM_CAN_SEEK |
MFP_MEDIAITEM_CAN_PAUSE | MFP_MEDIAITEM_HAS_SLOW_SEEK);
}
return hr;
}
static HRESULT WINAPI media_item_SetStreamSink(IMFPMediaItem *iface, DWORD index, IUnknown *sink)
{
FIXME("%p, %u, %p.\n", iface, index, sink);
return E_NOTIMPL;
}
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 HRESULT create_media_item(IMFPMediaPlayer *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);
*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 %#x.\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 %u.\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 %u.\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)
{
FIXME("%p, %s, %p.\n", iface, debugstr_guid(postype), position);
return E_NOTIMPL;
}
static HRESULT WINAPI media_player_GetDuration(IMFPMediaPlayer *iface, REFGUID postype, PROPVARIANT *position)
{
FIXME("%p, %s, %p.\n", iface, debugstr_guid(postype), position);
return E_NOTIMPL;
}
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 WINAPI media_player_CreateMediaItemFromURL(IMFPMediaPlayer *iface,
const WCHAR *url, BOOL sync, DWORD_PTR user_data, IMFPMediaItem **ret)
{
struct media_player *player = impl_from_IMFPMediaPlayer(iface);
struct media_item *item;
MF_OBJECT_TYPE obj_type;
IUnknown *object;
HRESULT hr;
TRACE("%p, %s, %d, %lx, %p.\n", iface, debugstr_w(url), sync, user_data, ret);
if (FAILED(hr = create_media_item(iface, 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,
player->propstore, &obj_type, &object)))
{
hr = media_item_set_source(item, object);
IUnknown_Release(object);
}
if (SUCCEEDED(hr))
*ret = &item->IMFPMediaItem_iface;
return hr;
}
else
{
hr = IMFSourceResolver_BeginCreateObjectFromURL(player->resolver, url, MF_RESOLUTION_MEDIASOURCE,
player->propstore, NULL, &player->resolver_callback, (IUnknown *)&item->IMFPMediaItem_iface);
IMFPMediaItem_Release(&item->IMFPMediaItem_iface);
}
return hr;
}
static HRESULT WINAPI media_player_CreateMediaItemFromObject(IMFPMediaPlayer *iface,
IUnknown *object, BOOL sync, DWORD_PTR user_data, IMFPMediaItem **item)
{
FIXME("%p, %p, %d, %lx, %p.\n", iface, object, sync, user_data, item);
return E_NOTIMPL;
}
static HRESULT WINAPI media_player_SetMediaItem(IMFPMediaPlayer *iface, IMFPMediaItem *item)
{
FIXME("%p, %p.\n", iface, item);
return E_NOTIMPL;
}
static HRESULT WINAPI media_player_ClearMediaItem(IMFPMediaPlayer *iface)
{
FIXME("%p.\n", iface);
return E_NOTIMPL;
}
static HRESULT WINAPI media_player_GetMediaItem(IMFPMediaPlayer *iface, IMFPMediaItem **item)
{
FIXME("%p, %p.\n", iface, item);
return E_NOTIMPL;
}
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 WINAPI media_player_GetNativeVideoSize(IMFPMediaPlayer *iface,
SIZE *video, SIZE *arvideo)
{
FIXME("%p, %p, %p.\n", iface, video, arvideo);
return E_NOTIMPL;
}
static HRESULT WINAPI media_player_GetIdealVideoSize(IMFPMediaPlayer *iface,
SIZE *min_size, SIZE *max_size)
{
FIXME("%p, %p, %p.\n", iface, min_size, max_size);
return E_NOTIMPL;
}
static HRESULT WINAPI media_player_SetVideoSourceRect(IMFPMediaPlayer *iface,
MFVideoNormalizedRect const *rect)
{
FIXME("%p, %p.\n", iface, rect);
return E_NOTIMPL;
}
static HRESULT WINAPI media_player_GetVideoSourceRect(IMFPMediaPlayer *iface,
MFVideoNormalizedRect *rect)
{
FIXME("%p, %p.\n", iface, rect);
return E_NOTIMPL;
}
static HRESULT WINAPI media_player_SetAspectRatioMode(IMFPMediaPlayer *iface, DWORD mode)
{
FIXME("%p, %u.\n", iface, mode);
return E_NOTIMPL;
}
static HRESULT WINAPI media_player_GetAspectRatioMode(IMFPMediaPlayer *iface,
DWORD *mode)
{
FIXME("%p, %p.\n", iface, mode);
return E_NOTIMPL;
}
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)
{
FIXME("%p.\n", iface);
return E_NOTIMPL;
}
static HRESULT WINAPI media_player_SetBorderColor(IMFPMediaPlayer *iface, COLORREF color)
{
FIXME("%p, %#x.\n", iface, color);
return E_NOTIMPL;
}
static HRESULT WINAPI media_player_GetBorderColor(IMFPMediaPlayer *iface, COLORREF *color)
{
FIXME("%p, %p.\n", iface, color);
return E_NOTIMPL;
}
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);
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, %u, %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 (SUCCEEDED(hr = IMFSourceResolver_EndCreateObjectFromURL(player->resolver, result, &obj_type, &object)))
{
hr = media_item_set_source(item, object);
IUnknown_Release(object);
}
if (FAILED(hr))
WARN("Failed to set media source, hr %#x.\n", hr);
if (FAILED(media_event_create(player, MFP_EVENT_TYPE_MEDIAITEM_CREATED, hr, &event)))
{
WARN("Failed to create event object.\n");
IUnknown_Release(state);
return S_OK;
}
if (SUCCEEDED(hr))
{
event->u.item_created.pMediaItem = &item->IMFPMediaItem_iface;
IMFPMediaItem_AddRef(event->u.item_created.pMediaItem);
}
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 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;
MFP_EVENT_TYPE event_type;
HRESULT hr, event_status;
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)
{
event_type = MFP_EVENT_TYPE_PLAY;
state = MFP_MEDIAPLAYER_STATE_PLAYING;
}
else if (session_event_type == MESessionStopped)
{
event_type = MFP_EVENT_TYPE_STOP;
state = MFP_MEDIAPLAYER_STATE_STOPPED;
}
else
{
event_type = MFP_EVENT_TYPE_PAUSE;
state = MFP_MEDIAPLAYER_STATE_PAUSED;
}
EnterCriticalSection(&player->cs);
media_player_set_state(player, state);
media_event_create(player, event_type, event_status, &event);
LeaveCriticalSection(&player->cs);
/* FIXME: set pMediaItem */
media_player_queue_event(player, event);
IUnknown_Release(&event->IUnknown_iface);
break;
default:
;
}
if (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,
};
HRESULT WINAPI MFPCreateMediaPlayer(const WCHAR *url, BOOL start_playback, MFP_CREATION_OPTIONS options,
IMFPMediaPlayerCallback *callback, HWND window, IMFPMediaPlayer **player)
{
struct media_player *object;
HRESULT hr;
TRACE("%s, %d, %#x, %p, %p, %p.\n", debugstr_w(url), start_playback, options, callback, window, player);
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);
}
*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;
}