Signed-off-by: Derek Lesho dlesho@codeweavers.com --- v2: Fix tests on environments without gstreamer. --- dlls/mfplat/tests/mfplat.c | 14 +- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_private.h | 6 + dlls/winegstreamer/media_source.c | 680 +++++++++++++++++++ dlls/winegstreamer/mfplat.c | 9 + dlls/winegstreamer/winegstreamer.rgs | 32 + dlls/winegstreamer/winegstreamer_classes.idl | 7 + include/mfidl.idl | 1 + 8 files changed, 746 insertions(+), 4 deletions(-) create mode 100644 dlls/winegstreamer/media_source.c
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 01749dd9ef8..29bcef7e46b 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -441,8 +441,8 @@ static void test_source_resolver(void) { struct test_callback callback = { { &test_create_from_url_callback_vtbl } }; struct test_callback callback2 = { { &test_create_from_file_handler_callback_vtbl } }; + IMFPresentationDescriptor *descriptor = NULL; IMFSourceResolver *resolver, *resolver2; - IMFPresentationDescriptor *descriptor; IMFSchemeHandler *scheme_handler; IMFMediaStream *video_stream; IMFAttributes *attributes; @@ -458,9 +458,12 @@ static void test_source_resolver(void) int i, sample_count; WCHAR *filename; PROPVARIANT var; + BOOL is_wine; HRESULT hr; GUID guid;
+ is_wine = !strcmp(winetest_platform, "wine"); + if (!pMFCreateSourceResolver) { win_skip("MFCreateSourceResolver() not found\n"); @@ -529,7 +532,10 @@ static void test_source_resolver(void) ok(obj_type == MF_OBJECT_MEDIASOURCE, "got %d\n", obj_type);
hr = IMFMediaSource_CreatePresentationDescriptor(mediasource, &descriptor); +if (!is_wine) ok(hr == S_OK, "Failed to get presentation descriptor, hr %#x.\n", hr); + if (FAILED(hr)) + goto skip_source_tests; ok(descriptor != NULL, "got %p\n", descriptor);
hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(descriptor, 0, &selected, &sd); @@ -540,7 +546,7 @@ static void test_source_resolver(void) IMFStreamDescriptor_Release(sd);
hr = IMFMediaTypeHandler_GetMajorType(handler, &guid); -todo_wine +if (!is_wine) ok(hr == S_OK, "Failed to get stream major type, hr %#x.\n", hr); if (FAILED(hr)) goto skip_source_tests; @@ -632,8 +638,8 @@ todo_wine ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
skip_source_tests: - - IMFPresentationDescriptor_Release(descriptor); + if (descriptor) + IMFPresentationDescriptor_Release(descriptor); IMFMediaSource_Release(mediasource); IMFByteStream_Release(stream);
diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 337c1086e6b..e578d194f7f 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -10,6 +10,7 @@ C_SRCS = \ gst_cbs.c \ gstdemux.c \ main.c \ + media_source.c \ mediatype.c \ mfplat.c \ pin.c \ diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index e6fb841fc87..71ca4290885 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -54,4 +54,10 @@ void start_dispatch_thread(void) DECLSPEC_HIDDEN;
extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN;
+enum source_type +{ + SOURCE_TYPE_MPEG_4, +}; +HRESULT container_stream_handler_construct(REFIID riid, void **obj, enum source_type); + #endif /* __GST_PRIVATE_INCLUDED__ */ diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c new file mode 100644 index 00000000000..df28f54444d --- /dev/null +++ b/dlls/winegstreamer/media_source.c @@ -0,0 +1,680 @@ +#include "gst_private.h" + +#include <stdarg.h> + +#define COBJMACROS +#define NONAMELESSUNION + +#include "mfapi.h" +#include "mferror.h" +#include "mfidl.h" + +#include "wine/debug.h" +#include "wine/heap.h" +#include "wine/list.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +struct media_source +{ + IMFMediaSource IMFMediaSource_iface; + LONG ref; + IMFMediaEventQueue *event_queue; +}; + +static inline struct media_source *impl_from_IMFMediaSource(IMFMediaSource *iface) +{ + return CONTAINING_RECORD(iface, struct media_source, IMFMediaSource_iface); +} + +static HRESULT WINAPI media_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("(%p)->(%s %p)\n", source, debugstr_guid(riid), out); + + if (IsEqualIID(riid, &IID_IMFMediaSource) || + IsEqualIID(riid, &IID_IMFMediaEventGenerator) || + IsEqualIID(riid, &IID_IUnknown)) + { + *out = &source->IMFMediaSource_iface; + } + else + { + FIXME("(%s, %p)\n", debugstr_guid(riid), out); + *out = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*out); + return S_OK; +} + +static ULONG WINAPI media_source_AddRef(IMFMediaSource *iface) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + ULONG ref = InterlockedIncrement(&source->ref); + + TRACE("(%p) ref=%u\n", source, ref); + + return ref; +} + +static ULONG WINAPI media_source_Release(IMFMediaSource *iface) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + ULONG ref = InterlockedDecrement(&source->ref); + + TRACE("(%p) ref=%u\n", source, ref); + + if (!ref) + { + heap_free(source); + } + + return ref; +} + +static HRESULT WINAPI media_source_GetEvent(IMFMediaSource *iface, DWORD flags, IMFMediaEvent **event) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("(%p)->(%#x, %p)\n", source, flags, event); + + return IMFMediaEventQueue_GetEvent(source->event_queue, flags, event); +} + +static HRESULT WINAPI media_source_BeginGetEvent(IMFMediaSource *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("(%p)->(%p, %p)\n", source, callback, state); + + return IMFMediaEventQueue_BeginGetEvent(source->event_queue, callback, state); +} + +static HRESULT WINAPI media_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("(%p)->(%p, %p)\n", source, result, event); + + return IMFMediaEventQueue_EndGetEvent(source->event_queue, result, event); +} + +static HRESULT WINAPI media_source_QueueEvent(IMFMediaSource *iface, MediaEventType event_type, REFGUID ext_type, + HRESULT hr, const PROPVARIANT *value) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("(%p)->(%d, %s, %#x, %p)\n", source, event_type, debugstr_guid(ext_type), hr, value); + + return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI media_source_GetCharacteristics(IMFMediaSource *iface, DWORD *characteristics) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + FIXME("(%p)->(%p): stub\n", source, characteristics); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **descriptor) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + FIXME("(%p)->(%p): stub\n", source, descriptor); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *descriptor, + const GUID *time_format, const PROPVARIANT *start_position) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + FIXME("(%p)->(%p, %p, %p): stub\n", source, descriptor, time_format, start_position); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + FIXME("(%p): stub\n", source); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + FIXME("(%p): stub\n", source); + + return E_NOTIMPL; +} + +static HRESULT media_source_teardown(struct media_source *source) +{ + if (source->event_queue) + IMFMediaEventQueue_Release(source->event_queue); + + return S_OK; +} + +static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + FIXME("(%p): stub\n", source); + + return E_NOTIMPL; +} + +static const IMFMediaSourceVtbl IMFMediaSource_vtbl = +{ + media_source_QueryInterface, + media_source_AddRef, + media_source_Release, + media_source_GetEvent, + media_source_BeginGetEvent, + media_source_EndGetEvent, + media_source_QueueEvent, + media_source_GetCharacteristics, + media_source_CreatePresentationDescriptor, + media_source_Start, + media_source_Stop, + media_source_Pause, + media_source_Shutdown, +}; + +static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_type type, struct media_source **out_media_source) +{ + struct media_source *object = heap_alloc_zero(sizeof(*object)); + HRESULT hr; + + if (!object) + return E_OUTOFMEMORY; + + if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) + goto fail; + + object->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl; + object->ref = 1; + + *out_media_source = object; + return S_OK; + + fail: + WARN("Failed to construct MFMediaSource, hr %#x.\n", hr); + + media_source_teardown(object); + heap_free(object); + return hr; +} + +/* IMFByteStreamHandler */ + +struct container_stream_handler_result +{ + struct list entry; + IMFAsyncResult *result; + MF_OBJECT_TYPE obj_type; + IUnknown *object; +}; + +struct container_stream_handler +{ + IMFByteStreamHandler IMFByteStreamHandler_iface; + IMFAsyncCallback IMFAsyncCallback_iface; + LONG refcount; + enum source_type type; + struct list results; + CRITICAL_SECTION cs; +}; + +static struct container_stream_handler *impl_from_IMFByteStreamHandler(IMFByteStreamHandler *iface) +{ + return CONTAINING_RECORD(iface, struct container_stream_handler, IMFByteStreamHandler_iface); +} + +static struct container_stream_handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct container_stream_handler, IMFAsyncCallback_iface); +} + +static HRESULT WINAPI container_stream_handler_QueryInterface(IMFByteStreamHandler *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFByteStreamHandler) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFByteStreamHandler_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI container_stream_handler_AddRef(IMFByteStreamHandler *iface) +{ + struct container_stream_handler *handler = impl_from_IMFByteStreamHandler(iface); + ULONG refcount = InterlockedIncrement(&handler->refcount); + + TRACE("%p, refcount %u.\n", handler, refcount); + + return refcount; +} + +static ULONG WINAPI container_stream_handler_Release(IMFByteStreamHandler *iface) +{ + struct container_stream_handler *handler = impl_from_IMFByteStreamHandler(iface); + ULONG refcount = InterlockedDecrement(&handler->refcount); + struct container_stream_handler_result *result, *next; + + TRACE("%p, refcount %u.\n", iface, refcount); + + if (!refcount) + { + LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct container_stream_handler_result, entry) + { + list_remove(&result->entry); + IMFAsyncResult_Release(result->result); + if (result->object) + IUnknown_Release(result->object); + heap_free(result); + } + DeleteCriticalSection(&handler->cs); + heap_free(handler); + } + + return refcount; +} + +struct create_object_context +{ + IUnknown IUnknown_iface; + LONG refcount; + + IPropertyStore *props; + IMFByteStream *stream; + WCHAR *url; + DWORD flags; +}; + +static struct create_object_context *impl_from_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct create_object_context, IUnknown_iface); +} + +static HRESULT WINAPI create_object_context_QueryInterface(IUnknown *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IUnknown_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI create_object_context_AddRef(IUnknown *iface) +{ + struct create_object_context *context = impl_from_IUnknown(iface); + ULONG refcount = InterlockedIncrement(&context->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI create_object_context_Release(IUnknown *iface) +{ + struct create_object_context *context = impl_from_IUnknown(iface); + ULONG refcount = InterlockedDecrement(&context->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + if (!refcount) + { + if (context->props) + IPropertyStore_Release(context->props); + if (context->stream) + IMFByteStream_Release(context->stream); + if (context->url) + heap_free(context->url); + heap_free(context); + } + + return refcount; +} + +static const IUnknownVtbl create_object_context_vtbl = +{ + create_object_context_QueryInterface, + create_object_context_AddRef, + create_object_context_Release, +}; + +static WCHAR *heap_strdupW(const WCHAR *str) +{ + WCHAR *ret = NULL; + + if (str) + { + unsigned int size; + + size = (lstrlenW(str) + 1) * sizeof(WCHAR); + ret = heap_alloc(size); + if (ret) + memcpy(ret, str, size); + } + + return ret; +} + +static HRESULT WINAPI container_stream_handler_BeginCreateObject(IMFByteStreamHandler *iface, IMFByteStream *stream, const WCHAR *url, DWORD flags, + IPropertyStore *props, IUnknown **cancel_cookie, IMFAsyncCallback *callback, IUnknown *state) +{ + struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface); + struct create_object_context *context; + IMFAsyncResult *caller, *item; + HRESULT hr; + + TRACE("%p, %s, %#x, %p, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, cancel_cookie, callback, state); + + if (cancel_cookie) + *cancel_cookie = NULL; + + if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &caller))) + return hr; + + context = heap_alloc(sizeof(*context)); + if (!context) + { + IMFAsyncResult_Release(caller); + return E_OUTOFMEMORY; + } + + context->IUnknown_iface.lpVtbl = &create_object_context_vtbl; + context->refcount = 1; + context->props = props; + if (context->props) + IPropertyStore_AddRef(context->props); + context->flags = flags; + context->stream = stream; + if (context->stream) + IMFByteStream_AddRef(context->stream); + if (url) + context->url = heap_strdupW(url); + if (!context->stream) + { + IMFAsyncResult_Release(caller); + IUnknown_Release(&context->IUnknown_iface); + return E_OUTOFMEMORY; + } + + hr = MFCreateAsyncResult(&context->IUnknown_iface, &this->IMFAsyncCallback_iface, (IUnknown *)caller, &item); + IUnknown_Release(&context->IUnknown_iface); + if (SUCCEEDED(hr)) + { + if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item))) + { + if (cancel_cookie) + { + *cancel_cookie = (IUnknown *)caller; + IUnknown_AddRef(*cancel_cookie); + } + } + + IMFAsyncResult_Release(item); + } + IMFAsyncResult_Release(caller); + + return hr; +} + +static HRESULT WINAPI container_stream_handler_EndCreateObject(IMFByteStreamHandler *iface, IMFAsyncResult *result, + MF_OBJECT_TYPE *obj_type, IUnknown **object) +{ + struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface); + struct container_stream_handler_result *found = NULL, *cur; + HRESULT hr; + + TRACE("%p, %p, %p, %p.\n", iface, result, obj_type, object); + + EnterCriticalSection(&this->cs); + + LIST_FOR_EACH_ENTRY(cur, &this->results, struct container_stream_handler_result, entry) + { + if (result == cur->result) + { + list_remove(&cur->entry); + found = cur; + break; + } + } + + LeaveCriticalSection(&this->cs); + + if (found) + { + *obj_type = found->obj_type; + *object = found->object; + hr = IMFAsyncResult_GetStatus(found->result); + IMFAsyncResult_Release(found->result); + heap_free(found); + } + else + { + *obj_type = MF_OBJECT_INVALID; + *object = NULL; + hr = MF_E_UNEXPECTED; + } + + return hr; +} + +static HRESULT WINAPI container_stream_handler_CancelObjectCreation(IMFByteStreamHandler *iface, IUnknown *cancel_cookie) +{ + struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface); + struct container_stream_handler_result *found = NULL, *cur; + + TRACE("%p, %p.\n", iface, cancel_cookie); + + EnterCriticalSection(&this->cs); + + LIST_FOR_EACH_ENTRY(cur, &this->results, struct container_stream_handler_result, entry) + { + if (cancel_cookie == (IUnknown *)cur->result) + { + list_remove(&cur->entry); + found = cur; + break; + } + } + + LeaveCriticalSection(&this->cs); + + if (found) + { + IMFAsyncResult_Release(found->result); + if (found->object) + IUnknown_Release(found->object); + heap_free(found); + } + + return found ? S_OK : MF_E_UNEXPECTED; +} + +static HRESULT WINAPI container_stream_handler_GetMaxNumberOfBytesRequiredForResolution(IMFByteStreamHandler *iface, QWORD *bytes) +{ + FIXME("stub (%p %p)\n", iface, bytes); + return E_NOTIMPL; +} + +static const IMFByteStreamHandlerVtbl container_stream_handler_vtbl = +{ + container_stream_handler_QueryInterface, + container_stream_handler_AddRef, + container_stream_handler_Release, + container_stream_handler_BeginCreateObject, + container_stream_handler_EndCreateObject, + container_stream_handler_CancelObjectCreation, + container_stream_handler_GetMaxNumberOfBytesRequiredForResolution, +}; + +static HRESULT WINAPI container_stream_handler_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; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI container_stream_handler_callback_AddRef(IMFAsyncCallback *iface) +{ + struct container_stream_handler *handler = impl_from_IMFAsyncCallback(iface); + return IMFByteStreamHandler_AddRef(&handler->IMFByteStreamHandler_iface); +} + +static ULONG WINAPI container_stream_handler_callback_Release(IMFAsyncCallback *iface) +{ + struct container_stream_handler *handler = impl_from_IMFAsyncCallback(iface); + return IMFByteStreamHandler_Release(&handler->IMFByteStreamHandler_iface); +} + +static HRESULT WINAPI container_stream_handler_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static HRESULT container_stream_handler_create_object(struct container_stream_handler *This, WCHAR *url, IMFByteStream *stream, DWORD flags, + IPropertyStore *props, IUnknown **out_object, MF_OBJECT_TYPE *out_obj_type) +{ + TRACE("(%p %s %p %u %p %p %p)\n", This, debugstr_w(url), stream, flags, props, out_object, out_obj_type); + + if (flags & MF_RESOLUTION_MEDIASOURCE) + { + HRESULT hr; + struct media_source *new_source; + + if (FAILED(hr = media_source_constructor(stream, This->type, &new_source))) + return hr; + + TRACE("->(%p)\n", new_source); + + *out_object = (IUnknown*)&new_source->IMFMediaSource_iface; + *out_obj_type = MF_OBJECT_MEDIASOURCE; + + return S_OK; + } + else + { + FIXME("flags = %08x\n", flags); + return E_NOTIMPL; + } +} + +static HRESULT WINAPI container_stream_handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct container_stream_handler *handler = impl_from_IMFAsyncCallback(iface); + struct container_stream_handler_result *handler_result; + MF_OBJECT_TYPE obj_type = MF_OBJECT_INVALID; + IUnknown *object = NULL, *context_object; + struct create_object_context *context; + IMFAsyncResult *caller; + HRESULT hr; + + caller = (IMFAsyncResult *)IMFAsyncResult_GetStateNoAddRef(result); + + if (FAILED(hr = IMFAsyncResult_GetObject(result, &context_object))) + { + WARN("Expected context set for callee result.\n"); + return hr; + } + + context = impl_from_IUnknown(context_object); + + hr = container_stream_handler_create_object(handler, context->url, context->stream, context->flags, context->props, &object, &obj_type); + + handler_result = heap_alloc(sizeof(*handler_result)); + if (handler_result) + { + handler_result->result = caller; + IMFAsyncResult_AddRef(handler_result->result); + handler_result->obj_type = obj_type; + handler_result->object = object; + + EnterCriticalSection(&handler->cs); + list_add_tail(&handler->results, &handler_result->entry); + LeaveCriticalSection(&handler->cs); + } + else + { + if (object) + IUnknown_Release(object); + hr = E_OUTOFMEMORY; + } + + IUnknown_Release(&context->IUnknown_iface); + + IMFAsyncResult_SetStatus(caller, hr); + MFInvokeCallback(caller); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl container_stream_handler_callback_vtbl = +{ + container_stream_handler_callback_QueryInterface, + container_stream_handler_callback_AddRef, + container_stream_handler_callback_Release, + container_stream_handler_callback_GetParameters, + container_stream_handler_callback_Invoke, +}; + +HRESULT container_stream_handler_construct(REFIID riid, void **obj, enum source_type type) +{ + struct container_stream_handler *this; + HRESULT hr; + + TRACE("%s, %p.\n", debugstr_guid(riid), obj); + + this = heap_alloc_zero(sizeof(*this)); + if (!this) + return E_OUTOFMEMORY; + + list_init(&this->results); + InitializeCriticalSection(&this->cs); + + this->type = type; + this->IMFByteStreamHandler_iface.lpVtbl = &container_stream_handler_vtbl; + this->IMFAsyncCallback_iface.lpVtbl = &container_stream_handler_callback_vtbl; + this->refcount = 1; + + hr = IMFByteStreamHandler_QueryInterface(&this->IMFByteStreamHandler_iface, riid, obj); + IMFByteStreamHandler_Release(&this->IMFByteStreamHandler_iface); + + return hr; +} \ No newline at end of file diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 55b9b088765..16e55247de1 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -398,6 +398,11 @@ failed: return hr; }
+static HRESULT mp4_stream_handler_create(REFIID riid, void **ret) +{ + return container_stream_handler_construct(riid, ret, SOURCE_TYPE_MPEG_4); +} + static const struct class_object { const GUID *clsid; @@ -406,6 +411,7 @@ static const struct class_object class_objects[] = { { &CLSID_VideoProcessorMFT, &video_processor_create }, + { &CLSID_MPEG4ByteStreamHandler, &mp4_stream_handler_create }, };
HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) @@ -414,6 +420,9 @@ HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) unsigned int i; HRESULT hr;
+ if (!(init_gstreamer())) + return CLASS_E_CLASSNOTAVAILABLE; + for (i = 0; i < ARRAY_SIZE(class_objects); ++i) { if (IsEqualGUID(class_objects[i].clsid, rclsid)) diff --git a/dlls/winegstreamer/winegstreamer.rgs b/dlls/winegstreamer/winegstreamer.rgs index 923ba673f8c..0bc26761e9d 100644 --- a/dlls/winegstreamer/winegstreamer.rgs +++ b/dlls/winegstreamer/winegstreamer.rgs @@ -12,3 +12,35 @@ HKCR } } } + +HKLM +{ + NoRemove 'Software' + { + NoRemove 'Microsoft' + { + NoRemove 'Windows Media Foundation' + { + NoRemove 'ByteStreamHandlers' + { + '.m4v' + { + val '{271C3902-6095-4c45-A22F-20091816EE9E}' = s 'MPEG4 Byte Stream Handler' + } + '.mp4' + { + val '{271C3902-6095-4c45-A22F-20091816EE9E}' = s 'MPEG4 Byte Stream Handler' + } + 'video/m4v' + { + val '{271C3902-6095-4c45-A22F-20091816EE9E}' = s 'MPEG4 Byte Stream Handler' + } + 'video/mp4' + { + val '{271C3902-6095-4c45-A22F-20091816EE9E}' = s 'MPEG4 Byte Stream Handler' + } + } + } + } + } +} \ No newline at end of file diff --git a/dlls/winegstreamer/winegstreamer_classes.idl b/dlls/winegstreamer/winegstreamer_classes.idl index fa0e1784057..997a28b052f 100644 --- a/dlls/winegstreamer/winegstreamer_classes.idl +++ b/dlls/winegstreamer/winegstreamer_classes.idl @@ -54,3 +54,10 @@ coclass Gstreamer_Splitter {} uuid(88753b26-5b24-49bd-b2e7-0c445c78c982) ] coclass VideoProcessorMFT {} + +[ + helpstring("MP4 Byte Stream Handler"), + threading(both), + uuid(271c3902-6095-4c45-a22f-20091816ee9e) +] +coclass MPEG4ByteStreamHandler {} diff --git a/include/mfidl.idl b/include/mfidl.idl index 4ceeb707bd0..fc13dfba7a1 100644 --- a/include/mfidl.idl +++ b/include/mfidl.idl @@ -1241,3 +1241,4 @@ cpp_quote("EXTERN_GUID(MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_ACTIVATE, 0xba491365, cpp_quote("EXTERN_GUID(MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_FLAGS, 0xba491366, 0xbe50, 0x451e, 0x95, 0xab, 0x6d, 0x4a, 0xcc, 0xc7, 0xda, 0xd8);")
cpp_quote("EXTERN_GUID(CLSID_VideoProcessorMFT, 0x88753b26, 0x5b24, 0x49bd, 0xb2, 0xe7, 0xc, 0x44, 0x5c, 0x78, 0xc9, 0x82);") +cpp_quote("EXTERN_GUID(CLSID_MPEG4ByteStreamHandler, 0x271c3902, 0x6095, 0x4c45, 0xa2, 0x2f, 0x20, 0x09, 0x18, 0x16, 0xee, 0x9e);") \ No newline at end of file
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/mfplat/tests/mfplat.c | 5 ++-- dlls/winegstreamer/media_source.c | 42 ++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 5 deletions(-)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 29bcef7e46b..69a9a4285bd 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -549,7 +549,7 @@ if (!is_wine) if (!is_wine) ok(hr == S_OK, "Failed to get stream major type, hr %#x.\n", hr); if (FAILED(hr)) - goto skip_source_tests; + goto skip_tests_for_stub;
/* Check major/minor type for the test media. */ ok(IsEqualGUID(&guid, &MFMediaType_Video), "Unexpected major type %s.\n", debugstr_guid(&guid)); @@ -631,13 +631,14 @@ if (!is_wine)
IMFMediaTypeHandler_Release(handler);
+skip_source_tests: hr = IMFMediaSource_Shutdown(mediasource); ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
hr = IMFMediaSource_CreatePresentationDescriptor(mediasource, NULL); ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
-skip_source_tests: +skip_tests_for_stub: if (descriptor) IMFPresentationDescriptor_Release(descriptor); IMFMediaSource_Release(mediasource); diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index df28f54444d..d2577b41046 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -20,6 +20,12 @@ struct media_source IMFMediaSource IMFMediaSource_iface; LONG ref; IMFMediaEventQueue *event_queue; + enum + { + SOURCE_OPENING, + SOURCE_STOPPED, + SOURCE_SHUTDOWN, + } state; };
static inline struct media_source *impl_from_IMFMediaSource(IMFMediaSource *iface) @@ -81,6 +87,9 @@ static HRESULT WINAPI media_source_GetEvent(IMFMediaSource *iface, DWORD flags,
TRACE("(%p)->(%#x, %p)\n", source, flags, event);
+ if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return IMFMediaEventQueue_GetEvent(source->event_queue, flags, event); }
@@ -90,6 +99,9 @@ static HRESULT WINAPI media_source_BeginGetEvent(IMFMediaSource *iface, IMFAsync
TRACE("(%p)->(%p, %p)\n", source, callback, state);
+ if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return IMFMediaEventQueue_BeginGetEvent(source->event_queue, callback, state); }
@@ -99,6 +111,9 @@ static HRESULT WINAPI media_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncRe
TRACE("(%p)->(%p, %p)\n", source, result, event);
+ if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return IMFMediaEventQueue_EndGetEvent(source->event_queue, result, event); }
@@ -109,6 +124,9 @@ static HRESULT WINAPI media_source_QueueEvent(IMFMediaSource *iface, MediaEventT
TRACE("(%p)->(%d, %s, %#x, %p)\n", source, event_type, debugstr_guid(ext_type), hr, value);
+ if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, ext_type, hr, value); }
@@ -118,6 +136,9 @@ static HRESULT WINAPI media_source_GetCharacteristics(IMFMediaSource *iface, DWO
FIXME("(%p)->(%p): stub\n", source, characteristics);
+ if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return E_NOTIMPL; }
@@ -127,6 +148,9 @@ static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource *
FIXME("(%p)->(%p): stub\n", source, descriptor);
+ if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return E_NOTIMPL; }
@@ -137,6 +161,9 @@ static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationD
FIXME("(%p)->(%p, %p, %p): stub\n", source, descriptor, time_format, start_position);
+ if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return E_NOTIMPL; }
@@ -146,6 +173,9 @@ static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface)
FIXME("(%p): stub\n", source);
+ if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return E_NOTIMPL; }
@@ -155,6 +185,9 @@ static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface)
FIXME("(%p): stub\n", source);
+ if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return E_NOTIMPL; }
@@ -170,9 +203,10 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) { struct media_source *source = impl_from_IMFMediaSource(iface);
- FIXME("(%p): stub\n", source); + TRACE("(%p)\n", source);
- return E_NOTIMPL; + source->state = SOURCE_SHUTDOWN; + return media_source_teardown(source); }
static const IMFMediaSourceVtbl IMFMediaSource_vtbl = @@ -203,6 +237,8 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) goto fail;
+ object->state = SOURCE_STOPPED; + object->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl; object->ref = 1;
@@ -677,4 +713,4 @@ HRESULT container_stream_handler_construct(REFIID riid, void **obj, enum source_ IMFByteStreamHandler_Release(&this->IMFByteStreamHandler_iface);
return hr; -} \ No newline at end of file +}
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=77638
Your paranoid android.
=== debiant (32 bit French report) ===
mfplat: Unhandled exception: page fault on execute access to 0x0a2e7823 in 32-bit code (0x0a2e7823).
Report validation errors: mfplat:mfplat crashed (c0000005)
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/gst_cbs.c | 58 ++++++++ dlls/winegstreamer/gst_cbs.h | 12 +- dlls/winegstreamer/media_source.c | 232 +++++++++++++++++++++++++++++- 3 files changed, 298 insertions(+), 4 deletions(-)
diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c index bf7103b1606..dfe33dd6277 100644 --- a/dlls/winegstreamer/gst_cbs.c +++ b/dlls/winegstreamer/gst_cbs.c @@ -49,6 +49,8 @@ static void CALLBACK perform_cb(TP_CALLBACK_INSTANCE *instance, void *user)
if (cbdata->type < GSTDEMUX_MAX) perform_cb_gstdemux(cbdata); + else if (cbdata->type < MEDIA_SOURCE_MAX) + perform_cb_media_source(cbdata);
pthread_mutex_lock(&cbdata->lock); cbdata->finished = 1; @@ -301,3 +303,59 @@ gboolean query_sink_wrapper(GstPad *pad, GstObject *parent, GstQuery *query)
return cbdata.u.query_sink_data.ret; } + +GstFlowReturn pull_from_bytestream_wrapper(GstPad *pad, GstObject *parent, guint64 ofs, guint len, + GstBuffer **buf) +{ + struct cb_data cbdata = { PULL_FROM_BYTESTREAM }; + + cbdata.u.getrange_data.pad = pad; + cbdata.u.getrange_data.parent = parent; + cbdata.u.getrange_data.ofs = ofs; + cbdata.u.getrange_data.len = len; + cbdata.u.getrange_data.buf = buf; + + call_cb(&cbdata); + + return cbdata.u.getrange_data.ret; +} + +gboolean query_bytestream_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) +{ + struct cb_data cbdata = { QUERY_BYTESTREAM }; + + cbdata.u.query_function_data.pad = pad; + cbdata.u.query_function_data.parent = parent; + cbdata.u.query_function_data.query = query; + + call_cb(&cbdata); + + return cbdata.u.query_function_data.ret; +} + +gboolean activate_bytestream_pad_mode_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) +{ + struct cb_data cbdata = { ACTIVATE_BYTESTREAM_PAD_MODE }; + + cbdata.u.activate_mode_data.pad = pad; + cbdata.u.activate_mode_data.parent = parent; + cbdata.u.activate_mode_data.mode = mode; + cbdata.u.activate_mode_data.activate = activate; + + call_cb(&cbdata); + + return cbdata.u.activate_mode_data.ret; +} + +gboolean process_bytestream_pad_event_wrapper(GstPad *pad, GstObject *parent, GstEvent *event) +{ + struct cb_data cbdata = { PROCESS_BYTESTREAM_PAD_EVENT }; + + cbdata.u.event_src_data.pad = pad; + cbdata.u.event_src_data.parent = parent; + cbdata.u.event_src_data.event = event; + + call_cb(&cbdata); + + return cbdata.u.event_src_data.ret; +} \ No newline at end of file diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h index 4725f23ad1a..10e999feea7 100644 --- a/dlls/winegstreamer/gst_cbs.h +++ b/dlls/winegstreamer/gst_cbs.h @@ -43,7 +43,12 @@ enum CB_TYPE { AUTOPLUG_BLACKLIST, UNKNOWN_TYPE, QUERY_SINK, - GSTDEMUX_MAX + GSTDEMUX_MAX, + PULL_FROM_BYTESTREAM, + QUERY_BYTESTREAM, + ACTIVATE_BYTESTREAM_PAD_MODE, + PROCESS_BYTESTREAM_PAD_EVENT, + MEDIA_SOURCE_MAX, };
struct cb_data { @@ -138,6 +143,7 @@ struct cb_data {
void mark_wine_thread(void) DECLSPEC_HIDDEN; void perform_cb_gstdemux(struct cb_data *data) DECLSPEC_HIDDEN; +void perform_cb_media_source(struct cb_data *data) DECLSPEC_HIDDEN;
GstBusSyncReply watch_bus_wrapper(GstBus *bus, GstMessage *msg, gpointer user) DECLSPEC_HIDDEN; void existing_new_pad_wrapper(GstElement *bin, GstPad *pad, gpointer user) DECLSPEC_HIDDEN; @@ -154,5 +160,9 @@ GstAutoplugSelectResult autoplug_blacklist_wrapper(GstElement *bin, GstPad *pad, void unknown_type_wrapper(GstElement *bin, GstPad *pad, GstCaps *caps, gpointer user) DECLSPEC_HIDDEN; void Gstreamer_transform_pad_added_wrapper(GstElement *filter, GstPad *pad, gpointer user) DECLSPEC_HIDDEN; gboolean query_sink_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) DECLSPEC_HIDDEN; +GstFlowReturn pull_from_bytestream_wrapper(GstPad *pad, GstObject *parent, guint64 ofs, guint len, GstBuffer **buf) DECLSPEC_HIDDEN; +gboolean query_bytestream_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) DECLSPEC_HIDDEN; +gboolean activate_bytestream_pad_mode_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) DECLSPEC_HIDDEN; +gboolean process_bytestream_pad_event_wrapper(GstPad *pad, GstObject *parent, GstEvent *event) DECLSPEC_HIDDEN;
#endif diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index d2577b41046..ba3de001738 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -1,4 +1,9 @@ +#include "config.h" + +#include <gst/gst.h> + #include "gst_private.h" +#include "gst_cbs.h"
#include <stdarg.h>
@@ -8,6 +13,7 @@ #include "mfapi.h" #include "mferror.h" #include "mfidl.h" +#include "mfobjects.h"
#include "wine/debug.h" #include "wine/heap.h" @@ -15,11 +21,24 @@
WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
+static struct source_desc +{ + GstStaticCaps bytestream_caps; +} source_descs[] = +{ + {/*SOURCE_TYPE_MPEG_4*/ + GST_STATIC_CAPS("video/quicktime"), + } +}; + struct media_source { IMFMediaSource IMFMediaSource_iface; LONG ref; + enum source_type type; IMFMediaEventQueue *event_queue; + IMFByteStream *byte_stream; + GstPad *my_src; enum { SOURCE_OPENING, @@ -33,6 +52,154 @@ static inline struct media_source *impl_from_IMFMediaSource(IMFMediaSource *ifac return CONTAINING_RECORD(iface, struct media_source, IMFMediaSource_iface); }
+GstFlowReturn pull_from_bytestream(GstPad *pad, GstObject *parent, guint64 ofs, guint len, + GstBuffer **buf) +{ + struct media_source *source = gst_pad_get_element_private(pad); + IMFByteStream *byte_stream = source->byte_stream; + ULONG bytes_read; + GstMapInfo info; + BOOL is_eof; + HRESULT hr; + + TRACE("gstreamer requesting %u bytes at %s from source %p into buffer %p\n", len, wine_dbgstr_longlong(ofs), source, buf); + + if (ofs != GST_BUFFER_OFFSET_NONE) + { + if (FAILED(IMFByteStream_SetCurrentPosition(byte_stream, ofs))) + return GST_FLOW_ERROR; + } + + if (FAILED(IMFByteStream_IsEndOfStream(byte_stream, &is_eof))) + return GST_FLOW_ERROR; + if (is_eof) + return GST_FLOW_EOS; + + if (!(*buf)) + *buf = gst_buffer_new_and_alloc(len); + gst_buffer_map(*buf, &info, GST_MAP_WRITE); + hr = IMFByteStream_Read(byte_stream, info.data, len, &bytes_read); + gst_buffer_unmap(*buf, &info); + + gst_buffer_set_size(*buf, bytes_read); + + if (FAILED(hr)) + { + return GST_FLOW_ERROR; + } + GST_BUFFER_OFFSET(*buf) = ofs; + return GST_FLOW_OK; +} + +static gboolean query_bytestream(GstPad *pad, GstObject *parent, GstQuery *query) +{ + struct media_source *source = gst_pad_get_element_private(pad); + GstFormat format; + QWORD bytestream_len; + + TRACE("GStreamer queries source %p for %s\n", source, GST_QUERY_TYPE_NAME(query)); + + if (FAILED(IMFByteStream_GetLength(source->byte_stream, &bytestream_len))) + return FALSE; + + switch (GST_QUERY_TYPE(query)) + { + case GST_QUERY_DURATION: + { + gst_query_parse_duration (query, &format, NULL); + if (format == GST_FORMAT_PERCENT) { + gst_query_set_duration (query, GST_FORMAT_PERCENT, GST_FORMAT_PERCENT_MAX); + return TRUE; + } + else if (format == GST_FORMAT_BYTES) + { + QWORD length; + IMFByteStream_GetLength(source->byte_stream, &length); + gst_query_set_duration (query, GST_FORMAT_BYTES, length); + return TRUE; + } + return FALSE; + } + case GST_QUERY_SEEKING: + { + gst_query_parse_seeking (query, &format, NULL, NULL, NULL); + if (format != GST_FORMAT_BYTES) + { + WARN("Cannot seek using format "%s".\n", gst_format_get_name(format)); + return FALSE; + } + gst_query_set_seeking(query, GST_FORMAT_BYTES, 1, 0, bytestream_len); + return TRUE; + } + case GST_QUERY_SCHEDULING: + { + gst_query_set_scheduling(query, GST_SCHEDULING_FLAG_SEEKABLE, 1, -1, 0); + gst_query_add_scheduling_mode(query, GST_PAD_MODE_PULL); + return TRUE; + } + case GST_QUERY_CAPS: + { + GstCaps *caps, *filter; + + gst_query_parse_caps(query, &filter); + + caps = gst_static_caps_get(&source_descs[source->type].bytestream_caps); + + if (filter) { + GstCaps* filtered; + filtered = gst_caps_intersect_full( + filter, caps, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref(caps); + caps = filtered; + } + gst_query_set_caps_result(query, caps); + gst_caps_unref(caps); + return TRUE; + } + default: + { + WARN("Unhandled query type %s\n", GST_QUERY_TYPE_NAME(query)); + return FALSE; + } + } +} + +static gboolean activate_bytestream_pad_mode(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) +{ + struct media_source *source = gst_pad_get_element_private(pad); + + TRACE("%s source pad for mediasource %p in %s mode.\n", + activate ? "Activating" : "Deactivating", source, gst_pad_mode_get_name(mode)); + + switch (mode) { + case GST_PAD_MODE_PULL: + return TRUE; + default: + return FALSE; + } + return FALSE; +} + +static gboolean process_bytestream_pad_event(GstPad *pad, GstObject *parent, GstEvent *event) +{ + struct media_source *source = gst_pad_get_element_private(pad); + + TRACE("source %p, type "%s".\n", source, GST_EVENT_TYPE_NAME(event)); + + switch (event->type) { + /* the seek event should fail in pull mode */ + case GST_EVENT_SEEK: + return FALSE; + default: + WARN("Ignoring "%s" event.\n", GST_EVENT_TYPE_NAME(event)); + case GST_EVENT_TAG: + case GST_EVENT_QOS: + case GST_EVENT_RECONFIGURE: + return gst_pad_event_default(pad, parent, event); + } + return TRUE; +} + static HRESULT WINAPI media_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out) { struct media_source *source = impl_from_IMFMediaSource(iface); @@ -193,8 +360,12 @@ static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface)
static HRESULT media_source_teardown(struct media_source *source) { + if (source->my_src) + gst_object_unref(GST_OBJECT(source->my_src)); if (source->event_queue) IMFMediaEventQueue_Release(source->event_queue); + if (source->byte_stream) + IMFByteStream_Release(source->byte_stream);
return S_OK; } @@ -228,19 +399,35 @@ static const IMFMediaSourceVtbl IMFMediaSource_vtbl =
static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_type type, struct media_source **out_media_source) { + GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE( + "mf_src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + source_descs[type].bytestream_caps); + struct media_source *object = heap_alloc_zero(sizeof(*object)); HRESULT hr;
if (!object) return E_OUTOFMEMORY;
+ object->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl; + object->ref = 1; + object->type = type; + object->byte_stream = bytestream; + IMFByteStream_AddRef(bytestream); + if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) goto fail;
- object->state = SOURCE_STOPPED; + object->my_src = gst_pad_new_from_static_template(&src_template, "mf-src"); + gst_pad_set_element_private(object->my_src, object); + gst_pad_set_getrange_function(object->my_src, pull_from_bytestream_wrapper); + gst_pad_set_query_function(object->my_src, query_bytestream_wrapper); + gst_pad_set_activatemode_function(object->my_src, activate_bytestream_pad_mode_wrapper); + gst_pad_set_event_function(object->my_src, process_bytestream_pad_event_wrapper);
- object->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl; - object->ref = 1; + object->state = SOURCE_STOPPED;
*out_media_source = object; return S_OK; @@ -250,6 +437,7 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
media_source_teardown(object); heap_free(object); + *out_media_source = NULL; return hr; }
@@ -714,3 +902,41 @@ HRESULT container_stream_handler_construct(REFIID riid, void **obj, enum source_
return hr; } + +/* helper for callback forwarding */ +void perform_cb_media_source(struct cb_data *cbdata) +{ + switch(cbdata->type) + { + case PULL_FROM_BYTESTREAM: + { + struct getrange_data *data = &cbdata->u.getrange_data; + cbdata->u.getrange_data.ret = pull_from_bytestream(data->pad, data->parent, + data->ofs, data->len, data->buf); + break; + } + case QUERY_BYTESTREAM: + { + struct query_function_data *data = &cbdata->u.query_function_data; + cbdata->u.query_function_data.ret = query_bytestream(data->pad, data->parent, data->query); + break; + } + case ACTIVATE_BYTESTREAM_PAD_MODE: + { + struct activate_mode_data *data = &cbdata->u.activate_mode_data; + cbdata->u.activate_mode_data.ret = activate_bytestream_pad_mode(data->pad, data->parent, data->mode, data->activate); + break; + } + case PROCESS_BYTESTREAM_PAD_EVENT: + { + struct event_src_data *data = &cbdata->u.event_src_data; + cbdata->u.event_src_data.ret = process_bytestream_pad_event(data->pad, data->parent, data->event); + break; + } + default: + { + ERR("Wrong callback forwarder called\n"); + return; + } + } +}
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/gst_cbs.c | 47 +++- dlls/winegstreamer/gst_cbs.h | 8 + dlls/winegstreamer/media_source.c | 418 +++++++++++++++++++++++++++++- 3 files changed, 471 insertions(+), 2 deletions(-)
diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c index dfe33dd6277..4755f5b42f1 100644 --- a/dlls/winegstreamer/gst_cbs.c +++ b/dlls/winegstreamer/gst_cbs.c @@ -358,4 +358,49 @@ gboolean process_bytestream_pad_event_wrapper(GstPad *pad, GstObject *parent, Gs call_cb(&cbdata);
return cbdata.u.event_src_data.ret; -} \ No newline at end of file +} + +GstBusSyncReply watch_source_bus_wrapper(GstBus *bus, GstMessage *message, gpointer user) +{ + struct cb_data cbdata = { WATCH_SOURCE_BUS }; + + cbdata.u.watch_bus_data.bus = bus; + cbdata.u.watch_bus_data.msg = message; + cbdata.u.watch_bus_data.user = user; + + call_cb(&cbdata); + + return cbdata.u.watch_bus_data.ret; +} + +void source_stream_added_wrapper(GstElement *bin, GstPad *pad, gpointer user) +{ + struct cb_data cbdata = { SOURCE_STREAM_ADDED }; + + cbdata.u.pad_added_data.element = bin; + cbdata.u.pad_added_data.pad = pad; + cbdata.u.pad_added_data.user = user; + + call_cb(&cbdata); +} + +void source_stream_removed_wrapper(GstElement *element, GstPad *pad, gpointer user) +{ + struct cb_data cbdata = { SOURCE_STREAM_REMOVED }; + + cbdata.u.pad_removed_data.element = element; + cbdata.u.pad_removed_data.pad = pad; + cbdata.u.pad_removed_data.user = user; + + call_cb(&cbdata); +} + +void source_all_streams_wrapper(GstElement *element, gpointer user) +{ + struct cb_data cbdata = { SOURCE_ALL_STREAMS }; + + cbdata.u.no_more_pads_data.element = element; + cbdata.u.no_more_pads_data.user = user; + + call_cb(&cbdata); +} diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h index 10e999feea7..d87cc8c21e9 100644 --- a/dlls/winegstreamer/gst_cbs.h +++ b/dlls/winegstreamer/gst_cbs.h @@ -48,6 +48,10 @@ enum CB_TYPE { QUERY_BYTESTREAM, ACTIVATE_BYTESTREAM_PAD_MODE, PROCESS_BYTESTREAM_PAD_EVENT, + WATCH_SOURCE_BUS, + SOURCE_STREAM_ADDED, + SOURCE_STREAM_REMOVED, + SOURCE_ALL_STREAMS, MEDIA_SOURCE_MAX, };
@@ -164,5 +168,9 @@ GstFlowReturn pull_from_bytestream_wrapper(GstPad *pad, GstObject *parent, guint gboolean query_bytestream_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) DECLSPEC_HIDDEN; gboolean activate_bytestream_pad_mode_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) DECLSPEC_HIDDEN; gboolean process_bytestream_pad_event_wrapper(GstPad *pad, GstObject *parent, GstEvent *event) DECLSPEC_HIDDEN; +GstBusSyncReply watch_source_bus_wrapper(GstBus *bus, GstMessage *message, gpointer user) DECLSPEC_HIDDEN; +void source_stream_added_wrapper(GstElement *bin, GstPad *pad, gpointer user) DECLSPEC_HIDDEN; +void source_stream_removed_wrapper(GstElement *element, GstPad *pad, gpointer user) DECLSPEC_HIDDEN; +void source_all_streams_wrapper(GstElement *element, gpointer user) DECLSPEC_HIDDEN;
#endif diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index ba3de001738..fa0e1065ea7 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -5,6 +5,7 @@ #include "gst_private.h" #include "gst_cbs.h"
+#include <assert.h> #include <stdarg.h>
#define COBJMACROS @@ -31,6 +32,23 @@ static struct source_desc } };
+struct media_stream +{ + IMFMediaStream IMFMediaStream_iface; + LONG ref; + struct media_source *parent_source; + IMFMediaEventQueue *event_queue; + GstElement *appsink; + GstPad *their_src, *my_sink; + /* usually reflects state of source */ + enum + { + STREAM_STUB, + STREAM_INACTIVE, + STREAM_SHUTDOWN, + } state; +}; + struct media_source { IMFMediaSource IMFMediaSource_iface; @@ -38,15 +56,26 @@ struct media_source enum source_type type; IMFMediaEventQueue *event_queue; IMFByteStream *byte_stream; - GstPad *my_src; + struct media_stream **streams; + ULONG stream_count; + GstBus *bus; + GstElement *container; + GstElement *decodebin; + GstPad *my_src, *their_sink; enum { SOURCE_OPENING, SOURCE_STOPPED, SOURCE_SHUTDOWN, } state; + HANDLE all_streams_event; };
+static inline struct media_stream *impl_from_IMFMediaStream(IMFMediaStream *iface) +{ + return CONTAINING_RECORD(iface, struct media_stream, IMFMediaStream_iface); +} + static inline struct media_source *impl_from_IMFMediaSource(IMFMediaSource *iface) { return CONTAINING_RECORD(iface, struct media_source, IMFMediaSource_iface); @@ -200,6 +229,243 @@ static gboolean process_bytestream_pad_event(GstPad *pad, GstObject *parent, Gst return TRUE; }
+GstBusSyncReply watch_source_bus(GstBus *bus, GstMessage *message, gpointer user) +{ + struct media_source *source = (struct media_source *) user; + gchar *dbg_info = NULL; + GError *err = NULL; + + TRACE("source %p message type %s\n", source, GST_MESSAGE_TYPE_NAME(message)); + + switch (message->type) + { + case GST_MESSAGE_ERROR: + gst_message_parse_error(message, &err, &dbg_info); + ERR("%s: %s\n", GST_OBJECT_NAME(message->src), err->message); + ERR("%s\n", dbg_info); + g_error_free(err); + g_free(dbg_info); + break; + case GST_MESSAGE_WARNING: + gst_message_parse_warning(message, &err, &dbg_info); + WARN("%s: %s\n", GST_OBJECT_NAME(message->src), err->message); + WARN("%s\n", dbg_info); + g_error_free(err); + g_free(dbg_info); + break; + default: + break; + } + + return GST_BUS_PASS; +} + +static HRESULT WINAPI media_stream_QueryInterface(IMFMediaStream *iface, REFIID riid, void **out) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%s %p)\n", stream, debugstr_guid(riid), out); + + if (IsEqualIID(riid, &IID_IMFMediaStream) || + IsEqualIID(riid, &IID_IMFMediaEventGenerator) || + IsEqualIID(riid, &IID_IUnknown)) + { + *out = &stream->IMFMediaStream_iface; + } + else + { + FIXME("(%s, %p)\n", debugstr_guid(riid), out); + *out = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*out); + return S_OK; +} + +static ULONG WINAPI media_stream_AddRef(IMFMediaStream *iface) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + ULONG ref = InterlockedIncrement(&stream->ref); + + TRACE("(%p) ref=%u\n", stream, ref); + + return ref; +} + +static ULONG WINAPI media_stream_Release(IMFMediaStream *iface) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + ULONG ref = InterlockedDecrement(&stream->ref); + + TRACE("(%p) ref=%u\n", stream, ref); + + if (!ref) + { + if (stream->my_sink) + gst_object_unref(GST_OBJECT(stream->my_sink)); + if (stream->event_queue) + IMFMediaEventQueue_Release(stream->event_queue); + if (stream->parent_source) + IMFMediaSource_Release(&stream->parent_source->IMFMediaSource_iface); + + heap_free(stream); + } + + return ref; +} + +static HRESULT WINAPI media_stream_GetEvent(IMFMediaStream *iface, DWORD flags, IMFMediaEvent **event) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%#x, %p)\n", stream, flags, event); + + if (stream->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_GetEvent(stream->event_queue, flags, event); +} + +static HRESULT WINAPI media_stream_BeginGetEvent(IMFMediaStream *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%p, %p)\n", stream, callback, state); + + if (stream->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_BeginGetEvent(stream->event_queue, callback, state); +} + +static HRESULT WINAPI media_stream_EndGetEvent(IMFMediaStream *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%p, %p)\n", stream, result, event); + + if (stream->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_EndGetEvent(stream->event_queue, result, event); +} + +static HRESULT WINAPI media_stream_QueueEvent(IMFMediaStream *iface, MediaEventType event_type, REFGUID ext_type, + HRESULT hr, const PROPVARIANT *value) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%d, %s, %#x, %p)\n", stream, event_type, debugstr_guid(ext_type), hr, value); + + if (stream->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI media_stream_GetMediaSource(IMFMediaStream *iface, IMFMediaSource **source) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + FIXME("stub (%p)->(%p)\n", stream, source); + + if (stream->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IMFStreamDescriptor **descriptor) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%p)\n", stream, descriptor); + + if (stream->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%p)\n", iface, token); + + if (stream->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return E_NOTIMPL; +} + +static const IMFMediaStreamVtbl media_stream_vtbl = +{ + media_stream_QueryInterface, + media_stream_AddRef, + media_stream_Release, + media_stream_GetEvent, + media_stream_BeginGetEvent, + media_stream_EndGetEvent, + media_stream_QueueEvent, + media_stream_GetMediaSource, + media_stream_GetStreamDescriptor, + media_stream_RequestSample +}; + +/* creates a stub stream */ +static HRESULT new_media_stream(struct media_source *source, GstPad *pad, DWORD stream_id, struct media_stream **out_stream) +{ + struct media_stream *object = heap_alloc_zero(sizeof(*object)); + HRESULT hr; + + TRACE("(%p %p)->(%p)\n", source, pad, out_stream); + + object->IMFMediaStream_iface.lpVtbl = &media_stream_vtbl; + object->ref = 1; + + IMFMediaSource_AddRef(&source->IMFMediaSource_iface); + object->parent_source = source; + object->their_src = pad; + + object->state = STREAM_STUB; + + if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) + goto fail; + + if (!(object->appsink = gst_element_factory_make("appsink", NULL))) + { + hr = E_OUTOFMEMORY; + goto fail; + } + gst_bin_add(GST_BIN(object->parent_source->container), object->appsink); + + g_object_set(object->appsink, "emit-signals", TRUE, NULL); + g_object_set(object->appsink, "sync", FALSE, NULL); + g_object_set(object->appsink, "max-buffers", 5, NULL); + g_object_set(object->appsink, "wait-on-eos", FALSE, NULL); + + object->my_sink = gst_element_get_static_pad(object->appsink, "sink"); + gst_pad_set_element_private(object->my_sink, object); + + gst_pad_link(object->their_src, object->my_sink); + + gst_element_sync_state_with_parent(object->appsink); + + TRACE("->(%p)\n", object); + *out_stream = object; + + return S_OK; + + fail: + WARN("Failed to construct media stream, hr %#x.\n", hr); + + IMFMediaStream_Release(&object->IMFMediaStream_iface); + return hr; +} + static HRESULT WINAPI media_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out) { struct media_source *source = impl_from_IMFMediaSource(iface); @@ -360,13 +626,34 @@ static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface)
static HRESULT media_source_teardown(struct media_source *source) { + if (source->container) + { + gst_element_set_state(source->container, GST_STATE_NULL); + gst_object_unref(GST_OBJECT(source->container)); + } + if (source->my_src) gst_object_unref(GST_OBJECT(source->my_src)); + if (source->their_sink) + gst_object_unref(GST_OBJECT(source->their_sink)); + if (source->event_queue) IMFMediaEventQueue_Release(source->event_queue); if (source->byte_stream) IMFByteStream_Release(source->byte_stream);
+ for (unsigned int i = 0; i < source->stream_count; i++) + { + source->streams[i]->state = STREAM_SHUTDOWN; + IMFMediaStream_Release(&source->streams[i]->IMFMediaStream_iface); + } + + if (source->stream_count) + heap_free(source->streams); + + if (source->all_streams_event) + CloseHandle(source->all_streams_event); + return S_OK; }
@@ -397,6 +684,63 @@ static const IMFMediaSourceVtbl IMFMediaSource_vtbl = media_source_Shutdown, };
+static void source_stream_added(GstElement *element, GstPad *pad, gpointer user) +{ + struct media_source *source = (struct media_source *) user; + struct media_stream **new_stream_array; + struct media_stream *stream; + gchar *g_stream_id; + DWORD stream_id; + + if (gst_pad_get_direction(pad) != GST_PAD_SRC) + return; + + /* Most/All seen randomly calculate the initial part of the stream id, the last three digits are the only deterministic part */ + g_stream_id = GST_PAD_NAME(pad); + sscanf(strstr(g_stream_id, "_"), "_%u", &stream_id); + + TRACE("stream-id: %u\n", stream_id); + + if (FAILED(new_media_stream(source, pad, stream_id, &stream))) + { + goto leave; + } + + if (!(new_stream_array = heap_realloc(source->streams, (source->stream_count + 1) * (sizeof(*new_stream_array))))) + { + ERR("Failed to add stream to source\n"); + goto leave; + } + + source->streams = new_stream_array; + source->streams[source->stream_count++] = stream; + + leave: + return; +} + +static void source_stream_removed(GstElement *element, GstPad *pad, gpointer user) +{ + struct media_source *source = (struct media_source *)user; + + for (unsigned int i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + if (stream->their_src != pad) + continue; + stream->their_src = NULL; + if (stream->state != STREAM_INACTIVE) + stream->state = STREAM_INACTIVE; + } +} + +static void source_all_streams(GstElement *element, gpointer user) +{ + struct media_source *source = (struct media_source *) user; + + SetEvent(source->all_streams_event); +} + static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_type type, struct media_source **out_media_source) { GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE( @@ -407,6 +751,7 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
struct media_source *object = heap_alloc_zero(sizeof(*object)); HRESULT hr; + int ret;
if (!object) return E_OUTOFMEMORY; @@ -416,10 +761,16 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t object->type = type; object->byte_stream = bytestream; IMFByteStream_AddRef(bytestream); + object->all_streams_event = CreateEventA(NULL, FALSE, FALSE, NULL);
if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) goto fail;
+ object->container = gst_bin_new(NULL); + object->bus = gst_bus_new(); + gst_bus_set_sync_handler(object->bus, watch_source_bus_wrapper, object, NULL); + gst_element_set_bus(object->container, object->bus); + object->my_src = gst_pad_new_from_static_template(&src_template, "mf-src"); gst_pad_set_element_private(object->my_src, object); gst_pad_set_getrange_function(object->my_src, pull_from_bytestream_wrapper); @@ -427,6 +778,47 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t gst_pad_set_activatemode_function(object->my_src, activate_bytestream_pad_mode_wrapper); gst_pad_set_event_function(object->my_src, process_bytestream_pad_event_wrapper);
+ object->decodebin = gst_element_factory_make("decodebin", NULL); + if (!(object->decodebin)) + { + WARN("Failed to create decodebin for source\n"); + hr = E_OUTOFMEMORY; + goto fail; + } + /* the appsinks determine the maximum amount of buffering instead, this means that if one stream isn't read, a leak will happen, like on windows */ + g_object_set(object->decodebin, "max-size-buffers", 0, NULL); + g_object_set(object->decodebin, "max-size-time", G_GUINT64_CONSTANT(0), NULL); + g_object_set(object->decodebin, "max-size-bytes", 0, NULL); + g_object_set(object->decodebin, "sink-caps", gst_static_caps_get(&source_descs[type].bytestream_caps), NULL); + + gst_bin_add(GST_BIN(object->container), object->decodebin); + + g_signal_connect(object->decodebin, "pad-added", G_CALLBACK(source_stream_added_wrapper), object); + g_signal_connect(object->decodebin, "pad-removed", G_CALLBACK(source_stream_removed_wrapper), object); + g_signal_connect(object->decodebin, "no-more-pads", G_CALLBACK(source_all_streams_wrapper), object); + + object->their_sink = gst_element_get_static_pad(object->decodebin, "sink"); + + if ((ret = gst_pad_link(object->my_src, object->their_sink)) < 0) + { + WARN("Failed to link our bytestream pad to the demuxer input\n"); + hr = E_OUTOFMEMORY; + goto fail; + } + + object->state = SOURCE_OPENING; + + gst_element_set_state(object->container, GST_STATE_PAUSED); + ret = gst_element_get_state(object->container, NULL, NULL, -1); + if (ret == GST_STATE_CHANGE_FAILURE) + { + ERR("Failed to play source.\n"); + hr = E_OUTOFMEMORY; + goto fail; + } + + WaitForSingleObject(object->all_streams_event, INFINITE); + object->state = SOURCE_STOPPED;
*out_media_source = object; @@ -933,6 +1325,30 @@ void perform_cb_media_source(struct cb_data *cbdata) cbdata->u.event_src_data.ret = process_bytestream_pad_event(data->pad, data->parent, data->event); break; } + case WATCH_SOURCE_BUS: + { + struct watch_bus_data *data = &cbdata->u.watch_bus_data; + cbdata->u.watch_bus_data.ret = watch_source_bus(data->bus, data->msg, data->user); + break; + } + case SOURCE_STREAM_ADDED: + { + struct pad_added_data *data = &cbdata->u.pad_added_data; + source_stream_added(data->element, data->pad, data->user); + break; + } + case SOURCE_STREAM_REMOVED: + { + struct pad_removed_data *data = &cbdata->u.pad_removed_data; + source_stream_removed(data->element, data->pad, data->user); + break; + } + case SOURCE_ALL_STREAMS: + { + struct no_more_pads_data *data = &cbdata->u.no_more_pads_data; + source_all_streams(data->element, data->user); + break; + } default: { ERR("Wrong callback forwarder called\n");
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/gst_cbs.c | 13 ++ dlls/winegstreamer/gst_cbs.h | 8 ++ dlls/winegstreamer/gst_private.h | 4 + dlls/winegstreamer/media_source.c | 137 +++++++++++++++++++- dlls/winegstreamer/mfplat.c | 208 ++++++++++++++++++++++++++++++ 5 files changed, 367 insertions(+), 3 deletions(-)
diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c index 4755f5b42f1..e56c987eb38 100644 --- a/dlls/winegstreamer/gst_cbs.c +++ b/dlls/winegstreamer/gst_cbs.c @@ -404,3 +404,16 @@ void source_all_streams_wrapper(GstElement *element, gpointer user)
call_cb(&cbdata); } + +GstPadProbeReturn caps_listener_wrapper(GstPad *pad, GstPadProbeInfo *info, gpointer user) +{ + struct cb_data cbdata = { STREAM_PAD_EVENT }; + + cbdata.u.pad_probe_data.pad = pad; + cbdata.u.pad_probe_data.info = info; + cbdata.u.pad_probe_data.user = user; + + call_cb(&cbdata); + + return cbdata.u.pad_probe_data.ret; +} diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h index d87cc8c21e9..7173c09746e 100644 --- a/dlls/winegstreamer/gst_cbs.h +++ b/dlls/winegstreamer/gst_cbs.h @@ -52,6 +52,7 @@ enum CB_TYPE { SOURCE_STREAM_ADDED, SOURCE_STREAM_REMOVED, SOURCE_ALL_STREAMS, + STREAM_PAD_EVENT, MEDIA_SOURCE_MAX, };
@@ -137,6 +138,12 @@ struct cb_data { GstQuery *query; gboolean ret; } query_sink_data; + struct pad_probe_data { + GstPad *pad; + GstPadProbeInfo *info; + gpointer user; + GstPadProbeReturn ret; + } pad_probe_data; } u;
int finished; @@ -172,5 +179,6 @@ GstBusSyncReply watch_source_bus_wrapper(GstBus *bus, GstMessage *message, gpoin void source_stream_added_wrapper(GstElement *bin, GstPad *pad, gpointer user) DECLSPEC_HIDDEN; void source_stream_removed_wrapper(GstElement *element, GstPad *pad, gpointer user) DECLSPEC_HIDDEN; void source_all_streams_wrapper(GstElement *element, gpointer user) DECLSPEC_HIDDEN; +GstPadProbeReturn caps_listener_wrapper(GstPad *pad, GstPadProbeInfo *info, gpointer user);
#endif diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 71ca4290885..780cf1b02fa 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -36,6 +36,7 @@ #include "winuser.h" #include "dshow.h" #include "strmif.h" +#include "mfobjects.h" #include "wine/heap.h" #include "wine/strmbase.h"
@@ -54,6 +55,9 @@ void start_dispatch_thread(void) DECLSPEC_HIDDEN;
extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN;
+GstCaps *make_mf_compatible_caps(GstCaps *caps); +IMFMediaType *mf_media_type_from_caps(GstCaps *caps); + enum source_type { SOURCE_TYPE_MPEG_4, diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index fa0e1065ea7..f08d46c48c5 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -38,8 +38,10 @@ struct media_stream LONG ref; struct media_source *parent_source; IMFMediaEventQueue *event_queue; + IMFStreamDescriptor *descriptor; GstElement *appsink; GstPad *their_src, *my_sink; + GstCaps *their_caps; /* usually reflects state of source */ enum { @@ -47,6 +49,9 @@ struct media_stream STREAM_INACTIVE, STREAM_SHUTDOWN, } state; + /* used when in STUB state: */ + DWORD stream_id; + HANDLE caps_event; };
struct media_source @@ -305,6 +310,8 @@ static ULONG WINAPI media_stream_Release(IMFMediaStream *iface) { if (stream->my_sink) gst_object_unref(GST_OBJECT(stream->my_sink)); + if (stream->descriptor) + IMFStreamDescriptor_Release(stream->descriptor); if (stream->event_queue) IMFMediaEventQueue_Release(stream->event_queue); if (stream->parent_source) @@ -386,7 +393,10 @@ static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IM if (stream->state == STREAM_SHUTDOWN) return MF_E_SHUTDOWN;
- return E_NOTIMPL; + IMFStreamDescriptor_AddRef(stream->descriptor); + *descriptor = stream->descriptor; + + return S_OK; }
static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) @@ -429,9 +439,12 @@ static HRESULT new_media_stream(struct media_source *source, GstPad *pad, DWORD IMFMediaSource_AddRef(&source->IMFMediaSource_iface); object->parent_source = source; object->their_src = pad; + object->stream_id = stream_id;
object->state = STREAM_STUB;
+ object->caps_event = CreateEventA(NULL, TRUE, FALSE, NULL); + if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) goto fail;
@@ -448,10 +461,10 @@ static HRESULT new_media_stream(struct media_source *source, GstPad *pad, DWORD g_object_set(object->appsink, "wait-on-eos", FALSE, NULL);
object->my_sink = gst_element_get_static_pad(object->appsink, "sink"); - gst_pad_set_element_private(object->my_sink, object); - gst_pad_link(object->their_src, object->my_sink);
+ gst_pad_add_probe(object->my_sink, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, caps_listener_wrapper, object, NULL); + gst_element_sync_state_with_parent(object->appsink);
TRACE("->(%p)\n", object); @@ -466,6 +479,43 @@ static HRESULT new_media_stream(struct media_source *source, GstPad *pad, DWORD return hr; }
+static HRESULT media_stream_init_desc(struct media_stream *stream) +{ + HRESULT hr; + IMFMediaTypeHandler *type_handler; + IMFMediaType *stream_type = NULL; + + stream->their_caps = gst_caps_fixate(stream->their_caps); + + stream_type = mf_media_type_from_caps(stream->their_caps); + gst_caps_unref(stream->their_caps); + if (!stream_type) + { + hr = E_FAIL; + goto fail; + } + + if (FAILED(hr = MFCreateStreamDescriptor(stream->stream_id, 1, &stream_type, &stream->descriptor))) + goto fail; + + if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &type_handler))) + goto fail; + + if (FAILED(hr = IMFMediaTypeHandler_SetCurrentMediaType(type_handler, stream_type))) + goto fail; + + IMFMediaTypeHandler_Release(type_handler); + + stream->state = STREAM_INACTIVE; + + return S_OK; + fail: + ERR("media stream initialization failed with %x\n", hr); + if (type_handler) + IMFMediaTypeHandler_Release(type_handler); + return hr; +} + static HRESULT WINAPI media_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out) { struct media_source *source = impl_from_IMFMediaSource(iface); @@ -684,6 +734,23 @@ static const IMFMediaSourceVtbl IMFMediaSource_vtbl = media_source_Shutdown, };
+/* If this callback is extended to use any significant win32 APIs, a wrapper function + should be added */ +gboolean stream_found(GstElement *bin, GstPad *pad, GstCaps *caps, gpointer user) +{ + GstCaps *target_caps; + + /* if the stream can be converted into an MF compatible type, we'll go that route + otherwise, we'll rely on decodebin for the whole process */ + + if ((target_caps = make_mf_compatible_caps(caps))) + { + gst_caps_unref(target_caps); + return FALSE; + } + return TRUE; +} + static void source_stream_added(GstElement *element, GstPad *pad, gpointer user) { struct media_source *source = (struct media_source *) user; @@ -701,6 +768,36 @@ static void source_stream_added(GstElement *element, GstPad *pad, gpointer user)
TRACE("stream-id: %u\n", stream_id);
+ /* This codepath is currently never triggered, as we don't need to ever restart the gstreamer pipeline. It is retained in + case this becomes necessary in the future, for example in a case where different media types require different + post-processing elements. */ + for (unsigned int i = 0; i < source->stream_count; i++) + { + DWORD existing_stream_id; + IMFStreamDescriptor *descriptor = source->streams[i]->descriptor; + + if (source->streams[i]->state == STREAM_STUB) + continue; + + if (FAILED(IMFStreamDescriptor_GetStreamIdentifier(descriptor, &existing_stream_id))) + goto leave; + + if (existing_stream_id == stream_id) + { + struct media_stream *existing_stream = source->streams[i]; + GstPadLinkReturn ret; + + TRACE("Found existing stream %p\n", existing_stream); + + existing_stream->their_src = pad; + + if ((ret = gst_pad_link(existing_stream->their_src, existing_stream->my_sink)) != GST_PAD_LINK_OK) + ERR("Error linking decodebin pad to stream %p, err = %d\n", existing_stream, ret); + + goto leave; + } + } + if (FAILED(new_media_stream(source, pad, stream_id, &stream))) { goto leave; @@ -741,6 +838,26 @@ static void source_all_streams(GstElement *element, gpointer user) SetEvent(source->all_streams_event); }
+static GstPadProbeReturn caps_listener(GstPad *pad, GstPadProbeInfo *info, gpointer user) +{ + struct media_stream *stream = (struct media_stream *) user; + GstEvent *event = gst_pad_probe_info_get_event(info); + + if (GST_EVENT_TYPE(event) == GST_EVENT_CAPS) + { + GstCaps *caps; + TRACE("got caps for stream %p\n", stream); + + gst_event_parse_caps(event, &caps); + stream->their_caps = gst_caps_copy(caps); + SetEvent(stream->caps_event); + + return GST_PAD_PROBE_REMOVE; + } + + return GST_PAD_PROBE_OK; +} + static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_type type, struct media_source **out_media_source) { GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE( @@ -793,6 +910,8 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
gst_bin_add(GST_BIN(object->container), object->decodebin);
+ if(!GetEnvironmentVariableA("MF_DECODE_IN_SOURCE", NULL, 0)) + g_signal_connect(object->decodebin, "autoplug-continue", G_CALLBACK(stream_found), object); g_signal_connect(object->decodebin, "pad-added", G_CALLBACK(source_stream_added_wrapper), object); g_signal_connect(object->decodebin, "pad-removed", G_CALLBACK(source_stream_removed_wrapper), object); g_signal_connect(object->decodebin, "no-more-pads", G_CALLBACK(source_all_streams_wrapper), object); @@ -818,6 +937,12 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t }
WaitForSingleObject(object->all_streams_event, INFINITE); + for (unsigned int i = 0; i < object->stream_count; i++) + { + WaitForSingleObject(object->streams[i]->caps_event, INFINITE); + if (FAILED(hr = media_stream_init_desc(object->streams[i]))) + goto fail; + }
object->state = SOURCE_STOPPED;
@@ -1349,6 +1474,12 @@ void perform_cb_media_source(struct cb_data *cbdata) source_all_streams(data->element, data->user); break; } + case STREAM_PAD_EVENT: + { + struct pad_probe_data *data = &cbdata->u.pad_probe_data; + cbdata->u.pad_probe_data.ret = caps_listener(data->pad, data->info, data->user); + break; + } default: { ERR("Wrong callback forwarder called\n"); diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 16e55247de1..06f7984bbe6 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -16,6 +16,11 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include "config.h" +#include <gst/gst.h> + +#include "gst_private.h" + #include <stdarg.h>
#include "gst_private.h" @@ -442,3 +447,206 @@ HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj)
return CLASS_E_CLASSNOTAVAILABLE; } + +const static struct +{ + const GUID *subtype; + GstVideoFormat format; +} +uncompressed_formats[] = +{ + {&MFVideoFormat_ARGB32, GST_VIDEO_FORMAT_BGRA}, + {&MFVideoFormat_RGB32, GST_VIDEO_FORMAT_BGRx}, + {&MFVideoFormat_RGB24, GST_VIDEO_FORMAT_BGR}, + {&MFVideoFormat_RGB565, GST_VIDEO_FORMAT_BGR16}, + {&MFVideoFormat_RGB555, GST_VIDEO_FORMAT_BGR15}, +}; + +/* caps will be modified to represent the exact type needed for the format */ +static IMFMediaType* transform_to_media_type(GstCaps *caps) +{ + IMFMediaType *media_type; + GstStructure *info; + const char *mime_type; + + if (TRACE_ON(mfplat)) + { + gchar *human_readable = gst_caps_to_string(caps); + TRACE("caps = %s\n", debugstr_a(human_readable)); + g_free(human_readable); + } + + if (FAILED(MFCreateMediaType(&media_type))) + { + return NULL; + } + + info = gst_caps_get_structure(caps, 0); + mime_type = gst_structure_get_name(info); + + if (!(strncmp(mime_type, "video", 5))) + { + GstVideoInfo video_info; + + if (!(gst_video_info_from_caps(&video_info, caps))) + { + return NULL; + } + + IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); + + IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_SIZE, ((UINT64)video_info.width << 32) | video_info.height); + + IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_RATE, ((UINT64)video_info.fps_n << 32) | video_info.fps_d); + + if (!(strcmp(mime_type, "video/x-raw"))) + { + GUID fourcc_subtype = MFVideoFormat_Base; + unsigned int i; + + IMFMediaType_SetUINT32(media_type, &MF_MT_COMPRESSED, FALSE); + + /* First try FOURCC */ + if ((fourcc_subtype.Data1 = gst_video_format_to_fourcc(video_info.finfo->format))) + { + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &fourcc_subtype); + } + else + { + for (i = 0; i < ARRAY_SIZE(uncompressed_formats); i++) + { + if (uncompressed_formats[i].format == video_info.finfo->format) + { + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, uncompressed_formats[i].subtype); + break; + } + } + if (i == ARRAY_SIZE(uncompressed_formats)) + FIXME("Unrecognized format.\n"); + } + } + else + { + FIXME("Unrecognized video format %s\n", mime_type); + IMFMediaType_Release(media_type); + return NULL; + } + } + else if (!(strncmp(mime_type, "audio", 5))) + { + gint rate, channels, bitrate; + guint64 channel_mask; + IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio); + + if (gst_structure_get_int(info, "rate", &rate)) + IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, rate); + + if (gst_structure_get_int(info, "channels", &channels)) + IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_NUM_CHANNELS, channels); + + if (gst_structure_get(info, "channel-mask", GST_TYPE_BITMASK, &channel_mask, NULL)) + IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_CHANNEL_MASK, (DWORD)channel_mask); + + if (gst_structure_get_int(info, "bitrate", &bitrate)) + IMFMediaType_SetUINT32(media_type, &MF_MT_AVG_BITRATE, bitrate); + + if (!(strcmp(mime_type, "audio/x-raw"))) + { + const char *format; + if ((format = gst_structure_get_string(info, "format"))) + { + char type; + unsigned int bits_per_sample; + char endian[2]; + char new_format[6]; + if ((strlen(format) > 5) || (sscanf(format, "%c%u%2c", &type, &bits_per_sample, endian) < 2)) + { + FIXME("Unhandled audio format %s\n", format); + IMFMediaType_Release(media_type); + return NULL; + } + + if (type == 'F') + { + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_Float); + } + else if (type == 'U' || type == 'S') + { + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_PCM); + if (bits_per_sample == 8) + type = 'U'; + else + type = 'S'; + } + else + { + FIXME("Unrecognized audio format: %s\n", format); + IMFMediaType_Release(media_type); + return NULL; + } + + IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, bits_per_sample); + + if (endian[0] == 'B') + endian[0] = 'L'; + + sprintf(new_format, "%c%u%.2s", type, bits_per_sample, bits_per_sample > 8 ? endian : 0); + gst_caps_set_simple(caps, "format", G_TYPE_STRING, new_format, NULL); + } + else + { + ERR("Failed to get audio format\n"); + } + } + else + { + FIXME("Unrecognized audio format %s\n", mime_type); + IMFMediaType_Release(media_type); + return NULL; + } + } + else + { + IMFMediaType_Release(media_type); + return NULL; + } + + return media_type; +} + +/* returns NULL if doesn't match exactly */ +IMFMediaType *mf_media_type_from_caps(GstCaps *caps) +{ + GstCaps *writeable_caps; + IMFMediaType *ret; + + writeable_caps = gst_caps_copy(caps); + ret = transform_to_media_type(writeable_caps); + + if (!(gst_caps_is_equal(caps, writeable_caps))) + { + IMFMediaType_Release(ret); + ret = NULL; + } + gst_caps_unref(writeable_caps); + return ret; +} + +GstCaps *make_mf_compatible_caps(GstCaps *caps) +{ + GstCaps *ret; + IMFMediaType *media_type; + + ret = gst_caps_copy(caps); + + if ((media_type = transform_to_media_type(ret))) + IMFMediaType_Release(media_type); + + if (!media_type) + { + gst_caps_unref(ret); + return NULL; + } + + return ret; +} \ No newline at end of file
This patch is still awfully large to review all at once. Just from skimming I see the "event_queue" field, which could be split into a separate patch, and the different "container_stream_handler" and "media_source" objects, which could be split into separate patches.
On 8/26/20 1:59 PM, Derek Lesho wrote:
Signed-off-by: Derek Lesho dlesho@codeweavers.com
v2: Fix tests on environments without gstreamer.
dlls/mfplat/tests/mfplat.c | 14 +- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_private.h | 6 + dlls/winegstreamer/media_source.c | 680 +++++++++++++++++++ dlls/winegstreamer/mfplat.c | 9 + dlls/winegstreamer/winegstreamer.rgs | 32 + dlls/winegstreamer/winegstreamer_classes.idl | 7 + include/mfidl.idl | 1 + 8 files changed, 746 insertions(+), 4 deletions(-) create mode 100644 dlls/winegstreamer/media_source.c
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 01749dd9ef8..29bcef7e46b 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -441,8 +441,8 @@ static void test_source_resolver(void) { struct test_callback callback = { { &test_create_from_url_callback_vtbl } }; struct test_callback callback2 = { { &test_create_from_file_handler_callback_vtbl } };
- IMFPresentationDescriptor *descriptor = NULL; IMFSourceResolver *resolver, *resolver2;
- IMFPresentationDescriptor *descriptor; IMFSchemeHandler *scheme_handler; IMFMediaStream *video_stream; IMFAttributes *attributes;
@@ -458,9 +458,12 @@ static void test_source_resolver(void) int i, sample_count; WCHAR *filename; PROPVARIANT var;
BOOL is_wine; HRESULT hr; GUID guid;
is_wine = !strcmp(winetest_platform, "wine");
if (!pMFCreateSourceResolver) { win_skip("MFCreateSourceResolver() not found\n");
@@ -529,7 +532,10 @@ static void test_source_resolver(void) ok(obj_type == MF_OBJECT_MEDIASOURCE, "got %d\n", obj_type);
hr = IMFMediaSource_CreatePresentationDescriptor(mediasource, &descriptor);
+if (!is_wine) ok(hr == S_OK, "Failed to get presentation descriptor, hr %#x.\n", hr);
I suspect you're doing this because of the fallback path in source_resolver_CreateObjectFromByteStream(), combined with the optional presence of winegstreamer. I would propose that this patch series first remove that fallback path, as it is (as I understand) entirely incorrect, and since any potential regression will be addressed by this patch series itself.
if (FAILED(hr))
goto skip_source_tests;
ok(descriptor != NULL, "got %p\n", descriptor);
hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(descriptor, 0, &selected, &sd);
@@ -540,7 +546,7 @@ static void test_source_resolver(void) IMFStreamDescriptor_Release(sd);
hr = IMFMediaTypeHandler_GetMajorType(handler, &guid);
-todo_wine +if (!is_wine) ok(hr == S_OK, "Failed to get stream major type, hr %#x.\n", hr); if (FAILED(hr)) goto skip_source_tests; @@ -632,8 +638,8 @@ todo_wine ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
skip_source_tests:
- IMFPresentationDescriptor_Release(descriptor);
- if (descriptor)
IMFMediaSource_Release(mediasource); IMFByteStream_Release(stream);IMFPresentationDescriptor_Release(descriptor);
diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 337c1086e6b..e578d194f7f 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -10,6 +10,7 @@ C_SRCS = \ gst_cbs.c \ gstdemux.c \ main.c \
- media_source.c \ mediatype.c \ mfplat.c \ pin.c \
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index e6fb841fc87..71ca4290885 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -54,4 +54,10 @@ void start_dispatch_thread(void) DECLSPEC_HIDDEN;
extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN;
+enum source_type +{
- SOURCE_TYPE_MPEG_4,
+};
This enumeration, and variables associated with it, are dead code, and would be best introduced when used.
+HRESULT container_stream_handler_construct(REFIID riid, void **obj, enum source_type);
Missing DECLSPEC_HIDDEN. Using the "_create" convention as elsewhere would probably not be a bad idea, either.
#endif /* __GST_PRIVATE_INCLUDED__ */
On 8/26/20 6:42 PM, Zebediah Figura wrote:
This patch is still awfully large to review all at once. Just from skimming I see the "event_queue" field, which could be split into a separate patch, and the different "container_stream_handler" and "media_source" objects, which could be split into separate patches.
On 8/26/20 1:59 PM, Derek Lesho wrote:
Signed-off-by: Derek Lesho dlesho@codeweavers.com
v2: Fix tests on environments without gstreamer.
dlls/mfplat/tests/mfplat.c | 14 +- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_private.h | 6 + dlls/winegstreamer/media_source.c | 680 +++++++++++++++++++ dlls/winegstreamer/mfplat.c | 9 + dlls/winegstreamer/winegstreamer.rgs | 32 + dlls/winegstreamer/winegstreamer_classes.idl | 7 + include/mfidl.idl | 1 + 8 files changed, 746 insertions(+), 4 deletions(-) create mode 100644 dlls/winegstreamer/media_source.c
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 01749dd9ef8..29bcef7e46b 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -441,8 +441,8 @@ static void test_source_resolver(void) { struct test_callback callback = { { &test_create_from_url_callback_vtbl } }; struct test_callback callback2 = { { &test_create_from_file_handler_callback_vtbl } };
- IMFPresentationDescriptor *descriptor = NULL; IMFSourceResolver *resolver, *resolver2;
- IMFPresentationDescriptor *descriptor; IMFSchemeHandler *scheme_handler; IMFMediaStream *video_stream; IMFAttributes *attributes;
@@ -458,9 +458,12 @@ static void test_source_resolver(void) int i, sample_count; WCHAR *filename; PROPVARIANT var;
BOOL is_wine; HRESULT hr; GUID guid;
is_wine = !strcmp(winetest_platform, "wine");
if (!pMFCreateSourceResolver) { win_skip("MFCreateSourceResolver() not found\n");
@@ -529,7 +532,10 @@ static void test_source_resolver(void) ok(obj_type == MF_OBJECT_MEDIASOURCE, "got %d\n", obj_type);
hr = IMFMediaSource_CreatePresentationDescriptor(mediasource, &descriptor);
+if (!is_wine) ok(hr == S_OK, "Failed to get presentation descriptor, hr %#x.\n", hr);
I suspect you're doing this because of the fallback path in source_resolver_CreateObjectFromByteStream(), combined with the optional presence of winegstreamer. I would propose that this patch series first remove that fallback path, as it is (as I understand) entirely incorrect, and since any potential regression will be addressed by this patch series itself.
Even after I remove the stub source, I think I still need two goto paths, one which triggers when the source fails to create, skipping all the actions on the source object, and the other which slowly recedes over the course of the patches. (skipping tests after ::CreatePresentationDescriptor before it's implemented, then skipping tests after ::Start before it's implemented, then finally disappearing once ::Start is implemented) Is this okay? Or alternatively should I just free the resources and return early when creating the media source fails, skipping some of the winegstreamer agnostic tests at the end.
In my opinion, the best solution would be to move the winegstreamer dependent part/s of the test function to the end so that we can return early instead of using a goto in this instance. But isn't there a preference against moving code around unnecessarily?
if (FAILED(hr))
goto skip_source_tests; ok(descriptor != NULL, "got %p\n", descriptor); hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(descriptor, 0, &selected, &sd);
@@ -540,7 +546,7 @@ static void test_source_resolver(void) IMFStreamDescriptor_Release(sd);
hr = IMFMediaTypeHandler_GetMajorType(handler, &guid);
-todo_wine +if (!is_wine) ok(hr == S_OK, "Failed to get stream major type, hr %#x.\n", hr); if (FAILED(hr)) goto skip_source_tests; @@ -632,8 +638,8 @@ todo_wine ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
skip_source_tests:
- IMFPresentationDescriptor_Release(descriptor);
- if (descriptor)
IMFPresentationDescriptor_Release(descriptor); IMFMediaSource_Release(mediasource); IMFByteStream_Release(stream);
diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 337c1086e6b..e578d194f7f 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -10,6 +10,7 @@ C_SRCS = \ gst_cbs.c \ gstdemux.c \ main.c \
- media_source.c \ mediatype.c \ mfplat.c \ pin.c \
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index e6fb841fc87..71ca4290885 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -54,4 +54,10 @@ void start_dispatch_thread(void) DECLSPEC_HIDDEN;
extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN;
+enum source_type +{
- SOURCE_TYPE_MPEG_4,
+};
This enumeration, and variables associated with it, are dead code, and would be best introduced when used.
+HRESULT container_stream_handler_construct(REFIID riid, void **obj, enum source_type);
Missing DECLSPEC_HIDDEN. Using the "_create" convention as elsewhere would probably not be a bad idea, either.
- #endif /* __GST_PRIVATE_INCLUDED__ */
On 8/27/20 10:18 AM, Derek Lesho wrote:
On 8/26/20 6:42 PM, Zebediah Figura wrote:
This patch is still awfully large to review all at once. Just from skimming I see the "event_queue" field, which could be split into a separate patch, and the different "container_stream_handler" and "media_source" objects, which could be split into separate patches.
On 8/26/20 1:59 PM, Derek Lesho wrote:
Signed-off-by: Derek Lesho dlesho@codeweavers.com
v2: Fix tests on environments without gstreamer.
dlls/mfplat/tests/mfplat.c | 14 +- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_private.h | 6 + dlls/winegstreamer/media_source.c | 680 +++++++++++++++++++ dlls/winegstreamer/mfplat.c | 9 + dlls/winegstreamer/winegstreamer.rgs | 32 + dlls/winegstreamer/winegstreamer_classes.idl | 7 + include/mfidl.idl | 1 + 8 files changed, 746 insertions(+), 4 deletions(-) create mode 100644 dlls/winegstreamer/media_source.c
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 01749dd9ef8..29bcef7e46b 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -441,8 +441,8 @@ static void test_source_resolver(void) { struct test_callback callback = { { &test_create_from_url_callback_vtbl } }; struct test_callback callback2 = { { &test_create_from_file_handler_callback_vtbl } };
- IMFPresentationDescriptor *descriptor = NULL; IMFSourceResolver *resolver, *resolver2;
- IMFPresentationDescriptor *descriptor; IMFSchemeHandler *scheme_handler; IMFMediaStream *video_stream; IMFAttributes *attributes;
@@ -458,9 +458,12 @@ static void test_source_resolver(void) int i, sample_count; WCHAR *filename; PROPVARIANT var;
BOOL is_wine; HRESULT hr; GUID guid;
is_wine = !strcmp(winetest_platform, "wine");
if (!pMFCreateSourceResolver) { win_skip("MFCreateSourceResolver() not found\n");
@@ -529,7 +532,10 @@ static void test_source_resolver(void) ok(obj_type == MF_OBJECT_MEDIASOURCE, "got %d\n", obj_type);
hr = IMFMediaSource_CreatePresentationDescriptor(mediasource, &descriptor);
+if (!is_wine) ok(hr == S_OK, "Failed to get presentation descriptor, hr %#x.\n", hr);
I suspect you're doing this because of the fallback path in source_resolver_CreateObjectFromByteStream(), combined with the optional presence of winegstreamer. I would propose that this patch series first remove that fallback path, as it is (as I understand) entirely incorrect, and since any potential regression will be addressed by this patch series itself.
Even after I remove the stub source, I think I still need two goto paths, one which triggers when the source fails to create, skipping all the actions on the source object, and the other which slowly recedes over the course of the patches. (skipping tests after ::CreatePresentationDescriptor before it's implemented, then skipping tests after ::Start before it's implemented, then finally disappearing once ::Start is implemented) Is this okay? Or alternatively should I just free the resources and return early when creating the media source fails, skipping some of the winegstreamer agnostic tests at the end.
I think either way works; I've seen both happen in practice. Generally I opt for not using a goto, in order to not thrash the end of the function.
In my opinion, the best solution would be to move the winegstreamer dependent part/s of the test function to the end so that we can return early instead of using a goto in this instance. But isn't there a preference against moving code around unnecessarily?
Sure, but improving something's architecture counts as a good reason, I think.
In general I'm inclined to prefer such an arrangement, but I'm not sure I'm familiar enough with mfplat to comment on test_source_resolver().
if (FAILED(hr))
goto skip_source_tests; ok(descriptor != NULL, "got %p\n", descriptor); hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(descriptor, 0, &selected, &sd);
@@ -540,7 +546,7 @@ static void test_source_resolver(void) IMFStreamDescriptor_Release(sd);
hr = IMFMediaTypeHandler_GetMajorType(handler, &guid);
-todo_wine +if (!is_wine) ok(hr == S_OK, "Failed to get stream major type, hr %#x.\n", hr); if (FAILED(hr)) goto skip_source_tests; @@ -632,8 +638,8 @@ todo_wine ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
skip_source_tests:
- IMFPresentationDescriptor_Release(descriptor);
- if (descriptor)
IMFPresentationDescriptor_Release(descriptor); IMFMediaSource_Release(mediasource); IMFByteStream_Release(stream);
diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 337c1086e6b..e578d194f7f 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -10,6 +10,7 @@ C_SRCS = \ gst_cbs.c \ gstdemux.c \ main.c \
- media_source.c \ mediatype.c \ mfplat.c \ pin.c \
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index e6fb841fc87..71ca4290885 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -54,4 +54,10 @@ void start_dispatch_thread(void) DECLSPEC_HIDDEN;
extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN;
+enum source_type +{
- SOURCE_TYPE_MPEG_4,
+};
This enumeration, and variables associated with it, are dead code, and would be best introduced when used.
+HRESULT container_stream_handler_construct(REFIID riid, void **obj, enum source_type);
Missing DECLSPEC_HIDDEN. Using the "_create" convention as elsewhere would probably not be a bad idea, either.
- #endif /* __GST_PRIVATE_INCLUDED__ */