[PATCH 3/3] winegstreamer: Support dynamic H264 decoder output format change.

Rémi Bernon rbernon at codeweavers.com
Tue Apr 5 07:26:16 CDT 2022


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                |  7 +---
 dlls/winegstreamer/h264_decoder.c | 59 ++++++++++++++++++++++++++++---
 2 files changed, 55 insertions(+), 11 deletions(-)

diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c
index 31a84e47bc1..e7d8de649ef 100644
--- a/dlls/mf/tests/mf.c
+++ b/dlls/mf/tests/mf.c
@@ -6868,19 +6868,15 @@ static void test_h264_decoder(void)
     ok(i == 2, "got %lu iterations\n", i);
     todo_wine
     ok(h264_encoded_data_len == 48194, "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);
-    if (status == MFT_PROCESS_OUTPUT_STATUS_NEW_STREAMS)
-        check_sample(output.pSample, NULL, 0, NULL);
+    check_sample(output.pSample, NULL, 0, NULL);
     ret = IMFSample_Release(output.pSample);
     ok(ret == 0, "Release returned %lu\n", ret);
 
@@ -6922,7 +6918,6 @@ static void test_h264_decoder(void)
     memset(&output, 0, sizeof(output));
     output.pSample = create_sample(NULL, actual_width * actual_height * 2);
     hr = IMFTransform_ProcessOutput(transform, 0, 1, &output, &status);
-    todo_wine
     ok(hr == S_OK, "ProcessOutput returned %#lx\n", hr);
     ok(output.dwStreamID == 0, "got dwStreamID %lu\n", output.dwStreamID);
     ok(!!output.pSample, "got pSample %p\n", output.pSample);
diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c
index 8bfa15529db..2fbbefd1e2d 100644
--- a/dlls/winegstreamer/h264_decoder.c
+++ b/dlls/winegstreamer/h264_decoder.c
@@ -50,6 +50,7 @@ struct h264_decoder
     IMFMediaType *output_type;
 
     struct wg_transform *wg_transform;
+    struct wg_format output_format;
 };
 
 static struct h264_decoder *impl_from_IMFTransform(IMFTransform *iface)
@@ -60,7 +61,6 @@ static struct h264_decoder *impl_from_IMFTransform(IMFTransform *iface)
 static HRESULT try_create_wg_transform(struct h264_decoder *decoder)
 {
     struct wg_format input_format;
-    struct wg_format output_format;
 
     if (decoder->wg_transform)
         wg_transform_destroy(decoder->wg_transform);
@@ -70,11 +70,18 @@ static HRESULT try_create_wg_transform(struct h264_decoder *decoder)
     if (input_format.major_type == WG_MAJOR_TYPE_UNKNOWN)
         return MF_E_INVALIDMEDIATYPE;
 
-    mf_media_type_to_wg_format(decoder->output_type, &output_format);
-    if (output_format.major_type == WG_MAJOR_TYPE_UNKNOWN)
+    mf_media_type_to_wg_format(decoder->output_type, &decoder->output_format);
+    if (decoder->output_format.major_type == WG_MAJOR_TYPE_UNKNOWN)
         return MF_E_INVALIDMEDIATYPE;
 
-    if (!(decoder->wg_transform = wg_transform_create(&input_format, &output_format)))
+    /* Don't force any output frame size, H264 streams already have the
+     * metadata for it and will generate a MF_E_TRANSFORM_STREAM_CHANGE
+     * result later.
+     */
+    decoder->output_format.u.video.width = 0;
+    decoder->output_format.u.video.height = 0;
+
+    if (!(decoder->wg_transform = wg_transform_create(&input_format, &decoder->output_format)))
         return E_FAIL;
 
     return S_OK;
@@ -369,7 +376,7 @@ static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWOR
     if (FAILED(hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, output_type)))
         goto done;
 
-    hr = fill_output_media_type(media_type, NULL);
+    hr = fill_output_media_type(media_type, decoder->output_type);
 
 done:
     if (SUCCEEDED(hr))
@@ -418,6 +425,7 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF
 {
     struct h264_decoder *decoder = impl_from_IMFTransform(iface);
     GUID major, subtype;
+    BOOL identical;
     HRESULT hr;
     ULONG i;
 
@@ -440,7 +448,14 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF
         return MF_E_INVALIDMEDIATYPE;
 
     if (decoder->output_type)
+    {
+        fill_output_media_type(decoder->output_type, NULL);
+        if (SUCCEEDED(hr = IMFMediaType_Compare(decoder->output_type, (IMFAttributes *)type,
+                MF_ATTRIBUTES_MATCH_THEIR_ITEMS, &identical)) && identical)
+            return S_OK;
         IMFMediaType_Release(decoder->output_type);
+    }
+
     IMFMediaType_AddRef((decoder->output_type = type));
 
     if (FAILED(hr = try_create_wg_transform(decoder)))
@@ -490,7 +505,23 @@ static HRESULT WINAPI transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFM
 
 static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param)
 {
+    struct h264_decoder *decoder = impl_from_IMFTransform(iface);
+
     FIXME("iface %p, message %#x, param %Ix stub!\n", iface, message, param);
+
+    switch (message)
+    {
+    case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING:
+        /* Call of Duty Black Ops 3 expects to receive an MF_E_TRANSFORM_STREAM_CHANGE result again
+         * after it has called ProcessMessage with BEGIN_STREAMING. We could destroy and re-create
+         * the wg_transform instead but resetting the output format should be enough.
+         */
+        memset(&decoder->output_format, 0, sizeof(decoder->output_format));
+        break;
+    default:
+        break;
+    }
+
     return S_OK;
 }
 
@@ -524,6 +555,8 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags,
     struct h264_decoder *decoder = impl_from_IMFTransform(iface);
     MFT_OUTPUT_STREAM_INFO info;
     struct wg_sample *wg_sample;
+    IMFMediaType *media_type;
+    UINT64 frame_rate;
     HRESULT hr;
 
     TRACE("iface %p, flags %#lx, count %lu, samples %p, status %p.\n", iface, flags, count, samples, status);
@@ -537,6 +570,9 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags,
     if (!decoder->wg_transform)
         return MF_E_TRANSFORM_TYPE_NOT_SET;
 
+    if (FAILED(IMFMediaType_GetUINT64(decoder->output_type, &MF_MT_FRAME_RATE, &frame_rate)))
+        frame_rate = (UINT64)30000 << 32 | 1001;
+
     *status = 0;
     samples[0].dwStatus = 0;
     if (!samples[0].pSample) return E_INVALIDARG;
@@ -544,11 +580,24 @@ 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->output_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)
+    {
+        media_type = mf_media_type_from_wg_format(&decoder->output_format);
+        IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_RATE, frame_rate);
+
+        IMFMediaType_Release(decoder->output_type);
+        decoder->output_type = media_type;
+
+        samples[0].dwStatus |= MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE;
+        *status |= MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE;
+    }
+
     mf_destroy_wg_sample(wg_sample);
     return hr;
 }
-- 
2.35.1




More information about the wine-devel mailing list