Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/mfplat/tests/mfplat.c | 9 +- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_private.h | 6 + dlls/winegstreamer/media_source.c | 679 +++++++++++++++++++ dlls/winegstreamer/mfplat.c | 9 + dlls/winegstreamer/winegstreamer.rgs | 32 + dlls/winegstreamer/winegstreamer_classes.idl | 7 + include/mfidl.idl | 1 + 8 files changed, 739 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..13a8db9d16 --- /dev/null +++ b/dlls/winegstreamer/media_source.c @@ -0,0 +1,679 @@ +#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); + IMFAsyncResult_Release(caller); + if (SUCCEEDED(hr)) + { + if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item))) + { + if (cancel_cookie) + IMFAsyncResult_GetState(item, cancel_cookie); + } + + IMFAsyncResult_Release(item); + } + + 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 13a8db9d16..bb6eb14b77 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=68738
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).
=== 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 bb6eb14b77..4d2aea54e1 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; }
@@ -711,4 +895,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 4d2aea54e1..d6380f059e 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; @@ -927,6 +1018,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");
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 d6380f059e..ae9880847c 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;
@@ -1024,6 +1438,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");
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/gst_private.h | 4 + dlls/winegstreamer/media_source.c | 54 ++++++++- dlls/winegstreamer/mfplat.c | 186 ++++++++++++++++++++++++++++++ 3 files changed, 243 insertions(+), 1 deletion(-)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 71ca429088..780cf1b02f 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 ae9880847c..080d2c43ca 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -207,7 +207,10 @@ static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IM if (This->state == STREAM_SHUTDOWN) return MF_E_SHUTDOWN;
- return E_NOTIMPL; + IMFStreamDescriptor_AddRef(This->descriptor); + *descriptor = This->descriptor; + + return S_OK; }
static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) @@ -269,6 +272,9 @@ static void media_stream_teardown(struct media_stream *This) static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad, DWORD stream_id, struct media_stream **out_stream) { HRESULT hr; + GstCaps *caps = NULL, *compatible_caps = NULL; + IMFMediaType *media_type; + IMFMediaTypeHandler *type_handler; struct media_stream *This = heap_alloc_zero(sizeof(*This));
TRACE("(%p %p)->(%p)\n", source, pad, out_stream); @@ -297,6 +303,44 @@ static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad g_object_set(This->appsink, "sync", FALSE, NULL); g_signal_connect(This->appsink, "new-sample", G_CALLBACK(stream_new_sample_wrapper), This);
+ if (FAILED(hr = MFCreateMediaType(&media_type))) + { + goto fail; + } + + caps = gst_pad_query_caps(pad, NULL); + + if (!(caps)) + { + goto fail; + } + + compatible_caps = make_mf_compatible_caps(caps); + media_type = mf_media_type_from_caps(compatible_caps); + + if (!media_type) + goto fail; + + MFCreateStreamDescriptor(stream_id, 1, &media_type, &This->descriptor); + + IMFStreamDescriptor_GetMediaTypeHandler(This->descriptor, &type_handler); + IMFMediaTypeHandler_SetCurrentMediaType(type_handler, media_type); + IMFMediaTypeHandler_Release(type_handler); + IMFMediaType_Release(media_type); + media_type = NULL; + + if (TRACE_ON(mfplat)) + { + gchar *caps_str = gst_caps_to_string(caps), *compatible_caps_str = gst_caps_to_string(compatible_caps); + TRACE("caps %s compatible caps %s\n", debugstr_a(caps_str), debugstr_a(compatible_caps_str)); + g_free(caps_str); + g_free(compatible_caps_str); + } + gst_caps_unref(caps); + caps = NULL; + gst_caps_unref(compatible_caps); + compatible_caps = NULL; + This->their_src = pad; This->my_sink = gst_element_get_static_pad(This->appsink, "sink"); gst_pad_set_element_private(pad, This); @@ -316,6 +360,14 @@ static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad fail: WARN("Failed to construct media stream, hr %#x.\n", hr);
+ /* Destroy temporary objects */ + if (caps) + gst_caps_unref(caps); + if (compatible_caps) + gst_caps_unref(compatible_caps); + if (media_type) + IMFMediaType_Release(media_type); + media_stream_teardown(This); heap_free(This); return hr; diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 16e55247de..09266ffab4 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,184 @@ 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); + } + else if (!(strncmp(mime_type, "audio", 5))) + { + IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio); + + 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); + } + 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
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/mfplat.c | 67 +++++++++++++++++++++++++++++++++++++ include/codecapi.h | 38 +++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 include/codecapi.h
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 09266ffab4..b76714c482 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -26,6 +26,7 @@ #include "gst_private.h" #include "mfapi.h" #include "mfidl.h" +#include "codecapi.h"
#include "wine/debug.h" #include "wine/heap.h" @@ -525,6 +526,72 @@ static IMFMediaType* transform_to_media_type(GstCaps *caps) FIXME("Unrecognized format.\n"); } } + else if (!(strcmp(mime_type, "video/x-h264"))) + { + const char *profile, *level; + + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_H264); + IMFMediaType_SetUINT32(media_type, &MF_MT_COMPRESSED, TRUE); + + if ((profile = gst_structure_get_string(info, "profile"))) + { + if (!(strcmp(profile, "main"))) + IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_PROFILE, eAVEncH264VProfile_Main); + else if (!(strcmp(profile, "high"))) + IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_PROFILE, eAVEncH264VProfile_High); + else if (!(strcmp(profile, "high-4:4:4"))) + IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_PROFILE, eAVEncH264VProfile_444); + else + FIXME("Unrecognized profile %s\n", profile); + } + if ((level = gst_structure_get_string(info, "level"))) + { + unsigned int i; + + const static struct + { + const char *name; + enum eAVEncH264VLevel val; + } levels[] = + { + {"1", eAVEncH264VLevel1}, + {"1.1", eAVEncH264VLevel1_1}, + {"1.2", eAVEncH264VLevel1_2}, + {"1.3", eAVEncH264VLevel1_3}, + {"2", eAVEncH264VLevel2}, + {"2.1", eAVEncH264VLevel2_1}, + {"2.2", eAVEncH264VLevel2_2}, + {"3", eAVEncH264VLevel3}, + {"3.1", eAVEncH264VLevel3_1}, + {"3.2", eAVEncH264VLevel3_2}, + {"4", eAVEncH264VLevel4}, + {"4.1", eAVEncH264VLevel4_1}, + {"4.2", eAVEncH264VLevel4_2}, + {"5", eAVEncH264VLevel5}, + {"5.1", eAVEncH264VLevel5_1}, + {"5.2", eAVEncH264VLevel5_2}, + }; + for (i = 0 ; i < ARRAY_SIZE(levels); i++) + { + if (!(strcmp(level, levels[i].name))) + { + IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_LEVEL, levels[i].val); + break; + } + } + if (i == ARRAY_SIZE(levels)) + { + FIXME("Unrecognized level %s", level); + } + } + gst_caps_set_simple(caps, "stream-format", G_TYPE_STRING, "byte-stream", NULL); + gst_caps_set_simple(caps, "alignment", G_TYPE_STRING, "au", NULL); + for (unsigned int i = 0; i < gst_caps_get_size(caps); i++) + { + GstStructure *structure = gst_caps_get_structure (caps, i); + gst_structure_remove_field(structure, "codec_data"); + } + } else FIXME("Unrecognized video format %s\n", mime_type); } diff --git a/include/codecapi.h b/include/codecapi.h new file mode 100644 index 0000000000..307f40cb27 --- /dev/null +++ b/include/codecapi.h @@ -0,0 +1,38 @@ +#ifndef __WINE_CODECAPI_H +#define __WINE_CODECAPI_H + +enum eAVEncH264VProfile +{ + eAVEncH264VProfile_unknown = 0, + eAVEncH264VProfile_Simple = 66, + eAVEncH264VProfile_Base = 66, + eAVEncH264VProfile_Main = 77, + eAVEncH264VProfile_High = 100, + eAVEncH264VProfile_422 = 122, + eAVEncH264VProfile_High10 = 110, + eAVEncH264VProfile_444 = 244, + eAVEncH264VProfile_Extended = 88, +}; + +enum eAVEncH264VLevel +{ + eAVEncH264VLevel1 = 10, + eAVEncH264VLevel1_b = 11, + eAVEncH264VLevel1_1 = 11, + eAVEncH264VLevel1_2 = 12, + eAVEncH264VLevel1_3 = 13, + eAVEncH264VLevel2 = 20, + eAVEncH264VLevel2_1 = 21, + eAVEncH264VLevel2_2 = 22, + eAVEncH264VLevel3 = 30, + eAVEncH264VLevel3_1 = 31, + eAVEncH264VLevel3_2 = 32, + eAVEncH264VLevel4 = 40, + eAVEncH264VLevel4_1 = 41, + eAVEncH264VLevel4_2 = 42, + eAVEncH264VLevel5 = 50, + eAVEncH264VLevel5_1 = 51, + eAVEncH264VLevel5_2 = 52 +}; + +#endif \ 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=68743
Your paranoid android.
=== debiant (build log) ===
Task: Patch failed to apply
=== debiant (build log) ===
Task: Patch failed to apply
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/mfplat.c | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+)
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index b76714c482..1b0f562dbb 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -592,6 +592,50 @@ static IMFMediaType* transform_to_media_type(GstCaps *caps) gst_structure_remove_field(structure, "codec_data"); } } + else if (!(strcmp(mime_type, "video/x-wmv"))) + { + gint wmv_version; + const char *format; + const GValue *codec_data; + + if (gst_structure_get_int(info, "wmvversion", &wmv_version)) + { + switch (wmv_version) + { + case 1: + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_WMV1); + break; + case 2: + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_WMV2); + break; + case 3: + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_WMV3); + break; + default: + FIXME("Unrecognized wmvversion %d\n", wmv_version); + } + } + + if ((format = gst_structure_get_string(info, "format"))) + { + if (!(strcmp(format, "WVC1"))) + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_WVC1); + else + FIXME("Unrecognized format %s\n", format); + } + + if ((codec_data = gst_structure_get_value(info, "codec_data"))) + { + GstBuffer *codec_data_buffer = gst_value_get_buffer(codec_data); + if (codec_data_buffer) + { + gsize codec_data_size = gst_buffer_get_size(codec_data_buffer); + gpointer codec_data_raw = heap_alloc(codec_data_size); + gst_buffer_extract(codec_data_buffer, 0, codec_data_raw, codec_data_size); + IMFMediaType_SetBlob(media_type, &MF_MT_USER_DATA, codec_data_raw, codec_data_size); + } + } + } else FIXME("Unrecognized video format %s\n", mime_type); }
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=68744
Your paranoid android.
=== debiant (build log) ===
Task: Patch failed to apply
=== debiant (build log) ===
Task: Patch failed to apply
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/mfplat.c | 118 ++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+)
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 1b0f562dbb..4a97bf0c70 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -463,6 +463,15 @@ uncompressed_formats[] = {&MFVideoFormat_RGB555, GST_VIDEO_FORMAT_BGR15}, };
+struct aac_user_data +{ + WORD payload_type; + WORD profile_level_indication; + WORD struct_type; + WORD reserved; + /* audio-specific-config is stored here */ +}; + /* caps will be modified to represent the exact type needed for the format */ static IMFMediaType* transform_to_media_type(GstCaps *caps) { @@ -691,6 +700,115 @@ static IMFMediaType* transform_to_media_type(GstCaps *caps) ERR("Failed to get audio format\n"); } } + else if (!(strcmp(mime_type, "audio/mpeg"))) + { + int mpeg_version = -1; + + IMFMediaType_SetUINT32(media_type, &MF_MT_COMPRESSED, TRUE); + + if (!(gst_structure_get_int(info, "mpegversion", &mpeg_version))) + ERR("Failed to get mpegversion\n"); + switch (mpeg_version) + { + case 2: + case 4: + { + const char *format, *profile, *level; + DWORD profile_level_indication = 0; + const GValue *codec_data; + DWORD asc_size = 0; + struct aac_user_data *user_data = NULL; + + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_AAC); + + codec_data = gst_structure_get_value(info, "codec_data"); + if (codec_data) + { + GstBuffer *codec_data_buffer = gst_value_get_buffer(codec_data); + if (codec_data_buffer) + { + if ((asc_size = gst_buffer_get_size(codec_data_buffer)) >= 2) + { + user_data = heap_alloc_zero(sizeof(*user_data)+asc_size); + gst_buffer_extract(codec_data_buffer, 0, (gpointer)(user_data + 1), asc_size); + } + else + ERR("Unexpected buffer size\n"); + } + else + ERR("codec_data not a buffer\n"); + } + else + ERR("codec_data not found\n"); + if (!user_data) + user_data = heap_alloc_zero(sizeof(*user_data)); + + { + int rate; + if (gst_structure_get_int(info, "rate", &rate)) + IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, rate); + } + { + int channels; + if (gst_structure_get_int(info, "channels", &channels)) + IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_NUM_CHANNELS, channels); + } + + if ((format = gst_structure_get_string(info, "stream-format"))) + { + DWORD payload_type = -1; + if (!(strcmp(format, "raw"))) + payload_type = 0; + else if (!(strcmp(format, "adts"))) + payload_type = 1; + else if (!(strcmp(format, "adif"))) + payload_type = 2; + else if (!(strcmp(format, "loas"))) + payload_type = 3; + else + FIXME("Unrecognized stream-format\n"); + if (payload_type != -1) + { + IMFMediaType_SetUINT32(media_type, &MF_MT_AAC_PAYLOAD_TYPE, payload_type); + user_data->payload_type = payload_type; + } + } + else + { + ERR("Stream format not present\n"); + } + + profile = gst_structure_get_string(info, "profile"); + level = gst_structure_get_string(info, "level"); + /* Data from http://archive.is/whp6P#45% */ + if (profile && level) + { + if (!(strcmp(profile, "lc")) && !(strcmp(level, "2"))) + profile_level_indication = 0x29; + else if (!(strcmp(profile, "lc")) && !(strcmp(level, "4"))) + profile_level_indication = 0x2A; + else if (!(strcmp(profile, "lc")) && !(strcmp(level, "5"))) + profile_level_indication = 0x2B; + else + FIXME("Unhandled profile/level combo\n"); + } + else + ERR("Profile or level not present\n"); + + if (profile_level_indication) + { + IMFMediaType_SetUINT32(media_type, &MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, profile_level_indication); + user_data->profile_level_indication = profile_level_indication; + } + + IMFMediaType_SetBlob(media_type, &MF_MT_USER_DATA, (BYTE *)user_data, sizeof(user_data) + asc_size); + heap_free(user_data); + break; + } + default: + FIXME("Unhandled mpegversion %d\n", mpeg_version); + } + } else FIXME("Unrecognized audio format %s\n", mime_type); }
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=68745
Your paranoid android.
=== debiant (build log) ===
Task: Patch failed to apply
=== debiant (build log) ===
Task: Patch failed to apply
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/mfplat.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 4a97bf0c70..02912ac3b6 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -645,6 +645,21 @@ static IMFMediaType* transform_to_media_type(GstCaps *caps) } } } + else if (!(strcmp(mime_type, "video/mpeg"))) + { + gint mpegversion; + if (gst_structure_get_int(info, "mpegversion", &mpegversion)) + { + switch (mpegversion) + { + case 1: IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_MPG1); break; + case 2: IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_MPEG2); break; + case 4: IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_M4S2); break; + default: FIXME("Unrecognized mpeg version %d\n", mpegversion); + } + } + IMFMediaType_SetUINT32(media_type, &MF_MT_COMPRESSED, TRUE); + } else FIXME("Unrecognized video format %s\n", mime_type); }
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=68746
Your paranoid android.
=== debiant (build log) ===
Task: Patch failed to apply
=== debiant (build log) ===
Task: Patch failed to apply
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/mfplat/tests/mfplat.c | 8 +++---- dlls/winegstreamer/media_source.c | 37 +++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 6 deletions(-)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index d2d7db1b29..bde6684515 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -491,10 +491,7 @@ 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); @@ -522,7 +519,10 @@ todo_wine
var.vt = VT_EMPTY; hr = IMFMediaSource_Start(mediasource, descriptor, &GUID_NULL, &var); +todo_wine ok(hr == S_OK, "Failed to start media source, hr %#x.\n", hr); + if (FAILED(hr)) + goto skip_source_tests;
get_event((IMFMediaEventGenerator *)mediasource, MENewStream, &var); ok(var.vt == VT_UNKNOWN, "Unexpected value type %u from MENewStream event.\n", var.vt); @@ -585,10 +585,10 @@ todo_wine
get_event((IMFMediaEventGenerator *)mediasource, MEEndOfPresentation, NULL);
+skip_source_tests: IMFMediaTypeHandler_Release(handler); IMFPresentationDescriptor_Release(descriptor);
-skip_source_tests: hr = IMFMediaSource_Shutdown(mediasource); ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 080d2c43ca..fcaba45cd9 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -63,6 +63,7 @@ struct media_source IMFByteStream *byte_stream; struct media_stream **streams; ULONG stream_count; + IMFPresentationDescriptor *pres_desc; GstBus *bus; GstElement *container; GstElement *demuxer; @@ -493,12 +494,19 @@ static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource * { struct media_source *This = impl_from_IMFMediaSource(iface);
- FIXME("(%p)->(%p): stub\n", This, descriptor); + TRACE("(%p)->(%p)\n", This, descriptor);
if (This->state == SOURCE_SHUTDOWN) return MF_E_SHUTDOWN;
- return E_NOTIMPL; + if (!(This->pres_desc)) + { + return MF_E_NOT_INITIALIZED; + } + + IMFPresentationDescriptor_Clone(This->pres_desc, descriptor); + + return S_OK; }
static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *descriptor, @@ -549,6 +557,8 @@ static HRESULT media_source_teardown(struct media_source *This) gst_element_set_state(This->container, GST_STATE_NULL); gst_object_unref(GST_OBJECT(This->container)); } + if (This->pres_desc) + IMFPresentationDescriptor_Release(This->pres_desc); if (This->event_queue) IMFMediaEventQueue_Release(This->event_queue); if (This->byte_stream) @@ -862,12 +872,30 @@ static void source_stream_removed(GstElement *element, GstPad *pad, gpointer use
static void source_all_streams(GstElement *element, gpointer user) { + IMFStreamDescriptor **descriptors; struct media_source *source = (struct media_source *) user;
EnterCriticalSection(&source->streams_cs); if (source->state != SOURCE_OPENING) goto leave;
+ /* Init presentation descriptor */ + + descriptors = heap_alloc(source->stream_count * sizeof(IMFStreamDescriptor*)); + for (unsigned int i = 0; i < source->stream_count; i++) + { + IMFMediaStream_GetStreamDescriptor(&source->streams[i]->IMFMediaStream_iface, &descriptors[i]); + } + + if (FAILED(MFCreatePresentationDescriptor(source->stream_count, descriptors, &source->pres_desc))) + goto leave; + + for (unsigned int i = 0; i < source->stream_count; i++) + { + IMFStreamDescriptor_Release(descriptors[i]); + } + heap_free(descriptors); + SetEvent(source->init_complete_event);
leave: @@ -978,6 +1006,11 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t This->init_complete_event = NULL;
gst_element_set_state(This->container, GST_STATE_READY); + if (!(This->pres_desc)) + { + hr = E_FAIL; + goto fail; + }
This->state = SOURCE_STOPPED;
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=68747
Your paranoid android.
=== build (build log) ===
Task: Patch failed to apply
=== debiant (build log) ===
Task: Patch failed to apply
=== debiant (build log) ===
Task: Patch failed to apply
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/mfplat/tests/mfplat.c | 8 ++--- dlls/winegstreamer/media_source.c | 59 +++++++++++++++++++++++++++++-- 2 files changed, 61 insertions(+), 6 deletions(-)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index bde6684515..b666b54cc0 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -519,10 +519,7 @@ static void test_source_resolver(void)
var.vt = VT_EMPTY; hr = IMFMediaSource_Start(mediasource, descriptor, &GUID_NULL, &var); -todo_wine ok(hr == S_OK, "Failed to start media source, hr %#x.\n", hr); - if (FAILED(hr)) - goto skip_source_tests;
get_event((IMFMediaEventGenerator *)mediasource, MENewStream, &var); ok(var.vt == VT_UNKNOWN, "Unexpected value type %u from MENewStream event.\n", var.vt); @@ -540,10 +537,13 @@ todo_wine hr = IMFMediaStream_RequestSample(video_stream, NULL); if (i == sample_count) break; +todo_wine ok(hr == S_OK, "Failed to request sample %u, hr %#x.\n", i + 1, hr); if (hr != S_OK) break; } + if (FAILED(hr)) + goto skip_source_tests;
for (i = 0; i < sample_count; ++i) { @@ -581,11 +581,11 @@ todo_wine
hr = IMFMediaStream_RequestSample(video_stream, NULL); ok(hr == MF_E_END_OF_STREAM, "Unexpected hr %#x.\n", hr); - IMFMediaStream_Release(video_stream);
get_event((IMFMediaEventGenerator *)mediasource, MEEndOfPresentation, NULL);
skip_source_tests: + IMFMediaStream_Release(video_stream); IMFMediaTypeHandler_Release(handler); IMFPresentationDescriptor_Release(descriptor);
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index fcaba45cd9..2b330a442e 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -513,13 +513,68 @@ static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationD const GUID *time_format, const PROPVARIANT *start_position) { struct media_source *This = impl_from_IMFMediaSource(iface); + int ret; + PROPVARIANT empty_var; + empty_var.vt = VT_EMPTY;
- FIXME("(%p)->(%p, %p, %p): stub\n", This, descriptor, time_format, start_position); + TRACE("(%p)->(%p, %p, %p)\n", This, descriptor, time_format, start_position);
if (This->state == SOURCE_SHUTDOWN) return MF_E_SHUTDOWN;
- return E_NOTIMPL; + /* Find out which streams are active */ + for (unsigned int i = 0; i < This->stream_count; i++) + { + IMFStreamDescriptor *stream_desc; + DWORD in_stream_id; + BOOL selected; + + IMFPresentationDescriptor_GetStreamDescriptorByIndex(descriptor, i, &selected, &stream_desc); + IMFStreamDescriptor_GetStreamIdentifier(stream_desc, &in_stream_id); + + for (unsigned int k = 0; k < This->stream_count; k++) + { + DWORD cur_stream_id; + + IMFStreamDescriptor_GetStreamIdentifier(This->streams[k]->descriptor, &cur_stream_id); + + if (in_stream_id == cur_stream_id) + { + BOOL was_active = This->streams[k]->state != STREAM_INACTIVE; + This->streams[k]->state = selected ? STREAM_RUNNING : STREAM_INACTIVE; + if (selected) + { + IMFMediaEventQueue_QueueEventParamUnk(This->event_queue, + was_active ? MEUpdatedStream : MENewStream, &GUID_NULL, + S_OK, (IUnknown*) &This->streams[k]->IMFMediaStream_iface); + IMFMediaEventQueue_QueueEventParamVar(This->streams[k]->event_queue, + MEStreamStarted, &GUID_NULL, S_OK, &empty_var); + } + } + } + + IMFStreamDescriptor_Release(stream_desc); + } + + if (!(IsEqualIID(time_format, &GUID_NULL) && + (start_position->vt == VT_EMPTY || (start_position->vt == VT_I8 && start_position->u.hVal.QuadPart == 0)))) + { + ERR("unhandled start time\n"); + return MF_E_UNSUPPORTED_TIME_FORMAT; + } + + This->state = SOURCE_RUNNING; + gst_element_set_state(This->container, GST_STATE_PLAYING); + ret = gst_element_get_state(This->container, NULL, NULL, -1); + if (ret == GST_STATE_CHANGE_FAILURE) + { + ERR("Failed to play source.\n"); + return E_FAIL; + } + + IMFMediaEventQueue_QueueEventParamVar(This->event_queue, MESourceStarted, &GUID_NULL, S_OK, &empty_var); + + return S_OK; }
static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface)
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=68748
Your paranoid android.
=== build (build log) ===
Task: Patch failed to apply
=== debiant (build log) ===
Task: Patch failed to apply
=== debiant (build log) ===
Task: Patch failed to apply
It's probably better to use caps negotiation here, but this seemed simpler for now.
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/media_source.c | 51 +++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-)
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 2b330a442e..dc1b5dca85 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -41,7 +41,7 @@ struct media_stream struct media_source *parent_source; IMFMediaEventQueue *event_queue; IMFStreamDescriptor *descriptor; - GstElement *appsink; + GstElement *parser, *appsink; GstPad *their_src, *my_sink; /* usually reflects state of source */ enum @@ -337,18 +337,63 @@ static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad g_free(caps_str); g_free(compatible_caps_str); } + + if (!(gst_caps_is_equal(caps, compatible_caps))) + { + GList *parser_list_one, *parser_list_two; + GstElementFactory *parser_factory; + + parser_list_one = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_PARSER, 1); + + parser_list_two = gst_element_factory_list_filter(parser_list_one, caps, GST_PAD_SINK, 0); + gst_plugin_feature_list_free(parser_list_one); + parser_list_one = parser_list_two; + + parser_list_two = gst_element_factory_list_filter(parser_list_one, compatible_caps, GST_PAD_SRC, 0); + gst_plugin_feature_list_free(parser_list_one); + parser_list_one = parser_list_two; + + if (!(g_list_length(parser_list_one))) + { + gst_plugin_feature_list_free(parser_list_one); + ERR("Failed to find parser for stream\n"); + hr = E_FAIL; + goto fail; + } + + parser_factory = g_list_first(parser_list_one)->data; + TRACE("Found parser %s.\n", GST_ELEMENT_NAME(parser_factory)); + + if (!(This->parser = gst_element_factory_create(parser_factory, NULL))) + { + hr = E_FAIL; + goto fail; + } + gst_bin_add(GST_BIN(This->parent_source->container), This->parser); + if (!(gst_element_link(This->parser, This->appsink))) + { + hr = E_FAIL; + goto fail; + } + + g_object_set(This->appsink, "caps", compatible_caps, NULL); + + gst_plugin_feature_list_free(parser_list_one); + } gst_caps_unref(caps); caps = NULL; gst_caps_unref(compatible_caps); compatible_caps = NULL;
This->their_src = pad; - This->my_sink = gst_element_get_static_pad(This->appsink, "sink"); + This->my_sink = gst_element_get_static_pad(This->parser ? This->parser : 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); + if (This->parser) + gst_element_sync_state_with_parent(This->parser);
This->IMFMediaStream_iface.lpVtbl = &media_stream_vtbl; This->ref = 1; @@ -877,6 +922,8 @@ static void source_stream_added(GstElement *element, GstPad *pad, gpointer user) ERR("Error linking demuxer to stream %p, err = %d\n", existing_stream, ret); } gst_element_sync_state_with_parent(existing_stream->appsink); + if (existing_stream->parser) + gst_element_sync_state_with_parent(existing_stream->parser);
goto leave; }
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=68749
Your paranoid android.
=== debiant (build log) ===
Task: Patch failed to apply
=== debiant (build log) ===
Task: Patch failed to apply
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/mfplat/tests/mfplat.c | 4 - dlls/winegstreamer/gst_cbs.c | 10 ++ dlls/winegstreamer/gst_cbs.h | 6 ++ dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/media_source.c | 169 +++++++++++++++++++++++++++++- dlls/winegstreamer/mfplat.c | 90 ++++++++++++++++ 6 files changed, 272 insertions(+), 8 deletions(-)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index b666b54cc0..cb001633e4 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -537,13 +537,10 @@ static void test_source_resolver(void) hr = IMFMediaStream_RequestSample(video_stream, NULL); if (i == sample_count) break; -todo_wine ok(hr == S_OK, "Failed to request sample %u, hr %#x.\n", i + 1, hr); if (hr != S_OK) break; } - if (FAILED(hr)) - goto skip_source_tests;
for (i = 0; i < sample_count; ++i) { @@ -584,7 +581,6 @@ todo_wine
get_event((IMFMediaEventGenerator *)mediasource, MEEndOfPresentation, NULL);
-skip_source_tests: IMFMediaStream_Release(video_stream); IMFMediaTypeHandler_Release(handler); IMFPresentationDescriptor_Release(descriptor); diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c index 5038ea3397..49c04a55e5 100644 --- a/dlls/winegstreamer/gst_cbs.c +++ b/dlls/winegstreamer/gst_cbs.c @@ -416,3 +416,13 @@ GstFlowReturn stream_new_sample_wrapper(GstElement *appsink, gpointer user)
return cbdata.u.new_sample_data.ret; } + +void stream_eos_wrapper(GstElement *appsink, gpointer user) +{ + struct cb_data cbdata = { STREAM_EOS }; + + cbdata.u.eos_data.appsink = appsink; + cbdata.u.eos_data.user = user; + + call_cb(&cbdata); +} diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h index 106368a064..4840b89b59 100644 --- a/dlls/winegstreamer/gst_cbs.h +++ b/dlls/winegstreamer/gst_cbs.h @@ -53,6 +53,7 @@ enum CB_TYPE { SOURCE_STREAM_REMOVED, SOURCE_ALL_STREAMS, STREAM_NEW_SAMPLE, + STREAM_EOS, MEDIA_SOURCE_MAX, };
@@ -143,6 +144,10 @@ struct cb_data { gpointer user; GstFlowReturn ret; } new_sample_data; + struct eos_data { + GstElement *appsink; + gpointer user; + } eos_data; } u;
int finished; @@ -179,5 +184,6 @@ void source_stream_added_wrapper(GstElement *bin, GstPad *pad, gpointer user) DE 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; +void stream_eos_wrapper(GstElement *appsink, gpointer user) DECLSPEC_HIDDEN;
#endif diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 780cf1b02f..823e023f52 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -57,6 +57,7 @@ extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj)
GstCaps *make_mf_compatible_caps(GstCaps *caps); IMFMediaType *mf_media_type_from_caps(GstCaps *caps); +IMFSample *mf_sample_from_gst_buffer(GstBuffer *in);
enum source_type { diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index dc1b5dca85..b0f0cf4162 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -32,6 +32,12 @@ static struct source_desc } };
+struct sample_request +{ + struct list entry; + IUnknown *token; +}; + struct media_source;
struct media_stream @@ -52,6 +58,10 @@ struct media_stream STREAM_RUNNING, STREAM_SHUTDOWN, } state; + BOOL eos; + CRITICAL_SECTION dispatch_samples_cs; + struct list sample_requests; + unsigned int pending_samples; };
struct media_source @@ -82,6 +92,87 @@ struct media_source
/* stream */
+static void media_source_notify_stream_ended(struct media_source *source); +static void stream_dispatch_samples(struct media_stream *This) +{ + struct sample_request *req, *cursor2; + unsigned int fufilled_counter = 0; + + if (This->state != STREAM_RUNNING && This->state != STREAM_SHUTDOWN) + return; + + EnterCriticalSection(&This->dispatch_samples_cs); + + LIST_FOR_EACH_ENTRY_SAFE(req, cursor2, &This->sample_requests, struct sample_request, entry) + { + IMFSample *sample; + + if (This->state == STREAM_SHUTDOWN + /* Not sure if this is correct: */ + || (!(This->pending_samples) && This->eos)) + { + if (req->token) + { + IUnknown_Release(req->token); + } + list_remove(&req->entry); + continue; + } + + if (!(This->pending_samples)) + { + break; + } + + /* Get the sample from the appsink, then construct an IMFSample */ + /* We do this in the dispatch function so we can have appsink buffer for us */ + { + GstSample *gst_sample; + + g_signal_emit_by_name(This->appsink, "pull-sample", &gst_sample); + if (!gst_sample) + { + ERR("Appsink has no samples and pending_samples != 0\n"); + break; + } + + sample = mf_sample_from_gst_buffer(gst_sample_get_buffer(gst_sample)); + + gst_sample_unref(gst_sample); + } + + if (req->token) + { + IMFSample_SetUnknown(sample, &MFSampleExtension_Token, req->token); + } + + IMFMediaEventQueue_QueueEventParamUnk(This->event_queue, MEMediaSample, &GUID_NULL, S_OK, (IUnknown *)sample); + + if (req->token) + { + IUnknown_Release(req->token); + } + + list_remove(&req->entry); + + This->pending_samples--; + fufilled_counter++; + } + + TRACE("Fufilled %u sample requests. %u pending samples. %s more requests.\n", + fufilled_counter, This->pending_samples, list_empty(&This->sample_requests) ? "no" : ""); + + if (This->eos && !This->pending_samples && This->state == STREAM_RUNNING) + { + PROPVARIANT empty; + empty.vt = VT_EMPTY; + + IMFMediaEventQueue_QueueEventParamVar(This->event_queue, MEEndOfStream, &GUID_NULL, S_OK, &empty); + media_source_notify_stream_ended(This->parent_source); + } + LeaveCriticalSection(&This->dispatch_samples_cs); +} + static inline struct media_stream *impl_from_IMFMediaStream(IMFMediaStream *iface) { return CONTAINING_RECORD(iface, struct media_stream, IMFMediaStream_iface); @@ -217,13 +308,31 @@ static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IM static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) { struct media_stream *This = impl_from_IMFMediaStream(iface); + struct sample_request *req;
TRACE("(%p)->(%p)\n", iface, token);
if (This->state == STREAM_SHUTDOWN) return MF_E_SHUTDOWN;
- return E_NOTIMPL; + if (This->state == STREAM_INACTIVE || This->state == STREAM_ENABLED) + { + WARN("Stream isn't active\n"); + return MF_E_MEDIA_SOURCE_WRONGSTATE; + } + + if (This->eos && !This->pending_samples) + return MF_E_END_OF_STREAM; + + req = heap_alloc(sizeof(*req)); + if (token) + IUnknown_AddRef(token); + req->token = token; + list_add_tail(&This->sample_requests, &req->entry); + + stream_dispatch_samples(This); + + return S_OK; }
static const IMFMediaStreamVtbl media_stream_vtbl = @@ -243,15 +352,33 @@ static const IMFMediaStreamVtbl media_stream_vtbl = 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); + if (This->state == STREAM_INACTIVE) + { + GstSample *discard_sample; + g_signal_emit_by_name(This->appsink, "pull-sample", &discard_sample); + gst_sample_unref(discard_sample); + return GST_FLOW_OK; + } + + This->pending_samples++; + stream_dispatch_samples(This); return GST_FLOW_OK; }
+void stream_eos(GstElement *appsink, gpointer user) +{ + struct media_stream *This = (struct media_stream *) user; + + TRACE("(%p) EOS\n", This); + + This->eos = TRUE; + + stream_dispatch_samples(This); +} + static void media_stream_teardown(struct media_stream *This) { TRACE("(%p)\n", This); @@ -262,12 +389,18 @@ static void media_stream_teardown(struct media_stream *This) gst_object_unref(GST_OBJECT(This->their_src)); if (This->my_sink) gst_object_unref(GST_OBJECT(This->my_sink)); + + /* Frees pending requests and samples when state == STREAM_SHUTDOWN */ + stream_dispatch_samples(This); + 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); + + DeleteCriticalSection(&This->dispatch_samples_cs); }
static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad, DWORD stream_id, struct media_stream **out_stream) @@ -281,6 +414,10 @@ static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad TRACE("(%p %p)->(%p)\n", source, pad, out_stream);
This->state = STREAM_INACTIVE; + This->pending_samples = 0; + list_init(&This->sample_requests); + This->eos = FALSE; + InitializeCriticalSection(&This->dispatch_samples_cs);
if (FAILED(hr = IMFMediaSource_AddRef(&source->IMFMediaSource_iface))) { @@ -303,6 +440,7 @@ static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad 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); + g_signal_connect(This->appsink, "eos", G_CALLBACK(stream_eos_wrapper), This);
if (FAILED(hr = MFCreateMediaType(&media_type))) { @@ -1004,6 +1142,23 @@ static void source_all_streams(GstElement *element, gpointer user) LeaveCriticalSection(&source->streams_cs); }
+static void media_source_notify_stream_ended(struct media_source *This) +{ + PROPVARIANT empty; + empty.vt = VT_EMPTY; + + /* A stream has ended, check whether all have */ + for (unsigned int i = 0; i < This->stream_count; i++) + { + struct media_stream *stream = This->streams[i]; + + if (!stream->eos) + return; + } + + IMFMediaEventQueue_QueueEventParamVar(This->event_queue, MEEndOfPresentation, &GUID_NULL, S_OK, &empty); +} + static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_type type, struct media_source **out_media_source) { GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE( @@ -1649,6 +1804,12 @@ void perform_cb_media_source(struct cb_data *cbdata) cbdata->u.new_sample_data.ret = stream_new_sample(data->appsink, data->user); break; } + case STREAM_EOS: + { + struct eos_data *data = &cbdata->u.eos_data; + stream_eos(data->appsink, data->user); + break; + } default: { ERR("Wrong callback forwarder called\n"); diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 02912ac3b6..8bb030f018 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -871,4 +871,94 @@ GstCaps *make_mf_compatible_caps(GstCaps *caps) }
return ret; +} + +/* IMFSample = GstBuffer + IMFBuffer = GstMemory */ + +/* TODO: Future optimization could be to create a custom + IMFMediaBuffer wrapper around GstMemory, and to utilize + gst_memory_new_wrapped on IMFMediaBuffer data. However, + this wouldn't work if we allow the callers to allocate + the buffers. */ + +IMFSample* mf_sample_from_gst_buffer(GstBuffer *gst_buffer) +{ + IMFSample *out = NULL; + LONGLONG duration, time; + int buffer_count; + HRESULT hr; + + if (FAILED(hr = MFCreateSample(&out))) + goto fail; + + duration = GST_BUFFER_DURATION(gst_buffer); + time = GST_BUFFER_PTS(gst_buffer); + + if (FAILED(IMFSample_SetSampleDuration(out, duration / 100))) + goto fail; + + if (FAILED(IMFSample_SetSampleTime(out, time / 100))) + goto fail; + + buffer_count = gst_buffer_n_memory(gst_buffer); + + for (unsigned int i = 0; i < buffer_count; i++) + { + GstMemory *memory = gst_buffer_get_memory(gst_buffer, i); + IMFMediaBuffer *mf_buffer = NULL; + GstMapInfo map_info; + BYTE *buf_data; + + if (!memory) + { + hr = E_FAIL; + goto loop_done; + } + + if (!(gst_memory_map(memory, &map_info, GST_MAP_READ))) + { + hr = E_FAIL; + goto loop_done; + } + + if (FAILED(hr = MFCreateMemoryBuffer(map_info.maxsize, &mf_buffer))) + { + gst_memory_unmap(memory, &map_info); + goto loop_done; + } + + if (FAILED(hr = IMFMediaBuffer_Lock(mf_buffer, &buf_data, NULL, NULL))) + { + gst_memory_unmap(memory, &map_info); + goto loop_done; + } + + memcpy(buf_data, map_info.data, map_info.size); + + gst_memory_unmap(memory, &map_info); + + if (FAILED(hr = IMFMediaBuffer_Unlock(mf_buffer))) + goto loop_done; + + if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(mf_buffer, map_info.size))) + goto loop_done; + + if (FAILED(hr = IMFSample_AddBuffer(out, mf_buffer))) + goto loop_done; + + loop_done: + if (mf_buffer) + IMFMediaBuffer_Release(mf_buffer); + if (memory) + gst_memory_unref(memory); + if (FAILED(hr)) + goto fail; + } + + return out; + fail: + ERR("Failed to copy IMFSample to GstBuffer, hr = %#x\n", hr); + IMFSample_Release(out); + return NULL; } \ 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=68750
Your paranoid android.
=== build (build log) ===
Task: Patch failed to apply
=== debiant (build log) ===
Task: Patch failed to apply
=== debiant (build log) ===
Task: Patch failed to apply
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/media_source.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index b0f0cf4162..e6effa0429 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -665,12 +665,14 @@ static HRESULT WINAPI media_source_GetCharacteristics(IMFMediaSource *iface, DWO { struct media_source *This = impl_from_IMFMediaSource(iface);
- FIXME("(%p)->(%p): stub\n", This, characteristics); + TRACE("(%p)->(%p)\n", This, characteristics);
if (This->state == SOURCE_SHUTDOWN) return MF_E_SHUTDOWN;
- return E_NOTIMPL; + *characteristics = 0; + + return S_OK; }
static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **descriptor)
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=68751
Your paranoid android.
=== debiant (build log) ===
Task: Patch failed to apply
=== debiant (build log) ===
Task: Patch failed to apply
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/media_source.c | 40 +++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+)
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index e6effa0429..4e87546f13 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -1264,6 +1264,46 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t CloseHandle(This->init_complete_event); This->init_complete_event = NULL;
+ /* miscelaneous presentation descriptor setup */ + { + IMFAttributes *byte_stream_attributes; + gint64 total_pres_time = 0; + + if (SUCCEEDED(IMFByteStream_QueryInterface(This->byte_stream, &IID_IMFAttributes, (void **)&byte_stream_attributes))) + { + WCHAR *mimeW = NULL; + DWORD length; + if (SUCCEEDED(IMFAttributes_GetAllocatedString(byte_stream_attributes, &MF_BYTESTREAM_CONTENT_TYPE, &mimeW, &length))) + { + IMFPresentationDescriptor_SetString(This->pres_desc, &MF_PD_MIME_TYPE, mimeW); + CoTaskMemFree(mimeW); + } + IMFAttributes_Release(byte_stream_attributes); + } + + for (unsigned int i = 0; i < This->stream_count; i++) + { + GstQuery *query = gst_query_new_duration(GST_FORMAT_TIME); + if (gst_pad_query(This->streams[i]->their_src, query)) + { + gint64 stream_pres_time; + gst_query_parse_duration(query, NULL, &stream_pres_time); + + TRACE("Stream %u has duration %lu\n", i, stream_pres_time); + + if (stream_pres_time > total_pres_time) + total_pres_time = stream_pres_time; + } + else + { + WARN("Unable to get presentation time of stream %u\n", i); + } + } + + if (This->stream_count) + IMFPresentationDescriptor_SetUINT64(This->pres_desc, &MF_PD_DURATION, total_pres_time / 100); + } + gst_element_set_state(This->container, GST_STATE_READY); if (!(This->pres_desc)) {
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=68752
Your paranoid android.
=== debiant (build log) ===
Task: Patch failed to apply
=== debiant (build log) ===
Task: Patch failed to apply
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- tools/make_makefiles | 45 +++++++++++++++++++++++++++----------------- tools/makedep.c | 30 ++++++++++++++++++++--------- 2 files changed, 49 insertions(+), 26 deletions(-)
diff --git a/tools/make_makefiles b/tools/make_makefiles index c18fa90e2d..1e34a280a4 100755 --- a/tools/make_makefiles +++ b/tools/make_makefiles @@ -231,14 +231,14 @@ sub parse_makefile($) { die "Configure substitution is not allowed in $file" unless $file eq "Makefile"; } - if (/^\s*(MODULE|IMPORTLIB|TESTDLL|PARENTSRC|APPMODE|EXTRADLLFLAGS)\s*=\s*(.*)/) + if (/^\s*(MODULE|IMPORTLIB|TESTDLL|APPMODE|EXTRADLLFLAGS)\s*=\s*(.*)/) { my $var = $1; $make{$var} = $2; next; } my $source_vars_regexp = join "|", @source_vars; - if (/^\s*($source_vars_regexp|PROGRAMS|EXTRA_TARGETS|EXTRA_OBJS|INSTALL_LIB|INSTALL_DEV)\s*=\s*(.*)/) + if (/^\s*($source_vars_regexp|PROGRAMS|EXTRA_TARGETS|EXTRA_OBJS|INSTALL_LIB|INSTALL_DEV|PARENTSRC)\s*=\s*(.*)/) { my $var = $1; my @list = split(/\s+/, $2); @@ -293,19 +293,27 @@ sub get_makedep_flags($) return %flags; }
-sub get_parent_makefile($) +sub get_parent_makefiles($) { my $file = shift; my %make = %{$makefiles{$file}}; - my $reldir = $make{"PARENTSRC"} || ""; - return "" unless $reldir; - (my $path = $file) =~ s//Makefile$///; - while ($reldir =~ /^..//) + my $pointer = $make{"PARENTSRC"} || (); + return () unless $pointer; + my @reldirs = @{$pointer}; + my @makefiles = (); + foreach my $reldir (@reldirs) { - $reldir =~ s/^..///; - $path =~ s/[^/]+/$//; + my $length = @reldirs; + (my $path = $file) =~ s//Makefile$///; + while ($reldir =~ /^..//) + { + $reldir =~ s/^..///; + $path =~ s/[^/]+/$//; + } + push @makefiles, "$path$reldir/Makefile"; } - return "$path$reldir/Makefile"; + + return @makefiles }
# preserve shared source files that are listed in the existing makefile @@ -410,13 +418,16 @@ sub assign_sources_to_makefiles(@) foreach my $file (@makefiles) { my $make = $makefiles{$file}; - my $parent = get_parent_makefile( $file ); - next unless $parent; - preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "C_SRCS" ); - preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "RC_SRCS" ); - preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "IDL_SRCS" ); - preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "LEX_SRCS" ); - preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "BISON_SRCS" ); + my @parents = get_parent_makefiles( $file ); + next unless @parents; + foreach my $parent (@parents) + { + preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "C_SRCS" ); + preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "RC_SRCS" ); + preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "IDL_SRCS" ); + preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "LEX_SRCS" ); + preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "BISON_SRCS" ); + } } }
diff --git a/tools/makedep.c b/tools/makedep.c index 4f19231e7c..31f577d49c 100644 --- a/tools/makedep.c +++ b/tools/makedep.c @@ -182,6 +182,7 @@ struct makefile struct strarray install_lib; struct strarray install_dev; struct strarray extra_targets; + struct strarray parent_dirs; struct list sources; struct list includes; const char *base_dir; @@ -189,7 +190,6 @@ struct makefile const char *obj_dir; const char *top_src_dir; const char *top_obj_dir; - const char *parent_dir; const char *module; const char *testdll; const char *sharedlib; @@ -1373,15 +1373,24 @@ static struct file *open_local_file( const struct makefile *make, const char *pa { char *src_path = root_dir_path( base_dir_path( make, path )); struct file *ret = load_file( src_path ); + unsigned int i;
- /* if not found, try parent dir */ - if (!ret && make->parent_dir) + /* if not found, try parent dirs */ + for (i = 0; !ret && i < make->parent_dirs.count; i++) { + char *new_path; + free( src_path ); - path = strmake( "%s/%s", make->parent_dir, path ); - src_path = root_dir_path( base_dir_path( make, path )); + new_path = strmake( "%s/%s", make->parent_dirs.str[i], path ); + src_path = root_dir_path( base_dir_path( make, new_path )); ret = load_file( src_path ); - if (ret) ret->flags |= FLAG_PARENTDIR; + if (ret) + { + ret->flags |= FLAG_PARENTDIR; + path = new_path; + } + else + free(new_path); }
if (ret) *filename = src_dir_path( make, path ); @@ -4186,13 +4195,13 @@ static void load_sources( struct makefile *make ) strarray_set_value( &make->vars, "top_srcdir", top_src_dir_path( make, "" )); strarray_set_value( &make->vars, "srcdir", src_dir_path( make, "" ));
- make->parent_dir = get_expanded_make_variable( make, "PARENTSRC" ); make->module = get_expanded_make_variable( make, "MODULE" ); make->testdll = get_expanded_make_variable( make, "TESTDLL" ); make->sharedlib = get_expanded_make_variable( make, "SHAREDLIB" ); make->staticlib = get_expanded_make_variable( make, "STATICLIB" ); make->importlib = get_expanded_make_variable( make, "IMPORTLIB" );
+ make->parent_dirs = get_expanded_make_var_array( make, "PARENTSRC" ); make->programs = get_expanded_make_var_array( make, "PROGRAMS" ); make->scripts = get_expanded_make_var_array( make, "SCRIPTS" ); make->imports = get_expanded_make_var_array( make, "IMPORTS" ); @@ -4237,8 +4246,11 @@ static void load_sources( struct makefile *make ) strarray_add( &make->include_args, strmake( "-I%s", obj_dir_path( make, "" ))); if (make->src_dir) strarray_add( &make->include_args, strmake( "-I%s", make->src_dir )); - if (make->parent_dir) - strarray_add( &make->include_args, strmake( "-I%s", src_dir_path( make, make->parent_dir ))); + if (make->parent_dirs.count) + { + for (i = 0; i < make->parent_dirs.count; i++) + strarray_add( &make->include_args, strmake( "-I%s", src_dir_path( make, make->parent_dirs.str[i] ))); + } strarray_add( &make->include_args, strmake( "-I%s", top_obj_dir_path( make, "include" ))); if (make->top_src_dir) strarray_add( &make->include_args, strmake( "-I%s", top_src_dir_path( make, "include" )));
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=68753
Your paranoid android.
=== debiant (build log) ===
Task: Patch failed to apply
=== debiant (build log) ===
Task: Patch failed to apply
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/mf/Makefile.in | 1 + dlls/mf/handler.c | 360 ++++++++++++++++++++++++++++ dlls/mf/handler.h | 30 +++ dlls/mf/main.c | 373 ++++-------------------------- dlls/winegstreamer/Makefile.in | 3 +- dlls/winegstreamer/media_source.c | 332 ++------------------------ 6 files changed, 447 insertions(+), 652 deletions(-) create mode 100644 dlls/mf/handler.c create mode 100644 dlls/mf/handler.h
diff --git a/dlls/mf/Makefile.in b/dlls/mf/Makefile.in index fe156e43ab..5c69ac38e5 100644 --- a/dlls/mf/Makefile.in +++ b/dlls/mf/Makefile.in @@ -5,6 +5,7 @@ IMPORTS = advapi32 mfplat ole32 uuid mfuuid EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \ + handler.c \ main.c \ samplegrabber.c \ sar.c \ diff --git a/dlls/mf/handler.c b/dlls/mf/handler.c new file mode 100644 index 0000000000..da40049377 --- /dev/null +++ b/dlls/mf/handler.c @@ -0,0 +1,360 @@ +#include <stdarg.h> + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "mfidl.h" + +#include "mfapi.h" +#include "mferror.h" + +#include "handler.h" + +#include "wine/debug.h" +#include "wine/heap.h" +#include "wine/list.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +struct create_object_context +{ + IUnknown IUnknown_iface; + LONG refcount; + + IPropertyStore *props; + IMFByteStream *stream; + WCHAR *url; + DWORD flags; +}; + +struct handler_result +{ + struct list entry; + IMFAsyncResult *result; + MF_OBJECT_TYPE obj_type; + IUnknown *object; +}; + +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, +}; + +/* Start async methods */ +static struct handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct handler, IMFAsyncCallback_iface); +} + +static HRESULT WINAPI 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; +} + +/* lifetime not managed with COM */ +static ULONG WINAPI handler_callback_AddRef(IMFAsyncCallback *iface) +{ + return 2; +} + +static ULONG WINAPI handler_callback_Release(IMFAsyncCallback *iface) +{ + return 1; +} + +static HRESULT WINAPI handler_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct handler *handler = impl_from_IMFAsyncCallback(iface); + struct 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 = 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 handler_callback_vtbl = +{ + handler_callback_QueryInterface, + handler_callback_AddRef, + handler_callback_Release, + handler_callback_GetParameters, + handler_callback_Invoke, +}; + +/* Start handler helpers */ + +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; +} + +HRESULT handler_begin_create_object(struct handler *handler, IMFByteStream *stream, + const WCHAR *url, DWORD flags, IPropertyStore *props, IUnknown **cancel_cookie, + IMFAsyncCallback *callback, IUnknown *state) +{ + struct create_object_context *context; + IMFAsyncResult *caller, *item; + HRESULT hr; + + 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->url && !context->stream) + { + IMFAsyncResult_Release(caller); + IUnknown_Release(&context->IUnknown_iface); + return E_OUTOFMEMORY; + } + + hr = MFCreateAsyncResult(&context->IUnknown_iface, &handler->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; +} + +HRESULT handler_end_create_object(struct handler *handler, IMFAsyncResult *result, + MF_OBJECT_TYPE *obj_type, IUnknown **object) +{ + struct handler_result *found = NULL, *cur; + HRESULT hr; + + EnterCriticalSection(&handler->cs); + + LIST_FOR_EACH_ENTRY(cur, &handler->results, struct handler_result, entry) + { + if (result == cur->result) + { + list_remove(&cur->entry); + found = cur; + break; + } + } + + LeaveCriticalSection(&handler->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; +} + +HRESULT handler_cancel_object_creation(struct handler *handler, IUnknown *cancel_cookie) +{ + struct handler_result *found = NULL, *cur; + + EnterCriticalSection(&handler->cs); + + LIST_FOR_EACH_ENTRY(cur, &handler->results, struct handler_result, entry) + { + if (cancel_cookie == (IUnknown *)cur->result) + { + list_remove(&cur->entry); + found = cur; + break; + } + } + + LeaveCriticalSection(&handler->cs); + + if (found) + { + IMFAsyncResult_Release(found->result); + if (found->object) + IUnknown_Release(found->object); + heap_free(found); + } + + return found ? S_OK : MF_E_UNEXPECTED; +} + +void handler_construct(struct handler *handler, p_create_object_callback create_object_callback) +{ + handler->IMFAsyncCallback_iface.lpVtbl = &handler_callback_vtbl; + handler->create_object = create_object_callback; + + list_init(&handler->results); + InitializeCriticalSection(&handler->cs); +} + +void handler_destruct(struct handler *handler) +{ + struct handler_result *result, *next; + + LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct handler_result, entry) + { + list_remove(&result->entry); + IMFAsyncResult_Release(result->result); + if (result->object) + IUnknown_Release(result->object); + heap_free(result); + } + DeleteCriticalSection(&handler->cs); +} \ No newline at end of file diff --git a/dlls/mf/handler.h b/dlls/mf/handler.h new file mode 100644 index 0000000000..fbebd26e8d --- /dev/null +++ b/dlls/mf/handler.h @@ -0,0 +1,30 @@ +#include "windef.h" + +#include "mfidl.h" +#include "mfapi.h" +#include "mfobjects.h" + +#include "wine/list.h" + +/* helper sub-object that handles ansyncronous nature of handlers */ + +struct handler; +typedef HRESULT (*p_create_object_callback)(struct handler *handler, WCHAR *url, IMFByteStream *stream, DWORD flags, IPropertyStore *props, + IUnknown **out_object, MF_OBJECT_TYPE *out_obj_type); + +struct handler +{ + IMFAsyncCallback IMFAsyncCallback_iface; + struct list results; + CRITICAL_SECTION cs; + p_create_object_callback create_object; +}; + +void handler_construct(struct handler *handler, p_create_object_callback create_object_callback); +void handler_destruct(struct handler *handler); +HRESULT handler_begin_create_object(struct handler *handler, IMFByteStream *stream, + const WCHAR *url, DWORD flags, IPropertyStore *props, IUnknown **cancel_cookie, + IMFAsyncCallback *callback, IUnknown *state); +HRESULT handler_end_create_object(struct handler *handler, IMFAsyncResult *result, + MF_OBJECT_TYPE *obj_type, IUnknown **object); +HRESULT handler_cancel_object_creation(struct handler *handler, IUnknown *cancel_cookie); \ No newline at end of file diff --git a/dlls/mf/main.c b/dlls/mf/main.c index 01fe17866c..b1b8f3430c 100644 --- a/dlls/mf/main.c +++ b/dlls/mf/main.c @@ -35,6 +35,7 @@ #include "mferror.h"
#include "mf_private.h" +#include "handler.h"
#include "wine/debug.h" #include "wine/heap.h" @@ -556,22 +557,12 @@ static const IClassFactoryVtbl class_factory_vtbl = class_factory_LockServer, };
-struct file_scheme_handler_result -{ - struct list entry; - IMFAsyncResult *result; - MF_OBJECT_TYPE obj_type; - IUnknown *object; -}; - struct file_scheme_handler { IMFSchemeHandler IMFSchemeHandler_iface; - IMFAsyncCallback IMFAsyncCallback_iface; LONG refcount; IMFSourceResolver *resolver; - struct list results; - CRITICAL_SECTION cs; + struct handler handler; };
static struct file_scheme_handler *impl_from_IMFSchemeHandler(IMFSchemeHandler *iface) @@ -579,11 +570,6 @@ static struct file_scheme_handler *impl_from_IMFSchemeHandler(IMFSchemeHandler * return CONTAINING_RECORD(iface, struct file_scheme_handler, IMFSchemeHandler_iface); }
-static struct file_scheme_handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) -{ - return CONTAINING_RECORD(iface, struct file_scheme_handler, IMFAsyncCallback_iface); -} - static HRESULT WINAPI file_scheme_handler_QueryInterface(IMFSchemeHandler *iface, REFIID riid, void **obj) { TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); @@ -613,242 +599,45 @@ static ULONG WINAPI file_scheme_handler_AddRef(IMFSchemeHandler *iface)
static ULONG WINAPI file_scheme_handler_Release(IMFSchemeHandler *iface) { - struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface); - ULONG refcount = InterlockedDecrement(&handler->refcount); - struct file_scheme_handler_result *result, *next; + struct file_scheme_handler *this = impl_from_IMFSchemeHandler(iface); + ULONG refcount = InterlockedDecrement(&this->refcount);
TRACE("%p, refcount %u.\n", iface, refcount);
if (!refcount) { - LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct file_scheme_handler_result, entry) - { - list_remove(&result->entry); - IMFAsyncResult_Release(result->result); - if (result->object) - IUnknown_Release(result->object); - heap_free(result); - } - DeleteCriticalSection(&handler->cs); - if (handler->resolver) - IMFSourceResolver_Release(handler->resolver); - heap_free(handler); + if (this->resolver) + IMFSourceResolver_Release(this->resolver); + handler_destruct(&this->handler); }
return refcount; }
-struct create_object_context -{ - IUnknown IUnknown_iface; - LONG refcount; - - IPropertyStore *props; - 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); - 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 file_scheme_handler_BeginCreateObject(IMFSchemeHandler *iface, const WCHAR *url, DWORD flags, IPropertyStore *props, IUnknown **cancel_cookie, IMFAsyncCallback *callback, IUnknown *state) { - struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface); - struct create_object_context *context; - IMFAsyncResult *caller, *item; - HRESULT hr; + struct file_scheme_handler *this = impl_from_IMFSchemeHandler(iface);
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->url = heap_strdupW(url); - if (!context->url) - { - IMFAsyncResult_Release(caller); - IUnknown_Release(&context->IUnknown_iface); - return E_OUTOFMEMORY; - } - - hr = MFCreateAsyncResult(&context->IUnknown_iface, &handler->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; + return handler_begin_create_object(&this->handler, NULL, url, flags, props, cancel_cookie, callback, state); }
static HRESULT WINAPI file_scheme_handler_EndCreateObject(IMFSchemeHandler *iface, IMFAsyncResult *result, MF_OBJECT_TYPE *obj_type, IUnknown **object) { - struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface); - struct file_scheme_handler_result *found = NULL, *cur; - HRESULT hr; + struct file_scheme_handler *this = impl_from_IMFSchemeHandler(iface);
TRACE("%p, %p, %p, %p.\n", iface, result, obj_type, object); - - EnterCriticalSection(&handler->cs); - - LIST_FOR_EACH_ENTRY(cur, &handler->results, struct file_scheme_handler_result, entry) - { - if (result == cur->result) - { - list_remove(&cur->entry); - found = cur; - break; - } - } - - LeaveCriticalSection(&handler->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; + return handler_end_create_object(&this->handler, result, obj_type, object); }
static HRESULT WINAPI file_scheme_handler_CancelObjectCreation(IMFSchemeHandler *iface, IUnknown *cancel_cookie) { - struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface); - struct file_scheme_handler_result *found = NULL, *cur; + struct file_scheme_handler *this = impl_from_IMFSchemeHandler(iface);
TRACE("%p, %p.\n", iface, cancel_cookie); - - EnterCriticalSection(&handler->cs); - - LIST_FOR_EACH_ENTRY(cur, &handler->results, struct file_scheme_handler_result, entry) - { - if (cancel_cookie == (IUnknown *)cur->result) - { - list_remove(&cur->entry); - found = cur; - break; - } - } - - LeaveCriticalSection(&handler->cs); - - if (found) - { - IMFAsyncResult_Release(found->result); - if (found->object) - IUnknown_Release(found->object); - heap_free(found); - } - - return found ? S_OK : MF_E_UNEXPECTED; + return handler_cancel_object_creation(&this->handler, cancel_cookie); }
static const IMFSchemeHandlerVtbl file_scheme_handler_vtbl = @@ -861,38 +650,6 @@ static const IMFSchemeHandlerVtbl file_scheme_handler_vtbl = file_scheme_handler_CancelObjectCreation, };
-static HRESULT WINAPI file_scheme_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 file_scheme_handler_callback_AddRef(IMFAsyncCallback *iface) -{ - struct file_scheme_handler *handler = impl_from_IMFAsyncCallback(iface); - return IMFSchemeHandler_AddRef(&handler->IMFSchemeHandler_iface); -} - -static ULONG WINAPI file_scheme_handler_callback_Release(IMFAsyncCallback *iface) -{ - struct file_scheme_handler *handler = impl_from_IMFAsyncCallback(iface); - return IMFSchemeHandler_Release(&handler->IMFSchemeHandler_iface); -} - -static HRESULT WINAPI file_scheme_handler_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) -{ - return E_NOTIMPL; -} - static HRESULT file_scheme_handler_get_resolver(struct file_scheme_handler *handler, IMFSourceResolver **resolver) { HRESULT hr; @@ -914,111 +671,63 @@ static HRESULT file_scheme_handler_get_resolver(struct file_scheme_handler *hand return S_OK; }
-static HRESULT WINAPI file_scheme_handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +static HRESULT file_scheme_handler_create_object(struct handler *handler, WCHAR *url, IMFByteStream *stream, DWORD flags, + IPropertyStore *props, IUnknown **out_object, MF_OBJECT_TYPE *out_obj_type) { static const WCHAR schemeW[] = {'f','i','l','e',':','/','/'}; - struct file_scheme_handler *handler = impl_from_IMFAsyncCallback(iface); - struct file_scheme_handler_result *handler_result; - MF_OBJECT_TYPE obj_type = MF_OBJECT_INVALID; - IUnknown *object = NULL, *context_object; - struct create_object_context *context; + HRESULT hr = S_OK; + WCHAR *path; + IMFByteStream *file_byte_stream; + struct file_scheme_handler *this = CONTAINING_RECORD(handler, struct file_scheme_handler, handler); IMFSourceResolver *resolver; - IMFAsyncResult *caller; - IMFByteStream *stream; - const WCHAR *url; - 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);
/* Strip from scheme, MFCreateFile() won't be expecting it. */ - url = context->url; - if (!wcsnicmp(context->url, schemeW, ARRAY_SIZE(schemeW))) - url += ARRAY_SIZE(schemeW); + path = url; + if (!wcsnicmp(url, schemeW, ARRAY_SIZE(schemeW))) + path += ARRAY_SIZE(schemeW);
- hr = MFCreateFile(context->flags & MF_RESOLUTION_WRITE ? MF_ACCESSMODE_READWRITE : MF_ACCESSMODE_READ, - MF_OPENMODE_FAIL_IF_NOT_EXIST, MF_FILEFLAGS_NONE, url, &stream); + hr = MFCreateFile(flags & MF_RESOLUTION_WRITE ? MF_ACCESSMODE_READWRITE : MF_ACCESSMODE_READ, + MF_OPENMODE_FAIL_IF_NOT_EXIST, MF_FILEFLAGS_NONE, path, &file_byte_stream); if (SUCCEEDED(hr)) { - if (context->flags & MF_RESOLUTION_MEDIASOURCE) + if (flags & MF_RESOLUTION_MEDIASOURCE) { - if (SUCCEEDED(hr = file_scheme_handler_get_resolver(handler, &resolver))) + if (SUCCEEDED(hr = file_scheme_handler_get_resolver(this, &resolver))) { - hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, stream, context->url, context->flags, - context->props, &obj_type, &object); + hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, file_byte_stream, url, flags, + props, out_obj_type, out_object); IMFSourceResolver_Release(resolver); - IMFByteStream_Release(stream); + IMFByteStream_Release(file_byte_stream); } } else { - object = (IUnknown *)stream; - obj_type = MF_OBJECT_BYTESTREAM; + *out_object = (IUnknown *)file_byte_stream; + *out_obj_type = MF_OBJECT_BYTESTREAM; } }
- 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; + return hr; }
-static const IMFAsyncCallbackVtbl file_scheme_handler_callback_vtbl = -{ - file_scheme_handler_callback_QueryInterface, - file_scheme_handler_callback_AddRef, - file_scheme_handler_callback_Release, - file_scheme_handler_callback_GetParameters, - file_scheme_handler_callback_Invoke, -}; - static HRESULT file_scheme_handler_construct(REFIID riid, void **obj) { - struct file_scheme_handler *handler; + struct file_scheme_handler *this; HRESULT hr;
TRACE("%s, %p.\n", debugstr_guid(riid), obj);
- handler = heap_alloc_zero(sizeof(*handler)); - if (!handler) + this = heap_alloc_zero(sizeof(*this)); + if (!this) return E_OUTOFMEMORY;
- handler->IMFSchemeHandler_iface.lpVtbl = &file_scheme_handler_vtbl; - handler->IMFAsyncCallback_iface.lpVtbl = &file_scheme_handler_callback_vtbl; - handler->refcount = 1; - list_init(&handler->results); - InitializeCriticalSection(&handler->cs); + handler_construct(&this->handler, file_scheme_handler_create_object); + + this->IMFSchemeHandler_iface.lpVtbl = &file_scheme_handler_vtbl; + this->refcount = 1;
- hr = IMFSchemeHandler_QueryInterface(&handler->IMFSchemeHandler_iface, riid, obj); - IMFSchemeHandler_Release(&handler->IMFSchemeHandler_iface); + hr = IMFSchemeHandler_QueryInterface(&this->IMFSchemeHandler_iface, riid, obj); + IMFSchemeHandler_Release(&this->IMFSchemeHandler_iface);
return hr; } diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index f4f72c6e96..61789884b8 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -2,12 +2,13 @@ MODULE = winegstreamer.dll IMPORTS = strmiids uuid winmm msacm32 msvfw32 ole32 oleaut32 user32 gdi32 advapi32 mfplat mfuuid EXTRAINCL = $(GSTREAMER_CFLAGS) EXTRALIBS = $(GSTREAMER_LIBS) $(PTHREAD_LIBS) -PARENTSRC = ../strmbase +PARENTSRC = ../strmbase ../mf
C_SRCS = \ filter.c \ gst_cbs.c \ gstdemux.c \ + handler.c \ main.c \ media_source.c \ mediatype.c \ diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 4e87546f13..bca3decc3a 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -4,6 +4,7 @@
#include "gst_private.h" #include "gst_cbs.h" +#include "handler.h"
#include <assert.h> #include <stdarg.h> @@ -1329,22 +1330,12 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
/* 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; + struct handler handler; };
static struct container_stream_handler *impl_from_IMFByteStreamHandler(IMFByteStreamHandler *iface) @@ -1352,11 +1343,6 @@ static struct container_stream_handler *impl_from_IMFByteStreamHandler(IMFByteSt 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); @@ -1386,245 +1372,44 @@ static ULONG WINAPI container_stream_handler_AddRef(IMFByteStreamHandler *iface)
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); + struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface); + ULONG refcount = InterlockedDecrement(&this->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); + handler_destruct(&this->handler); + heap_free(this); }
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); - IMFAsyncResult_Release(caller); - if (SUCCEEDED(hr)) - { - if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item))) - { - if (cancel_cookie) - IMFAsyncResult_GetState(item, cancel_cookie); - } - - IMFAsyncResult_Release(item); - } - - return hr; + return handler_begin_create_object(&this->handler, stream, url, flags, props, cancel_cookie, callback, state); }
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; + return handler_end_create_object(&this->handler, result, obj_type, object); }
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; + return handler_cancel_object_creation(&this->handler, cancel_cookie); }
static HRESULT WINAPI container_stream_handler_GetMaxNumberOfBytesRequiredForResolution(IMFByteStreamHandler *iface, QWORD *bytes) @@ -1644,47 +1429,16 @@ static const IMFByteStreamHandlerVtbl container_stream_handler_vtbl = 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, +static HRESULT container_stream_handler_create_object(struct handler *handler, 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); + TRACE("(%p %s %p %u %p %p %p)\n", handler, debugstr_w(url), stream, flags, props, out_object, out_obj_type);
if (flags & MF_RESOLUTION_MEDIASOURCE) { HRESULT hr; struct media_source *new_source; + struct container_stream_handler *This = CONTAINING_RECORD(handler, struct container_stream_handler, handler);
if (FAILED(hr = media_source_constructor(stream, This->type, &new_source))) return hr; @@ -1703,64 +1457,6 @@ static HRESULT container_stream_handler_create_object(struct container_stream_ha } }
-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; @@ -1772,12 +1468,10 @@ HRESULT container_stream_handler_construct(REFIID riid, void **obj, enum source_ if (!this) return E_OUTOFMEMORY;
- list_init(&this->results); - InitializeCriticalSection(&this->cs); + handler_construct(&this->handler, container_stream_handler_create_object);
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);
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=68754
Your paranoid android.
=== debiant (build log) ===
Task: Patch failed to apply
=== debiant (build log) ===
Task: Patch failed to apply
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=68737
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).
=== 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).
On 4/1/20 5:05 PM, Derek Lesho wrote:
Signed-off-by: Derek Lesho dlesho@codeweavers.com
dlls/mfplat/tests/mfplat.c | 9 +- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_private.h | 6 + dlls/winegstreamer/media_source.c | 679 +++++++++++++++++++ dlls/winegstreamer/mfplat.c | 9 + dlls/winegstreamer/winegstreamer.rgs | 32 + dlls/winegstreamer/winegstreamer_classes.idl | 7 + include/mfidl.idl | 1 + 8 files changed, 739 insertions(+), 5 deletions(-) create mode 100644 dlls/winegstreamer/media_source.c
Before you submit this entire series again, can I ask you to please submit less patches at a time? 5 is a reasonable number.
Separately, I think this patch can still be split up more—at least the container_stream_handler and its multiple interfaces can be separate patches. On the other hand, that's an mfplat thing, so I'll leave it up to Nikolay.
On 4/1/20 6:08 PM, Zebediah Figura wrote:
On 4/1/20 5:05 PM, Derek Lesho wrote:
Signed-off-by: Derek Lesho dlesho@codeweavers.com
dlls/mfplat/tests/mfplat.c | 9 +- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_private.h | 6 + dlls/winegstreamer/media_source.c | 679 +++++++++++++++++++ dlls/winegstreamer/mfplat.c | 9 + dlls/winegstreamer/winegstreamer.rgs | 32 + dlls/winegstreamer/winegstreamer_classes.idl | 7 + include/mfidl.idl | 1 + 8 files changed, 739 insertions(+), 5 deletions(-) create mode 100644 dlls/winegstreamer/media_source.c
Before you submit this entire series again, can I ask you to please submit less patches at a time? 5 is a reasonable number.
Sure thing.
Separately, I think this patch can still be split up more—at least the container_stream_handler and its multiple interfaces can be separate patches. On the other hand, that's an mfplat thing, so I'll leave it up to Nikolay.