From: Rémi Bernon rbernon@codeweavers.com
Instead of going through a custom intermediate format. --- dlls/winegstreamer/gst_private.h | 2 - dlls/winegstreamer/main.c | 133 +++++++++++--------- dlls/winegstreamer/quartz_transform.c | 25 ++-- dlls/winegstreamer/unixlib.h | 6 +- dlls/winegstreamer/wg_parser.c | 22 +++- dlls/winegstreamer/wg_transform.c | 171 ++++++++++---------------- 6 files changed, 168 insertions(+), 191 deletions(-)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 6525381312d..142de9bce9f 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -81,8 +81,6 @@ char *wg_parser_stream_get_tag(wg_parser_stream_t stream, enum wg_parser_tag tag void wg_parser_stream_seek(wg_parser_stream_t stream, double rate, uint64_t start_pos, uint64_t stop_pos, DWORD start_flags, DWORD stop_flags);
-wg_transform_t wg_transform_create(const struct wg_format *input_format, - const struct wg_format *output_format, const struct wg_transform_attrs *attrs); HRESULT wg_transform_create_mf(IMFMediaType *input_type, IMFMediaType *output_type, const struct wg_transform_attrs *attrs, wg_transform_t *transform); HRESULT wg_transform_create_quartz(const AM_MEDIA_TYPE *input_format, const AM_MEDIA_TYPE *output_format, diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 203ac6a7a61..2d144a6f71f 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -435,55 +435,59 @@ void wg_parser_stream_seek(wg_parser_stream_t stream, double rate, WINE_UNIX_CALL(unix_wg_parser_stream_seek, ¶ms); }
-wg_transform_t wg_transform_create(const struct wg_format *input_format, - const struct wg_format *output_format, const struct wg_transform_attrs *attrs) +HRESULT wg_transform_create_mf(IMFMediaType *input_type, IMFMediaType *output_type, + const struct wg_transform_attrs *attrs, wg_transform_t *transform) { struct wg_transform_create_params params = { - .input_format = input_format, - .output_format = output_format, - .attrs = attrs, + .attrs = *attrs, }; + NTSTATUS status; + HRESULT hr;
- TRACE("input_format %p, output_format %p.\n", input_format, output_format); - - if (WINE_UNIX_CALL(unix_wg_transform_create, ¶ms)) - return 0; + TRACE("input_type %p, output_type %p.\n", input_type, output_type);
- TRACE("Returning transform %#I64x.\n", params.transform); - return params.transform; -} - -HRESULT wg_transform_create_mf(IMFMediaType *input_type, IMFMediaType *output_type, - const struct wg_transform_attrs *attrs, wg_transform_t *transform) -{ - struct wg_format input_format, output_format; + if (FAILED(hr = wg_media_type_from_mf(input_type, ¶ms.input_type))) + return hr; + if (FAILED(hr = wg_media_type_from_mf(output_type, ¶ms.output_type))) + { + CoTaskMemFree(params.input_type.u.format); + return hr; + }
- mf_media_type_to_wg_format(input_type, &input_format); - if (input_format.major_type == WG_MAJOR_TYPE_UNKNOWN) - return MF_E_INVALIDMEDIATYPE; - mf_media_type_to_wg_format(output_type, &output_format); - if (output_format.major_type == WG_MAJOR_TYPE_UNKNOWN) - return MF_E_INVALIDMEDIATYPE; + if ((status = WINE_UNIX_CALL(unix_wg_transform_create, ¶ms))) + { + WARN("Failed to create transform, status %#lx\n", status); + hr = HRESULT_FROM_NT(status); + }
- if (!(*transform = wg_transform_create(&input_format, &output_format, attrs))) - return E_FAIL; - return S_OK; + CoTaskMemFree(params.output_type.u.format); + CoTaskMemFree(params.input_type.u.format); + *transform = params.transform; + return hr; }
-HRESULT wg_transform_create_quartz(const AM_MEDIA_TYPE *input_type, const AM_MEDIA_TYPE *output_type, +HRESULT wg_transform_create_quartz(const AM_MEDIA_TYPE *input_format, const AM_MEDIA_TYPE *output_format, const struct wg_transform_attrs *attrs, wg_transform_t *transform) { - struct wg_format input_format, output_format; + IMFMediaType *input_type, *output_type; + HRESULT hr;
- if (!amt_to_wg_format(input_type, &input_format)) - return E_FAIL; - if (!amt_to_wg_format(output_type, &output_format)) - return E_FAIL; + TRACE("input_format %p, output_format %p.\n", input_format, output_format);
- if (!(*transform = wg_transform_create(&input_format, &output_format, attrs))) - return E_FAIL; - return S_OK; + /* through IMFMediaType to normalize representation to MFVIDEOFORMAT / WAVEFORMATEX */ + if (FAILED(hr = MFCreateMediaTypeFromRepresentation(AM_MEDIA_TYPE_REPRESENTATION, (void *)input_format, &input_type))) + return 0; + if (FAILED(hr = MFCreateMediaTypeFromRepresentation(AM_MEDIA_TYPE_REPRESENTATION, (void *)output_format, &output_type))) + { + IMFMediaType_Release(input_type); + return 0; + } + + hr = wg_transform_create_mf(input_type, output_type, attrs, transform); + IMFMediaType_Release(output_type); + IMFMediaType_Release(input_type); + return hr; }
void wg_transform_destroy(wg_transform_t transform) @@ -773,24 +777,22 @@ HRESULT wg_muxer_finalize(wg_muxer_t muxer) return S_OK; }
-HRESULT check_audio_transform_support(const WAVEFORMATEX *input, const WAVEFORMATEX *output) +static HRESULT check_transform_support(const struct wg_media_type *input, const struct wg_media_type *output) { IMFMediaType *input_type, *output_type; struct wg_transform_attrs attrs = {0}; wg_transform_t transform; HRESULT hr;
- if (FAILED(hr = MFCreateMediaType(&input_type))) + if (FAILED(hr = wg_media_type_to_mf(input, &input_type))) return hr; - if (FAILED(hr = MFCreateMediaType(&output_type))) + if (FAILED(hr = wg_media_type_to_mf(output, &output_type))) { IMFMediaType_Release(input_type); return hr; }
- if (SUCCEEDED(hr = MFInitMediaTypeFromWaveFormatEx(input_type, input, sizeof(*input) + input->cbSize)) - && SUCCEEDED(hr = MFInitMediaTypeFromWaveFormatEx(output_type, output, sizeof(*output) + output->cbSize)) - && SUCCEEDED(hr = wg_transform_create_mf(input_type, output_type, &attrs, &transform))) + if (SUCCEEDED(hr = wg_transform_create_mf(input_type, output_type, &attrs, &transform))) wg_transform_destroy(transform);
IMFMediaType_Release(output_type); @@ -798,29 +800,38 @@ HRESULT check_audio_transform_support(const WAVEFORMATEX *input, const WAVEFORMA return hr; }
-HRESULT check_video_transform_support(const MFVIDEOFORMAT *input, const MFVIDEOFORMAT *output) +HRESULT check_audio_transform_support(const WAVEFORMATEX *input, const WAVEFORMATEX *output) { - IMFMediaType *input_type, *output_type; - struct wg_transform_attrs attrs = {0}; - wg_transform_t transform; - HRESULT hr; - - if (FAILED(hr = MFCreateMediaType(&input_type))) - return hr; - if (FAILED(hr = MFCreateMediaType(&output_type))) + const struct wg_media_type input_type = { - IMFMediaType_Release(input_type); - return hr; - } - - if (SUCCEEDED(hr = MFInitMediaTypeFromMFVideoFormat(input_type, input, input->dwSize)) - && SUCCEEDED(hr = MFInitMediaTypeFromMFVideoFormat(output_type, output, output->dwSize)) - && SUCCEEDED(hr = wg_transform_create_mf(input_type, output_type, &attrs, &transform))) - wg_transform_destroy(transform); + .major = MFMediaType_Audio, + .format_size = sizeof(*input) + input->cbSize, + .u.audio = (WAVEFORMATEX *)input, + }; + const struct wg_media_type output_type = + { + .major = MFMediaType_Audio, + .format_size = sizeof(*output) + output->cbSize, + .u.audio = (WAVEFORMATEX *)output, + }; + return check_transform_support(&input_type, &output_type); +}
- IMFMediaType_Release(output_type); - IMFMediaType_Release(input_type); - return hr; +HRESULT check_video_transform_support(const MFVIDEOFORMAT *input, const MFVIDEOFORMAT *output) +{ + const struct wg_media_type input_type = + { + .major = MFMediaType_Video, + .format_size = input->dwSize, + .u.video = (MFVIDEOFORMAT *)input, + }; + const struct wg_media_type output_type = + { + .major = MFMediaType_Video, + .format_size = output->dwSize, + .u.video = (MFVIDEOFORMAT *)output, + }; + return check_transform_support(&input_type, &output_type); }
#define ALIGN(n, alignment) (((n) + (alignment) - 1) & ~((alignment) - 1)) diff --git a/dlls/winegstreamer/quartz_transform.c b/dlls/winegstreamer/quartz_transform.c index 31150a71298..ff57ea02ed0 100644 --- a/dlls/winegstreamer/quartz_transform.c +++ b/dlls/winegstreamer/quartz_transform.c @@ -874,31 +874,26 @@ static const struct transform_ops mpeg_video_codec_transform_ops =
HRESULT mpeg_video_codec_create(IUnknown *outer, IUnknown **out) { - static const struct wg_format output_format = + const MFVIDEOFORMAT output_format = { - .major_type = WG_MAJOR_TYPE_VIDEO, - .u.video = { - .format = WG_VIDEO_FORMAT_I420, - /* size doesn't matter, this one is only used to check if the GStreamer plugin exists */ - }, + .dwSize = sizeof(MFVIDEOFORMAT), + .videoInfo = {.dwWidth = 1920, .dwHeight = 1080}, + .guidFormat = MEDIASUBTYPE_NV12, }; - static const struct wg_format input_format = + const MFVIDEOFORMAT input_format = { - .major_type = WG_MAJOR_TYPE_VIDEO_MPEG1, - .u.video = {}, + .dwSize = sizeof(MFVIDEOFORMAT), + .videoInfo = {.dwWidth = 1920, .dwHeight = 1080}, + .guidFormat = MEDIASUBTYPE_MPEG1Payload, }; - struct wg_transform_attrs attrs = {0}; - wg_transform_t transform; struct transform *object; HRESULT hr;
- transform = wg_transform_create(&input_format, &output_format, &attrs); - if (!transform) + if (FAILED(hr = check_video_transform_support(&input_format, &output_format))) { ERR_(winediag)("GStreamer doesn't support MPEG-1 video decoding, please install appropriate plugins.\n"); - return E_FAIL; + return hr; } - wg_transform_destroy(transform);
hr = transform_create(outer, &CLSID_CMpegVideoCodec, &mpeg_video_codec_transform_ops, &object); if (FAILED(hr)) diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 94b2b8e347b..19270bd731b 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -340,9 +340,9 @@ struct wg_transform_attrs struct wg_transform_create_params { wg_transform_t transform; - const struct wg_format *input_format; - const struct wg_format *output_format; - const struct wg_transform_attrs *attrs; + struct wg_media_type input_type; + struct wg_media_type output_type; + struct wg_transform_attrs attrs; };
struct wg_transform_push_data_params diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index be49ec8b2aa..8690e6baf81 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -2058,15 +2058,25 @@ NTSTATUS wow64_wg_transform_create(void *args) struct { wg_transform_t transform; - PTR32 input_format; - PTR32 output_format; - PTR32 attrs; + struct wg_media_type32 input_type; + struct wg_media_type32 output_type; + struct wg_transform_attrs attrs; } *params32 = args; struct wg_transform_create_params params = { - .input_format = ULongToPtr(params32->input_format), - .output_format = ULongToPtr(params32->output_format), - .attrs = ULongToPtr(params32->attrs), + .input_type = + { + .major = params32->input_type.major, + .format_size = params32->input_type.format_size, + .u.format = ULongToPtr(params32->input_type.format), + }, + .output_type = + { + .major = params32->output_type.major, + .format_size = params32->output_type.format_size, + .u.format = ULongToPtr(params32->output_type.format), + }, + .attrs = params32->attrs, }; NTSTATUS ret;
diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 10345353e89..614125522a8 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -53,9 +53,8 @@ struct wg_transform GstQuery *drain_query;
GstAtomicQueue *input_queue; - - bool input_is_flipped; GstElement *video_flip; + MFVideoInfo input_info;
GstAtomicQueue *output_queue; GstSample *output_sample; @@ -337,60 +336,32 @@ NTSTATUS wg_transform_destroy(void *args) return STATUS_SUCCESS; }
-static bool wg_format_video_is_flipped(const struct wg_format *format) -{ - return format->major_type == WG_MAJOR_TYPE_VIDEO && (format->u.video.height < 0); -} - -static GstCaps *transform_get_parsed_caps(struct wg_format *format, const char *media_type) +static GstCaps *transform_get_parsed_caps(GstCaps *caps, const char *media_type) { + GstStructure *structure = gst_caps_get_structure(caps, 0); GstCaps *parsed_caps; + gint value;
if (!(parsed_caps = gst_caps_new_empty_simple(media_type))) return NULL;
- switch (format->major_type) + if (gst_structure_get_int(structure, "mpegversion", &value)) { - case WG_MAJOR_TYPE_AUDIO_MPEG1: - gst_caps_set_simple(parsed_caps, "parsed", G_TYPE_BOOLEAN, true, "mpegversion", G_TYPE_INT, 1, - "layer", G_TYPE_INT, format->u.audio.layer, NULL); - break; - case WG_MAJOR_TYPE_AUDIO_MPEG4: + if (value == 4) gst_caps_set_simple(parsed_caps, "framed", G_TYPE_BOOLEAN, true, "mpegversion", G_TYPE_INT, 4, NULL); - break; - case WG_MAJOR_TYPE_AUDIO_WMA: - gst_caps_set_simple(parsed_caps, "wmaversion", G_TYPE_INT, format->u.audio.version, NULL); - break; - case WG_MAJOR_TYPE_VIDEO_H264: - gst_caps_set_simple(parsed_caps, "parsed", G_TYPE_BOOLEAN, true, NULL); - break; - case WG_MAJOR_TYPE_VIDEO_MPEG1: - gst_caps_set_simple(parsed_caps, "parsed", G_TYPE_BOOLEAN, true, "mpegversion", G_TYPE_INT, 1, NULL); - break; - case WG_MAJOR_TYPE_VIDEO_WMV: - switch (format->u.video.format) - { - case WG_VIDEO_FORMAT_WMV1: - gst_caps_set_simple(parsed_caps, "wmvversion", G_TYPE_INT, 1, NULL); - break; - case WG_VIDEO_FORMAT_WMV2: - gst_caps_set_simple(parsed_caps, "wmvversion", G_TYPE_INT, 2, NULL); - break; - case WG_VIDEO_FORMAT_WMV3: - case WG_VIDEO_FORMAT_WMVA: - case WG_VIDEO_FORMAT_WVC1: - gst_caps_set_simple(parsed_caps, "wmvversion", G_TYPE_INT, 3, NULL); - break; - default: - GST_WARNING("Unknown WMV format %u.", format->u.video.format); - break; - } - break; - case WG_MAJOR_TYPE_AUDIO: - case WG_MAJOR_TYPE_VIDEO: - case WG_MAJOR_TYPE_UNKNOWN: - break; + else + { + gst_caps_set_simple(parsed_caps, "parsed", G_TYPE_BOOLEAN, true, "mpegversion", G_TYPE_INT, value, NULL); + if (gst_structure_get_int(structure, "layer", &value)) + gst_caps_set_simple(parsed_caps, "layer", G_TYPE_INT, value, NULL); + } } + else if (gst_structure_get_int(structure, "wmaversion", &value)) + gst_caps_set_simple(parsed_caps, "parsed", G_TYPE_BOOLEAN, true, "wmaversion", G_TYPE_INT, value, NULL); + else if (gst_structure_get_int(structure, "wmvversion", &value)) + gst_caps_set_simple(parsed_caps, "parsed", G_TYPE_BOOLEAN, true, "wmvversion", G_TYPE_INT, value, NULL); + else + gst_caps_set_simple(parsed_caps, "parsed", G_TYPE_BOOLEAN, true, NULL);
return parsed_caps; } @@ -398,14 +369,13 @@ static GstCaps *transform_get_parsed_caps(struct wg_format *format, const char * NTSTATUS wg_transform_create(void *args) { struct wg_transform_create_params *params = args; - struct wg_format output_format = *params->output_format; - struct wg_format input_format = *params->input_format; GstElement *first = NULL, *last = NULL, *element; GstCaps *sink_caps = NULL, *src_caps = NULL, *parsed_caps = NULL; NTSTATUS status = STATUS_UNSUCCESSFUL; + const gchar *input_mime, *output_mime; GstPadTemplate *template = NULL; struct wg_transform *transform; - const gchar *media_type; + MFVideoInfo output_info = {0}; GstEvent *event;
if (!(transform = calloc(1, sizeof(*transform)))) @@ -420,9 +390,14 @@ NTSTATUS wg_transform_create(void *args) goto out; if (!(transform->allocator = wg_allocator_create())) goto out; - transform->attrs = *params->attrs; + transform->attrs = params->attrs;
- if (!(src_caps = wg_format_to_caps(&input_format))) + if (IsEqualGUID(¶ms->input_type.major, &MFMediaType_Video)) + transform->input_info = params->input_type.u.video->videoInfo; + if (IsEqualGUID(¶ms->output_type.major, &MFMediaType_Video)) + output_info = params->output_type.u.video->videoInfo; + + if (!(src_caps = caps_from_media_type(¶ms->input_type))) goto out; if (!(template = gst_pad_template_new("src", GST_PAD_SRC, GST_PAD_ALWAYS, src_caps))) goto out; @@ -436,7 +411,7 @@ NTSTATUS wg_transform_create(void *args) gst_pad_set_element_private(transform->my_src, transform); gst_pad_set_query_function(transform->my_src, transform_src_query_cb);
- if (!(transform->output_caps = wg_format_to_caps(&output_format))) + if (!(transform->output_caps = caps_from_media_type(¶ms->output_type))) goto out; transform->desired_caps = gst_caps_ref(transform->output_caps); if (!(template = gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_ALWAYS, transform->output_caps))) @@ -453,53 +428,43 @@ NTSTATUS wg_transform_create(void *args) gst_pad_set_query_function(transform->my_sink, transform_sink_query_cb); gst_pad_set_chain_function(transform->my_sink, transform_sink_chain_cb);
- media_type = gst_structure_get_name(gst_caps_get_structure(src_caps, 0)); - if (!(parsed_caps = transform_get_parsed_caps(&input_format, media_type))) + input_mime = gst_structure_get_name(gst_caps_get_structure(src_caps, 0)); + if (!(parsed_caps = transform_get_parsed_caps(src_caps, input_mime))) goto out;
/* 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(transform->output_caps, 0)); - if (!(sink_caps = gst_caps_new_empty_simple(media_type))) + output_mime = gst_structure_get_name(gst_caps_get_structure(transform->output_caps, 0)); + if (!(sink_caps = gst_caps_new_empty_simple(output_mime))) goto out;
- switch (input_format.major_type) + if (strcmp(input_mime, "audio/x-raw") && strcmp(input_mime, "video/x-raw")) { - case WG_MAJOR_TYPE_VIDEO_H264: - case WG_MAJOR_TYPE_AUDIO_MPEG1: - case WG_MAJOR_TYPE_AUDIO_MPEG4: - case WG_MAJOR_TYPE_AUDIO_WMA: - case WG_MAJOR_TYPE_VIDEO_CINEPAK: - case WG_MAJOR_TYPE_VIDEO_INDEO: - case WG_MAJOR_TYPE_VIDEO_WMV: - case WG_MAJOR_TYPE_VIDEO_MPEG1: - if ((element = find_element(GST_ELEMENT_FACTORY_TYPE_PARSER, src_caps, parsed_caps)) - && !append_element(transform->container, element, &first, &last)) - goto out; - else if (!element) - { - gst_caps_unref(parsed_caps); - parsed_caps = gst_caps_ref(src_caps); - } - - if (!(element = find_element(GST_ELEMENT_FACTORY_TYPE_DECODER, parsed_caps, sink_caps)) - || !append_element(transform->container, element, &first, &last)) - goto out; - break; + if ((element = find_element(GST_ELEMENT_FACTORY_TYPE_PARSER, src_caps, parsed_caps)) + && !append_element(transform->container, element, &first, &last)) + goto out; + else if (!element) + { + gst_caps_unref(parsed_caps); + parsed_caps = gst_caps_ref(src_caps); + }
- case WG_MAJOR_TYPE_AUDIO: - case WG_MAJOR_TYPE_VIDEO: - break; - case WG_MAJOR_TYPE_UNKNOWN: - GST_FIXME("Format %u not implemented!", input_format.major_type); + if (!(element = find_element(GST_ELEMENT_FACTORY_TYPE_DECODER, parsed_caps, sink_caps)) + || !append_element(transform->container, element, &first, &last)) goto out; }
- switch (output_format.major_type) + if (g_str_has_prefix(output_mime, "audio/")) { - case WG_MAJOR_TYPE_AUDIO: + if (strcmp(output_mime, "audio/x-raw")) + { + GST_FIXME("output caps %"GST_PTR_FORMAT" not implemented!", transform->output_caps); + goto out; + } + else + { /* The MF audio decoder transforms allow decoding to various formats * as well as resampling the audio at the same time, whereas * GStreamer decoder plugins usually only support decoding to a @@ -515,36 +480,33 @@ NTSTATUS wg_transform_create(void *args) if (!(element = create_element("audioresample", "base")) || !append_element(transform->container, element, &first, &last)) goto out; - break; + } + }
- case WG_MAJOR_TYPE_VIDEO: + if (g_str_has_prefix(output_mime, "video/")) + { + if (strcmp(output_mime, "video/x-raw")) + { + GST_FIXME("output caps %"GST_PTR_FORMAT" not implemented!", transform->output_caps); + goto out; + } + else + { if (!(element = create_element("videoconvert", "base")) || !append_element(transform->container, element, &first, &last)) goto out; if (!(transform->video_flip = create_element("videoflip", "base")) || !append_element(transform->container, transform->video_flip, &first, &last)) goto out; - transform->input_is_flipped = wg_format_video_is_flipped(&input_format); - if (transform->input_is_flipped != wg_format_video_is_flipped(&output_format)) + + if ((transform->input_info.VideoFlags ^ output_info.VideoFlags) & MFVideoFlag_BottomUpLinearRep) gst_util_set_object_arg(G_OBJECT(transform->video_flip), "method", "vertical-flip"); if (!(element = create_element("videoconvert", "base")) || !append_element(transform->container, element, &first, &last)) goto out; /* Let GStreamer choose a default number of threads. */ gst_util_set_object_arg(G_OBJECT(element), "n-threads", "0"); - break; - - case WG_MAJOR_TYPE_UNKNOWN: - case WG_MAJOR_TYPE_AUDIO_MPEG1: - case WG_MAJOR_TYPE_AUDIO_MPEG4: - case WG_MAJOR_TYPE_AUDIO_WMA: - case WG_MAJOR_TYPE_VIDEO_CINEPAK: - case WG_MAJOR_TYPE_VIDEO_H264: - case WG_MAJOR_TYPE_VIDEO_INDEO: - case WG_MAJOR_TYPE_VIDEO_WMV: - case WG_MAJOR_TYPE_VIDEO_MPEG1: - GST_FIXME("Format %u not implemented!", output_format.major_type); - goto out; + } }
if (!link_src_to_element(transform->my_src, first)) @@ -673,7 +635,8 @@ NTSTATUS wg_transform_set_output_type(void *args) if (transform->video_flip) { const char *value; - if (transform->input_is_flipped != !!(output_info.VideoFlags & MFVideoFlag_BottomUpLinearRep)) + + if ((transform->input_info.VideoFlags ^ output_info.VideoFlags) & MFVideoFlag_BottomUpLinearRep) value = "vertical-flip"; else value = "none";