[PATCH 3/5] winegstreamer: Support dynamic wg_transform output format change.

Rémi Bernon wine at gitlab.winehq.org
Mon May 2 16:24:40 CDT 2022


From: Rémi Bernon <rbernon at codeweavers.com>

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45988
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47084
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=49715
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52183
Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---
 dlls/mf/tests/mf.c                |  4 --
 dlls/winegstreamer/h264_decoder.c |  8 +++
 dlls/winegstreamer/unixlib.h      |  1 +
 dlls/winegstreamer/wg_transform.c | 84 ++++++++++++++++++++++++-------
 4 files changed, 74 insertions(+), 23 deletions(-)

diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c
index 4d7ca4d825c..1104f164225 100644
--- a/dlls/mf/tests/mf.c
+++ b/dlls/mf/tests/mf.c
@@ -6966,20 +6966,16 @@ static void test_h264_decoder(void)
     ok(i == 2, "got %lu iterations\n", i);
     todo_wine
     ok(h264_encoded_data_len == 1180, "got h264_encoded_data_len %lu\n", h264_encoded_data_len);
-    todo_wine
     ok(hr == MF_E_TRANSFORM_STREAM_CHANGE, "ProcessOutput returned %#lx\n", hr);
     ok(output.dwStreamID == 0, "got dwStreamID %lu\n", output.dwStreamID);
     ok(!!output.pSample, "got pSample %p\n", output.pSample);
-    todo_wine
     ok(output.dwStatus == MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE,
             "got dwStatus %#lx\n", output.dwStatus);
     ok(!output.pEvents, "got pEvents %p\n", output.pEvents);
-    todo_wine
     ok(status == MFT_PROCESS_OUTPUT_STATUS_NEW_STREAMS,
             "got status %#lx\n", status);
     hr = IMFSample_GetTotalLength(output.pSample, &length);
     ok(hr == S_OK, "GetTotalLength returned %#lx\n", hr);
-    todo_wine
     ok(length == 0, "got length %lu\n", length);
     ret = IMFSample_Release(output.pSample);
     ok(ret == 0, "Release returned %lu\n", ret);
diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c
index 52d0fbaef27..bd55bb2f504 100644
--- a/dlls/winegstreamer/h264_decoder.c
+++ b/dlls/winegstreamer/h264_decoder.c
@@ -49,6 +49,7 @@ struct h264_decoder
     IMFMediaType *input_type;
     IMFMediaType *output_type;
 
+    struct wg_format stream_format;
     struct wg_transform *wg_transform;
 };
 
@@ -560,11 +561,18 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags,
     if (FAILED(hr = mf_create_wg_sample(samples[0].pSample, &wg_sample)))
         return hr;
 
+    wg_sample->format = &decoder->stream_format;
     if (wg_sample->max_size < info.cbSize)
         hr = MF_E_BUFFERTOOSMALL;
     else
         hr = wg_transform_read_data(decoder->wg_transform, wg_sample);
 
+    if (hr == MF_E_TRANSFORM_STREAM_CHANGE)
+    {
+        samples[0].dwStatus |= MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE;
+        *status |= MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE;
+    }
+
     mf_destroy_wg_sample(wg_sample);
     return hr;
 }
diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h
index f4e2ea4966b..7a31020fb87 100644
--- a/dlls/winegstreamer/unixlib.h
+++ b/dlls/winegstreamer/unixlib.h
@@ -122,6 +122,7 @@ struct wg_sample
     UINT32 max_size;
     UINT32 size;
     BYTE *data;
+    struct wg_format *format;
 };
 
 struct wg_parser_buffer
diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c
index 58eb1286401..e0b0ce77a44 100644
--- a/dlls/winegstreamer/wg_transform.c
+++ b/dlls/winegstreamer/wg_transform.c
@@ -51,40 +51,73 @@ struct wg_transform
     GstBufferList *input;
     guint input_max_length;
     GstAtomicQueue *output_queue;
-    GstBuffer *output_buffer;
+    GstSample *output_sample;
+    GstCaps *sink_caps;
 };
 
 static GstFlowReturn transform_sink_chain_cb(GstPad *pad, GstObject *parent, GstBuffer *buffer)
 {
     struct wg_transform *transform = gst_pad_get_element_private(pad);
+    GstSample *sample;
 
     GST_LOG("transform %p, buffer %p.", transform, buffer);
 
-    gst_atomic_queue_push(transform->output_queue, buffer);
+    if ((sample = gst_sample_new(buffer, transform->sink_caps, NULL, NULL)))
+        gst_atomic_queue_push(transform->output_queue, sample);
 
-    return GST_FLOW_OK;
+    gst_buffer_unref(buffer);
+
+    return sample ? GST_FLOW_OK : GST_FLOW_ERROR;
+}
+
+static gboolean transform_sink_event_cb(GstPad *pad, GstObject *parent, GstEvent *event)
+{
+    struct wg_transform *transform = gst_pad_get_element_private(pad);
+
+    GST_LOG("transform %p, type \"%s\".", transform, GST_EVENT_TYPE_NAME(event));
+
+    switch (event->type)
+    {
+        case GST_EVENT_CAPS:
+        {
+            GstCaps *caps;
+
+            gst_event_parse_caps(event, &caps);
+
+            gst_caps_unref(transform->sink_caps);
+            transform->sink_caps = gst_caps_ref(caps);
+            break;
+        }
+        default:
+            GST_WARNING("Ignoring \"%s\" event.", GST_EVENT_TYPE_NAME(event));
+            break;
+    }
+
+    gst_event_unref(event);
+    return TRUE;
 }
 
 NTSTATUS wg_transform_destroy(void *args)
 {
     struct wg_transform *transform = args;
-    GstBuffer *buffer;
+    GstSample *sample;
 
     if (transform->input)
         gst_buffer_list_unref(transform->input);
 
     gst_element_set_state(transform->container, GST_STATE_NULL);
 
-    if (transform->output_buffer)
-        gst_buffer_unref(transform->output_buffer);
-    while ((buffer = gst_atomic_queue_pop(transform->output_queue)))
-        gst_buffer_unref(buffer);
+    if (transform->output_sample)
+        gst_sample_unref(transform->output_sample);
+    while ((sample = gst_atomic_queue_pop(transform->output_queue)))
+        gst_sample_unref(sample);
 
     g_object_unref(transform->their_sink);
     g_object_unref(transform->their_src);
     g_object_unref(transform->container);
     g_object_unref(transform->my_sink);
     g_object_unref(transform->my_src);
+    gst_caps_unref(transform->sink_caps);
     gst_atomic_queue_unref(transform->output_queue);
     free(transform);
 
@@ -162,10 +195,10 @@ static bool transform_append_element(struct wg_transform *transform, GstElement
 NTSTATUS wg_transform_create(void *args)
 {
     struct wg_transform_create_params *params = args;
-    GstCaps *raw_caps = NULL, *src_caps = NULL, *sink_caps = NULL;
     struct wg_format output_format = *params->output_format;
     struct wg_format input_format = *params->input_format;
     GstElement *first = NULL, *last = NULL, *element;
+    GstCaps *raw_caps = NULL, *src_caps = NULL;
     NTSTATUS status = STATUS_UNSUCCESSFUL;
     GstPadTemplate *template = NULL;
     struct wg_transform *transform;
@@ -194,9 +227,9 @@ NTSTATUS wg_transform_create(void *args)
     if (!transform->my_src)
         goto out;
 
-    if (!(sink_caps = wg_format_to_caps(&output_format)))
+    if (!(transform->sink_caps = wg_format_to_caps(&output_format)))
         goto out;
-    if (!(template = gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_ALWAYS, sink_caps)))
+    if (!(template = gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_ALWAYS, transform->sink_caps)))
         goto out;
     transform->my_sink = gst_pad_new_from_template(template, "sink");
     g_object_unref(template);
@@ -204,13 +237,14 @@ NTSTATUS wg_transform_create(void *args)
         goto out;
 
     gst_pad_set_element_private(transform->my_sink, transform);
+    gst_pad_set_event_function(transform->my_sink, transform_sink_event_cb);
     gst_pad_set_chain_function(transform->my_sink, transform_sink_chain_cb);
 
     /* Since we append conversion elements, we don't want to filter decoders
      * based on the actual output caps now. Matching decoders with the
      * raw output media type should be enough.
      */
-    media_type = gst_structure_get_name(gst_caps_get_structure(sink_caps, 0));
+    media_type = gst_structure_get_name(gst_caps_get_structure(transform->sink_caps, 0));
     if (!(raw_caps = gst_caps_new_empty_simple(media_type)))
         goto out;
 
@@ -306,7 +340,6 @@ NTSTATUS wg_transform_create(void *args)
             || !gst_pad_push_event(transform->my_src, event))
         goto out;
 
-    gst_caps_unref(sink_caps);
     gst_caps_unref(src_caps);
 
     GST_INFO("Created winegstreamer transform %p.", transform);
@@ -320,8 +353,8 @@ out:
         gst_object_unref(transform->their_src);
     if (transform->my_sink)
         gst_object_unref(transform->my_sink);
-    if (sink_caps)
-        gst_caps_unref(sink_caps);
+    if (transform->sink_caps)
+        gst_caps_unref(transform->sink_caps);
     if (transform->my_src)
         gst_object_unref(transform->my_src);
     if (src_caps)
@@ -403,8 +436,10 @@ NTSTATUS wg_transform_read_data(void *args)
     struct wg_transform *transform = params->transform;
     struct wg_sample *sample = params->sample;
     GstBufferList *input = transform->input;
+    struct wg_format format;
     GstFlowReturn ret;
     NTSTATUS status;
+    GstCaps *caps;
 
     if (!gst_buffer_list_length(transform->input))
         GST_DEBUG("Not input buffer queued");
@@ -419,7 +454,7 @@ NTSTATUS wg_transform_read_data(void *args)
         return STATUS_UNSUCCESSFUL;
     }
 
-    if (!transform->output_buffer && !(transform->output_buffer = gst_atomic_queue_pop(transform->output_queue)))
+    if (!transform->output_sample && !(transform->output_sample = gst_atomic_queue_pop(transform->output_queue)))
     {
         sample->size = 0;
         params->result = MF_E_TRANSFORM_NEED_MORE_INPUT;
@@ -427,7 +462,18 @@ NTSTATUS wg_transform_read_data(void *args)
         return STATUS_SUCCESS;
     }
 
-    if ((status = read_transform_output_data(transform->output_buffer, sample)))
+    if (sample->format && (caps = gst_sample_get_caps(transform->output_sample)))
+    {
+        wg_format_from_caps(&format, caps);
+        if (!wg_format_compare(&format, sample->format))
+        {
+            *sample->format = format;
+            params->result = MF_E_TRANSFORM_STREAM_CHANGE;
+            return STATUS_SUCCESS;
+        }
+    }
+
+    if ((status = read_transform_output_data(gst_sample_get_buffer(transform->output_sample), sample)))
     {
         sample->size = 0;
         return status;
@@ -435,8 +481,8 @@ NTSTATUS wg_transform_read_data(void *args)
 
     if (!(sample->flags & WG_SAMPLE_FLAG_INCOMPLETE))
     {
-        gst_buffer_unref(transform->output_buffer);
-        transform->output_buffer = NULL;
+        gst_sample_unref(transform->output_sample);
+        transform->output_sample = NULL;
     }
 
     params->result = S_OK;
-- 
GitLab


https://gitlab.winehq.org/wine/wine/-/merge_requests/22



More information about the wine-devel mailing list