Instead of going through a custom intermediate format.
-- v3: winegstreamer: Create transforms from MFVIDEOFORMAT / WAVEFORMATEX. winegstreamer: Translate MFVIDEOFORMAT / WAVEFORMATEX directly to GstCaps in wg_transform. winegstreamer: Translate GstCaps directly to MFVIDEOFORMAT / WAVEFORMATEX in wg_transform.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_private.h | 2 +- dlls/winegstreamer/main.c | 63 +++- dlls/winegstreamer/unix_private.h | 9 +- dlls/winegstreamer/unixlib.h | 37 ++- dlls/winegstreamer/video_decoder.c | 10 +- dlls/winegstreamer/wg_format.c | 2 +- dlls/winegstreamer/wg_media_type.c | 461 +++++++++++++++++++++++++++++ dlls/winegstreamer/wg_parser.c | 31 +- dlls/winegstreamer/wg_transform.c | 33 +-- 10 files changed, 597 insertions(+), 52 deletions(-) create mode 100644 dlls/winegstreamer/wg_media_type.c
diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 83093620af4..447c2c74cf3 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -23,6 +23,7 @@ SOURCES = \ video_processor.c \ wg_allocator.c \ wg_format.c \ + wg_media_type.c \ wg_muxer.c \ wg_parser.c \ wg_sample.c \ diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index bc798db5980..12cd4bcaa30 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -88,7 +88,7 @@ HRESULT wg_transform_create_mf(IMFMediaType *input_type, IMFMediaType *output_ty 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); void wg_transform_destroy(wg_transform_t transform); -bool wg_transform_get_output_format(wg_transform_t transform, struct wg_format *format); +HRESULT wg_transform_get_output_type(wg_transform_t transform, IMFMediaType **media_type); bool wg_transform_set_output_format(wg_transform_t transform, struct wg_format *format); bool wg_transform_get_status(wg_transform_t transform, bool *accepts_input); HRESULT wg_transform_drain(wg_transform_t transform); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 747479daf40..e9e8c955d2a 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -70,6 +70,41 @@ bool array_reserve(void **elements, size_t *capacity, size_t count, size_t size) return TRUE; }
+static HRESULT media_type_from_video_format(const MFVIDEOFORMAT *format, IMFMediaType **media_type) +{ + HRESULT hr; + + if (FAILED(hr = MFCreateVideoMediaType(format, (IMFVideoMediaType **)media_type)) || format->dwSize <= sizeof(*format)) + return hr; + + /* fixup MPEG video formats here, so we can consistently use MFVIDEOFORMAT internally */ + if (IsEqualGUID(&format->guidFormat, &MEDIASUBTYPE_MPEG1Payload) + || IsEqualGUID(&format->guidFormat, &MEDIASUBTYPE_MPEG1Packet) + || IsEqualGUID(&format->guidFormat, &MEDIASUBTYPE_MPEG2_VIDEO)) + { + struct mpeg_video_format *mpeg = (struct mpeg_video_format *)format; + IMFMediaType_SetBlob(*media_type, &MF_MT_MPEG_SEQUENCE_HEADER, mpeg->sequence_header, mpeg->sequence_header_count); + IMFMediaType_SetUINT32(*media_type, &MF_MT_MPEG_START_TIME_CODE, mpeg->start_time_code); + IMFMediaType_SetUINT32(*media_type, &MF_MT_MPEG2_PROFILE, mpeg->profile); + IMFMediaType_SetUINT32(*media_type, &MF_MT_MPEG2_LEVEL, mpeg->level); + IMFMediaType_SetUINT32(*media_type, &MF_MT_MPEG2_FLAGS, mpeg->flags); + IMFMediaType_DeleteItem(*media_type, &MF_MT_USER_DATA); + } + + return hr; +} + +static HRESULT wg_media_type_to_mf(const struct wg_media_type *wg_media_type, IMFMediaType **media_type) +{ + if (IsEqualGUID(&wg_media_type->major, &MFMediaType_Video)) + return media_type_from_video_format(wg_media_type->u.video, media_type); + if (IsEqualGUID(&wg_media_type->major, &MFMediaType_Audio)) + return MFCreateAudioMediaType(wg_media_type->u.audio, (IMFAudioMediaType **)media_type); + + FIXME("Unsupported major type %s\n", debugstr_guid(&wg_media_type->major)); + return E_NOTIMPL; +} + wg_parser_t wg_parser_create(bool output_compressed) { struct wg_parser_create_params params = @@ -448,17 +483,35 @@ bool wg_transform_get_status(wg_transform_t transform, bool *accepts_input) return true; }
-bool wg_transform_get_output_format(wg_transform_t transform, struct wg_format *format) +HRESULT wg_transform_get_output_type(wg_transform_t transform, IMFMediaType **media_type) { - struct wg_transform_get_output_format_params params = + struct wg_transform_get_output_type_params params = { .transform = transform, - .format = format, }; + NTSTATUS status; + HRESULT hr;
- TRACE("transform %#I64x, format %p.\n", transform, format); + TRACE("transform %#I64x, media_type %p.\n", transform, media_type);
- return !WINE_UNIX_CALL(unix_wg_transform_get_output_format, ¶ms); + if ((status = WINE_UNIX_CALL(unix_wg_transform_get_output_type, ¶ms)) + && status == STATUS_BUFFER_TOO_SMALL) + { + if (!(params.media_type.u.format = CoTaskMemAlloc(params.media_type.format_size))) + return ERROR_OUTOFMEMORY; + status = WINE_UNIX_CALL(unix_wg_transform_get_output_type, ¶ms); + } + + if (status) + { + CoTaskMemFree(params.media_type.u.format); + WARN("Failed to get output media type, status %#lx\n", status); + return HRESULT_FROM_NT(status); + } + + hr = wg_media_type_to_mf(¶ms.media_type, media_type); + CoTaskMemFree(params.media_type.u.format); + return hr; }
bool wg_transform_set_output_format(wg_transform_t transform, struct wg_format *format) diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 6f01b3a5a69..6f23e5f987e 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -25,6 +25,7 @@
#include <stdbool.h> #include <gst/gst.h> +#include <gst/audio/audio.h>
/* unixlib.c */
@@ -51,12 +52,13 @@ extern bool push_event(GstPad *pad, GstEvent *event); extern void wg_format_from_caps(struct wg_format *format, const GstCaps *caps); extern bool wg_format_compare(const struct wg_format *a, const struct wg_format *b); extern GstCaps *wg_format_to_caps(const struct wg_format *format); +extern uint32_t wg_channel_mask_from_gst(const GstAudioInfo *info);
/* wg_transform.c */
extern NTSTATUS wg_transform_create(void *args); extern NTSTATUS wg_transform_destroy(void *args); -extern NTSTATUS wg_transform_get_output_format(void *args); +extern NTSTATUS wg_transform_get_output_type(void *args); extern NTSTATUS wg_transform_set_output_format(void *args); extern NTSTATUS wg_transform_push_data(void *args); extern NTSTATUS wg_transform_read_data(void *args); @@ -65,6 +67,11 @@ extern NTSTATUS wg_transform_drain(void *args); extern NTSTATUS wg_transform_flush(void *args); extern NTSTATUS wg_transform_notify_qos(void *args);
+/* wg_media_type.c */ + +extern NTSTATUS caps_to_media_type(GstCaps *caps, struct wg_media_type *media_type, + UINT32 video_plane_align); + /* wg_muxer.c */
extern NTSTATUS wg_muxer_create(void *args); diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 153b6c91306..a5f693a09de 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -26,9 +26,40 @@ #include "winternl.h" #include "wtypes.h" #include "mmreg.h" +#include "vfw.h" +#include "dshow.h" +#include "dvdmedia.h" +#include "mfobjects.h"
#include "wine/unixlib.h"
+/* same as MPEG1VIDEOINFO / MPEG2VIDEOINFO but with MFVIDEOFORMAT */ +struct mpeg_video_format +{ + MFVIDEOFORMAT hdr; + UINT32 start_time_code; + UINT32 profile; + UINT32 level; + UINT32 flags; + UINT32 sequence_header_count; + UINT32 __pad; + BYTE sequence_header[]; +}; + +C_ASSERT(sizeof(struct mpeg_video_format) == offsetof(struct mpeg_video_format, sequence_header[0])); + +struct wg_media_type +{ + GUID major; + UINT32 format_size; + union + { + void *format; + WAVEFORMATEX *audio; + MFVIDEOFORMAT *video; + } u; +}; + typedef UINT32 wg_major_type; enum wg_major_type { @@ -328,10 +359,10 @@ struct wg_transform_read_data_params HRESULT result; };
-struct wg_transform_get_output_format_params +struct wg_transform_get_output_type_params { wg_transform_t transform; - struct wg_format *format; + struct wg_media_type media_type; };
struct wg_transform_set_output_format_params @@ -415,7 +446,7 @@ enum unix_funcs
unix_wg_transform_create, unix_wg_transform_destroy, - unix_wg_transform_get_output_format, + unix_wg_transform_get_output_type, unix_wg_transform_set_output_format,
unix_wg_transform_push_data, diff --git a/dlls/winegstreamer/video_decoder.c b/dlls/winegstreamer/video_decoder.c index 891d2d10e83..9c50743eac7 100644 --- a/dlls/winegstreamer/video_decoder.c +++ b/dlls/winegstreamer/video_decoder.c @@ -855,15 +855,15 @@ static HRESULT output_sample(struct video_decoder *decoder, IMFSample **out, IMF static HRESULT handle_stream_type_change(struct video_decoder *decoder) { UINT64 frame_size, frame_rate; - struct wg_format format; HRESULT hr;
if (decoder->stream_type) IMFMediaType_Release(decoder->stream_type); - if (!(wg_transform_get_output_format(decoder->wg_transform, &format))) - return E_FAIL; - if (!(decoder->stream_type = mf_media_type_from_wg_format(&format))) - return E_OUTOFMEMORY; + if (FAILED(hr = wg_transform_get_output_type(decoder->wg_transform, &decoder->stream_type))) + { + WARN("Failed to get transform output type, hr %#lx\n", hr); + return hr; + }
if (SUCCEEDED(IMFMediaType_GetUINT64(decoder->output_type, &MF_MT_FRAME_RATE, &frame_rate)) && FAILED(hr = IMFMediaType_SetUINT64(decoder->stream_type, &MF_MT_FRAME_RATE, frame_rate))) diff --git a/dlls/winegstreamer/wg_format.c b/dlls/winegstreamer/wg_format.c index ec239142339..dac618fd7f5 100644 --- a/dlls/winegstreamer/wg_format.c +++ b/dlls/winegstreamer/wg_format.c @@ -96,7 +96,7 @@ static uint32_t wg_channel_position_from_gst(GstAudioChannelPosition position) return 0; }
-static uint32_t wg_channel_mask_from_gst(const GstAudioInfo *info) +uint32_t wg_channel_mask_from_gst(const GstAudioInfo *info) { uint32_t mask = 0, position; unsigned int i; diff --git a/dlls/winegstreamer/wg_media_type.c b/dlls/winegstreamer/wg_media_type.c new file mode 100644 index 00000000000..21338e89399 --- /dev/null +++ b/dlls/winegstreamer/wg_media_type.c @@ -0,0 +1,461 @@ +/* + * Copyright 2024 Rémi Bernon 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 <stdarg.h> +#include <stddef.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 "windef.h" +#include "winbase.h" + +#include "initguid.h" +#include "d3d9types.h" +#include "mfapi.h" +#include "wmcodecdsp.h" + +#include "unix_private.h" + +#define WG_GUID_FORMAT "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}" +#define WG_GUID_ARGS(guid) (int)(guid).Data1, (guid).Data2, (guid).Data3, (guid).Data4[0], \ + (guid).Data4[1], (guid).Data4[2], (guid).Data4[3], (guid).Data4[4], \ + (guid).Data4[5], (guid).Data4[6], (guid).Data4[7] + +#define WG_RATIO_FORMAT "%d:%d" +#define WG_RATIO_ARGS(ratio) (int)(ratio).Numerator, (int)(ratio).Denominator + +#define WG_APERTURE_FORMAT "(%d,%d)-(%d,%d)" +#define WG_APERTURE_ARGS(aperture) (int)(aperture).OffsetX.value, (int)(aperture).OffsetY.value, \ + (int)(aperture).Area.cx, (int)(aperture).Area.cy + +static const GUID GUID_NULL; + +DEFINE_MEDIATYPE_GUID(MFAudioFormat_RAW_AAC,WAVE_FORMAT_RAW_AAC1); +DEFINE_MEDIATYPE_GUID(MFAudioFormat_MSAudio1,WAVE_FORMAT_MSAUDIO1); + +DEFINE_MEDIATYPE_GUID(MFVideoFormat_CVID,MAKEFOURCC('c','v','i','d')); +DEFINE_MEDIATYPE_GUID(MFVideoFormat_IV50,MAKEFOURCC('I','V','5','0')); +DEFINE_MEDIATYPE_GUID(MFVideoFormat_VC1S,MAKEFOURCC('V','C','1','S')); +DEFINE_MEDIATYPE_GUID(MFVideoFormat_ABGR32,D3DFMT_A8B8G8R8); + +static WORD wave_format_tag_from_gst_audio_format(GstAudioFormat audio_format) +{ + switch (audio_format) + { + case GST_AUDIO_FORMAT_U8: return WAVE_FORMAT_PCM; + case GST_AUDIO_FORMAT_S16LE: return WAVE_FORMAT_PCM; + case GST_AUDIO_FORMAT_S24LE: return WAVE_FORMAT_PCM; + case GST_AUDIO_FORMAT_S32LE: return WAVE_FORMAT_PCM; + case GST_AUDIO_FORMAT_F32LE: return WAVE_FORMAT_IEEE_FLOAT; + case GST_AUDIO_FORMAT_F64LE: return WAVE_FORMAT_IEEE_FLOAT; + default: return WAVE_FORMAT_EXTENSIBLE; + } +} + +static GUID subtype_from_gst_audio_format(GstAudioFormat audio_format) +{ + switch (audio_format) + { + case GST_AUDIO_FORMAT_U8: return MFAudioFormat_PCM; + case GST_AUDIO_FORMAT_S16LE: return MFAudioFormat_PCM; + case GST_AUDIO_FORMAT_S24LE: return MFAudioFormat_PCM; + case GST_AUDIO_FORMAT_S32LE: return MFAudioFormat_PCM; + case GST_AUDIO_FORMAT_F32LE: return MFAudioFormat_Float; + case GST_AUDIO_FORMAT_F64LE: return MFAudioFormat_Float; + default: return GUID_NULL; + } +} + +static void init_wave_format_ex_from_gst_caps(const GstCaps *caps, WORD format_tag, gint depth, + WAVEFORMATEX *format, UINT32 format_size) +{ + const GstStructure *structure = gst_caps_get_structure(caps, 0); + gint bitrate, channels, rate, block_align; + + memset(format, 0, format_size); + format->cbSize = format_size - sizeof(*format); + format->wFormatTag = format_tag; + format->wBitsPerSample = depth; + + if (gst_structure_get_int(structure, "channels", &channels)) + format->nChannels = channels; + if (gst_structure_get_int(structure, "rate", &rate)) + format->nSamplesPerSec = rate; + if (gst_structure_get_int(structure, "depth", &depth)) + format->wBitsPerSample = depth; + + format->nBlockAlign = format->wBitsPerSample * format->nChannels / 8; + format->nAvgBytesPerSec = format->nSamplesPerSec * format->nBlockAlign; + + if (gst_structure_get_int(structure, "block_align", &block_align)) + format->nBlockAlign = block_align; + if (gst_structure_get_int(structure, "bitrate", &bitrate)) + format->nAvgBytesPerSec = bitrate / 8; +} + +static GstBuffer *caps_get_buffer(const GstCaps *caps, const char *name, UINT32 *buffer_size) +{ + const GstStructure *structure = gst_caps_get_structure(caps, 0); + const GValue *buffer_value; + + if ((buffer_value = gst_structure_get_value(structure, name))) + { + GstBuffer *buffer = gst_value_get_buffer(buffer_value); + *buffer_size = gst_buffer_get_size(buffer); + return buffer; + } + + *buffer_size = 0; + return NULL; +} + +static NTSTATUS wave_format_extensible_from_gst_caps(const GstCaps *caps, const GUID *subtype, UINT32 depth, + UINT64 channel_mask, WAVEFORMATEXTENSIBLE *format, UINT32 *format_size) +{ + UINT32 capacity = *format_size, codec_data_size; + GstBuffer *codec_data = caps_get_buffer(caps, "codec_data", &codec_data_size); + + *format_size = sizeof(*format) + codec_data_size; + if (*format_size > capacity) + return STATUS_BUFFER_TOO_SMALL; + + init_wave_format_ex_from_gst_caps(caps, WAVE_FORMAT_EXTENSIBLE, depth, &format->Format, *format_size); + format->Samples.wValidBitsPerSample = 0; + format->dwChannelMask = channel_mask; + format->SubFormat = *subtype; + + if (codec_data) + gst_buffer_extract(codec_data, 0, format + 1, codec_data_size); + + GST_TRACE("tag %#x, %u channels, sample rate %u, %u bytes/sec, alignment %u, %u bits/sample, " + "%u valid bps, channel mask %#x, subtype " WG_GUID_FORMAT ".", + format->Format.wFormatTag, format->Format.nChannels, (int)format->Format.nSamplesPerSec, + (int)format->Format.nAvgBytesPerSec, format->Format.nBlockAlign, format->Format.wBitsPerSample, + format->Samples.wValidBitsPerSample, (int)format->dwChannelMask, WG_GUID_ARGS(format->SubFormat)); + if (format->Format.cbSize) + { + guint extra_size = sizeof(WAVEFORMATEX) + format->Format.cbSize - sizeof(WAVEFORMATEXTENSIBLE); + GST_MEMDUMP("extra bytes:", (guint8 *)(format + 1), extra_size); + } + + return STATUS_SUCCESS; +} + +static NTSTATUS wave_format_ex_from_gst_caps(const GstCaps *caps, WORD format_tag, UINT32 depth, + UINT32 wave_format_size, WAVEFORMATEX *format, UINT32 *format_size) +{ + UINT32 capacity = *format_size, codec_data_size; + GstBuffer *codec_data = caps_get_buffer(caps, "codec_data", &codec_data_size); + + *format_size = max(wave_format_size, sizeof(*format) + codec_data_size); + if (*format_size > capacity) + return STATUS_BUFFER_TOO_SMALL; + + init_wave_format_ex_from_gst_caps(caps, format_tag, depth, format, *format_size); + + if (codec_data) + gst_buffer_extract(codec_data, 0, format + 1, codec_data_size); + + GST_TRACE("tag %#x, %u channels, sample rate %u, %u bytes/sec, alignment %u, %u bits/sample.", + format->wFormatTag, format->nChannels, (int)format->nSamplesPerSec, + (int)format->nAvgBytesPerSec, format->nBlockAlign, format->wBitsPerSample); + if (format->cbSize) GST_MEMDUMP("extra bytes:", (guint8 *)(format + 1), format->cbSize); + + return STATUS_SUCCESS; +} + +static NTSTATUS wave_format_from_gst_caps(const GstCaps *caps, void *format, UINT32 *format_size) +{ + const GstStructure *structure = gst_caps_get_structure(caps, 0); + GstAudioFormat audio_format = GST_AUDIO_FORMAT_ENCODED; + WORD format_tag = WAVE_FORMAT_EXTENSIBLE; + gint channels, depth = 0; + const gchar *str_value; + guint64 channel_mask; + + if ((str_value = gst_structure_get_string(structure, "format"))) + { + audio_format = gst_audio_format_from_string(str_value); + format_tag = wave_format_tag_from_gst_audio_format(audio_format); + depth = GST_AUDIO_FORMAT_INFO_DEPTH(gst_audio_format_get_info(audio_format)); + } + + if (!gst_structure_get_int(structure, "channels", &channels)) + channels = 1; + if (!gst_structure_get(structure, "channel-mask", GST_TYPE_BITMASK, &channel_mask, NULL)) + channel_mask = 0; + + if (format_tag == WAVE_FORMAT_EXTENSIBLE || channel_mask != 0) + { + GUID subtype = subtype_from_gst_audio_format(audio_format); + return wave_format_extensible_from_gst_caps(caps, &subtype, depth, channel_mask, format, format_size); + } + + return wave_format_ex_from_gst_caps(caps, format_tag, depth, sizeof(WAVEFORMATEX), format, format_size); +} + +static NTSTATUS mpeg_wave_format_from_gst_caps(const GstCaps *caps, WAVEFORMATEX *format, UINT32 *format_size) +{ + const GstStructure *structure = gst_caps_get_structure(caps, 0); + NTSTATUS status; + gint layer = 0; + + if (gst_structure_get_int(structure, "layer", &layer) && layer == 3) + return wave_format_ex_from_gst_caps(caps, WAVE_FORMAT_MPEGLAYER3, 0, sizeof(MPEGLAYER3WAVEFORMAT), format, format_size); + + if (!(status = wave_format_ex_from_gst_caps(caps, WAVE_FORMAT_MPEG, 0, sizeof(MPEG1WAVEFORMAT), format, format_size))) + { + MPEG1WAVEFORMAT *mpeg = CONTAINING_RECORD(format, MPEG1WAVEFORMAT, wfx); + mpeg->fwHeadLayer = layer; + } + + return status; +} + +static NTSTATUS wma_wave_format_from_gst_caps(const GstCaps *caps, WAVEFORMATEX *format, UINT32 *format_size) +{ + const GstStructure *structure = gst_caps_get_structure(caps, 0); + gint wmaversion; + + if (!gst_structure_get_int(structure, "wmaversion", &wmaversion)) + { + GST_WARNING("Missing "wmaversion" value in %" GST_PTR_FORMAT ".", caps); + return STATUS_INVALID_PARAMETER; + } + + if (wmaversion == 1) + return wave_format_ex_from_gst_caps(caps, WAVE_FORMAT_MSAUDIO1, 0, sizeof(MSAUDIO1WAVEFORMAT), format, format_size); + if (wmaversion == 2) + return wave_format_ex_from_gst_caps(caps, WAVE_FORMAT_WMAUDIO2, 0, sizeof(WMAUDIO2WAVEFORMAT), format, format_size); + if (wmaversion == 3) + return wave_format_ex_from_gst_caps(caps, WAVE_FORMAT_WMAUDIO3, 0, sizeof(WMAUDIO3WAVEFORMAT), format, format_size); + + GST_FIXME("Unsupported wmaversion %u", wmaversion); + return STATUS_NOT_IMPLEMENTED; +} + +static GUID subtype_from_gst_video_format(GstVideoFormat video_format) +{ + switch (video_format) + { + case GST_VIDEO_FORMAT_BGRA: return MFVideoFormat_ARGB32; + case GST_VIDEO_FORMAT_BGRx: return MFVideoFormat_RGB32; + case GST_VIDEO_FORMAT_BGR: return MFVideoFormat_RGB24; + case GST_VIDEO_FORMAT_RGBA: return MFVideoFormat_ABGR32; + case GST_VIDEO_FORMAT_RGB15: return MFVideoFormat_RGB555; + case GST_VIDEO_FORMAT_RGB16: return MFVideoFormat_RGB565; + case GST_VIDEO_FORMAT_AYUV: return MFVideoFormat_AYUV; + case GST_VIDEO_FORMAT_I420: return MFVideoFormat_I420; + case GST_VIDEO_FORMAT_NV12: return MFVideoFormat_NV12; + case GST_VIDEO_FORMAT_UYVY: return MFVideoFormat_UYVY; + case GST_VIDEO_FORMAT_YUY2: return MFVideoFormat_YUY2; + case GST_VIDEO_FORMAT_YV12: return MFVideoFormat_YV12; + case GST_VIDEO_FORMAT_YVYU: return MFVideoFormat_YVYU; + default: return GUID_NULL; + } +} + +static void init_mf_video_format_from_gst_caps(const GstCaps *caps, const GUID *subtype, MFVIDEOFORMAT *format, + UINT32 format_size, UINT32 video_plane_align) +{ + const GstStructure *structure = gst_caps_get_structure(caps, 0); + gint width = 0, height = 0, num_value, den_value; + const gchar *str_value; + + memset(format, 0, format_size); + format->dwSize = format_size; + + if (subtype) + format->guidFormat = *subtype; + else if ((str_value = gst_structure_get_string(structure, "format"))) + { + GstVideoFormat video_format = gst_video_format_from_string(str_value); + format->guidFormat = subtype_from_gst_video_format(video_format); + } + + if (gst_structure_get_int(structure, "width", &width)) + format->videoInfo.dwWidth = (width + video_plane_align) & ~video_plane_align; + if (gst_structure_get_int(structure, "height", &height)) + format->videoInfo.dwHeight = (height + video_plane_align) & ~video_plane_align; + if (format->videoInfo.dwWidth != width || format->videoInfo.dwHeight != height) + { + format->videoInfo.MinimumDisplayAperture.Area.cx = width; + format->videoInfo.MinimumDisplayAperture.Area.cy = height; + } + format->videoInfo.GeometricAperture = format->videoInfo.MinimumDisplayAperture; + format->videoInfo.PanScanAperture = format->videoInfo.MinimumDisplayAperture; + + if (gst_structure_get_fraction(structure, "pixel-aspect-ratio", &num_value, &den_value)) + { + format->videoInfo.PixelAspectRatio.Numerator = num_value; + format->videoInfo.PixelAspectRatio.Denominator = den_value; + } + if (gst_structure_get_fraction(structure, "framerate", &num_value, &den_value)) + { + format->videoInfo.FramesPerSecond.Numerator = num_value; + format->videoInfo.FramesPerSecond.Denominator = den_value; + } +} + +static NTSTATUS video_format_from_gst_caps(const GstCaps *caps, const GUID *subtype, MFVIDEOFORMAT *format, + UINT32 *format_size, UINT32 video_plane_align) +{ + UINT32 capacity = *format_size, codec_data_size; + GstBuffer *codec_data = caps_get_buffer(caps, "codec_data", &codec_data_size); + + *format_size = sizeof(*format) + codec_data_size; + if (*format_size > capacity) + return STATUS_BUFFER_TOO_SMALL; + + init_mf_video_format_from_gst_caps(caps, subtype, format, *format_size, video_plane_align); + + if (codec_data) + gst_buffer_extract(codec_data, 0, format + 1, codec_data_size); + + GST_TRACE("subtype " WG_GUID_FORMAT " %ux%u, FPS " WG_RATIO_FORMAT ", aperture " WG_APERTURE_FORMAT ", " + "PAR " WG_RATIO_FORMAT ", videoFlags %#x.", + WG_GUID_ARGS(format->guidFormat), (int)format->videoInfo.dwWidth, (int)format->videoInfo.dwHeight, + WG_RATIO_ARGS(format->videoInfo.FramesPerSecond), WG_APERTURE_ARGS(format->videoInfo.MinimumDisplayAperture), + WG_RATIO_ARGS(format->videoInfo.PixelAspectRatio), (int)format->videoInfo.VideoFlags ); + if (format->dwSize > sizeof(*format)) GST_MEMDUMP("extra bytes:", (guint8 *)(format + 1), format->dwSize - sizeof(*format)); + + return STATUS_SUCCESS; +} + +static NTSTATUS wmv_video_format_from_gst_caps(const GstCaps *caps, MFVIDEOFORMAT *format, + UINT32 *format_size, UINT32 video_plane_align) +{ + const GstStructure *structure = gst_caps_get_structure(caps, 0); + gchar format_buffer[5] = {'W','M','V','0',0}; + const gchar *wmv_format_str; + gint wmv_version = 0; + const GUID *subtype; + + if (!(wmv_format_str = gst_structure_get_string(structure, "format"))) + { + if (!gst_structure_get_int(structure, "wmvversion", &wmv_version)) + GST_WARNING("Unable to get WMV format."); + format_buffer[3] += wmv_version; + wmv_format_str = format_buffer; + } + + if (!strcmp(wmv_format_str, "WMV1")) + subtype = &MFVideoFormat_WMV1; + else if (!strcmp(wmv_format_str, "WMV2")) + subtype = &MFVideoFormat_WMV2; + else if (!strcmp(wmv_format_str, "WMV3")) + subtype = &MFVideoFormat_WMV3; + else if (!strcmp(wmv_format_str, "WMVA")) + subtype = &MEDIASUBTYPE_WMVA; + else if (!strcmp(wmv_format_str, "WVC1")) + subtype = &MFVideoFormat_WVC1; + else + { + GST_WARNING("Unknown "wmvversion" value."); + return STATUS_INVALID_PARAMETER; + } + + return video_format_from_gst_caps(caps, subtype, format, format_size, video_plane_align); +} + +static NTSTATUS mpeg_video_format_from_gst_caps(const GstCaps *caps, struct mpeg_video_format *format, + UINT32 *format_size, UINT32 video_plane_align) +{ + UINT32 capacity = *format_size, codec_data_size; + GstBuffer *codec_data = caps_get_buffer(caps, "codec_data", &codec_data_size); + + *format_size = sizeof(*format) + codec_data_size; + if (*format_size > capacity) + return STATUS_BUFFER_TOO_SMALL; + + init_mf_video_format_from_gst_caps(caps, &MEDIASUBTYPE_MPEG1Payload, &format->hdr, *format_size, video_plane_align); + + if (codec_data) + { + gst_buffer_extract(codec_data, 0, format->sequence_header, codec_data_size); + format->sequence_header_count = codec_data_size; + } + + GST_TRACE("subtype " WG_GUID_FORMAT " %ux%u, FPS " WG_RATIO_FORMAT ", aperture " WG_APERTURE_FORMAT ", " + "PAR " WG_RATIO_FORMAT ", videoFlags %#x, start_time_code %u, profile %u, level %u, flags %#x.", + WG_GUID_ARGS(format->hdr.guidFormat), (int)format->hdr.videoInfo.dwWidth, (int)format->hdr.videoInfo.dwHeight, + WG_RATIO_ARGS(format->hdr.videoInfo.FramesPerSecond), WG_APERTURE_ARGS(format->hdr.videoInfo.MinimumDisplayAperture), + WG_RATIO_ARGS(format->hdr.videoInfo.PixelAspectRatio), (int)format->hdr.videoInfo.VideoFlags, format->start_time_code, + format->profile, format->level, format->flags ); + if (format->sequence_header_count) GST_MEMDUMP("extra bytes:", format->sequence_header, format->sequence_header_count); + + return STATUS_SUCCESS; +} + +NTSTATUS caps_to_media_type(GstCaps *caps, struct wg_media_type *media_type, UINT32 video_plane_align) +{ + const GstStructure *structure = gst_caps_get_structure(caps, 0); + const char *name = gst_structure_get_name(structure); + gboolean parsed; + + GST_TRACE("caps %"GST_PTR_FORMAT, caps); + + if (g_str_has_prefix(name, "audio/")) + { + media_type->major = MFMediaType_Audio; + + if (!strcmp(name, "audio/x-raw")) + return wave_format_from_gst_caps(caps, media_type->u.audio, &media_type->format_size); + if (!strcmp(name, "audio/mpeg") && gst_structure_get_boolean(structure, "parsed", &parsed) && parsed) + return mpeg_wave_format_from_gst_caps(caps, media_type->u.audio, &media_type->format_size); + if (!strcmp(name, "audio/x-wma")) + return wma_wave_format_from_gst_caps(caps, media_type->u.audio, &media_type->format_size); + + GST_FIXME("Unhandled caps %" GST_PTR_FORMAT ".", caps); + return STATUS_UNSUCCESSFUL; + } + else if (g_str_has_prefix(name, "video/")) + { + media_type->major = MFMediaType_Video; + + if (!strcmp(name, "video/x-raw")) + return video_format_from_gst_caps(caps, NULL, media_type->u.video, &media_type->format_size, video_plane_align); + if (!strcmp(name, "video/x-cinepak")) + return video_format_from_gst_caps(caps, &MFVideoFormat_CVID, media_type->u.video, &media_type->format_size, video_plane_align); + if (!strcmp(name, "video/x-wmv")) + return wmv_video_format_from_gst_caps(caps, media_type->u.video, &media_type->format_size, video_plane_align); + if (!strcmp(name, "video/mpeg") && gst_structure_get_boolean(structure, "parsed", &parsed) && parsed) + return mpeg_video_format_from_gst_caps(caps, media_type->u.format, &media_type->format_size, video_plane_align); + + GST_FIXME("Unhandled caps %" GST_PTR_FORMAT ".", caps); + return STATUS_UNSUCCESSFUL; + } + else + { + GST_FIXME("Unhandled caps %" GST_PTR_FORMAT ".", caps); + return STATUS_UNSUCCESSFUL; + } + + return STATUS_SUCCESS; +} diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 7253013b6a3..06cd1aeeb0e 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1882,7 +1882,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
X(wg_transform_create), X(wg_transform_destroy), - X(wg_transform_get_output_format), + X(wg_transform_get_output_type), X(wg_transform_set_output_format),
X(wg_transform_push_data), @@ -1907,6 +1907,13 @@ C_ASSERT(ARRAYSIZE(__wine_unix_call_funcs) == unix_wg_funcs_count);
typedef ULONG PTR32;
+struct wg_media_type32 +{ + GUID major; + UINT32 format_size; + PTR32 format; +}; + static NTSTATUS wow64_wg_parser_connect(void *args) { struct @@ -2068,19 +2075,29 @@ NTSTATUS wow64_wg_transform_create(void *args) return ret; }
-NTSTATUS wow64_wg_transform_get_output_format(void *args) +NTSTATUS wow64_wg_transform_get_output_type(void *args) { struct { wg_transform_t transform; - PTR32 format; + struct wg_media_type32 media_type; } *params32 = args; - struct wg_transform_get_output_format_params params = + struct wg_transform_get_output_type_params params = { .transform = params32->transform, - .format = ULongToPtr(params32->format), + .media_type = + { + .major = params32->media_type.major, + .format_size = params32->media_type.format_size, + .u.format = ULongToPtr(params32->media_type.format), + }, }; - return wg_transform_get_output_format(¶ms); + NTSTATUS status; + + status = wg_transform_get_output_type(¶ms); + params32->media_type.major = params.media_type.major; + params32->media_type.format_size = params.media_type.format_size; + return status; }
NTSTATUS wow64_wg_transform_set_output_format(void *args) @@ -2247,7 +2264,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] =
X64(wg_transform_create), X(wg_transform_destroy), - X64(wg_transform_get_output_format), + X64(wg_transform_get_output_type), X64(wg_transform_set_output_format),
X64(wg_transform_push_data), diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 08c2c678024..4f678573e83 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -36,6 +36,7 @@ #define WIN32_NO_STATUS #include "winternl.h" #include "mferror.h" +#include "mfapi.h"
#include "unix_private.h"
@@ -616,12 +617,10 @@ out: return status; }
-NTSTATUS wg_transform_get_output_format(void *args) +NTSTATUS wg_transform_get_output_type(void *args) { - struct wg_transform_get_output_format_params *params = args; + struct wg_transform_get_output_type_params *params = args; struct wg_transform *transform = get_transform(params->transform); - struct wg_format *format = params->format; - GstVideoInfo video_info; GstCaps *output_caps;
if (transform->output_sample) @@ -631,31 +630,7 @@ NTSTATUS wg_transform_get_output_format(void *args)
GST_INFO("transform %p output caps %"GST_PTR_FORMAT, transform, output_caps);
- wg_format_from_caps(format, output_caps); - - if (stream_type_from_caps(output_caps) == GST_STREAM_TYPE_VIDEO - && gst_video_info_from_caps(&video_info, output_caps)) - { - gsize plane_align = transform->attrs.output_plane_align; - GstVideoAlignment align = {0}; - - /* set the desired output buffer alignment on the dest video info */ - align_video_info_planes(plane_align, &video_info, &align); - - GST_INFO("Returning video alignment left %u, top %u, right %u, bottom %u.", align.padding_left, - align.padding_top, align.padding_right, align.padding_bottom); - - format->u.video.padding.left = align.padding_left; - format->u.video.width += format->u.video.padding.left; - format->u.video.padding.right = align.padding_right; - format->u.video.width += format->u.video.padding.right; - format->u.video.padding.top = align.padding_top; - format->u.video.height += format->u.video.padding.top; - format->u.video.padding.bottom = align.padding_bottom; - format->u.video.height += format->u.video.padding.bottom; - } - - return STATUS_SUCCESS; + return caps_to_media_type(output_caps, ¶ms->media_type, transform->attrs.output_plane_align); }
NTSTATUS wg_transform_set_output_format(void *args)
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winegstreamer/gst_private.h | 2 +- dlls/winegstreamer/main.c | 83 ++++++- dlls/winegstreamer/unix_private.h | 3 +- dlls/winegstreamer/unixlib.h | 6 +- dlls/winegstreamer/video_decoder.c | 20 +- dlls/winegstreamer/wg_media_type.c | 365 +++++++++++++++++++++++++++++ dlls/winegstreamer/wg_parser.c | 19 +- dlls/winegstreamer/wg_transform.c | 15 +- 8 files changed, 476 insertions(+), 37 deletions(-)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 12cd4bcaa30..6525381312d 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -89,7 +89,7 @@ HRESULT wg_transform_create_quartz(const AM_MEDIA_TYPE *input_format, const AM_M const struct wg_transform_attrs *attrs, wg_transform_t *transform); void wg_transform_destroy(wg_transform_t transform); HRESULT wg_transform_get_output_type(wg_transform_t transform, IMFMediaType **media_type); -bool wg_transform_set_output_format(wg_transform_t transform, struct wg_format *format); +HRESULT wg_transform_set_output_type(wg_transform_t transform, IMFMediaType *media_type); 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); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index e9e8c955d2a..203ac6a7a61 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -70,6 +70,66 @@ bool array_reserve(void **elements, size_t *capacity, size_t count, size_t size) return TRUE; }
+static HRESULT video_format_from_media_type(IMFMediaType *media_type, MFVIDEOFORMAT **format, UINT32 *format_size) +{ + GUID subtype; + HRESULT hr; + + if (FAILED(hr = IMFMediaType_GetGUID(media_type, &MF_MT_SUBTYPE, &subtype))) + return hr; + if (FAILED(hr = MFCreateMFVideoFormatFromMFMediaType(media_type, format, format_size))) + return hr; + + /* fixup MPEG video formats here, so we can consistently use MFVIDEOFORMAT internally */ + if (IsEqualGUID(&subtype, &MEDIASUBTYPE_MPEG1Payload) + || IsEqualGUID(&subtype, &MEDIASUBTYPE_MPEG1Packet) + || IsEqualGUID(&subtype, &MEDIASUBTYPE_MPEG2_VIDEO)) + { + struct mpeg_video_format *mpeg; + UINT32 mpeg_size, len; + + if (FAILED(IMFMediaType_GetBlobSize(media_type, &MF_MT_MPEG_SEQUENCE_HEADER, &len))) + len = 0; + mpeg_size = offsetof(struct mpeg_video_format, sequence_header[len]); + + if ((mpeg = CoTaskMemAlloc(mpeg_size))) + { + memset(mpeg, 0, mpeg_size); + mpeg->hdr = **format; + + IMFMediaType_GetBlob(media_type, &MF_MT_MPEG_SEQUENCE_HEADER, mpeg->sequence_header, len, NULL); + IMFMediaType_GetUINT32(media_type, &MF_MT_MPEG_START_TIME_CODE, (UINT32 *)&mpeg->start_time_code); + IMFMediaType_GetUINT32(media_type, &MF_MT_MPEG2_PROFILE, &mpeg->profile); + IMFMediaType_GetUINT32(media_type, &MF_MT_MPEG2_LEVEL, &mpeg->level); + IMFMediaType_GetUINT32(media_type, &MF_MT_MPEG2_FLAGS, &mpeg->flags); + + CoTaskMemFree(*format); + *format = &mpeg->hdr; + *format_size = mpeg_size; + } + } + + return hr; +} + +static HRESULT wg_media_type_from_mf(IMFMediaType *media_type, struct wg_media_type *wg_media_type) +{ + HRESULT hr; + + if (FAILED(hr = IMFMediaType_GetMajorType(media_type, &wg_media_type->major))) + return hr; + + if (IsEqualGUID(&wg_media_type->major, &MFMediaType_Video)) + return video_format_from_media_type(media_type, &wg_media_type->u.video, + &wg_media_type->format_size); + if (IsEqualGUID(&wg_media_type->major, &MFMediaType_Audio)) + return MFCreateWaveFormatExFromMFMediaType(media_type, &wg_media_type->u.audio, + &wg_media_type->format_size, 0); + + FIXME("Unsupported major type %s\n", debugstr_guid(&wg_media_type->major)); + return E_NOTIMPL; +} + static HRESULT media_type_from_video_format(const MFVIDEOFORMAT *format, IMFMediaType **media_type) { HRESULT hr; @@ -514,17 +574,30 @@ HRESULT wg_transform_get_output_type(wg_transform_t transform, IMFMediaType **me return hr; }
-bool wg_transform_set_output_format(wg_transform_t transform, struct wg_format *format) +HRESULT wg_transform_set_output_type(wg_transform_t transform, IMFMediaType *media_type) { - struct wg_transform_set_output_format_params params = + struct wg_transform_set_output_type_params params = { .transform = transform, - .format = format, }; + NTSTATUS status; + HRESULT hr;
- TRACE("transform %#I64x, format %p.\n", transform, format); + TRACE("transform %#I64x, media_type %p.\n", transform, media_type);
- return !WINE_UNIX_CALL(unix_wg_transform_set_output_format, ¶ms); + if (FAILED(hr = wg_media_type_from_mf(media_type, ¶ms.media_type))) + { + WARN("Failed to initialize media type, hr %#lx\n", hr); + return hr; + } + if ((status = WINE_UNIX_CALL(unix_wg_transform_set_output_type, ¶ms))) + { + WARN("Failed to set transform output type, status %#lx\n", status); + hr = HRESULT_FROM_NT(status); + } + + CoTaskMemFree(params.media_type.u.format); + return hr; }
HRESULT wg_transform_drain(wg_transform_t transform) diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 6f23e5f987e..985b70a925c 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -59,7 +59,7 @@ extern uint32_t wg_channel_mask_from_gst(const GstAudioInfo *info); extern NTSTATUS wg_transform_create(void *args); extern NTSTATUS wg_transform_destroy(void *args); extern NTSTATUS wg_transform_get_output_type(void *args); -extern NTSTATUS wg_transform_set_output_format(void *args); +extern NTSTATUS wg_transform_set_output_type(void *args); extern NTSTATUS wg_transform_push_data(void *args); extern NTSTATUS wg_transform_read_data(void *args); extern NTSTATUS wg_transform_get_status(void *args); @@ -69,6 +69,7 @@ extern NTSTATUS wg_transform_notify_qos(void *args);
/* wg_media_type.c */
+extern GstCaps *caps_from_media_type(const struct wg_media_type *media_type); extern NTSTATUS caps_to_media_type(GstCaps *caps, struct wg_media_type *media_type, UINT32 video_plane_align);
diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index a5f693a09de..94b2b8e347b 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -365,10 +365,10 @@ struct wg_transform_get_output_type_params struct wg_media_type media_type; };
-struct wg_transform_set_output_format_params +struct wg_transform_set_output_type_params { wg_transform_t transform; - const struct wg_format *format; + struct wg_media_type media_type; };
struct wg_transform_get_status_params @@ -447,7 +447,7 @@ enum unix_funcs unix_wg_transform_create, unix_wg_transform_destroy, unix_wg_transform_get_output_type, - unix_wg_transform_set_output_format, + unix_wg_transform_set_output_type,
unix_wg_transform_push_data, unix_wg_transform_read_data, diff --git a/dlls/winegstreamer/video_decoder.c b/dlls/winegstreamer/video_decoder.c index 9c50743eac7..3e91e55a779 100644 --- a/dlls/winegstreamer/video_decoder.c +++ b/dlls/winegstreamer/video_decoder.c @@ -694,25 +694,17 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF }
if (decoder->wg_transform) - { - struct wg_format output_format; - mf_media_type_to_wg_format(output_type, &output_format); + hr = wg_transform_set_output_type(decoder->wg_transform, output_type); + else + hr = try_create_wg_transform(decoder, output_type);
- if (output_format.major_type == WG_MAJOR_TYPE_UNKNOWN - || !wg_transform_set_output_format(decoder->wg_transform, &output_format)) - { - IMFMediaType_Release(decoder->output_type); - decoder->output_type = NULL; - hr = MF_E_INVALIDMEDIATYPE; - } - } - else if (FAILED(hr = try_create_wg_transform(decoder, output_type))) + IMFMediaType_Release(output_type); + + if (FAILED(hr)) { IMFMediaType_Release(decoder->output_type); decoder->output_type = NULL; } - - IMFMediaType_Release(output_type); return hr; }
diff --git a/dlls/winegstreamer/wg_media_type.c b/dlls/winegstreamer/wg_media_type.c index 21338e89399..14fc1a9cdf4 100644 --- a/dlls/winegstreamer/wg_media_type.c +++ b/dlls/winegstreamer/wg_media_type.c @@ -64,6 +64,371 @@ DEFINE_MEDIATYPE_GUID(MFVideoFormat_IV50,MAKEFOURCC('I','V','5','0')); DEFINE_MEDIATYPE_GUID(MFVideoFormat_VC1S,MAKEFOURCC('V','C','1','S')); DEFINE_MEDIATYPE_GUID(MFVideoFormat_ABGR32,D3DFMT_A8B8G8R8);
+static void init_caps_codec_data(GstCaps *caps, const void *codec_data, int codec_data_size) +{ + GstBuffer *buffer; + + if (codec_data_size > 0 && (buffer = gst_buffer_new_and_alloc(codec_data_size))) + { + gst_buffer_fill(buffer, 0, codec_data, codec_data_size); + gst_caps_set_simple(caps, "codec_data", GST_TYPE_BUFFER, buffer, NULL); + gst_buffer_unref(buffer); + } +} + +static void init_caps_from_wave_format_mpeg1(GstCaps *caps, const MPEG1WAVEFORMAT *format, UINT32 format_size) +{ + init_caps_codec_data(caps, &format->wfx + 1, format->wfx.cbSize); + + gst_structure_remove_field(gst_caps_get_structure(caps, 0), "format"); + gst_structure_set_name(gst_caps_get_structure(caps, 0), "audio/mpeg"); + gst_caps_set_simple(caps, "mpegversion", G_TYPE_INT, 1, NULL); + gst_caps_set_simple(caps, "layer", G_TYPE_INT, format->fwHeadLayer, NULL); + gst_caps_set_simple(caps, "parsed", G_TYPE_BOOLEAN, TRUE, NULL); +} + +static void init_caps_from_wave_format_mp3(GstCaps *caps, const MPEGLAYER3WAVEFORMAT *format, UINT32 format_size) +{ + init_caps_codec_data(caps, &format->wfx + 1, format->wfx.cbSize); + + gst_structure_remove_field(gst_caps_get_structure(caps, 0), "format"); + gst_structure_set_name(gst_caps_get_structure(caps, 0), "audio/mpeg"); + gst_caps_set_simple(caps, "mpegversion", G_TYPE_INT, 1, NULL); + gst_caps_set_simple(caps, "layer", G_TYPE_INT, 3, NULL); + gst_caps_set_simple(caps, "parsed", G_TYPE_BOOLEAN, TRUE, NULL); +} + +static void init_caps_from_wave_format_aac(GstCaps *caps, const HEAACWAVEFORMAT *format, UINT32 format_size) +{ + init_caps_codec_data(caps, format->pbAudioSpecificConfig, format_size - sizeof(format->wfInfo)); + + gst_structure_remove_field(gst_caps_get_structure(caps, 0), "format"); + gst_structure_set_name(gst_caps_get_structure(caps, 0), "audio/mpeg"); + gst_caps_set_simple(caps, "mpegversion", G_TYPE_INT, 4, NULL); + + switch (format->wfInfo.wPayloadType) + { + case 0: gst_caps_set_simple(caps, "stream-format", G_TYPE_STRING, "raw", NULL); break; + case 1: gst_caps_set_simple(caps, "stream-format", G_TYPE_STRING, "adts", NULL); break; + case 2: gst_caps_set_simple(caps, "stream-format", G_TYPE_STRING, "adif", NULL); break; + case 3: gst_caps_set_simple(caps, "stream-format", G_TYPE_STRING, "loas", NULL); break; + } + + /* FIXME: Use gst_codec_utils_aac_caps_set_level_and_profile from GStreamer pbutils library */ +} + +static void init_caps_from_wave_format_aac_raw(GstCaps *caps, const WAVEFORMATEX *format, UINT32 format_size) +{ + init_caps_codec_data(caps, format + 1, format->cbSize); + + gst_structure_remove_field(gst_caps_get_structure(caps, 0), "format"); + gst_structure_set_name(gst_caps_get_structure(caps, 0), "audio/mpeg"); + gst_caps_set_simple(caps, "stream-format", G_TYPE_STRING, "raw", NULL); + gst_caps_set_simple(caps, "mpegversion", G_TYPE_INT, 4, NULL); +} + +static void init_caps_from_wave_format_wma1(GstCaps *caps, const MSAUDIO1WAVEFORMAT *format, UINT32 format_size) +{ + init_caps_codec_data(caps, &format->wfx + 1, format->wfx.cbSize); + + gst_structure_remove_field(gst_caps_get_structure(caps, 0), "format"); + gst_structure_set_name(gst_caps_get_structure(caps, 0), "audio/x-wma"); + gst_caps_set_simple(caps, "wmaversion", G_TYPE_INT, 1, NULL); + gst_caps_set_simple(caps, "block_align", G_TYPE_INT, format->wfx.nBlockAlign, NULL); + gst_caps_set_simple(caps, "depth", G_TYPE_INT, format->wfx.wBitsPerSample, NULL); + gst_caps_set_simple(caps, "bitrate", G_TYPE_INT, format->wfx.nAvgBytesPerSec * 8, NULL); +} + +static void init_caps_from_wave_format_wma2(GstCaps *caps, const WMAUDIO2WAVEFORMAT *format, UINT32 format_size) +{ + init_caps_codec_data(caps, &format->wfx + 1, format->wfx.cbSize); + + gst_structure_remove_field(gst_caps_get_structure(caps, 0), "format"); + gst_structure_set_name(gst_caps_get_structure(caps, 0), "audio/x-wma"); + gst_caps_set_simple(caps, "wmaversion", G_TYPE_INT, 2, NULL); + gst_caps_set_simple(caps, "block_align", G_TYPE_INT, format->wfx.nBlockAlign, NULL); + gst_caps_set_simple(caps, "depth", G_TYPE_INT, format->wfx.wBitsPerSample, NULL); + gst_caps_set_simple(caps, "bitrate", G_TYPE_INT, format->wfx.nAvgBytesPerSec * 8, NULL); +} + +static void init_caps_from_wave_format_wma3(GstCaps *caps, const WMAUDIO3WAVEFORMAT *format, UINT32 format_size) +{ + init_caps_codec_data(caps, &format->wfx + 1, format->wfx.cbSize); + + gst_structure_remove_field(gst_caps_get_structure(caps, 0), "format"); + gst_structure_set_name(gst_caps_get_structure(caps, 0), "audio/x-wma"); + gst_caps_set_simple(caps, "wmaversion", G_TYPE_INT, 3, NULL); + gst_caps_set_simple(caps, "block_align", G_TYPE_INT, format->wfx.nBlockAlign, NULL); + gst_caps_set_simple(caps, "depth", G_TYPE_INT, format->wfx.wBitsPerSample, NULL); + gst_caps_set_simple(caps, "bitrate", G_TYPE_INT, format->wfx.nAvgBytesPerSec * 8, NULL); +} + +static void init_caps_from_wave_format(GstCaps *caps, const GUID *subtype, + const void *format, UINT32 format_size) +{ + if (IsEqualGUID(subtype, &MFAudioFormat_MPEG)) + return init_caps_from_wave_format_mpeg1(caps, format, format_size); + if (IsEqualGUID(subtype, &MFAudioFormat_MP3)) + return init_caps_from_wave_format_mp3(caps, format, format_size); + if (IsEqualGUID(subtype, &MFAudioFormat_AAC)) + return init_caps_from_wave_format_aac(caps, format, format_size); + if (IsEqualGUID(subtype, &MFAudioFormat_RAW_AAC)) + return init_caps_from_wave_format_aac_raw(caps, format, format_size); + if (IsEqualGUID(subtype, &MFAudioFormat_MSAudio1)) + return init_caps_from_wave_format_wma1(caps, format, format_size); + if (IsEqualGUID(subtype, &MFAudioFormat_WMAudioV8)) + return init_caps_from_wave_format_wma2(caps, format, format_size); + if (IsEqualGUID(subtype, &MFAudioFormat_WMAudioV9) + || IsEqualGUID(subtype, &MFAudioFormat_WMAudio_Lossless)) + return init_caps_from_wave_format_wma3(caps, format, format_size); + + GST_FIXME("Unsupported subtype " WG_GUID_FORMAT, WG_GUID_ARGS(*subtype)); +} + +static GstAudioFormat wave_format_tag_to_gst_audio_format(UINT tag, UINT depth) +{ + switch (tag) + { + case WAVE_FORMAT_PCM: + if (depth == 32) return GST_AUDIO_FORMAT_S32LE; + if (depth == 24) return GST_AUDIO_FORMAT_S24LE; + if (depth == 16) return GST_AUDIO_FORMAT_S16LE; + if (depth == 8) return GST_AUDIO_FORMAT_U8; + break; + + case WAVE_FORMAT_IEEE_FLOAT: + if (depth == 64) return GST_AUDIO_FORMAT_F64LE; + if (depth == 32) return GST_AUDIO_FORMAT_F32LE; + break; + } + + return GST_AUDIO_FORMAT_ENCODED; +} + +static GstCaps *caps_from_wave_format_ex(const WAVEFORMATEX *format, UINT32 format_size, const GUID *subtype, UINT64 channel_mask) +{ + GstAudioFormat audio_format = wave_format_tag_to_gst_audio_format(subtype->Data1, format->wBitsPerSample); + GstCaps *caps; + + if (!(caps = gst_caps_new_simple("audio/x-raw", "format", G_TYPE_STRING, gst_audio_format_to_string(audio_format), + "layout", G_TYPE_STRING, "interleaved", "rate", G_TYPE_INT, format->nSamplesPerSec, + "channels", G_TYPE_INT, format->nChannels, "channel-mask", GST_TYPE_BITMASK, channel_mask, NULL))) + return NULL; + + if (audio_format == GST_AUDIO_FORMAT_ENCODED) + init_caps_from_wave_format(caps, subtype, format, format_size); + + return caps; +} + +static WAVEFORMATEX *strip_wave_format_extensible(const WAVEFORMATEXTENSIBLE *format_ext) +{ + UINT32 extra_size = format_ext->Format.cbSize + sizeof(WAVEFORMATEX) - sizeof(WAVEFORMATEXTENSIBLE); + WAVEFORMATEX *format; + + if (!(format = malloc(sizeof(*format) + extra_size))) + return NULL; + + *format = format_ext->Format; + format->cbSize = extra_size; + format->wFormatTag = format_ext->SubFormat.Data1; + memcpy(format + 1, format_ext + 1, extra_size); + return format; +} + +static GstCaps *caps_from_wave_format_extensible(const WAVEFORMATEXTENSIBLE *format, UINT32 format_size) +{ + WAVEFORMATEX *wfx; + GstCaps *caps; + + GST_TRACE("tag %#x, %u channels, sample rate %u, %u bytes/sec, alignment %u, %u bits/sample, " + "%u valid bps, channel mask %#x, subtype " WG_GUID_FORMAT ".", + format->Format.wFormatTag, format->Format.nChannels, (int)format->Format.nSamplesPerSec, + (int)format->Format.nAvgBytesPerSec, format->Format.nBlockAlign, format->Format.wBitsPerSample, + format->Samples.wValidBitsPerSample, (int)format->dwChannelMask, WG_GUID_ARGS(format->SubFormat)); + if (format->Format.cbSize) + { + guint extra_size = sizeof(WAVEFORMATEX) + format->Format.cbSize - sizeof(WAVEFORMATEXTENSIBLE); + GST_MEMDUMP("extra bytes:", (guint8 *)(format + 1), extra_size); + } + + if (!(wfx = strip_wave_format_extensible(format))) + return NULL; + + caps = caps_from_wave_format_ex(wfx, format_size + sizeof(*wfx) - sizeof(*format), + &format->SubFormat, format->dwChannelMask); + free(wfx); + return caps; +} + +static GstCaps *caps_from_wave_format(const void *format, UINT32 format_size) +{ + const WAVEFORMATEX *wfx = format; + GUID subtype = MFAudioFormat_Base; + UINT channel_mask; + + if (wfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) + return caps_from_wave_format_extensible(format, format_size); + + GST_TRACE("tag %#x, %u channels, sample rate %u, %u bytes/sec, alignment %u, %u bits/sample.", + wfx->wFormatTag, wfx->nChannels, (int)wfx->nSamplesPerSec, + (int)wfx->nAvgBytesPerSec, wfx->nBlockAlign, wfx->wBitsPerSample); + if (wfx->cbSize) GST_MEMDUMP("extra bytes:", (guint8 *)(wfx + 1), wfx->cbSize); + + subtype.Data1 = wfx->wFormatTag; + channel_mask = gst_audio_channel_get_fallback_mask(wfx->nChannels); + return caps_from_wave_format_ex(format, format_size, &subtype, channel_mask); +} + +static void init_caps_from_video_cinepak(GstCaps *caps, const MFVIDEOFORMAT *format, UINT format_size) +{ + init_caps_codec_data(caps, format + 1, format_size - sizeof(*format)); + + gst_structure_remove_field(gst_caps_get_structure(caps, 0), "format"); + gst_structure_set_name(gst_caps_get_structure(caps, 0), "video/x-cinepak"); +} + +static void init_caps_from_video_h264(GstCaps *caps, const MFVIDEOFORMAT *format, UINT format_size) +{ + init_caps_codec_data(caps, format + 1, format_size - sizeof(*format)); + + gst_structure_remove_field(gst_caps_get_structure(caps, 0), "format"); + gst_structure_set_name(gst_caps_get_structure(caps, 0), "video/x-h264"); + gst_caps_set_simple(caps, "stream-format", G_TYPE_STRING, "byte-stream", NULL); + gst_caps_set_simple(caps, "alignment", G_TYPE_STRING, "au", NULL); +} + +static void init_caps_from_video_wmv(GstCaps *caps, const MFVIDEOFORMAT *format, UINT format_size, + int wmv_version, const char *wmv_format) +{ + init_caps_codec_data(caps, format + 1, format_size - sizeof(*format)); + + gst_structure_remove_field(gst_caps_get_structure(caps, 0), "format"); + gst_structure_set_name(gst_caps_get_structure(caps, 0), "video/x-wmv"); + gst_caps_set_simple(caps, "wmvversion", G_TYPE_INT, wmv_version, NULL); + gst_caps_set_simple(caps, "format", G_TYPE_STRING, wmv_format, NULL); +} + +static void init_caps_from_video_indeo(GstCaps *caps, const MFVIDEOFORMAT *format, UINT format_size) +{ + init_caps_codec_data(caps, format + 1, format_size - sizeof(*format)); + + gst_structure_remove_field(gst_caps_get_structure(caps, 0), "format"); + gst_structure_set_name(gst_caps_get_structure(caps, 0), "video/x-indeo"); + gst_caps_set_simple(caps, "indeoversion", G_TYPE_INT, 5, NULL); +} + +static void init_caps_from_video_mpeg(GstCaps *caps, const struct mpeg_video_format *format, UINT format_size) +{ + init_caps_codec_data(caps, format->sequence_header, format->sequence_header_count); + + gst_structure_remove_field(gst_caps_get_structure(caps, 0), "format"); + gst_structure_set_name(gst_caps_get_structure(caps, 0), "video/mpeg"); + gst_caps_set_simple(caps, "mpegversion", G_TYPE_INT, 1, NULL); + gst_caps_set_simple(caps, "systemstream", G_TYPE_BOOLEAN, FALSE, NULL); + gst_caps_set_simple(caps, "parsed", G_TYPE_BOOLEAN, TRUE, NULL); +} + +static void init_caps_from_video_subtype(GstCaps *caps, const GUID *subtype, const void *format, UINT format_size) +{ + if (IsEqualGUID(subtype, &MFVideoFormat_CVID)) + return init_caps_from_video_cinepak(caps, format, format_size); + if (IsEqualGUID(subtype, &MFVideoFormat_H264)) + return init_caps_from_video_h264(caps, format, format_size); + if (IsEqualGUID(subtype, &MFVideoFormat_WMV1)) + return init_caps_from_video_wmv(caps, format, format_size, 1, "WMV1"); + if (IsEqualGUID(subtype, &MFVideoFormat_WMV2)) + return init_caps_from_video_wmv(caps, format, format_size, 2, "WMV2"); + if (IsEqualGUID(subtype, &MFVideoFormat_WMV3)) + return init_caps_from_video_wmv(caps, format, format_size, 3, "WMV3"); + if (IsEqualGUID(subtype, &MEDIASUBTYPE_WMVA)) + return init_caps_from_video_wmv(caps, format, format_size, 3, "WMVA"); + if (IsEqualGUID(subtype, &MFVideoFormat_WVC1)) + return init_caps_from_video_wmv(caps, format, format_size, 3, "WVC1"); + if (IsEqualGUID(subtype, &MFVideoFormat_IV50)) + return init_caps_from_video_indeo(caps, format, format_size); + if (IsEqualGUID(subtype, &MEDIASUBTYPE_MPEG1Payload)) + return init_caps_from_video_mpeg(caps, format, format_size); + + GST_FIXME("Unsupported subtype " WG_GUID_FORMAT, WG_GUID_ARGS(*subtype)); +} + +static GstVideoFormat subtype_to_gst_video_format(const GUID *subtype) +{ + GUID base = *subtype; + base.Data1 = 0; + + if (IsEqualGUID(&base, &MFVideoFormat_Base)) + { + switch (subtype->Data1) + { + case D3DFMT_A8R8G8B8: return GST_VIDEO_FORMAT_BGRA; + case D3DFMT_X8R8G8B8: return GST_VIDEO_FORMAT_BGRx; + case D3DFMT_R8G8B8: return GST_VIDEO_FORMAT_BGR; + case D3DFMT_A8B8G8R8: return GST_VIDEO_FORMAT_RGBA; + case D3DFMT_X1R5G5B5: return GST_VIDEO_FORMAT_RGB15; + case D3DFMT_R5G6B5: return GST_VIDEO_FORMAT_RGB16; + case MAKEFOURCC('A','Y','U','V'): return GST_VIDEO_FORMAT_AYUV; + case MAKEFOURCC('I','4','2','0'): return GST_VIDEO_FORMAT_I420; + case MAKEFOURCC('N','V','1','2'): return GST_VIDEO_FORMAT_NV12; + case MAKEFOURCC('U','Y','V','Y'): return GST_VIDEO_FORMAT_UYVY; + case MAKEFOURCC('Y','U','Y','2'): return GST_VIDEO_FORMAT_YUY2; + case MAKEFOURCC('Y','V','1','2'): return GST_VIDEO_FORMAT_YV12; + case MAKEFOURCC('Y','V','Y','U'): return GST_VIDEO_FORMAT_YVYU; + } + } + + return GST_VIDEO_FORMAT_ENCODED; +} + +static GstCaps *caps_from_video_format(const MFVIDEOFORMAT *format, UINT32 format_size) +{ + GstVideoFormat video_format = subtype_to_gst_video_format(&format->guidFormat); + GstCaps *caps; + + GST_TRACE("subtype " WG_GUID_FORMAT " %ux%u, FPS " WG_RATIO_FORMAT ", aperture " WG_APERTURE_FORMAT ", " + "PAR " WG_RATIO_FORMAT ", videoFlags %#x.", + WG_GUID_ARGS(format->guidFormat), (int)format->videoInfo.dwWidth, (int)format->videoInfo.dwHeight, + WG_RATIO_ARGS(format->videoInfo.FramesPerSecond), WG_APERTURE_ARGS(format->videoInfo.MinimumDisplayAperture), + WG_RATIO_ARGS(format->videoInfo.PixelAspectRatio), (int)format->videoInfo.VideoFlags ); + if (format->dwSize > sizeof(*format)) GST_MEMDUMP("extra bytes:", (guint8 *)(format + 1), format->dwSize - sizeof(*format)); + + if (!(caps = gst_caps_new_simple("video/x-raw", "format", G_TYPE_STRING, gst_video_format_to_string(video_format), NULL))) + return NULL; + + if (format->videoInfo.dwWidth) + gst_caps_set_simple(caps, "width", G_TYPE_INT, format->videoInfo.dwWidth, NULL); + if (format->videoInfo.dwHeight) + gst_caps_set_simple(caps, "height", G_TYPE_INT, format->videoInfo.dwHeight, NULL); + + if (format->videoInfo.PixelAspectRatio.Denominator) + gst_caps_set_simple(caps, "pixel-aspect-ratio", GST_TYPE_FRACTION, + format->videoInfo.PixelAspectRatio.Numerator, + format->videoInfo.PixelAspectRatio.Denominator, NULL); + if (format->videoInfo.FramesPerSecond.Denominator) + gst_caps_set_simple(caps, "framerate", GST_TYPE_FRACTION, + format->videoInfo.FramesPerSecond.Numerator, + format->videoInfo.FramesPerSecond.Denominator, NULL); + + if (video_format == GST_VIDEO_FORMAT_ENCODED) + init_caps_from_video_subtype(caps, &format->guidFormat, format, format_size); + + return caps; +} + +GstCaps *caps_from_media_type(const struct wg_media_type *media_type) +{ + GstCaps *caps = NULL; + + if (IsEqualGUID(&media_type->major, &MFMediaType_Video)) + caps = caps_from_video_format(media_type->u.video, media_type->format_size); + else if (IsEqualGUID(&media_type->major, &MFMediaType_Audio)) + caps = caps_from_wave_format(media_type->u.audio, media_type->format_size); + + GST_TRACE("caps %"GST_PTR_FORMAT, caps); + return caps; +} + static WORD wave_format_tag_from_gst_audio_format(GstAudioFormat audio_format) { switch (audio_format) diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 06cd1aeeb0e..be49ec8b2aa 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1883,7 +1883,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_transform_create), X(wg_transform_destroy), X(wg_transform_get_output_type), - X(wg_transform_set_output_format), + X(wg_transform_set_output_type),
X(wg_transform_push_data), X(wg_transform_read_data), @@ -2100,19 +2100,24 @@ NTSTATUS wow64_wg_transform_get_output_type(void *args) return status; }
-NTSTATUS wow64_wg_transform_set_output_format(void *args) +NTSTATUS wow64_wg_transform_set_output_type(void *args) { struct { wg_transform_t transform; - PTR32 format; + struct wg_media_type32 media_type; } *params32 = args; - struct wg_transform_set_output_format_params params = + struct wg_transform_set_output_type_params params = { .transform = params32->transform, - .format = ULongToPtr(params32->format), + .media_type = + { + .major = params32->media_type.major, + .format_size = params32->media_type.format_size, + .u.format = ULongToPtr(params32->media_type.format), + }, }; - return wg_transform_set_output_format(¶ms); + return wg_transform_set_output_type(¶ms); }
NTSTATUS wow64_wg_transform_push_data(void *args) @@ -2265,7 +2270,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = X64(wg_transform_create), X(wg_transform_destroy), X64(wg_transform_get_output_type), - X64(wg_transform_set_output_format), + X64(wg_transform_set_output_type),
X64(wg_transform_push_data), X64(wg_transform_read_data), diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 4f678573e83..10345353e89 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -633,17 +633,20 @@ NTSTATUS wg_transform_get_output_type(void *args) return caps_to_media_type(output_caps, ¶ms->media_type, transform->attrs.output_plane_align); }
-NTSTATUS wg_transform_set_output_format(void *args) +NTSTATUS wg_transform_set_output_type(void *args) { - struct wg_transform_set_output_format_params *params = args; + struct wg_transform_set_output_type_params *params = args; struct wg_transform *transform = get_transform(params->transform); - const struct wg_format *format = params->format; + MFVideoInfo output_info = {0}; GstCaps *caps, *stripped; GstSample *sample;
- if (!(caps = wg_format_to_caps(format))) + if (IsEqualGUID(¶ms->media_type.major, &MFMediaType_Video)) + output_info = params->media_type.u.video->videoInfo; + + if (!(caps = caps_from_media_type(¶ms->media_type))) { - GST_ERROR("Failed to convert format %p to caps.", format); + GST_ERROR("Failed to convert media type to caps."); return STATUS_UNSUCCESSFUL; }
@@ -670,7 +673,7 @@ NTSTATUS wg_transform_set_output_format(void *args) if (transform->video_flip) { const char *value; - if (transform->input_is_flipped != wg_format_video_is_flipped(format)) + if (transform->input_is_flipped != !!(output_info.VideoFlags & MFVideoFlag_BottomUpLinearRep)) value = "vertical-flip"; else value = "none";
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";