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 ---
v3: * Retitle PATCH 3, clear format struct, avoid unnecessary cast, use enum values.
* Add a comment to PATCH 4.
dlls/mf/tests/mf.c | 6 ------ dlls/winegstreamer/h264_decoder.c | 16 ++++++++++++++-- 2 files changed, 14 insertions(+), 8 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index e58d8278b82..6563ca81b29 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -6790,17 +6790,11 @@ static void test_h264_decoder(void) flags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_INPUT_STREAM_FIXED_SAMPLE_SIZE; memset(&input_info, 0xcd, sizeof(input_info)); hr = IMFTransform_GetInputStreamInfo(transform, 0, &input_info); - todo_wine ok(hr == S_OK, "GetInputStreamInfo returned %#lx\n", hr); - todo_wine ok(input_info.hnsMaxLatency == 0, "got hnsMaxLatency %s\n", wine_dbgstr_longlong(input_info.hnsMaxLatency)); - todo_wine ok(input_info.dwFlags == flags, "got dwFlags %#lx\n", input_info.dwFlags); - todo_wine ok(input_info.cbSize == 0x1000, "got cbSize %lu\n", input_info.cbSize); - todo_wine ok(input_info.cbMaxLookahead == 0, "got cbMaxLookahead %#lx\n", input_info.cbMaxLookahead); - todo_wine ok(input_info.cbAlignment == 0, "got cbAlignment %#lx\n", input_info.cbAlignment);
flags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE; diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index 2f4b42ed101..f24b07ef496 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -217,8 +217,20 @@ static HRESULT WINAPI transform_GetStreamIDs(IMFTransform *iface, DWORD input_si
static HRESULT WINAPI transform_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info) { - FIXME("iface %p, id %#lx, info %p stub!\n", iface, id, info); - return E_NOTIMPL; + struct h264_decoder *decoder = impl_from_IMFTransform(iface); + + TRACE("iface %p, id %#lx, info %p.\n", iface, id, info); + + if (!decoder->input_type) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + info->hnsMaxLatency = 0; + info->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_INPUT_STREAM_FIXED_SAMPLE_SIZE; + info->cbSize = 0x1000; + info->cbMaxLookahead = 0; + info->cbAlignment = 0; + + return S_OK; }
static HRESULT WINAPI transform_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info)
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/mf/tests/mf.c | 22 ++++------------------ dlls/winegstreamer/h264_decoder.c | 23 +++++++++++++++++++++-- 2 files changed, 25 insertions(+), 20 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 6563ca81b29..0df524b4e16 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -6688,13 +6688,9 @@ static void test_h264_decoder(void) flags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE; memset(&output_info, 0xcd, sizeof(output_info)); hr = IMFTransform_GetOutputStreamInfo(transform, 0, &output_info); - todo_wine ok(hr == S_OK, "GetOutputStreamInfo returned %#lx\n", hr); - todo_wine ok(output_info.dwFlags == flags, "got dwFlags %#lx\n", output_info.dwFlags); - todo_wine - ok(output_info.cbSize == 0x3fc000, "got cbSize %#lx\n", output_info.cbSize); - todo_wine + ok(output_info.cbSize == 1920 * 1088 * 2, "got cbSize %#lx\n", output_info.cbSize); ok(output_info.cbAlignment == 0, "got cbAlignment %#lx\n", output_info.cbAlignment);
i = -1; @@ -6729,14 +6725,11 @@ static void test_h264_decoder(void) flags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE; memset(&output_info, 0xcd, sizeof(output_info)); hr = IMFTransform_GetOutputStreamInfo(transform, 0, &output_info); - todo_wine ok(hr == S_OK, "GetOutputStreamInfo returned %#lx\n", hr); - todo_wine ok(output_info.dwFlags == flags, "got dwFlags %#lx\n", output_info.dwFlags); todo_wine - ok(output_info.cbSize == 0x3f4800 || broken(output_info.cbSize == 0x3fc000) /* Win7 */, + ok(output_info.cbSize == 1920 * 1080 * 2 || broken(output_info.cbSize == 1920 * 1088 * 2) /* Win7 */, "got cbSize %#lx\n", output_info.cbSize); - todo_wine ok(output_info.cbAlignment == 0, "got cbAlignment %#lx\n", output_info.cbAlignment);
/* output types can now be enumerated (though they are actually the same for all input types) */ @@ -6800,14 +6793,10 @@ static void test_h264_decoder(void) flags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE; memset(&output_info, 0xcd, sizeof(output_info)); hr = IMFTransform_GetOutputStreamInfo(transform, 0, &output_info); - todo_wine ok(hr == S_OK, "GetOutputStreamInfo returned %#lx\n", hr); - todo_wine ok(output_info.dwFlags == flags, "got dwFlags %#lx\n", output_info.dwFlags); - todo_wine - ok(output_info.cbSize == 0x3f4800 || broken(output_info.cbSize == 0x3fc000) /* Win7 */, + ok(output_info.cbSize == 1920 * 1080 * 2 || broken(output_info.cbSize == 1920 * 1088 * 2) /* Win7 */, "got cbSize %#lx\n", output_info.cbSize); - todo_wine ok(output_info.cbAlignment == 0, "got cbAlignment %#lx\n", output_info.cbAlignment);
input_count = output_count = 0xdeadbeef; @@ -6846,7 +6835,7 @@ static void test_h264_decoder(void) { status = 0; memset(&output, 0, sizeof(output)); - output.pSample = create_sample(NULL, 0x3fc000); + output.pSample = create_sample(NULL, output_info.cbSize); hr = IMFTransform_ProcessOutput(transform, 0, 1, &output, &status); if (hr != MF_E_TRANSFORM_NEED_MORE_INPUT) break; ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); @@ -6900,13 +6889,10 @@ static void test_h264_decoder(void) flags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE; memset(&output_info, 0xcd, sizeof(output_info)); hr = IMFTransform_GetOutputStreamInfo(transform, 0, &output_info); - todo_wine ok(hr == S_OK, "GetOutputStreamInfo returned %#lx\n", hr); - todo_wine ok(output_info.dwFlags == flags, "got dwFlags %#lx\n", output_info.dwFlags); todo_wine ok(output_info.cbSize == 0x3200, "got cbSize %#lx\n", output_info.cbSize); - todo_wine ok(output_info.cbAlignment == 0, "got cbAlignment %#lx\n", output_info.cbAlignment);
i = -1; diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index f24b07ef496..36ca33ceca3 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -235,8 +235,27 @@ static HRESULT WINAPI transform_GetInputStreamInfo(IMFTransform *iface, DWORD id
static HRESULT WINAPI transform_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info) { - FIXME("iface %p, id %#lx, info %p stub!\n", iface, id, info); - return E_NOTIMPL; + struct h264_decoder *decoder = impl_from_IMFTransform(iface); + UINT32 sample_size; + UINT64 frame_size; + + TRACE("iface %p, id %#lx, info %p.\n", iface, id, info); + + if (!decoder->output_type) + sample_size = 1920 * 1088 * 2; + else if (FAILED(IMFMediaType_GetUINT32(decoder->output_type, &MF_MT_SAMPLE_SIZE, &sample_size))) + { + if (FAILED(IMFMediaType_GetUINT64(decoder->output_type, &MF_MT_FRAME_SIZE, &frame_size))) + sample_size = 1920 * 1088 * 2; + else + sample_size = (frame_size >> 32) * (UINT32)frame_size * 2; + } + + info->dwFlags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE; + info->cbSize = sample_size; + info->cbAlignment = 0; + + return S_OK; }
static HRESULT WINAPI transform_GetAttributes(IMFTransform *iface, IMFAttributes **attributes)
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com
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;
Signed-off-by: Zebediah Figura zfigura@codeweavers.com
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/wg_transform.c | 38 +++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 9 deletions(-)
diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 0c43a9a0c5a..200dd946e02 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -48,7 +48,8 @@ struct wg_transform GstPad *my_src, *my_sink; GstPad *their_sink, *their_src; GstSegment segment; - GstBuffer *input; + GstBufferList *input; + guint input_max_length;
pthread_mutex_t mutex; GstBuffer *output; @@ -75,7 +76,7 @@ NTSTATUS wg_transform_destroy(void *args) struct wg_transform *transform = args;
if (transform->input) - gst_buffer_unref(transform->input); + gst_buffer_list_unref(transform->input); if (transform->output) gst_buffer_unref(transform->output);
@@ -179,6 +180,9 @@ NTSTATUS wg_transform_create(void *args) return STATUS_NO_MEMORY; if (!(transform->container = gst_bin_new("wg_transform"))) goto out; + if (!(transform->input = gst_buffer_list_new())) + goto out; + transform->input_max_length = 1;
if (!(src_caps = wg_format_to_caps(&input_format))) goto out; @@ -212,6 +216,13 @@ NTSTATUS wg_transform_create(void *args) switch (input_format.major_type) { case WG_MAJOR_TYPE_H264: + /* Call of Duty: Black Ops 3 doesn't care about the ProcessInput/ProcessOutput + * return values, it calls them in a specific order and and expects the decoder + * transform to be able to queue its input buffers. We need to use a buffer list + * to match its expectations. + */ + transform->input_max_length = 16; + /* fallthrough */ 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)) @@ -316,6 +327,8 @@ out: gst_object_unref(transform->my_src); if (src_caps) gst_caps_unref(src_caps); + if (transform->input) + gst_buffer_list_unref(transform->input); if (transform->container) { gst_element_set_state(transform->container, GST_STATE_NULL); @@ -332,10 +345,12 @@ NTSTATUS wg_transform_push_data(void *args) struct wg_transform *transform = params->transform; struct wg_sample *sample = params->sample; GstBuffer *buffer; + guint length;
- if (transform->input) + length = gst_buffer_list_length(transform->input); + if (length >= transform->input_max_length) { - GST_INFO("Refusing %u bytes, a buffer is already queued", sample->size); + GST_INFO("Refusing %u bytes, %u buffers already queued", sample->size, length); params->result = MF_E_NOTACCEPTING; return STATUS_SUCCESS; } @@ -346,9 +361,9 @@ NTSTATUS wg_transform_push_data(void *args) return STATUS_NO_MEMORY; } gst_buffer_fill(buffer, 0, sample->data, sample->size); - transform->input = buffer; + gst_buffer_list_insert(transform->input, -1, buffer);
- GST_INFO("Copied %u bytes from sample %p to input buffer", sample->size, sample); + GST_INFO("Copied %u bytes from sample %p to input buffer list", sample->size, sample); params->result = S_OK; return STATUS_SUCCESS; } @@ -392,17 +407,22 @@ NTSTATUS wg_transform_read_data(void *args) struct wg_transform_read_data_params *params = args; struct wg_transform *transform = params->transform; struct wg_sample *sample = params->sample; + GstBufferList *input = transform->input; GstFlowReturn ret; NTSTATUS status;
- if (!transform->input) + if (!gst_buffer_list_length(transform->input)) GST_DEBUG("Not input buffer queued"); - else if ((ret = gst_pad_push(transform->my_src, transform->input))) + else if (!(transform->input = gst_buffer_list_new())) + { + GST_ERROR("Failed to allocate new input queue"); + return STATUS_NO_MEMORY; + } + else if ((ret = gst_pad_push_list(transform->my_src, input))) { GST_ERROR("Failed to push transform input, error %d", ret); return STATUS_UNSUCCESSFUL; } - transform->input = NULL;
sample->size = 0; pthread_mutex_lock(&transform->mutex);
Signed-off-by: Zebediah Figura zfigura@codeweavers.com
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/mf/tests/mf.c | 3 -- dlls/winegstreamer/h264_decoder.c | 54 ++++++++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 7 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 0df524b4e16..0c1065572ca 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -6821,7 +6821,6 @@ static void test_h264_decoder(void) status = 0; memset(&output, 0, sizeof(output)); hr = IMFTransform_ProcessOutput(transform, 0, 1, &output, &status); - todo_wine ok(hr == E_INVALIDARG || hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); ok(output.dwStreamID == 0, "got dwStreamID %lu\n", output.dwStreamID); ok(!output.pSample, "got pSample %p\n", output.pSample); @@ -6849,14 +6848,12 @@ static void test_h264_decoder(void) ok(ret == 0, "Release returned %lu\n", ret);
hr = IMFTransform_ProcessInput(transform, 0, sample, 0); - todo_wine ok(hr == S_OK, "ProcessInput returned %#lx\n", hr); ret = IMFSample_Release(sample); ok(ret <= 1, "Release returned %lu\n", ret); sample = next_h264_sample(&h264_encoded_data, &h264_encoded_data_len);
hr = IMFTransform_ProcessInput(transform, 0, sample, 0); - todo_wine ok(hr == S_OK, "ProcessInput returned %#lx\n", hr); ret = IMFSample_Release(sample); ok(ret <= 1, "Release returned %lu\n", ret); diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index 4edd4cdf39c..950d9ebf335 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -496,15 +496,61 @@ static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_
static HRESULT WINAPI transform_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags) { - FIXME("iface %p, id %#lx, sample %p, flags %#lx stub!\n", iface, id, sample, flags); - return E_NOTIMPL; + struct h264_decoder *decoder = impl_from_IMFTransform(iface); + struct wg_sample *wg_sample; + MFT_INPUT_STREAM_INFO info; + HRESULT hr; + + TRACE("iface %p, id %#lx, sample %p, flags %#lx.\n", iface, id, sample, flags); + + if (FAILED(hr = IMFTransform_GetInputStreamInfo(iface, 0, &info))) + return hr; + + if (!decoder->wg_transform) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (FAILED(hr = mf_create_wg_sample(sample, &wg_sample))) + return hr; + + hr = wg_transform_push_data(decoder->wg_transform, wg_sample); + + mf_destroy_wg_sample(wg_sample); + return hr; }
static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) { - FIXME("iface %p, flags %#lx, count %lu, samples %p, status %p stub!\n", iface, flags, count, samples, status); - return E_NOTIMPL; + struct h264_decoder *decoder = impl_from_IMFTransform(iface); + MFT_OUTPUT_STREAM_INFO info; + struct wg_sample *wg_sample; + HRESULT hr; + + TRACE("iface %p, flags %#lx, count %lu, samples %p, status %p.\n", iface, flags, count, samples, status); + + if (count != 1) + return E_INVALIDARG; + + if (FAILED(hr = IMFTransform_GetOutputStreamInfo(iface, 0, &info))) + return hr; + + if (!decoder->wg_transform) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + *status = 0; + samples[0].dwStatus = 0; + if (!samples[0].pSample) return E_INVALIDARG; + + if (FAILED(hr = mf_create_wg_sample(samples[0].pSample, &wg_sample))) + return hr; + + if (wg_sample->max_size < info.cbSize) + hr = MF_E_BUFFERTOOSMALL; + else + hr = wg_transform_read_data(decoder->wg_transform, wg_sample); + + mf_destroy_wg_sample(wg_sample); + return hr; }
static const IMFTransformVtbl transform_vtbl =
Signed-off-by: Zebediah Figura zfigura@codeweavers.com
On 3/21/22 23:11, Rémi Bernon wrote:
static HRESULT WINAPI transform_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info) {
- FIXME("iface %p, id %#lx, info %p stub!\n", iface, id, info);
- return E_NOTIMPL;
struct h264_decoder *decoder = impl_from_IMFTransform(iface);
TRACE("iface %p, id %#lx, info %p.\n", iface, id, info);
if (!decoder->input_type)
return MF_E_TRANSFORM_TYPE_NOT_SET;
info->hnsMaxLatency = 0;
info->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_INPUT_STREAM_FIXED_SAMPLE_SIZE;
info->cbSize = 0x1000;
info->cbMaxLookahead = 0;
info->cbAlignment = 0;
return S_OK; }
static HRESULT WINAPI transform_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info)
Is it really using static size here, regardless of input stream parameters? Like frame size for example? Not sure how useful this field is in a first place for decoder input, maybe they set it to be non-zero.
On 3/23/22 06:10, Nikolay Sivov wrote:
On 3/21/22 23:11, Rémi Bernon wrote:
static HRESULT WINAPI transform_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info) { - FIXME("iface %p, id %#lx, info %p stub!\n", iface, id, info); - return E_NOTIMPL; + struct h264_decoder *decoder = impl_from_IMFTransform(iface);
+ TRACE("iface %p, id %#lx, info %p.\n", iface, id, info);
+ if (!decoder->input_type) + return MF_E_TRANSFORM_TYPE_NOT_SET;
+ info->hnsMaxLatency = 0; + info->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_INPUT_STREAM_FIXED_SAMPLE_SIZE; + info->cbSize = 0x1000; + info->cbMaxLookahead = 0; + info->cbAlignment = 0;
+ return S_OK; } static HRESULT WINAPI transform_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info)
Is it really using static size here, regardless of input stream parameters? Like frame size for example? Not sure how useful this field is in a first place for decoder input, maybe they set it to be non-zero.
Yeah I don't think it's really useful, and I'm not even sure it's actually required. Input stream parameters is really just the codec subtype, I believe H264 streams have their metadata embedded in the first packets.
On 3/23/22 16:46, Rémi Bernon wrote:
On 3/23/22 06:10, Nikolay Sivov wrote:
On 3/21/22 23:11, Rémi Bernon wrote:
static HRESULT WINAPI transform_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info) { - FIXME("iface %p, id %#lx, info %p stub!\n", iface, id, info); - return E_NOTIMPL; + struct h264_decoder *decoder = impl_from_IMFTransform(iface);
+ TRACE("iface %p, id %#lx, info %p.\n", iface, id, info);
+ if (!decoder->input_type) + return MF_E_TRANSFORM_TYPE_NOT_SET;
+ info->hnsMaxLatency = 0; + info->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_INPUT_STREAM_FIXED_SAMPLE_SIZE; + info->cbSize = 0x1000; + info->cbMaxLookahead = 0; + info->cbAlignment = 0;
+ return S_OK; } static HRESULT WINAPI transform_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info)
Is it really using static size here, regardless of input stream parameters? Like frame size for example? Not sure how useful this field is in a first place for decoder input, maybe they set it to be non-zero.
Yeah I don't think it's really useful, and I'm not even sure it's actually required. Input stream parameters is really just the codec subtype, I believe H264 streams have their metadata embedded in the first packets.
FWIW choosing one input media type or the other doesn't change this value, neither does it change after the stream change event has been received. So it really looks like to be a static value.
It's maybe not very useful but I don't think it's wrong.