Still working in progress.
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 | 6 ++++++ dlls/winegstreamer/wg_format.c | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+)
diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 527822cf15b..1c7bad2f95c 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 39391b44735..446cf23d283 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -164,6 +164,12 @@ struct wg_format } u; };
+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/unixlib.h | 24 +++ dlls/winegstreamer/wg_muxer.c | 291 ++++++++++++++++++++++++++++++++++ 2 files changed, 315 insertions(+) create mode 100644 dlls/winegstreamer/wg_muxer.c
diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 446cf23d283..23ec48c0889 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -370,6 +370,30 @@ struct wg_transform_get_status_params UINT32 accepts_input; };
+struct wg_muxer_create_params +{ + struct wg_muxer *muxer; + enum wg_container_type container_type; +}; + +struct wg_muxer_destroy_params +{ + struct wg_muxer *muxer; +}; + +struct wg_muxer_add_stream_params +{ + struct wg_muxer *muxer; + DWORD stream_id; + const struct wg_format *format; +}; + +struct wg_muxer_stream_push_sample_params +{ + struct wg_muxer_stream *stream; + struct wg_sample *sample; +}; + enum unix_funcs { unix_wg_init_gstreamer, diff --git a/dlls/winegstreamer/wg_muxer.c b/dlls/winegstreamer/wg_muxer.c new file mode 100644 index 00000000000..b2a171a7980 --- /dev/null +++ b/dlls/winegstreamer/wg_muxer.c @@ -0,0 +1,291 @@ +/* + * 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 "config.h" + +#include <assert.h> +#include <stdarg.h> +#include <stdio.h> + +#include <gst/gst.h> +#include <gst/video/video.h> +#include <gst/audio/audio.h> + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "winternl.h" +#include "mferror.h" + +#include "wine/list.h" + +#include "unix_private.h" + +struct wg_muxer +{ + GstElement *container, *muxer; + + struct wg_muxer_stream **streams; + unsigned int stream_count; + + GstPad *my_sink; + GstAtomicQueue *output_queue; +}; + +struct wg_muxer_stream +{ + struct wg_muxer *muxer; + DWORD id; + struct wg_format format; + + GstPad *my_src; + GstAtomicQueue *input_queue; +}; + +static gboolean muxer_sink_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) +{ + /* FIXME: TODO. */ +} + +static GstFlowReturn muxer_sink_chain_cb(GstPad *pad, GstObject *parent, GstBuffer *buffer) +{ + /* FIXME: TODO. */ +} + +static void sample_free_notify(void *arg) +{ + struct wg_sample *sample = arg; + GST_DEBUG("Releasing wg_sample %p", sample); + InterlockedDecrement(&sample->refcount); +} + +static void stream_release(struct wg_muxer_stream *stream) +{ + if (stream->my_src) + gst_object_unref(stream->my_src); + free(stream); +} + +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; + + if (!(muxer = calloc(1, sizeof(*muxer)))) + return STATUS_NO_MEMORY; + if (!(muxer->container = gst_bin_new("wg_muxer"))) + 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_query_function(muxer->my_sink, muxer_sink_query_cb); + gst_pad_set_chain_function(muxer->my_sink, muxer_sink_chain_cb); + + /* Create muxer. */ + if (!(muxer->muxer = find_element(GST_ELEMENT_FACTORY_TYPE_MUXER, NULL, sink_caps))) + goto out; + + /* Link. */ + 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 = 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->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_destroy_params *params = args; + struct wg_muxer *muxer = params->muxer; + unsigned int i; + + for (i = 0; i < muxer->stream_count; ++i) + stream_release(muxer->streams[i]); + if (muxer->my_sink) + gst_object_unref(muxer->my_sink); + 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; + char src_pad_name[32], *muxer_sink_pad_name; + struct wg_muxer *muxer = params->muxer; + NTSTATUS status = STATUS_UNSUCCESSFUL; + DWORD stream_id = params->stream_id; + GstPadTemplate *template = NULL; + struct wg_muxer_stream *stream; + GstPad *muxer_sink; + GstCaps *src_caps; + + /* Create object. */ + if (!(stream = calloc(1, sizeof(*stream)))) + return STATUS_NO_MEMORY; + stream->muxer = muxer; + stream->format = *params->format; + stream->id = params->stream_id; + + if (!(stream->input_queue = gst_atomic_queue_new(8))) + goto out; + + /* 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); + + /* Link src to muxer. */ + 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; + 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; + + 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); + if (stream->input_queue) + gst_atomic_queue_unref(stream->input_queue); + free(stream); + + return status; +} + + +NTSTATUS wg_muxer_stream_push_sample(void *args) +{ + struct wg_muxer_stream_push_sample_params *params = args; + struct wg_muxer_stream *stream = params->stream; + struct wg_sample *sample = params->sample; + guint length, queue_length = 16; + GstBuffer *buffer; + + length = gst_atomic_queue_length(stream->input_queue); + if (length >= queue_length) + { + GST_INFO("Refusing %u bytes, %u buffers already queued", sample->size, length); + return STATUS_UNSUCCESSFUL; + } + + 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); + gst_atomic_queue_push(stream->input_queue, buffer); + + return STATUS_SUCCESS; +}