Adding H264 format support in wg_format.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45988 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47084 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=49715 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52183 Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winegstreamer/h264_decoder.c | 35 ++++++++++++++- dlls/winegstreamer/mfplat.c | 47 +++++++++++++++++--- dlls/winegstreamer/quartz_parser.c | 11 ++--- dlls/winegstreamer/unixlib.h | 8 ++++ dlls/winegstreamer/wg_format.c | 69 ++++++++++++++++++++++++++++-- dlls/winegstreamer/wg_transform.c | 4 ++ dlls/winegstreamer/wm_reader.c | 8 +++- 7 files changed, 165 insertions(+), 17 deletions(-)
diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index 36ca33ceca3..4edd4cdf39c 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -48,6 +48,8 @@ struct h264_decoder LONG refcount; IMFMediaType *input_type; IMFMediaType *output_type; + + struct wg_transform *wg_transform; };
static struct h264_decoder *impl_from_IMFTransform(IMFTransform *iface) @@ -55,6 +57,29 @@ static struct h264_decoder *impl_from_IMFTransform(IMFTransform *iface) return CONTAINING_RECORD(iface, struct h264_decoder, IMFTransform_iface); }
+static HRESULT try_create_wg_transform(struct h264_decoder *decoder) +{ + struct wg_format input_format; + struct wg_format output_format; + + if (decoder->wg_transform) + wg_transform_destroy(decoder->wg_transform); + decoder->wg_transform = NULL; + + mf_media_type_to_wg_format(decoder->input_type, &input_format); + if (input_format.major_type == WG_MAJOR_TYPE_UNKNOWN) + return MF_E_INVALIDMEDIATYPE; + + mf_media_type_to_wg_format(decoder->output_type, &output_format); + if (output_format.major_type == WG_MAJOR_TYPE_UNKNOWN) + return MF_E_INVALIDMEDIATYPE; + + if (!(decoder->wg_transform = wg_transform_create(&input_format, &output_format))) + return E_FAIL; + + return S_OK; +} + static HRESULT fill_output_media_type(IMFMediaType *media_type, IMFMediaType *default_type) { UINT32 value, width, height; @@ -183,6 +208,8 @@ static ULONG WINAPI transform_Release(IMFTransform *iface)
if (!refcount) { + if (decoder->wg_transform) + wg_transform_destroy(decoder->wg_transform); if (decoder->input_type) IMFMediaType_Release(decoder->input_type); if (decoder->output_type) @@ -416,7 +443,13 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF IMFMediaType_Release(decoder->output_type); IMFMediaType_AddRef((decoder->output_type = type));
- return S_OK; + if (FAILED(hr = try_create_wg_transform(decoder))) + { + IMFMediaType_Release(decoder->output_type); + decoder->output_type = NULL; + } + + return hr; }
static HRESULT WINAPI transform_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index ed460144e8b..97e27bb7301 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -659,11 +659,11 @@ IMFMediaType *mf_media_type_from_wg_format(const struct wg_format *format) { switch (format->major_type) { - case WG_MAJOR_TYPE_UNKNOWN: - return NULL; - + case WG_MAJOR_TYPE_H264: case WG_MAJOR_TYPE_WMA: - FIXME("WMA format not implemented!\n"); + FIXME("Format %u not implemented!\n", format->major_type); + /* fallthrough */ + case WG_MAJOR_TYPE_UNKNOWN: return NULL;
case WG_MAJOR_TYPE_AUDIO: @@ -822,6 +822,38 @@ static void mf_media_type_to_wg_format_wma(IMFMediaType *type, const GUID *subty memcpy(format->u.wma.codec_data, codec_data, codec_data_len); }
+static void mf_media_type_to_wg_format_h264(IMFMediaType *type, struct wg_format *format) +{ + UINT64 frame_rate, frame_size; + UINT32 profile, level; + + memset(format, 0, sizeof(*format)); + format->major_type = WG_MAJOR_TYPE_H264; + + if (SUCCEEDED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size))) + { + format->u.h264.width = frame_size >> 32; + format->u.h264.height = (UINT32)frame_size; + } + + if (SUCCEEDED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &frame_rate)) && (UINT32)frame_rate) + { + format->u.h264.fps_n = frame_rate >> 32; + format->u.h264.fps_d = (UINT32)frame_rate; + } + else + { + format->u.h264.fps_n = 1; + format->u.h264.fps_d = 1; + } + + if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_MPEG2_PROFILE, &profile))) + format->u.h264.profile = profile; + + if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_MPEG2_LEVEL, &level))) + format->u.h264.level = level; +} + void mf_media_type_to_wg_format(IMFMediaType *type, struct wg_format *format) { GUID major_type, subtype; @@ -850,7 +882,12 @@ void mf_media_type_to_wg_format(IMFMediaType *type, struct wg_format *format) mf_media_type_to_wg_format_audio(type, &subtype, format); } else if (IsEqualGUID(&major_type, &MFMediaType_Video)) - mf_media_type_to_wg_format_video(type, &subtype, format); + { + if (IsEqualGUID(&subtype, &MFVideoFormat_H264)) + mf_media_type_to_wg_format_h264(type, format); + else + mf_media_type_to_wg_format_video(type, &subtype, format); + } else FIXME("Unrecognized major type %s.\n", debugstr_guid(&major_type)); } diff --git a/dlls/winegstreamer/quartz_parser.c b/dlls/winegstreamer/quartz_parser.c index d369d4c7f20..bb7a71283c3 100644 --- a/dlls/winegstreamer/quartz_parser.c +++ b/dlls/winegstreamer/quartz_parser.c @@ -328,8 +328,9 @@ unsigned int wg_format_get_max_size(const struct wg_format *format) break; }
+ case WG_MAJOR_TYPE_H264: case WG_MAJOR_TYPE_WMA: - FIXME("WMA format not implemented!\n"); + FIXME("Format %u not implemented!\n", format->major_type); return 0;
case WG_MAJOR_TYPE_UNKNOWN: @@ -423,11 +424,11 @@ bool amt_from_wg_format(AM_MEDIA_TYPE *mt, const struct wg_format *format, bool
switch (format->major_type) { - case WG_MAJOR_TYPE_UNKNOWN: - return false; - + case WG_MAJOR_TYPE_H264: case WG_MAJOR_TYPE_WMA: - FIXME("WMA format not implemented!\n"); + FIXME("Format %u not implemented!\n", format->major_type); + /* fallthrough */ + case WG_MAJOR_TYPE_UNKNOWN: return false;
case WG_MAJOR_TYPE_AUDIO: diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index e1c0ec3f3b7..f4e2ea4966b 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -38,6 +38,7 @@ struct wg_format WG_MAJOR_TYPE_VIDEO, WG_MAJOR_TYPE_AUDIO, WG_MAJOR_TYPE_WMA, + WG_MAJOR_TYPE_H264, } major_type;
union @@ -100,6 +101,13 @@ struct wg_format uint32_t codec_data_len; unsigned char codec_data[64]; } wma; + struct + { + int32_t width, height; + uint32_t fps_n, fps_d; + uint32_t profile; + uint32_t level; + } h264; } u; };
diff --git a/dlls/winegstreamer/wg_format.c b/dlls/winegstreamer/wg_format.c index fc50fad6d12..b4455a6e6d0 100644 --- a/dlls/winegstreamer/wg_format.c +++ b/dlls/winegstreamer/wg_format.c @@ -35,6 +35,7 @@ #include <gst/audio/audio.h>
#include "winternl.h" +#include "codecapi.h" #include "dshow.h"
#include "unix_private.h" @@ -431,6 +432,64 @@ static GstCaps *wg_format_to_caps_wma(const struct wg_format *format) return caps; }
+static GstCaps *wg_format_to_caps_h264(const struct wg_format *format) +{ + const char *profile, *level; + GstCaps *caps; + + caps = gst_caps_new_empty_simple("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); + + if (format->u.h264.width) + gst_caps_set_simple(caps, "width", G_TYPE_INT, format->u.h264.width, NULL); + if (format->u.h264.height) + gst_caps_set_simple(caps, "height", G_TYPE_INT, format->u.h264.height, NULL); + if (format->u.h264.fps_n || format->u.h264.fps_d) + gst_caps_set_simple(caps, "framerate", GST_TYPE_FRACTION, format->u.h264.fps_n, format->u.h264.fps_d, NULL); + + switch (format->u.h264.profile) + { + case eAVEncH264VProfile_Main: profile = "main"; break; + case eAVEncH264VProfile_High: profile = "high"; break; + case eAVEncH264VProfile_444: profile = "high-4:4:4"; break; + default: + GST_FIXME("H264 profile attribute %u not implemented.", format->u.h264.profile); + profile = NULL; + break; + } + if (profile) + gst_caps_set_simple(caps, "profile", G_TYPE_STRING, profile, NULL); + + switch (format->u.h264.level) + { + case eAVEncH264VLevel1: level = "1"; break; + case eAVEncH264VLevel1_1: level = "1.1"; break; + case eAVEncH264VLevel1_2: level = "1.2"; break; + case eAVEncH264VLevel1_3: level = "1.3"; break; + case eAVEncH264VLevel2: level = "2"; break; + case eAVEncH264VLevel2_1: level = "2.1"; break; + case eAVEncH264VLevel2_2: level = "2.2"; break; + case eAVEncH264VLevel3: level = "3"; break; + case eAVEncH264VLevel3_1: level = "3.1"; break; + case eAVEncH264VLevel3_2: level = "3.2"; break; + case eAVEncH264VLevel4: level = "4"; break; + case eAVEncH264VLevel4_1: level = "4.1"; break; + case eAVEncH264VLevel4_2: level = "4.2"; break; + case eAVEncH264VLevel5: level = "5"; break; + case eAVEncH264VLevel5_1: level = "5.1"; break; + case eAVEncH264VLevel5_2: level = "5.2"; break; + default: + GST_FIXME("H264 level attribute %u not implemented.", format->u.h264.level); + level = NULL; + break; + } + if (level) + gst_caps_set_simple(caps, "level", G_TYPE_STRING, level, NULL); + + return caps; +} + GstCaps *wg_format_to_caps(const struct wg_format *format) { switch (format->major_type) @@ -439,6 +498,8 @@ GstCaps *wg_format_to_caps(const struct wg_format *format) return NULL; case WG_MAJOR_TYPE_WMA: return wg_format_to_caps_wma(format); + case WG_MAJOR_TYPE_H264: + return wg_format_to_caps_h264(format); case WG_MAJOR_TYPE_AUDIO: return wg_format_to_caps_audio(format); case WG_MAJOR_TYPE_VIDEO: @@ -455,11 +516,11 @@ bool wg_format_compare(const struct wg_format *a, const struct wg_format *b)
switch (a->major_type) { - case WG_MAJOR_TYPE_UNKNOWN: - return false; - case WG_MAJOR_TYPE_WMA: - GST_FIXME("WMA format not implemented!\n"); + case WG_MAJOR_TYPE_H264: + GST_FIXME("Format %u not implemented!", a->major_type); + /* fallthrough */ + case WG_MAJOR_TYPE_UNKNOWN: return false;
case WG_MAJOR_TYPE_AUDIO: diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 6e272634653..0c43a9a0c5a 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -211,6 +211,7 @@ NTSTATUS wg_transform_create(void *args)
switch (input_format.major_type) { + case WG_MAJOR_TYPE_H264: case WG_MAJOR_TYPE_WMA: if (!(element = transform_find_element(GST_ELEMENT_FACTORY_TYPE_DECODER, src_caps, raw_caps)) || !transform_append_element(transform, element, &first, &last)) @@ -251,6 +252,9 @@ NTSTATUS wg_transform_create(void *args) break;
case WG_MAJOR_TYPE_VIDEO: + break; + + case WG_MAJOR_TYPE_H264: case WG_MAJOR_TYPE_WMA: case WG_MAJOR_TYPE_UNKNOWN: GST_FIXME("Format %u not implemented!", output_format.major_type); diff --git a/dlls/winegstreamer/wm_reader.c b/dlls/winegstreamer/wm_reader.c index c911cb59d1f..57ba8633a84 100644 --- a/dlls/winegstreamer/wm_reader.c +++ b/dlls/winegstreamer/wm_reader.c @@ -1687,7 +1687,8 @@ HRESULT wm_reader_get_output_format_count(struct wm_reader *reader, DWORD output break;
case WG_MAJOR_TYPE_WMA: - FIXME("WMA format not implemented!\n"); + case WG_MAJOR_TYPE_H264: + FIXME("Format %u not implemented!\n", format.major_type); /* fallthrough */ case WG_MAJOR_TYPE_AUDIO: case WG_MAJOR_TYPE_UNKNOWN: @@ -1736,7 +1737,8 @@ HRESULT wm_reader_get_output_format(struct wm_reader *reader, DWORD output, break;
case WG_MAJOR_TYPE_WMA: - FIXME("WMA format not implemented!\n"); + case WG_MAJOR_TYPE_H264: + FIXME("Format %u not implemented!\n", format.major_type); break; case WG_MAJOR_TYPE_UNKNOWN: break; @@ -1815,6 +1817,8 @@ static const char *get_major_type_string(enum wg_major_type type) return "unknown"; case WG_MAJOR_TYPE_WMA: return "wma"; + case WG_MAJOR_TYPE_H264: + return "h264"; } assert(0); return NULL;