Instead of going through a custom intermediate format.
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/mfplat.c | 8 + 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 | 425 +++++++++++++++++++++++++++++ dlls/winegstreamer/wg_parser.c | 31 ++- dlls/winegstreamer/wg_transform.c | 33 +-- 11 files changed, 569 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 e33aa6c4d18..bbce2848bac 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -22,6 +22,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 ba9064bc467..6b1208af802 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/mfplat.c b/dlls/winegstreamer/mfplat.c index 5775e8552db..db72892f434 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -17,6 +17,14 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include <stddef.h> +#include <stdarg.h> + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" + #include "gst_private.h"
#include "ks.h" 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..29dd0356dd5 --- /dev/null +++ b/dlls/winegstreamer/wg_media_type.c @@ -0,0 +1,425 @@ +/* + * 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" +#include "wine/debug.h" + +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 *wg_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 = wg_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); + + 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 = wg_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); + + 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, void *format, UINT32 *format_size) +{ + const GstStructure *structure = gst_caps_get_structure(caps, 0); + NTSTATUS status; + gint layer; + + if (!gst_structure_get_int(structure, "layer", &layer)) + { + GST_WARNING("Missing "layer" value in %" GST_PTR_FORMAT ".", caps); + return STATUS_INVALID_PARAMETER; + } + + if (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 = format; + mpeg->fwHeadLayer = layer; + } + + return status; +} + +static NTSTATUS wma_wave_format_from_gst_caps(const GstCaps *caps, void *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\n", 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 = wg_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); + + 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 = wg_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; + } + + 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_ERROR("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 f9b76b12f8f..b9c33e60f0b 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1889,7 +1889,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), @@ -1914,6 +1914,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 @@ -2075,19 +2082,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) @@ -2254,7 +2271,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 | 356 +++++++++++++++++++++++++++++ dlls/winegstreamer/wg_parser.c | 19 +- dlls/winegstreamer/wg_transform.c | 15 +- 8 files changed, 467 insertions(+), 37 deletions(-)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 6b1208af802..8017ecc4075 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 29dd0356dd5..0713b6d74b8 100644 --- a/dlls/winegstreamer/wg_media_type.c +++ b/dlls/winegstreamer/wg_media_type.c @@ -53,6 +53,362 @@ 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 %s", debugstr_guid(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_ext, UINT32 format_size) +{ + WAVEFORMATEX *format; + GstCaps *caps; + + if (!(format = strip_wave_format_extensible(format_ext))) + return NULL; + + caps = caps_from_wave_format_ex(format, format_size + sizeof(*format) - sizeof(*format_ext), + &format_ext->SubFormat, format_ext->dwChannelMask); + free(format); + return caps; +} + +static GstCaps *caps_from_wave_format(const void *format, UINT32 format_size) +{ + const WAVEFORMATEX *wave_format = format; + GUID subtype = MFAudioFormat_Base; + UINT channel_mask; + + if (wave_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) + return caps_from_wave_format_extensible(format, format_size); + + subtype.Data1 = wave_format->wFormatTag; + channel_mask = gst_audio_channel_get_fallback_mask(wave_format->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 %s", debugstr_guid(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); + GstVideoAlignment align; + GstCaps *caps; + + 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 (format->videoInfo.MinimumDisplayAperture.OffsetX.value || format->videoInfo.MinimumDisplayAperture.OffsetY.value + || format->videoInfo.MinimumDisplayAperture.Area.cx || format->videoInfo.MinimumDisplayAperture.Area.cy) + { + align.padding_left = format->videoInfo.MinimumDisplayAperture.OffsetX.value; + align.padding_top = format->videoInfo.MinimumDisplayAperture.OffsetY.value; + align.padding_right = format->videoInfo.dwWidth - format->videoInfo.MinimumDisplayAperture.Area.cx; + align.padding_bottom = format->videoInfo.dwHeight - format->videoInfo.MinimumDisplayAperture.Area.cy; + } + if (format->videoInfo.VideoFlags & MFVideoFlag_BottomUpLinearRep) + { + gsize top = align.padding_top; + align.padding_top = align.padding_bottom; + align.padding_bottom = top; + } + + 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("returning 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 b9c33e60f0b..590a023e471 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1890,7 +1890,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), @@ -2107,19 +2107,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) @@ -2272,7 +2277,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 8017ecc4075..1800683164f 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 590a023e471..40843969b7d 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -2065,15 +2065,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";
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=146134
Your paranoid android.
=== debian11b (64 bit WoW report) ===
user32: msg.c:6912: Test failed: SetFocus(hwnd) on a button: 3: the winevent_hook 0x8005 was expected, but got hook 0x0005 instead msg.c:6912: Test failed: SetFocus(hwnd) on a button: 3: the winevent_hook 0x8005 was expected, but got winevent_hook 0x0003 instead msg.c:6912: Test failed: SetFocus(hwnd) on a button: 3: the winevent_hook 0x8005 was expected, but got msg 0x030f instead msg.c:6912: Test failed: SetFocus(hwnd) on a button: 3: the winevent_hook 0x8005 was expected, but got msg 0x001c instead msg.c:6912: Test failed: SetFocus(hwnd) on a button: 3: the winevent_hook 0x8005 was expected, but got msg 0x0086 instead msg.c:6912: Test failed: SetFocus(hwnd) on a button: 3: the winevent_hook 0x8005 was expected, but got msg 0x0006 instead msg.c:6912: Test failed: SetFocus(hwnd) on a button: 3: the winevent_hook 0x8005 was expected, but got hook 0x0009 instead
Sorry, I'm still not quite convinced this is a worthwhile tradeoff.
On Tue Jun 18 04:03:03 2024 +0000, Elizabeth Figura wrote:
Sorry, I'm still not quite convinced this is a worthwhile tradeoff.
In my opion, getting rid of wg_format, using something in frontend to represent the media formats is better than what we have now. One problem I found in current wg_format is, in Proton, sometimes a format have a large codec data, it's not a good idea to let wg_format holding a large data. If we can translate from frontend to GstCaps directly, this won't be a problem.
On Tue Jun 18 04:03:03 2024 +0000, Ziqing Hui wrote:
In my opion, getting rid of wg_format, using something in frontend to represent the media formats is better than what we have now. One problem I found in current wg_format is, in Proton, sometimes a format have a large codec data, it's not a good idea to let wg_format holding a large data. If we can translate from frontend to GstCaps directly, this won't be a problem.
If we want wg_format be able to hold large codec data. One way I can come up with is allocating the data in heap, then add a refcount member to wg_format, and we have to free the data when refcount goes to 0. But this makes wg_format really complicate, I don't think this is expected.
On Tue Jun 18 04:07:27 2024 +0000, Ziqing Hui wrote:
If we want wg_format be able to hold large codec data. One way I can come up with is allocating the data in heap, then add a refcount member to wg_format, and we have to free the data when refcount goes to 0. But this makes wg_format really complicate, I don't think this is expected.
If I understand correctly, your concern is about the size of wg_format, either that it's too large or that we currently risk overflowing it if codec_data is larger than we expect?
Making wg_format dynamically sized is a good idea, I think, and I don't have any objection to that part. I'm relatively amenable to performing a two-step conversion as described in 5615, as well.
However, this part of the conversion only has the motivation of reducing LoC, and simply getting rid of wg_format does not even significantly do that.
At the same time, one thing I learned when redesigning winegstreamer is that having an internal, netural, well-designed API surface that each frontend can consume makes winegstreamer a great deal easier to work with. Getting rid of wg_format does away with that.