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 */