Signed-off-by: Derek Lesho dlesho@codeweavers.com --- I asked Nikolay what he thought about splitting up this patch, and said he didn't have an opinion. I think that since all of the bytestream handler stuff is a direct copy/paste from the version in mf/main.c, it should all go in at once. --- dlls/mfplat/tests/mfplat.c | 9 +- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_private.h | 6 + dlls/winegstreamer/media_source.c | 682 +++++++++++++++++++ dlls/winegstreamer/mfplat.c | 9 + dlls/winegstreamer/winegstreamer.rgs | 32 + dlls/winegstreamer/winegstreamer_classes.idl | 7 + include/mfidl.idl | 1 + 8 files changed, 742 insertions(+), 5 deletions(-) create mode 100644 dlls/winegstreamer/media_source.c
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 78d73b7580..dfac973a5e 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -491,7 +491,10 @@ static void test_source_resolver(void) ok(obj_type == MF_OBJECT_MEDIASOURCE, "got %d\n", obj_type);
hr = IMFMediaSource_CreatePresentationDescriptor(mediasource, &descriptor); +todo_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); @@ -502,10 +505,7 @@ static void test_source_resolver(void) IMFStreamDescriptor_Release(sd);
hr = IMFMediaTypeHandler_GetMajorType(handler, &guid); -todo_wine ok(hr == S_OK, "Failed to get stream major type, hr %#x.\n", hr); - if (FAILED(hr)) - goto skip_source_tests;
/* Check major/minor type for the test media. */ ok(IsEqualGUID(&guid, &MFMediaType_Video), "Unexpected major type %s.\n", debugstr_guid(&guid)); @@ -586,6 +586,7 @@ todo_wine get_event((IMFMediaEventGenerator *)mediasource, MEEndOfPresentation, NULL);
IMFMediaTypeHandler_Release(handler); + IMFPresentationDescriptor_Release(descriptor);
hr = IMFMediaSource_Shutdown(mediasource); ok(hr == S_OK, "Unexpected hr %#x.\n", hr); @@ -594,8 +595,6 @@ todo_wine ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
skip_source_tests: - - IMFPresentationDescriptor_Release(descriptor); IMFMediaSource_Release(mediasource); IMFByteStream_Release(stream);
diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 2364ac84dc..f4f72c6e96 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -9,6 +9,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 e6fb841fc8..71ca429088 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 0000000000..535c9873a7 --- /dev/null +++ b/dlls/winegstreamer/media_source.c @@ -0,0 +1,682 @@ +#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; +}; + +/* source */ + +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 *This = impl_from_IMFMediaSource(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), out); + + if (IsEqualIID(riid, &IID_IMFMediaSource) || + IsEqualIID(riid, &IID_IMFMediaEventGenerator) || + IsEqualIID(riid, &IID_IUnknown)) + { + *out = &This->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 *This = impl_from_IMFMediaSource(iface); + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%u\n", This, ref); + + return ref; +} + +static ULONG WINAPI media_source_Release(IMFMediaSource *iface) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%u\n", This, ref); + + if (!ref) + { + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI media_source_GetEvent(IMFMediaSource *iface, DWORD flags, IMFMediaEvent **event) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + TRACE("(%p)->(%#x, %p)\n", This, flags, event); + + return IMFMediaEventQueue_GetEvent(This->event_queue, flags, event); +} + +static HRESULT WINAPI media_source_BeginGetEvent(IMFMediaSource *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + TRACE("(%p)->(%p, %p)\n", This, callback, state); + + return IMFMediaEventQueue_BeginGetEvent(This->event_queue, callback, state); +} + +static HRESULT WINAPI media_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + TRACE("(%p)->(%p, %p)\n", This, result, event); + + return IMFMediaEventQueue_EndGetEvent(This->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 *This = impl_from_IMFMediaSource(iface); + + TRACE("(%p)->(%d, %s, %#x, %p)\n", This, event_type, debugstr_guid(ext_type), hr, value); + + return IMFMediaEventQueue_QueueEventParamVar(This->event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI media_source_GetCharacteristics(IMFMediaSource *iface, DWORD *characteristics) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + FIXME("(%p)->(%p): stub\n", This, characteristics); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **descriptor) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + FIXME("(%p)->(%p): stub\n", This, 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 *This = impl_from_IMFMediaSource(iface); + + FIXME("(%p)->(%p, %p, %p): stub\n", This, descriptor, time_format, start_position); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + FIXME("(%p): stub\n", This); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + FIXME("(%p): stub\n", This); + + return E_NOTIMPL; +} + +static HRESULT media_source_teardown(struct media_source *This) +{ + if (This->event_queue) + IMFMediaEventQueue_Release(This->event_queue); + + return S_OK; +} + +static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + FIXME("(%p): stub\n", This); + + 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 *This = heap_alloc_zero(sizeof(*This)); + HRESULT hr; + + if (!This) + return E_OUTOFMEMORY; + + if (FAILED(hr = MFCreateEventQueue(&This->event_queue))) + goto fail; + + This->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl; + This->ref = 1; + + *out_media_source = This; + return S_OK; + + fail: + WARN("Failed to construct MFMediaSource, hr %#x.\n", hr); + + media_source_teardown(This); + heap_free(This); + 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 55b9b08876..16e55247de 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 923ba673f8..0bc26761e9 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 fa0e178405..997a28b052 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 a5fb8bc0bd..15a68a4253 100644 --- a/include/mfidl.idl +++ b/include/mfidl.idl @@ -1066,3 +1066,4 @@ cpp_quote("EXTERN_GUID(MF_SESSION_APPROX_EVENT_OCCURRENCE_TIME, 0x190e852f, 0x62 cpp_quote("EXTERN_GUID(MF_PMP_SERVER_CONTEXT, 0x2f00c910, 0xd2cf, 0x4278, 0x8b, 0x6a, 0xd0, 0x77, 0xfa, 0xc3, 0xa2, 0x5f);")
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 | 2 +- dlls/winegstreamer/media_source.c | 39 +++++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 3 deletions(-)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index dfac973a5e..d2d7db1b29 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -588,13 +588,13 @@ todo_wine IMFMediaTypeHandler_Release(handler); IMFPresentationDescriptor_Release(descriptor);
+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: IMFMediaSource_Release(mediasource); IMFByteStream_Release(stream);
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 535c9873a7..d6895b0513 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -20,6 +20,14 @@ struct media_source IMFMediaSource IMFMediaSource_iface; LONG ref; IMFMediaEventQueue *event_queue; + enum + { + SOURCE_OPENING, + SOURCE_STOPPED, /* (READY) */ + SOURCE_PAUSED, + SOURCE_RUNNING, + SOURCE_SHUTDOWN, + } state; };
/* source */ @@ -83,6 +91,9 @@ static HRESULT WINAPI media_source_GetEvent(IMFMediaSource *iface, DWORD flags,
TRACE("(%p)->(%#x, %p)\n", This, flags, event);
+ if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return IMFMediaEventQueue_GetEvent(This->event_queue, flags, event); }
@@ -92,6 +103,9 @@ static HRESULT WINAPI media_source_BeginGetEvent(IMFMediaSource *iface, IMFAsync
TRACE("(%p)->(%p, %p)\n", This, callback, state);
+ if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return IMFMediaEventQueue_BeginGetEvent(This->event_queue, callback, state); }
@@ -101,6 +115,9 @@ static HRESULT WINAPI media_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncRe
TRACE("(%p)->(%p, %p)\n", This, result, event);
+ if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return IMFMediaEventQueue_EndGetEvent(This->event_queue, result, event); }
@@ -111,6 +128,9 @@ static HRESULT WINAPI media_source_QueueEvent(IMFMediaSource *iface, MediaEventT
TRACE("(%p)->(%d, %s, %#x, %p)\n", This, event_type, debugstr_guid(ext_type), hr, value);
+ if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return IMFMediaEventQueue_QueueEventParamVar(This->event_queue, event_type, ext_type, hr, value); }
@@ -120,6 +140,9 @@ static HRESULT WINAPI media_source_GetCharacteristics(IMFMediaSource *iface, DWO
FIXME("(%p)->(%p): stub\n", This, characteristics);
+ if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return E_NOTIMPL; }
@@ -129,6 +152,9 @@ static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource *
FIXME("(%p)->(%p): stub\n", This, descriptor);
+ if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return E_NOTIMPL; }
@@ -139,6 +165,9 @@ static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationD
FIXME("(%p)->(%p, %p, %p): stub\n", This, descriptor, time_format, start_position);
+ if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return E_NOTIMPL; }
@@ -148,6 +177,9 @@ static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface)
FIXME("(%p): stub\n", This);
+ if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return E_NOTIMPL; }
@@ -172,9 +204,10 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) { struct media_source *This = impl_from_IMFMediaSource(iface);
- FIXME("(%p): stub\n", This); + TRACE("(%p)\n", This);
- return E_NOTIMPL; + This->state = SOURCE_SHUTDOWN; + return media_source_teardown(This); }
static const IMFMediaSourceVtbl IMFMediaSource_vtbl = @@ -205,6 +238,8 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t if (FAILED(hr = MFCreateEventQueue(&This->event_queue))) goto fail;
+ This->state = SOURCE_STOPPED; + This->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl; This->ref = 1;
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=69027
Your paranoid android.
=== debiant (32 bit report) ===
mfplat: mfplat.c:495: Test succeeded inside todo block: Failed to get presentation descriptor, hr 0. mfplat.c:508: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:511: Test failed: Unexpected major type {0063fb28-fb20-0063-0400-000020fb6300}. mfplat.c:514: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on read access to 0x00000004 in 32-bit code (0x0040eb38).
=== debiant (32 bit French report) ===
mfplat: mfplat.c:495: Test succeeded inside todo block: Failed to get presentation descriptor, hr 0. mfplat.c:508: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:511: Test failed: Unexpected major type {0063fb28-fb20-0063-0400-000020fb6300}. mfplat.c:514: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on read access to 0x00000004 in 32-bit code (0x0040eb38).
Report validation errors: mfplat:mfplat crashed (c0000005)
=== debiant (32 bit Japanese:Japan report) ===
mfplat: mfplat.c:495: Test succeeded inside todo block: Failed to get presentation descriptor, hr 0. mfplat.c:508: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:511: Test failed: Unexpected major type {0063fb28-fb20-0063-0400-000020fb6300}. mfplat.c:514: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on read access to 0x00000004 in 32-bit code (0x0040eb38).
Report validation errors: mfplat:mfplat crashed (c0000005)
=== debiant (32 bit Chinese:China report) ===
mfplat: mfplat.c:495: Test succeeded inside todo block: Failed to get presentation descriptor, hr 0. mfplat.c:508: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:511: Test failed: Unexpected major type {0063fb28-fb20-0063-0400-000020fb6300}. mfplat.c:514: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on read access to 0x00000004 in 32-bit code (0x0040eb38).
=== debiant (32 bit WoW report) ===
mfplat: mfplat.c:495: Test succeeded inside todo block: Failed to get presentation descriptor, hr 0. mfplat.c:508: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:511: Test failed: Unexpected major type {0063fb28-fb20-0063-0400-000020fb6300}. mfplat.c:514: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on read access to 0x00000004 in 32-bit code (0x0040eb38).
=== debiant (64 bit WoW report) ===
mfplat: mfplat.c:495: Test succeeded inside todo block: Failed to get presentation descriptor, hr 0. mfplat.c:508: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:511: Test failed: Unexpected major type {0063fb28-fb20-0063-0400-000020fb6300}. mfplat.c:514: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on read access to 0x00000004 in 32-bit code (0x0040eb38).
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 | 224 +++++++++++++++++++++++++++++- 3 files changed, 292 insertions(+), 2 deletions(-)
diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c index bf7103b160..141ab3c611 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.query_function_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 4725f23ad1..10e999feea 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 d6895b0513..c0d35df6fa 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, @@ -189,13 +208,20 @@ static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface)
FIXME("(%p): stub\n", This);
+ if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return E_NOTIMPL; }
static HRESULT media_source_teardown(struct media_source *This) { + if (This->my_src) + gst_object_unref(GST_OBJECT(This->my_src)); if (This->event_queue) IMFMediaEventQueue_Release(This->event_queue); + if (This->byte_stream) + IMFByteStream_Release(This->byte_stream);
return S_OK; } @@ -227,19 +253,176 @@ static const IMFMediaSourceVtbl IMFMediaSource_vtbl = media_source_Shutdown, };
+GstFlowReturn pull_from_bytestream(GstPad *pad, GstObject *parent, guint64 ofs, guint len, + GstBuffer **buf) +{ + struct media_source *This = gst_pad_get_element_private(pad); + IMFByteStream *byte_stream = This->byte_stream; + BOOL is_eof; + GstMapInfo info; + ULONG bytes_read; + HRESULT hr; + + TRACE("gstreamer requesting %u bytes at %s from source %p into buffer %p\n", len, wine_dbgstr_longlong(ofs), This, 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; + + *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 *This = gst_pad_get_element_private(pad); + GstFormat format; + QWORD bytestream_len; + + TRACE("GStreamer queries source %p for %s\n", This, GST_QUERY_TYPE_NAME(query)); + + if (FAILED(IMFByteStream_GetLength(This->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; + } + 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[This->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 *This = gst_pad_get_element_private(pad); + + TRACE("filter %p, type "%s".\n", This, GST_EVENT_TYPE_NAME(event)); + + switch (event->type) { + 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 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 *This = heap_alloc_zero(sizeof(*This)); HRESULT hr;
if (!This) return E_OUTOFMEMORY;
+ This->type = type; + if (FAILED(hr = MFCreateEventQueue(&This->event_queue))) goto fail;
This->state = SOURCE_STOPPED;
+ if (FAILED(hr = IMFByteStream_QueryInterface(bytestream, &IID_IMFByteStream, (void **)&This->byte_stream))) + { + goto fail; + } + + This->my_src = gst_pad_new_from_static_template(&src_template, "mf-src"); + gst_pad_set_element_private(This->my_src, This); + gst_pad_set_getrange_function(This->my_src, pull_from_bytestream_wrapper); + gst_pad_set_query_function(This->my_src, query_bytestream_wrapper); + gst_pad_set_activatemode_function(This->my_src, activate_bytestream_pad_mode_wrapper); + gst_pad_set_event_function(This->my_src, process_bytestream_pad_event_wrapper); + This->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl; This->ref = 1;
@@ -251,6 +434,7 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
media_source_teardown(This); heap_free(This); + *out_media_source = NULL; return hr; }
@@ -714,4 +898,42 @@ HRESULT container_stream_handler_construct(REFIID riid, void **obj, enum source_ IMFByteStreamHandler_Release(&this->IMFByteStreamHandler_iface);
return hr; -} \ No newline at end of file +} + +/* 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; + } + } +}
We can add a path later which uses decodebin as the demuxer, for use with formats not supported on windows. (like Theora)
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/gst_cbs.c | 15 ++++- dlls/winegstreamer/gst_cbs.h | 2 + dlls/winegstreamer/media_source.c | 101 +++++++++++++++++++++++++++++- 3 files changed, 115 insertions(+), 3 deletions(-)
diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c index 141ab3c611..51b017ded6 100644 --- a/dlls/winegstreamer/gst_cbs.c +++ b/dlls/winegstreamer/gst_cbs.c @@ -358,4 +358,17 @@ 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; +} diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h index 10e999feea..0d7acaf0b8 100644 --- a/dlls/winegstreamer/gst_cbs.h +++ b/dlls/winegstreamer/gst_cbs.h @@ -48,6 +48,7 @@ enum CB_TYPE { QUERY_BYTESTREAM, ACTIVATE_BYTESTREAM_PAD_MODE, PROCESS_BYTESTREAM_PAD_EVENT, + WATCH_SOURCE_BUS, MEDIA_SOURCE_MAX, };
@@ -164,5 +165,6 @@ 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;
#endif diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index c0d35df6fa..d3ed74876e 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -38,7 +38,12 @@ 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 *demuxer; + GstPad *my_src, *their_sink; enum { SOURCE_OPENING, @@ -218,6 +223,13 @@ static HRESULT media_source_teardown(struct media_source *This) { if (This->my_src) gst_object_unref(GST_OBJECT(This->my_src)); + if (This->their_sink) + gst_object_unref(GST_OBJECT(This->their_sink)); + if (This->container) + { + gst_element_set_state(This->container, GST_STATE_NULL); + gst_object_unref(GST_OBJECT(This->container)); + } if (This->event_queue) IMFMediaEventQueue_Release(This->event_queue); if (This->byte_stream) @@ -390,6 +402,37 @@ 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_stream *This = (struct media_stream *) user; + GError *err = NULL; + gchar *dbg_info = NULL; + + TRACE("source %p message type %s\n", This, 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_DROP; +} + static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_type type, struct media_source **out_media_source) { GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE( @@ -399,6 +442,9 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t source_descs[type].bytestream_caps);
struct media_source *This = heap_alloc_zero(sizeof(*This)); + GList *demuxer_list_one, *demuxer_list_two; + GstElementFactory *demuxer_factory = NULL; + int ret; HRESULT hr;
if (!This) @@ -409,7 +455,25 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t if (FAILED(hr = MFCreateEventQueue(&This->event_queue))) goto fail;
- This->state = SOURCE_STOPPED; + /* Find demuxer */ + demuxer_list_one = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DEMUXER, 1); + + demuxer_list_two = gst_element_factory_list_filter(demuxer_list_one, gst_static_caps_get(&source_descs[type].bytestream_caps), GST_PAD_SINK, 0); + gst_plugin_feature_list_free(demuxer_list_one); + + if (!(g_list_length(demuxer_list_two))) + { + ERR("Failed to find demuxer for source.\n"); + gst_plugin_feature_list_free(demuxer_list_two); + hr = E_FAIL; + goto fail; + } + + demuxer_factory = g_list_first(demuxer_list_two)->data; + gst_object_ref(demuxer_factory); + gst_plugin_feature_list_free(demuxer_list_two); + + TRACE("Found demuxer %s.\n", GST_ELEMENT_NAME(demuxer_factory));
if (FAILED(hr = IMFByteStream_QueryInterface(bytestream, &IID_IMFByteStream, (void **)&This->byte_stream))) { @@ -423,6 +487,31 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t gst_pad_set_activatemode_function(This->my_src, activate_bytestream_pad_mode_wrapper); gst_pad_set_event_function(This->my_src, process_bytestream_pad_event_wrapper);
+ This->container = gst_bin_new(NULL); + This->bus = gst_bus_new(); + gst_bus_set_sync_handler(This->bus, watch_source_bus_wrapper, This, NULL); + gst_element_set_bus(This->container, This->bus); + + This->demuxer = gst_element_factory_create(demuxer_factory, NULL); + if (!(This->demuxer)) + { + WARN("Failed to create demuxer for source\n"); + hr = E_OUTOFMEMORY; + goto fail; + } + gst_bin_add(GST_BIN(This->container), This->demuxer); + + This->their_sink = gst_element_get_static_pad(This->demuxer, "sink"); + + if ((ret = gst_pad_link(This->my_src, This->their_sink)) < 0) + { + WARN("Failed to link our bytestream pad to the demuxer input\n"); + hr = E_OUTOFMEMORY; + goto fail; + } + + This->state = SOURCE_STOPPED; + This->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl; This->ref = 1;
@@ -432,6 +521,8 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t fail: WARN("Failed to construct MFMediaSource, hr %#x.\n", hr);
+ if (demuxer_factory) + gst_object_unref(demuxer_factory); media_source_teardown(This); heap_free(This); *out_media_source = NULL; @@ -930,6 +1021,12 @@ 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; + } default: { ERR("Wrong callback forwarder called\n");
On 4/6/20 12:57 PM, Derek Lesho wrote:
We can add a path later which uses decodebin as the demuxer, for use with formats not supported on windows. (like Theora)
I still don't see a good reason why this can't be the general solution for all formats.
Signed-off-by: Derek Lesho dlesho@codeweavers.com
dlls/winegstreamer/gst_cbs.c | 15 ++++- dlls/winegstreamer/gst_cbs.h | 2 + dlls/winegstreamer/media_source.c | 101 +++++++++++++++++++++++++++++- 3 files changed, 115 insertions(+), 3 deletions(-)
diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c index 141ab3c611..51b017ded6 100644 --- a/dlls/winegstreamer/gst_cbs.c +++ b/dlls/winegstreamer/gst_cbs.c @@ -358,4 +358,17 @@ 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;
+} diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h index 10e999feea..0d7acaf0b8 100644 --- a/dlls/winegstreamer/gst_cbs.h +++ b/dlls/winegstreamer/gst_cbs.h @@ -48,6 +48,7 @@ enum CB_TYPE { QUERY_BYTESTREAM, ACTIVATE_BYTESTREAM_PAD_MODE, PROCESS_BYTESTREAM_PAD_EVENT,
- WATCH_SOURCE_BUS, MEDIA_SOURCE_MAX,
};
@@ -164,5 +165,6 @@ 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;
#endif diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index c0d35df6fa..d3ed74876e 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -38,7 +38,12 @@ 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 *demuxer;
- GstPad *my_src, *their_sink; enum { SOURCE_OPENING,
@@ -218,6 +223,13 @@ static HRESULT media_source_teardown(struct media_source *This) { if (This->my_src) gst_object_unref(GST_OBJECT(This->my_src));
- if (This->their_sink)
gst_object_unref(GST_OBJECT(This->their_sink));
- if (This->container)
- {
gst_element_set_state(This->container, GST_STATE_NULL);
gst_object_unref(GST_OBJECT(This->container));
- } if (This->event_queue) IMFMediaEventQueue_Release(This->event_queue); if (This->byte_stream)
@@ -390,6 +402,37 @@ 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_stream *This = (struct media_stream *) user;
- GError *err = NULL;
- gchar *dbg_info = NULL;
- TRACE("source %p message type %s\n", This, 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_DROP;
+}
static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_type type, struct media_source **out_media_source) { GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE( @@ -399,6 +442,9 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t source_descs[type].bytestream_caps);
struct media_source *This = heap_alloc_zero(sizeof(*This));
GList *demuxer_list_one, *demuxer_list_two;
GstElementFactory *demuxer_factory = NULL;
int ret; HRESULT hr;
if (!This)
@@ -409,7 +455,25 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t if (FAILED(hr = MFCreateEventQueue(&This->event_queue))) goto fail;
- This->state = SOURCE_STOPPED;
/* Find demuxer */
demuxer_list_one = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DEMUXER, 1);
demuxer_list_two = gst_element_factory_list_filter(demuxer_list_one, gst_static_caps_get(&source_descs[type].bytestream_caps), GST_PAD_SINK, 0);
gst_plugin_feature_list_free(demuxer_list_one);
if (!(g_list_length(demuxer_list_two)))
{
ERR("Failed to find demuxer for source.\n");
gst_plugin_feature_list_free(demuxer_list_two);
hr = E_FAIL;
goto fail;
}
demuxer_factory = g_list_first(demuxer_list_two)->data;
gst_object_ref(demuxer_factory);
gst_plugin_feature_list_free(demuxer_list_two);
TRACE("Found demuxer %s.\n", GST_ELEMENT_NAME(demuxer_factory));
if (FAILED(hr = IMFByteStream_QueryInterface(bytestream, &IID_IMFByteStream, (void **)&This->byte_stream))) {
@@ -423,6 +487,31 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t gst_pad_set_activatemode_function(This->my_src, activate_bytestream_pad_mode_wrapper); gst_pad_set_event_function(This->my_src, process_bytestream_pad_event_wrapper);
- This->container = gst_bin_new(NULL);
- This->bus = gst_bus_new();
- gst_bus_set_sync_handler(This->bus, watch_source_bus_wrapper, This, NULL);
- gst_element_set_bus(This->container, This->bus);
- This->demuxer = gst_element_factory_create(demuxer_factory, NULL);
- if (!(This->demuxer))
- {
WARN("Failed to create demuxer for source\n");
hr = E_OUTOFMEMORY;
goto fail;
- }
- gst_bin_add(GST_BIN(This->container), This->demuxer);
- This->their_sink = gst_element_get_static_pad(This->demuxer, "sink");
- if ((ret = gst_pad_link(This->my_src, This->their_sink)) < 0)
- {
WARN("Failed to link our bytestream pad to the demuxer input\n");
hr = E_OUTOFMEMORY;
goto fail;
- }
- This->state = SOURCE_STOPPED;
- This->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl; This->ref = 1;
@@ -432,6 +521,8 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t fail: WARN("Failed to construct MFMediaSource, hr %#x.\n", hr);
- if (demuxer_factory)
media_source_teardown(This); heap_free(This); *out_media_source = NULL;gst_object_unref(demuxer_factory);
@@ -930,6 +1021,12 @@ 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;
default: { ERR("Wrong callback forwarder called\n");}
On 4/6/20 2:02 PM, Zebediah Figura wrote:
On 4/6/20 12:57 PM, Derek Lesho wrote:
We can add a path later which uses decodebin as the demuxer, for use with formats not supported on windows. (like Theora)
I still don't see a good reason why this can't be the general solution for all formats.
We've already extensively discussed this, and it seems mostly to be a matter of preference. From my view, it doesn't match windows, and I'd prefer to keep the more accurate path as default. Would you like me to add the decodebin fallback before submitting or something?
On 4/6/20 4:20 PM, Derek Lesho wrote:
On 4/6/20 2:02 PM, Zebediah Figura wrote:
On 4/6/20 12:57 PM, Derek Lesho wrote:
We can add a path later which uses decodebin as the demuxer, for use with formats not supported on windows. (like Theora)
I still don't see a good reason why this can't be the general solution for all formats.
We've already extensively discussed this, and it seems mostly to be a matter of preference. From my view, it doesn't match windows, and I'd prefer to keep the more accurate path as default. Would you like me to add the decodebin fallback before submitting or something?
I don't think it's a matter of preference at all, or at least, not such that both options are equally valid.
I think the attempt to zealously match Windows implementation details as a top priority is misguided; that's never been the top priority of Wine. As I understand, we should match implementation details where there's no reason not to (e.g. using the same internal registry keys Windows uses) or where applications require it. Hence, if we have a good reason to do otherwise, and applications don't care, then I don't think we should feel bound to match Windows implementation details.
Accordingly, what I'd like to see in terms of patches would be to first fully implement a media source built on decodebin without worrying about anything else. If applications are manually creating e.g. the MPEG-4 source, we can essentially redirect that to the same COM object. Only if we ever find an application that depends on nontrivial details of GetNativeMediaType() would we then bother replicating those details in winegstreamer.
Since there's at least consensus that a decodebin source is desirable, and it's the bulk of the code anyway, I think it'd be a good idea to start with that.
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/gst_cbs.c | 44 +++ dlls/winegstreamer/gst_cbs.h | 13 + dlls/winegstreamer/media_source.c | 440 +++++++++++++++++++++++++++++- 3 files changed, 496 insertions(+), 1 deletion(-)
diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c index 51b017ded6..5038ea3397 100644 --- a/dlls/winegstreamer/gst_cbs.c +++ b/dlls/winegstreamer/gst_cbs.c @@ -372,3 +372,47 @@ GstBusSyncReply watch_source_bus_wrapper(GstBus *bus, GstMessage *message, gpoin
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); +} + +GstFlowReturn stream_new_sample_wrapper(GstElement *appsink, gpointer user) +{ + struct cb_data cbdata = { STREAM_NEW_SAMPLE }; + + cbdata.u.new_sample_data.appsink = appsink; + cbdata.u.new_sample_data.user = user; + + call_cb(&cbdata); + + return cbdata.u.new_sample_data.ret; +} diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h index 0d7acaf0b8..106368a064 100644 --- a/dlls/winegstreamer/gst_cbs.h +++ b/dlls/winegstreamer/gst_cbs.h @@ -49,6 +49,10 @@ enum CB_TYPE { ACTIVATE_BYTESTREAM_PAD_MODE, PROCESS_BYTESTREAM_PAD_EVENT, WATCH_SOURCE_BUS, + SOURCE_STREAM_ADDED, + SOURCE_STREAM_REMOVED, + SOURCE_ALL_STREAMS, + STREAM_NEW_SAMPLE, MEDIA_SOURCE_MAX, };
@@ -134,6 +138,11 @@ struct cb_data { GstQuery *query; gboolean ret; } query_sink_data; + struct new_sample_data { + GstElement *appsink; + gpointer user; + GstFlowReturn ret; + } new_sample_data; } u;
int finished; @@ -166,5 +175,9 @@ gboolean query_bytestream_wrapper(GstPad *pad, GstObject *parent, GstQuery *quer 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; +GstFlowReturn stream_new_sample_wrapper(GstElement *appsink, gpointer user) DECLSPEC_HIDDEN;
#endif diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index d3ed74876e..1dc3b40b65 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,28 @@ static struct source_desc } };
+struct media_source; + +struct media_stream +{ + IMFMediaStream IMFMediaStream_iface; + LONG ref; + struct media_source *parent_source; + IMFMediaEventQueue *event_queue; + IMFStreamDescriptor *descriptor; + GstElement *appsink; + GstPad *their_src, *my_sink; + /* usually reflects state of source */ + enum + { + STREAM_INACTIVE, + STREAM_ENABLED, + STREAM_PAUSED, + STREAM_RUNNING, + STREAM_SHUTDOWN, + } state; +}; + struct media_source { IMFMediaSource IMFMediaSource_iface; @@ -52,8 +75,252 @@ struct media_source SOURCE_RUNNING, SOURCE_SHUTDOWN, } state; + CRITICAL_SECTION streams_cs; + HANDLE init_complete_event; };
+/* stream */ + +static inline struct media_stream *impl_from_IMFMediaStream(IMFMediaStream *iface) +{ + return CONTAINING_RECORD(iface, struct media_stream, IMFMediaStream_iface); +} + +static HRESULT WINAPI media_stream_QueryInterface(IMFMediaStream *iface, REFIID riid, void **out) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), out); + + if (IsEqualIID(riid, &IID_IMFMediaStream) || + IsEqualIID(riid, &IID_IMFMediaEventGenerator) || + IsEqualIID(riid, &IID_IUnknown)) + { + *out = &This->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 *This = impl_from_IMFMediaStream(iface); + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%u\n", This, ref); + + return ref; +} + +static ULONG WINAPI media_stream_Release(IMFMediaStream *iface) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%u\n", This, ref); + + if (!ref) + { + if (This->state != STREAM_SHUTDOWN) + ERR("incomplete cleanup\n"); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI media_stream_GetEvent(IMFMediaStream *iface, DWORD flags, IMFMediaEvent **event) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%#x, %p)\n", This, flags, event); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_GetEvent(This->event_queue, flags, event); +} + +static HRESULT WINAPI media_stream_BeginGetEvent(IMFMediaStream *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%p, %p)\n", This, callback, state); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_BeginGetEvent(This->event_queue, callback, state); +} + +static HRESULT WINAPI media_stream_EndGetEvent(IMFMediaStream *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%p, %p)\n", This, result, event); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_EndGetEvent(This->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 *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%d, %s, %#x, %p)\n", This, event_type, debugstr_guid(ext_type), hr, value); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_QueueEventParamVar(This->event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI media_stream_GetMediaSource(IMFMediaStream *iface, IMFMediaSource **source) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + FIXME("stub (%p)->(%p)\n", This, source); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IMFStreamDescriptor **descriptor) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%p)\n", This, descriptor); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%p)\n", iface, token); + + if (This->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 +}; + +static GstFlowReturn stream_new_sample(GstElement *appsink, gpointer user) +{ + struct media_stream *This = (struct media_stream *) user; + GstSample *discard_sample; + + TRACE("(%p) got sample\n", This); + + g_signal_emit_by_name(This->appsink, "pull-sample", &discard_sample); + gst_sample_unref(discard_sample); + return GST_FLOW_OK; +} + +static void media_stream_teardown(struct media_stream *This) +{ + TRACE("(%p)\n", This); + + This->state = STREAM_SHUTDOWN; + + if (This->their_src) + gst_object_unref(GST_OBJECT(This->their_src)); + if (This->my_sink) + gst_object_unref(GST_OBJECT(This->my_sink)); + if (This->descriptor) + IMFStreamDescriptor_Release(This->descriptor); + if (This->event_queue) + IMFMediaEventQueue_Release(This->event_queue); + if (This->parent_source) + IMFMediaSource_Release(&This->parent_source->IMFMediaSource_iface); +} + +static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad, DWORD stream_id, struct media_stream **out_stream) +{ + HRESULT hr; + struct media_stream *This = heap_alloc_zero(sizeof(*This)); + + TRACE("(%p %p)->(%p)\n", source, pad, out_stream); + + This->state = STREAM_INACTIVE; + + if (FAILED(hr = IMFMediaSource_AddRef(&source->IMFMediaSource_iface))) + { + goto fail; + } + This->parent_source = source; + + if (FAILED(hr = MFCreateEventQueue(&This->event_queue))) + { + goto fail; + } + + if (!(This->appsink = gst_element_factory_make("appsink", NULL))) + { + hr = E_OUTOFMEMORY; + goto fail; + } + gst_bin_add(GST_BIN(This->parent_source->container), This->appsink); + + g_object_set(This->appsink, "emit-signals", TRUE, NULL); + g_object_set(This->appsink, "sync", FALSE, NULL); + g_signal_connect(This->appsink, "new-sample", G_CALLBACK(stream_new_sample_wrapper), This); + + This->their_src = pad; + This->my_sink = gst_element_get_static_pad(This->appsink, "sink"); + gst_pad_set_element_private(pad, This); + + gst_pad_link(This->their_src, This->my_sink); + + gst_element_sync_state_with_parent(This->appsink); + + This->IMFMediaStream_iface.lpVtbl = &media_stream_vtbl; + This->ref = 1; + + TRACE("->(%p)\n", This); + + *out_stream = This; + return S_OK; + + fail: + WARN("Failed to construct media stream, hr %#x.\n", hr); + + media_stream_teardown(This); + heap_free(This); + return hr; +} + /* source */
static inline struct media_source *impl_from_IMFMediaSource(IMFMediaSource *iface) @@ -235,6 +502,19 @@ static HRESULT media_source_teardown(struct media_source *This) if (This->byte_stream) IMFByteStream_Release(This->byte_stream);
+ for (unsigned int i = 0; i < This->stream_count; i++) + { + media_stream_teardown(This->streams[i]); + IMFMediaStream_Release(&This->streams[i]->IMFMediaStream_iface); + } + + if (This->stream_count) + heap_free(This->streams); + + if (This->init_complete_event) + CloseHandle(This->init_complete_event); + DeleteCriticalSection(&This->streams_cs); + return S_OK; }
@@ -433,6 +713,115 @@ GstBusSyncReply watch_source_bus(GstBus *bus, GstMessage *message, gpointer user return GST_BUS_DROP; }
+static void source_stream_added(GstElement *element, GstPad *pad, gpointer user) +{ + struct media_stream *stream; + struct media_source *source = (struct media_source *) user; + struct media_stream **new_stream_array; + gchar *g_stream_id; + const char *stream_id_string; + DWORD stream_id; + + EnterCriticalSection(&source->streams_cs); + + g_stream_id = gst_pad_get_stream_id(pad); + stream_id_string = strstr(g_stream_id, "/"); + sscanf(stream_id_string, "/%03u", &stream_id); + TRACE("stream-id: %u\n", stream_id); + g_free(g_stream_id); + + /* find existing stream */ + for (unsigned int i = 0; i < source->stream_count; i++) + { + DWORD existing_stream_id; + IMFStreamDescriptor *descriptor = source->streams[i]->descriptor; + + 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); + + if (!existing_stream->my_sink) + { + ERR("Couldn't find our sink\n"); + goto leave; + } + + existing_stream->their_src = pad; + gst_pad_set_element_private(pad, existing_stream); + + if ((ret = gst_pad_link(existing_stream->their_src, existing_stream->my_sink)) != GST_PAD_LINK_OK) + { + ERR("Error linking demuxer to stream %p, err = %d\n", existing_stream, ret); + } + gst_element_sync_state_with_parent(existing_stream->appsink); + + goto leave; + } + } + + if (FAILED(media_stream_constructor(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: + LeaveCriticalSection(&source->streams_cs); + return; +} + +static void source_stream_removed(GstElement *element, GstPad *pad, gpointer user) +{ + struct media_stream *stream; + + if (gst_pad_get_direction(pad) != GST_PAD_SRC) + { + return; + } + + stream = (struct media_stream *) gst_pad_get_element_private(pad); + + if (stream) + { + TRACE("Stream %p of Source %p removed\n", stream, stream->parent_source); + + assert (stream->their_src == pad); + + gst_pad_unlink(stream->their_src, stream->my_sink); + + stream->their_src = NULL; + gst_pad_set_element_private(pad, NULL); + } +} + +static void source_all_streams(GstElement *element, gpointer user) +{ + struct media_source *source = (struct media_source *) user; + + EnterCriticalSection(&source->streams_cs); + if (source->state != SOURCE_OPENING) + goto leave; + + SetEvent(source->init_complete_event); + + leave: + LeaveCriticalSection(&source->streams_cs); +} + static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_type type, struct media_source **out_media_source) { GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE( @@ -510,11 +899,36 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t goto fail; }
- This->state = SOURCE_STOPPED; + g_signal_connect(This->demuxer, "pad-added", G_CALLBACK(source_stream_added_wrapper), This); + g_signal_connect(This->demuxer, "pad-removed", G_CALLBACK(source_stream_removed_wrapper), This); + g_signal_connect(This->demuxer, "no-more-pads", G_CALLBACK(source_all_streams_wrapper), This); + + InitializeCriticalSection(&This->streams_cs); + This->init_complete_event = CreateEventA(NULL, TRUE, FALSE, NULL);
+ This->state = SOURCE_OPENING; + + /* Setup interface early as the streams interact with us during initialization */ This->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl; This->ref = 1;
+ gst_element_set_state(This->container, GST_STATE_PAUSED); + ret = gst_element_get_state(This->container, NULL, NULL, -1); + if (ret == GST_STATE_CHANGE_FAILURE) + { + ERR("Failed to play source.\n"); + hr = E_OUTOFMEMORY; + goto fail; + } + + WaitForSingleObject(This->init_complete_event, INFINITE); + CloseHandle(This->init_complete_event); + This->init_complete_event = NULL; + + gst_element_set_state(This->container, GST_STATE_READY); + + This->state = SOURCE_STOPPED; + *out_media_source = This; return S_OK;
@@ -1027,6 +1441,30 @@ void perform_cb_media_source(struct cb_data *cbdata) 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; + } + case STREAM_NEW_SAMPLE: + { + struct new_sample_data *data = &cbdata->u.new_sample_data; + cbdata->u.new_sample_data.ret = stream_new_sample(data->appsink, data->user); + break; + } default: { ERR("Wrong callback forwarder called\n");
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=69026
Your paranoid android.
=== debiant (32 bit report) ===
mfplat: mfplat.c:495: Test succeeded inside todo block: Failed to get presentation descriptor, hr 0. mfplat.c:508: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:511: Test failed: Unexpected major type {0063fb28-fb20-0063-0400-000020fb6300}. mfplat.c:514: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on read access to 0x00000004 in 32-bit code (0x0040eb38).
=== debiant (32 bit French report) ===
mfplat: mfplat.c:495: Test succeeded inside todo block: Failed to get presentation descriptor, hr 0. mfplat.c:508: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:511: Test failed: Unexpected major type {0063fb28-fb20-0063-0400-000020fb6300}. mfplat.c:514: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on read access to 0x00000004 in 32-bit code (0x0040eb38).
Report validation errors: mfplat:mfplat crashed (c0000005)
=== debiant (32 bit Japanese:Japan report) ===
mfplat: mfplat.c:495: Test succeeded inside todo block: Failed to get presentation descriptor, hr 0. mfplat.c:508: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:511: Test failed: Unexpected major type {0063fb28-fb20-0063-0400-000020fb6300}. mfplat.c:514: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on read access to 0x00000004 in 32-bit code (0x0040eb38).
Report validation errors: mfplat:mfplat crashed (c0000005)
=== debiant (32 bit Chinese:China report) ===
mfplat: mfplat.c:495: Test succeeded inside todo block: Failed to get presentation descriptor, hr 0. mfplat.c:508: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:511: Test failed: Unexpected major type {0063fb28-fb20-0063-0400-000020fb6300}. mfplat.c:514: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on read access to 0x00000004 in 32-bit code (0x0040eb38).
=== debiant (32 bit WoW report) ===
mfplat: mfplat.c:495: Test succeeded inside todo block: Failed to get presentation descriptor, hr 0. mfplat.c:508: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:511: Test failed: Unexpected major type {0063fb28-fb20-0063-0400-000020fb6300}. mfplat.c:514: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on read access to 0x00000004 in 32-bit code (0x0040eb38).
=== debiant (64 bit WoW report) ===
mfplat: mfplat.c:495: Test succeeded inside todo block: Failed to get presentation descriptor, hr 0. mfplat.c:508: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:511: Test failed: Unexpected major type {0063fb28-fb20-0063-0400-000020fb6300}. mfplat.c:514: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on read access to 0x00000004 in 32-bit code (0x0040eb38).