Still working in progress.
-- v2: temp winegstreamer: Implement wg_muxer. winegstreamer: Introduce array_reserve to unix lib. winegstreamer: Add mpeg4 sink class factory. winegstreamer: Add enum wg_container_type. winegstreamer: Make find_element accept NULL caps. winegstreamer: Add async command handling to media sink. winegstreamer: Add IMFClockStateSink stubs for media sink. winegstreamer: Implement IMFMediaEventGenerator for media sink. winegstreamer: Add stubs for stream sink. winegstreamer: Add stubs for media sink. mf/tests: Test IMFClockStateSink in shutdown state. mf/tests: Test sample processing for MPEG4 media sink. mf/tests: Use IMFMediaEventGenerator interface in event wait helper.
From: Ziqing Hui zhui@codeweavers.com
--- dlls/mf/tests/mf.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 743946f7fba..379bfd5c4b0 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -1971,18 +1971,23 @@ static IMFAsyncCallback *create_test_callback(BOOL check_media_event) }
#define wait_media_event(a, b, c, d, e) wait_media_event_(__LINE__, a, b, c, d, e) -static HRESULT wait_media_event_(int line, IMFMediaSession *session, IMFAsyncCallback *callback, +static HRESULT wait_media_event_(int line, void *object, IMFAsyncCallback *callback, MediaEventType expect_type, DWORD timeout, PROPVARIANT *value) { struct test_callback *impl = impl_from_IMFAsyncCallback(callback); + IMFMediaEventGenerator *event_generator; MediaEventType type; HRESULT hr, status; DWORD ret; GUID guid;
+ hr = IUnknown_QueryInterface((IUnknown *)object, &IID_IMFMediaEventGenerator, (void **)&event_generator); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + do { - hr = IMFMediaSession_BeginGetEvent(session, &impl->IMFAsyncCallback_iface, (IUnknown *)session); + hr = IMFMediaEventGenerator_BeginGetEvent(event_generator, + &impl->IMFAsyncCallback_iface, (IUnknown *)event_generator); ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); ret = WaitForSingleObject(impl->event, timeout); ok_(__FILE__, line)(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", ret); @@ -1990,6 +1995,8 @@ static HRESULT wait_media_event_(int line, IMFMediaSession *session, IMFAsyncCal ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); } while (type != expect_type);
+ IMFMediaEventGenerator_Release(event_generator); + ok_(__FILE__, line)(type == expect_type, "got type %lu\n", type);
hr = IMFMediaEvent_GetExtendedType(impl->media_event, &guid); @@ -2006,18 +2013,23 @@ static HRESULT wait_media_event_(int line, IMFMediaSession *session, IMFAsyncCal }
#define wait_media_event_until_blocking(a, b, c, d, e) wait_media_event_until_blocking_(__LINE__, a, b, c, d, e) -static HRESULT wait_media_event_until_blocking_(int line, IMFMediaSession *session, IMFAsyncCallback *callback, - MediaEventType expect_type, DWORD timeout, PROPVARIANT *value) +static HRESULT wait_media_event_until_blocking_(int line, void *object, IMFAsyncCallback *callback, + MediaEventType expect_type, DWORD timeout, PROPVARIANT *value) { struct test_callback *impl = impl_from_IMFAsyncCallback(callback); + IMFMediaEventGenerator *event_generator; MediaEventType type; HRESULT hr, status; DWORD ret; GUID guid;
+ hr = IUnknown_QueryInterface((IUnknown *)object, &IID_IMFMediaEventGenerator, (void **)&event_generator); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + do { - hr = IMFMediaSession_BeginGetEvent(session, &impl->IMFAsyncCallback_iface, (IUnknown *)session); + hr = IMFMediaEventGenerator_BeginGetEvent(event_generator, + &impl->IMFAsyncCallback_iface, (IUnknown *)event_generator); ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); ret = WaitForSingleObject(impl->event, timeout); if (ret == WAIT_TIMEOUT) return WAIT_TIMEOUT; @@ -2025,6 +2037,8 @@ static HRESULT wait_media_event_until_blocking_(int line, IMFMediaSession *sessi ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); } while (type != expect_type);
+ IMFMediaEventGenerator_Release(event_generator); + ok_(__FILE__, line)(type == expect_type, "got type %lu\n", type);
hr = IMFMediaEvent_GetExtendedType(impl->media_event, &guid);
From: Ziqing Hui zhui@codeweavers.com
--- dlls/mf/tests/mf.c | 168 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 161 insertions(+), 7 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 379bfd5c4b0..30b2d0eb8d5 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -81,6 +81,44 @@ HRESULT (WINAPI *pMFGetTopoNodeCurrentType)(IMFTopologyNode *node, DWORD stream, HRESULT (WINAPI *pMFCreateDXGIDeviceManager)(UINT *token, IMFDXGIDeviceManager **manager); BOOL has_video_processor;
+const static BYTE test_h264_header[] = +{ + 0x00, 0x00, 0x01, 0x67, 0x64, 0x00, 0x14, 0xac, 0xd9, 0x46, 0x36, 0xc0, + 0x5a, 0x83, 0x03, 0x03, 0x52, 0x80, 0x00, 0x00, 0x03, 0x00, 0x80, 0x00, + 0x00, 0x03, 0x01, 0x47, 0x8a, 0x14, 0xcb, 0x00, 0x00, 0x01, 0x68, 0xeb, + 0xec, 0xb2, 0x2c, +}; + +const static BYTE test_h264_frame[] = +{ + 0x00, 0x00, 0x01, 0x65, 0x88, 0x84, 0x00, 0x17, 0xff, 0xe8, 0xff, 0xf2, + 0x3f, 0x9b, 0x0f, 0x5c, 0xdd, 0x08, 0x3f, 0xf5, 0xe8, 0xfc, 0xbb, 0xed, + 0x67, 0xbd, 0x22, 0xa1, 0xd7, 0xba, 0x21, 0xe6, 0x75, 0x8d, 0x3c, 0x11, + 0x12, 0x18, 0xd9, 0x81, 0x11, 0x75, 0x6a, 0x9b, 0x14, 0xcc, 0x50, 0x96, + 0x3f, 0x70, 0xd4, 0xf8, 0x3d, 0x17, 0xc9, 0x4e, 0x23, 0x96, 0x4e, 0x37, + 0xb9, 0xbe, 0x74, 0xf1, 0x53, 0x9f, 0xb4, 0x59, 0x57, 0x32, 0xee, 0x7f, + 0xfd, 0xea, 0x48, 0x2d, 0x80, 0x9e, 0x19, 0x61, 0x59, 0xcb, 0x14, 0xbd, + 0xcd, 0xb3, 0x3e, 0x81, 0x05, 0x56, 0x8e, 0x9c, 0xd9, 0x3f, 0x01, 0x6b, + 0x3e, 0x3c, 0x95, 0xcb, 0xc4, 0x1c, 0xfd, 0xb1, 0x72, 0x23, 0xbb, 0x7b, + 0xf8, 0xb8, 0x50, 0xda, 0x3c, 0x70, 0xc5, 0x7a, 0xc1, 0xe3, 0x13, 0x29, + 0x79, 0x7a, 0xbe, 0xff, 0x5a, 0x26, 0xc3, 0xb6, 0x56, 0xbb, 0x6a, 0x97, + 0x4d, 0xdc, 0x1e, 0x07, 0x4a, 0xaf, 0xff, 0x9e, 0x60, 0x20, 0x69, 0xf9, + 0xfc, 0xe8, 0xe0, 0xa6, 0x10, 0xa3, 0xab, 0x0f, 0xbe, 0x9c, 0x59, 0xa6, + 0xb4, 0x69, 0x4d, 0xc6, 0x09, 0xaa, 0xa8, 0xab, 0xbc, 0x64, 0xfd, 0x7e, + 0xde, 0x5f, 0x55, 0x06, 0xb9, 0xae, 0xce, 0x76, 0x5f, 0x63, 0x3a, 0x12, + 0x2e, 0x9e, 0xbd, 0x28, 0x71, 0x69, 0x34, 0xc9, 0xab, 0x20, 0x28, 0xb8, + 0x4b, 0x20, 0x1c, 0xe1, 0xc8, 0xc4, 0xa6, 0x7d, 0x73, 0x53, 0x73, 0xbf, + 0x21, 0x19, 0x9a, 0xd5, 0xa7, 0xcf, 0x47, 0x5a, 0xda, 0x34, 0x50, 0x7b, + 0x69, 0x8e, 0x52, 0xb2, 0x61, 0xda, 0x8e, 0x20, 0x95, 0x73, 0xc5, 0xb9, + 0x2b, 0x14, 0x48, 0xc1, 0x68, 0x3a, 0x7c, 0x78, 0x14, 0xe9, 0x92, 0xc7, + 0x89, 0xfc, 0x4f, 0x90, 0xaf, 0x54, 0x1e, 0xd0, 0xf0, 0x00, 0x25, 0x3e, + 0xcf, 0xbc, 0x18, 0xad, 0xc9, 0x6b, 0x9d, 0x77, 0x21, 0x6d, 0x5d, 0x2e, + 0xce, 0x09, 0xd9, 0xee, 0x79, 0xb6, 0xe7, 0xe4, 0xf4, 0x7f, 0x6e, 0x11, + 0x7b, 0x32, 0xfb, 0xf6, 0x8c, 0xbf, 0x05, 0xe1, 0x9a, 0x9c, 0x6c, 0x48, + 0x79, 0xac, 0x8f, 0x16, 0xb6, 0xf6, 0x3e, 0x76, 0xab, 0x40, 0x28, 0x61, + +}; + static BOOL is_vista(void) { return !pMFGetTopoNodeCurrentType; @@ -1866,7 +1904,7 @@ struct test_callback
HANDLE event; IMFMediaEvent *media_event; - BOOL check_media_event; + BOOL check_media_event, finalize_media_sink; };
static struct test_callback *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) @@ -1940,6 +1978,18 @@ static HRESULT WINAPI testcallback_Invoke(IMFAsyncCallback *iface, IMFAsyncResul IUnknown_Release(object); }
+ if (callback->finalize_media_sink) + { + hr = IMFAsyncResult_GetObject(result, &object); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = IMFAsyncResult_GetState(result, &object); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFFinalizableMediaSink_EndFinalize((IMFFinalizableMediaSink *)object, result); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IUnknown_Release(object); + } + SetEvent(callback->event);
return S_OK; @@ -1954,7 +2004,7 @@ static const IMFAsyncCallbackVtbl testcallbackvtbl = testcallback_Invoke, };
-static IMFAsyncCallback *create_test_callback(BOOL check_media_event) +static IMFAsyncCallback *create_test_callback(BOOL check_media_event, BOOL finalize_media_sink) { struct test_callback *callback;
@@ -1963,6 +2013,7 @@ static IMFAsyncCallback *create_test_callback(BOOL check_media_event)
callback->refcount = 1; callback->check_media_event = check_media_event; + callback->finalize_media_sink = finalize_media_sink; callback->IMFAsyncCallback_iface.lpVtbl = &testcallbackvtbl; callback->event = CreateEventW(NULL, FALSE, FALSE, NULL); ok(!!callback->event, "CreateEventW failed, error %lu\n", GetLastError()); @@ -1970,6 +2021,26 @@ static IMFAsyncCallback *create_test_callback(BOOL check_media_event) return &callback->IMFAsyncCallback_iface; }
+static void test_callback_set(IMFAsyncCallback *iface, BOOL check_media_event, BOOL finalize_media_sink) +{ + struct test_callback *callback = CONTAINING_RECORD(iface, struct test_callback, IMFAsyncCallback_iface); + + callback->check_media_event = check_media_event; + callback->finalize_media_sink = finalize_media_sink; + + if (!callback->check_media_event && callback->media_event) + { + IMFMediaEvent_Release(callback->media_event); + callback->media_event = NULL; + } +} + +static DWORD test_callback_wait_event(IMFAsyncCallback *iface, DWORD time) +{ + struct test_callback *callback = CONTAINING_RECORD(iface, struct test_callback, IMFAsyncCallback_iface); + return WaitForSingleObject(callback->event, time); +} + #define wait_media_event(a, b, c, d, e) wait_media_event_(__LINE__, a, b, c, d, e) static HRESULT wait_media_event_(int line, void *object, IMFAsyncCallback *callback, MediaEventType expect_type, DWORD timeout, PROPVARIANT *value) @@ -2146,8 +2217,8 @@ static void test_media_session_events(void) hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); ok(hr == S_OK, "Startup failure, hr %#lx.\n", hr);
- callback = create_test_callback(TRUE); - callback2 = create_test_callback(TRUE); + callback = create_test_callback(TRUE, FALSE); + callback2 = create_test_callback(TRUE, FALSE);
hr = MFCreateMediaSession(NULL, &session); ok(hr == S_OK, "Failed to create media session, hr %#lx.\n", hr); @@ -2202,7 +2273,7 @@ static void test_media_session_events(void) IMFAsyncCallback_Release(callback2);
- callback = create_test_callback(TRUE); + callback = create_test_callback(TRUE, FALSE);
hr = MFCreateMediaSession(NULL, &session); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -4302,7 +4373,7 @@ static void test_presentation_clock(void) hr = IMFPresentationClock_GetCorrelatedTime(clock, 0, &time, &systime); ok(hr == S_OK, "got hr %#lx.\n", hr);
- callback = create_test_callback(FALSE); + callback = create_test_callback(FALSE, FALSE); timer_callback = impl_from_IMFAsyncCallback(callback); hr = IMFTimer_SetTimer(timer, 0, 100000, callback, NULL, &timer_cancel_key); ok(hr == S_OK, "got hr %#lx.\n", hr); @@ -4968,7 +5039,7 @@ static void test_sample_grabber_orientation(GUID subtype) goto done; }
- callback = create_test_callback(TRUE); + callback = create_test_callback(TRUE, FALSE); grabber_callback = impl_from_IMFSampleGrabberSinkCallback(create_test_grabber_callback());
grabber_callback->ready_event = CreateEventW(NULL, FALSE, FALSE, NULL); @@ -7065,6 +7136,88 @@ static void test_mpeg4_media_sink(void) IMFMediaType_Release(audio_type); }
+static void test_mpeg4_media_sink_process(void) +{ + DWORD width = 96, height = 96, fps = 1, ret; + IMFClockStateSink *clock_sink; + IMFAsyncCallback *callback; + IMFStreamSink *stream_sink; + IMFByteStream *bytestream; + IMFMediaSink *media_sink; + IMFMediaType *input_type; + IMFSample *input_sample; + PROPVARIANT variant; + HRESULT hr; + + hr = MFCreateMediaType(&input_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetGUID(input_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetGUID(input_type, &MF_MT_SUBTYPE, &MFVideoFormat_H264); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT64(input_type, &MF_MT_FRAME_SIZE, ((UINT64)width << 32) | height); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT64(input_type, &MF_MT_FRAME_RATE, ((UINT64)fps << 32) | 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetBlob(input_type, &MF_MT_MPEG_SEQUENCE_HEADER, test_h264_header, sizeof(test_h264_header)); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateTempFile(MF_ACCESSMODE_READWRITE, MF_OPENMODE_DELETE_IF_EXIST, MF_FILEFLAGS_NONE, &bytestream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateMPEG4MediaSink(bytestream, input_type, NULL, &media_sink); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaType_Release(input_type); + if (hr != S_OK) + { + IMFByteStream_Release(bytestream); + return; + } + hr = IMFMediaSink_QueryInterface(media_sink, &IID_IMFClockStateSink, (void **)&clock_sink); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Start streaming. */ + callback = create_test_callback(TRUE, FALSE); + hr = IMFMediaSink_GetStreamSinkById(media_sink, 1, &stream_sink); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFClockStateSink_OnClockStart(clock_sink, MFGetSystemTime(), 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + wait_media_event(stream_sink, callback, MEStreamSinkStarted, 3000, &variant); + + /* Process sample. */ + input_sample = create_sample(test_h264_frame, sizeof(test_h264_frame)); + hr = IMFSample_SetSampleTime(input_sample, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFSample_SetSampleDuration(input_sample, 10000000); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFStreamSink_ProcessSample(stream_sink, input_sample); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFSample_Release(input_sample); + + /* Wait for stop event to make sure samples have been processed. */ + hr = IMFClockStateSink_OnClockStop(clock_sink, MFGetSystemTime()); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + wait_media_event(stream_sink, callback, MEStreamSinkStopped, 3000, &variant); + + /* Finalize. */ + test_callback_set(callback, FALSE, TRUE); + hr = IMFFinalizableMediaSink_BeginFinalize((IMFFinalizableMediaSink *)media_sink, callback, (IUnknown *)media_sink); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ret = test_callback_wait_event(callback, 3000); + ok(ret == WAIT_OBJECT_0, "Unexpected ret %#lx\n", ret); + hr = IMFMediaSink_Shutdown(media_sink); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFClockStateSink_Release(clock_sink); + ret = IMFMediaSink_Release(media_sink); + ok(ret == 0, "Release returned %lu.\n", ret); + + IMFAsyncCallback_Release(callback); + IMFStreamSink_Release(stream_sink); + IMFByteStream_Release(bytestream); +} + START_TEST(mf) { init_functions(); @@ -7099,4 +7252,5 @@ START_TEST(mf) test_MFGetTopoNodeCurrentType(); test_MFRequireProtectedEnvironment(); test_mpeg4_media_sink(); + test_mpeg4_media_sink_process(); }
From: Ziqing Hui zhui@codeweavers.com
--- dlls/mf/tests/mf.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 30b2d0eb8d5..d365bfe2b8c 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -6844,6 +6844,7 @@ static void test_mpeg4_media_sink(void) DWORD id, count, flags, width = 16, height = 16, fps = 10; IMFMediaType *audio_type, *video_type, *media_type; IMFMediaTypeHandler *type_handler = NULL; + IMFClockStateSink *clock_sink; IMFPresentationClock *clock; IMFStreamSink *stream_sink; HRESULT hr; @@ -7122,6 +7123,20 @@ static void test_mpeg4_media_sink(void) hr = IMFMediaSink_GetCharacteristics(sink, &flags); ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr);
+ hr = IMFMediaSink_QueryInterface(sink, &IID_IMFClockStateSink, (void **)&clock_sink); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFClockStateSink_OnClockStart(clock_sink, MFGetSystemTime(), 0); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + hr = IMFClockStateSink_OnClockStop(clock_sink, MFGetSystemTime()); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + hr = IMFClockStateSink_OnClockPause(clock_sink, MFGetSystemTime()); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + hr = IMFClockStateSink_OnClockRestart(clock_sink, MFGetSystemTime()); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + hr = IMFClockStateSink_OnClockSetRate(clock_sink, MFGetSystemTime(), 1.0f); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + IMFClockStateSink_Release(clock_sink); + IMFMediaTypeHandler_Release(type_handler); IMFMediaSink_Release(sink); IMFMediaSink_Release(sink_video);
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_private.h | 2 + dlls/winegstreamer/media_sink.c | 282 +++++++++++++++++++++++++++++++ 3 files changed, 285 insertions(+) create mode 100644 dlls/winegstreamer/media_sink.c
diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 1c701bfa9f6..78bdd6c0ef1 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -11,6 +11,7 @@ C_SRCS = \ color_convert.c \ h264_decoder.c \ main.c \ + media_sink.c \ media_source.c \ mfplat.c \ quartz_parser.c \ diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 920ffc9d252..ed867f741d9 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -35,6 +35,7 @@ #include "mfidl.h" #include "wine/debug.h" #include "wine/strmbase.h" +#include "wine/mfinternal.h"
#include "unixlib.h"
@@ -121,6 +122,7 @@ HRESULT wma_decoder_create(IUnknown *outer, IUnknown **out); HRESULT wmv_decoder_create(IUnknown *outer, IUnknown **out); HRESULT resampler_create(IUnknown *outer, IUnknown **out); HRESULT color_convert_create(IUnknown *outer, IUnknown **out); +HRESULT sink_class_factory_create(IUnknown *outer, IUnknown **out);
bool amt_from_wg_format(AM_MEDIA_TYPE *mt, const struct wg_format *format, bool wm); bool amt_to_wg_format(const AM_MEDIA_TYPE *mt, struct wg_format *format); diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c new file mode 100644 index 00000000000..d7d24c2235a --- /dev/null +++ b/dlls/winegstreamer/media_sink.c @@ -0,0 +1,282 @@ +/* Gstreamer Media Sink + * + * Copyright 2023 Ziqing Hui for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "gst_private.h" +#include "mferror.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +struct media_sink +{ + IMFFinalizableMediaSink IMFFinalizableMediaSink_iface; + LONG refcount; + CRITICAL_SECTION cs; + bool shutdown; + + IMFByteStream *bytestream; +}; + +static struct media_sink *impl_from_IMFFinalizableMediaSink(IMFFinalizableMediaSink *iface) +{ + return CONTAINING_RECORD(iface, struct media_sink, IMFFinalizableMediaSink_iface); +} + +static HRESULT WINAPI media_sink_QueryInterface(IMFFinalizableMediaSink *iface, REFIID riid, void **obj) +{ + TRACE("iface %p, riid %s, obj %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFFinalizableMediaSink) || + IsEqualIID(riid, &IID_IMFMediaSink) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + } + else + { + WARN("Unsupported interface %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*obj); + + return S_OK; +} + +static ULONG WINAPI media_sink_AddRef(IMFFinalizableMediaSink *iface) +{ + struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface); + ULONG refcount = InterlockedIncrement(&media_sink->refcount); + TRACE("iface %p, refcount %lu.\n", iface, refcount); + return refcount; +} + +static ULONG WINAPI media_sink_Release(IMFFinalizableMediaSink *iface) +{ + struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface); + ULONG refcount = InterlockedDecrement(&media_sink->refcount); + + TRACE("iface %p, refcount %lu.\n", iface, refcount); + + if (!refcount) + { + IMFFinalizableMediaSink_Shutdown(iface); + IMFByteStream_Release(media_sink->bytestream); + free(media_sink); + } + + return refcount; +} + +static HRESULT WINAPI media_sink_GetCharacteristics(IMFFinalizableMediaSink *iface, DWORD *flags) +{ + FIXME("iface %p, flags %p stub!\n", iface, flags); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_AddStreamSink(IMFFinalizableMediaSink *iface, DWORD stream_sink_id, + IMFMediaType *media_type, IMFStreamSink **stream_sink) +{ + FIXME("iface %p, stream_sink_id %#lx, media_type %p, stream_sink %p stub!\n", + iface, stream_sink_id, media_type, stream_sink); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_RemoveStreamSink(IMFFinalizableMediaSink *iface, DWORD stream_sink_id) +{ + FIXME("iface %p, stream_sink_id %#lx stub!\n", iface, stream_sink_id); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_GetStreamSinkCount(IMFFinalizableMediaSink *iface, DWORD *count) +{ + FIXME("iface %p, count %p stub!\n", iface, count); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_GetStreamSinkByIndex(IMFFinalizableMediaSink *iface, DWORD index, + IMFStreamSink **stream) +{ + FIXME("iface %p, index %lu, stream %p stub!\n", iface, index, stream); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_GetStreamSinkById(IMFFinalizableMediaSink *iface, DWORD stream_sink_id, + IMFStreamSink **stream) +{ + FIXME("iface %p, stream_sink_id %#lx, stream %p stub!\n", iface, stream_sink_id, stream); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_SetPresentationClock(IMFFinalizableMediaSink *iface, IMFPresentationClock *clock) +{ + FIXME("iface %p, clock %p stub!\n", iface, clock); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_GetPresentationClock(IMFFinalizableMediaSink *iface, IMFPresentationClock **clock) +{ + FIXME("iface %p, clock %p stub!\n", iface, clock); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_Shutdown(IMFFinalizableMediaSink *iface) +{ + struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface); + + TRACE("iface %p.\n", iface); + + EnterCriticalSection(&media_sink->cs); + + if (media_sink->shutdown) + { + LeaveCriticalSection(&media_sink->cs); + return MF_E_SHUTDOWN; + } + + IMFByteStream_Close(media_sink->bytestream); + + media_sink->shutdown = TRUE; + + LeaveCriticalSection(&media_sink->cs); + + return S_OK; +} + +static HRESULT WINAPI media_sink_BeginFinalize(IMFFinalizableMediaSink *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + FIXME("iface %p, callback %p, state %p stub!\n", iface, callback, state); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_EndFinalize(IMFFinalizableMediaSink *iface, IMFAsyncResult *result) +{ + FIXME("iface %p, result %p stub!\n", iface, result); + + return E_NOTIMPL; +} + +static const IMFFinalizableMediaSinkVtbl media_sink_vtbl = +{ + media_sink_QueryInterface, + media_sink_AddRef, + media_sink_Release, + media_sink_GetCharacteristics, + media_sink_AddStreamSink, + media_sink_RemoveStreamSink, + media_sink_GetStreamSinkCount, + media_sink_GetStreamSinkByIndex, + media_sink_GetStreamSinkById, + media_sink_SetPresentationClock, + media_sink_GetPresentationClock, + media_sink_Shutdown, + media_sink_BeginFinalize, + media_sink_EndFinalize, +}; + +static HRESULT media_sink_create(IMFByteStream *bytestream, struct media_sink **out) +{ + struct media_sink *media_sink; + + TRACE("bytestream %p, out %p.\n", bytestream, out); + + if (!bytestream) + return E_POINTER; + + if (!(media_sink = calloc(1, sizeof(*media_sink)))) + return E_OUTOFMEMORY; + + media_sink->IMFFinalizableMediaSink_iface.lpVtbl = &media_sink_vtbl; + media_sink->refcount = 1; + InitializeCriticalSection(&media_sink->cs); + media_sink->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs"); + IMFByteStream_AddRef(media_sink->bytestream = bytestream); + + *out = media_sink; + TRACE("Created media sink %p.\n", media_sink); + + return S_OK; +} + +static HRESULT WINAPI sink_class_factory_QueryInterface(IMFSinkClassFactory *iface, REFIID riid, void **out) +{ + if (IsEqualIID(riid, &IID_IMFSinkClassFactory) + || IsEqualIID(riid, &IID_IUnknown)) + { + *out = iface; + IMFSinkClassFactory_AddRef(iface); + return S_OK; + } + + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI sink_class_factory_AddRef(IMFSinkClassFactory *iface) +{ + return 2; +} + +static ULONG WINAPI sink_class_factory_Release(IMFSinkClassFactory *iface) +{ + return 1; +} + +static HRESULT WINAPI sink_class_factory_CreateMediaSink(IMFSinkClassFactory *iface, IMFByteStream *bytestream, + IMFMediaType *video_type, IMFMediaType *audio_type, IMFMediaSink **out) +{ + struct media_sink *media_sink; + HRESULT hr; + + TRACE("iface %p, bytestream %p, video_type %p, audio_type %p, out %p.\n", + iface, bytestream, video_type, audio_type, out); + + if (FAILED(hr = media_sink_create(bytestream, &media_sink))) + return hr; + + *out = (IMFMediaSink *)&media_sink->IMFFinalizableMediaSink_iface; + return S_OK; +} + +static const IMFSinkClassFactoryVtbl sink_class_factory_vtbl = +{ + sink_class_factory_QueryInterface, + sink_class_factory_AddRef, + sink_class_factory_Release, + sink_class_factory_CreateMediaSink, +}; + +static IMFSinkClassFactory sink_class_factory = { &sink_class_factory_vtbl }; + +HRESULT sink_class_factory_create(IUnknown *outer, IUnknown **out) +{ + *out = (IUnknown *)&sink_class_factory; + return S_OK; +}
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/media_sink.c | 291 +++++++++++++++++++++++++++++++- 1 file changed, 288 insertions(+), 3 deletions(-)
diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c index d7d24c2235a..57f38729bbb 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -19,11 +19,23 @@
#include "gst_private.h" #include "mferror.h" +#include "mfapi.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
+struct stream_sink +{ + IMFStreamSink IMFStreamSink_iface; + LONG refcount; + DWORD id; + + IMFMediaType *type; + IMFMediaEventQueue *event_queue; + struct media_sink *media_sink; +}; + struct media_sink { IMFFinalizableMediaSink IMFFinalizableMediaSink_iface; @@ -32,13 +44,225 @@ struct media_sink bool shutdown;
IMFByteStream *bytestream; + + struct stream_sink **stream_sinks; + size_t stream_sink_count; + size_t stream_sink_size; };
+static struct stream_sink *impl_from_IMFStreamSink(IMFStreamSink *iface) +{ + return CONTAINING_RECORD(iface, struct stream_sink, IMFStreamSink_iface); +} + static struct media_sink *impl_from_IMFFinalizableMediaSink(IMFFinalizableMediaSink *iface) { return CONTAINING_RECORD(iface, struct media_sink, IMFFinalizableMediaSink_iface); }
+static HRESULT WINAPI stream_sink_QueryInterface(IMFStreamSink *iface, REFIID riid, void **obj) +{ + struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); + + TRACE("iface %p, riid %s, obj %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFStreamSink) || + IsEqualGUID(riid, &IID_IMFMediaEventGenerator) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = &stream_sink->IMFStreamSink_iface; + } + else + { + WARN("Unsupported interface %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*obj); + + return S_OK; +} + +static ULONG WINAPI stream_sink_AddRef(IMFStreamSink *iface) +{ + struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); + ULONG refcount = InterlockedIncrement(&stream_sink->refcount); + TRACE("iface %p, refcount %lu.\n", iface, refcount); + return refcount; +} + +static ULONG WINAPI stream_sink_Release(IMFStreamSink *iface) +{ + struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); + ULONG refcount = InterlockedDecrement(&stream_sink->refcount); + + TRACE("iface %p, refcount %lu.\n", iface, refcount); + + if (!refcount) + { + IMFMediaEventQueue_Release(stream_sink->event_queue); + IMFFinalizableMediaSink_Release(&stream_sink->media_sink->IMFFinalizableMediaSink_iface); + IMFMediaType_Release(stream_sink->type); + free(stream_sink); + } + + return refcount; +} + +static HRESULT WINAPI stream_sink_GetEvent(IMFStreamSink *iface, DWORD flags, IMFMediaEvent **event) +{ + struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); + + TRACE("iface %p, flags %#lx, event %p.\n", iface, flags, event); + + return IMFMediaEventQueue_GetEvent(stream_sink->event_queue, flags, event); +} + +static HRESULT WINAPI stream_sink_BeginGetEvent(IMFStreamSink *iface, IMFAsyncCallback *callback, + IUnknown *state) +{ + struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); + + TRACE("iface %p, callback %p, state %p.\n", iface, callback, state); + + return IMFMediaEventQueue_BeginGetEvent(stream_sink->event_queue, callback, state); +} + +static HRESULT WINAPI stream_sink_EndGetEvent(IMFStreamSink *iface, IMFAsyncResult *result, + IMFMediaEvent **event) +{ + struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); + + TRACE("iface %p, result %p, event %p.\n", iface, result, event); + + return IMFMediaEventQueue_EndGetEvent(stream_sink->event_queue, result, event); +} + +static HRESULT WINAPI stream_sink_QueueEvent(IMFStreamSink *iface, MediaEventType event_type, + REFGUID ext_type, HRESULT hr, const PROPVARIANT *value) +{ + struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); + + TRACE("iface %p, event_type %lu, ext_type %s, hr %#lx, value %p.\n", + iface, event_type, debugstr_guid(ext_type), hr, value); + + return IMFMediaEventQueue_QueueEventParamVar(stream_sink->event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI stream_sink_GetMediaSink(IMFStreamSink *iface, IMFMediaSink **ret) +{ + struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); + + TRACE("iface %p, ret %p.\n", iface, ret); + + *ret = (IMFMediaSink *)&stream_sink->media_sink->IMFFinalizableMediaSink_iface; + + return S_OK; +} + +static HRESULT WINAPI stream_sink_GetIdentifier(IMFStreamSink *iface, DWORD *identifier) +{ + struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); + + TRACE("iface %p, identifier %p.\n", iface, identifier); + + *identifier = stream_sink->id; + + return S_OK; +} + +static HRESULT WINAPI stream_sink_GetMediaTypeHandler(IMFStreamSink *iface, IMFMediaTypeHandler **handler) +{ + FIXME("iface %p, handler %p stub!\n", iface, handler); + + return E_NOTIMPL; +} + +static HRESULT WINAPI stream_sink_ProcessSample(IMFStreamSink *iface, IMFSample *sample) +{ + FIXME("iface %p, sample %p stub!\n", iface, sample); + + return E_NOTIMPL; +} + +static HRESULT WINAPI stream_sink_PlaceMarker(IMFStreamSink *iface, MFSTREAMSINK_MARKER_TYPE marker_type, + const PROPVARIANT *marker_value, const PROPVARIANT *context_value) +{ + FIXME("iface %p, marker_type %d, marker_value %p, context_value %p stub!\n", + iface, marker_type, marker_value, context_value); + + return E_NOTIMPL; +} + +static HRESULT WINAPI stream_sink_Flush(IMFStreamSink *iface) +{ + FIXME("iface %p stub!\n", iface); + + return E_NOTIMPL; +} + +static const IMFStreamSinkVtbl stream_sink_vtbl = +{ + stream_sink_QueryInterface, + stream_sink_AddRef, + stream_sink_Release, + stream_sink_GetEvent, + stream_sink_BeginGetEvent, + stream_sink_EndGetEvent, + stream_sink_QueueEvent, + stream_sink_GetMediaSink, + stream_sink_GetIdentifier, + stream_sink_GetMediaTypeHandler, + stream_sink_ProcessSample, + stream_sink_PlaceMarker, + stream_sink_Flush, +}; + +static HRESULT stream_sink_create(DWORD stream_sink_id, IMFMediaType *media_type, struct media_sink *media_sink, + struct stream_sink **out) +{ + struct stream_sink *stream_sink; + HRESULT hr; + + TRACE("stream_sink_id %#lx, media_type %p, media_sink %p, out %p.\n", + stream_sink_id, media_type, media_sink, out); + + if (!(stream_sink = calloc(1, sizeof(*stream_sink)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = MFCreateEventQueue(&stream_sink->event_queue))) + { + free(stream_sink); + return hr; + } + + stream_sink->IMFStreamSink_iface.lpVtbl = &stream_sink_vtbl; + stream_sink->refcount = 1; + stream_sink->id = stream_sink_id; + IMFMediaType_AddRef(stream_sink->type = media_type); + stream_sink->media_sink = media_sink; + IMFFinalizableMediaSink_AddRef(&media_sink->IMFFinalizableMediaSink_iface); + + TRACE("Created stream sink %p.\n", stream_sink); + *out = stream_sink; + + return S_OK; +} + +static struct stream_sink *media_sink_get_stream_sink_by_id(struct media_sink *media_sink, DWORD id) +{ + size_t i; + + for (i = 0; i < media_sink->stream_sink_count; ++i) + { + if (media_sink->stream_sinks[i]->id == id) + return media_sink->stream_sinks[i]; + } + + return NULL; +} + static HRESULT WINAPI media_sink_QueryInterface(IMFFinalizableMediaSink *iface, REFIID riid, void **obj) { TRACE("iface %p, riid %s, obj %p.\n", iface, debugstr_guid(riid), obj); @@ -96,10 +320,41 @@ static HRESULT WINAPI media_sink_GetCharacteristics(IMFFinalizableMediaSink *ifa static HRESULT WINAPI media_sink_AddStreamSink(IMFFinalizableMediaSink *iface, DWORD stream_sink_id, IMFMediaType *media_type, IMFStreamSink **stream_sink) { - FIXME("iface %p, stream_sink_id %#lx, media_type %p, stream_sink %p stub!\n", + struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface); + struct stream_sink *object; + HRESULT hr; + + TRACE("iface %p, stream_sink_id %#lx, media_type %p, stream_sink %p.\n", iface, stream_sink_id, media_type, stream_sink);
- return E_NOTIMPL; + if (!media_type) + return E_POINTER; + if (media_sink_get_stream_sink_by_id(media_sink, stream_sink_id)) + return MF_E_STREAMSINK_EXISTS; + + if (FAILED(hr = stream_sink_create(stream_sink_id, media_type, media_sink, &object))) + { + WARN("Failed to create stream sink, hr %#lx.\n", hr); + return hr; + } + + EnterCriticalSection(&media_sink->cs); + + if (!array_reserve((void **)&media_sink->stream_sinks, + &media_sink->stream_sink_size, media_sink->stream_sink_count + 1, sizeof(*object))) + { + IMFStreamSink_Release(&object->IMFStreamSink_iface); + LeaveCriticalSection(&media_sink->cs); + return E_OUTOFMEMORY; + } + media_sink->stream_sinks[media_sink->stream_sink_count++] = object; + + LeaveCriticalSection(&media_sink->cs); + + if (stream_sink) + IMFStreamSink_AddRef(*stream_sink = &object->IMFStreamSink_iface); + + return S_OK; }
static HRESULT WINAPI media_sink_RemoveStreamSink(IMFFinalizableMediaSink *iface, DWORD stream_sink_id) @@ -149,6 +404,7 @@ static HRESULT WINAPI media_sink_GetPresentationClock(IMFFinalizableMediaSink *i static HRESULT WINAPI media_sink_Shutdown(IMFFinalizableMediaSink *iface) { struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface); + unsigned int i;
TRACE("iface %p.\n", iface);
@@ -160,6 +416,14 @@ static HRESULT WINAPI media_sink_Shutdown(IMFFinalizableMediaSink *iface) return MF_E_SHUTDOWN; }
+ for (i = 0; i < media_sink->stream_sink_count; ++i) + { + struct stream_sink *stream_sink = media_sink->stream_sinks[i]; + IMFMediaEventQueue_Shutdown(stream_sink->event_queue); + IMFStreamSink_Release(&stream_sink->IMFStreamSink_iface); + } + free(media_sink->stream_sinks); + IMFByteStream_Close(media_sink->bytestream);
media_sink->shutdown = TRUE; @@ -252,6 +516,7 @@ static ULONG WINAPI sink_class_factory_Release(IMFSinkClassFactory *iface) static HRESULT WINAPI sink_class_factory_CreateMediaSink(IMFSinkClassFactory *iface, IMFByteStream *bytestream, IMFMediaType *video_type, IMFMediaType *audio_type, IMFMediaSink **out) { + IMFFinalizableMediaSink *media_sink_iface; struct media_sink *media_sink; HRESULT hr;
@@ -260,8 +525,28 @@ static HRESULT WINAPI sink_class_factory_CreateMediaSink(IMFSinkClassFactory *if
if (FAILED(hr = media_sink_create(bytestream, &media_sink))) return hr; + media_sink_iface = &media_sink->IMFFinalizableMediaSink_iface; + + if (video_type) + { + if (FAILED(hr = IMFFinalizableMediaSink_AddStreamSink(media_sink_iface, 1, video_type, NULL))) + { + IMFFinalizableMediaSink_Shutdown(media_sink_iface); + IMFFinalizableMediaSink_Release(media_sink_iface); + return hr; + } + } + if (audio_type) + { + if (FAILED(hr = IMFFinalizableMediaSink_AddStreamSink(media_sink_iface, 2, audio_type, NULL))) + { + IMFFinalizableMediaSink_Shutdown(media_sink_iface); + IMFFinalizableMediaSink_Release(media_sink_iface); + return hr; + } + }
- *out = (IMFMediaSink *)&media_sink->IMFFinalizableMediaSink_iface; + *out = (IMFMediaSink *)media_sink_iface; return S_OK; }
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/media_sink.c | 92 +++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+)
diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c index 57f38729bbb..c9560a7ecb0 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -39,11 +39,13 @@ struct stream_sink struct media_sink { IMFFinalizableMediaSink IMFFinalizableMediaSink_iface; + IMFMediaEventGenerator IMFMediaEventGenerator_iface; LONG refcount; CRITICAL_SECTION cs; bool shutdown;
IMFByteStream *bytestream; + IMFMediaEventQueue *event_queue;
struct stream_sink **stream_sinks; size_t stream_sink_count; @@ -60,6 +62,11 @@ static struct media_sink *impl_from_IMFFinalizableMediaSink(IMFFinalizableMediaS return CONTAINING_RECORD(iface, struct media_sink, IMFFinalizableMediaSink_iface); }
+static struct media_sink *impl_from_IMFMediaEventGenerator(IMFMediaEventGenerator *iface) +{ + return CONTAINING_RECORD(iface, struct media_sink, IMFMediaEventGenerator_iface); +} + static HRESULT WINAPI stream_sink_QueryInterface(IMFStreamSink *iface, REFIID riid, void **obj) { struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); @@ -265,6 +272,8 @@ static struct stream_sink *media_sink_get_stream_sink_by_id(struct media_sink *m
static HRESULT WINAPI media_sink_QueryInterface(IMFFinalizableMediaSink *iface, REFIID riid, void **obj) { + struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface); + TRACE("iface %p, riid %s, obj %p.\n", iface, debugstr_guid(riid), obj);
if (IsEqualIID(riid, &IID_IMFFinalizableMediaSink) || @@ -273,6 +282,10 @@ static HRESULT WINAPI media_sink_QueryInterface(IMFFinalizableMediaSink *iface, { *obj = iface; } + else if (IsEqualGUID(riid, &IID_IMFMediaEventGenerator)) + { + *obj = &media_sink->IMFMediaEventGenerator_iface; + } else { WARN("Unsupported interface %s.\n", debugstr_guid(riid)); @@ -303,6 +316,7 @@ static ULONG WINAPI media_sink_Release(IMFFinalizableMediaSink *iface) if (!refcount) { IMFFinalizableMediaSink_Shutdown(iface); + IMFMediaEventQueue_Release(media_sink->event_queue); IMFByteStream_Release(media_sink->bytestream); free(media_sink); } @@ -424,6 +438,7 @@ static HRESULT WINAPI media_sink_Shutdown(IMFFinalizableMediaSink *iface) } free(media_sink->stream_sinks);
+ IMFMediaEventQueue_Shutdown(media_sink->event_queue); IMFByteStream_Close(media_sink->bytestream);
media_sink->shutdown = TRUE; @@ -465,9 +480,79 @@ static const IMFFinalizableMediaSinkVtbl media_sink_vtbl = media_sink_EndFinalize, };
+static HRESULT WINAPI media_sink_event_QueryInterface(IMFMediaEventGenerator *iface, REFIID riid, void **obj) +{ + struct media_sink *media_sink = impl_from_IMFMediaEventGenerator(iface); + return IMFFinalizableMediaSink_QueryInterface(&media_sink->IMFFinalizableMediaSink_iface, riid, obj); +} + +static ULONG WINAPI media_sink_event_AddRef(IMFMediaEventGenerator *iface) +{ + struct media_sink *media_sink = impl_from_IMFMediaEventGenerator(iface); + return IMFFinalizableMediaSink_AddRef(&media_sink->IMFFinalizableMediaSink_iface); +} + +static ULONG WINAPI media_sink_event_Release(IMFMediaEventGenerator *iface) +{ + struct media_sink *media_sink = impl_from_IMFMediaEventGenerator(iface); + return IMFFinalizableMediaSink_Release(&media_sink->IMFFinalizableMediaSink_iface); +} + +static HRESULT WINAPI media_sink_event_GetEvent(IMFMediaEventGenerator *iface, DWORD flags, IMFMediaEvent **event) +{ + struct media_sink *media_sink = impl_from_IMFMediaEventGenerator(iface); + + TRACE("iface %p, flags %#lx, event %p.\n", iface, flags, event); + + return IMFMediaEventQueue_GetEvent(media_sink->event_queue, flags, event); +} + +static HRESULT WINAPI media_sink_event_BeginGetEvent(IMFMediaEventGenerator *iface, IMFAsyncCallback *callback, + IUnknown *state) +{ + struct media_sink *media_sink = impl_from_IMFMediaEventGenerator(iface); + + TRACE("iface %p, callback %p, state %p.\n", iface, callback, state); + + return IMFMediaEventQueue_BeginGetEvent(media_sink->event_queue, callback, state); +} + +static HRESULT WINAPI media_sink_event_EndGetEvent(IMFMediaEventGenerator *iface, IMFAsyncResult *result, + IMFMediaEvent **event) +{ + struct media_sink *media_sink = impl_from_IMFMediaEventGenerator(iface); + + TRACE("iface %p, result %p, event %p.\n", iface, result, event); + + return IMFMediaEventQueue_EndGetEvent(media_sink->event_queue, result, event); +} + +static HRESULT WINAPI media_sink_event_QueueEvent(IMFMediaEventGenerator *iface, MediaEventType event_type, + REFGUID ext_type, HRESULT hr, const PROPVARIANT *value) +{ + struct media_sink *media_sink = impl_from_IMFMediaEventGenerator(iface); + + TRACE("iface %p, event_type %lu, ext_type %s, hr %#lx, value %p.\n", + iface, event_type, debugstr_guid(ext_type), hr, value); + + return IMFMediaEventQueue_QueueEventParamVar(media_sink->event_queue, event_type, ext_type, hr, value); +} + +static const IMFMediaEventGeneratorVtbl media_sink_event_vtbl = +{ + media_sink_event_QueryInterface, + media_sink_event_AddRef, + media_sink_event_Release, + media_sink_event_GetEvent, + media_sink_event_BeginGetEvent, + media_sink_event_EndGetEvent, + media_sink_event_QueueEvent, +}; + static HRESULT media_sink_create(IMFByteStream *bytestream, struct media_sink **out) { struct media_sink *media_sink; + HRESULT hr;
TRACE("bytestream %p, out %p.\n", bytestream, out);
@@ -477,7 +562,14 @@ static HRESULT media_sink_create(IMFByteStream *bytestream, struct media_sink ** if (!(media_sink = calloc(1, sizeof(*media_sink)))) return E_OUTOFMEMORY;
+ if (FAILED(hr = MFCreateEventQueue(&media_sink->event_queue))) + { + free(media_sink); + return hr; + } + media_sink->IMFFinalizableMediaSink_iface.lpVtbl = &media_sink_vtbl; + media_sink->IMFMediaEventGenerator_iface.lpVtbl = &media_sink_event_vtbl; media_sink->refcount = 1; InitializeCriticalSection(&media_sink->cs); media_sink->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs");
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/media_sink.c | 76 +++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+)
diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c index c9560a7ecb0..290a923d310 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -40,6 +40,7 @@ struct media_sink { IMFFinalizableMediaSink IMFFinalizableMediaSink_iface; IMFMediaEventGenerator IMFMediaEventGenerator_iface; + IMFClockStateSink IMFClockStateSink_iface; LONG refcount; CRITICAL_SECTION cs; bool shutdown; @@ -67,6 +68,11 @@ static struct media_sink *impl_from_IMFMediaEventGenerator(IMFMediaEventGenerato return CONTAINING_RECORD(iface, struct media_sink, IMFMediaEventGenerator_iface); }
+static struct media_sink *impl_from_IMFClockStateSink(IMFClockStateSink *iface) +{ + return CONTAINING_RECORD(iface, struct media_sink, IMFClockStateSink_iface); +} + static HRESULT WINAPI stream_sink_QueryInterface(IMFStreamSink *iface, REFIID riid, void **obj) { struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); @@ -286,6 +292,10 @@ static HRESULT WINAPI media_sink_QueryInterface(IMFFinalizableMediaSink *iface, { *obj = &media_sink->IMFMediaEventGenerator_iface; } + else if (IsEqualIID(riid, &IID_IMFClockStateSink)) + { + *obj = &media_sink->IMFClockStateSink_iface; + } else { WARN("Unsupported interface %s.\n", debugstr_guid(riid)); @@ -549,6 +559,71 @@ static const IMFMediaEventGeneratorVtbl media_sink_event_vtbl = media_sink_event_QueueEvent, };
+static HRESULT WINAPI media_sink_clock_sink_QueryInterface(IMFClockStateSink *iface, REFIID riid, void **obj) +{ + struct media_sink *media_sink = impl_from_IMFClockStateSink(iface); + return IMFFinalizableMediaSink_QueryInterface(&media_sink->IMFFinalizableMediaSink_iface, riid, obj); +} + +static ULONG WINAPI media_sink_clock_sink_AddRef(IMFClockStateSink *iface) +{ + struct media_sink *media_sink = impl_from_IMFClockStateSink(iface); + return IMFFinalizableMediaSink_AddRef(&media_sink->IMFFinalizableMediaSink_iface); +} + +static ULONG WINAPI media_sink_clock_sink_Release(IMFClockStateSink *iface) +{ + struct media_sink *media_sink = impl_from_IMFClockStateSink(iface); + return IMFFinalizableMediaSink_Release(&media_sink->IMFFinalizableMediaSink_iface); +} + +static HRESULT WINAPI media_sink_clock_sink_OnClockStart(IMFClockStateSink *iface, MFTIME systime, LONGLONG offset) +{ + FIXME("iface %p, systime %s, offset %s stub!\n", iface, debugstr_time(systime), debugstr_time(offset)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_clock_sink_OnClockStop(IMFClockStateSink *iface, MFTIME systime) +{ + FIXME("iface %p, systime %s stub!\n", iface, debugstr_time(systime)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_clock_sink_OnClockPause(IMFClockStateSink *iface, MFTIME systime) +{ + FIXME("%p, %s stub!\n", iface, debugstr_time(systime)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_clock_sink_OnClockRestart(IMFClockStateSink *iface, MFTIME systime) +{ + FIXME("iface %p, systime %s stub!\n", iface, debugstr_time(systime)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_clock_sink_OnClockSetRate(IMFClockStateSink *iface, MFTIME systime, float rate) +{ + FIXME("iface %p, systime %s, rate %f stub!\n", iface, debugstr_time(systime), rate); + + return E_NOTIMPL; +} + +static const IMFClockStateSinkVtbl media_sink_clock_sink_vtbl = +{ + media_sink_clock_sink_QueryInterface, + media_sink_clock_sink_AddRef, + media_sink_clock_sink_Release, + media_sink_clock_sink_OnClockStart, + media_sink_clock_sink_OnClockStop, + media_sink_clock_sink_OnClockPause, + media_sink_clock_sink_OnClockRestart, + media_sink_clock_sink_OnClockSetRate, +}; + static HRESULT media_sink_create(IMFByteStream *bytestream, struct media_sink **out) { struct media_sink *media_sink; @@ -570,6 +645,7 @@ static HRESULT media_sink_create(IMFByteStream *bytestream, struct media_sink **
media_sink->IMFFinalizableMediaSink_iface.lpVtbl = &media_sink_vtbl; media_sink->IMFMediaEventGenerator_iface.lpVtbl = &media_sink_event_vtbl; + media_sink->IMFClockStateSink_iface.lpVtbl = &media_sink_clock_sink_vtbl; media_sink->refcount = 1; InitializeCriticalSection(&media_sink->cs); media_sink->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs");
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/media_sink.c | 308 ++++++++++++++++++++++++++++++-- 1 file changed, 297 insertions(+), 11 deletions(-)
diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c index 290a923d310..b5dee71370b 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -25,6 +25,21 @@
WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
+enum async_op +{ + ASYNC_START, + ASYNC_STOP, + ASYNC_PAUSE, +}; + +struct async_command +{ + IUnknown IUnknown_iface; + LONG refcount; + + enum async_op op; +}; + struct stream_sink { IMFStreamSink IMFStreamSink_iface; @@ -41,12 +56,22 @@ struct media_sink IMFFinalizableMediaSink IMFFinalizableMediaSink_iface; IMFMediaEventGenerator IMFMediaEventGenerator_iface; IMFClockStateSink IMFClockStateSink_iface; + IMFAsyncCallback async_callback; LONG refcount; CRITICAL_SECTION cs; - bool shutdown; + + enum + { + STATE_OPENED, + STATE_STARTED, + STATE_STOPPED, + STATE_PAUSED, + STATE_SHUTDOWN, + } state;
IMFByteStream *bytestream; IMFMediaEventQueue *event_queue; + DWORD async_queue;
struct stream_sink **stream_sinks; size_t stream_sink_count; @@ -73,6 +98,71 @@ static struct media_sink *impl_from_IMFClockStateSink(IMFClockStateSink *iface) return CONTAINING_RECORD(iface, struct media_sink, IMFClockStateSink_iface); }
+static struct media_sink *impl_from_async_callback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct media_sink, async_callback); +} + +static struct async_command *impl_from_async_command_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct async_command, IUnknown_iface); +} + +static HRESULT WINAPI async_command_QueryInterface(IUnknown *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IUnknown_AddRef(iface); + return S_OK; + } + + WARN("Unsupported interface %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI async_command_AddRef(IUnknown *iface) +{ + struct async_command *command = impl_from_async_command_IUnknown(iface); + return InterlockedIncrement(&command->refcount); +} + +static ULONG WINAPI async_command_Release(IUnknown *iface) +{ + struct async_command *command = impl_from_async_command_IUnknown(iface); + ULONG refcount = InterlockedDecrement(&command->refcount); + + if (!refcount) + free(command); + + return refcount; +} + +static const IUnknownVtbl async_command_vtbl = +{ + async_command_QueryInterface, + async_command_AddRef, + async_command_Release, +}; + +static HRESULT async_command_create(enum async_op op, struct async_command **out) +{ + struct async_command *command; + + if (!(command = calloc(1, sizeof(*command)))) + return E_OUTOFMEMORY; + + command->IUnknown_iface.lpVtbl = &async_command_vtbl; + command->refcount = 1; + command->op = op; + + TRACE("Created async command %p.\n", command); + *out = command; + + return S_OK; +} + static HRESULT WINAPI stream_sink_QueryInterface(IMFStreamSink *iface, REFIID riid, void **obj) { struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); @@ -276,6 +366,74 @@ static struct stream_sink *media_sink_get_stream_sink_by_id(struct media_sink *m return NULL; }
+static HRESULT media_sink_queue_command(struct media_sink *media_sink, enum async_op op) +{ + struct async_command *command; + HRESULT hr; + + if (media_sink->state == STATE_SHUTDOWN) + return MF_E_SHUTDOWN; + + if (FAILED(hr = (async_command_create(op, &command)))) + return hr; + + return MFPutWorkItem(media_sink->async_queue, &media_sink->async_callback, &command->IUnknown_iface); +} + +static HRESULT media_sink_queue_stream_event(struct media_sink *media_sink, MediaEventType type) +{ + HRESULT hr; + size_t i; + + for (i = 0; i < media_sink->stream_sink_count; ++i) + { + struct stream_sink *stream_sink = media_sink->stream_sinks[i]; + if (FAILED(hr = IMFMediaEventQueue_QueueEventParamVar(stream_sink->event_queue, type, &GUID_NULL, S_OK, NULL))) + return hr; + } + + return S_OK; +} + +static HRESULT media_sink_start(struct media_sink *media_sink) +{ + HRESULT hr; + + EnterCriticalSection(&media_sink->cs); + + media_sink->state = STATE_STARTED; + hr = media_sink_queue_stream_event(media_sink, MEStreamSinkStarted); + + LeaveCriticalSection(&media_sink->cs); + return hr; +} + +static HRESULT media_sink_stop(struct media_sink *media_sink) +{ + HRESULT hr; + + EnterCriticalSection(&media_sink->cs); + + media_sink->state = STATE_STOPPED; + hr = media_sink_queue_stream_event(media_sink, MEStreamSinkStopped); + + LeaveCriticalSection(&media_sink->cs); + return hr; +} + +static HRESULT media_sink_pause(struct media_sink *media_sink) +{ + HRESULT hr; + + EnterCriticalSection(&media_sink->cs); + + media_sink->state = STATE_PAUSED; + hr = media_sink_queue_stream_event(media_sink, MEStreamSinkPaused); + + LeaveCriticalSection(&media_sink->cs); + return hr; +} + static HRESULT WINAPI media_sink_QueryInterface(IMFFinalizableMediaSink *iface, REFIID riid, void **obj) { struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface); @@ -434,7 +592,7 @@ static HRESULT WINAPI media_sink_Shutdown(IMFFinalizableMediaSink *iface)
EnterCriticalSection(&media_sink->cs);
- if (media_sink->shutdown) + if (media_sink->state == STATE_SHUTDOWN) { LeaveCriticalSection(&media_sink->cs); return MF_E_SHUTDOWN; @@ -448,10 +606,11 @@ static HRESULT WINAPI media_sink_Shutdown(IMFFinalizableMediaSink *iface) } free(media_sink->stream_sinks);
+ MFUnlockWorkQueue(media_sink->async_queue); IMFMediaEventQueue_Shutdown(media_sink->event_queue); IMFByteStream_Close(media_sink->bytestream);
- media_sink->shutdown = TRUE; + media_sink->state = STATE_SHUTDOWN;
LeaveCriticalSection(&media_sink->cs);
@@ -579,30 +738,62 @@ static ULONG WINAPI media_sink_clock_sink_Release(IMFClockStateSink *iface)
static HRESULT WINAPI media_sink_clock_sink_OnClockStart(IMFClockStateSink *iface, MFTIME systime, LONGLONG offset) { - FIXME("iface %p, systime %s, offset %s stub!\n", iface, debugstr_time(systime), debugstr_time(offset)); + struct media_sink *media_sink = impl_from_IMFClockStateSink(iface); + HRESULT hr;
- return E_NOTIMPL; + TRACE("iface %p, systime %s, offset %s.\n", iface, debugstr_time(systime), debugstr_time(offset)); + + EnterCriticalSection(&media_sink->cs); + + hr = media_sink_queue_command(media_sink, ASYNC_START); + + LeaveCriticalSection(&media_sink->cs); + return hr; }
static HRESULT WINAPI media_sink_clock_sink_OnClockStop(IMFClockStateSink *iface, MFTIME systime) { - FIXME("iface %p, systime %s stub!\n", iface, debugstr_time(systime)); + struct media_sink *media_sink = impl_from_IMFClockStateSink(iface); + HRESULT hr;
- return E_NOTIMPL; + TRACE("iface %p, systime %s.\n", iface, debugstr_time(systime)); + + EnterCriticalSection(&media_sink->cs); + + hr = media_sink_queue_command(media_sink, ASYNC_STOP); + + LeaveCriticalSection(&media_sink->cs); + return hr; }
static HRESULT WINAPI media_sink_clock_sink_OnClockPause(IMFClockStateSink *iface, MFTIME systime) { - FIXME("%p, %s stub!\n", iface, debugstr_time(systime)); + struct media_sink *media_sink = impl_from_IMFClockStateSink(iface); + HRESULT hr;
- return E_NOTIMPL; + TRACE("iface %p, systime %s.\n", iface, debugstr_time(systime)); + + EnterCriticalSection(&media_sink->cs); + + hr = media_sink_queue_command(media_sink, ASYNC_PAUSE); + + LeaveCriticalSection(&media_sink->cs); + return hr; }
static HRESULT WINAPI media_sink_clock_sink_OnClockRestart(IMFClockStateSink *iface, MFTIME systime) { - FIXME("iface %p, systime %s stub!\n", iface, debugstr_time(systime)); + struct media_sink *media_sink = impl_from_IMFClockStateSink(iface); + HRESULT hr;
- return E_NOTIMPL; + TRACE("iface %p, systime %s.\n", iface, debugstr_time(systime)); + + EnterCriticalSection(&media_sink->cs); + + hr = media_sink_queue_command(media_sink, ASYNC_START); + + LeaveCriticalSection(&media_sink->cs); + return hr; }
static HRESULT WINAPI media_sink_clock_sink_OnClockSetRate(IMFClockStateSink *iface, MFTIME systime, float rate) @@ -624,6 +815,92 @@ static const IMFClockStateSinkVtbl media_sink_clock_sink_vtbl = media_sink_clock_sink_OnClockSetRate, };
+static HRESULT WINAPI media_sink_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) +{ + TRACE("iface %p, riid %s, obj %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFAsyncCallback) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFAsyncCallback_AddRef(iface); + return S_OK; + } + + WARN("Unsupported interface %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI media_sink_callback_AddRef(IMFAsyncCallback *iface) +{ + struct media_sink *sink = impl_from_async_callback(iface); + return IMFFinalizableMediaSink_AddRef(&sink->IMFFinalizableMediaSink_iface); +} + +static ULONG WINAPI media_sink_callback_Release(IMFAsyncCallback *iface) +{ + struct media_sink *sink = impl_from_async_callback(iface); + return IMFFinalizableMediaSink_Release(&sink->IMFFinalizableMediaSink_iface); +} + +static HRESULT WINAPI media_sink_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + TRACE("iface %p, flags %p, queue %p.\n", iface, flags, queue); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *async_result) +{ + struct media_sink *media_sink = impl_from_async_callback(iface); + struct async_command *command; + IUnknown *state; + HRESULT hr; + + TRACE("iface %p, async_result %p.\n", iface, async_result); + + EnterCriticalSection(&media_sink->cs); + + if (FAILED(hr = IMFAsyncResult_GetState(async_result, &state))) + { + LeaveCriticalSection(&media_sink->cs); + return hr; + } + + command = impl_from_async_command_IUnknown(state); + switch (command->op) + { + case ASYNC_START: + hr = media_sink_start(media_sink); + break; + case ASYNC_STOP: + hr = media_sink_stop(media_sink); + break; + case ASYNC_PAUSE: + hr = media_sink_pause(media_sink); + break; + default: + WARN("Unsupported op %u.\n", command->op); + break; + } + + LeaveCriticalSection(&media_sink->cs); + + IUnknown_Release(state); + + return hr; +} + +static const IMFAsyncCallbackVtbl media_sink_callback_vtbl = +{ + media_sink_callback_QueryInterface, + media_sink_callback_AddRef, + media_sink_callback_Release, + media_sink_callback_GetParameters, + media_sink_callback_Invoke, +}; + static HRESULT media_sink_create(IMFByteStream *bytestream, struct media_sink **out) { struct media_sink *media_sink; @@ -643,10 +920,19 @@ static HRESULT media_sink_create(IMFByteStream *bytestream, struct media_sink ** return hr; }
+ if (FAILED(hr = MFAllocateWorkQueue(&media_sink->async_queue))) + { + IMFMediaEventQueue_Release(media_sink->event_queue); + free(media_sink); + return hr; + } + media_sink->IMFFinalizableMediaSink_iface.lpVtbl = &media_sink_vtbl; media_sink->IMFMediaEventGenerator_iface.lpVtbl = &media_sink_event_vtbl; media_sink->IMFClockStateSink_iface.lpVtbl = &media_sink_clock_sink_vtbl; + media_sink->async_callback.lpVtbl = &media_sink_callback_vtbl; media_sink->refcount = 1; + media_sink->state = STATE_OPENED; InitializeCriticalSection(&media_sink->cs); media_sink->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs"); IMFByteStream_AddRef(media_sink->bytestream = bytestream);
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/unixlib.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-)
diff --git a/dlls/winegstreamer/unixlib.c b/dlls/winegstreamer/unixlib.c index 6ed38260536..4fcf7d0d469 100644 --- a/dlls/winegstreamer/unixlib.c +++ b/dlls/winegstreamer/unixlib.c @@ -87,15 +87,21 @@ GstElement *find_element(GstElementFactoryListType type, GstCaps *src_caps, GstC if (!(transforms = gst_element_factory_list_get_elements(type, GST_RANK_MARGINAL))) goto done;
- tmp = gst_element_factory_list_filter(transforms, src_caps, GST_PAD_SINK, FALSE); - gst_plugin_feature_list_free(transforms); - if (!(transforms = tmp)) - goto done; + if (src_caps) + { + tmp = gst_element_factory_list_filter(transforms, src_caps, GST_PAD_SINK, FALSE); + gst_plugin_feature_list_free(transforms); + if (!(transforms = tmp)) + goto done; + }
- tmp = gst_element_factory_list_filter(transforms, sink_caps, GST_PAD_SRC, FALSE); - gst_plugin_feature_list_free(transforms); - if (!(transforms = tmp)) - goto done; + if (sink_caps) + { + tmp = gst_element_factory_list_filter(transforms, sink_caps, GST_PAD_SRC, FALSE); + gst_plugin_feature_list_free(transforms); + if (!(transforms = tmp)) + goto done; + }
transforms = g_list_sort(transforms, gst_plugin_feature_rank_compare_func); for (tmp = transforms; tmp != NULL && element == NULL; tmp = tmp->next)
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/unix_private.h | 1 + dlls/winegstreamer/unixlib.h | 7 +++++++ dlls/winegstreamer/wg_format.c | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+)
diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index db6bf3ebb2e..ba7ce33e7c3 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -45,6 +45,7 @@ extern bool link_element_to_sink(GstElement *element, GstPad *sink_pad) DECLSPEC extern void wg_format_from_caps(struct wg_format *format, const GstCaps *caps) DECLSPEC_HIDDEN; extern bool wg_format_compare(const struct wg_format *a, const struct wg_format *b) DECLSPEC_HIDDEN; extern GstCaps *wg_format_to_caps(const struct wg_format *format) DECLSPEC_HIDDEN; +extern GstCaps *wg_container_type_to_caps(enum wg_container_type container_type) DECLSPEC_HIDDEN;
/* wg_transform.c */
diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 702bd7aa69b..5a56b1d2b74 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -164,6 +164,13 @@ struct wg_format } u; };
+typedef UINT32 wg_container_type; +enum wg_container_type +{ + WG_CONTAINER_TYPE_UNKNOWN = 0, + WG_CONTAINER_TYPE_QUICKTIME, +}; + enum wg_sample_flag { WG_SAMPLE_FLAG_INCOMPLETE = 1, diff --git a/dlls/winegstreamer/wg_format.c b/dlls/winegstreamer/wg_format.c index bd0ebf892c1..195fe37ea53 100644 --- a/dlls/winegstreamer/wg_format.c +++ b/dlls/winegstreamer/wg_format.c @@ -753,3 +753,21 @@ bool wg_format_compare(const struct wg_format *a, const struct wg_format *b) assert(0); return false; } + +GstCaps *wg_container_type_to_caps(enum wg_container_type container_type) +{ + GstCaps *caps = NULL; + + switch (container_type) + { + case WG_CONTAINER_TYPE_QUICKTIME: + caps = gst_caps_new_empty_simple("video/x-quicktime"); + break; + case WG_CONTAINER_TYPE_UNKNOWN: + default: + GST_WARNING("Container type %u not implemented.\n", container_type); + break; + } + + return caps; +}
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/gst_private.h | 2 +- dlls/winegstreamer/main.c | 3 +++ dlls/winegstreamer/media_sink.c | 27 +++++++++++++------- dlls/winegstreamer/winegstreamer_classes.idl | 7 +++++ 4 files changed, 29 insertions(+), 10 deletions(-)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index ed867f741d9..2136abd73a4 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -122,7 +122,7 @@ HRESULT wma_decoder_create(IUnknown *outer, IUnknown **out); HRESULT wmv_decoder_create(IUnknown *outer, IUnknown **out); HRESULT resampler_create(IUnknown *outer, IUnknown **out); HRESULT color_convert_create(IUnknown *outer, IUnknown **out); -HRESULT sink_class_factory_create(IUnknown *outer, IUnknown **out); +HRESULT mpeg4_sink_class_factory_create(IUnknown *outer, IUnknown **out);
bool amt_from_wg_format(AM_MEDIA_TYPE *mt, const struct wg_format *format, bool wm); bool amt_to_wg_format(const AM_MEDIA_TYPE *mt, struct wg_format *format); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index a92d440c42f..d637dd95231 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -612,6 +612,7 @@ static struct class_factory wma_decoder_cf = {{&class_factory_vtbl}, wma_decoder static struct class_factory wmv_decoder_cf = {{&class_factory_vtbl}, wmv_decoder_create}; static struct class_factory resampler_cf = {{&class_factory_vtbl}, resampler_create}; static struct class_factory color_convert_cf = {{&class_factory_vtbl}, color_convert_create}; +static struct class_factory mpeg4_sink_class_factory_cf = {{&class_factory_vtbl}, mpeg4_sink_class_factory_create};
HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void **out) { @@ -646,6 +647,8 @@ HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void **out) factory = &resampler_cf; else if (IsEqualGUID(clsid, &CLSID_CColorConvertDMO)) factory = &color_convert_cf; + else if (IsEqualGUID(clsid, &CLSID_MFMPEG4SinkClassFactory)) + factory = &mpeg4_sink_class_factory_cf; else { FIXME("%s not implemented, returning CLASS_E_CLASSNOTAVAILABLE.\n", debugstr_guid(clsid)); diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c index b5dee71370b..86a69d0d9ba 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -68,6 +68,7 @@ struct media_sink STATE_PAUSED, STATE_SHUTDOWN, } state; + wg_container_type type;
IMFByteStream *bytestream; IMFMediaEventQueue *event_queue; @@ -901,7 +902,7 @@ static const IMFAsyncCallbackVtbl media_sink_callback_vtbl = media_sink_callback_Invoke, };
-static HRESULT media_sink_create(IMFByteStream *bytestream, struct media_sink **out) +static HRESULT media_sink_create(IMFByteStream *bytestream, wg_container_type type, struct media_sink **out) { struct media_sink *media_sink; HRESULT hr; @@ -933,6 +934,7 @@ static HRESULT media_sink_create(IMFByteStream *bytestream, struct media_sink ** media_sink->async_callback.lpVtbl = &media_sink_callback_vtbl; media_sink->refcount = 1; media_sink->state = STATE_OPENED; + media_sink->type = type; InitializeCriticalSection(&media_sink->cs); media_sink->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs"); IMFByteStream_AddRef(media_sink->bytestream = bytestream); @@ -967,8 +969,8 @@ static ULONG WINAPI sink_class_factory_Release(IMFSinkClassFactory *iface) return 1; }
-static HRESULT WINAPI sink_class_factory_CreateMediaSink(IMFSinkClassFactory *iface, IMFByteStream *bytestream, - IMFMediaType *video_type, IMFMediaType *audio_type, IMFMediaSink **out) +static HRESULT WINAPI sink_class_factory_create_media_sink(IMFSinkClassFactory *iface, IMFByteStream *bytestream, + wg_container_type type, IMFMediaType *video_type, IMFMediaType *audio_type, IMFMediaSink **out) { IMFFinalizableMediaSink *media_sink_iface; struct media_sink *media_sink; @@ -977,7 +979,7 @@ static HRESULT WINAPI sink_class_factory_CreateMediaSink(IMFSinkClassFactory *if TRACE("iface %p, bytestream %p, video_type %p, audio_type %p, out %p.\n", iface, bytestream, video_type, audio_type, out);
- if (FAILED(hr = media_sink_create(bytestream, &media_sink))) + if (FAILED(hr = media_sink_create(bytestream, type, &media_sink))) return hr; media_sink_iface = &media_sink->IMFFinalizableMediaSink_iface;
@@ -1004,18 +1006,25 @@ static HRESULT WINAPI sink_class_factory_CreateMediaSink(IMFSinkClassFactory *if return S_OK; }
-static const IMFSinkClassFactoryVtbl sink_class_factory_vtbl = +static HRESULT WINAPI mpeg4_sink_class_factory_CreateMediaSink(IMFSinkClassFactory *iface, IMFByteStream *bytestream, + IMFMediaType *video_type, IMFMediaType *audio_type, IMFMediaSink **out) +{ + return sink_class_factory_create_media_sink(iface, + bytestream, WG_CONTAINER_TYPE_QUICKTIME, video_type, audio_type, out); +} + +static const IMFSinkClassFactoryVtbl mpeg4_sink_class_factory_vtbl = { sink_class_factory_QueryInterface, sink_class_factory_AddRef, sink_class_factory_Release, - sink_class_factory_CreateMediaSink, + mpeg4_sink_class_factory_CreateMediaSink, };
-static IMFSinkClassFactory sink_class_factory = { &sink_class_factory_vtbl }; +static IMFSinkClassFactory mpeg4_sink_class_factory = { &mpeg4_sink_class_factory_vtbl };
-HRESULT sink_class_factory_create(IUnknown *outer, IUnknown **out) +HRESULT mpeg4_sink_class_factory_create(IUnknown *outer, IUnknown **out) { - *out = (IUnknown *)&sink_class_factory; + *out = (IUnknown *)&mpeg4_sink_class_factory; return S_OK; } diff --git a/dlls/winegstreamer/winegstreamer_classes.idl b/dlls/winegstreamer/winegstreamer_classes.idl index 30a99c9acfb..db36fc368c0 100644 --- a/dlls/winegstreamer/winegstreamer_classes.idl +++ b/dlls/winegstreamer/winegstreamer_classes.idl @@ -111,3 +111,10 @@ coclass CResamplerMediaObject {} uuid(98230571-0087-4204-b020-3282538e57d3) ] coclass CColorConvertDMO {} + +[ + helpstring("MF MPEG4 Sink Class Factory"), + threading(both), + uuid(a22c4fc7-6e91-4e1d-89e9-53b2667b72ba) +] +coclass MFMPEG4SinkClassFactory {}
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/unix_private.h | 1 + dlls/winegstreamer/unixlib.c | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+)
diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index ba7ce33e7c3..4c7540c29b6 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -33,6 +33,7 @@ GST_DEBUG_CATEGORY_EXTERN(wine) DECLSPEC_HIDDEN;
extern NTSTATUS wg_init_gstreamer(void *args) DECLSPEC_HIDDEN;
+extern bool array_reserve(void **elements, size_t *capacity, size_t count, size_t size) DECLSPEC_HIDDEN; extern GstStreamType stream_type_from_caps(GstCaps *caps) DECLSPEC_HIDDEN; extern GstElement *create_element(const char *name, const char *plugin_set) DECLSPEC_HIDDEN; extern GstElement *find_element(GstElementFactoryListType type, GstCaps *src_caps, GstCaps *sink_caps) DECLSPEC_HIDDEN; diff --git a/dlls/winegstreamer/unixlib.c b/dlls/winegstreamer/unixlib.c index 4fcf7d0d469..642d67d0ce6 100644 --- a/dlls/winegstreamer/unixlib.c +++ b/dlls/winegstreamer/unixlib.c @@ -47,6 +47,33 @@
GST_DEBUG_CATEGORY(wine);
+bool array_reserve(void **elements, size_t *capacity, size_t count, size_t size) +{ + unsigned int new_capacity, max_capacity; + void *new_elements; + + if (count <= *capacity) + return TRUE; + + max_capacity = ~(size_t)0 / size; + if (count > max_capacity) + return FALSE; + + new_capacity = max(4, *capacity); + while (new_capacity < count && new_capacity <= max_capacity / 2) + new_capacity *= 2; + if (new_capacity < count) + new_capacity = max_capacity; + + if (!(new_elements = realloc(*elements, new_capacity * size))) + return FALSE; + + *elements = new_elements; + *capacity = new_capacity; + + return TRUE; +} + GstStreamType stream_type_from_caps(GstCaps *caps) { const gchar *media_type;
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_private.h | 8 + dlls/winegstreamer/main.c | 110 +++++++++ dlls/winegstreamer/unix_private.h | 10 + dlls/winegstreamer/unixlib.h | 43 ++++ dlls/winegstreamer/wg_muxer.c | 372 ++++++++++++++++++++++++++++++ dlls/winegstreamer/wg_parser.c | 65 ++++++ 7 files changed, 609 insertions(+) create mode 100644 dlls/winegstreamer/wg_muxer.c
diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 78bdd6c0ef1..f963dcea7f0 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -22,6 +22,7 @@ C_SRCS = \ video_processor.c \ wg_allocator.c \ wg_format.c \ + wg_muxer.c \ wg_parser.c \ wg_sample.c \ wg_transform.c \ diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 2136abd73a4..0307b7ba375 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -110,6 +110,14 @@ bool wg_transform_get_status(wg_transform_t transform, bool *accepts_input); HRESULT wg_transform_drain(wg_transform_t transform); HRESULT wg_transform_flush(wg_transform_t transform);
+wg_muxer_t wg_muxer_create(enum wg_container_type container_type); +void wg_muxer_destroy(wg_muxer_t muxer); +HRESULT wg_muxer_add_stream(wg_muxer_t muxer, DWORD stream_id, const struct wg_format *format); +HRESULT wg_muxer_push_sample(wg_muxer_t muxer, struct wg_sample *sample); +bool wg_muxer_get_buffer(wg_muxer_t muxer, uint32_t *size); +HRESULT wg_muxer_copy_buffer(wg_muxer_t muxer, void *buffer, UINT32 size); +void wg_muxer_free_buffer(wg_muxer_t muxer); + unsigned int wg_format_get_max_size(const struct wg_format *format);
HRESULT avi_splitter_create(IUnknown *outer, IUnknown **out); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index d637dd95231..c6a1993740b 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -456,6 +456,116 @@ HRESULT wg_transform_flush(wg_transform_t transform) return S_OK; }
+wg_muxer_t wg_muxer_create(enum wg_container_type container_type) +{ + struct wg_muxer_create_params params = + { + .container_type = container_type, + }; + + TRACE("container_type %up.\n", container_type); + + if (WINE_UNIX_CALL(unix_wg_muxer_create, ¶ms)) + return 0; + + TRACE("Returning muxer %#I64x.\n", params.muxer); + return params.muxer; +} + +void wg_muxer_destroy(wg_muxer_t muxer) +{ + TRACE("muxer %#I64x.\n", muxer); + + WINE_UNIX_CALL(unix_wg_muxer_destroy, &muxer); +} + +HRESULT wg_muxer_add_stream(wg_muxer_t muxer, DWORD stream_id, const struct wg_format *format) +{ + struct wg_muxer_add_stream_params params = + { + .muxer = muxer, + .stream_id = stream_id, + .format = format, + }; + NTSTATUS status; + + TRACE("muxer %#I64x, stream_id %lu, format %p.\n", muxer, stream_id, format); + + if ((status = WINE_UNIX_CALL(unix_wg_muxer_add_stream, ¶ms))) + { + WARN("wg_muxer_push_sample returned status %#lx\n", status); + return HRESULT_FROM_NT(status); + } + + return S_OK; +} + +HRESULT wg_muxer_push_sample(wg_muxer_t muxer, struct wg_sample *sample) +{ + struct wg_muxer_push_sample_params params = + { + .muxer = muxer, + .sample = sample, + }; + NTSTATUS status; + + TRACE("muxer %#I64x, sample %p.\n", muxer, sample); + + if ((status = WINE_UNIX_CALL(unix_wg_muxer_push_sample, ¶ms))) + { + WARN("wg_muxer_push_sample returned status %#lx\n", status); + return HRESULT_FROM_NT(status); + } + + return S_OK; +} + +bool wg_muxer_get_buffer(wg_muxer_t muxer, uint32_t *size) +{ + struct wg_muxer_copy_buffer_params params = + { + .muxer = muxer, + }; + + TRACE("muxer %#I64x, size %p.\n", muxer, size); + + if (!WINE_UNIX_CALL(unix_wg_muxer_get_buffer, ¶ms)) + { + *size = params.size; + return true; + } + + return false; +} + +HRESULT wg_muxer_copy_buffer(wg_muxer_t muxer, void *buffer, uint32_t size) +{ + struct wg_muxer_copy_buffer_params params = + { + .muxer = muxer, + .buffer = buffer, + .size = size, + }; + NTSTATUS status; + + TRACE("muxer %#I64x, buffer %p, size %u.\n", muxer, buffer, size); + + if ((status = WINE_UNIX_CALL(unix_wg_muxer_copy_buffer, ¶ms))) + { + WARN("wg_muxer_copy_buffer returned status %#lx\n", status); + return HRESULT_FROM_NT(status); + } + + return S_OK; +} + +void wg_muxer_free_buffer(wg_muxer_t muxer) +{ + TRACE("muxer %#I64x.\n", muxer); + + WINE_UNIX_CALL(unix_wg_muxer_free_buffer, &muxer); +} + #define ALIGN(n, alignment) (((n) + (alignment) - 1) & ~((alignment) - 1))
unsigned int wg_format_get_stride(const struct wg_format *format) diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 4c7540c29b6..90da144f967 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -59,6 +59,16 @@ extern NTSTATUS wg_transform_get_status(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_transform_drain(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_transform_flush(void *args) DECLSPEC_HIDDEN;
+/* wg_muxer.c */ + +extern NTSTATUS wg_muxer_create(void *args) DECLSPEC_HIDDEN; +extern NTSTATUS wg_muxer_destroy(void *args) DECLSPEC_HIDDEN; +extern NTSTATUS wg_muxer_add_stream(void *args) DECLSPEC_HIDDEN; +extern NTSTATUS wg_muxer_push_sample(void *args) DECLSPEC_HIDDEN; +extern NTSTATUS wg_muxer_get_buffer(void *args) DECLSPEC_HIDDEN; +extern NTSTATUS wg_muxer_copy_buffer(void *args) DECLSPEC_HIDDEN; +extern NTSTATUS wg_muxer_free_buffer(void *args) DECLSPEC_HIDDEN; + /* wg_allocator.c */
static inline BYTE *wg_sample_data(struct wg_sample *sample) diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 5a56b1d2b74..05ef465d58f 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -190,6 +190,7 @@ struct wg_sample UINT32 max_size; UINT32 size; UINT64 data; /* pointer to user memory */ + UINT32 stream_id; };
struct wg_parser_buffer @@ -214,6 +215,8 @@ enum wg_parser_type typedef UINT64 wg_parser_t; typedef UINT64 wg_parser_stream_t; typedef UINT64 wg_transform_t; +typedef UINT64 wg_muxer_t; +typedef UINT64 wg_muxer_stream_t;
struct wg_parser_create_params { @@ -370,6 +373,38 @@ struct wg_transform_get_status_params UINT32 accepts_input; };
+struct wg_muxer_create_params +{ + wg_muxer_t muxer; + wg_container_type container_type; +}; + +struct wg_muxer_add_stream_params +{ + wg_muxer_t muxer; + DWORD stream_id; + const struct wg_format *format; +}; + +struct wg_muxer_push_sample_params +{ + wg_muxer_t muxer; + struct wg_sample *sample; +}; + +struct wg_muxer_get_buffer_params +{ + wg_muxer_t muxer; + UINT32 size; +}; + +struct wg_muxer_copy_buffer_params +{ + wg_muxer_t muxer; + void *buffer; + UINT32 size; +}; + enum unix_funcs { unix_wg_init_gstreamer, @@ -409,6 +444,14 @@ enum unix_funcs unix_wg_transform_get_status, unix_wg_transform_drain, unix_wg_transform_flush, + + unix_wg_muxer_create, + unix_wg_muxer_destroy, + unix_wg_muxer_add_stream, + unix_wg_muxer_push_sample, + unix_wg_muxer_get_buffer, + unix_wg_muxer_copy_buffer, + unix_wg_muxer_free_buffer, };
#endif /* __WINE_WINEGSTREAMER_UNIXLIB_H */ diff --git a/dlls/winegstreamer/wg_muxer.c b/dlls/winegstreamer/wg_muxer.c new file mode 100644 index 00000000000..b3968707bdc --- /dev/null +++ b/dlls/winegstreamer/wg_muxer.c @@ -0,0 +1,372 @@ +/* + * GStreamer muxer backend + * + * Copyright 2023 Ziqing Hui for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#if 0 +#pragma makedep unix +#endif + +#include <assert.h> +#include <stdio.h> + +#include <gst/gst.h> + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "winternl.h" + +#include "unix_private.h" + +struct wg_muxer +{ + GstElement *container, *muxer; + + struct wg_muxer_stream **streams; + size_t streams_size; + size_t stream_count; + + GstPad *my_sink; + GstAtomicQueue *output_queue; + GstMapInfo map_info; + GstBuffer *buffer; +}; + +struct wg_muxer_stream +{ + struct wg_muxer *muxer; + struct wg_format format; + DWORD id; + + GstPad *my_src; +}; + +static struct wg_muxer *get_muxer(wg_muxer_t muxer) +{ + return (struct wg_muxer *)(ULONG_PTR)muxer; +} + +static struct wg_muxer_stream *muxer_get_stream_by_id(struct wg_muxer *muxer, DWORD id) +{ + unsigned int i; + + for (i = 0; i < muxer->stream_count; ++i) + { + if (muxer->streams[i]->id == id) + return muxer->streams[i]; + } + + return NULL; +} + +static GstFlowReturn muxer_sink_chain_cb(GstPad *pad, GstObject *parent, GstBuffer *buffer) +{ + struct wg_muxer *muxer = gst_pad_get_element_private(pad); + + GST_DEBUG("pad %p, parent %p, buffer %p, muxer %p.", pad, parent, buffer, muxer); + + gst_atomic_queue_push(muxer->output_queue, buffer); + + GST_LOG("Pushed buffer %p to queue %p, %u buffers in queue.", + buffer, muxer->output_queue, gst_atomic_queue_length(muxer->output_queue)); + + return GST_FLOW_OK; +} + +static void sample_free_notify(void *arg) +{ + struct wg_sample *sample = arg; + GST_DEBUG("Releasing wg_sample %p", sample); + InterlockedDecrement(&sample->refcount); +} + +NTSTATUS wg_muxer_create(void *args) +{ + struct wg_muxer_create_params *params = args; + NTSTATUS status = STATUS_UNSUCCESSFUL; + GstPadTemplate *template = NULL; + struct wg_muxer *muxer; + GstCaps *sink_caps; + + /* Create wg_muxer object. */ + if (!(muxer = calloc(1, sizeof(*muxer)))) + return STATUS_NO_MEMORY; + if (!(muxer->container = gst_bin_new("wg_muxer"))) + goto out; + if (!(muxer->output_queue = gst_atomic_queue_new(8))) + goto out; + + /* Create sink pad. */ + if (!(sink_caps = wg_container_type_to_caps(params->container_type))) + goto out; + if (!(template = gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_ALWAYS, sink_caps))) + goto out; + muxer->my_sink = gst_pad_new_from_template(template, "sink"); + gst_object_unref(template); + if (!muxer->my_sink) + goto out; + gst_pad_set_element_private(muxer->my_sink, muxer); + gst_pad_set_chain_function(muxer->my_sink, muxer_sink_chain_cb); + + /* Create gstreamer muxer element. */ + if (!(muxer->muxer = find_element(GST_ELEMENT_FACTORY_TYPE_MUXER, NULL, sink_caps))) + goto out; + + /* Link muxer to sink pad. */ + if (!link_element_to_sink(muxer->muxer, muxer->my_sink)) + goto out; + if (!gst_pad_set_active(muxer->my_sink, 1)) + goto out; + + /* Set to pause state. */ + gst_element_set_state(muxer->container, GST_STATE_PAUSED); + if (!gst_element_get_state(muxer->container, NULL, NULL, -1)) + goto out; + + gst_caps_unref(sink_caps); + + GST_INFO("Created winegstreamer muxer %p.", muxer); + params->muxer = (wg_transform_t)(ULONG_PTR)muxer; + + return STATUS_SUCCESS; + +out: + if (muxer->my_sink) + gst_object_unref(muxer->my_sink); + if (sink_caps) + gst_caps_unref(sink_caps); + if (muxer->output_queue) + gst_atomic_queue_unref(muxer->output_queue); + if (muxer->container) + { + gst_element_set_state(muxer->container, GST_STATE_NULL); + gst_object_unref(muxer->container); + } + + return status; +} + +NTSTATUS wg_muxer_destroy(void *args) +{ + struct wg_muxer *muxer = get_muxer(*(wg_muxer_t *)args); + struct wg_muxer_stream *stream; + unsigned int i; + + for (i = 0; i < muxer->stream_count; ++i) + { + stream = muxer->streams[i]; + if (stream->my_src) + gst_object_unref(stream->my_src); + free(stream); + } + if (muxer->my_sink) + gst_object_unref(muxer->my_sink); + if (muxer->output_queue) + gst_atomic_queue_unref(muxer->output_queue); + if (muxer->container) + { + gst_element_set_state(muxer->container, GST_STATE_NULL); + gst_object_unref(muxer->container); + } + + return STATUS_SUCCESS; +} + +NTSTATUS wg_muxer_add_stream(void *args) +{ + struct wg_muxer_add_stream_params *params = args; + struct wg_muxer *muxer = get_muxer(params->muxer); + NTSTATUS status = STATUS_UNSUCCESSFUL; + DWORD stream_id = params->stream_id; + GstPadTemplate *template = NULL; + const char *muxer_sink_pad_name; + struct wg_muxer_stream *stream; + char src_pad_name[32]; + GstPad *muxer_sink; + GstCaps *src_caps; + + /* Create stream object. */ + if (!(stream = calloc(1, sizeof(*stream)))) + return STATUS_NO_MEMORY; + stream->muxer = muxer; + stream->format = *params->format; + stream->id = params->stream_id; + + /* Create src pad. */ + if (!(src_caps = wg_format_to_caps(params->format))) + goto out; + if (!(template = gst_pad_template_new("src", GST_PAD_SRC, GST_PAD_ALWAYS, src_caps))) + goto out; + sprintf(src_pad_name, "wg_muxer_src_%u", stream_id); + stream->my_src = gst_pad_new_from_template(template, src_pad_name); + gst_object_unref(template); + if (!stream->my_src) + goto out; + gst_pad_set_element_private(stream->my_src, stream); + + /* Request src pad. */ + if (stream->format.major_type == WG_MAJOR_TYPE_VIDEO || + stream->format.major_type == WG_MAJOR_TYPE_VIDEO_CINEPAK || + stream->format.major_type == WG_MAJOR_TYPE_VIDEO_H264 || + stream->format.major_type == WG_MAJOR_TYPE_VIDEO_WMV || + stream->format.major_type == WG_MAJOR_TYPE_VIDEO_INDEO) + { + muxer_sink_pad_name = "video_%u"; + } + else if (stream->format.major_type == WG_MAJOR_TYPE_AUDIO || + stream->format.major_type == WG_MAJOR_TYPE_AUDIO_MPEG1 || + stream->format.major_type == WG_MAJOR_TYPE_AUDIO_MPEG4 || + stream->format.major_type == WG_MAJOR_TYPE_AUDIO_WMA) + { + muxer_sink_pad_name = "audio_%u"; + } + else + { + goto out; + } + if (!(muxer_sink = gst_element_request_pad_simple(muxer->muxer, muxer_sink_pad_name))) + goto out; + + /* Link src pad to muxer. */ + if (!gst_pad_link(stream->my_src, muxer_sink)) + { + GST_ERROR("Failed to link %s to muxer.\n", src_pad_name); + goto out; + } + + if (!gst_pad_push_event(stream->my_src, gst_event_new_caps(src_caps))) + goto out; + + /* Add stream to stream array. */ + if (!array_reserve((void **)&muxer->streams, &muxer->streams_size, + muxer->stream_count + 1, sizeof(*muxer->streams))) + goto out; + muxer->streams[muxer->stream_count++] = stream; + + gst_caps_unref(src_caps); + GST_INFO("Created winegstreamer muxer stream %p.", stream); + + return STATUS_SUCCESS; + +out: + if (muxer_sink) + gst_object_unref(muxer_sink); + if (stream->my_src) + gst_object_unref(stream->my_src); + if (src_caps) + gst_object_unref(src_caps); + free(stream); + + return status; +} + +NTSTATUS wg_muxer_push_sample(void *args) +{ + struct wg_muxer_push_sample_params *params = args; + struct wg_muxer *muxer = get_muxer(params->muxer); + struct wg_sample *sample = params->sample; + struct wg_muxer_stream *stream; + GstFlowReturn ret; + GstBuffer *buffer; + + if (!(stream = muxer_get_stream_by_id(muxer, sample->stream_id))) + return STATUS_NOT_FOUND; + + if (!(buffer = gst_buffer_new_wrapped_full(GST_MEMORY_FLAG_READONLY, sample->data, sample->max_size, + 0, sample->size, sample, sample_free_notify))) + { + GST_ERROR("Failed to allocate input buffer"); + return STATUS_NO_MEMORY; + } + + InterlockedIncrement(&sample->refcount); + GST_INFO("Wrapped %u/%u bytes from sample %p to buffer %p", sample->size, sample->max_size, sample, buffer); + + if (sample->flags & WG_SAMPLE_FLAG_HAS_PTS) + GST_BUFFER_PTS(buffer) = sample->pts * 100; + if (sample->flags & WG_SAMPLE_FLAG_HAS_DURATION) + GST_BUFFER_DURATION(buffer) = sample->duration * 100; + if (!(sample->flags & WG_SAMPLE_FLAG_SYNC_POINT)) + GST_BUFFER_FLAG_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT); + if (sample->flags & WG_SAMPLE_FLAG_DISCONTINUITY) + GST_BUFFER_FLAG_SET(buffer, GST_BUFFER_FLAG_DISCONT); + + if (!(ret = gst_pad_push(stream->my_src, buffer))) + { + GST_ERROR("Failed to push buffer %p to pad %s, reason %s.", + buffer, gst_pad_get_name(stream->my_src), gst_flow_get_name(ret)); + return STATUS_UNSUCCESSFUL; + } + + return STATUS_SUCCESS; +} + + +NTSTATUS wg_muxer_get_buffer(void *args) +{ + struct wg_muxer_get_buffer_params *params = args; + struct wg_muxer *muxer = get_muxer(params->muxer); + + if (!muxer->buffer) + { + if (!(muxer->buffer = gst_atomic_queue_pop(muxer->output_queue))) + return STATUS_NO_MEMORY; + + if (!gst_buffer_map(muxer->buffer, &muxer->map_info, GST_MAP_READ)) + { + GST_ERROR("Failed to map buffer %p, dropped", muxer->buffer); + gst_buffer_unref(muxer->buffer); + muxer->buffer = NULL; + return STATUS_NO_MEMORY; + } + } + + params->size = muxer->map_info.size; + + return STATUS_SUCCESS; +} + +NTSTATUS wg_muxer_copy_buffer(void *args) +{ + struct wg_muxer_copy_buffer_params *params = args; + struct wg_muxer *muxer = get_muxer(params->muxer); + + if (!muxer->buffer) + return STATUS_NO_MEMORY; + + if (params->size < muxer->map_info.size) + return STATUS_BUFFER_TOO_SMALL; + + memcpy(params->buffer, muxer->map_info.data, muxer->map_info.size); + + return STATUS_SUCCESS; +} + +NTSTATUS wg_muxer_free_buffer(void *args) +{ + struct wg_muxer *muxer = get_muxer(*(wg_muxer_t *)args); + + if (!muxer->buffer) + return STATUS_NO_MEMORY; + + gst_buffer_unmap(muxer->buffer, &muxer->map_info); + gst_buffer_unref(muxer->buffer); + muxer->buffer = NULL; + + return STATUS_SUCCESS; +} diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 298c05c6b88..b05d1c78cfc 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1945,6 +1945,14 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_transform_get_status), X(wg_transform_drain), X(wg_transform_flush), + + X(wg_muxer_create), + X(wg_muxer_destroy), + X(wg_muxer_add_stream), + X(wg_muxer_push_sample), + X(wg_muxer_get_buffer), + X(wg_muxer_copy_buffer), + X(wg_muxer_free_buffer), };
#ifdef _WIN64 @@ -2152,6 +2160,55 @@ NTSTATUS wow64_wg_transform_read_data(void *args) return ret; }
+NTSTATUS wow64_wg_muxer_add_stream(void *args) +{ + struct + { + wg_muxer_t muxer; + DWORD stream_id; + PTR32 format; + } *params32 = args; + struct wg_muxer_add_stream_params params = + { + .muxer = params32->muxer, + .stream_id = params32->stream_id, + .format = ULongToPtr(params32->format), + }; + return wg_transform_read_data(¶ms); +} + +NTSTATUS wow64_wg_muxer_push_sample(void *args) +{ + struct + { + wg_muxer_t muxer; + PTR32 sample; + } *params32 = args; + struct wg_muxer_push_sample_params params = + { + .muxer = params32->muxer, + .sample = ULongToPtr(params32->sample), + }; + return wg_muxer_push_sample(¶ms); +} + +NTSTATUS wow64_wg_muxer_copy_buffer(void *args) +{ + struct + { + wg_muxer_t muxer; + PTR32 buffer; + UINT32 size; + } *params32 = args; + struct wg_muxer_copy_buffer_params params = + { + .muxer = params32->muxer, + .buffer = ULongToPtr(params32->buffer), + .size = params32->size, + }; + return wg_muxer_copy_buffer(¶ms); +} + const unixlib_entry_t __wine_unix_call_wow64_funcs[] = { #define X64(name) [unix_ ## name] = wow64_ ## name @@ -2192,6 +2249,14 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = X(wg_transform_get_status), X(wg_transform_drain), X(wg_transform_flush), + + X(wg_muxer_create), + X(wg_muxer_destroy), + X64(wg_muxer_add_stream), + X64(wg_muxer_push_sample), + X(wg_muxer_get_buffer), + X64(wg_muxer_copy_buffer), + X(wg_muxer_free_buffer), };
#endif /* _WIN64 */
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/gst_private.h | 2 +- dlls/winegstreamer/main.c | 5 +- dlls/winegstreamer/media_sink.c | 125 +++++++++++++++++++++++++++---- dlls/winegstreamer/unixlib.h | 1 + dlls/winegstreamer/wg_muxer.c | 1 + 5 files changed, 116 insertions(+), 18 deletions(-)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 0307b7ba375..f6b992c2b78 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -114,7 +114,7 @@ wg_muxer_t wg_muxer_create(enum wg_container_type container_type); void wg_muxer_destroy(wg_muxer_t muxer); HRESULT wg_muxer_add_stream(wg_muxer_t muxer, DWORD stream_id, const struct wg_format *format); HRESULT wg_muxer_push_sample(wg_muxer_t muxer, struct wg_sample *sample); -bool wg_muxer_get_buffer(wg_muxer_t muxer, uint32_t *size); +bool wg_muxer_get_buffer(wg_muxer_t muxer, uint32_t *size, uint64_t *offset); HRESULT wg_muxer_copy_buffer(wg_muxer_t muxer, void *buffer, UINT32 size); void wg_muxer_free_buffer(wg_muxer_t muxer);
diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index c6a1993740b..6f3fa5a550f 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -520,9 +520,9 @@ HRESULT wg_muxer_push_sample(wg_muxer_t muxer, struct wg_sample *sample) return S_OK; }
-bool wg_muxer_get_buffer(wg_muxer_t muxer, uint32_t *size) +bool wg_muxer_get_buffer(wg_muxer_t muxer, uint32_t *size, uint64_t *offset) { - struct wg_muxer_copy_buffer_params params = + struct wg_muxer_get_buffer_params params = { .muxer = muxer, }; @@ -532,6 +532,7 @@ bool wg_muxer_get_buffer(wg_muxer_t muxer, uint32_t *size) if (!WINE_UNIX_CALL(unix_wg_muxer_get_buffer, ¶ms)) { *size = params.size; + *offset = params.offset; return true; }
diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c index 86a69d0d9ba..63790552ff2 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -30,6 +30,7 @@ enum async_op ASYNC_START, ASYNC_STOP, ASYNC_PAUSE, + ASYNC_PROCESS, };
struct async_command @@ -38,6 +39,9 @@ struct async_command LONG refcount;
enum async_op op; + + IMFSample *sample; + UINT32 stream_id; };
struct stream_sink @@ -77,6 +81,8 @@ struct media_sink struct stream_sink **stream_sinks; size_t stream_sink_count; size_t stream_sink_size; + + wg_muxer_t muxer; };
static struct stream_sink *impl_from_IMFStreamSink(IMFStreamSink *iface) @@ -135,7 +141,11 @@ static ULONG WINAPI async_command_Release(IUnknown *iface) ULONG refcount = InterlockedDecrement(&command->refcount);
if (!refcount) + { + if (command->sample) + IMFSample_Release(command->sample); free(command); + }
return refcount; } @@ -285,9 +295,39 @@ static HRESULT WINAPI stream_sink_GetMediaTypeHandler(IMFStreamSink *iface, IMFM
static HRESULT WINAPI stream_sink_ProcessSample(IMFStreamSink *iface, IMFSample *sample) { - FIXME("iface %p, sample %p stub!\n", iface, sample); + struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); + struct media_sink *media_sink = stream_sink->media_sink; + struct async_command *command; + HRESULT hr;
- return E_NOTIMPL; + TRACE("iface %p, sample %p.\n", iface, sample); + + EnterCriticalSection(&media_sink->cs); + + if (media_sink->state == STATE_SHUTDOWN) + { + LeaveCriticalSection(&media_sink->cs); + return MF_E_SHUTDOWN; + } + + if (media_sink->state != STATE_STARTED && media_sink->state != STATE_PAUSED) + { + LeaveCriticalSection(&media_sink->cs); + return MF_E_INVALIDREQUEST; + } + + if (FAILED(hr = (async_command_create(ASYNC_PROCESS, &command)))) + { + LeaveCriticalSection(&media_sink->cs); + return hr; + } + + IMFSample_AddRef(command->sample = sample); + command->stream_id = stream_sink->id; + hr = MFPutWorkItem(media_sink->async_queue, &media_sink->async_callback, &command->IUnknown_iface); + + LeaveCriticalSection(&media_sink->cs); + return hr; }
static HRESULT WINAPI stream_sink_PlaceMarker(IMFStreamSink *iface, MFSTREAMSINK_MARKER_TYPE marker_type, @@ -435,6 +475,38 @@ static HRESULT media_sink_pause(struct media_sink *media_sink) return hr; }
+static HRESULT media_sink_process(struct media_sink *media_sink, IMFSample *sample, UINT32 stream_id) +{ + wg_muxer_t muxer = media_sink->muxer; + struct wg_sample *wg_sample; + ULONG size, written; + QWORD offset; + BYTE *data; + HRESULT hr; + + if (FAILED(hr = wg_sample_create_mf(sample, &wg_sample))) + return hr; + wg_sample->stream_id = stream_id; + + if (FAILED(hr = wg_muxer_push_sample(muxer, wg_sample))) + return hr; + + while (wg_muxer_get_buffer(muxer, &size, &offset)) + { + if (!(data = malloc(size))) + return E_OUTOFMEMORY; + if (!(wg_muxer_copy_buffer(muxer, data, size))) + return E_FAIL; + + if (FAILED(hr = IMFByteStream_SetCurrentPosition(media_sink->bytestream, offset))) /* FIXME. */ + return hr; + if (FAILED(hr = IMFByteStream_Write(media_sink->bytestream, data, size, &written))) + return hr; + + wg_muxer_free_buffer(muxer); + } +} + static HRESULT WINAPI media_sink_QueryInterface(IMFFinalizableMediaSink *iface, REFIID riid, void **obj) { struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface); @@ -487,6 +559,7 @@ static ULONG WINAPI media_sink_Release(IMFFinalizableMediaSink *iface) IMFFinalizableMediaSink_Shutdown(iface); IMFMediaEventQueue_Release(media_sink->event_queue); IMFByteStream_Release(media_sink->bytestream); + wg_muxer_destroy(media_sink->muxer); free(media_sink); }
@@ -505,6 +578,7 @@ static HRESULT WINAPI media_sink_AddStreamSink(IMFFinalizableMediaSink *iface, D { struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface); struct stream_sink *object; + struct wg_format format; HRESULT hr;
TRACE("iface %p, stream_sink_id %#lx, media_type %p, stream_sink %p.\n", @@ -521,6 +595,13 @@ static HRESULT WINAPI media_sink_AddStreamSink(IMFFinalizableMediaSink *iface, D return hr; }
+ mf_media_type_to_wg_format(media_type, &format); + if (FAILED(hr = wg_muxer_add_stream(media_sink->muxer, stream_sink_id, &format))) + { + IMFStreamSink_Release(&object->IMFStreamSink_iface); + return hr; + } + EnterCriticalSection(&media_sink->cs);
if (!array_reserve((void **)&media_sink->stream_sinks, @@ -565,9 +646,18 @@ static HRESULT WINAPI media_sink_GetStreamSinkByIndex(IMFFinalizableMediaSink *i static HRESULT WINAPI media_sink_GetStreamSinkById(IMFFinalizableMediaSink *iface, DWORD stream_sink_id, IMFStreamSink **stream) { - FIXME("iface %p, stream_sink_id %#lx, stream %p stub!\n", iface, stream_sink_id, stream); + struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface); + struct stream_sink *stream_sink;
- return E_NOTIMPL; + TRACE("iface %p, stream_sink_id %#lx, stream %p stub!\n", iface, stream_sink_id, stream); + + if ((stream_sink = media_sink_get_stream_sink_by_id(media_sink, stream_sink_id))) + { + *stream = &stream_sink->IMFStreamSink_iface; + return S_OK; + } + + return MF_E_INVALIDSTREAMNUMBER; }
static HRESULT WINAPI media_sink_SetPresentationClock(IMFFinalizableMediaSink *iface, IMFPresentationClock *clock) @@ -881,6 +971,9 @@ static HRESULT WINAPI media_sink_callback_Invoke(IMFAsyncCallback *iface, IMFAsy case ASYNC_PAUSE: hr = media_sink_pause(media_sink); break; + case ASYNC_PROCESS: + hr = media_sink_process(media_sink, command->sample, command->stream_id); + break; default: WARN("Unsupported op %u.\n", command->op); break; @@ -905,7 +998,7 @@ static const IMFAsyncCallbackVtbl media_sink_callback_vtbl = static HRESULT media_sink_create(IMFByteStream *bytestream, wg_container_type type, struct media_sink **out) { struct media_sink *media_sink; - HRESULT hr; + HRESULT hr = E_FAIL;
TRACE("bytestream %p, out %p.\n", bytestream, out);
@@ -915,18 +1008,12 @@ static HRESULT media_sink_create(IMFByteStream *bytestream, wg_container_type ty if (!(media_sink = calloc(1, sizeof(*media_sink)))) return E_OUTOFMEMORY;
+ if (!(media_sink->muxer = wg_muxer_create(type))) + goto fail; if (FAILED(hr = MFCreateEventQueue(&media_sink->event_queue))) - { - free(media_sink); - return hr; - } - + goto fail; if (FAILED(hr = MFAllocateWorkQueue(&media_sink->async_queue))) - { - IMFMediaEventQueue_Release(media_sink->event_queue); - free(media_sink); - return hr; - } + goto fail;
media_sink->IMFFinalizableMediaSink_iface.lpVtbl = &media_sink_vtbl; media_sink->IMFMediaEventGenerator_iface.lpVtbl = &media_sink_event_vtbl; @@ -943,6 +1030,14 @@ static HRESULT media_sink_create(IMFByteStream *bytestream, wg_container_type ty TRACE("Created media sink %p.\n", media_sink);
return S_OK; + +fail: + if (media_sink->event_queue) + IMFMediaEventQueue_Release(media_sink->event_queue); + if (media_sink->muxer) + wg_muxer_destroy(media_sink->muxer); + free(media_sink); + return hr; }
static HRESULT WINAPI sink_class_factory_QueryInterface(IMFSinkClassFactory *iface, REFIID riid, void **out) diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 05ef465d58f..2c009e2b84b 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -396,6 +396,7 @@ struct wg_muxer_get_buffer_params { wg_muxer_t muxer; UINT32 size; + UINT64 offset; };
struct wg_muxer_copy_buffer_params diff --git a/dlls/winegstreamer/wg_muxer.c b/dlls/winegstreamer/wg_muxer.c index b3968707bdc..0e9e9541773 100644 --- a/dlls/winegstreamer/wg_muxer.c +++ b/dlls/winegstreamer/wg_muxer.c @@ -337,6 +337,7 @@ NTSTATUS wg_muxer_get_buffer(void *args) }
params->size = muxer->map_info.size; + params->offset = GST_BUFFER_OFFSET(muxer->buffer);
return STATUS_SUCCESS; }
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=135722
Your paranoid android.
=== debian11 (build log) ===
/home/winetest/tools/testbot/var/wine-win32/../wine/dlls/winegstreamer/wg_muxer.c:242: undefined reference to `gst_element_request_pad_simple' collect2: error: ld returned 1 exit status Task: The win32 Wine build failed
=== debian11b (build log) ===
/home/winetest/tools/testbot/var/wine-wow64/../wine/dlls/winegstreamer/wg_muxer.c:242: undefined reference to `gst_element_request_pad_simple' collect2: error: ld returned 1 exit status Task: The wow64 Wine build failed