From a1eb142293ee22b943e13ee134b1c5e510e5a6ed Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Mon, 16 Oct 2023 16:54:57 -0300 Subject: [PATCH 001/413] mlang/tests: Test for GetGlobalFontLinkObject. (cherry picked from commit 6360992deaf60bd486c1e1f6f11492a6b4ec9aaa) --- dlls/mlang/tests/mlang.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/dlls/mlang/tests/mlang.c b/dlls/mlang/tests/mlang.c index ee5ef5af738..99664a0e556 100644 --- a/dlls/mlang/tests/mlang.c +++ b/dlls/mlang/tests/mlang.c @@ -2295,6 +2295,11 @@ static void test_GetGlobalFontLinkObject(void) { HRESULT ret; void *unknown; + LONG refcount; + IMLangFontLink2 *IMLFL2; + IMLangFontLink *IMLFL; + IMLangCodePages *IMLCP; + IMultiLanguage *IML; ret = GetGlobalFontLinkObject(NULL); ok(ret == E_INVALIDARG, "expected E_INVALIDARG got %#lx\n", ret); @@ -2305,7 +2310,26 @@ todo_wine { ok(ret == S_OK, "expected S_OK got %#lx\n", ret); ok(unknown != NULL && unknown != (void *)0xdeadbeef, "GetGlobalFontLinkObject() returned %p\n", unknown); + if (unknown == (void *)0xdeadbeef || !unknown) return; } + + ret = IUnknown_QueryInterface((IUnknown*)unknown, &IID_IMLangFontLink2, (void**)&IMLFL2); + ok(ret == E_NOINTERFACE, "expected E_NOINTERFACE got %#lx\n", ret); + + ret = IUnknown_QueryInterface((IUnknown*)unknown, &IID_IMultiLanguage, (void**)&IML); + ok(ret == E_NOINTERFACE, "expected E_NOINTERFACE got %#lx\n", ret); + + ret = IUnknown_QueryInterface((IUnknown*)unknown, &IID_IMLangFontLink, (void**)&IMLFL); + ok(ret == S_OK, "expected S_OK got %#lx\n", ret); + IMLangFontLink_Release(IMLFL); + + ret = IUnknown_QueryInterface((IUnknown*)unknown, &IID_IMLangCodePages, (void**)&IMLCP); + ok(ret == S_OK, "expected S_OK got %#lx\n", ret); + IMLangCodePages_Release(IMLCP); + + + refcount = IUnknown_Release((IUnknown*)unknown); + ok(refcount == 1, "Got refcount %ld\n", refcount); } static void test_IMLangConvertCharset(IMultiLanguage *ml) @@ -2778,6 +2802,8 @@ START_TEST(mlang) if (!init_function_ptrs()) return; + test_GetGlobalFontLinkObject(); + CoInitialize(NULL); test_Rfc1766ToLcid(); test_LcidToRfc1766(); @@ -2785,8 +2811,6 @@ START_TEST(mlang) test_ConvertINetUnicodeToMultiByte(); test_JapaneseConversion(); - test_GetGlobalFontLinkObject(); - trace("IMultiLanguage\n"); ret = CoCreateInstance(&CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER, &IID_IMultiLanguage, (void **)&iML); From 6f381b87b4a12661f9f9d75b58755e9230150e41 Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Mon, 16 Oct 2023 17:12:56 -0300 Subject: [PATCH 002/413] mlang: Implement GetGlobalFontLinkObject. (cherry picked from commit 829307455833e5182b93be7dd9b26529f1f136d2) --- dlls/mlang/mlang.c | 21 +++++++++++++++++++-- dlls/mlang/tests/mlang.c | 8 ++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/dlls/mlang/mlang.c b/dlls/mlang/mlang.c index f424c87b860..55e6f6edaa5 100644 --- a/dlls/mlang/mlang.c +++ b/dlls/mlang/mlang.c @@ -44,6 +44,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(mlang); #include "initguid.h" +static INIT_ONCE font_link_global_init_once = INIT_ONCE_STATIC_INIT; +static IUnknown *font_link_global = NULL; + static HRESULT MultiLanguage_create(IUnknown *pUnkOuter, LPVOID *ppObj); static HRESULT MLangConvertCharset_create(IUnknown *outer, void **obj); static HRESULT EnumRfc1766_create(LANGID LangId, IEnumRfc1766 **ppEnum); @@ -3882,11 +3885,25 @@ HRESULT WINAPI DllCanUnloadNow(void) return dll_count == 0 ? S_OK : S_FALSE; } +static BOOL WINAPI allocate_font_link_cb(PINIT_ONCE init_once, PVOID args, PVOID *context) +{ + return SUCCEEDED(MultiLanguage_create(NULL, (void**)&font_link_global)); +} + HRESULT WINAPI GetGlobalFontLinkObject(void **unknown) { + TRACE("%p\n", unknown); + if (!unknown) return E_INVALIDARG; - FIXME("%p: stub\n", unknown); + if (!InitOnceExecuteOnce(&font_link_global_init_once, allocate_font_link_cb, NULL, NULL)) + { + ERR("Failed to create global font link object.\n"); + return E_FAIL; + } - return S_FALSE; + IUnknown_AddRef(font_link_global); + *unknown = font_link_global; + + return S_OK; } diff --git a/dlls/mlang/tests/mlang.c b/dlls/mlang/tests/mlang.c index 99664a0e556..4ab4b579245 100644 --- a/dlls/mlang/tests/mlang.c +++ b/dlls/mlang/tests/mlang.c @@ -2306,18 +2306,18 @@ static void test_GetGlobalFontLinkObject(void) unknown = (void *)0xdeadbeef; ret = GetGlobalFontLinkObject(&unknown); -todo_wine { ok(ret == S_OK, "expected S_OK got %#lx\n", ret); ok(unknown != NULL && unknown != (void *)0xdeadbeef, "GetGlobalFontLinkObject() returned %p\n", unknown); if (unknown == (void *)0xdeadbeef || !unknown) return; - } ret = IUnknown_QueryInterface((IUnknown*)unknown, &IID_IMLangFontLink2, (void**)&IMLFL2); - ok(ret == E_NOINTERFACE, "expected E_NOINTERFACE got %#lx\n", ret); + todo_wine ok(ret == E_NOINTERFACE, "expected E_NOINTERFACE got %#lx\n", ret); + if (ret == S_OK) IMLangFontLink2_Release(IMLFL2); ret = IUnknown_QueryInterface((IUnknown*)unknown, &IID_IMultiLanguage, (void**)&IML); - ok(ret == E_NOINTERFACE, "expected E_NOINTERFACE got %#lx\n", ret); + todo_wine ok(ret == E_NOINTERFACE, "expected E_NOINTERFACE got %#lx\n", ret); + if (ret == S_OK) IMultiLanguage_Release(IML); ret = IUnknown_QueryInterface((IUnknown*)unknown, &IID_IMLangFontLink, (void**)&IMLFL); ok(ret == S_OK, "expected S_OK got %#lx\n", ret); From 38815d44953ccb68e3e1aa356e64119f3e3e917e Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Tue, 28 Nov 2023 16:51:18 -0300 Subject: [PATCH 003/413] mlang/tests: Test codepages priority bug in GetStrCodepages. (cherry picked from commit bc6231481119a244e7d3de29089878b8fbcecbf9) --- dlls/mlang/tests/mlang.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dlls/mlang/tests/mlang.c b/dlls/mlang/tests/mlang.c index 4ab4b579245..f8e37565115 100644 --- a/dlls/mlang/tests/mlang.c +++ b/dlls/mlang/tests/mlang.c @@ -1324,6 +1324,14 @@ static void IMLangFontLink_Test(IMLangFontLink* iMLFL) ok(dwCodePages == dwCmpCodePages, "expected %lx, got %lx\n", dwCmpCodePages, dwCodePages); ok(processed == 2, "expected 2, got %ld\n", processed); + dwCmpCodePages = FS_JISJAPAN; + dwCodePages = 0; + processed = 0; + ret = IMLangFontLink_GetStrCodePages(iMLFL, L"\uff90a", 2, FS_LATIN1, &dwCodePages, &processed); + ok(ret == S_OK, "IMLangFontLink_GetStrCodePages error %lx\n", ret); + ok(dwCodePages == dwCmpCodePages, "expected %lx, got %lx\n", dwCmpCodePages, dwCodePages); + todo_wine ok(processed == 1, "expected 1, got %ld\n", processed); + dwCodePages = 0xffff; processed = -1; ret = IMLangFontLink_GetStrCodePages(iMLFL, &str[2], 1, 0, &dwCodePages, &processed); From 0e4dac179b4cd619454ecee616434d04038613d9 Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Tue, 28 Nov 2023 17:29:21 -0300 Subject: [PATCH 004/413] mlang: Fix bug with codepage priority in GetStrCodePages. (cherry picked from commit b549ae11fbbe02564417e4029baa789bffe9d5aa) --- dlls/mlang/mlang.c | 9 +++------ dlls/mlang/tests/mlang.c | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/dlls/mlang/mlang.c b/dlls/mlang/mlang.c index 55e6f6edaa5..c7e7f360f0b 100644 --- a/dlls/mlang/mlang.c +++ b/dlls/mlang/mlang.c @@ -3336,23 +3336,20 @@ static HRESULT WINAPI fnIMLangFontLink2_GetStrCodePages( IMLangFontLink2* iface, for (i = 0; i < src_len; i++) { - DWORD cp, next_cp = 0; + DWORD cp = 0; HRESULT ret; ret = IMLangFontLink2_GetCharCodePages(iface, src[i], &cp); - if (i + 1 < src_len) - ret = IMLangFontLink2_GetCharCodePages(iface, src[i + 1], &next_cp); if (ret != S_OK) return E_FAIL; if (!cps) cps = cp; - else if ((cps & cp) != 0) cps &= cp; + else if ((cps & cp) != 0 && + !((priority_cp & cps) ^ (priority_cp & cp))) cps &= cp; else { i--; break; } - - if ((priority_cp & cps) && !(priority_cp & next_cp)) break; } if (codepages) *codepages = cps; diff --git a/dlls/mlang/tests/mlang.c b/dlls/mlang/tests/mlang.c index f8e37565115..6bb973dc285 100644 --- a/dlls/mlang/tests/mlang.c +++ b/dlls/mlang/tests/mlang.c @@ -1330,7 +1330,7 @@ static void IMLangFontLink_Test(IMLangFontLink* iMLFL) ret = IMLangFontLink_GetStrCodePages(iMLFL, L"\uff90a", 2, FS_LATIN1, &dwCodePages, &processed); ok(ret == S_OK, "IMLangFontLink_GetStrCodePages error %lx\n", ret); ok(dwCodePages == dwCmpCodePages, "expected %lx, got %lx\n", dwCmpCodePages, dwCodePages); - todo_wine ok(processed == 1, "expected 1, got %ld\n", processed); + ok(processed == 1, "expected 1, got %ld\n", processed); dwCodePages = 0xffff; processed = -1; From c7425a1862b2985d1f42362a0cceb14837af08e2 Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Tue, 30 Jan 2024 20:43:22 -0300 Subject: [PATCH 005/413] gdiplus: Replace HDC with GpGraphics in parameters. (cherry picked from commit 1454ffe7ddf01226aacec07836d4afa62fecd3fa) --- dlls/gdiplus/gdiplus_private.h | 4 ++-- dlls/gdiplus/graphics.c | 36 ++++++++++++++++------------------ dlls/gdiplus/graphicspath.c | 8 ++++---- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index 6f7e72124c2..a72731d79ac 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -606,13 +606,13 @@ static inline const void *buffer_read(struct memory_buffer *mbuf, INT size) return NULL; } -typedef GpStatus (*gdip_format_string_callback)(HDC hdc, +typedef GpStatus (*gdip_format_string_callback)(GpGraphics *graphics, GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, INT underlined_index_count, void *user_data); -GpStatus gdip_format_string(HDC hdc, +GpStatus gdip_format_string(GpGraphics *graphics, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, int ignore_empty_clip, gdip_format_string_callback callback, void *user_data); diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index 36ff473a6af..8480c45930b 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -5161,7 +5161,7 @@ GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT w return GdipIsVisibleRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, result); } -GpStatus gdip_format_string(HDC hdc, +GpStatus gdip_format_string(GpGraphics *graphics, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, int ignore_empty_clip, gdip_format_string_callback callback, void *user_data) @@ -5243,7 +5243,7 @@ GpStatus gdip_format_string(HDC hdc, halign = format->align; while(sum < length){ - GetTextExtentExPointW(hdc, stringdup + sum, length - sum, + GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum, nwidth, &fit, NULL, &size); fitcpy = fit; @@ -5292,7 +5292,7 @@ GpStatus gdip_format_string(HDC hdc, else lineend = fit; - GetTextExtentExPointW(hdc, stringdup + sum, lineend, + GetTextExtentExPointW(graphics->hdc, stringdup + sum, lineend, nwidth, &j, NULL, &size); bounds.Width = size.cx; @@ -5326,7 +5326,7 @@ GpStatus gdip_format_string(HDC hdc, if (hotkeyprefix_offsets[hotkeyprefix_end_pos] >= sum + lineend) break; - stat = callback(hdc, stringdup, sum, lineend, + stat = callback(graphics, stringdup, sum, lineend, font, rect, format, lineno, &bounds, &hotkeyprefix_offsets[hotkeyprefix_pos], hotkeyprefix_end_pos-hotkeyprefix_pos, user_data); @@ -5392,7 +5392,7 @@ struct measure_ranges_args { REAL rel_width, rel_height; }; -static GpStatus measure_ranges_callback(HDC hdc, +static GpStatus measure_ranges_callback(GpGraphics *graphics, GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, @@ -5414,11 +5414,11 @@ static GpStatus measure_ranges_callback(HDC hdc, range_rect.Y = bounds->Y / args->rel_height; range_rect.Height = bounds->Height / args->rel_height; - GetTextExtentExPointW(hdc, string + index, range_start - index, + GetTextExtentExPointW(graphics->hdc, string + index, range_start - index, INT_MAX, NULL, NULL, &range_size); range_rect.X = (bounds->X + range_size.cx) / args->rel_width; - GetTextExtentExPointW(hdc, string + index, range_end - index, + GetTextExtentExPointW(graphics->hdc, string + index, range_end - index, INT_MAX, NULL, NULL, &range_size); range_rect.Width = (bounds->X + range_size.cx) / args->rel_width - range_rect.X; @@ -5496,7 +5496,7 @@ GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics, gdi_transform_acquire(graphics); - stat = gdip_format_string(hdc, string, length, font, &scaled_rect, stringFormat, + stat = gdip_format_string(graphics, string, length, font, &scaled_rect, stringFormat, (stringFormat->attr & StringFormatFlagsNoClip) != 0, measure_ranges_callback, &args); gdi_transform_release(graphics); @@ -5517,7 +5517,7 @@ struct measure_string_args { REAL rel_width, rel_height; }; -static GpStatus measure_string_callback(HDC hdc, +static GpStatus measure_string_callback(GpGraphics *graphics, GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, @@ -5619,7 +5619,7 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics, gdi_transform_acquire(graphics); - gdip_format_string(hdc, string, length, font, &scaled_rect, format, TRUE, + gdip_format_string(graphics, string, length, font, &scaled_rect, format, TRUE, measure_string_callback, &args); gdi_transform_release(graphics); @@ -5640,12 +5640,11 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics, } struct draw_string_args { - GpGraphics *graphics; GDIPCONST GpBrush *brush; REAL x, y, rel_width, rel_height, ascent; }; -static GpStatus draw_string_callback(HDC hdc, +static GpStatus draw_string_callback(GpGraphics *graphics, GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, @@ -5658,7 +5657,7 @@ static GpStatus draw_string_callback(HDC hdc, position.X = args->x + bounds->X / args->rel_width; position.Y = args->y + bounds->Y / args->rel_height + args->ascent; - stat = draw_driver_string(args->graphics, &string[index], length, font, format, + stat = draw_driver_string(graphics, &string[index], length, font, format, args->brush, &position, DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL); @@ -5668,7 +5667,7 @@ static GpStatus draw_string_callback(HDC hdc, REAL underline_y, underline_height; int i; - GetOutlineTextMetricsW(hdc, sizeof(otm), &otm); + GetOutlineTextMetricsW(graphics->hdc, sizeof(otm), &otm); underline_height = otm.otmsUnderscoreSize / args->rel_height; underline_y = position.Y - otm.otmsUnderscorePosition / args->rel_height - underline_height / 2; @@ -5679,13 +5678,13 @@ static GpStatus draw_string_callback(HDC hdc, SIZE text_size; INT ofs = underlined_indexes[i] - index; - GetTextExtentExPointW(hdc, string + index, ofs, INT_MAX, NULL, NULL, &text_size); + GetTextExtentExPointW(graphics->hdc, string + index, ofs, INT_MAX, NULL, NULL, &text_size); start_x = text_size.cx / args->rel_width; - GetTextExtentExPointW(hdc, string + index, ofs+1, INT_MAX, NULL, NULL, &text_size); + GetTextExtentExPointW(graphics->hdc, string + index, ofs+1, INT_MAX, NULL, NULL, &text_size); end_x = text_size.cx / args->rel_width; - GdipFillRectangle(args->graphics, (GpBrush*)args->brush, position.X+start_x, underline_y, end_x-start_x, underline_height); + GdipFillRectangle(graphics, (GpBrush*)args->brush, position.X+start_x, underline_y, end_x-start_x, underline_height); } } @@ -5783,7 +5782,6 @@ GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string get_font_hfont(graphics, font, format, &gdifont, NULL, NULL); SelectObject(hdc, gdifont); - args.graphics = graphics; args.brush = brush; args.x = rect->X; @@ -5797,7 +5795,7 @@ GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string GetTextMetricsW(hdc, &textmetric); args.ascent = textmetric.tmAscent / rel_height; - gdip_format_string(hdc, string, length, font, &scaled_rect, format, TRUE, + gdip_format_string(graphics, string, length, font, &scaled_rect, format, TRUE, draw_string_callback, &args); gdi_transform_release(graphics); diff --git a/dlls/gdiplus/graphicspath.c b/dlls/gdiplus/graphicspath.c index 24c2888cfe8..391ba72de98 100644 --- a/dlls/gdiplus/graphicspath.c +++ b/dlls/gdiplus/graphicspath.c @@ -949,7 +949,7 @@ struct format_string_args float ascent; }; -static GpStatus format_string_callback(HDC dc, +static GpStatus format_string_callback(GpGraphics *graphics, GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, @@ -975,7 +975,7 @@ static GpStatus format_string_callback(HDC dc, TTPOLYGONHEADER *ph = NULL, *origph; char *start; DWORD len, ofs = 0; - len = GetGlyphOutlineW(dc, string[i], GGO_BEZIER, &gm, 0, NULL, &identity); + len = GetGlyphOutlineW(graphics->hdc, string[i], GGO_BEZIER, &gm, 0, NULL, &identity); if (len == GDI_ERROR) { status = GenericError; @@ -989,7 +989,7 @@ static GpStatus format_string_callback(HDC dc, status = OutOfMemory; break; } - GetGlyphOutlineW(dc, string[i], GGO_BEZIER, &gm, len, start, &identity); + GetGlyphOutlineW(graphics->hdc, string[i], GGO_BEZIER, &gm, len, start, &identity); ofs = 0; while (ofs < len) @@ -1117,7 +1117,7 @@ GpStatus WINGDIPAPI GdipAddPathString(GpPath* path, GDIPCONST WCHAR* string, INT args.maxY = 0; args.scale = emSize / native_height; args.ascent = textmetric.tmAscent * args.scale; - status = gdip_format_string(dc, string, length, NULL, &scaled_layout_rect, + status = gdip_format_string(graphics, string, length, NULL, &scaled_layout_rect, format, TRUE, format_string_callback, &args); DeleteDC(dc); From bd459e840c0cbf4135f4667033a71544a43ee4de Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Mon, 11 Dec 2023 10:03:32 -0300 Subject: [PATCH 006/413] gdiplus/tests: Add interactive test for font linking. (cherry picked from commit d68a9d1213c7f80bf1a24f1a4db64cd3d34858b1) --- dlls/gdiplus/tests/graphics.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/dlls/gdiplus/tests/graphics.c b/dlls/gdiplus/tests/graphics.c index d71bc78c2ae..18fe1078e76 100644 --- a/dlls/gdiplus/tests/graphics.c +++ b/dlls/gdiplus/tests/graphics.c @@ -2305,10 +2305,29 @@ static void test_GdipDrawString(void) } expect(Ok, status); - status = GdipCreateSolidFill((ARGB)0xdeadbeef, (GpSolidFill**)&brush); + status = GdipCreateStringFormat(0,0,&format); expect(Ok, status); - status = GdipCreateStringFormat(0,0,&format); + if (winetest_interactive) + { + status = GdipCreateSolidFill(0xFF000000, (GpSolidFill**)&brush); + expect(Ok, status); + rect.X = 0; + rect.Y = 0; + rect.Width = 0; + rect.Height = 14; + GdipRotateWorldTransform(graphics, 45, MatrixOrderPrepend); + GdipScaleWorldTransform(graphics, 2, 2, MatrixOrderPrepend); + GdipGraphicsClear(graphics, 0xFFFFFFFF); + status = GdipDrawString(graphics, L"\u8336Hola\u8336", 6, fnt, &rect, format, brush); + expect(Ok, status); + RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); /* FIXME: In Windows this test works without this line. */ + Sleep(4000); + GdipDeleteBrush(brush); + GdipResetWorldTransform(graphics); + } + + status = GdipCreateSolidFill((ARGB)0xdeadbeef, (GpSolidFill**)&brush); expect(Ok, status); rect.X = 0; From d8260d903f886a934abf41749cfeb702c9139e1e Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Tue, 14 Nov 2023 19:22:25 -0300 Subject: [PATCH 007/413] gdiplus: Implement font linking for gdiplus. (cherry picked from commit ba4681e776c0c1cef9b5b2610dc67f634999d095) --- dlls/gdiplus/Makefile.in | 2 +- dlls/gdiplus/gdiplus_private.h | 16 +++- dlls/gdiplus/graphics.c | 159 +++++++++++++++++++++++++++++---- dlls/gdiplus/graphicspath.c | 2 +- 4 files changed, 158 insertions(+), 21 deletions(-) diff --git a/dlls/gdiplus/Makefile.in b/dlls/gdiplus/Makefile.in index 382033be901..76474f2e086 100644 --- a/dlls/gdiplus/Makefile.in +++ b/dlls/gdiplus/Makefile.in @@ -1,6 +1,6 @@ MODULE = gdiplus.dll IMPORTLIB = gdiplus -IMPORTS = uuid shlwapi ole32 oleaut32 user32 gdi32 +IMPORTS = uuid shlwapi ole32 oleaut32 user32 gdi32 mlang DELAYIMPORTS = windowscodecs SOURCES = \ diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index a72731d79ac..2b5dbee43e9 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -606,8 +606,22 @@ static inline const void *buffer_read(struct memory_buffer *mbuf, INT size) return NULL; } +/* Represents a string section and the font it should use. */ +struct gdip_font_link_section { + struct list entry; + DWORD start; /* The starting index of the string where the font applies. */ + DWORD end; /* The end index of the string. */ + GpFont *font; +}; + +struct gdip_font_link_info { + GDIPCONST GpFont *base_font; + struct list sections; +}; + + typedef GpStatus (*gdip_format_string_callback)(GpGraphics *graphics, - GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, + GDIPCONST WCHAR *string, INT index, INT length, struct gdip_font_link_info *sections, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, INT underlined_index_count, void *user_data); diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index 8480c45930b..707fa55cfda 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -34,6 +34,7 @@ #include "winreg.h" #include "shlwapi.h" +#include "mlang.h" #include "gdiplus.h" #include "gdiplus_private.h" #include "wine/debug.h" @@ -5161,6 +5162,110 @@ GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT w return GdipIsVisibleRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, result); } +/* Populates gdip_font_link_info struct based on the base_font and input string */ +static void generate_font_link_info(GpGraphics *graphics, WCHAR *string, DWORD length, GDIPCONST GpFont *base_font, + struct gdip_font_link_info *font_link_info) +{ + IUnknown *unk; + IMLangFontLink *iMLFL; + GpFont *gpfont; + HFONT map_hfont, hfont, old_font; + LONG processed, progress = 0; + struct gdip_font_link_section *section; + DWORD font_codepages, string_codepages; + + list_init(&font_link_info->sections); + font_link_info->base_font = base_font; + + GetGlobalFontLinkObject((void**)&unk); + IUnknown_QueryInterface(unk, &IID_IMLangFontLink, (void**)&iMLFL); + IUnknown_Release(unk); + + get_font_hfont(graphics, base_font, NULL, &hfont, NULL, NULL); + IMLangFontLink_GetFontCodePages(iMLFL, graphics->hdc, hfont, &font_codepages); + + while (progress < length) + { + section = calloc(1, sizeof(*section)); + section->start = progress; + IMLangFontLink_GetStrCodePages(iMLFL, &string[progress], length - progress, + font_codepages, &string_codepages, &processed); + + if (font_codepages & string_codepages) + { + section->font = (GpFont *)base_font; + } + else + { + IMLangFontLink_MapFont(iMLFL, graphics->hdc, string_codepages, hfont, &map_hfont); + old_font = SelectObject(graphics->hdc, map_hfont); + GdipCreateFontFromDC(graphics->hdc, &gpfont); + SelectObject(graphics->hdc, old_font); + IMLangFontLink_ReleaseFont(iMLFL, map_hfont); + section->font = gpfont; + } + + section->end = section->start + processed; + list_add_tail(&font_link_info->sections, §ion->entry); + progress += processed; + } + + DeleteObject(hfont); + IMLangFontLink_Release(iMLFL); +} + +static void font_link_get_text_extent_point(struct gdip_font_link_info *font_link_info, GpGraphics *graphics, LPCWSTR string, + INT index, int length, int max_ext, LPINT fit, SIZE *size) +{ + DWORD to_measure_length; + HFONT hfont, oldhfont; + SIZE sizeaux = { 0 }; + int i = index, fitaux = 0; + struct gdip_font_link_section *section; + + size->cx = 0; + size->cy = 0; + + if (fit) + *fit = 0; + + LIST_FOR_EACH_ENTRY(section, &font_link_info->sections, struct gdip_font_link_section, entry) + { + if (i >= section->end) continue; + + to_measure_length = min(length - (i - index), section->end - i); + + get_font_hfont(graphics, section->font, NULL, &hfont, NULL, NULL); + oldhfont = SelectObject(graphics->hdc, hfont); + GetTextExtentExPointW(graphics->hdc, &string[i], to_measure_length, max_ext, &fitaux, NULL, &sizeaux); + SelectObject(graphics->hdc, oldhfont); + DeleteObject(hfont); + + max_ext -= sizeaux.cx; + if (fit) + *fit += fitaux; + size->cx += sizeaux.cx; + size->cy = max(size->cy, sizeaux.cy); + + i += to_measure_length; + if ((i - index) >= length || fitaux < to_measure_length) break; + } +} + +static void release_font_link_info(struct gdip_font_link_info *font_link_info) +{ + struct list *entry; + + while ((entry = list_head(&font_link_info->sections))) + { + struct gdip_font_link_section *section = LIST_ENTRY(entry, struct gdip_font_link_section, entry); + list_remove(entry); + if (section->font != font_link_info->base_font) + GdipDeleteFont(section->font); + free(section); + } +} + GpStatus gdip_format_string(GpGraphics *graphics, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, int ignore_empty_clip, @@ -5177,6 +5282,7 @@ GpStatus gdip_format_string(GpGraphics *graphics, INT *hotkeyprefix_offsets=NULL; INT hotkeyprefix_count=0; INT hotkeyprefix_pos=0, hotkeyprefix_end_pos=0; + struct gdip_font_link_info font_link_info = { 0 }; BOOL seen_prefix = FALSE, unixstyle_newline = TRUE; if(length == -1) length = lstrlenW(string); @@ -5242,9 +5348,10 @@ GpStatus gdip_format_string(GpGraphics *graphics, halign = format->align; + generate_font_link_info(graphics, stringdup, length, font, &font_link_info); + while(sum < length){ - GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum, - nwidth, &fit, NULL, &size); + font_link_get_text_extent_point(&font_link_info, graphics, stringdup, sum, length - sum, nwidth, &fit, &size); fitcpy = fit; if(fit == 0) @@ -5292,8 +5399,7 @@ GpStatus gdip_format_string(GpGraphics *graphics, else lineend = fit; - GetTextExtentExPointW(graphics->hdc, stringdup + sum, lineend, - nwidth, &j, NULL, &size); + font_link_get_text_extent_point(&font_link_info, graphics, stringdup, sum, lineend, nwidth, &j, &size); bounds.Width = size.cx; @@ -5327,7 +5433,7 @@ GpStatus gdip_format_string(GpGraphics *graphics, break; stat = callback(graphics, stringdup, sum, lineend, - font, rect, format, lineno, &bounds, + &font_link_info, rect, format, lineno, &bounds, &hotkeyprefix_offsets[hotkeyprefix_pos], hotkeyprefix_end_pos-hotkeyprefix_pos, user_data); @@ -5358,6 +5464,7 @@ GpStatus gdip_format_string(GpGraphics *graphics, break; } + release_font_link_info(&font_link_info); free(stringdup); free(hotkeyprefix_offsets); @@ -5393,7 +5500,8 @@ struct measure_ranges_args { }; static GpStatus measure_ranges_callback(GpGraphics *graphics, - GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, + GDIPCONST WCHAR *string, INT index, INT length, + struct gdip_font_link_info *font_link_info, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, INT underlined_index_count, void *user_data) @@ -5414,12 +5522,10 @@ static GpStatus measure_ranges_callback(GpGraphics *graphics, range_rect.Y = bounds->Y / args->rel_height; range_rect.Height = bounds->Height / args->rel_height; - GetTextExtentExPointW(graphics->hdc, string + index, range_start - index, - INT_MAX, NULL, NULL, &range_size); + font_link_get_text_extent_point(font_link_info, graphics, string, index, range_start - index, INT_MAX, NULL, &range_size); range_rect.X = (bounds->X + range_size.cx) / args->rel_width; - GetTextExtentExPointW(graphics->hdc, string + index, range_end - index, - INT_MAX, NULL, NULL, &range_size); + font_link_get_text_extent_point(font_link_info, graphics, string, index, range_end - index, INT_MAX, NULL, &range_size); range_rect.Width = (bounds->X + range_size.cx) / args->rel_width - range_rect.X; stat = GdipCombineRegionRect(args->regions[i], &range_rect, CombineModeUnion); @@ -5518,7 +5624,8 @@ struct measure_string_args { }; static GpStatus measure_string_callback(GpGraphics *graphics, - GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, + GDIPCONST WCHAR *string, INT index, INT length, + struct gdip_font_link_info *font_link_info, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, INT underlined_index_count, void *user_data) @@ -5645,21 +5752,37 @@ struct draw_string_args { }; static GpStatus draw_string_callback(GpGraphics *graphics, - GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, + GDIPCONST WCHAR *string, INT index, INT length, + struct gdip_font_link_info *font_link_info, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, INT underlined_index_count, void *user_data) { struct draw_string_args *args = user_data; + int i = index; PointF position; - GpStatus stat; + SIZE size; + DWORD to_draw_length; + struct gdip_font_link_section *section; + GpStatus stat = Ok; position.X = args->x + bounds->X / args->rel_width; position.Y = args->y + bounds->Y / args->rel_height + args->ascent; - stat = draw_driver_string(graphics, &string[index], length, font, format, - args->brush, &position, - DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL); + LIST_FOR_EACH_ENTRY(section, &font_link_info->sections, struct gdip_font_link_section, entry) + { + if (i >= section->end) continue; + + to_draw_length = min(length - (i - index), section->end - i); + TRACE("index %d, todraw %ld, used %s\n", i, to_draw_length, section->font == font_link_info->base_font ? "base font" : "map"); + font_link_get_text_extent_point(font_link_info, graphics, string, i, to_draw_length, 0, NULL, &size); + stat = draw_driver_string(graphics, &string[i], to_draw_length, + section->font, format, args->brush, &position, + DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL); + position.X += size.cx / args->rel_width; + i += to_draw_length; + if (stat != Ok || (i - index) >= length) break; + } if (stat == Ok && underlined_index_count) { @@ -5678,10 +5801,10 @@ static GpStatus draw_string_callback(GpGraphics *graphics, SIZE text_size; INT ofs = underlined_indexes[i] - index; - GetTextExtentExPointW(graphics->hdc, string + index, ofs, INT_MAX, NULL, NULL, &text_size); + font_link_get_text_extent_point(font_link_info, graphics, string, index, ofs, INT_MAX, NULL, &text_size); start_x = text_size.cx / args->rel_width; - GetTextExtentExPointW(graphics->hdc, string + index, ofs+1, INT_MAX, NULL, NULL, &text_size); + font_link_get_text_extent_point(font_link_info, graphics, string, index, ofs+1, INT_MAX, NULL, &text_size); end_x = text_size.cx / args->rel_width; GdipFillRectangle(graphics, (GpBrush*)args->brush, position.X+start_x, underline_y, end_x-start_x, underline_height); diff --git a/dlls/gdiplus/graphicspath.c b/dlls/gdiplus/graphicspath.c index 391ba72de98..20e0787de16 100644 --- a/dlls/gdiplus/graphicspath.c +++ b/dlls/gdiplus/graphicspath.c @@ -950,7 +950,7 @@ struct format_string_args }; static GpStatus format_string_callback(GpGraphics *graphics, - GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, + GDIPCONST WCHAR *string, INT index, INT length, struct gdip_font_link_info *font_link_info, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, INT underlined_index_count, void *priv) From d5d4d8c4578d0af11f4b2d5171a55d6ee2ef9c83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 16 Nov 2023 11:31:22 +0100 Subject: [PATCH 008/413] include: Add HEAACWAVEINFO and HEAACWAVEFORMAT definitions. (cherry picked from commit 681d20146226132c2ed18ae7ecb53003e6ffcf44) --- dlls/winegstreamer/mfplat.c | 18 +----------------- include/mmreg.h | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index aae1b63de5d..c386ce492d1 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -25,6 +25,7 @@ #include "initguid.h" #include "d3d9types.h" #include "mfapi.h" +#include "mmreg.h" #include "wine/debug.h" #include "wine/list.h" @@ -642,23 +643,6 @@ static void mf_media_type_to_wg_format_audio(IMFMediaType *type, const GUID *sub static void mf_media_type_to_wg_format_audio_mpeg4(IMFMediaType *type, const GUID *subtype, struct wg_format *format) { - /* Audio specific config is stored at after HEAACWAVEINFO in MF_MT_USER_DATA - * https://docs.microsoft.com/en-us/windows/win32/api/mmreg/ns-mmreg-heaacwaveformat - */ - typedef struct - { - WORD wPayloadType; - WORD wAudioProfileLevelIndication; - WORD wStructType; - WORD wReserved1; - DWORD dwReserved2; - } HEAACWAVEINFO; - typedef struct - { - HEAACWAVEINFO wfInfo; - BYTE pbAudioSpecificConfig[1]; - } HEAACWAVEFORMAT; - BYTE buffer[64]; HEAACWAVEFORMAT *user_data = (HEAACWAVEFORMAT *)buffer; UINT32 codec_data_size; diff --git a/include/mmreg.h b/include/mmreg.h index 1b19e1f068f..a8b0056064e 100644 --- a/include/mmreg.h +++ b/include/mmreg.h @@ -645,6 +645,22 @@ typedef struct mpeg1waveformat_tag { typedef MPEG1WAVEFORMAT *PMPEG1WAVEFORMAT, *NPMPEG1WAVEFORMAT, *LPMPEG1WAVEFORMAT; +typedef struct heaacwaveinfo_tag +{ + WAVEFORMATEX wfx; + WORD wPayloadType; + WORD wAudioProfileLevelIndication; + WORD wStructType; + WORD wReserved1; + DWORD dwReserved2; +} HEAACWAVEINFO, *PHEAACWAVEINFO; + +typedef struct heaacwaveformat_tag +{ + HEAACWAVEINFO wfInfo; + BYTE pbAudioSpecificConfig[1]; +} HEAACWAVEFORMAT, *PHEAACWAVEFORMAT; + #define ACM_MPEG_LAYER1 0x0001 #define ACM_MPEG_LAYER2 0x0002 #define ACM_MPEG_LAYER3 0x0004 From 5e2a5b44aeeaf1b5a279e58b6494cb0534cc4e6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 16 Nov 2023 11:26:57 +0100 Subject: [PATCH 009/413] mfplat/tests: Test MFInitMediaTypeFromWaveFormatEx wrt MF_MT_FIXED_SIZE_SAMPLES. (cherry picked from commit 2155817a3646329699069a4be4905b2685f987ba) --- dlls/mfplat/tests/mfplat.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index c7ee087bb93..86096f7222b 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -6953,6 +6953,11 @@ static void validate_media_type(IMFMediaType *mediatype, const WAVEFORMATEX *for } else ok(FAILED(hr), "Unexpected ALL_SAMPLES_INDEPENDENT.\n"); + + hr = IMFMediaType_GetUINT32(mediatype, &MF_MT_FIXED_SIZE_SAMPLES, &value); + ok(FAILED(hr), "Unexpected FIXED_SIZE_SAMPLES.\n"); + hr = IMFMediaType_GetUINT32(mediatype, &MF_MT_COMPRESSED, &value); + ok(FAILED(hr), "Unexpected COMPRESSED.\n"); } static void test_MFInitMediaTypeFromWaveFormatEx(void) From 9d714077711ff0be0a387582b1f481b4f68c0d07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 16 Nov 2023 11:43:33 +0100 Subject: [PATCH 010/413] mfplat/tests: Add MFInitMediaTypeFromWaveFormatEx tests with HEAACWAVEFORMAT. (cherry picked from commit aa648beec8b6f24fbec2b9b7e3ed2dd59b7fc70a) --- dlls/mfplat/tests/mfplat.c | 45 +++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 86096f7222b..db47aaa2f1c 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -6991,11 +6991,13 @@ static void test_MFInitMediaTypeFromWaveFormatEx(void) { WAVE_FORMAT_WMASPDIF }, }; - UINT8 buff[MPEGLAYER3_WFX_EXTRA_BYTES]; + UINT8 buff[1024]; WAVEFORMATEXTENSIBLE waveformatext; MPEGLAYER3WAVEFORMAT mp3format; + HEAACWAVEFORMAT aacformat; IMFMediaType *mediatype; unsigned int i, size; + UINT32 value; HRESULT hr; hr = MFCreateMediaType(&mediatype); @@ -7048,6 +7050,47 @@ static void test_MFInitMediaTypeFromWaveFormatEx(void) ok(size == mp3format.wfx.cbSize, "Unexpected size %u.\n", size); ok(!memcmp(buff, (WAVEFORMATEX *)&mp3format + 1, size), "Unexpected user data.\n"); + /* HEAACWAVEFORMAT */ + aacformat.wfInfo.wfx.wFormatTag = WAVE_FORMAT_MPEG_HEAAC; + aacformat.wfInfo.wfx.nChannels = 2; + aacformat.wfInfo.wfx.nSamplesPerSec = 44100; + aacformat.wfInfo.wfx.nAvgBytesPerSec = 16000; + aacformat.wfInfo.wfx.nBlockAlign = 1; + aacformat.wfInfo.wfx.wBitsPerSample = 0; + aacformat.wfInfo.wPayloadType = 2; + aacformat.wfInfo.wAudioProfileLevelIndication = 1; + aacformat.pbAudioSpecificConfig[0] = 0xcd; + + /* test with invalid format size */ + aacformat.wfInfo.wfx.cbSize = sizeof(aacformat) - 2 - sizeof(WAVEFORMATEX); + hr = MFInitMediaTypeFromWaveFormatEx(mediatype, (WAVEFORMATEX *)&aacformat, sizeof(aacformat) - 2); + todo_wine + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + aacformat.wfInfo.wfx.cbSize = sizeof(aacformat) - sizeof(WAVEFORMATEX); + hr = MFInitMediaTypeFromWaveFormatEx(mediatype, (WAVEFORMATEX *)&aacformat, sizeof(aacformat)); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + validate_media_type(mediatype, &aacformat.wfInfo.wfx); + + value = 0xdeadbeef; + hr = IMFMediaType_GetUINT32(mediatype, &MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, &value); + todo_wine + ok(hr == S_OK, "Failed to get attribute, hr %#lx.\n", hr); + todo_wine + ok(value == aacformat.wfInfo.wAudioProfileLevelIndication, "Unexpected AAC_AUDIO_PROFILE_LEVEL_INDICATION %u.\n", value); + value = 0xdeadbeef; + hr = IMFMediaType_GetUINT32(mediatype, &MF_MT_AAC_PAYLOAD_TYPE, &value); + todo_wine + ok(hr == S_OK, "Failed to get attribute, hr %#lx.\n", hr); + todo_wine + ok(value == aacformat.wfInfo.wPayloadType, "Unexpected AAC_PAYLOAD_TYPE %u.\n", value); + + hr = IMFMediaType_GetBlob(mediatype, &MF_MT_USER_DATA, buff, sizeof(buff), &size); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(size == aacformat.wfInfo.wfx.cbSize, "Unexpected size %u.\n", size); + ok(!memcmp(buff, (WAVEFORMATEX *)&aacformat + 1, size), "Unexpected user data.\n"); + IMFMediaType_Release(mediatype); } From edec70dc458d8b8ff91ca767316d118b38d14590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 16 Nov 2023 14:28:10 +0100 Subject: [PATCH 011/413] mfplat/tests: Test MFWaveFormatExConvertFlag_ForceExtensible with HEAACWAVEFORMAT. (cherry picked from commit 96346d24c1ceac83ea6e5436314403aa81563e99) --- dlls/mfplat/tests/mfplat.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index db47aaa2f1c..a7d2146b8b6 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -6994,6 +6994,7 @@ static void test_MFInitMediaTypeFromWaveFormatEx(void) UINT8 buff[1024]; WAVEFORMATEXTENSIBLE waveformatext; MPEGLAYER3WAVEFORMAT mp3format; + WAVEFORMATEXTENSIBLE *format; HEAACWAVEFORMAT aacformat; IMFMediaType *mediatype; unsigned int i, size; @@ -7091,6 +7092,40 @@ static void test_MFInitMediaTypeFromWaveFormatEx(void) ok(size == aacformat.wfInfo.wfx.cbSize, "Unexpected size %u.\n", size); ok(!memcmp(buff, (WAVEFORMATEX *)&aacformat + 1, size), "Unexpected user data.\n"); + hr = MFCreateWaveFormatExFromMFMediaType(mediatype, (WAVEFORMATEX **)&format, &size, MFWaveFormatExConvertFlag_ForceExtensible); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (hr == S_OK) + { + ok(format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE, "got wFormatTag %#x\n", format->Format.wFormatTag); + ok(format->Format.cbSize == aacformat.wfInfo.wfx.cbSize + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX), + "got cbSize %u\n", format->Format.cbSize); + ok(IsEqualGUID(&format->SubFormat, &MFAudioFormat_AAC), "got SubFormat %s\n", debugstr_guid(&format->SubFormat)); + ok(format->dwChannelMask == 3, "got dwChannelMask %#lx\n", format->dwChannelMask); + ok(format->Samples.wSamplesPerBlock == 0, "got wSamplesPerBlock %u\n", format->Samples.wSamplesPerBlock); + ok(!memcmp(format + 1, &aacformat.wfInfo.wfx + 1, aacformat.wfInfo.wfx.cbSize), "Unexpected user data.\n"); + CoTaskMemFree(format); + } + + /* test with invalid format size */ + aacformat.wfInfo.wfx.cbSize = 1; + hr = IMFMediaType_SetBlob(mediatype, &MF_MT_USER_DATA, buff, aacformat.wfInfo.wfx.cbSize); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = MFCreateWaveFormatExFromMFMediaType(mediatype, (WAVEFORMATEX **)&format, &size, MFWaveFormatExConvertFlag_ForceExtensible); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (hr == S_OK) + { + ok(format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE, "got wFormatTag %#x\n", format->Format.wFormatTag); + ok(format->Format.cbSize == aacformat.wfInfo.wfx.cbSize + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX), + "got cbSize %u\n", format->Format.cbSize); + ok(IsEqualGUID(&format->SubFormat, &MFAudioFormat_AAC), "got SubFormat %s\n", debugstr_guid(&format->SubFormat)); + ok(format->dwChannelMask == 3, "got dwChannelMask %#lx\n", format->dwChannelMask); + ok(format->Samples.wSamplesPerBlock == 0, "got wSamplesPerBlock %u\n", format->Samples.wSamplesPerBlock); + ok(!memcmp(format + 1, &aacformat.wfInfo.wfx + 1, aacformat.wfInfo.wfx.cbSize), "Unexpected user data.\n"); + CoTaskMemFree(format); + } + IMFMediaType_Release(mediatype); } From b3e4acdde91dd98106367756cf039f775f0d3a99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 16 Nov 2023 11:51:35 +0100 Subject: [PATCH 012/413] mfplat: Support AAC format attributes in MFInitMediaTypeFromWaveFormatEx. (cherry picked from commit 1939bfff9ff402c009bf72d1522aac050c469484) --- dlls/mfplat/mediatype.c | 9 +++++++++ dlls/mfplat/tests/mfplat.c | 5 ----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/dlls/mfplat/mediatype.c b/dlls/mfplat/mediatype.c index c73c92be642..8b96880c865 100644 --- a/dlls/mfplat/mediatype.c +++ b/dlls/mfplat/mediatype.c @@ -3126,6 +3126,15 @@ HRESULT WINAPI MFInitMediaTypeFromWaveFormatEx(IMFMediaType *mediatype, const WA mediatype_set_uint32(mediatype, &MF_MT_ALL_SAMPLES_INDEPENDENT, 1, &hr); } + if (IsEqualGUID(&subtype, &MFAudioFormat_AAC)) + { + HEAACWAVEINFO *info = CONTAINING_RECORD(format, HEAACWAVEINFO, wfx); + if (format->cbSize < sizeof(HEAACWAVEINFO) - sizeof(WAVEFORMATEX)) + return E_INVALIDARG; + mediatype_set_uint32(mediatype, &MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, info->wAudioProfileLevelIndication, &hr); + mediatype_set_uint32(mediatype, &MF_MT_AAC_PAYLOAD_TYPE, info->wPayloadType, &hr); + } + if (format->cbSize && format->wFormatTag != WAVE_FORMAT_EXTENSIBLE) mediatype_set_blob(mediatype, &MF_MT_USER_DATA, (const UINT8 *)(format + 1), format->cbSize, &hr); diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index a7d2146b8b6..0c1e6b55321 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -7065,7 +7065,6 @@ static void test_MFInitMediaTypeFromWaveFormatEx(void) /* test with invalid format size */ aacformat.wfInfo.wfx.cbSize = sizeof(aacformat) - 2 - sizeof(WAVEFORMATEX); hr = MFInitMediaTypeFromWaveFormatEx(mediatype, (WAVEFORMATEX *)&aacformat, sizeof(aacformat) - 2); - todo_wine ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); aacformat.wfInfo.wfx.cbSize = sizeof(aacformat) - sizeof(WAVEFORMATEX); @@ -7076,15 +7075,11 @@ static void test_MFInitMediaTypeFromWaveFormatEx(void) value = 0xdeadbeef; hr = IMFMediaType_GetUINT32(mediatype, &MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, &value); - todo_wine ok(hr == S_OK, "Failed to get attribute, hr %#lx.\n", hr); - todo_wine ok(value == aacformat.wfInfo.wAudioProfileLevelIndication, "Unexpected AAC_AUDIO_PROFILE_LEVEL_INDICATION %u.\n", value); value = 0xdeadbeef; hr = IMFMediaType_GetUINT32(mediatype, &MF_MT_AAC_PAYLOAD_TYPE, &value); - todo_wine ok(hr == S_OK, "Failed to get attribute, hr %#lx.\n", hr); - todo_wine ok(value == aacformat.wfInfo.wPayloadType, "Unexpected AAC_PAYLOAD_TYPE %u.\n", value); hr = IMFMediaType_GetBlob(mediatype, &MF_MT_USER_DATA, buff, sizeof(buff), &size); From d0b93ab1beb75957f354ab613e207a32a831fb55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 16 Nov 2023 12:15:17 +0100 Subject: [PATCH 013/413] mfplat: Support compressed WAVEFORMATEX in MFCreateWaveFormatExFromMFMediaType. (cherry picked from commit a634c30f00c5cf51b9499a0781f517fc0d2d835f) --- dlls/mfplat/mediatype.c | 50 ++++++++++++++++++-------------------- dlls/mfplat/tests/mfplat.c | 40 ++++++++++++------------------ 2 files changed, 39 insertions(+), 51 deletions(-) diff --git a/dlls/mfplat/mediatype.c b/dlls/mfplat/mediatype.c index 8b96880c865..a159ae4b56f 100644 --- a/dlls/mfplat/mediatype.c +++ b/dlls/mfplat/mediatype.c @@ -2965,10 +2965,10 @@ HRESULT WINAPI MFUnwrapMediaType(IMFMediaType *wrapper, IMFMediaType **ret) HRESULT WINAPI MFCreateWaveFormatExFromMFMediaType(IMFMediaType *mediatype, WAVEFORMATEX **ret_format, UINT32 *size, UINT32 flags) { - WAVEFORMATEXTENSIBLE *format_ext = NULL; + UINT32 value, extra_size = 0, user_size; WAVEFORMATEX *format; GUID major, subtype; - UINT32 value; + void *user_data; HRESULT hr; TRACE("%p, %p, %p, %#x.\n", mediatype, ret_format, size, flags); @@ -2982,36 +2982,24 @@ HRESULT WINAPI MFCreateWaveFormatExFromMFMediaType(IMFMediaType *mediatype, WAVE if (!IsEqualGUID(&major, &MFMediaType_Audio)) return E_INVALIDARG; - if (!IsEqualGUID(&subtype, &MFAudioFormat_PCM) && !IsEqualGUID(&subtype, &MFAudioFormat_Float)) + if (FAILED(hr = IMFMediaType_GetBlobSize(mediatype, &MF_MT_USER_DATA, &user_size))) { - FIXME("Unsupported audio format %s.\n", debugstr_guid(&subtype)); - return E_NOTIMPL; + if (!IsEqualGUID(&subtype, &MFAudioFormat_PCM) && !IsEqualGUID(&subtype, &MFAudioFormat_Float)) + return hr; + user_size = 0; } - /* FIXME: probably WAVE_FORMAT_MPEG/WAVE_FORMAT_MPEGLAYER3 should be handled separately. */ if (flags == MFWaveFormatExConvertFlag_ForceExtensible) - { - format_ext = CoTaskMemAlloc(sizeof(*format_ext)); - *size = sizeof(*format_ext); - format = (WAVEFORMATEX *)format_ext; - } - else - { - format = CoTaskMemAlloc(sizeof(*format)); - *size = sizeof(*format); - } + extra_size = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(*format); - if (!format) + *size = sizeof(*format) + user_size + extra_size; + if (!(format = CoTaskMemAlloc(*size))) return E_OUTOFMEMORY; memset(format, 0, *size); - - if (format_ext) - format->wFormatTag = WAVE_FORMAT_EXTENSIBLE; - else if (IsEqualGUID(&subtype, &MFAudioFormat_Float)) - format->wFormatTag = WAVE_FORMAT_IEEE_FLOAT; - else - format->wFormatTag = WAVE_FORMAT_PCM; + format->wFormatTag = subtype.Data1; + format->cbSize = user_size + extra_size; + user_data = format + 1; if (SUCCEEDED(IMFMediaType_GetUINT32(mediatype, &MF_MT_AUDIO_NUM_CHANNELS, &value))) format->nChannels = value; @@ -3023,18 +3011,26 @@ HRESULT WINAPI MFCreateWaveFormatExFromMFMediaType(IMFMediaType *mediatype, WAVE format->nBlockAlign = value; if (SUCCEEDED(IMFMediaType_GetUINT32(mediatype, &MF_MT_AUDIO_BITS_PER_SAMPLE, &value))) format->wBitsPerSample = value; - if (format_ext) + + if (flags == MFWaveFormatExConvertFlag_ForceExtensible) { - format->cbSize = sizeof(*format_ext) - sizeof(*format); + WAVEFORMATEXTENSIBLE *format_ext = CONTAINING_RECORD(format, WAVEFORMATEXTENSIBLE, Format); + + format_ext->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + format_ext->SubFormat = subtype; + user_data = format_ext + 1; if (SUCCEEDED(IMFMediaType_GetUINT32(mediatype, &MF_MT_AUDIO_VALID_BITS_PER_SAMPLE, &value))) format_ext->Samples.wSamplesPerBlock = value; if (SUCCEEDED(IMFMediaType_GetUINT32(mediatype, &MF_MT_AUDIO_CHANNEL_MASK, &value))) format_ext->dwChannelMask = value; - memcpy(&format_ext->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM, sizeof(format_ext->SubFormat)); + else if (format_ext->Format.nChannels < ARRAY_SIZE(default_channel_mask)) + format_ext->dwChannelMask = default_channel_mask[format_ext->Format.nChannels]; } + IMFMediaType_GetBlob(mediatype, &MF_MT_USER_DATA, user_data, user_size, NULL); + *ret_format = format; return S_OK; diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 0c1e6b55321..f785df89b32 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -7088,38 +7088,30 @@ static void test_MFInitMediaTypeFromWaveFormatEx(void) ok(!memcmp(buff, (WAVEFORMATEX *)&aacformat + 1, size), "Unexpected user data.\n"); hr = MFCreateWaveFormatExFromMFMediaType(mediatype, (WAVEFORMATEX **)&format, &size, MFWaveFormatExConvertFlag_ForceExtensible); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (hr == S_OK) - { - ok(format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE, "got wFormatTag %#x\n", format->Format.wFormatTag); - ok(format->Format.cbSize == aacformat.wfInfo.wfx.cbSize + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX), - "got cbSize %u\n", format->Format.cbSize); - ok(IsEqualGUID(&format->SubFormat, &MFAudioFormat_AAC), "got SubFormat %s\n", debugstr_guid(&format->SubFormat)); - ok(format->dwChannelMask == 3, "got dwChannelMask %#lx\n", format->dwChannelMask); - ok(format->Samples.wSamplesPerBlock == 0, "got wSamplesPerBlock %u\n", format->Samples.wSamplesPerBlock); - ok(!memcmp(format + 1, &aacformat.wfInfo.wfx + 1, aacformat.wfInfo.wfx.cbSize), "Unexpected user data.\n"); - CoTaskMemFree(format); - } + ok(format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE, "got wFormatTag %#x\n", format->Format.wFormatTag); + ok(format->Format.cbSize == aacformat.wfInfo.wfx.cbSize + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX), + "got cbSize %u\n", format->Format.cbSize); + ok(IsEqualGUID(&format->SubFormat, &MFAudioFormat_AAC), "got SubFormat %s\n", debugstr_guid(&format->SubFormat)); + ok(format->dwChannelMask == 3, "got dwChannelMask %#lx\n", format->dwChannelMask); + ok(format->Samples.wSamplesPerBlock == 0, "got wSamplesPerBlock %u\n", format->Samples.wSamplesPerBlock); + ok(!memcmp(format + 1, &aacformat.wfInfo.wfx + 1, aacformat.wfInfo.wfx.cbSize), "Unexpected user data.\n"); + CoTaskMemFree(format); /* test with invalid format size */ aacformat.wfInfo.wfx.cbSize = 1; hr = IMFMediaType_SetBlob(mediatype, &MF_MT_USER_DATA, buff, aacformat.wfInfo.wfx.cbSize); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = MFCreateWaveFormatExFromMFMediaType(mediatype, (WAVEFORMATEX **)&format, &size, MFWaveFormatExConvertFlag_ForceExtensible); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (hr == S_OK) - { - ok(format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE, "got wFormatTag %#x\n", format->Format.wFormatTag); - ok(format->Format.cbSize == aacformat.wfInfo.wfx.cbSize + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX), - "got cbSize %u\n", format->Format.cbSize); - ok(IsEqualGUID(&format->SubFormat, &MFAudioFormat_AAC), "got SubFormat %s\n", debugstr_guid(&format->SubFormat)); - ok(format->dwChannelMask == 3, "got dwChannelMask %#lx\n", format->dwChannelMask); - ok(format->Samples.wSamplesPerBlock == 0, "got wSamplesPerBlock %u\n", format->Samples.wSamplesPerBlock); - ok(!memcmp(format + 1, &aacformat.wfInfo.wfx + 1, aacformat.wfInfo.wfx.cbSize), "Unexpected user data.\n"); - CoTaskMemFree(format); - } + ok(format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE, "got wFormatTag %#x\n", format->Format.wFormatTag); + ok(format->Format.cbSize == aacformat.wfInfo.wfx.cbSize + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX), + "got cbSize %u\n", format->Format.cbSize); + ok(IsEqualGUID(&format->SubFormat, &MFAudioFormat_AAC), "got SubFormat %s\n", debugstr_guid(&format->SubFormat)); + ok(format->dwChannelMask == 3, "got dwChannelMask %#lx\n", format->dwChannelMask); + ok(format->Samples.wSamplesPerBlock == 0, "got wSamplesPerBlock %u\n", format->Samples.wSamplesPerBlock); + ok(!memcmp(format + 1, &aacformat.wfInfo.wfx + 1, aacformat.wfInfo.wfx.cbSize), "Unexpected user data.\n"); + CoTaskMemFree(format); IMFMediaType_Release(mediatype); } From 0040835075ce0d357e4868d2eb9226fe64262b2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 23 Jan 2024 08:52:36 +0100 Subject: [PATCH 014/413] winegstreamer: Fix reading MF_MT_USER_DATA into HEAACWAVEFORMAT. Fixes 681d20146226132c2ed18ae7ecb53003e6ffcf44. The winegstreamer private declaration of HEAACWAVEINFO previously didn't include the WAVEFORMATEX member as it should. (cherry picked from commit 42c96b9c8fb0c4bcc912e365733b564f3faf5f7c) --- dlls/winegstreamer/mfplat.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index c386ce492d1..4372678fe0e 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -643,12 +643,14 @@ static void mf_media_type_to_wg_format_audio(IMFMediaType *type, const GUID *sub static void mf_media_type_to_wg_format_audio_mpeg4(IMFMediaType *type, const GUID *subtype, struct wg_format *format) { - BYTE buffer[64]; - HEAACWAVEFORMAT *user_data = (HEAACWAVEFORMAT *)buffer; + BYTE buffer[sizeof(HEAACWAVEFORMAT) + 64]; + HEAACWAVEFORMAT *wfx = (HEAACWAVEFORMAT *)buffer; UINT32 codec_data_size; BOOL raw_aac; - if (FAILED(IMFMediaType_GetBlob(type, &MF_MT_USER_DATA, buffer, sizeof(buffer), &codec_data_size))) + wfx->wfInfo.wfx.cbSize = sizeof(buffer) - sizeof(wfx->wfInfo.wfx); + if (FAILED(IMFMediaType_GetBlob(type, &MF_MT_USER_DATA, (BYTE *)(&wfx->wfInfo.wfx + 1), + wfx->wfInfo.wfx.cbSize, &codec_data_size))) { FIXME("Codec data is not set.\n"); return; @@ -656,16 +658,16 @@ static void mf_media_type_to_wg_format_audio_mpeg4(IMFMediaType *type, const GUI raw_aac = IsEqualGUID(subtype, &MFAudioFormat_RAW_AAC); if (!raw_aac) - codec_data_size -= min(codec_data_size, offsetof(HEAACWAVEFORMAT, pbAudioSpecificConfig)); + codec_data_size -= min(codec_data_size, sizeof(HEAACWAVEINFO) - sizeof(WAVEFORMATEX)); if (codec_data_size > sizeof(format->u.audio_mpeg4.codec_data)) { FIXME("Codec data needs %u bytes.\n", codec_data_size); return; } if (raw_aac) - memcpy(format->u.audio_mpeg4.codec_data, buffer, codec_data_size); + memcpy(format->u.audio_mpeg4.codec_data, (BYTE *)(&wfx->wfInfo.wfx + 1), codec_data_size); else - memcpy(format->u.audio_mpeg4.codec_data, user_data->pbAudioSpecificConfig, codec_data_size); + memcpy(format->u.audio_mpeg4.codec_data, wfx->pbAudioSpecificConfig, codec_data_size); format->major_type = WG_MAJOR_TYPE_AUDIO_MPEG4; From 607dfa206841a51066c0cdd0ad007d7674d365b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 16 Nov 2023 11:59:50 +0100 Subject: [PATCH 015/413] winegstreamer: Use MFCreateAudioMediaType in the AAC decoder. (cherry picked from commit c65703eac726f41b64ee58fb45fbdc9bbd83f5d7) --- dlls/winegstreamer/aac_decoder.c | 203 ++++++++++--------------------- 1 file changed, 66 insertions(+), 137 deletions(-) diff --git a/dlls/winegstreamer/aac_decoder.c b/dlls/winegstreamer/aac_decoder.c index dfab7a8b0c0..811313b6fe4 100644 --- a/dlls/winegstreamer/aac_decoder.c +++ b/dlls/winegstreamer/aac_decoder.c @@ -32,22 +32,26 @@ WINE_DEFAULT_DEBUG_CHANNEL(mfplat); WINE_DECLARE_DEBUG_CHANNEL(winediag); -static struct +static HEAACWAVEINFO aac_decoder_input_types[] = { - const GUID *const guid; - UINT32 payload_type; -} aac_decoder_input_types[] = -{ - {&MFAudioFormat_AAC, 0}, - {&MFAudioFormat_RAW_AAC, -1}, - {&MFAudioFormat_AAC, 1}, - {&MFAudioFormat_AAC, 3}, - {&MFAudioFormat_ADTS, -1}, +#define MAKE_HEAACWAVEINFO(format, payload) \ + {.wfx = {.wFormatTag = format, .nChannels = 6, .nSamplesPerSec = 48000, .nAvgBytesPerSec = 1152000, \ + .nBlockAlign = 24, .wBitsPerSample = 32, .cbSize = sizeof(HEAACWAVEINFO) - sizeof(WAVEFORMATEX)}, \ + .wPayloadType = payload} + + MAKE_HEAACWAVEINFO(WAVE_FORMAT_MPEG_HEAAC, 0), + MAKE_HEAACWAVEINFO(WAVE_FORMAT_RAW_AAC1, 0), + MAKE_HEAACWAVEINFO(WAVE_FORMAT_MPEG_HEAAC, 1), + MAKE_HEAACWAVEINFO(WAVE_FORMAT_MPEG_HEAAC, 3), + MAKE_HEAACWAVEINFO(WAVE_FORMAT_MPEG_ADTS_AAC, 0), + +#undef MAKE_HEAACWAVEINFO }; -static const GUID *const aac_decoder_output_types[] = + +static WAVEFORMATEXTENSIBLE const aac_decoder_output_types[] = { - &MFAudioFormat_PCM, - &MFAudioFormat_Float, + {.Format = {.wFormatTag = WAVE_FORMAT_IEEE_FLOAT, .wBitsPerSample = 32, .nSamplesPerSec = 48000, .nChannels = 2}}, + {.Format = {.wFormatTag = WAVE_FORMAT_PCM, .wBitsPerSample = 16, .nSamplesPerSec = 48000, .nChannels = 2}}, }; static const UINT32 default_channel_mask[7] = @@ -234,79 +238,41 @@ static HRESULT WINAPI transform_AddInputStreams(IMFTransform *iface, DWORD strea static HRESULT WINAPI transform_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, IMFMediaType **type) { - IMFMediaType *media_type; - const GUID *subtype; - HRESULT hr; - TRACE("iface %p, id %#lx, index %#lx, type %p.\n", iface, id, index, type); + *type = NULL; if (id) return MF_E_INVALIDSTREAMNUMBER; - - *type = NULL; if (index >= ARRAY_SIZE(aac_decoder_input_types)) return MF_E_NO_MORE_TYPES; - subtype = aac_decoder_input_types[index].guid; - - if (FAILED(hr = MFCreateMediaType(&media_type))) - return hr; - - if (FAILED(hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio))) - goto done; - if (FAILED(hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, subtype))) - goto done; - - if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, 32))) - goto done; - if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_NUM_CHANNELS, 6))) - goto done; - if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, 24))) - goto done; - if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, 48000))) - goto done; - if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 1152000))) - goto done; - if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1))) - goto done; - if (IsEqualGUID(subtype, &MFAudioFormat_AAC)) - { - if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, 0))) - goto done; - if (aac_decoder_input_types[index].payload_type != -1 - && FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AAC_PAYLOAD_TYPE, - aac_decoder_input_types[index].payload_type))) - goto done; - } - -done: - if (SUCCEEDED(hr)) - IMFMediaType_AddRef((*type = media_type)); - - IMFMediaType_Release(media_type); - return hr; + return MFCreateAudioMediaType(&aac_decoder_input_types[index].wfx, (IMFAudioMediaType **)type); } static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWORD id, DWORD index, IMFMediaType **type) { - UINT32 channel_count, sample_size, sample_rate, block_alignment; struct aac_decoder *decoder = impl_from_IMFTransform(iface); + UINT32 channel_count, sample_rate; + WAVEFORMATEXTENSIBLE wfx = {{0}}; IMFMediaType *media_type; - const GUID *output_type; HRESULT hr; TRACE("iface %p, id %#lx, index %#lx, type %p.\n", iface, id, index, type); + *type = NULL; + if (id) return MF_E_INVALIDSTREAMNUMBER; if (!decoder->input_type) return MF_E_TRANSFORM_TYPE_NOT_SET; - *type = NULL; + wfx = aac_decoder_output_types[index % ARRAY_SIZE(aac_decoder_output_types)]; if (FAILED(hr = IMFMediaType_GetUINT32(decoder->input_type, &MF_MT_AUDIO_NUM_CHANNELS, &channel_count)) || !channel_count) - channel_count = 2; + channel_count = wfx.Format.nChannels; + if (FAILED(hr = IMFMediaType_GetUINT32(decoder->input_type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &sample_rate))) + sample_rate = wfx.Format.nSamplesPerSec; if (channel_count >= ARRAY_SIZE(default_channel_mask)) return MF_E_INVALIDMEDIATYPE; @@ -321,54 +287,24 @@ static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWOR if (index >= ARRAY_SIZE(aac_decoder_output_types)) return MF_E_NO_MORE_TYPES; - index = ARRAY_SIZE(aac_decoder_output_types) - index - 1; - output_type = aac_decoder_output_types[index]; - if (FAILED(hr = MFCreateMediaType(&media_type))) - return hr; + wfx.Format.nChannels = channel_count; + wfx.Format.nSamplesPerSec = sample_rate; + wfx.Format.nBlockAlign = wfx.Format.wBitsPerSample * wfx.Format.nChannels / 8; + wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign; - if (FAILED(hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio))) - goto done; - if (FAILED(hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, output_type))) - goto done; - - if (IsEqualGUID(output_type, &MFAudioFormat_Float)) - sample_size = 32; - else if (IsEqualGUID(output_type, &MFAudioFormat_PCM)) - sample_size = 16; - else + if (wfx.Format.nChannels >= 3) { - FIXME("Subtype %s not implemented!\n", debugstr_guid(output_type)); - hr = E_NOTIMPL; - goto done; + wfx.SubFormat = MFAudioFormat_Base; + wfx.SubFormat.Data1 = wfx.Format.wFormatTag; + wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + wfx.dwChannelMask = default_channel_mask[wfx.Format.nChannels]; } - if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, sample_size))) - goto done; - - if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_NUM_CHANNELS, channel_count))) - goto done; - - if (FAILED(hr = IMFMediaType_GetUINT32(decoder->input_type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &sample_rate))) - goto done; - if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, sample_rate))) - goto done; - - block_alignment = sample_size * channel_count / 8; - if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, block_alignment))) - goto done; - if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, sample_rate * block_alignment))) - goto done; - - if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, 1))) - goto done; + if (FAILED(hr = MFCreateAudioMediaType(&wfx.Format, (IMFAudioMediaType **)&media_type))) + return hr; if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_FIXED_SIZE_SAMPLES, 1))) goto done; - if (channel_count < 3 && FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1))) - goto done; - if (channel_count >= 3 && FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_CHANNEL_MASK, - default_channel_mask[channel_count]))) - goto done; done: if (SUCCEEDED(hr)) @@ -378,12 +314,22 @@ static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWOR return hr; } +static BOOL matches_format(const WAVEFORMATEXTENSIBLE *a, const WAVEFORMATEXTENSIBLE *b) +{ + if (a->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && b->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) + return IsEqualGUID(&a->SubFormat, &b->SubFormat); + if (a->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) + return a->SubFormat.Data1 == b->Format.wFormatTag; + if (b->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) + return b->SubFormat.Data1 == a->Format.wFormatTag; + return a->Format.wFormatTag == b->Format.wFormatTag; +} + static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) { struct aac_decoder *decoder = impl_from_IMFTransform(iface); - MF_ATTRIBUTE_TYPE item_type; - UINT32 channel_count; - GUID major, subtype; + WAVEFORMATEXTENSIBLE *format, wfx; + UINT32 size; HRESULT hr; ULONG i; @@ -392,28 +338,19 @@ static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFM if (id) return MF_E_INVALIDSTREAMNUMBER; - if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major))) - return E_INVALIDARG; - - if (!IsEqualGUID(&major, &MFMediaType_Audio) - || FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype))) - return MF_E_INVALIDMEDIATYPE; + if (FAILED(hr = MFCreateWaveFormatExFromMFMediaType(type, (WAVEFORMATEX **)&format, &size, + MFWaveFormatExConvertFlag_ForceExtensible))) + return hr; + wfx = *format; + CoTaskMemFree(format); for (i = 0; i < ARRAY_SIZE(aac_decoder_input_types); ++i) - if (IsEqualGUID(&subtype, aac_decoder_input_types[i].guid)) + if (matches_format((WAVEFORMATEXTENSIBLE *)&aac_decoder_input_types[i], &wfx)) break; if (i == ARRAY_SIZE(aac_decoder_input_types)) return MF_E_INVALIDMEDIATYPE; - if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, &channel_count)) - && channel_count >= ARRAY_SIZE(default_channel_mask)) - return MF_E_INVALIDMEDIATYPE; - - if (FAILED(IMFMediaType_GetItemType(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &item_type)) - || item_type != MF_ATTRIBUTE_UINT32) - return MF_E_INVALIDMEDIATYPE; - if (FAILED(IMFMediaType_GetItemType(type, &MF_MT_USER_DATA, &item_type)) - || item_type != MF_ATTRIBUTE_BLOB) + if (wfx.Format.nChannels >= ARRAY_SIZE(default_channel_mask) || !wfx.Format.nSamplesPerSec || !wfx.Format.cbSize) return MF_E_INVALIDMEDIATYPE; if (flags & MFT_SET_TYPE_TEST_ONLY) return S_OK; @@ -433,8 +370,8 @@ static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFM static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) { struct aac_decoder *decoder = impl_from_IMFTransform(iface); - MF_ATTRIBUTE_TYPE item_type; - GUID major, subtype; + WAVEFORMATEXTENSIBLE *format, wfx; + UINT32 size; HRESULT hr; ULONG i; @@ -445,27 +382,19 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF if (!decoder->input_type) return MF_E_TRANSFORM_TYPE_NOT_SET; - if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major)) - || FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype))) + if (FAILED(hr = MFCreateWaveFormatExFromMFMediaType(type, (WAVEFORMATEX **)&format, &size, + MFWaveFormatExConvertFlag_ForceExtensible))) return hr; - - if (!IsEqualGUID(&major, &MFMediaType_Audio)) - return MF_E_INVALIDMEDIATYPE; + wfx = *format; + CoTaskMemFree(format); for (i = 0; i < ARRAY_SIZE(aac_decoder_output_types); ++i) - if (IsEqualGUID(&subtype, aac_decoder_output_types[i])) + if (matches_format(&aac_decoder_output_types[i], &wfx)) break; if (i == ARRAY_SIZE(aac_decoder_output_types)) return MF_E_INVALIDMEDIATYPE; - if (FAILED(IMFMediaType_GetItemType(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &item_type)) - || item_type != MF_ATTRIBUTE_UINT32) - return MF_E_INVALIDMEDIATYPE; - if (FAILED(IMFMediaType_GetItemType(type, &MF_MT_AUDIO_NUM_CHANNELS, &item_type)) - || item_type != MF_ATTRIBUTE_UINT32) - return MF_E_INVALIDMEDIATYPE; - if (FAILED(IMFMediaType_GetItemType(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &item_type)) - || item_type != MF_ATTRIBUTE_UINT32) + if (!wfx.Format.wBitsPerSample || !wfx.Format.nChannels || !wfx.Format.nSamplesPerSec) return MF_E_INVALIDMEDIATYPE; if (flags & MFT_SET_TYPE_TEST_ONLY) return S_OK; From 4fcdde0abcd904514ae5cd7ea290a72825ed6a9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 17 Nov 2023 11:20:54 +0100 Subject: [PATCH 016/413] winegstreamer: Use an array for the audio decoder input types. (cherry picked from commit 68f8b53d69453eace97ce02eb68dfd88d351a27a) --- dlls/winegstreamer/aac_decoder.c | 58 +++++++++++++++++++------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/dlls/winegstreamer/aac_decoder.c b/dlls/winegstreamer/aac_decoder.c index 811313b6fe4..c0bc57f278e 100644 --- a/dlls/winegstreamer/aac_decoder.c +++ b/dlls/winegstreamer/aac_decoder.c @@ -32,21 +32,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(mfplat); WINE_DECLARE_DEBUG_CHANNEL(winediag); -static HEAACWAVEINFO aac_decoder_input_types[] = -{ -#define MAKE_HEAACWAVEINFO(format, payload) \ - {.wfx = {.wFormatTag = format, .nChannels = 6, .nSamplesPerSec = 48000, .nAvgBytesPerSec = 1152000, \ - .nBlockAlign = 24, .wBitsPerSample = 32, .cbSize = sizeof(HEAACWAVEINFO) - sizeof(WAVEFORMATEX)}, \ - .wPayloadType = payload} - - MAKE_HEAACWAVEINFO(WAVE_FORMAT_MPEG_HEAAC, 0), - MAKE_HEAACWAVEINFO(WAVE_FORMAT_RAW_AAC1, 0), - MAKE_HEAACWAVEINFO(WAVE_FORMAT_MPEG_HEAAC, 1), - MAKE_HEAACWAVEINFO(WAVE_FORMAT_MPEG_HEAAC, 3), - MAKE_HEAACWAVEINFO(WAVE_FORMAT_MPEG_ADTS_AAC, 0), - -#undef MAKE_HEAACWAVEINFO -}; +#define NEXT_WAVEFORMATEXTENSIBLE(format) (WAVEFORMATEXTENSIBLE *)((BYTE *)(&(format)->Format + 1) + (format)->Format.cbSize) static WAVEFORMATEXTENSIBLE const aac_decoder_output_types[] = { @@ -69,6 +55,10 @@ struct aac_decoder { IMFTransform IMFTransform_iface; LONG refcount; + + UINT input_type_count; + WAVEFORMATEXTENSIBLE *input_types; + IMFMediaType *input_type; IMFMediaType *output_type; @@ -238,14 +228,18 @@ static HRESULT WINAPI transform_AddInputStreams(IMFTransform *iface, DWORD strea static HRESULT WINAPI transform_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, IMFMediaType **type) { + struct aac_decoder *decoder = impl_from_IMFTransform(iface); + const WAVEFORMATEXTENSIBLE *format = decoder->input_types; + UINT count = decoder->input_type_count; + TRACE("iface %p, id %#lx, index %#lx, type %p.\n", iface, id, index, type); *type = NULL; if (id) return MF_E_INVALIDSTREAMNUMBER; - if (index >= ARRAY_SIZE(aac_decoder_input_types)) - return MF_E_NO_MORE_TYPES; - return MFCreateAudioMediaType(&aac_decoder_input_types[index].wfx, (IMFAudioMediaType **)type); + for (format = decoder->input_types; index > 0 && count > 0; index--, count--) + format = NEXT_WAVEFORMATEXTENSIBLE(format); + return count ? MFCreateAudioMediaType(&format->Format, (IMFAudioMediaType **)type) : MF_E_NO_MORE_TYPES; } static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWORD id, DWORD index, @@ -328,10 +322,9 @@ static BOOL matches_format(const WAVEFORMATEXTENSIBLE *a, const WAVEFORMATEXTENS static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) { struct aac_decoder *decoder = impl_from_IMFTransform(iface); + UINT32 size, count = decoder->input_type_count; WAVEFORMATEXTENSIBLE *format, wfx; - UINT32 size; HRESULT hr; - ULONG i; TRACE("iface %p, id %#lx, type %p, flags %#lx.\n", iface, id, type, flags); @@ -344,10 +337,9 @@ static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFM wfx = *format; CoTaskMemFree(format); - for (i = 0; i < ARRAY_SIZE(aac_decoder_input_types); ++i) - if (matches_format((WAVEFORMATEXTENSIBLE *)&aac_decoder_input_types[i], &wfx)) - break; - if (i == ARRAY_SIZE(aac_decoder_input_types)) + for (format = decoder->input_types; count > 0 && !matches_format(format, &wfx); count--) + format = NEXT_WAVEFORMATEXTENSIBLE(format); + if (!count) return MF_E_INVALIDMEDIATYPE; if (wfx.Format.nChannels >= ARRAY_SIZE(default_channel_mask) || !wfx.Format.nSamplesPerSec || !wfx.Format.cbSize) @@ -574,6 +566,22 @@ static const IMFTransformVtbl transform_vtbl = transform_ProcessOutput, }; +static HEAACWAVEINFO aac_decoder_input_types[] = +{ +#define MAKE_HEAACWAVEINFO(format, payload) \ + {.wfx = {.wFormatTag = format, .nChannels = 6, .nSamplesPerSec = 48000, .nAvgBytesPerSec = 1152000, \ + .nBlockAlign = 24, .wBitsPerSample = 32, .cbSize = sizeof(HEAACWAVEINFO) - sizeof(WAVEFORMATEX)}, \ + .wPayloadType = payload} + + MAKE_HEAACWAVEINFO(WAVE_FORMAT_MPEG_HEAAC, 0), + MAKE_HEAACWAVEINFO(WAVE_FORMAT_RAW_AAC1, 0), + MAKE_HEAACWAVEINFO(WAVE_FORMAT_MPEG_HEAAC, 1), + MAKE_HEAACWAVEINFO(WAVE_FORMAT_MPEG_HEAAC, 3), + MAKE_HEAACWAVEINFO(WAVE_FORMAT_MPEG_ADTS_AAC, 0), + +#undef MAKE_HEAACWAVEINFO +}; + HRESULT aac_decoder_create(REFIID riid, void **ret) { static const struct wg_format output_format = @@ -604,6 +612,8 @@ HRESULT aac_decoder_create(REFIID riid, void **ret) if (!(decoder = calloc(1, sizeof(*decoder)))) return E_OUTOFMEMORY; + decoder->input_types = (WAVEFORMATEXTENSIBLE *)aac_decoder_input_types; + decoder->input_type_count = ARRAY_SIZE(aac_decoder_input_types); if (FAILED(hr = wg_sample_queue_create(&decoder->wg_sample_queue))) { From 62669b012eb5da250711cd0e372ab405abfdda49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 24 Jan 2024 14:00:43 +0100 Subject: [PATCH 017/413] mf/tests: Check inserted topology loader transforms explicitly. (cherry picked from commit 56b1f8021048621d0354dac302d694500df1062e) --- dlls/mf/tests/mf.c | 180 ++++++++++++++++++++++++++++++--------------- 1 file changed, 120 insertions(+), 60 deletions(-) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 3dd0d9b076d..f5a7821f0f8 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -2983,8 +2983,6 @@ static IMFSampleGrabberSinkCallback *create_test_grabber_callback(void) enum loader_test_flags { - LOADER_EXPECTED_DECODER = 0x1, - LOADER_EXPECTED_CONVERTER = 0x2, LOADER_TODO = 0x4, LOADER_NEEDS_VIDEO_PROCESSOR = 0x8, LOADER_SET_ENUMERATE_SOURCE_TYPES = 0x10, @@ -3066,6 +3064,18 @@ static void test_topology_loader(void) ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 1), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 8), }; + static const media_type_desc audio_float_44100_stereo = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), + ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_Float), + ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), + ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 2 * 4), + ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100), + ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 2 * 4 * 44100), + ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 4 * 8), + ATTR_UINT32(MF_MT_AUDIO_CHANNEL_MASK, 3), + ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), + }; static const media_type_desc video_i420_1280 = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), @@ -3089,6 +3099,25 @@ static void test_topology_loader(void) ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), }; + static const media_type_desc video_h264_1280 = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_H264), + ATTR_RATIO(MF_MT_FRAME_SIZE, 1280, 720), + }; + static const media_type_desc video_nv12_1280 = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12), + ATTR_RATIO(MF_MT_FRAME_RATE, 30000, 1001), + ATTR_RATIO(MF_MT_FRAME_SIZE, 1280, 720), + ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), + ATTR_UINT32(MF_MT_SAMPLE_SIZE, 1280 * 720 * 3 / 2), + ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), + ATTR_UINT32(MF_MT_DEFAULT_STRIDE, 1280), + ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), + ATTR_UINT32(MF_MT_INTERLACE_MODE, 7), + }; static const media_type_desc video_dummy = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), @@ -3099,10 +3128,13 @@ static void test_topology_loader(void) const media_type_desc *input_type; const media_type_desc *output_type; const media_type_desc *current_input; + const media_type_desc *decoded_type; MF_CONNECT_METHOD source_method; MF_CONNECT_METHOD sink_method; HRESULT expected_result; unsigned int flags; + GUID decoder_class; + GUID converter_class; } loader_tests[] = { @@ -3167,27 +3199,24 @@ static void test_topology_loader(void) { /* PCM -> PCM, different enumerated bps, no current type, sink allow converter */ .input_type = &audio_pcm_44100, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_ALLOW_CONVERTER, .source_method = MF_CONNECT_DIRECT, - .expected_result = S_OK, - .flags = LOADER_EXPECTED_CONVERTER, + .expected_result = S_OK, .converter_class = CLSID_CResamplerMediaObject, }, { /* PCM -> PCM, different enumerated bps, same current type, sink allow converter, force enumerate */ .input_type = &audio_pcm_44100, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_ALLOW_CONVERTER, .source_method = -1, .current_input = &audio_pcm_48000, - .expected_result = S_OK, - .flags = LOADER_EXPECTED_CONVERTER | LOADER_SET_ENUMERATE_SOURCE_TYPES, + .expected_result = S_OK, .converter_class = CLSID_CResamplerMediaObject, + .flags = LOADER_SET_ENUMERATE_SOURCE_TYPES, }, { /* PCM -> PCM, different enumerated bps, no current type, sink allow decoder */ .input_type = &audio_pcm_44100, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_ALLOW_DECODER, .source_method = MF_CONNECT_DIRECT, - .expected_result = S_OK, - .flags = LOADER_EXPECTED_CONVERTER, + .expected_result = S_OK, .converter_class = CLSID_CResamplerMediaObject, }, { /* PCM -> PCM, different enumerated bps, no current type, default methods */ .input_type = &audio_pcm_44100, .output_type = &audio_pcm_48000, .sink_method = -1, .source_method = -1, - .expected_result = S_OK, - .flags = LOADER_EXPECTED_CONVERTER, + .expected_result = S_OK, .converter_class = CLSID_CResamplerMediaObject, }, { /* PCM -> PCM, different enumerated bps, no current type, source allow converter */ @@ -3198,14 +3227,14 @@ static void test_topology_loader(void) { /* Float -> PCM, refuse input type, add converter */ .input_type = &audio_float_44100, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, - .expected_result = MF_E_NO_MORE_TYPES, - .flags = LOADER_SET_INVALID_INPUT | LOADER_ADD_RESAMPLER_MFT | LOADER_EXPECTED_CONVERTER, + .expected_result = MF_E_NO_MORE_TYPES, .converter_class = CLSID_CResamplerMediaObject, + .flags = LOADER_SET_INVALID_INPUT | LOADER_ADD_RESAMPLER_MFT, }, { /* Float -> PCM, refuse input type, add converter, allow resampler output type */ .input_type = &audio_float_44100, .output_type = &audio_pcm_48000_resampler, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, - .expected_result = S_OK, - .flags = LOADER_SET_INVALID_INPUT | LOADER_ADD_RESAMPLER_MFT | LOADER_EXPECTED_CONVERTER, + .expected_result = S_OK, .converter_class = CLSID_CResamplerMediaObject, + .flags = LOADER_SET_INVALID_INPUT | LOADER_ADD_RESAMPLER_MFT, }, { @@ -3232,34 +3261,39 @@ static void test_topology_loader(void) /* MP3 -> PCM */ .input_type = &audio_mp3_44100, .output_type = &audio_pcm_44100, .sink_method = MF_CONNECT_ALLOW_DECODER, .source_method = -1, .current_input = &audio_mp3_44100, - .expected_result = S_OK, - .flags = LOADER_EXPECTED_DECODER | LOADER_TODO, + .expected_result = S_OK, .decoder_class = CLSID_CMP3DecMediaObject, + .flags = LOADER_TODO, }, { /* MP3 -> PCM, need both decoder and converter */ .input_type = &audio_mp3_44100, .output_type = &audio_float_48000, .sink_method = MF_CONNECT_ALLOW_DECODER, .source_method = -1, - .current_input = &audio_mp3_44100, - .expected_result = S_OK, - .flags = LOADER_EXPECTED_DECODER | LOADER_EXPECTED_CONVERTER | LOADER_TODO, + .current_input = &audio_mp3_44100, .decoded_type = &audio_float_44100_stereo, + .expected_result = S_OK, .decoder_class = CLSID_CMP3DecMediaObject, .converter_class = CLSID_CResamplerMediaObject, + .flags = LOADER_TODO, }, { /* I420 -> RGB32, Color Convert media type */ .input_type = &video_i420_1280, .output_type = &video_color_convert_1280_rgb32, .sink_method = -1, .source_method = -1, - .expected_result = MF_E_TOPO_CODEC_NOT_FOUND, - .flags = LOADER_NEEDS_VIDEO_PROCESSOR | LOADER_EXPECTED_CONVERTER, + .expected_result = MF_E_TOPO_CODEC_NOT_FOUND, .converter_class = CLSID_CColorConvertDMO, + .flags = LOADER_NEEDS_VIDEO_PROCESSOR, }, { /* I420 -> RGB32, Video Processor media type */ .input_type = &video_i420_1280, .output_type = &video_video_processor_1280_rgb32, .sink_method = -1, .source_method = -1, - .expected_result = S_OK, - .flags = LOADER_EXPECTED_CONVERTER, + .expected_result = S_OK, .converter_class = CLSID_CColorConvertDMO, }, { /* I420 -> RGB32, Video Processor media type without frame size */ .input_type = &video_i420_1280, .output_type = &video_video_processor_rgb32, .sink_method = -1, .source_method = -1, - .expected_result = S_OK, - .flags = LOADER_EXPECTED_CONVERTER, + .expected_result = S_OK, .converter_class = CLSID_CColorConvertDMO, + }, + { + /* H264 -> RGB32, Video Processor media type */ + .input_type = &video_h264_1280, .output_type = &video_video_processor_1280_rgb32, .sink_method = -1, .source_method = -1, + .decoded_type = &video_nv12_1280, + .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, .converter_class = CLSID_CColorConvertDMO, + .flags = LOADER_TODO, }, { /* RGB32 -> Any Video, no current output type */ @@ -3270,14 +3304,14 @@ static void test_topology_loader(void) { /* RGB32 -> Any Video, no current output type, refuse input type */ .input_type = &video_i420_1280, .output_type = &video_dummy, .sink_method = -1, .source_method = -1, - .expected_result = S_OK, - .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_INVALID_INPUT | LOADER_EXPECTED_CONVERTER, + .expected_result = S_OK, .converter_class = CLSID_CColorConvertDMO, + .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_INVALID_INPUT, }, { /* RGB32 -> Any Video, no current output type, refuse input type */ .input_type = &video_i420_1280, .output_type = &video_video_processor_rgb32, .sink_method = -1, .source_method = -1, - .expected_result = S_OK, - .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_INVALID_INPUT | LOADER_SET_MEDIA_TYPES | LOADER_EXPECTED_CONVERTER, + .expected_result = S_OK, .converter_class = CLSID_CColorConvertDMO, + .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_INVALID_INPUT | LOADER_SET_MEDIA_TYPES, }, }; @@ -3536,14 +3570,14 @@ todo_wine { ok(value == MF_TOPOLOGY_RESOLUTION_SUCCEEDED, "Unexpected value %#x.\n", value); } count = 2; - if (test->flags & LOADER_EXPECTED_DECODER) + if (!IsEqualGUID(&test->decoder_class, &GUID_NULL)) count++; - if (test->flags & LOADER_EXPECTED_CONVERTER) + if (!IsEqualGUID(&test->converter_class, &GUID_NULL)) count++; hr = IMFTopology_GetNodeCount(full_topology, &node_count); ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); - todo_wine_if(test->flags & LOADER_EXPECTED_DECODER) + todo_wine_if(!IsEqualGUID(&test->decoder_class, &GUID_NULL)) ok(node_count == count, "Unexpected node count %u.\n", node_count); hr = IMFTopologyNode_GetTopoNodeID(src_node, &node_id); @@ -3558,29 +3592,31 @@ todo_wine { hr = IMFTopology_GetNodeByID(full_topology, node_id, &sink_node2); ok(hr == S_OK, "Failed to get sink in resolved topology, hr %#lx.\n", hr); - if (test->flags & (LOADER_EXPECTED_DECODER | LOADER_EXPECTED_CONVERTER)) + if (!IsEqualGUID(&test->decoder_class, &GUID_NULL)) { + GUID class_id; + hr = IMFTopologyNode_GetOutput(src_node2, 0, &mft_node, &index); - ok(hr == S_OK, "Failed to get transform node in resolved topology, hr %#lx.\n", hr); + ok(hr == S_OK, "Failed to get decoder in resolved topology, hr %#lx.\n", hr); ok(!index, "Unexpected stream index %lu.\n", index); hr = IMFTopologyNode_GetNodeType(mft_node, &node_type); ok(hr == S_OK, "Failed to get transform node type in resolved topology, hr %#lx.\n", hr); ok(node_type == MF_TOPOLOGY_TRANSFORM_NODE, "Unexpected node type %u.\n", node_type); - hr = IMFTopologyNode_GetObject(mft_node, &node_object); - ok(hr == S_OK, "Failed to get object of transform node, hr %#lx.\n", hr); - - if (test->flags & LOADER_EXPECTED_DECODER) - { - value = 0; - hr = IMFTopologyNode_GetUINT32(mft_node, &MF_TOPONODE_DECODER, &value); - ok(hr == S_OK, "Failed to get attribute, hr %#lx.\n", hr); - ok(value == 1, "Unexpected value.\n"); - } + value = 0; + hr = IMFTopologyNode_GetUINT32(mft_node, &MF_TOPONODE_DECODER, &value); + ok(hr == S_OK, "Failed to get attribute, hr %#lx.\n", hr); + ok(value == 1, "Unexpected value.\n"); - hr = IMFTopologyNode_GetItem(mft_node, &MF_TOPONODE_TRANSFORM_OBJECTID, NULL); + class_id = GUID_NULL; + hr = IMFTopologyNode_GetGUID(mft_node, &MF_TOPONODE_TRANSFORM_OBJECTID, &class_id); ok(hr == S_OK, "Failed to get attribute, hr %#lx.\n", hr); + ok(IsEqualGUID(&class_id, &test->decoder_class), "got MF_TOPONODE_TRANSFORM_OBJECTID %s.\n", debugstr_guid(&class_id)); + + hr = IMFTopologyNode_GetObject(mft_node, &node_object); + ok(hr == S_OK, "Failed to get object of transform node, hr %#lx.\n", hr); + IMFTopologyNode_Release(mft_node); hr = IUnknown_QueryInterface(node_object, &IID_IMFTransform, (void **)&transform); ok(hr == S_OK, "Failed to get IMFTransform from transform node's object, hr %#lx.\n", hr); @@ -3588,51 +3624,75 @@ todo_wine { hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); ok(hr == S_OK, "Failed to get transform input type, hr %#lx.\n", hr); - hr = IMFMediaType_Compare(input_type, (IMFAttributes *)media_type, MF_ATTRIBUTES_MATCH_OUR_ITEMS, &ret); ok(hr == S_OK, "Failed to compare media types, hr %#lx.\n", hr); ok(ret, "Input type of first transform doesn't match source node type.\n"); + IMFMediaType_Release(media_type); - IMFTopologyNode_Release(mft_node); + hr = IMFTransform_GetOutputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Failed to get transform input type, hr %#lx.\n", hr); + if (IsEqualGUID(&test->converter_class, &GUID_NULL)) + { + hr = IMFMediaType_Compare(output_type, (IMFAttributes *)media_type, MF_ATTRIBUTES_MATCH_OUR_ITEMS, &ret); + ok(hr == S_OK, "Failed to compare media types, hr %#lx.\n", hr); + ok(ret, "Output type of first transform doesn't match sink node type.\n"); + } + else if (test->decoded_type) + { + check_media_type(media_type, *test->decoded_type, -1); + } IMFMediaType_Release(media_type); + IMFTransform_Release(transform); + } + + if (!IsEqualGUID(&test->converter_class, &GUID_NULL)) + { + GUID class_id; hr = IMFTopologyNode_GetInput(sink_node2, 0, &mft_node, &index); - ok(hr == S_OK, "Failed to get transform node in resolved topology, hr %#lx.\n", hr); + ok(hr == S_OK, "Failed to get decoder in resolved topology, hr %#lx.\n", hr); ok(!index, "Unexpected stream index %lu.\n", index); hr = IMFTopologyNode_GetNodeType(mft_node, &node_type); ok(hr == S_OK, "Failed to get transform node type in resolved topology, hr %#lx.\n", hr); ok(node_type == MF_TOPOLOGY_TRANSFORM_NODE, "Unexpected node type %u.\n", node_type); - hr = IMFTopologyNode_GetItem(mft_node, &MF_TOPONODE_TRANSFORM_OBJECTID, NULL); + class_id = GUID_NULL; + hr = IMFTopologyNode_GetGUID(mft_node, &MF_TOPONODE_TRANSFORM_OBJECTID, &class_id); ok(hr == S_OK, "Failed to get attribute, hr %#lx.\n", hr); + todo_wine_if(IsEqualGUID(&test->converter_class, &CLSID_CColorConvertDMO)) + ok(IsEqualGUID(&class_id, &test->converter_class), "got MF_TOPONODE_TRANSFORM_OBJECTID %s.\n", debugstr_guid(&class_id)); hr = IMFTopologyNode_GetObject(mft_node, &node_object); ok(hr == S_OK, "Failed to get object of transform node, hr %#lx.\n", hr); + IMFTopologyNode_Release(mft_node); - hr = IUnknown_QueryInterface(node_object, &IID_IMFTransform, (void**) &transform); + hr = IUnknown_QueryInterface(node_object, &IID_IMFTransform, (void **)&transform); ok(hr == S_OK, "Failed to get IMFTransform from transform node's object, hr %#lx.\n", hr); IUnknown_Release(node_object); - hr = IMFTransform_GetOutputCurrentType(transform, 0, &media_type); - ok(hr == S_OK, "Failed to get transform output type, hr %#lx.\n", hr); - hr = IMFMediaType_Compare(output_type, (IMFAttributes *)media_type, MF_ATTRIBUTES_MATCH_OUR_ITEMS, &ret); - ok(hr == S_OK, "Failed to compare media types, hr %#lx.\n", hr); - ok(ret, "Output type of last transform doesn't match sink node type.\n"); - IMFMediaType_Release(media_type); - hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); ok(hr == S_OK, "Failed to get transform input type, hr %#lx.\n", hr); - if ((test->flags & (LOADER_EXPECTED_CONVERTER | LOADER_EXPECTED_DECODER)) != (LOADER_EXPECTED_CONVERTER | LOADER_EXPECTED_DECODER)) + if (IsEqualGUID(&test->decoder_class, &GUID_NULL)) { hr = IMFMediaType_Compare(input_type, (IMFAttributes *)media_type, MF_ATTRIBUTES_MATCH_OUR_ITEMS, &ret); ok(hr == S_OK, "Failed to compare media types, hr %#lx.\n", hr); - ok(ret, "Input type of transform doesn't match source node type.\n"); + ok(ret, "Input type of last transform doesn't match source node type.\n"); + } + else if (test->decoded_type) + { + check_media_type(media_type, *test->decoded_type, -1); } IMFMediaType_Release(media_type); - IMFTopologyNode_Release(mft_node); + hr = IMFTransform_GetOutputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Failed to get transform input type, hr %#lx.\n", hr); + hr = IMFMediaType_Compare(output_type, (IMFAttributes *)media_type, MF_ATTRIBUTES_MATCH_OUR_ITEMS, &ret); + ok(hr == S_OK, "Failed to compare media types, hr %#lx.\n", hr); + ok(ret, "Output type of last transform doesn't match sink node type.\n"); + IMFMediaType_Release(media_type); + IMFTransform_Release(transform); } From 7a6231bb22a69e310331b895195d1a53dddce5fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 7 Jun 2023 11:35:17 +0200 Subject: [PATCH 018/413] mf/topology_loader: Use a local variable for the indirect connection method. Instead of modifying the method_mask parameter, in case we need to retry with another transform. (cherry picked from commit 0dab1ffdf945fc28badb106cffb8f7fb88e9c930) --- dlls/mf/topology_loader.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dlls/mf/topology_loader.c b/dlls/mf/topology_loader.c index def7c28089c..c0d7b8ad8c9 100644 --- a/dlls/mf/topology_loader.c +++ b/dlls/mf/topology_loader.c @@ -296,6 +296,7 @@ static HRESULT topology_branch_connect_indirect(IMFTopology *topology, MF_CONNEC { struct topology_branch down_branch = {.up.node = node, .down = branch->down}; struct topology_branch up_branch = {.up = branch->up, .down.node = node}; + MF_CONNECT_METHOD method = method_mask; if (FAILED(IMFActivate_ActivateObject(activates[i], &IID_IMFTransform, (void **)&transform))) continue; @@ -313,12 +314,12 @@ static HRESULT topology_branch_connect_indirect(IMFTopology *topology, MF_CONNEC if (SUCCEEDED(hr)) hr = IMFTransform_SetOutputType(transform, 0, down_type, 0); if (SUCCEEDED(hr)) - method_mask = MF_CONNECT_DIRECT; + method = MF_CONNECT_DIRECT; } IMFTransform_Release(transform); if (SUCCEEDED(hr)) - hr = topology_branch_connect(topology, method_mask, &down_branch, !down_type); + hr = topology_branch_connect(topology, method, &down_branch, !down_type); if (SUCCEEDED(hr)) hr = IMFTopology_AddNode(topology, node); if (SUCCEEDED(hr)) From 6bf4f3045c2d054928c6482858eb422419f2c920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 19 Jan 2024 12:09:11 +0100 Subject: [PATCH 019/413] mf/topology_loader: Ignore SetOutputType errors when doing indirect connect. It only succeeds with converters, or if a decoder can directly output the downstream media type. If it fails we may have to add a converter after the decoder, and we will call topology_branch_connect on the downstream branch for that. (cherry picked from commit 2a185126caf6c1d94763ecae4a13c63de9e680f2) --- dlls/mf/tests/mf.c | 15 +++++++++++++-- dlls/mf/topology_loader.c | 7 ++----- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index f5a7821f0f8..bcfb0e3dbcf 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -3293,7 +3293,6 @@ static void test_topology_loader(void) .input_type = &video_h264_1280, .output_type = &video_video_processor_1280_rgb32, .sink_method = -1, .source_method = -1, .decoded_type = &video_nv12_1280, .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, .converter_class = CLSID_CColorConvertDMO, - .flags = LOADER_TODO, }, { /* RGB32 -> Any Video, no current output type */ @@ -3577,7 +3576,7 @@ todo_wine { hr = IMFTopology_GetNodeCount(full_topology, &node_count); ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); - todo_wine_if(!IsEqualGUID(&test->decoder_class, &GUID_NULL)) + todo_wine_if(IsEqualGUID(&test->decoder_class, &CLSID_CMP3DecMediaObject)) ok(node_count == count, "Unexpected node count %u.\n", node_count); hr = IMFTopologyNode_GetTopoNodeID(src_node, &node_id); @@ -3623,14 +3622,21 @@ todo_wine { IUnknown_Release(node_object); hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); + todo_wine ok(hr == S_OK, "Failed to get transform input type, hr %#lx.\n", hr); + if (hr == S_OK) + { hr = IMFMediaType_Compare(input_type, (IMFAttributes *)media_type, MF_ATTRIBUTES_MATCH_OUR_ITEMS, &ret); ok(hr == S_OK, "Failed to compare media types, hr %#lx.\n", hr); ok(ret, "Input type of first transform doesn't match source node type.\n"); IMFMediaType_Release(media_type); + } hr = IMFTransform_GetOutputCurrentType(transform, 0, &media_type); + todo_wine ok(hr == S_OK, "Failed to get transform input type, hr %#lx.\n", hr); + if (hr == S_OK) + { if (IsEqualGUID(&test->converter_class, &GUID_NULL)) { hr = IMFMediaType_Compare(output_type, (IMFAttributes *)media_type, MF_ATTRIBUTES_MATCH_OUR_ITEMS, &ret); @@ -3642,6 +3648,7 @@ todo_wine { check_media_type(media_type, *test->decoded_type, -1); } IMFMediaType_Release(media_type); + } IMFTransform_Release(transform); } @@ -3702,13 +3709,17 @@ todo_wine { hr = IMFTopology_SetUINT32(full_topology, &IID_IMFTopology, 123); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFTopoLoader_Load(loader, full_topology, &topology2, NULL); + todo_wine_if(IsEqualGUID(&test->decoder_class, &CLSID_MSH264DecoderMFT)) ok(hr == S_OK, "Failed to resolve topology, hr %#lx.\n", hr); + if (hr == S_OK) + { ok(full_topology != topology2, "Unexpected instance.\n"); hr = IMFTopology_GetUINT32(topology2, &IID_IMFTopology, &value); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ref = IMFTopology_Release(topology2); ok(ref == 0, "Release returned %ld\n", ref); + } ref = IMFTopology_Release(full_topology); ok(ref == 0, "Release returned %ld\n", ref); } diff --git a/dlls/mf/topology_loader.c b/dlls/mf/topology_loader.c index c0d7b8ad8c9..2356c703937 100644 --- a/dlls/mf/topology_loader.c +++ b/dlls/mf/topology_loader.c @@ -309,11 +309,8 @@ static HRESULT topology_branch_connect_indirect(IMFTopology *topology, MF_CONNEC hr = topology_branch_connect_down(topology, MF_CONNECT_DIRECT, &up_branch, up_type); if (down_type) { - if (SUCCEEDED(hr)) - hr = topology_branch_fill_media_type(up_type, down_type); - if (SUCCEEDED(hr)) - hr = IMFTransform_SetOutputType(transform, 0, down_type, 0); - if (SUCCEEDED(hr)) + if (SUCCEEDED(topology_branch_fill_media_type(up_type, down_type)) + && SUCCEEDED(IMFTransform_SetOutputType(transform, 0, down_type, 0))) method = MF_CONNECT_DIRECT; } IMFTransform_Release(transform); From 6e5230c186bf8a8664cca47d7bfd45b91534f81e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 19 Jan 2024 12:09:11 +0100 Subject: [PATCH 020/413] mf/topology_loader: Initialize transform output type before adding converter. Otherwise the next topology_branch_connect call will fail when it will try to lookup the upstream element media type. (cherry picked from commit d95d1132302a9e5556668147c32e1191b5fd6ab4) --- dlls/mf/tests/mf.c | 4 ---- dlls/mf/topology_loader.c | 9 +++++++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index bcfb0e3dbcf..16e4300c3c8 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -3633,10 +3633,7 @@ todo_wine { } hr = IMFTransform_GetOutputCurrentType(transform, 0, &media_type); - todo_wine ok(hr == S_OK, "Failed to get transform input type, hr %#lx.\n", hr); - if (hr == S_OK) - { if (IsEqualGUID(&test->converter_class, &GUID_NULL)) { hr = IMFMediaType_Compare(output_type, (IMFAttributes *)media_type, MF_ATTRIBUTES_MATCH_OUR_ITEMS, &ret); @@ -3648,7 +3645,6 @@ todo_wine { check_media_type(media_type, *test->decoded_type, -1); } IMFMediaType_Release(media_type); - } IMFTransform_Release(transform); } diff --git a/dlls/mf/topology_loader.c b/dlls/mf/topology_loader.c index 2356c703937..386d6978e6c 100644 --- a/dlls/mf/topology_loader.c +++ b/dlls/mf/topology_loader.c @@ -297,6 +297,7 @@ static HRESULT topology_branch_connect_indirect(IMFTopology *topology, MF_CONNEC struct topology_branch down_branch = {.up.node = node, .down = branch->down}; struct topology_branch up_branch = {.up = branch->up, .down.node = node}; MF_CONNECT_METHOD method = method_mask; + IMFMediaType *media_type; if (FAILED(IMFActivate_ActivateObject(activates[i], &IID_IMFTransform, (void **)&transform))) continue; @@ -315,6 +316,14 @@ static HRESULT topology_branch_connect_indirect(IMFTopology *topology, MF_CONNEC } IMFTransform_Release(transform); + if (SUCCEEDED(hr) && method != MF_CONNECT_DIRECT + && SUCCEEDED(IMFTransform_GetOutputAvailableType(transform, 0, 0, &media_type))) + { + if (SUCCEEDED(topology_branch_fill_media_type(up_type, media_type))) + IMFTransform_SetOutputType(transform, 0, media_type, 0); + IMFMediaType_Release(media_type); + } + if (SUCCEEDED(hr)) hr = topology_branch_connect(topology, method, &down_branch, !down_type); if (SUCCEEDED(hr)) From 4a9036dac15eacf044d3d3985cf0d22514f42343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 24 Jan 2024 15:27:25 +0100 Subject: [PATCH 021/413] mf/topology_loader: Try to connect transform nodes with their current types first. And only if that fails try again by enumerating types. (cherry picked from commit 2d88c5771611ad43767d19dc5b4fe990ec252304) --- dlls/mf/tests/mf.c | 4 ---- dlls/mf/topology_loader.c | 7 +++++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 16e4300c3c8..3f51e563624 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -3705,17 +3705,13 @@ todo_wine { hr = IMFTopology_SetUINT32(full_topology, &IID_IMFTopology, 123); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFTopoLoader_Load(loader, full_topology, &topology2, NULL); - todo_wine_if(IsEqualGUID(&test->decoder_class, &CLSID_MSH264DecoderMFT)) ok(hr == S_OK, "Failed to resolve topology, hr %#lx.\n", hr); - if (hr == S_OK) - { ok(full_topology != topology2, "Unexpected instance.\n"); hr = IMFTopology_GetUINT32(topology2, &IID_IMFTopology, &value); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ref = IMFTopology_Release(topology2); ok(ref == 0, "Release returned %ld\n", ref); - } ref = IMFTopology_Release(full_topology); ok(ref == 0, "Release returned %ld\n", ref); } diff --git a/dlls/mf/topology_loader.c b/dlls/mf/topology_loader.c index 386d6978e6c..a56fb3e3909 100644 --- a/dlls/mf/topology_loader.c +++ b/dlls/mf/topology_loader.c @@ -528,8 +528,11 @@ static HRESULT topology_loader_resolve_branches(struct topoloader_context *conte else if (FAILED(hr = topology_branch_clone_nodes(context, branch))) WARN("Failed to clone nodes for branch %s\n", debugstr_topology_branch(branch)); else - hr = topology_branch_connect(context->output_topology, MF_CONNECT_ALLOW_DECODER, - branch, enumerate_source_types || node_type == MF_TOPOLOGY_TRANSFORM_NODE); + { + hr = topology_branch_connect(context->output_topology, MF_CONNECT_ALLOW_DECODER, branch, enumerate_source_types); + if (hr == MF_E_INVALIDMEDIATYPE && !enumerate_source_types && node_type == MF_TOPOLOGY_TRANSFORM_NODE) + hr = topology_branch_connect(context->output_topology, MF_CONNECT_ALLOW_DECODER, branch, TRUE); + } topology_branch_destroy(branch); if (FAILED(hr)) From 7989ff796762c9a26220bbbc0f590275e067e9f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 24 Jan 2024 14:47:55 +0100 Subject: [PATCH 022/413] winegstreamer: Implement H264 decoder GetInputCurrentType. (cherry picked from commit 6aca31f1a1662ec9a2ab720c31ea981f31af4d7f) --- dlls/mf/tests/mf.c | 4 ---- dlls/mf/tests/transform.c | 2 +- dlls/winegstreamer/h264_decoder.c | 16 +++++++++++++--- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 3f51e563624..0a34329bd75 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -3622,15 +3622,11 @@ todo_wine { IUnknown_Release(node_object); hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); - todo_wine ok(hr == S_OK, "Failed to get transform input type, hr %#lx.\n", hr); - if (hr == S_OK) - { hr = IMFMediaType_Compare(input_type, (IMFAttributes *)media_type, MF_ATTRIBUTES_MATCH_OUR_ITEMS, &ret); ok(hr == S_OK, "Failed to compare media types, hr %#lx.\n", hr); ok(ret, "Input type of first transform doesn't match source node type.\n"); IMFMediaType_Release(media_type); - } hr = IMFTransform_GetOutputCurrentType(transform, 0, &media_type); ok(hr == S_OK, "Failed to get transform input type, hr %#lx.\n", hr); diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index f9e6b3b45cd..06e638bea16 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -4262,7 +4262,7 @@ static void test_h264_decoder(void) check_mft_set_input_type_required(transform, input_type_desc); check_mft_set_input_type(transform, input_type_desc); - check_mft_get_input_current_type_(transform, expect_input_type_desc, TRUE, FALSE); + check_mft_get_input_current_type_(transform, expect_input_type_desc, FALSE, TRUE); check_mft_get_input_stream_info(transform, S_OK, &input_info); check_mft_get_output_stream_info(transform, S_OK, &output_info); diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index 5a098c6e467..958bf9a9982 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -580,8 +580,18 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF static HRESULT WINAPI transform_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) { - FIXME("iface %p, id %#lx, type %p stub!\n", iface, id, type); - return E_NOTIMPL; + struct h264_decoder *decoder = impl_from_IMFTransform(iface); + HRESULT hr; + + TRACE("iface %p, id %#lx, type %p\n", iface, id, type); + + if (!decoder->input_type) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (FAILED(hr = MFCreateMediaType(type))) + return hr; + + return IMFMediaType_CopyAllItems(decoder->input_type, (IMFAttributes *)*type); } static HRESULT WINAPI transform_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) @@ -589,7 +599,7 @@ static HRESULT WINAPI transform_GetOutputCurrentType(IMFTransform *iface, DWORD struct h264_decoder *decoder = impl_from_IMFTransform(iface); HRESULT hr; - FIXME("iface %p, id %#lx, type %p stub!\n", iface, id, type); + TRACE("iface %p, id %#lx, type %p\n", iface, id, type); if (!decoder->output_type) return MF_E_TRANSFORM_TYPE_NOT_SET; From 39829319b38701bcde92d877c8e90de1ceff6f1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 30 Jan 2024 17:05:11 +0100 Subject: [PATCH 023/413] winegstreamer: Trace wg_transform input and output caps. (cherry picked from commit 6dae92cfb916847f7cf1160e74d1c97553d3bced) --- dlls/winegstreamer/wg_transform.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 1f6bdb103d6..d429890cb59 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -328,6 +328,8 @@ NTSTATUS wg_transform_create(void *args) if (!transform->my_src) goto out; + GST_INFO("transform %p input caps %"GST_PTR_FORMAT, transform, src_caps); + gst_pad_set_element_private(transform->my_src, transform); gst_pad_set_query_function(transform->my_src, transform_src_query_cb); @@ -340,6 +342,8 @@ NTSTATUS wg_transform_create(void *args) if (!transform->my_sink) goto out; + GST_INFO("transform %p output caps %"GST_PTR_FORMAT, transform, transform->output_caps); + gst_pad_set_element_private(transform->my_sink, transform); gst_pad_set_event_function(transform->my_sink, transform_sink_event_cb); gst_pad_set_query_function(transform->my_sink, transform_sink_query_cb); @@ -537,6 +541,8 @@ NTSTATUS wg_transform_set_output_format(void *args) } transform->output_format = *format; + GST_INFO("transform %p output caps %"GST_PTR_FORMAT, transform, caps); + if (gst_caps_is_always_compatible(transform->output_caps, caps)) { gst_caps_unref(caps); @@ -827,6 +833,8 @@ NTSTATUS wg_transform_read_data(void *args) { GST_MINI_OBJECT_FLAG_UNSET(transform->output_sample, GST_SAMPLE_FLAG_WG_CAPS_CHANGED); + GST_INFO("transform %p output caps %"GST_PTR_FORMAT, transform, output_caps); + if (format) { gsize plane_align = transform->attrs.output_plane_align; From 6330df9ed342641003b72ce358725478caff1bcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 30 Jan 2024 17:16:10 +0100 Subject: [PATCH 024/413] winegstreamer: Handle allocation query in a separate helper. (cherry picked from commit ec759079a58599da90a23c83e879e33b74a4b7cf) --- dlls/winegstreamer/wg_transform.c | 114 ++++++++++++++++-------------- 1 file changed, 59 insertions(+), 55 deletions(-) diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index d429890cb59..baa0b963348 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -125,74 +125,78 @@ static gboolean transform_src_query_cb(GstPad *pad, GstObject *parent, GstQuery } } -static gboolean transform_sink_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) +static gboolean transform_sink_query_allocation(struct wg_transform *transform, GstQuery *query) { - struct wg_transform *transform = gst_pad_get_element_private(pad); + gsize plane_align = transform->attrs.output_plane_align; + GstStructure *config, *params; + GstVideoAlignment align; + gboolean needs_pool; + GstBufferPool *pool; + GstVideoInfo info; + GstCaps *caps; - GST_LOG("transform %p, type \"%s\".", transform, gst_query_type_get_name(query->type)); + gst_query_parse_allocation(query, &caps, &needs_pool); + if (stream_type_from_caps(caps) != GST_STREAM_TYPE_VIDEO || !needs_pool) + return false; - switch (query->type) - { - case GST_QUERY_ALLOCATION: - { - gsize plane_align = transform->attrs.output_plane_align; - GstStructure *config, *params; - GstVideoAlignment align; - gboolean needs_pool; - GstBufferPool *pool; - GstVideoInfo info; - GstCaps *caps; + if (!gst_video_info_from_caps(&info, caps) + || !(pool = gst_video_buffer_pool_new())) + return false; - gst_query_parse_allocation(query, &caps, &needs_pool); - if (stream_type_from_caps(caps) != GST_STREAM_TYPE_VIDEO || !needs_pool) - break; + align_video_info_planes(plane_align, &info, &align); - if (!gst_video_info_from_caps(&info, caps) - || !(pool = gst_video_buffer_pool_new())) - break; + if ((params = gst_structure_new("video-meta", + "padding-top", G_TYPE_UINT, align.padding_top, + "padding-bottom", G_TYPE_UINT, align.padding_bottom, + "padding-left", G_TYPE_UINT, align.padding_left, + "padding-right", G_TYPE_UINT, align.padding_right, + NULL))) + { + gst_query_add_allocation_meta(query, GST_VIDEO_META_API_TYPE, params); + gst_structure_free(params); + } + + if (!(config = gst_buffer_pool_get_config(pool))) + GST_ERROR("Failed to get pool %p config.", pool); + else + { + gst_buffer_pool_config_add_option(config, GST_BUFFER_POOL_OPTION_VIDEO_META); + gst_buffer_pool_config_add_option(config, GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT); + gst_buffer_pool_config_set_video_alignment(config, &align); - align_video_info_planes(plane_align, &info, &align); + gst_buffer_pool_config_set_params(config, caps, + info.size, 0, 0); + gst_buffer_pool_config_set_allocator(config, transform->allocator, NULL); + if (!gst_buffer_pool_set_config(pool, config)) + GST_ERROR("Failed to set pool %p config.", pool); + } - if ((params = gst_structure_new("video-meta", - "padding-top", G_TYPE_UINT, align.padding_top, - "padding-bottom", G_TYPE_UINT, align.padding_bottom, - "padding-left", G_TYPE_UINT, align.padding_left, - "padding-right", G_TYPE_UINT, align.padding_right, - NULL))) - { - gst_query_add_allocation_meta(query, GST_VIDEO_META_API_TYPE, params); - gst_structure_free(params); - } + /* Prevent pool reconfiguration, we don't want another alignment. */ + if (!gst_buffer_pool_set_active(pool, true)) + GST_ERROR("Pool %p failed to activate.", pool); - if (!(config = gst_buffer_pool_get_config(pool))) - GST_ERROR("Failed to get pool %p config.", pool); - else - { - gst_buffer_pool_config_add_option(config, GST_BUFFER_POOL_OPTION_VIDEO_META); - gst_buffer_pool_config_add_option(config, GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT); - gst_buffer_pool_config_set_video_alignment(config, &align); - - gst_buffer_pool_config_set_params(config, caps, - info.size, 0, 0); - gst_buffer_pool_config_set_allocator(config, transform->allocator, NULL); - if (!gst_buffer_pool_set_config(pool, config)) - GST_ERROR("Failed to set pool %p config.", pool); - } + gst_query_add_allocation_pool(query, pool, info.size, 0, 0); + gst_query_add_allocation_param(query, transform->allocator, NULL); - /* Prevent pool reconfiguration, we don't want another alignment. */ - if (!gst_buffer_pool_set_active(pool, true)) - GST_ERROR("Pool %p failed to activate.", pool); + GST_INFO("Proposing pool %p, buffer size %#zx, allocator %p, for query %p.", + pool, info.size, transform->allocator, query); - gst_query_add_allocation_pool(query, pool, info.size, 0, 0); - gst_query_add_allocation_param(query, transform->allocator, NULL); + g_object_unref(pool); + return true; +} - GST_INFO("Proposing pool %p, buffer size %#zx, allocator %p, for query %p.", - pool, info.size, transform->allocator, query); +static gboolean transform_sink_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) +{ + struct wg_transform *transform = gst_pad_get_element_private(pad); - g_object_unref(pool); - return true; - } + GST_LOG("transform %p, type \"%s\".", transform, gst_query_type_get_name(query->type)); + switch (query->type) + { + case GST_QUERY_ALLOCATION: + if (transform_sink_query_allocation(transform, query)) + return true; + break; case GST_QUERY_CAPS: { GstCaps *caps, *filter, *temp; From a2f3fcfd1985ecec3ff0770f9fc2528ff9e485f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 30 Jan 2024 17:17:25 +0100 Subject: [PATCH 025/413] winegstreamer: Handle sink caps query in a separate helper. (cherry picked from commit f39156ee6a0950cb71fabd4f50a768510f7e2366) --- dlls/winegstreamer/wg_transform.c | 48 +++++++++++++++++-------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index baa0b963348..7553c96053f 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -185,6 +185,30 @@ static gboolean transform_sink_query_allocation(struct wg_transform *transform, return true; } +static gboolean transform_sink_query_caps(struct wg_transform *transform, GstQuery *query) +{ + GstCaps *caps, *filter, *temp; + + GST_LOG("transform %p, query %"GST_PTR_FORMAT, transform, query); + + gst_query_parse_caps(query, &filter); + if (!(caps = wg_format_to_caps(&transform->output_format))) + return false; + + if (filter) + { + temp = gst_caps_intersect(caps, filter); + gst_caps_unref(caps); + caps = temp; + } + + GST_INFO("Returning caps %" GST_PTR_FORMAT, caps); + + gst_query_set_caps_result(query, caps); + gst_caps_unref(caps); + return true; +} + static gboolean transform_sink_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) { struct wg_transform *transform = gst_pad_get_element_private(pad); @@ -198,27 +222,9 @@ static gboolean transform_sink_query_cb(GstPad *pad, GstObject *parent, GstQuery return true; break; case GST_QUERY_CAPS: - { - GstCaps *caps, *filter, *temp; - - gst_query_parse_caps(query, &filter); - if (!(caps = wg_format_to_caps(&transform->output_format))) - break; - - if (filter) - { - temp = gst_caps_intersect(caps, filter); - gst_caps_unref(caps); - caps = temp; - } - - GST_INFO("Returning caps %" GST_PTR_FORMAT, caps); - - gst_query_set_caps_result(query, caps); - gst_caps_unref(caps); - return true; - } - + if (transform_sink_query_caps(transform, query)) + return true; + break; default: GST_WARNING("Ignoring \"%s\" query.", gst_query_type_get_name(query->type)); break; From ff56a0496a58c0b07c68f4dc31195f4f8cc239c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 30 Jan 2024 17:20:07 +0100 Subject: [PATCH 026/413] winegstreamer: Handle sink event caps in a separate helper. (cherry picked from commit 4f349d442a14b0cec0bdd25ac8d0ddfcd3a1e71e) --- dlls/winegstreamer/wg_transform.c | 35 +++++++++++++++++-------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 7553c96053f..e27db5c4bbe 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -233,6 +233,19 @@ static gboolean transform_sink_query_cb(GstPad *pad, GstObject *parent, GstQuery return gst_pad_query_default(pad, parent, query); } +static void transform_sink_event_caps(struct wg_transform *transform, GstEvent *event) +{ + GstCaps *caps; + + gst_event_parse_caps(event, &caps); + + transform->output_caps_changed = transform->output_caps_changed + || !gst_caps_is_always_compatible(transform->output_caps, caps); + + gst_caps_unref(transform->output_caps); + transform->output_caps = gst_caps_ref(caps); +} + static gboolean transform_sink_event_cb(GstPad *pad, GstObject *parent, GstEvent *event) { struct wg_transform *transform = gst_pad_get_element_private(pad); @@ -241,22 +254,12 @@ static gboolean transform_sink_event_cb(GstPad *pad, GstObject *parent, GstEvent switch (event->type) { - case GST_EVENT_CAPS: - { - GstCaps *caps; - - gst_event_parse_caps(event, &caps); - - transform->output_caps_changed = transform->output_caps_changed - || !gst_caps_is_always_compatible(transform->output_caps, caps); - - gst_caps_unref(transform->output_caps); - transform->output_caps = gst_caps_ref(caps); - break; - } - default: - GST_WARNING("Ignoring \"%s\" event.", GST_EVENT_TYPE_NAME(event)); - break; + case GST_EVENT_CAPS: + transform_sink_event_caps(transform, event); + break; + default: + GST_WARNING("Ignoring \"%s\" event.", GST_EVENT_TYPE_NAME(event)); + break; } gst_event_unref(event); From 7caa268afa0602f6a7f89af031098c0025edda80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 30 Jan 2024 17:24:49 +0100 Subject: [PATCH 027/413] winegstreamer: Use GST_PTR_FORMAT to trace GStreamer objects. (cherry picked from commit 62955f22295e69539f14d808adf179afd9583e5a) --- dlls/winegstreamer/wg_transform.c | 53 ++++++++++++++++--------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index e27db5c4bbe..10541c012e4 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -87,7 +87,7 @@ static GstFlowReturn transform_sink_chain_cb(GstPad *pad, GstObject *parent, Gst struct wg_transform *transform = gst_pad_get_element_private(pad); GstSample *sample; - GST_LOG("transform %p, buffer %p.", transform, buffer); + GST_LOG("transform %p, %"GST_PTR_FORMAT, transform, buffer); if (!(sample = gst_sample_new(buffer, transform->output_caps, NULL, NULL))) { @@ -107,7 +107,7 @@ static GstFlowReturn transform_sink_chain_cb(GstPad *pad, GstObject *parent, Gst static gboolean transform_src_query_latency(struct wg_transform *transform, GstQuery *query) { - GST_LOG("transform %p, query %p", transform, query); + GST_LOG("transform %p, %"GST_PTR_FORMAT, transform, query); gst_query_set_latency(query, transform->attrs.low_latency, 0, 0); return true; } @@ -121,6 +121,7 @@ static gboolean transform_src_query_cb(GstPad *pad, GstObject *parent, GstQuery case GST_QUERY_LATENCY: return transform_src_query_latency(transform, query); default: + GST_TRACE("transform %p, ignoring %"GST_PTR_FORMAT, transform, query); return gst_pad_query_default(pad, parent, query); } } @@ -135,6 +136,8 @@ static gboolean transform_sink_query_allocation(struct wg_transform *transform, GstVideoInfo info; GstCaps *caps; + GST_LOG("transform %p, %"GST_PTR_FORMAT, transform, query); + gst_query_parse_allocation(query, &caps, &needs_pool); if (stream_type_from_caps(caps) != GST_STREAM_TYPE_VIDEO || !needs_pool) return false; @@ -157,7 +160,7 @@ static gboolean transform_sink_query_allocation(struct wg_transform *transform, } if (!(config = gst_buffer_pool_get_config(pool))) - GST_ERROR("Failed to get pool %p config.", pool); + GST_ERROR("Failed to get %"GST_PTR_FORMAT" config.", pool); else { gst_buffer_pool_config_add_option(config, GST_BUFFER_POOL_OPTION_VIDEO_META); @@ -168,17 +171,17 @@ static gboolean transform_sink_query_allocation(struct wg_transform *transform, info.size, 0, 0); gst_buffer_pool_config_set_allocator(config, transform->allocator, NULL); if (!gst_buffer_pool_set_config(pool, config)) - GST_ERROR("Failed to set pool %p config.", pool); + GST_ERROR("Failed to set %"GST_PTR_FORMAT" config.", pool); } /* Prevent pool reconfiguration, we don't want another alignment. */ if (!gst_buffer_pool_set_active(pool, true)) - GST_ERROR("Pool %p failed to activate.", pool); + GST_ERROR("%"GST_PTR_FORMAT" failed to activate.", pool); gst_query_add_allocation_pool(query, pool, info.size, 0, 0); gst_query_add_allocation_param(query, transform->allocator, NULL); - GST_INFO("Proposing pool %p, buffer size %#zx, allocator %p, for query %p.", + GST_INFO("Proposing %"GST_PTR_FORMAT", buffer size %#zx, %"GST_PTR_FORMAT", for %"GST_PTR_FORMAT, pool, info.size, transform->allocator, query); g_object_unref(pool); @@ -189,7 +192,7 @@ static gboolean transform_sink_query_caps(struct wg_transform *transform, GstQue { GstCaps *caps, *filter, *temp; - GST_LOG("transform %p, query %"GST_PTR_FORMAT, transform, query); + GST_LOG("transform %p, %"GST_PTR_FORMAT, transform, query); gst_query_parse_caps(query, &filter); if (!(caps = wg_format_to_caps(&transform->output_format))) @@ -213,23 +216,21 @@ static gboolean transform_sink_query_cb(GstPad *pad, GstObject *parent, GstQuery { struct wg_transform *transform = gst_pad_get_element_private(pad); - GST_LOG("transform %p, type \"%s\".", transform, gst_query_type_get_name(query->type)); - switch (query->type) { - case GST_QUERY_ALLOCATION: - if (transform_sink_query_allocation(transform, query)) - return true; - break; - case GST_QUERY_CAPS: - if (transform_sink_query_caps(transform, query)) - return true; - break; - default: - GST_WARNING("Ignoring \"%s\" query.", gst_query_type_get_name(query->type)); - break; + case GST_QUERY_ALLOCATION: + if (transform_sink_query_allocation(transform, query)) + return true; + break; + case GST_QUERY_CAPS: + if (transform_sink_query_caps(transform, query)) + return true; + break; + default: + break; } + GST_TRACE("transform %p, ignoring %"GST_PTR_FORMAT, transform, query); return gst_pad_query_default(pad, parent, query); } @@ -237,6 +238,8 @@ static void transform_sink_event_caps(struct wg_transform *transform, GstEvent * { GstCaps *caps; + GST_LOG("transform %p, %"GST_PTR_FORMAT, transform, event); + gst_event_parse_caps(event, &caps); transform->output_caps_changed = transform->output_caps_changed @@ -250,15 +253,13 @@ static gboolean transform_sink_event_cb(GstPad *pad, GstObject *parent, GstEvent { struct wg_transform *transform = gst_pad_get_element_private(pad); - GST_LOG("transform %p, type \"%s\".", transform, GST_EVENT_TYPE_NAME(event)); - switch (event->type) { case GST_EVENT_CAPS: transform_sink_event_caps(transform, event); break; default: - GST_WARNING("Ignoring \"%s\" event.", GST_EVENT_TYPE_NAME(event)); + GST_TRACE("transform %p, ignoring %"GST_PTR_FORMAT, transform, event); break; } @@ -633,7 +634,7 @@ NTSTATUS wg_transform_push_data(void *args) else { InterlockedIncrement(&sample->refcount); - GST_INFO("Wrapped %u/%u bytes from sample %p to buffer %p", sample->size, sample->max_size, sample, buffer); + GST_INFO("Wrapped %u/%u bytes from sample %p to %"GST_PTR_FORMAT, sample->size, sample->max_size, sample, buffer); } if (sample->flags & WG_SAMPLE_FLAG_HAS_PTS) @@ -740,7 +741,7 @@ static NTSTATUS read_transform_output_data(GstBuffer *buffer, GstCaps *caps, gsi if (!gst_buffer_map(buffer, &info, GST_MAP_READ)) { - GST_ERROR("Failed to map buffer %p", buffer); + GST_ERROR("Failed to map buffer %"GST_PTR_FORMAT, buffer); sample->size = 0; return STATUS_UNSUCCESSFUL; } @@ -757,7 +758,7 @@ static NTSTATUS read_transform_output_data(GstBuffer *buffer, GstCaps *caps, gsi if (status) { - GST_ERROR("Failed to copy buffer %p", buffer); + GST_ERROR("Failed to copy buffer %"GST_PTR_FORMAT, buffer); sample->size = 0; return status; } From 679945b52a317180473510083a358feb68062560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 30 Jan 2024 19:25:03 +0100 Subject: [PATCH 028/413] winegstreamer: Ignore wg_transform input / output video format fps. Decoders might output some fps information and encoders might input fps, but otherwise is unnecessary and may prevent compatible caps matching. (cherry picked from commit 98b8ab9b88b77e2f8f0c51adb681094191a0be1b) --- dlls/winegstreamer/h264_decoder.c | 4 --- dlls/winegstreamer/video_decoder.c | 6 ----- dlls/winegstreamer/wg_transform.c | 39 +++++++++++++++++++++++++----- dlls/winegstreamer/wmv_decoder.c | 12 --------- 4 files changed, 33 insertions(+), 28 deletions(-) diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index 958bf9a9982..ae4d46d40a2 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -111,8 +111,6 @@ static HRESULT try_create_wg_transform(struct h264_decoder *decoder) */ output_format.u.video.width = 0; output_format.u.video.height = 0; - output_format.u.video.fps_d = 0; - output_format.u.video.fps_n = 0; if (SUCCEEDED(IMFAttributes_GetUINT32(decoder->attributes, &MF_LOW_LATENCY, &low_latency))) attrs.low_latency = !!low_latency; @@ -558,8 +556,6 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF */ output_format.u.video.width = 0; output_format.u.video.height = 0; - output_format.u.video.fps_d = 0; - output_format.u.video.fps_n = 0; if (output_format.major_type == WG_MAJOR_TYPE_UNKNOWN || !wg_transform_set_output_format(decoder->wg_transform, &output_format)) diff --git a/dlls/winegstreamer/video_decoder.c b/dlls/winegstreamer/video_decoder.c index 2faab78faf2..f24c25e03f2 100644 --- a/dlls/winegstreamer/video_decoder.c +++ b/dlls/winegstreamer/video_decoder.c @@ -84,9 +84,6 @@ static HRESULT try_create_wg_transform(struct video_decoder *decoder) if (output_format.major_type == WG_MAJOR_TYPE_UNKNOWN) return MF_E_INVALIDMEDIATYPE; - output_format.u.video.fps_d = 0; - output_format.u.video.fps_n = 0; - if (!(decoder->wg_transform = wg_transform_create(&input_format, &output_format, &attrs))) { ERR("Failed to create transform with input major_type %u.\n", input_format.major_type); @@ -311,9 +308,6 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF { mf_media_type_to_wg_format(decoder->output_type, &output_format); - output_format.u.video.fps_d = 0; - output_format.u.video.fps_n = 0; - if (output_format.major_type == WG_MAJOR_TYPE_UNKNOWN || !wg_transform_set_output_format(decoder->wg_transform, &output_format)) { diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 10541c012e4..a1f5dc5971b 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -188,6 +188,16 @@ static gboolean transform_sink_query_allocation(struct wg_transform *transform, return true; } +static GstCaps *transform_format_to_caps(struct wg_transform *transform, const struct wg_format *format) +{ + struct wg_format copy = *format; + + if (format->major_type == WG_MAJOR_TYPE_VIDEO) + copy.u.video.fps_n = copy.u.video.fps_d = 0; + + return wg_format_to_caps(©); +} + static gboolean transform_sink_query_caps(struct wg_transform *transform, GstQuery *query) { GstCaps *caps, *filter, *temp; @@ -195,7 +205,7 @@ static gboolean transform_sink_query_caps(struct wg_transform *transform, GstQue GST_LOG("transform %p, %"GST_PTR_FORMAT, transform, query); gst_query_parse_caps(query, &filter); - if (!(caps = wg_format_to_caps(&transform->output_format))) + if (!(caps = transform_format_to_caps(transform, &transform->output_format))) return false; if (filter) @@ -234,6 +244,23 @@ static gboolean transform_sink_query_cb(GstPad *pad, GstObject *parent, GstQuery return gst_pad_query_default(pad, parent, query); } +static gboolean transform_output_caps_is_compatible(struct wg_transform *transform, GstCaps *caps) +{ + GstCaps *copy = gst_caps_copy(caps); + gboolean ret; + gsize i; + + for (i = 0; i < gst_caps_get_size(copy); ++i) + { + GstStructure *structure = gst_caps_get_structure(copy, i); + gst_structure_remove_fields(structure, "framerate", NULL); + } + + ret = gst_caps_is_always_compatible(transform->output_caps, copy); + gst_caps_unref(copy); + return ret; +} + static void transform_sink_event_caps(struct wg_transform *transform, GstEvent *event) { GstCaps *caps; @@ -243,7 +270,7 @@ static void transform_sink_event_caps(struct wg_transform *transform, GstEvent * gst_event_parse_caps(event, &caps); transform->output_caps_changed = transform->output_caps_changed - || !gst_caps_is_always_compatible(transform->output_caps, caps); + || !transform_output_caps_is_compatible(transform, caps); gst_caps_unref(transform->output_caps); transform->output_caps = gst_caps_ref(caps); @@ -333,7 +360,7 @@ NTSTATUS wg_transform_create(void *args) transform->attrs = *params->attrs; transform->output_format = output_format; - if (!(src_caps = wg_format_to_caps(&input_format))) + if (!(src_caps = transform_format_to_caps(transform, &input_format))) goto out; if (!(template = gst_pad_template_new("src", GST_PAD_SRC, GST_PAD_ALWAYS, src_caps))) goto out; @@ -347,7 +374,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 = transform_format_to_caps(transform, &output_format))) goto out; if (!(template = gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_ALWAYS, transform->output_caps))) goto out; @@ -548,7 +575,7 @@ NTSTATUS wg_transform_set_output_format(void *args) GstSample *sample; GstCaps *caps; - if (!(caps = wg_format_to_caps(format))) + if (!(caps = transform_format_to_caps(transform, format))) { GST_ERROR("Failed to convert format %p to caps.", format); return STATUS_UNSUCCESSFUL; @@ -557,7 +584,7 @@ NTSTATUS wg_transform_set_output_format(void *args) GST_INFO("transform %p output caps %"GST_PTR_FORMAT, transform, caps); - if (gst_caps_is_always_compatible(transform->output_caps, caps)) + if (transform_output_caps_is_compatible(transform, caps)) { gst_caps_unref(caps); return STATUS_SUCCESS; diff --git a/dlls/winegstreamer/wmv_decoder.c b/dlls/winegstreamer/wmv_decoder.c index 48940573f08..edd6c744e94 100644 --- a/dlls/winegstreamer/wmv_decoder.c +++ b/dlls/winegstreamer/wmv_decoder.c @@ -511,16 +511,6 @@ static HRESULT WINAPI media_object_SetInputType(IMediaObject *iface, DWORD index if (!amt_to_wg_format((const AM_MEDIA_TYPE *)type, &wg_format)) return DMO_E_TYPE_NOT_ACCEPTED; - if (wg_format.major_type == WG_MAJOR_TYPE_VIDEO_WMV) - { - wg_format.u.video_wmv.fps_n = 0; - wg_format.u.video_wmv.fps_d = 0; - } - else if (wg_format.major_type == WG_MAJOR_TYPE_VIDEO) - { - wg_format.u.video.fps_n = 0; - wg_format.u.video.fps_d = 0; - } if (flags & DMO_SET_TYPEF_TEST_ONLY) return S_OK; @@ -578,8 +568,6 @@ static HRESULT WINAPI media_object_SetOutputType(IMediaObject *iface, DWORD inde if (!amt_to_wg_format((const AM_MEDIA_TYPE *)type, &wg_format)) return DMO_E_TYPE_NOT_ACCEPTED; assert(wg_format.major_type == WG_MAJOR_TYPE_VIDEO); - wg_format.u.video.fps_n = 0; - wg_format.u.video.fps_d = 0; if (flags & DMO_SET_TYPEF_TEST_ONLY) return S_OK; From 61653d9e15382f7a9222e131bbe0e83f92ec97f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 30 Jan 2024 20:21:45 +0100 Subject: [PATCH 029/413] winegstreamer: Allow wg_transform size changes with an explicit attribute. (cherry picked from commit 6979a9f059d7390e64f7de50d424e8d4363575c8) --- dlls/winegstreamer/h264_decoder.c | 13 +------------ dlls/winegstreamer/unixlib.h | 1 + dlls/winegstreamer/wg_transform.c | 4 ++++ 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index ae4d46d40a2..aff9e73d051 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -89,6 +89,7 @@ static HRESULT try_create_wg_transform(struct h264_decoder *decoder) { .output_plane_align = 15, .input_queue_length = 15, + .allow_size_change = TRUE, }; struct wg_format input_format; struct wg_format output_format; @@ -106,12 +107,6 @@ static HRESULT try_create_wg_transform(struct h264_decoder *decoder) if (output_format.major_type == WG_MAJOR_TYPE_UNKNOWN) return MF_E_INVALIDMEDIATYPE; - /* Don't force any specific size, H264 streams already have the metadata for it - * and will generate a MF_E_TRANSFORM_STREAM_CHANGE result later. - */ - output_format.u.video.width = 0; - output_format.u.video.height = 0; - if (SUCCEEDED(IMFAttributes_GetUINT32(decoder->attributes, &MF_LOW_LATENCY, &low_latency))) attrs.low_latency = !!low_latency; @@ -551,12 +546,6 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF struct wg_format output_format; mf_media_type_to_wg_format(decoder->output_type, &output_format); - /* Don't force any specific size, H264 streams already have the metadata for it - * and will generate a MF_E_TRANSFORM_STREAM_CHANGE result later. - */ - output_format.u.video.width = 0; - output_format.u.video.height = 0; - if (output_format.major_type == WG_MAJOR_TYPE_UNKNOWN || !wg_transform_set_output_format(decoder->wg_transform, &output_format)) { diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 4a7d7fc39c9..c8fabe6574b 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -351,6 +351,7 @@ struct wg_transform_attrs { UINT32 output_plane_align; UINT32 input_queue_length; + BOOL allow_size_change; BOOL low_latency; }; diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index a1f5dc5971b..557d7024246 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -193,7 +193,11 @@ static GstCaps *transform_format_to_caps(struct wg_transform *transform, const s struct wg_format copy = *format; if (format->major_type == WG_MAJOR_TYPE_VIDEO) + { + if (transform->attrs.allow_size_change) + copy.u.video.width = copy.u.video.height = 0; copy.u.video.fps_n = copy.u.video.fps_d = 0; + } return wg_format_to_caps(©); } From 0499432fdb0e551a8415d5feadccef04679b33dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 29 Jan 2024 13:13:43 +0100 Subject: [PATCH 030/413] mf/tests: Report more transform current type mismatches. (cherry picked from commit 5a12be3a9af038382559831fef764d69ff6b07b0) --- dlls/mf/tests/transform.c | 86 ++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 38 deletions(-) diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index 06e638bea16..977e819cdf8 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -709,8 +709,8 @@ static void check_mft_set_input_type(IMFTransform *transform, const struct attri IMFMediaType_Release(media_type); } -#define check_mft_get_input_current_type(a, b) check_mft_get_input_current_type_(a, b, FALSE, FALSE) -static void check_mft_get_input_current_type_(IMFTransform *transform, const struct attribute_desc *attributes, +#define check_mft_get_input_current_type(a, b) check_mft_get_input_current_type_(__LINE__, a, b, FALSE, FALSE) +static void check_mft_get_input_current_type_(int line, IMFTransform *transform, const struct attribute_desc *attributes, BOOL todo_current, BOOL todo_compare) { HRESULT hr, expect_hr = attributes ? S_OK : MF_E_TRANSFORM_TYPE_NOT_SET; @@ -719,19 +719,21 @@ static void check_mft_get_input_current_type_(IMFTransform *transform, const str hr = IMFTransform_GetInputCurrentType(transform, 0, ¤t_type); todo_wine_if(todo_current) - ok(hr == expect_hr, "GetInputCurrentType returned hr %#lx.\n", hr); + ok_(__FILE__, line)(hr == expect_hr, "GetInputCurrentType returned hr %#lx.\n", hr); if (FAILED(hr)) return; hr = MFCreateMediaType(&media_type); - ok(hr == S_OK, "MFCreateMediaType returned hr %#lx.\n", hr); + ok_(__FILE__, line)(hr == S_OK, "MFCreateMediaType returned hr %#lx.\n", hr); init_media_type(media_type, attributes, -1); hr = IMFMediaType_Compare(current_type, (IMFAttributes *)media_type, MF_ATTRIBUTES_MATCH_ALL_ITEMS, &result); - ok(hr == S_OK, "Compare returned hr %#lx.\n", hr); + ok_(__FILE__, line)(hr == S_OK, "Compare returned hr %#lx.\n", hr); todo_wine_if(todo_compare) - ok(result, "got result %u.\n", !!result); + ok_(__FILE__, line)(result, "got result %u.\n", !!result); + + check_attributes_(__FILE__, line, (IMFAttributes *)current_type, attributes, -1); IMFMediaType_Release(media_type); IMFMediaType_Release(current_type); @@ -785,24 +787,31 @@ static void check_mft_set_output_type(IMFTransform *transform, const struct attr IMFMediaType_Release(media_type); } -#define check_mft_get_output_current_type(a, b) check_mft_get_output_current_type_(a, b, FALSE) -static void check_mft_get_output_current_type_(IMFTransform *transform, const struct attribute_desc *attributes, - BOOL todo_current) +#define check_mft_get_output_current_type(a, b) check_mft_get_output_current_type_(__LINE__, a, b, FALSE, FALSE) +static void check_mft_get_output_current_type_(int line, IMFTransform *transform, const struct attribute_desc *attributes, + BOOL todo_current, BOOL todo_compare) { HRESULT hr, expect_hr = attributes ? S_OK : MF_E_TRANSFORM_TYPE_NOT_SET; IMFMediaType *media_type, *current_type; + BOOL result; hr = IMFTransform_GetOutputCurrentType(transform, 0, ¤t_type); todo_wine_if(todo_current) - ok(hr == expect_hr, "GetOutputCurrentType returned hr %#lx.\n", hr); + ok_(__FILE__, line)(hr == expect_hr, "GetOutputCurrentType returned hr %#lx.\n", hr); if (FAILED(hr)) return; hr = MFCreateMediaType(&media_type); - ok(hr == S_OK, "MFCreateMediaType returned hr %#lx.\n", hr); + ok_(__FILE__, line)(hr == S_OK, "MFCreateMediaType returned hr %#lx.\n", hr); init_media_type(media_type, attributes, -1); - check_attributes((IMFAttributes *)current_type, attributes, -1); + hr = IMFMediaType_Compare(current_type, (IMFAttributes *)media_type, + MF_ATTRIBUTES_MATCH_ALL_ITEMS, &result); + ok_(__FILE__, line)(hr == S_OK, "Compare returned hr %#lx.\n", hr); + todo_wine_if(todo_compare) + ok_(__FILE__, line)(result, "got result %u.\n", !!result); + + check_attributes_(__FILE__, line, (IMFAttributes *)current_type, attributes, -1); IMFMediaType_Release(media_type); IMFMediaType_Release(current_type); @@ -3191,11 +3200,11 @@ static void test_wma_decoder(void) /* setting output media type first doesn't work */ check_mft_set_output_type(transform, output_type_desc, MF_E_TRANSFORM_TYPE_NOT_SET); - check_mft_get_output_current_type_(transform, NULL, TRUE); + check_mft_get_output_current_type_(__LINE__, transform, NULL, TRUE, FALSE); check_mft_set_input_type_required(transform, input_type_desc); check_mft_set_input_type(transform, input_type_desc); - check_mft_get_input_current_type_(transform, expect_input_type_desc, TRUE, FALSE); + check_mft_get_input_current_type_(__LINE__, transform, expect_input_type_desc, TRUE, FALSE); check_mft_get_input_stream_info(transform, MF_E_TRANSFORM_TYPE_NOT_SET, NULL); check_mft_get_output_stream_info(transform, MF_E_TRANSFORM_TYPE_NOT_SET, NULL); @@ -3217,7 +3226,7 @@ static void test_wma_decoder(void) check_mft_set_output_type_required(transform, output_type_desc); check_mft_set_output_type(transform, output_type_desc, S_OK); - check_mft_get_output_current_type_(transform, expect_output_type_desc, TRUE); + check_mft_get_output_current_type_(__LINE__, transform, expect_output_type_desc, TRUE, FALSE); check_mft_get_input_stream_info(transform, S_OK, &input_info); check_mft_get_output_stream_info(transform, S_OK, &output_info); @@ -3828,6 +3837,7 @@ static void test_h264_encoder(void) ATTR_RATIO(MF_MT_FRAME_RATE, 30000, 1001), ATTR_UINT32(MF_MT_AVG_BITRATE, 193540), ATTR_BLOB(MF_MT_MPEG_SEQUENCE_HEADER, test_h264_sequence_header, sizeof(test_h264_sequence_header)), + ATTR_UINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive), {0}, }; static const MFT_OUTPUT_STREAM_INFO expect_output_info = {.cbSize = 0x8000}; @@ -4019,11 +4029,11 @@ static void test_h264_decoder(void) ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_H264), ATTR_RATIO(MF_MT_FRAME_SIZE, input_width, input_height), - ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 0), - ATTR_UINT32(MF_MT_DEFAULT_STRIDE, 0), - ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 0), - ATTR_UINT32(MF_MT_AVG_BIT_ERROR_RATE, 0), - ATTR_UINT32(MF_MT_COMPRESSED, 1), + ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 0, .todo = TRUE), + ATTR_UINT32(MF_MT_DEFAULT_STRIDE, 0, .todo = TRUE), + ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 0, .todo = TRUE), + ATTR_UINT32(MF_MT_AVG_BIT_ERROR_RATE, 0, .todo = TRUE), + ATTR_UINT32(MF_MT_COMPRESSED, 1, .todo = TRUE), {0}, }; const struct attribute_desc expect_output_type_desc[] = @@ -4262,7 +4272,7 @@ static void test_h264_decoder(void) check_mft_set_input_type_required(transform, input_type_desc); check_mft_set_input_type(transform, input_type_desc); - check_mft_get_input_current_type_(transform, expect_input_type_desc, FALSE, TRUE); + check_mft_get_input_current_type_(__LINE__, transform, expect_input_type_desc, FALSE, TRUE); check_mft_get_input_stream_info(transform, S_OK, &input_info); check_mft_get_output_stream_info(transform, S_OK, &output_info); @@ -4284,7 +4294,7 @@ static void test_h264_decoder(void) check_mft_set_output_type_required(transform, output_type_desc); check_mft_set_output_type(transform, output_type_desc, S_OK); - check_mft_get_output_current_type_(transform, expect_output_type_desc, FALSE); + check_mft_get_output_current_type_(__LINE__, transform, expect_output_type_desc, FALSE, TRUE); /* check that the output media type we've selected don't change the enumeration */ @@ -4382,7 +4392,7 @@ static void test_h264_decoder(void) ok(i == 5, "%lu output media types\n", i); /* current output type is still the one we selected */ - check_mft_get_output_current_type_(transform, expect_output_type_desc, FALSE); + check_mft_get_output_current_type_(__LINE__, transform, expect_output_type_desc, FALSE, TRUE); hr = MFCreateCollection(&output_samples); ok(hr == S_OK, "MFCreateCollection returned %#lx\n", hr); @@ -4412,7 +4422,7 @@ static void test_h264_decoder(void) ret = IMFMediaType_Release(media_type); ok(ret == 1, "Release returned %lu\n", ret); - check_mft_get_output_current_type_(transform, expect_new_output_type_desc, FALSE); + check_mft_get_output_current_type_(__LINE__, transform, expect_new_output_type_desc, FALSE, TRUE); output_sample = create_sample(NULL, actual_width * actual_height * 2); hr = check_mft_process_output(transform, output_sample, &output_status); @@ -4810,8 +4820,8 @@ static void test_audio_convert(void) ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 22050), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 8), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 22050 * 8), - ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), - ATTR_UINT32(MF_MT_AUDIO_CHANNEL_MASK, 3), + ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1, .todo = TRUE), + ATTR_UINT32(MF_MT_AUDIO_CHANNEL_MASK, 3, .todo = TRUE), {0}, }; const struct attribute_desc expect_output_type_desc[] = @@ -4939,7 +4949,7 @@ static void test_audio_convert(void) check_mft_set_input_type_required(transform, input_type_desc); check_mft_set_input_type(transform, input_type_desc); - check_mft_get_input_current_type_(transform, expect_input_type_desc, FALSE, TRUE); + check_mft_get_input_current_type_(__LINE__, transform, expect_input_type_desc, FALSE, TRUE); check_mft_get_input_stream_info(transform, MF_E_TRANSFORM_TYPE_NOT_SET, NULL); check_mft_get_output_stream_info(transform, MF_E_TRANSFORM_TYPE_NOT_SET, NULL); @@ -4961,7 +4971,7 @@ static void test_audio_convert(void) check_mft_set_output_type_required(transform, output_type_desc); check_mft_set_output_type(transform, output_type_desc, S_OK); - check_mft_get_output_current_type_(transform, expect_output_type_desc, FALSE); + check_mft_get_output_current_type_(__LINE__, transform, expect_output_type_desc, FALSE, TRUE); check_mft_get_input_stream_info(transform, S_OK, &input_info); check_mft_get_output_stream_info(transform, S_OK, &output_info); @@ -5414,11 +5424,11 @@ static void test_wmv_encoder(void) check_mft_set_input_type_required(transform, input_type_desc); check_mft_set_input_type(transform, input_type_desc); - check_mft_get_input_current_type_(transform, expect_input_type_desc, FALSE, TRUE); + check_mft_get_input_current_type_(__LINE__, transform, expect_input_type_desc, FALSE, TRUE); check_mft_set_output_type_required(transform, output_type_desc); check_mft_set_output_type(transform, output_type_desc, S_OK); - check_mft_get_output_current_type_(transform, expect_output_type_desc, FALSE); + check_mft_get_output_current_type_(__LINE__, transform, expect_output_type_desc, FALSE, FALSE); check_mft_get_input_stream_info(transform, S_OK, &expect_input_info); check_mft_get_output_stream_info(transform, S_OK, &expect_output_info); @@ -5975,7 +5985,7 @@ static void test_wmv_decoder(void) check_mft_set_input_type_required(transform, input_type_desc); check_mft_set_input_type(transform, input_type_desc); - check_mft_get_input_current_type_(transform, expect_input_type_desc, FALSE, TRUE); + check_mft_get_input_current_type_(__LINE__, transform, expect_input_type_desc, FALSE, TRUE); i = -1; while (SUCCEEDED(hr = IMFTransform_GetOutputAvailableType(transform, 0, ++i, &media_type))) @@ -5998,7 +6008,7 @@ static void test_wmv_decoder(void) check_mft_set_output_type_required(transform, transform_tests[j].output_type_desc); check_mft_set_output_type(transform, transform_tests[j].output_type_desc, S_OK); - check_mft_get_output_current_type_(transform, transform_tests[j].expect_output_type_desc, FALSE); + check_mft_get_output_current_type_(__LINE__, transform, transform_tests[j].expect_output_type_desc, FALSE, FALSE); check_mft_get_input_stream_info(transform, S_OK, transform_tests[j].expect_input_info); check_mft_get_output_stream_info(transform, S_OK, transform_tests[j].expect_output_info); @@ -6914,10 +6924,10 @@ static void test_color_convert(void) ATTR_BLOB(MF_MT_MINIMUM_DISPLAY_APERTURE, &actual_aperture, 16), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, actual_width), - ATTR_UINT32(MF_MT_SAMPLE_SIZE, actual_width * actual_height * 3 / 2), - ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), - ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), - ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), + ATTR_UINT32(MF_MT_SAMPLE_SIZE, actual_width * actual_height * 3 / 2, .todo = TRUE), + ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1, .todo = TRUE), + ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1, .todo = TRUE), + ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1, .todo = TRUE), {0}, }; const struct attribute_desc expect_output_type_desc[] = @@ -7077,14 +7087,14 @@ static void test_color_convert(void) check_mft_set_input_type_required(transform, input_type_desc); check_mft_set_input_type(transform, input_type_desc); - check_mft_get_input_current_type_(transform, expect_input_type_desc, FALSE, TRUE); + check_mft_get_input_current_type_(__LINE__, transform, expect_input_type_desc, FALSE, TRUE); for (i = 0; i < ARRAY_SIZE(color_conversion_tests); i++) { winetest_push_context("color conversion #%lu", i); check_mft_set_output_type_required(transform, color_conversion_tests[i].output_type_desc); check_mft_set_output_type(transform, color_conversion_tests[i].output_type_desc, S_OK); - check_mft_get_output_current_type_(transform, color_conversion_tests[i].expect_output_type_desc, FALSE); + check_mft_get_output_current_type_(__LINE__, transform, color_conversion_tests[i].expect_output_type_desc, FALSE, TRUE); check_mft_get_input_stream_info(transform, S_OK, &input_info); check_mft_get_output_stream_info(transform, S_OK, &output_info); From fc38d0877df8f97df137176fcd8eb626e4119fe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 29 Jan 2024 23:33:13 +0100 Subject: [PATCH 031/413] mf/tests: Add some tests with video processor aperture handling. (cherry picked from commit 604bc7ccf93bd19247742c2116425109b4a36c5e) --- dlls/mf/tests/transform.c | 55 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index 977e819cdf8..468239b0fd4 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -692,19 +692,21 @@ static void check_mft_set_input_type_required_(int line, IMFTransform *transform ok_(__FILE__, line)(!ref, "Release returned %lu\n", ref); } -static void check_mft_set_input_type(IMFTransform *transform, const struct attribute_desc *attributes) +#define check_mft_set_input_type(a, b) check_mft_set_input_type_(__LINE__, a, b, FALSE) +static void check_mft_set_input_type_(int line, IMFTransform *transform, const struct attribute_desc *attributes, BOOL todo) { IMFMediaType *media_type; HRESULT hr; hr = MFCreateMediaType(&media_type); - ok(hr == S_OK, "MFCreateMediaType returned hr %#lx.\n", hr); + ok_(__FILE__, line)(hr == S_OK, "MFCreateMediaType returned hr %#lx.\n", hr); init_media_type(media_type, attributes, -1); hr = IMFTransform_SetInputType(transform, 0, media_type, MFT_SET_TYPE_TEST_ONLY); - ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); + ok_(__FILE__, line)(hr == S_OK, "SetInputType returned %#lx.\n", hr); hr = IMFTransform_SetInputType(transform, 0, media_type, 0); - ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); + todo_wine_if(todo) + ok_(__FILE__, line)(hr == S_OK, "SetInputType returned %#lx.\n", hr); IMFMediaType_Release(media_type); } @@ -7349,6 +7351,28 @@ static void test_video_processor(void) ATTR_UINT32(MF_MT_DEFAULT_STRIDE, actual_width * 2), {0}, }; + const struct attribute_desc nv12_no_aperture[] = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12), + ATTR_RATIO(MF_MT_FRAME_SIZE, 82, 84), + {0}, + }; + const struct attribute_desc nv12_with_aperture[] = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12), + ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), + ATTR_BLOB(MF_MT_MINIMUM_DISPLAY_APERTURE, &actual_aperture, 16), + {0}, + }; + const struct attribute_desc rgb32_no_aperture[] = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), + ATTR_RATIO(MF_MT_FRAME_SIZE, 82, 84), + {0}, + }; const MFT_OUTPUT_STREAM_INFO initial_output_info = {0}; const MFT_INPUT_STREAM_INFO initial_input_info = {0}; MFT_OUTPUT_STREAM_INFO output_info = {0}; @@ -7910,6 +7934,29 @@ static void test_video_processor(void) ret = IMFTransform_Release(transform); ok(ret == 0, "Release returned %ld\n", ret); + + /* check that it is possible to change input media type frame size using geometric aperture */ + + hr = CoCreateInstance(class_id, NULL, CLSCTX_INPROC_SERVER, + &IID_IMFTransform, (void **)&transform); + ok(hr == S_OK, "got hr %#lx\n", hr); + + check_mft_set_input_type(transform, nv12_no_aperture); + check_mft_get_input_current_type(transform, nv12_no_aperture); + + check_mft_set_output_type(transform, rgb32_no_aperture, S_OK); + check_mft_get_output_current_type(transform, rgb32_no_aperture); + + check_mft_set_input_type_(__LINE__, transform, nv12_with_aperture, TRUE); + check_mft_get_input_current_type_(__LINE__, transform, nv12_with_aperture, TRUE, FALSE); + + /* output type is the same as before */ + check_mft_get_output_current_type(transform, rgb32_no_aperture); + + ret = IMFTransform_Release(transform); + ok(ret == 0, "Release returned %ld\n", ret); + + failed: winetest_pop_context(); CoUninitialize(); From 318289fd006c49580187f7f8cfee908b206d46cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 23 Jan 2024 18:04:49 +0100 Subject: [PATCH 032/413] mfreadwrite/tests: Initialize test source stream types from descriptors. (cherry picked from commit 5122f6ad9f2271844531410805bd7a918b9d95c9) --- dlls/mfreadwrite/tests/mfplat.c | 179 ++++++++++++++++++++++---------- 1 file changed, 126 insertions(+), 53 deletions(-) diff --git a/dlls/mfreadwrite/tests/mfplat.c b/dlls/mfreadwrite/tests/mfplat.c index c3544a31ac6..a93b49a64a6 100644 --- a/dlls/mfreadwrite/tests/mfplat.c +++ b/dlls/mfreadwrite/tests/mfplat.c @@ -38,11 +38,47 @@ DEFINE_GUID(GUID_NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); #include "mfidl.h" #include "mferror.h" #include "mfreadwrite.h" +#include "propvarutil.h" #include "d3d9.h" #include "dxva2api.h" #include "wine/test.h" +struct attribute_desc +{ + const GUID *key; + const char *name; + PROPVARIANT value; + BOOL ratio; + BOOL required; + BOOL todo; + BOOL todo_value; +}; + +#define ATTR_GUID(k, g, ...) {.key = &k, .name = #k, {.vt = VT_CLSID, .puuid = (GUID *)&g}, __VA_ARGS__ } +#define ATTR_UINT32(k, v, ...) {.key = &k, .name = #k, {.vt = VT_UI4, .ulVal = v}, __VA_ARGS__ } +#define ATTR_BLOB(k, p, n, ...) {.key = &k, .name = #k, {.vt = VT_VECTOR | VT_UI1, .caub = {.pElems = (void *)p, .cElems = n}}, __VA_ARGS__ } +#define ATTR_RATIO(k, n, d, ...) {.key = &k, .name = #k, {.vt = VT_UI8, .uhVal = {.HighPart = n, .LowPart = d}}, .ratio = TRUE, __VA_ARGS__ } +#define ATTR_UINT64(k, v, ...) {.key = &k, .name = #k, {.vt = VT_UI8, .uhVal = {.QuadPart = v}}, __VA_ARGS__ } + +#define init_media_type(a, b, c) init_attributes_(__FILE__, __LINE__, (IMFAttributes *)a, b, c) +#define init_attributes(a, b, c) init_attributes_(__FILE__, __LINE__, a, b, c) +static void init_attributes_(const char *file, int line, IMFAttributes *attributes, + const struct attribute_desc *desc, ULONG limit) +{ + HRESULT hr; + ULONG i; + + hr = IMFAttributes_DeleteAllItems(attributes); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + for (i = 0; i < limit && desc[i].key; ++i) + { + hr = IMFAttributes_SetItem(attributes, desc[i].key, &desc[i].value); + ok_(file, line)(hr == S_OK, "SetItem %s returned %#lx\n", debugstr_a(desc[i].name), hr); + } +} + static ULONG get_refcount(void *iface) { IUnknown *unknown = iface; @@ -357,48 +393,10 @@ static HRESULT WINAPI test_source_GetCharacteristics(IMFMediaSource *iface, DWOR static HRESULT WINAPI test_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **pd) { struct test_source *source = impl_from_IMFMediaSource(iface); - IMFStreamDescriptor *sds[ARRAY_SIZE(source->streams)]; - IMFMediaType *media_type; HRESULT hr = S_OK; - int i; - EnterCriticalSection(&source->cs); - - if (source->pd) - { - *pd = source->pd; - IMFPresentationDescriptor_AddRef(*pd); - } - else - { - for (i = 0; i < source->stream_count; ++i) - { - hr = MFCreateMediaType(&media_type); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_PCM); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, 32); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = MFCreateStreamDescriptor(i, 1, &media_type, &sds[i]); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - IMFMediaType_Release(media_type); - } - - hr = MFCreatePresentationDescriptor(source->stream_count, sds, &source->pd); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - for (i = 0; i < source->stream_count; ++i) - IMFStreamDescriptor_Release(sds[i]); - - *pd = source->pd; - IMFPresentationDescriptor_AddRef(*pd); - } - - LeaveCriticalSection(&source->cs); + *pd = source->pd; + IMFPresentationDescriptor_AddRef(*pd); return hr; } @@ -522,17 +520,22 @@ static struct test_media_stream *create_test_stream(DWORD stream_index, IMFMedia return stream; } -static IMFMediaSource *create_test_source(int stream_count) +static IMFMediaSource *create_test_source(IMFStreamDescriptor **streams, UINT stream_count) { struct test_source *source; + HRESULT hr; int i; source = calloc(1, sizeof(*source)); source->IMFMediaSource_iface.lpVtbl = &test_source_vtbl; source->refcount = 1; source->stream_count = stream_count; - MFCreateEventQueue(&source->event_queue); + hr = MFCreatePresentationDescriptor(stream_count, streams, &source->pd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = MFCreateEventQueue(&source->event_queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); InitializeCriticalSection(&source->cs); + for (i = 0; i < source->stream_count; ++i) source->streams[i] = create_test_stream(i, &source->IMFMediaSource_iface); @@ -1028,7 +1031,15 @@ static void test_source_reader(const char *filename, bool video) static void test_source_reader_from_media_source(void) { static const DWORD expected_sample_order[10] = {0, 0, 1, 1, 0, 0, 0, 0, 1, 0}; + static const struct attribute_desc audio_stream_type_desc[] = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), + ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), + ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 32), + {0}, + }; + IMFStreamDescriptor *audio_streams[3]; struct async_callback *callback; IMFSourceReader *reader; IMFMediaSource *source; @@ -1043,7 +1054,18 @@ static void test_source_reader_from_media_source(void) int i; PROPVARIANT pos; - source = create_test_source(3); + for (i = 0; i < ARRAY_SIZE(audio_streams); i++) + { + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(media_type, audio_stream_type_desc, -1); + + hr = MFCreateStreamDescriptor(i, 1, &media_type, &audio_streams[i]); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaType_Release(media_type); + } + + source = create_test_source(audio_streams, 3); ok(!!source, "Failed to create test source.\n"); hr = MFCreateSourceReaderFromMediaSource(source, NULL, &reader); @@ -1122,7 +1144,7 @@ static void test_source_reader_from_media_source(void) IMFSourceReader_Release(reader); IMFMediaSource_Release(source); - source = create_test_source(1); + source = create_test_source(audio_streams, 1); ok(!!source, "Failed to create test source.\n"); hr = MFCreateSourceReaderFromMediaSource(source, NULL, &reader); @@ -1159,7 +1181,7 @@ static void test_source_reader_from_media_source(void) IMFMediaSource_Release(source); /* Request from stream 0. */ - source = create_test_source(3); + source = create_test_source(audio_streams, 3); ok(!!source, "Failed to create test source.\n"); hr = MFCreateSourceReaderFromMediaSource(source, NULL, &reader); @@ -1193,7 +1215,7 @@ static void test_source_reader_from_media_source(void) IMFMediaSource_Release(source); /* Request a non-native bit depth. */ - source = create_test_source(1); + source = create_test_source(audio_streams, 1); ok(!!source, "Failed to create test source.\n"); hr = MFCreateSourceReaderFromMediaSource(source, NULL, &reader); @@ -1241,7 +1263,7 @@ static void test_source_reader_from_media_source(void) IMFMediaSource_Release(source); /* Async mode. */ - source = create_test_source(3); + source = create_test_source(audio_streams, 3); ok(!!source, "Failed to create test source.\n"); callback = create_async_callback(); @@ -1286,7 +1308,7 @@ static void test_source_reader_from_media_source(void) IMFMediaSource_Release(source); /* RequestSample failure. */ - source = create_test_source(3); + source = create_test_source(audio_streams, 3); ok(!!source, "Failed to create test source.\n"); fail_request_sample = TRUE; @@ -1329,7 +1351,7 @@ static void test_source_reader_from_media_source(void) fail_request_sample = FALSE; /* MF_SOURCE_READER_ANY_STREAM with streams of different sample sizes */ - source = create_test_source(2); + source = create_test_source(audio_streams, 2); ok(!!source, "Failed to create test source.\n"); test_source = impl_from_IMFMediaSource(source); @@ -1359,19 +1381,32 @@ static void test_source_reader_from_media_source(void) IMFSourceReader_Release(reader); IMFMediaSource_Release(source); + + for (i = 0; i < ARRAY_SIZE(audio_streams); i++) + IMFStreamDescriptor_Release(audio_streams[i]); } static void test_reader_d3d9(void) { + static const struct attribute_desc audio_stream_type_desc[] = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), + ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), + ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 32), + {0}, + }; + + IMFStreamDescriptor *audio_streams[3]; IDirect3DDeviceManager9 *d3d9_manager; IDirect3DDevice9 *d3d9_device; IMFAttributes *attributes; + IMFMediaType *media_type; IMFSourceReader *reader; IMFMediaSource *source; IDirect3D9 *d3d9; HWND window; HRESULT hr; - UINT token; + UINT i, token; ULONG refcount; d3d9 = Direct3DCreate9(D3D_SDK_VERSION); @@ -1387,13 +1422,24 @@ static void test_reader_d3d9(void) goto done; } + for (i = 0; i < ARRAY_SIZE(audio_streams); i++) + { + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(media_type, audio_stream_type_desc, -1); + + hr = MFCreateStreamDescriptor(i, 1, &media_type, &audio_streams[i]); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaType_Release(media_type); + } + hr = DXVA2CreateDirect3DDeviceManager9(&token, &d3d9_manager); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IDirect3DDeviceManager9_ResetDevice(d3d9_manager, d3d9_device, token); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - source = create_test_source(3); + source = create_test_source(audio_streams, 3); ok(!!source, "Failed to create test source.\n"); hr = MFCreateAttributes(&attributes, 1); @@ -1409,11 +1455,13 @@ static void test_reader_d3d9(void) IMFSourceReader_Release(reader); + for (i = 0; i < ARRAY_SIZE(audio_streams); i++) + IMFStreamDescriptor_Release(audio_streams[i]); + refcount = IDirect3DDeviceManager9_Release(d3d9_manager); ok(!refcount, "Unexpected refcount %lu.\n", refcount); IDirect3DDevice9_Release(d3d9_device); - done: IDirect3D9_Release(d3d9); DestroyWindow(window); @@ -1511,12 +1559,34 @@ static void test_sink_writer_mp4(void) static void test_interfaces(void) { + static const struct attribute_desc audio_stream_type_desc[] = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), + ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), + ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 32), + {0}, + }; + + IMFStreamDescriptor *audio_streams[1]; + IMFMediaType *media_type; IMFSourceReader *reader; IMFMediaSource *source; IUnknown *unk; HRESULT hr; + UINT i; + + for (i = 0; i < ARRAY_SIZE(audio_streams); i++) + { + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(media_type, audio_stream_type_desc, -1); - source = create_test_source(1); + hr = MFCreateStreamDescriptor(i, 1, &media_type, &audio_streams[i]); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaType_Release(media_type); + } + + source = create_test_source(audio_streams, 1); ok(!!source, "Failed to create test source.\n"); hr = MFCreateSourceReaderFromMediaSource(source, NULL, &reader); @@ -1529,6 +1599,9 @@ static void test_interfaces(void) IMFSourceReader_Release(reader); IMFMediaSource_Release(source); + + for (i = 0; i < ARRAY_SIZE(audio_streams); i++) + IMFStreamDescriptor_Release(audio_streams[i]); } START_TEST(mfplat) From 7ef5dcb83e810552220e8e7a29ea4fed836b550c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 23 Jan 2024 18:05:44 +0100 Subject: [PATCH 033/413] mfreadwrite/tests: Test source reader exposed transforms and types. (cherry picked from commit f5daee4a2fb50760347cbeeed5fe39049841aef1) --- dlls/mfreadwrite/tests/Makefile.in | 2 +- dlls/mfreadwrite/tests/mfplat.c | 385 +++++++++++++++++++++++++++++ 2 files changed, 386 insertions(+), 1 deletion(-) diff --git a/dlls/mfreadwrite/tests/Makefile.in b/dlls/mfreadwrite/tests/Makefile.in index 90bac9e0896..cada1bf22ff 100644 --- a/dlls/mfreadwrite/tests/Makefile.in +++ b/dlls/mfreadwrite/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = mfreadwrite.dll -IMPORTS = ole32 user32 d3d9 dxva2 mfplat mfreadwrite mfuuid +IMPORTS = ole32 user32 d3d9 dxva2 mfplat mfreadwrite mfuuid propsys SOURCES = \ mfplat.c \ diff --git a/dlls/mfreadwrite/tests/mfplat.c b/dlls/mfreadwrite/tests/mfplat.c index a93b49a64a6..04c077dd027 100644 --- a/dlls/mfreadwrite/tests/mfplat.c +++ b/dlls/mfreadwrite/tests/mfplat.c @@ -61,6 +61,53 @@ struct attribute_desc #define ATTR_RATIO(k, n, d, ...) {.key = &k, .name = #k, {.vt = VT_UI8, .uhVal = {.HighPart = n, .LowPart = d}}, .ratio = TRUE, __VA_ARGS__ } #define ATTR_UINT64(k, v, ...) {.key = &k, .name = #k, {.vt = VT_UI8, .uhVal = {.QuadPart = v}}, __VA_ARGS__ } +#define check_media_type(a, b, c) check_attributes_(__FILE__, __LINE__, (IMFAttributes *)a, b, c) +#define check_attributes(a, b, c) check_attributes_(__FILE__, __LINE__, a, b, c) +void check_attributes_(const char *file, int line, IMFAttributes *attributes, + const struct attribute_desc *desc, ULONG limit) +{ + char buffer[1024], *buf = buffer; + PROPVARIANT value; + int i, j, ret; + HRESULT hr; + + for (i = 0; i < limit && desc[i].key; ++i) + { + hr = IMFAttributes_GetItem(attributes, desc[i].key, &value); + todo_wine_if(desc[i].todo) + ok_(file, line)(hr == S_OK, "%s missing, hr %#lx\n", debugstr_a(desc[i].name), hr); + if (hr != S_OK) continue; + + switch (value.vt) + { + default: sprintf(buffer, "??"); break; + case VT_CLSID: sprintf(buffer, "%s", debugstr_guid(value.puuid)); break; + case VT_UI4: sprintf(buffer, "%lu", value.ulVal); break; + case VT_UI8: + if (desc[i].ratio) + sprintf(buffer, "%lu:%lu", value.uhVal.HighPart, value.uhVal.LowPart); + else + sprintf(buffer, "%I64u", value.uhVal.QuadPart); + break; + case VT_VECTOR | VT_UI1: + buf += sprintf(buf, "size %lu, data {", value.caub.cElems); + for (j = 0; j < 128 && j < value.caub.cElems; ++j) + buf += sprintf(buf, "0x%02x,", value.caub.pElems[j]); + if (value.caub.cElems > 128) + buf += sprintf(buf, "...}"); + else + buf += sprintf(buf - (j ? 1 : 0), "}"); + break; + } + + ret = PropVariantCompareEx(&value, &desc[i].value, 0, 0); + todo_wine_if(desc[i].todo_value) + ok_(file, line)(ret == 0, "%s mismatch, type %u, value %s\n", + debugstr_a(desc[i].name), value.vt, buffer); + PropVariantClear(&value); + } +} + #define init_media_type(a, b, c) init_attributes_(__FILE__, __LINE__, (IMFAttributes *)a, b, c) #define init_attributes(a, b, c) init_attributes_(__FILE__, __LINE__, a, b, c) static void init_attributes_(const char *file, int line, IMFAttributes *attributes, @@ -1604,6 +1651,343 @@ static void test_interfaces(void) IMFStreamDescriptor_Release(audio_streams[i]); } +static void test_source_reader_transforms(void) +{ + static const struct attribute_desc h264_stream_type_desc[] = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_H264), + ATTR_RATIO(MF_MT_FRAME_SIZE, 96, 96), + {0}, + }; + static const struct attribute_desc nv12_stream_type_desc[] = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12), + ATTR_RATIO(MF_MT_FRAME_SIZE, 96, 96), + {0}, + }; + static const struct attribute_desc nv12_expect_desc[] = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12), + ATTR_RATIO(MF_MT_FRAME_SIZE, 96, 96), + ATTR_RATIO(MF_MT_FRAME_RATE, 30000, 1001), + ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), + ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), + ATTR_UINT32(MF_MT_AVG_BIT_ERROR_RATE, 0, .todo = TRUE), + ATTR_UINT32(MF_MT_COMPRESSED, 0, .todo = TRUE), + ATTR_UINT32(MF_MT_DEFAULT_STRIDE, 96), + ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), + ATTR_UINT32(MF_MT_INTERLACE_MODE, 7), + ATTR_UINT32(MF_MT_SAMPLE_SIZE, 13824), + ATTR_UINT32(MF_MT_VIDEO_ROTATION, 0), + {0}, + }; + static const struct attribute_desc yuy2_stream_type_desc[] = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_YUY2), + ATTR_RATIO(MF_MT_FRAME_SIZE, 96, 96), + {0}, + }; + static const struct attribute_desc yuy2_expect_desc[] = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_YUY2), + ATTR_RATIO(MF_MT_FRAME_SIZE, 96, 96), + ATTR_RATIO(MF_MT_FRAME_RATE, 30000, 1001), + ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), + ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), + ATTR_UINT32(MF_MT_AVG_BIT_ERROR_RATE, 0, .todo = TRUE), + ATTR_UINT32(MF_MT_COMPRESSED, 0, .todo = TRUE), + ATTR_UINT32(MF_MT_DEFAULT_STRIDE, 192), + ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), + ATTR_UINT32(MF_MT_INTERLACE_MODE, 7), + ATTR_UINT32(MF_MT_SAMPLE_SIZE, 18432), + ATTR_UINT32(MF_MT_VIDEO_ROTATION, 0), + {0}, + }; + static const struct attribute_desc rgb32_stream_type_desc[] = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), + ATTR_RATIO(MF_MT_FRAME_SIZE, 96, 96), + {0}, + }; + IMFStreamDescriptor *video_stream; + IMFSourceReaderEx *reader_ex; + IMFMediaType *media_type; + IMFSourceReader *reader; + IMFTransform *transform; + IMFMediaSource *source; + GUID category; + HRESULT hr; + + /* test source reader with a RGB32 source */ + + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(media_type, rgb32_stream_type_desc, -1); + hr = MFCreateStreamDescriptor(0, 1, &media_type, &video_stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaType_Release(media_type); + + source = create_test_source(&video_stream, 1); + ok(!!source, "Failed to create test source.\n"); + + hr = MFCreateSourceReaderFromMediaSource(source, NULL, &reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFSourceReader_SetStreamSelection(reader, 0, TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* skip tests on Win7 which misses IMFSourceReaderEx and has uninteresting media types differences */ + hr = IMFSourceReader_QueryInterface(reader, &IID_IMFSourceReaderEx, (void **)&reader_ex); + ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* Win7 */, "Unexpected hr %#lx.\n", hr); + if (broken(hr == E_NOINTERFACE)) + { + win_skip("missing IMFSourceReaderEx interface, skipping tests on Win7\n"); + goto skip_tests; + } + IMFSourceReaderEx_Release(reader_ex); + + hr = IMFSourceReader_GetNativeMediaType(reader, 0, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, rgb32_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + hr = IMFSourceReader_GetCurrentMediaType(reader, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, rgb32_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + /* only one stream type and only one stream */ + hr = IMFSourceReader_GetNativeMediaType(reader, 0, 1, &media_type); + ok(hr == MF_E_NO_MORE_TYPES, "Unexpected hr %#lx.\n", hr); + hr = IMFSourceReader_GetNativeMediaType(reader, 1, 0, &media_type); + ok(hr == MF_E_INVALIDSTREAMNUMBER, "Unexpected hr %#lx.\n", hr); + hr = IMFSourceReader_GetCurrentMediaType(reader, 1, &media_type); + ok(hr == MF_E_INVALIDSTREAMNUMBER, "Unexpected hr %#lx.\n", hr); + + /* cannot request encoding to compressed media type */ + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(media_type, h264_stream_type_desc, -1); + hr = IMFSourceReader_SetCurrentMediaType(reader, 0, NULL, media_type); + ok(hr == MF_E_TOPO_CODEC_NOT_FOUND, "Unexpected hr %#lx.\n", hr); + IMFMediaType_Release(media_type); + + /* SetCurrentMediaType needs major type and subtype */ + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFSourceReader_SetCurrentMediaType(reader, 0, NULL, media_type); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + init_media_type(media_type, nv12_stream_type_desc, 1); + hr = IMFSourceReader_SetCurrentMediaType(reader, 0, NULL, media_type); + todo_wine ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + /* RGB32 -> NV12 conversion */ + init_media_type(media_type, nv12_stream_type_desc, -1); + hr = IMFSourceReader_SetCurrentMediaType(reader, 0, NULL, media_type); + ok(hr == MF_E_TOPO_CODEC_NOT_FOUND, "Unexpected hr %#lx.\n", hr); + IMFMediaType_Release(media_type); + + hr = IMFSourceReader_GetCurrentMediaType(reader, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, rgb32_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + hr = IMFSourceReader_GetServiceForStream(reader, 0, &GUID_NULL, &IID_IMFTransform, (void **)&transform); + ok(hr == E_NOINTERFACE, "Unexpected hr %#lx.\n", hr); + + IMFSourceReader_Release(reader); + IMFMediaSource_Release(source); + IMFStreamDescriptor_Release(video_stream); + + + /* test source reader with a NV12 source */ + + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(media_type, nv12_stream_type_desc, -1); + hr = MFCreateStreamDescriptor(0, 1, &media_type, &video_stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaType_Release(media_type); + + source = create_test_source(&video_stream, 1); + ok(!!source, "Failed to create test source.\n"); + + hr = MFCreateSourceReaderFromMediaSource(source, NULL, &reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFSourceReader_SetStreamSelection(reader, 0, TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFSourceReader_GetNativeMediaType(reader, 0, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, nv12_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + hr = IMFSourceReader_GetCurrentMediaType(reader, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, nv12_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + /* NV12 -> RGB32 conversion */ + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(media_type, rgb32_stream_type_desc, -1); + hr = IMFSourceReader_SetCurrentMediaType(reader, 0, NULL, media_type); + ok(hr == MF_E_TOPO_CODEC_NOT_FOUND, "Unexpected hr %#lx.\n", hr); + IMFMediaType_Release(media_type); + + hr = IMFSourceReader_GetServiceForStream(reader, 0, &GUID_NULL, &IID_IMFTransform, (void **)&transform); + ok(hr == E_NOINTERFACE, "Unexpected hr %#lx.\n", hr); + + /* NV12 -> YUY2 conversion */ + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(media_type, yuy2_stream_type_desc, -1); + hr = IMFSourceReader_SetCurrentMediaType(reader, 0, NULL, media_type); + ok(hr == MF_E_TOPO_CODEC_NOT_FOUND, "Unexpected hr %#lx.\n", hr); + IMFMediaType_Release(media_type); + + hr = IMFSourceReader_GetCurrentMediaType(reader, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, nv12_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + hr = IMFSourceReader_GetServiceForStream(reader, 0, &GUID_NULL, &IID_IMFTransform, (void **)&transform); + ok(hr == E_NOINTERFACE, "Unexpected hr %#lx.\n", hr); + + IMFSourceReader_Release(reader); + IMFMediaSource_Release(source); + IMFStreamDescriptor_Release(video_stream); + + + /* test source reader with a H264 source */ + + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(media_type, h264_stream_type_desc, -1); + hr = MFCreateStreamDescriptor(0, 1, &media_type, &video_stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaType_Release(media_type); + + source = create_test_source(&video_stream, 1); + ok(!!source, "Failed to create test source.\n"); + + hr = MFCreateSourceReaderFromMediaSource(source, NULL, &reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFSourceReader_SetStreamSelection(reader, 0, TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFSourceReader_GetNativeMediaType(reader, 0, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, h264_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + hr = IMFSourceReader_GetCurrentMediaType(reader, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, h264_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + /* when H264 output is used, there's no decoder transform */ + hr = IMFSourceReader_GetServiceForStream(reader, 0, &GUID_NULL, &IID_IMFTransform, (void **)&transform); + ok(hr == E_NOINTERFACE, "Unexpected hr %#lx.\n", hr); + + /* H264 -> RGB32 conversion */ + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(media_type, rgb32_stream_type_desc, -1); + hr = IMFSourceReader_SetCurrentMediaType(reader, 0, NULL, media_type); + todo_wine ok(hr == MF_E_INVALIDMEDIATYPE, "Unexpected hr %#lx.\n", hr); + IMFMediaType_Release(media_type); + + /* H264 -> NV12 conversion */ + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(media_type, nv12_stream_type_desc, 2); /* doesn't need the frame size */ + hr = IMFSourceReader_SetCurrentMediaType(reader, 0, NULL, media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaType_Release(media_type); + + hr = IMFSourceReader_GetCurrentMediaType(reader, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, nv12_expect_desc, -1); + IMFMediaType_Release(media_type); + + /* the H264 decoder transform can now be accessed */ + hr = IMFSourceReader_GetServiceForStream(reader, 0, &GUID_NULL, &IID_IMFTransform, (void **)&transform); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, h264_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + hr = IMFTransform_GetOutputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, nv12_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + IMFTransform_Release(transform); + + /* YUY2 output works too */ + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(media_type, yuy2_stream_type_desc, 2); /* doesn't need the frame size */ + hr = IMFSourceReader_SetCurrentMediaType(reader, 0, NULL, media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaType_Release(media_type); + + hr = IMFSourceReader_GetCurrentMediaType(reader, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, yuy2_expect_desc, -1); + IMFMediaType_Release(media_type); + + /* decoder transform is also available through the IMFSourceReaderEx interface */ + hr = IMFSourceReader_QueryInterface(reader, &IID_IMFSourceReaderEx, (void **)&reader_ex); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFSourceReaderEx_GetTransformForStream(reader_ex, 0, 0, &category, &transform); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (hr == S_OK) + { + hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, h264_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + hr = IMFTransform_GetOutputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, yuy2_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + /* changing the transform media type doesn't change the reader output type */ + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(media_type, nv12_stream_type_desc, -1); + hr = IMFTransform_SetOutputType(transform, 0, media_type, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaType_Release(media_type); + + hr = IMFSourceReader_GetCurrentMediaType(reader, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, yuy2_expect_desc, -1); + IMFMediaType_Release(media_type); + + IMFTransform_Release(transform); + } + + hr = IMFSourceReaderEx_GetTransformForStream(reader_ex, 0, 1, &category, &transform); + todo_wine ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); + IMFSourceReaderEx_Release(reader_ex); + +skip_tests: + IMFSourceReader_Release(reader); + IMFMediaSource_Release(source); + IMFStreamDescriptor_Release(video_stream); +} + START_TEST(mfplat) { HRESULT hr; @@ -1618,6 +2002,7 @@ START_TEST(mfplat) test_source_reader("test.wav", false); test_source_reader("test.mp4", true); test_source_reader_from_media_source(); + test_source_reader_transforms(); test_reader_d3d9(); test_sink_writer_create(); test_sink_writer_mp4(); From 4345846699e50498dc847674cede7a5fd64fd8f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 23 Jan 2024 20:24:28 +0100 Subject: [PATCH 034/413] mfreadwrite/tests: Test source reader transforms with MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING. (cherry picked from commit b37a16c7f39c7054238570d714c56d31af6c5f7d) --- dlls/mfreadwrite/tests/mfplat.c | 134 ++++++++++++++++++++++++++++---- 1 file changed, 121 insertions(+), 13 deletions(-) diff --git a/dlls/mfreadwrite/tests/mfplat.c b/dlls/mfreadwrite/tests/mfplat.c index 04c077dd027..aa333b67065 100644 --- a/dlls/mfreadwrite/tests/mfplat.c +++ b/dlls/mfreadwrite/tests/mfplat.c @@ -1651,7 +1651,7 @@ static void test_interfaces(void) IMFStreamDescriptor_Release(audio_streams[i]); } -static void test_source_reader_transforms(void) +static void test_source_reader_transforms(BOOL enable_processing) { static const struct attribute_desc h264_stream_type_desc[] = { @@ -1715,8 +1715,20 @@ static void test_source_reader_transforms(void) ATTR_RATIO(MF_MT_FRAME_SIZE, 96, 96), {0}, }; + static const struct attribute_desc rgb32_expect_desc[] = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32, .todo_value = TRUE), + ATTR_RATIO(MF_MT_FRAME_SIZE, 96, 96), + ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1, .todo = TRUE), + ATTR_UINT32(MF_MT_DEFAULT_STRIDE, 384, .todo = TRUE), + ATTR_UINT32(MF_MT_INTERLACE_MODE, 2, .todo = TRUE), + ATTR_UINT32(MF_MT_SAMPLE_SIZE, 36864, .todo = TRUE), + {0}, + }; IMFStreamDescriptor *video_stream; IMFSourceReaderEx *reader_ex; + IMFAttributes *attributes; IMFMediaType *media_type; IMFSourceReader *reader; IMFTransform *transform; @@ -1724,6 +1736,13 @@ static void test_source_reader_transforms(void) GUID category; HRESULT hr; + winetest_push_context("vp %u", enable_processing); + + hr = MFCreateAttributes(&attributes, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFAttributes_SetUINT32(attributes, &MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, enable_processing); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + /* test source reader with a RGB32 source */ hr = MFCreateMediaType(&media_type); @@ -1736,7 +1755,7 @@ static void test_source_reader_transforms(void) source = create_test_source(&video_stream, 1); ok(!!source, "Failed to create test source.\n"); - hr = MFCreateSourceReaderFromMediaSource(source, NULL, &reader); + hr = MFCreateSourceReaderFromMediaSource(source, attributes, &reader); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFSourceReader_SetStreamSelection(reader, 0, TRUE); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -1817,7 +1836,7 @@ static void test_source_reader_transforms(void) source = create_test_source(&video_stream, 1); ok(!!source, "Failed to create test source.\n"); - hr = MFCreateSourceReaderFromMediaSource(source, NULL, &reader); + hr = MFCreateSourceReaderFromMediaSource(source, attributes, &reader); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFSourceReader_SetStreamSelection(reader, 0, TRUE); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -1832,12 +1851,23 @@ static void test_source_reader_transforms(void) check_media_type(media_type, nv12_stream_type_desc, -1); IMFMediaType_Release(media_type); - /* NV12 -> RGB32 conversion */ + /* NV12 -> RGB32 conversion with MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING */ hr = MFCreateMediaType(&media_type); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - init_media_type(media_type, rgb32_stream_type_desc, -1); + init_media_type(media_type, rgb32_stream_type_desc, 2); /* doesn't need the frame size */ hr = IMFSourceReader_SetCurrentMediaType(reader, 0, NULL, media_type); - ok(hr == MF_E_TOPO_CODEC_NOT_FOUND, "Unexpected hr %#lx.\n", hr); + if (enable_processing) + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + else + ok(hr == MF_E_TOPO_CODEC_NOT_FOUND, "Unexpected hr %#lx.\n", hr); + IMFMediaType_Release(media_type); + + hr = IMFSourceReader_GetCurrentMediaType(reader, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (enable_processing) + check_media_type(media_type, rgb32_expect_desc, -1); + else + check_media_type(media_type, nv12_stream_type_desc, -1); IMFMediaType_Release(media_type); hr = IMFSourceReader_GetServiceForStream(reader, 0, &GUID_NULL, &IID_IMFTransform, (void **)&transform); @@ -1853,12 +1883,25 @@ static void test_source_reader_transforms(void) hr = IMFSourceReader_GetCurrentMediaType(reader, 0, &media_type); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - check_media_type(media_type, nv12_stream_type_desc, -1); + if (enable_processing) + check_media_type(media_type, rgb32_expect_desc, -1); + else + check_media_type(media_type, nv12_stream_type_desc, -1); IMFMediaType_Release(media_type); + /* even though we are now converting to RGB32 the converter transform is not exposed */ hr = IMFSourceReader_GetServiceForStream(reader, 0, &GUID_NULL, &IID_IMFTransform, (void **)&transform); ok(hr == E_NOINTERFACE, "Unexpected hr %#lx.\n", hr); + /* not even through the IMFSourceReaderEx interface */ + hr = IMFSourceReader_QueryInterface(reader, &IID_IMFSourceReaderEx, (void **)&reader_ex); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFSourceReaderEx_GetTransformForStream(reader_ex, 0, 0, &category, &transform); + todo_wine ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); + hr = IMFSourceReaderEx_GetTransformForStream(reader_ex, 0, 1, &category, &transform); + todo_wine ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); + IMFSourceReaderEx_Release(reader_ex); + IMFSourceReader_Release(reader); IMFMediaSource_Release(source); IMFStreamDescriptor_Release(video_stream); @@ -1876,7 +1919,7 @@ static void test_source_reader_transforms(void) source = create_test_source(&video_stream, 1); ok(!!source, "Failed to create test source.\n"); - hr = MFCreateSourceReaderFromMediaSource(source, NULL, &reader); + hr = MFCreateSourceReaderFromMediaSource(source, attributes, &reader); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFSourceReader_SetStreamSelection(reader, 0, TRUE); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -1895,14 +1938,74 @@ static void test_source_reader_transforms(void) hr = IMFSourceReader_GetServiceForStream(reader, 0, &GUID_NULL, &IID_IMFTransform, (void **)&transform); ok(hr == E_NOINTERFACE, "Unexpected hr %#lx.\n", hr); - /* H264 -> RGB32 conversion */ + /* H264 -> RGB32 conversion with MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING */ hr = MFCreateMediaType(&media_type); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - init_media_type(media_type, rgb32_stream_type_desc, -1); + init_media_type(media_type, rgb32_stream_type_desc, 2); /* doesn't need the frame size */ hr = IMFSourceReader_SetCurrentMediaType(reader, 0, NULL, media_type); - todo_wine ok(hr == MF_E_INVALIDMEDIATYPE, "Unexpected hr %#lx.\n", hr); + if (enable_processing) + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + else + todo_wine ok(hr == MF_E_INVALIDMEDIATYPE, "Unexpected hr %#lx.\n", hr); + IMFMediaType_Release(media_type); + + hr = IMFSourceReader_GetCurrentMediaType(reader, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (enable_processing) + check_media_type(media_type, rgb32_expect_desc, -1); + else + check_media_type(media_type, h264_stream_type_desc, -1); IMFMediaType_Release(media_type); + /* H264 decoder transform is exposed with MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING */ + hr = IMFSourceReader_GetServiceForStream(reader, 0, &GUID_NULL, &IID_IMFTransform, (void **)&transform); + if (!enable_processing) + ok(hr == E_NOINTERFACE, "Unexpected hr %#lx.\n", hr); + else + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (hr == S_OK) + { + hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, h264_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + /* with NV12 output */ + hr = IMFTransform_GetOutputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, nv12_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + IMFTransform_Release(transform); + } + + /* H264 decoder transform is also available through the IMFSourceReaderEx interface */ + hr = IMFSourceReader_QueryInterface(reader, &IID_IMFSourceReaderEx, (void **)&reader_ex); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFSourceReaderEx_GetTransformForStream(reader_ex, 0, 0, &category, &transform); + if (!enable_processing) + todo_wine ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); + else + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (hr == S_OK) + { + hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, h264_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + hr = IMFTransform_GetOutputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, nv12_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + IMFTransform_Release(transform); + } + + hr = IMFSourceReaderEx_GetTransformForStream(reader_ex, 0, 1, &category, &transform); + todo_wine ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); + IMFSourceReaderEx_Release(reader_ex); + /* H264 -> NV12 conversion */ hr = MFCreateMediaType(&media_type); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -1945,7 +2048,7 @@ static void test_source_reader_transforms(void) check_media_type(media_type, yuy2_expect_desc, -1); IMFMediaType_Release(media_type); - /* decoder transform is also available through the IMFSourceReaderEx interface */ + /* H264 decoder transform is also available through the IMFSourceReaderEx interface */ hr = IMFSourceReader_QueryInterface(reader, &IID_IMFSourceReaderEx, (void **)&reader_ex); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFSourceReaderEx_GetTransformForStream(reader_ex, 0, 0, &category, &transform); @@ -1986,6 +2089,10 @@ static void test_source_reader_transforms(void) IMFSourceReader_Release(reader); IMFMediaSource_Release(source); IMFStreamDescriptor_Release(video_stream); + + IMFAttributes_Release(attributes); + + winetest_pop_context(); } START_TEST(mfplat) @@ -2002,7 +2109,8 @@ START_TEST(mfplat) test_source_reader("test.wav", false); test_source_reader("test.mp4", true); test_source_reader_from_media_source(); - test_source_reader_transforms(); + test_source_reader_transforms(FALSE); + test_source_reader_transforms(TRUE); test_reader_d3d9(); test_sink_writer_create(); test_sink_writer_mp4(); From 89462edaa54906819f5181ba8647af19f7faf669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 1 Feb 2024 09:37:30 +0100 Subject: [PATCH 035/413] mfreadwrite/tests: Test source reader transforms with MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING. (cherry picked from commit 3c644480377a4aac6bcc0b23b79f726366346c7a) --- dlls/mfreadwrite/tests/mfplat.c | 202 +++++++++++++++++++++++++++----- 1 file changed, 173 insertions(+), 29 deletions(-) diff --git a/dlls/mfreadwrite/tests/mfplat.c b/dlls/mfreadwrite/tests/mfplat.c index aa333b67065..08e19589ad1 100644 --- a/dlls/mfreadwrite/tests/mfplat.c +++ b/dlls/mfreadwrite/tests/mfplat.c @@ -1651,7 +1651,7 @@ static void test_interfaces(void) IMFStreamDescriptor_Release(audio_streams[i]); } -static void test_source_reader_transforms(BOOL enable_processing) +static void test_source_reader_transforms(BOOL enable_processing, BOOL enable_advanced) { static const struct attribute_desc h264_stream_type_desc[] = { @@ -1684,6 +1684,16 @@ static void test_source_reader_transforms(BOOL enable_processing) ATTR_UINT32(MF_MT_VIDEO_ROTATION, 0), {0}, }; + static const struct attribute_desc nv12_expect_advanced_desc[] = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12, .todo_value = TRUE), + ATTR_RATIO(MF_MT_FRAME_SIZE, 96, 96), + ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1, .todo = TRUE), + ATTR_UINT32(MF_MT_COMPRESSED, 0, .todo = TRUE), + ATTR_UINT32(MF_MT_INTERLACE_MODE, 2, .todo = TRUE), + {0}, + }; static const struct attribute_desc yuy2_stream_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), @@ -1708,6 +1718,16 @@ static void test_source_reader_transforms(BOOL enable_processing) ATTR_UINT32(MF_MT_VIDEO_ROTATION, 0), {0}, }; + static const struct attribute_desc yuy2_expect_advanced_desc[] = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_YUY2, .todo_value = TRUE), + ATTR_RATIO(MF_MT_FRAME_SIZE, 96, 96), + ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1, .todo = TRUE), + ATTR_UINT32(MF_MT_COMPRESSED, 0, .todo = TRUE), + ATTR_UINT32(MF_MT_INTERLACE_MODE, 2, .todo = TRUE), + {0}, + }; static const struct attribute_desc rgb32_stream_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), @@ -1726,6 +1746,15 @@ static void test_source_reader_transforms(BOOL enable_processing) ATTR_UINT32(MF_MT_SAMPLE_SIZE, 36864, .todo = TRUE), {0}, }; + static const struct attribute_desc rgb32_expect_advanced_desc[] = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32, .todo_value = TRUE), + ATTR_RATIO(MF_MT_FRAME_SIZE, 96, 96), + ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1, .todo = TRUE), + ATTR_UINT32(MF_MT_COMPRESSED, 0, .todo = TRUE), + ATTR_UINT32(MF_MT_INTERLACE_MODE, 2, .todo = TRUE), + }; IMFStreamDescriptor *video_stream; IMFSourceReaderEx *reader_ex; IMFAttributes *attributes; @@ -1736,12 +1765,14 @@ static void test_source_reader_transforms(BOOL enable_processing) GUID category; HRESULT hr; - winetest_push_context("vp %u", enable_processing); + winetest_push_context("vp %u adv %u", enable_processing, enable_advanced); hr = MFCreateAttributes(&attributes, 1); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFAttributes_SetUINT32(attributes, &MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, enable_processing); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFAttributes_SetUINT32(attributes, &MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING, enable_advanced); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); /* test source reader with a RGB32 source */ @@ -1805,19 +1836,43 @@ static void test_source_reader_transforms(BOOL enable_processing) hr = IMFSourceReader_SetCurrentMediaType(reader, 0, NULL, media_type); todo_wine ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); - /* RGB32 -> NV12 conversion */ - init_media_type(media_type, nv12_stream_type_desc, -1); + /* RGB32 -> NV12 conversion with MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING */ + init_media_type(media_type, nv12_stream_type_desc, 2); /* doesn't need the frame size */ hr = IMFSourceReader_SetCurrentMediaType(reader, 0, NULL, media_type); - ok(hr == MF_E_TOPO_CODEC_NOT_FOUND, "Unexpected hr %#lx.\n", hr); + if (enable_advanced) + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + else + ok(hr == MF_E_TOPO_CODEC_NOT_FOUND, "Unexpected hr %#lx.\n", hr); IMFMediaType_Release(media_type); hr = IMFSourceReader_GetCurrentMediaType(reader, 0, &media_type); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - check_media_type(media_type, rgb32_stream_type_desc, -1); + if (enable_advanced) + check_media_type(media_type, nv12_expect_advanced_desc, -1); + else + check_media_type(media_type, rgb32_stream_type_desc, -1); IMFMediaType_Release(media_type); + /* video processor is accessible with MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING */ hr = IMFSourceReader_GetServiceForStream(reader, 0, &GUID_NULL, &IID_IMFTransform, (void **)&transform); - ok(hr == E_NOINTERFACE, "Unexpected hr %#lx.\n", hr); + if (!enable_advanced) + ok(hr == E_NOINTERFACE, "Unexpected hr %#lx.\n", hr); + else + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (hr == S_OK) + { + hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, rgb32_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + hr = IMFTransform_GetOutputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, nv12_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + IMFTransform_Release(transform); + } IMFSourceReader_Release(reader); IMFMediaSource_Release(source); @@ -1851,12 +1906,12 @@ static void test_source_reader_transforms(BOOL enable_processing) check_media_type(media_type, nv12_stream_type_desc, -1); IMFMediaType_Release(media_type); - /* NV12 -> RGB32 conversion with MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING */ + /* NV12 -> RGB32 conversion with MF_SOURCE_READER_ENABLE_(ADVANCED_)VIDEO_PROCESSING */ hr = MFCreateMediaType(&media_type); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); init_media_type(media_type, rgb32_stream_type_desc, 2); /* doesn't need the frame size */ hr = IMFSourceReader_SetCurrentMediaType(reader, 0, NULL, media_type); - if (enable_processing) + if (enable_processing || enable_advanced) todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); else ok(hr == MF_E_TOPO_CODEC_NOT_FOUND, "Unexpected hr %#lx.\n", hr); @@ -1864,40 +1919,99 @@ static void test_source_reader_transforms(BOOL enable_processing) hr = IMFSourceReader_GetCurrentMediaType(reader, 0, &media_type); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (enable_processing) + if (enable_advanced) + check_media_type(media_type, rgb32_expect_advanced_desc, -1); + else if (enable_processing) check_media_type(media_type, rgb32_expect_desc, -1); else check_media_type(media_type, nv12_stream_type_desc, -1); IMFMediaType_Release(media_type); + /* convert transform is only exposed with MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING */ hr = IMFSourceReader_GetServiceForStream(reader, 0, &GUID_NULL, &IID_IMFTransform, (void **)&transform); - ok(hr == E_NOINTERFACE, "Unexpected hr %#lx.\n", hr); + if (!enable_advanced) + ok(hr == E_NOINTERFACE, "Unexpected hr %#lx.\n", hr); + else + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (hr == S_OK) + { + hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, nv12_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + hr = IMFTransform_GetOutputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, rgb32_stream_type_desc, -1); + IMFMediaType_Release(media_type); - /* NV12 -> YUY2 conversion */ + IMFTransform_Release(transform); + } + + /* NV12 -> YUY2 conversion with MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING */ hr = MFCreateMediaType(&media_type); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - init_media_type(media_type, yuy2_stream_type_desc, -1); + init_media_type(media_type, yuy2_stream_type_desc, 2); /* doesn't need the frame size */ hr = IMFSourceReader_SetCurrentMediaType(reader, 0, NULL, media_type); - ok(hr == MF_E_TOPO_CODEC_NOT_FOUND, "Unexpected hr %#lx.\n", hr); + if (enable_advanced) + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + else + ok(hr == MF_E_TOPO_CODEC_NOT_FOUND, "Unexpected hr %#lx.\n", hr); IMFMediaType_Release(media_type); hr = IMFSourceReader_GetCurrentMediaType(reader, 0, &media_type); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (enable_processing) + if (enable_advanced) + check_media_type(media_type, yuy2_expect_advanced_desc, -1); + else if (enable_processing) check_media_type(media_type, rgb32_expect_desc, -1); else check_media_type(media_type, nv12_stream_type_desc, -1); IMFMediaType_Release(media_type); - /* even though we are now converting to RGB32 the converter transform is not exposed */ + /* convert transform is only exposed with MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING */ hr = IMFSourceReader_GetServiceForStream(reader, 0, &GUID_NULL, &IID_IMFTransform, (void **)&transform); - ok(hr == E_NOINTERFACE, "Unexpected hr %#lx.\n", hr); + if (!enable_advanced) + ok(hr == E_NOINTERFACE, "Unexpected hr %#lx.\n", hr); + else + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (hr == S_OK) + { + hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, nv12_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + hr = IMFTransform_GetOutputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, yuy2_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + IMFTransform_Release(transform); + } - /* not even through the IMFSourceReaderEx interface */ hr = IMFSourceReader_QueryInterface(reader, &IID_IMFSourceReaderEx, (void **)&reader_ex); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFSourceReaderEx_GetTransformForStream(reader_ex, 0, 0, &category, &transform); - todo_wine ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); + if (!enable_advanced) + todo_wine ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); + else + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (hr == S_OK) + { + hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, nv12_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + hr = IMFTransform_GetOutputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, yuy2_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + IMFTransform_Release(transform); + } + hr = IMFSourceReaderEx_GetTransformForStream(reader_ex, 0, 1, &category, &transform); todo_wine ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); IMFSourceReaderEx_Release(reader_ex); @@ -1938,12 +2052,12 @@ static void test_source_reader_transforms(BOOL enable_processing) hr = IMFSourceReader_GetServiceForStream(reader, 0, &GUID_NULL, &IID_IMFTransform, (void **)&transform); ok(hr == E_NOINTERFACE, "Unexpected hr %#lx.\n", hr); - /* H264 -> RGB32 conversion with MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING */ + /* H264 -> RGB32 conversion with MF_SOURCE_READER_ENABLE_(ADVANCED_)VIDEO_PROCESSING */ hr = MFCreateMediaType(&media_type); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); init_media_type(media_type, rgb32_stream_type_desc, 2); /* doesn't need the frame size */ hr = IMFSourceReader_SetCurrentMediaType(reader, 0, NULL, media_type); - if (enable_processing) + if (enable_processing || enable_advanced) todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); else todo_wine ok(hr == MF_E_INVALIDMEDIATYPE, "Unexpected hr %#lx.\n", hr); @@ -1951,15 +2065,17 @@ static void test_source_reader_transforms(BOOL enable_processing) hr = IMFSourceReader_GetCurrentMediaType(reader, 0, &media_type); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (enable_processing) + if (enable_advanced) + check_media_type(media_type, rgb32_expect_advanced_desc, -1); + else if (enable_processing) check_media_type(media_type, rgb32_expect_desc, -1); else check_media_type(media_type, h264_stream_type_desc, -1); IMFMediaType_Release(media_type); - /* H264 decoder transform is exposed with MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING */ + /* the exposed transform is the H264 decoder or the converter with MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING */ hr = IMFSourceReader_GetServiceForStream(reader, 0, &GUID_NULL, &IID_IMFTransform, (void **)&transform); - if (!enable_processing) + if (!enable_processing && !enable_advanced) ok(hr == E_NOINTERFACE, "Unexpected hr %#lx.\n", hr); else todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -1967,13 +2083,19 @@ static void test_source_reader_transforms(BOOL enable_processing) { hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - check_media_type(media_type, h264_stream_type_desc, -1); + if (enable_advanced) + check_media_type(media_type, nv12_stream_type_desc, -1); + else + check_media_type(media_type, h264_stream_type_desc, -1); IMFMediaType_Release(media_type); /* with NV12 output */ hr = IMFTransform_GetOutputCurrentType(transform, 0, &media_type); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - check_media_type(media_type, nv12_stream_type_desc, -1); + if (enable_advanced) + check_media_type(media_type, rgb32_stream_type_desc, -1); + else + check_media_type(media_type, nv12_stream_type_desc, -1); IMFMediaType_Release(media_type); IMFTransform_Release(transform); @@ -1983,7 +2105,7 @@ static void test_source_reader_transforms(BOOL enable_processing) hr = IMFSourceReader_QueryInterface(reader, &IID_IMFSourceReaderEx, (void **)&reader_ex); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFSourceReaderEx_GetTransformForStream(reader_ex, 0, 0, &category, &transform); - if (!enable_processing) + if (!enable_processing && !enable_advanced) todo_wine ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); else todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -2002,7 +2124,28 @@ static void test_source_reader_transforms(BOOL enable_processing) IMFTransform_Release(transform); } + /* the video processor can be accessed at index 1 with MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING */ hr = IMFSourceReaderEx_GetTransformForStream(reader_ex, 0, 1, &category, &transform); + if (!enable_advanced) + todo_wine ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); + else + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (hr == S_OK) + { + hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, nv12_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + hr = IMFTransform_GetOutputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_media_type(media_type, rgb32_stream_type_desc, -1); + IMFMediaType_Release(media_type); + + IMFTransform_Release(transform); + } + + hr = IMFSourceReaderEx_GetTransformForStream(reader_ex, 0, 2, &category, &transform); todo_wine ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); IMFSourceReaderEx_Release(reader_ex); @@ -2109,8 +2252,9 @@ START_TEST(mfplat) test_source_reader("test.wav", false); test_source_reader("test.mp4", true); test_source_reader_from_media_source(); - test_source_reader_transforms(FALSE); - test_source_reader_transforms(TRUE); + test_source_reader_transforms(FALSE, FALSE); + test_source_reader_transforms(TRUE, FALSE); + test_source_reader_transforms(FALSE, TRUE); test_reader_d3d9(); test_sink_writer_create(); test_sink_writer_mp4(); From e7b96cc66ef5f22e8c7198b31d308737dcddb057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 22 Nov 2023 22:22:22 +0100 Subject: [PATCH 036/413] HACK: winegstreamer/new_media_source: Clone the media source to new_media_source.c. For an experimental, non-decoding, source rewrite, guarded with WINE_NEW_MEDIA_SOURCE env var. CW-Bug-Id: #21953 --- dlls/mfplat/main.c | 5 + dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/mfplat.c | 2 + dlls/winegstreamer/new_media_source.c | 2053 ++++++++++++++++++ dlls/winegstreamer/winegstreamer_classes.idl | 6 + 6 files changed, 2068 insertions(+) create mode 100644 dlls/winegstreamer/new_media_source.c diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index 4805812e4d1..e545fd5b999 100644 --- a/dlls/mfplat/main.c +++ b/dlls/mfplat/main.c @@ -6296,6 +6296,11 @@ static HRESULT resolver_get_bytestream_url_hint(IMFByteStream *stream, WCHAR con static HRESULT resolver_create_gstreamer_handler(IMFByteStreamHandler **handler) { static const GUID CLSID_GStreamerByteStreamHandler = {0x317df618, 0x5e5a, 0x468a, {0x9f, 0x15, 0xd8, 0x27, 0xa9, 0xa0, 0x81, 0x62}}; + static const GUID CLSID_GStreamerByteStreamHandler2 = {0x317df619, 0x5e5a, 0x468a, {0x9f, 0x15, 0xd8, 0x27, 0xa9, 0xa0, 0x81, 0x62}}; + + const char *env = getenv("WINE_NEW_MEDIA_SOURCE"); + if (env && atoi(env)) return CoCreateInstance(&CLSID_GStreamerByteStreamHandler2, NULL, CLSCTX_INPROC_SERVER, &IID_IMFByteStreamHandler, (void **)handler); + return CoCreateInstance(&CLSID_GStreamerByteStreamHandler, NULL, CLSCTX_INPROC_SERVER, &IID_IMFByteStreamHandler, (void **)handler); } diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 02b1b04fdf9..af64d13b7b7 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -14,6 +14,7 @@ SOURCES = \ media_sink.c \ media_source.c \ mfplat.c \ + new_media_source.c \ quartz_parser.c \ quartz_transform.c \ resampler.c \ diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 8bc50d6cd99..e258a5ce532 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -166,6 +166,7 @@ HRESULT wg_transform_read_quartz(wg_transform_t transform, struct wg_sample *sam HRESULT wg_transform_read_dmo(wg_transform_t transform, DMO_OUTPUT_DATA_BUFFER *buffer); HRESULT gstreamer_byte_stream_handler_create(REFIID riid, void **obj); +HRESULT gstreamer_byte_stream_handler_2_create(REFIID riid, void **obj); unsigned int wg_format_get_stride(const struct wg_format *format); diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 4372678fe0e..2537c6bf455 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -122,6 +122,7 @@ static const IClassFactoryVtbl class_factory_vtbl = }; static const GUID CLSID_GStreamerByteStreamHandler = {0x317df618, 0x5e5a, 0x468a, {0x9f, 0x15, 0xd8, 0x27, 0xa9, 0xa0, 0x81, 0x62}}; +static const GUID CLSID_GStreamerByteStreamHandler2 = {0x317df619, 0x5e5a, 0x468a, {0x9f, 0x15, 0xd8, 0x27, 0xa9, 0xa0, 0x81, 0x62}}; static const GUID CLSID_GStreamerSchemePlugin = {0x587eeb6a,0x7336,0x4ebd,{0xa4,0xf2,0x91,0xc9,0x48,0xde,0x62,0x2c}}; @@ -134,6 +135,7 @@ class_objects[] = { { &CLSID_VideoProcessorMFT, &video_processor_create }, { &CLSID_GStreamerByteStreamHandler, &gstreamer_byte_stream_handler_create }, + { &CLSID_GStreamerByteStreamHandler2, &gstreamer_byte_stream_handler_2_create }, { &CLSID_MSAACDecMFT, &aac_decoder_create }, { &CLSID_MSH264DecoderMFT, &h264_decoder_create }, { &CLSID_GStreamerSchemePlugin, &gstreamer_scheme_handler_create }, diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c new file mode 100644 index 00000000000..2250a5faab8 --- /dev/null +++ b/dlls/winegstreamer/new_media_source.c @@ -0,0 +1,2053 @@ +/* GStreamer Media Source + * + * Copyright 2020 Derek Lesho + * Copyright 2020 Zebediah Figura 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 + */ + +#include "gst_private.h" + +#include "mfapi.h" +#include "mferror.h" + +#include "wine/list.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +struct object_context +{ + IUnknown IUnknown_iface; + LONG refcount; + + IMFAsyncResult *result; + IMFByteStream *stream; + UINT64 file_size; + WCHAR *url; +}; + +static struct object_context *impl_from_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct object_context, IUnknown_iface); +} + +static HRESULT WINAPI object_context_QueryInterface(IUnknown *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IUnknown_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI object_context_AddRef(IUnknown *iface) +{ + struct object_context *context = impl_from_IUnknown(iface); + ULONG refcount = InterlockedIncrement(&context->refcount); + + TRACE("%p, refcount %lu.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI object_context_Release(IUnknown *iface) +{ + struct object_context *context = impl_from_IUnknown(iface); + ULONG refcount = InterlockedDecrement(&context->refcount); + + TRACE("%p, refcount %lu.\n", iface, refcount); + + if (!refcount) + { + IMFAsyncResult_Release(context->result); + IMFByteStream_Release(context->stream); + free(context->url); + free(context); + } + + return refcount; +} + +static const IUnknownVtbl object_context_vtbl = +{ + object_context_QueryInterface, + object_context_AddRef, + object_context_Release, +}; + +static HRESULT object_context_create(DWORD flags, IMFByteStream *stream, const WCHAR *url, + QWORD file_size, IMFAsyncResult *result, IUnknown **out) +{ + WCHAR *tmp_url = url ? wcsdup(url) : NULL; + struct object_context *context; + + if (!(context = calloc(1, sizeof(*context)))) + { + free(tmp_url); + return E_OUTOFMEMORY; + } + + context->IUnknown_iface.lpVtbl = &object_context_vtbl; + context->refcount = 1; + context->stream = stream; + IMFByteStream_AddRef(context->stream); + context->file_size = file_size; + context->url = tmp_url; + context->result = result; + IMFAsyncResult_AddRef(context->result); + + *out = &context->IUnknown_iface; + return S_OK; +} + +struct media_stream +{ + IMFMediaStream IMFMediaStream_iface; + LONG ref; + + IMFMediaSource *media_source; + IMFMediaEventQueue *event_queue; + IMFStreamDescriptor *descriptor; + + wg_parser_stream_t wg_stream; + + IUnknown **token_queue; + LONG token_queue_count; + LONG token_queue_cap; + + DWORD stream_id; + BOOL active; + BOOL eos; +}; + +enum source_async_op +{ + SOURCE_ASYNC_START, + SOURCE_ASYNC_PAUSE, + SOURCE_ASYNC_STOP, + SOURCE_ASYNC_REQUEST_SAMPLE, +}; + +struct source_async_command +{ + IUnknown IUnknown_iface; + LONG refcount; + enum source_async_op op; + union + { + struct + { + IMFPresentationDescriptor *descriptor; + GUID format; + PROPVARIANT position; + } start; + struct + { + struct media_stream *stream; + IUnknown *token; + } request_sample; + } u; +}; + +struct media_source +{ + IMFMediaSource IMFMediaSource_iface; + IMFGetService IMFGetService_iface; + IMFRateSupport IMFRateSupport_iface; + IMFRateControl IMFRateControl_iface; + IMFAsyncCallback async_commands_callback; + LONG ref; + DWORD async_commands_queue; + IMFMediaEventQueue *event_queue; + IMFByteStream *byte_stream; + + CRITICAL_SECTION cs; + + UINT64 file_size; + wg_parser_t wg_parser; + UINT64 duration; + + IMFStreamDescriptor **descriptors; + struct media_stream **streams; + ULONG stream_count; + + enum + { + SOURCE_OPENING, + SOURCE_STOPPED, + SOURCE_PAUSED, + SOURCE_RUNNING, + SOURCE_SHUTDOWN, + } state; + float rate; + + HANDLE read_thread; + bool read_thread_shutdown; +}; + +static inline struct media_stream *impl_from_IMFMediaStream(IMFMediaStream *iface) +{ + return CONTAINING_RECORD(iface, struct media_stream, IMFMediaStream_iface); +} + +static inline struct media_source *impl_from_IMFMediaSource(IMFMediaSource *iface) +{ + return CONTAINING_RECORD(iface, struct media_source, IMFMediaSource_iface); +} + +static inline struct media_source *impl_from_IMFGetService(IMFGetService *iface) +{ + return CONTAINING_RECORD(iface, struct media_source, IMFGetService_iface); +} + +static inline struct media_source *impl_from_IMFRateSupport(IMFRateSupport *iface) +{ + return CONTAINING_RECORD(iface, struct media_source, IMFRateSupport_iface); +} + +static inline struct media_source *impl_from_IMFRateControl(IMFRateControl *iface) +{ + return CONTAINING_RECORD(iface, struct media_source, IMFRateControl_iface); +} + +static inline struct media_source *impl_from_async_commands_callback_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct media_source, async_commands_callback); +} + +static inline struct source_async_command *impl_from_async_command_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct source_async_command, IUnknown_iface); +} + +static HRESULT WINAPI source_async_command_QueryInterface(IUnknown *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IUnknown_AddRef(iface); + return S_OK; + } + + WARN("Unsupported interface %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI source_async_command_AddRef(IUnknown *iface) +{ + struct source_async_command *command = impl_from_async_command_IUnknown(iface); + return InterlockedIncrement(&command->refcount); +} + +static ULONG WINAPI source_async_command_Release(IUnknown *iface) +{ + struct source_async_command *command = impl_from_async_command_IUnknown(iface); + ULONG refcount = InterlockedDecrement(&command->refcount); + + if (!refcount) + { + if (command->op == SOURCE_ASYNC_START) + { + IMFPresentationDescriptor_Release(command->u.start.descriptor); + PropVariantClear(&command->u.start.position); + } + else if (command->op == SOURCE_ASYNC_REQUEST_SAMPLE) + { + if (command->u.request_sample.token) + IUnknown_Release(command->u.request_sample.token); + } + free(command); + } + + return refcount; +} + +static const IUnknownVtbl source_async_command_vtbl = +{ + source_async_command_QueryInterface, + source_async_command_AddRef, + source_async_command_Release, +}; + +static HRESULT source_create_async_op(enum source_async_op op, IUnknown **out) +{ + struct source_async_command *command; + + if (!(command = calloc(1, sizeof(*command)))) + return E_OUTOFMEMORY; + + command->IUnknown_iface.lpVtbl = &source_async_command_vtbl; + command->refcount = 1; + command->op = op; + + *out = &command->IUnknown_iface; + return S_OK; +} + +static HRESULT WINAPI callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFAsyncCallback) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFAsyncCallback_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static HRESULT WINAPI callback_GetParameters(IMFAsyncCallback *iface, + DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static ULONG WINAPI source_async_commands_callback_AddRef(IMFAsyncCallback *iface) +{ + struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface); + return IMFMediaSource_AddRef(&source->IMFMediaSource_iface); +} + +static ULONG WINAPI source_async_commands_callback_Release(IMFAsyncCallback *iface) +{ + struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface); + return IMFMediaSource_Release(&source->IMFMediaSource_iface); +} + +static HRESULT stream_descriptor_get_media_type(IMFStreamDescriptor *descriptor, IMFMediaType **media_type) +{ + IMFMediaTypeHandler *handler; + HRESULT hr; + + if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(descriptor, &handler))) + return hr; + hr = IMFMediaTypeHandler_GetCurrentMediaType(handler, media_type); + IMFMediaTypeHandler_Release(handler); + + return hr; +} + +static HRESULT wg_format_from_stream_descriptor(IMFStreamDescriptor *descriptor, struct wg_format *format) +{ + IMFMediaType *media_type; + HRESULT hr; + + if (FAILED(hr = stream_descriptor_get_media_type(descriptor, &media_type))) + return hr; + mf_media_type_to_wg_format(media_type, format); + IMFMediaType_Release(media_type); + + return hr; +} + +static HRESULT stream_descriptor_set_tag(IMFStreamDescriptor *descriptor, wg_parser_stream_t stream, + const GUID *attr, enum wg_parser_tag tag) +{ + WCHAR *strW; + HRESULT hr; + DWORD len; + char *str; + + if (!(str = wg_parser_stream_get_tag(stream, tag)) + || !(len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0))) + hr = S_OK; + else if (!(strW = malloc(len * sizeof(*strW)))) + hr = E_OUTOFMEMORY; + else + { + if (MultiByteToWideChar(CP_UTF8, 0, str, -1, strW, len)) + hr = IMFStreamDescriptor_SetString(descriptor, attr, strW); + else + hr = E_FAIL; + free(strW); + } + + free(str); + return hr; +} + +static HRESULT init_video_media_types(struct wg_format *format, IMFMediaType *types[6], DWORD *types_count) +{ + /* Try to prefer YUV formats over RGB ones. Most decoders output in the + * YUV color space, and it's generally much less expensive for + * videoconvert to do YUV -> YUV transformations. */ + static const enum wg_video_format video_formats[] = + { + WG_VIDEO_FORMAT_NV12, + WG_VIDEO_FORMAT_YV12, + WG_VIDEO_FORMAT_YUY2, + WG_VIDEO_FORMAT_I420, + }; + UINT count = *types_count, i; + GUID base_subtype; + HRESULT hr; + + if (FAILED(hr = IMFMediaType_GetGUID(types[0], &MF_MT_SUBTYPE, &base_subtype))) + return hr; + + for (i = 0; i < ARRAY_SIZE(video_formats); ++i) + { + struct wg_format new_format = *format; + IMFMediaType *new_type; + + new_format.u.video.format = video_formats[i]; + + if (!(new_type = mf_media_type_from_wg_format(&new_format))) + { + hr = E_OUTOFMEMORY; + goto done; + } + types[count++] = new_type; + + if (video_formats[i] == WG_VIDEO_FORMAT_I420) + { + IMFMediaType *iyuv_type; + + if (FAILED(hr = MFCreateMediaType(&iyuv_type))) + goto done; + if (FAILED(hr = IMFMediaType_CopyAllItems(new_type, (IMFAttributes *)iyuv_type))) + goto done; + if (FAILED(hr = IMFMediaType_SetGUID(iyuv_type, &MF_MT_SUBTYPE, &MFVideoFormat_IYUV))) + goto done; + types[count++] = iyuv_type; + } + } + +done: + *types_count = count; + return hr; +} + +static HRESULT init_audio_media_types(struct wg_format *format, IMFMediaType *types[6], DWORD *types_count) +{ + /* Expose at least one PCM and one floating point type for the + consumer to pick from. */ + static const enum wg_audio_format audio_types[] = + { + WG_AUDIO_FORMAT_S16LE, + WG_AUDIO_FORMAT_F32LE, + }; + UINT count = *types_count, i; + + for (i = 0; i < ARRAY_SIZE(audio_types); i++) + { + struct wg_format new_format = *format; + if (new_format.u.audio.format == audio_types[i]) + continue; + new_format.u.audio.format = audio_types[i]; + if ((types[count] = mf_media_type_from_wg_format(&new_format))) + count++; + } + + *types_count = count; + return S_OK; +} + +static HRESULT stream_descriptor_create(UINT32 id, struct wg_format *format, IMFStreamDescriptor **out) +{ + IMFStreamDescriptor *descriptor; + IMFMediaTypeHandler *handler; + IMFMediaType *types[6]; + DWORD count = 0; + HRESULT hr; + + if (!(types[0] = mf_media_type_from_wg_format(format))) + return MF_E_INVALIDMEDIATYPE; + count = 1; + + if (format->major_type == WG_MAJOR_TYPE_VIDEO) + { + if (FAILED(hr = init_video_media_types(format, types, &count))) + goto done; + } + else if (format->major_type == WG_MAJOR_TYPE_AUDIO) + { + if (FAILED(hr = init_audio_media_types(format, types, &count))) + goto done; + } + + assert(count <= ARRAY_SIZE(types)); + + if (FAILED(hr = MFCreateStreamDescriptor(id, count, types, &descriptor))) + goto done; + + if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(descriptor, &handler))) + IMFStreamDescriptor_Release(descriptor); + else + { + hr = IMFMediaTypeHandler_SetCurrentMediaType(handler, types[0]); + IMFMediaTypeHandler_Release(handler); + } + +done: + while (count--) + IMFMediaType_Release(types[count]); + *out = SUCCEEDED(hr) ? descriptor : NULL; + return hr; +} + +static BOOL enqueue_token(struct media_stream *stream, IUnknown *token) +{ + if (stream->token_queue_count == stream->token_queue_cap) + { + IUnknown **buf; + stream->token_queue_cap = stream->token_queue_cap * 2 + 1; + buf = realloc(stream->token_queue, stream->token_queue_cap * sizeof(*buf)); + if (buf) + stream->token_queue = buf; + else + { + stream->token_queue_cap = stream->token_queue_count; + return FALSE; + } + } + stream->token_queue[stream->token_queue_count++] = token; + return TRUE; +} + +static void flush_token_queue(struct media_stream *stream, BOOL send) +{ + struct media_source *source = impl_from_IMFMediaSource(stream->media_source); + LONG i; + + for (i = 0; i < stream->token_queue_count; i++) + { + if (send) + { + IUnknown *op; + HRESULT hr; + + if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_REQUEST_SAMPLE, &op))) + { + struct source_async_command *command = impl_from_async_command_IUnknown(op); + command->u.request_sample.stream = stream; + command->u.request_sample.token = stream->token_queue[i]; + + hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, op); + IUnknown_Release(op); + } + if (FAILED(hr)) + WARN("Could not enqueue sample request, hr %#lx\n", hr); + } + else if (stream->token_queue[i]) + IUnknown_Release(stream->token_queue[i]); + } + free(stream->token_queue); + stream->token_queue = NULL; + stream->token_queue_count = 0; + stream->token_queue_cap = 0; +} + +static HRESULT media_stream_start(struct media_stream *stream, BOOL active, BOOL seeking, const PROPVARIANT *position) +{ + struct media_source *source = impl_from_IMFMediaSource(stream->media_source); + struct wg_format format; + HRESULT hr; + + TRACE("source %p, stream %p\n", source, stream); + + if (FAILED(hr = wg_format_from_stream_descriptor(stream->descriptor, &format))) + WARN("Failed to get wg_format from stream descriptor, hr %#lx\n", hr); + wg_parser_stream_enable(stream->wg_stream, &format, 0); + + if (FAILED(hr = IMFMediaEventQueue_QueueEventParamUnk(source->event_queue, active ? MEUpdatedStream : MENewStream, + &GUID_NULL, S_OK, (IUnknown *)&stream->IMFMediaStream_iface))) + WARN("Failed to send source stream event, hr %#lx\n", hr); + return IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, seeking ? MEStreamSeeked : MEStreamStarted, + &GUID_NULL, S_OK, position); +} + +static HRESULT media_source_start(struct media_source *source, IMFPresentationDescriptor *descriptor, + GUID *format, PROPVARIANT *position) +{ + BOOL starting = source->state == SOURCE_STOPPED, seek_message = !starting && position->vt != VT_EMPTY; + IMFStreamDescriptor **descriptors; + DWORD i, count; + HRESULT hr; + + TRACE("source %p, descriptor %p, format %s, position %s\n", source, descriptor, + debugstr_guid(format), wine_dbgstr_variant((VARIANT *)position)); + + if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + /* seek to beginning on stop->play */ + if (source->state == SOURCE_STOPPED && position->vt == VT_EMPTY) + { + position->vt = VT_I8; + position->hVal.QuadPart = 0; + } + + if (!(descriptors = calloc(source->stream_count, sizeof(*descriptors)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = IMFPresentationDescriptor_GetStreamDescriptorCount(descriptor, &count))) + WARN("Failed to get presentation descriptor stream count, hr %#lx\n", hr); + + for (i = 0; i < count; i++) + { + IMFStreamDescriptor *stream_descriptor; + BOOL selected; + DWORD id; + + if (FAILED(hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(descriptor, i, + &selected, &stream_descriptor))) + WARN("Failed to get presentation stream descriptor, hr %#lx\n", hr); + else + { + if (FAILED(hr = IMFStreamDescriptor_GetStreamIdentifier(stream_descriptor, &id))) + WARN("Failed to get stream descriptor id, hr %#lx\n", hr); + else if (id >= source->stream_count) + WARN("Invalid stream descriptor id %lu, hr %#lx\n", id, hr); + else if (selected) + IMFStreamDescriptor_AddRef((descriptors[id] = stream_descriptor)); + + IMFStreamDescriptor_Release(stream_descriptor); + } + } + + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + BOOL was_active = !starting && stream->active; + + if (position->vt != VT_EMPTY) + stream->eos = FALSE; + + if (!(stream->active = !!descriptors[i])) + wg_parser_stream_disable(stream->wg_stream); + else + { + if (FAILED(hr = media_stream_start(stream, was_active, seek_message, position))) + WARN("Failed to start media stream, hr %#lx\n", hr); + IMFStreamDescriptor_Release(descriptors[i]); + } + } + + free(descriptors); + + source->state = SOURCE_RUNNING; + + if (position->vt == VT_I8) + wg_parser_stream_seek(source->streams[0]->wg_stream, 1.0, position->hVal.QuadPart, 0, + AM_SEEKING_AbsolutePositioning, AM_SEEKING_NoPositioning); + + for (i = 0; i < source->stream_count; i++) + flush_token_queue(source->streams[i], position->vt == VT_EMPTY); + + return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, + seek_message ? MESourceSeeked : MESourceStarted, &GUID_NULL, S_OK, position); +} + +static HRESULT media_source_pause(struct media_source *source) +{ + unsigned int i; + HRESULT hr; + + TRACE("source %p\n", source); + + if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + if (stream->active && FAILED(hr = IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEStreamPaused, + &GUID_NULL, S_OK, NULL))) + WARN("Failed to queue MEStreamPaused event, hr %#lx\n", hr); + } + + source->state = SOURCE_PAUSED; + return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MESourcePaused, &GUID_NULL, S_OK, NULL); +} + +static HRESULT media_source_stop(struct media_source *source) +{ + unsigned int i; + HRESULT hr; + + TRACE("source %p\n", source); + + if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + if (stream->active && FAILED(hr = IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEStreamStopped, + &GUID_NULL, S_OK, NULL))) + WARN("Failed to queue MEStreamStopped event, hr %#lx\n", hr); + } + + source->state = SOURCE_STOPPED; + + for (i = 0; i < source->stream_count; i++) + flush_token_queue(source->streams[i], FALSE); + + return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MESourceStopped, &GUID_NULL, S_OK, NULL); +} + +static HRESULT media_stream_send_sample(struct media_stream *stream, const struct wg_parser_buffer *wg_buffer, IUnknown *token) +{ + IMFSample *sample = NULL; + IMFMediaBuffer *buffer; + HRESULT hr; + BYTE *data; + + if (FAILED(hr = MFCreateMemoryBuffer(wg_buffer->size, &buffer))) + return hr; + if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(buffer, wg_buffer->size))) + goto out; + if (FAILED(hr = IMFMediaBuffer_Lock(buffer, &data, NULL, NULL))) + goto out; + + if (!wg_parser_stream_copy_buffer(stream->wg_stream, data, 0, wg_buffer->size)) + { + wg_parser_stream_release_buffer(stream->wg_stream); + IMFMediaBuffer_Unlock(buffer); + goto out; + } + wg_parser_stream_release_buffer(stream->wg_stream); + + if (FAILED(hr = IMFMediaBuffer_Unlock(buffer))) + goto out; + + if (FAILED(hr = MFCreateSample(&sample))) + goto out; + if (FAILED(hr = IMFSample_AddBuffer(sample, buffer))) + goto out; + if (FAILED(hr = IMFSample_SetSampleTime(sample, wg_buffer->pts))) + goto out; + if (FAILED(hr = IMFSample_SetSampleDuration(sample, wg_buffer->duration))) + goto out; + if (token && FAILED(hr = IMFSample_SetUnknown(sample, &MFSampleExtension_Token, token))) + goto out; + + hr = IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, + &GUID_NULL, S_OK, (IUnknown *)sample); + +out: + if (sample) + IMFSample_Release(sample); + IMFMediaBuffer_Release(buffer); + return hr; +} + +static HRESULT media_stream_send_eos(struct media_source *source, struct media_stream *stream) +{ + PROPVARIANT empty = {.vt = VT_EMPTY}; + HRESULT hr; + UINT i; + + TRACE("source %p, stream %p\n", source, stream); + + stream->eos = TRUE; + if (FAILED(hr = IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEEndOfStream, &GUID_NULL, S_OK, &empty))) + WARN("Failed to queue MEEndOfStream event, hr %#lx\n", hr); + + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + if (stream->active && !stream->eos) + return S_OK; + } + + if (FAILED(hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MEEndOfPresentation, &GUID_NULL, S_OK, &empty))) + WARN("Failed to queue MEEndOfPresentation event, hr %#lx\n", hr); + return S_OK; +} + +static HRESULT wait_on_sample(struct media_stream *stream, IUnknown *token) +{ + struct media_source *source = impl_from_IMFMediaSource(stream->media_source); + struct wg_parser_buffer buffer; + + TRACE("%p, %p\n", stream, token); + + if (wg_parser_stream_get_buffer(source->wg_parser, stream->wg_stream, &buffer)) + return media_stream_send_sample(stream, &buffer, token); + + return media_stream_send_eos(source, stream); +} + +static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface); + struct source_async_command *command; + IUnknown *state; + HRESULT hr; + + if (FAILED(hr = IMFAsyncResult_GetState(result, &state))) + return hr; + + EnterCriticalSection(&source->cs); + + command = impl_from_async_command_IUnknown(state); + switch (command->op) + { + case SOURCE_ASYNC_START: + { + IMFPresentationDescriptor *descriptor = command->u.start.descriptor; + GUID format = command->u.start.format; + PROPVARIANT position = command->u.start.position; + + if (FAILED(hr = media_source_start(source, descriptor, &format, &position))) + WARN("Failed to start source %p, hr %#lx\n", source, hr); + break; + } + case SOURCE_ASYNC_PAUSE: + if (FAILED(hr = media_source_pause(source))) + WARN("Failed to pause source %p, hr %#lx\n", source, hr); + break; + case SOURCE_ASYNC_STOP: + if (FAILED(hr = media_source_stop(source))) + WARN("Failed to stop source %p, hr %#lx\n", source, hr); + break; + case SOURCE_ASYNC_REQUEST_SAMPLE: + if (source->state == SOURCE_PAUSED) + enqueue_token(command->u.request_sample.stream, command->u.request_sample.token); + else if (source->state == SOURCE_RUNNING) + { + if (FAILED(hr = wait_on_sample(command->u.request_sample.stream, command->u.request_sample.token))) + WARN("Failed to request sample, hr %#lx\n", hr); + } + break; + } + + LeaveCriticalSection(&source->cs); + + IUnknown_Release(state); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl source_async_commands_callback_vtbl = +{ + callback_QueryInterface, + source_async_commands_callback_AddRef, + source_async_commands_callback_Release, + callback_GetParameters, + source_async_commands_Invoke, +}; + +static DWORD CALLBACK read_thread(void *arg) +{ + struct media_source *source = arg; + IMFByteStream *byte_stream = source->byte_stream; + size_t buffer_size = 4096; + QWORD file_size; + void *data; + + if (!(data = malloc(buffer_size))) + return 0; + + IMFByteStream_GetLength(byte_stream, &file_size); + + TRACE("Starting read thread for media source %p.\n", source); + + while (!source->read_thread_shutdown) + { + uint64_t offset; + ULONG ret_size; + uint32_t size; + HRESULT hr; + + if (!wg_parser_get_next_read_offset(source->wg_parser, &offset, &size)) + continue; + + if (offset >= file_size) + size = 0; + else if (offset + size >= file_size) + size = file_size - offset; + + /* Some IMFByteStreams (including the standard file-based stream) return + * an error when reading past the file size. */ + if (!size) + { + wg_parser_push_data(source->wg_parser, data, 0); + continue; + } + + if (!array_reserve(&data, &buffer_size, size, 1)) + { + free(data); + return 0; + } + + ret_size = 0; + + if (SUCCEEDED(hr = IMFByteStream_SetCurrentPosition(byte_stream, offset))) + hr = IMFByteStream_Read(byte_stream, data, size, &ret_size); + if (FAILED(hr)) + ERR("Failed to read %u bytes at offset %I64u, hr %#lx.\n", size, offset, hr); + else if (ret_size != size) + ERR("Unexpected short read: requested %u bytes, got %lu.\n", size, ret_size); + wg_parser_push_data(source->wg_parser, SUCCEEDED(hr) ? data : NULL, ret_size); + } + + free(data); + TRACE("Media source is shutting down; exiting.\n"); + return 0; +} + +static HRESULT WINAPI media_stream_QueryInterface(IMFMediaStream *iface, REFIID riid, void **out) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), out); + + if (IsEqualIID(riid, &IID_IMFMediaStream) || + IsEqualIID(riid, &IID_IMFMediaEventGenerator) || + IsEqualIID(riid, &IID_IUnknown)) + { + *out = &stream->IMFMediaStream_iface; + } + else + { + FIXME("(%s, %p)\n", debugstr_guid(riid), out); + *out = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*out); + return S_OK; +} + +static ULONG WINAPI media_stream_AddRef(IMFMediaStream *iface) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + ULONG ref = InterlockedIncrement(&stream->ref); + + TRACE("%p, refcount %lu.\n", iface, ref); + + return ref; +} + +static ULONG WINAPI media_stream_Release(IMFMediaStream *iface) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + ULONG ref = InterlockedDecrement(&stream->ref); + + TRACE("%p, refcount %lu.\n", iface, ref); + + if (!ref) + { + IMFMediaSource_Release(stream->media_source); + IMFStreamDescriptor_Release(stream->descriptor); + IMFMediaEventQueue_Release(stream->event_queue); + flush_token_queue(stream, FALSE); + free(stream); + } + + return ref; +} + +static HRESULT WINAPI media_stream_GetEvent(IMFMediaStream *iface, DWORD flags, IMFMediaEvent **event) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("%p, %#lx, %p.\n", iface, flags, event); + + return IMFMediaEventQueue_GetEvent(stream->event_queue, flags, event); +} + +static HRESULT WINAPI media_stream_BeginGetEvent(IMFMediaStream *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("%p, %p, %p.\n", iface, callback, state); + + return IMFMediaEventQueue_BeginGetEvent(stream->event_queue, callback, state); +} + +static HRESULT WINAPI media_stream_EndGetEvent(IMFMediaStream *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("%p, %p, %p.\n", stream, result, event); + + return IMFMediaEventQueue_EndGetEvent(stream->event_queue, result, event); +} + +static HRESULT WINAPI media_stream_QueueEvent(IMFMediaStream *iface, MediaEventType event_type, REFGUID ext_type, + HRESULT hr, const PROPVARIANT *value) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("%p, %lu, %s, %#lx, %p.\n", iface, event_type, debugstr_guid(ext_type), hr, value); + + return IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI media_stream_GetMediaSource(IMFMediaStream *iface, IMFMediaSource **out) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + struct media_source *source = impl_from_IMFMediaSource(stream->media_source); + HRESULT hr = S_OK; + + TRACE("%p, %p.\n", iface, out); + + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else + { + IMFMediaSource_AddRef(&source->IMFMediaSource_iface); + *out = &source->IMFMediaSource_iface; + } + + LeaveCriticalSection(&source->cs); + + return hr; +} + +static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IMFStreamDescriptor **descriptor) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + struct media_source *source = impl_from_IMFMediaSource(stream->media_source); + HRESULT hr = S_OK; + + TRACE("%p, %p.\n", iface, descriptor); + + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else + { + IMFStreamDescriptor_AddRef(stream->descriptor); + *descriptor = stream->descriptor; + } + + LeaveCriticalSection(&source->cs); + + return hr; +} + +static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + struct media_source *source = impl_from_IMFMediaSource(stream->media_source); + IUnknown *op; + HRESULT hr; + + TRACE("%p, %p.\n", iface, token); + + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else if (!stream->active) + hr = MF_E_MEDIA_SOURCE_WRONGSTATE; + else if (stream->eos) + hr = MF_E_END_OF_STREAM; + else if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_REQUEST_SAMPLE, &op))) + { + struct source_async_command *command = impl_from_async_command_IUnknown(op); + command->u.request_sample.stream = stream; + if (token) + IUnknown_AddRef(token); + command->u.request_sample.token = token; + + hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, op); + IUnknown_Release(op); + } + + LeaveCriticalSection(&source->cs); + + return hr; +} + +static const IMFMediaStreamVtbl media_stream_vtbl = +{ + media_stream_QueryInterface, + media_stream_AddRef, + media_stream_Release, + media_stream_GetEvent, + media_stream_BeginGetEvent, + media_stream_EndGetEvent, + media_stream_QueueEvent, + media_stream_GetMediaSource, + media_stream_GetStreamDescriptor, + media_stream_RequestSample +}; + +static HRESULT media_stream_create(IMFMediaSource *source, IMFStreamDescriptor *descriptor, + wg_parser_stream_t wg_stream, struct media_stream **out) +{ + struct media_stream *object; + HRESULT hr; + + TRACE("source %p, descriptor %p, wg_stream %#I64x.\n", source, descriptor, wg_stream); + + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + + object->IMFMediaStream_iface.lpVtbl = &media_stream_vtbl; + object->ref = 1; + + if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) + { + free(object); + return hr; + } + + IMFMediaSource_AddRef(source); + object->media_source = source; + IMFStreamDescriptor_AddRef(descriptor); + object->descriptor = descriptor; + + object->active = TRUE; + object->eos = FALSE; + object->wg_stream = wg_stream; + + TRACE("Created stream object %p.\n", object); + + *out = object; + return S_OK; +} + +static HRESULT WINAPI media_source_get_service_QueryInterface(IMFGetService *iface, REFIID riid, void **obj) +{ + struct media_source *source = impl_from_IMFGetService(iface); + return IMFMediaSource_QueryInterface(&source->IMFMediaSource_iface, riid, obj); +} + +static ULONG WINAPI media_source_get_service_AddRef(IMFGetService *iface) +{ + struct media_source *source = impl_from_IMFGetService(iface); + return IMFMediaSource_AddRef(&source->IMFMediaSource_iface); +} + +static ULONG WINAPI media_source_get_service_Release(IMFGetService *iface) +{ + struct media_source *source = impl_from_IMFGetService(iface); + return IMFMediaSource_Release(&source->IMFMediaSource_iface); +} + +static HRESULT WINAPI media_source_get_service_GetService(IMFGetService *iface, REFGUID service, REFIID riid, void **obj) +{ + struct media_source *source = impl_from_IMFGetService(iface); + + TRACE("%p, %s, %s, %p.\n", iface, debugstr_guid(service), debugstr_guid(riid), obj); + + *obj = NULL; + + if (IsEqualGUID(service, &MF_RATE_CONTROL_SERVICE)) + { + if (IsEqualIID(riid, &IID_IMFRateSupport)) + { + *obj = &source->IMFRateSupport_iface; + } + else if (IsEqualIID(riid, &IID_IMFRateControl)) + { + *obj = &source->IMFRateControl_iface; + } + } + else + FIXME("Unsupported service %s.\n", debugstr_guid(service)); + + if (*obj) + IUnknown_AddRef((IUnknown *)*obj); + + return *obj ? S_OK : E_NOINTERFACE; +} + +static const IMFGetServiceVtbl media_source_get_service_vtbl = +{ + media_source_get_service_QueryInterface, + media_source_get_service_AddRef, + media_source_get_service_Release, + media_source_get_service_GetService, +}; + +static HRESULT WINAPI media_source_rate_support_QueryInterface(IMFRateSupport *iface, REFIID riid, void **obj) +{ + struct media_source *source = impl_from_IMFRateSupport(iface); + return IMFMediaSource_QueryInterface(&source->IMFMediaSource_iface, riid, obj); +} + +static ULONG WINAPI media_source_rate_support_AddRef(IMFRateSupport *iface) +{ + struct media_source *source = impl_from_IMFRateSupport(iface); + return IMFMediaSource_AddRef(&source->IMFMediaSource_iface); +} + +static ULONG WINAPI media_source_rate_support_Release(IMFRateSupport *iface) +{ + struct media_source *source = impl_from_IMFRateSupport(iface); + return IMFMediaSource_Release(&source->IMFMediaSource_iface); +} + +static HRESULT WINAPI media_source_rate_support_GetSlowestRate(IMFRateSupport *iface, MFRATE_DIRECTION direction, BOOL thin, float *rate) +{ + TRACE("%p, %d, %d, %p.\n", iface, direction, thin, rate); + + *rate = 0.0f; + + return S_OK; +} + +static HRESULT WINAPI media_source_rate_support_GetFastestRate(IMFRateSupport *iface, MFRATE_DIRECTION direction, BOOL thin, float *rate) +{ + TRACE("%p, %d, %d, %p.\n", iface, direction, thin, rate); + + *rate = direction == MFRATE_FORWARD ? 1e6f : -1e6f; + + return S_OK; +} + +static HRESULT WINAPI media_source_rate_support_IsRateSupported(IMFRateSupport *iface, BOOL thin, float rate, + float *nearest_rate) +{ + TRACE("%p, %d, %f, %p.\n", iface, thin, rate, nearest_rate); + + if (nearest_rate) + *nearest_rate = rate; + + return rate >= -1e6f && rate <= 1e6f ? S_OK : MF_E_UNSUPPORTED_RATE; +} + +static const IMFRateSupportVtbl media_source_rate_support_vtbl = +{ + media_source_rate_support_QueryInterface, + media_source_rate_support_AddRef, + media_source_rate_support_Release, + media_source_rate_support_GetSlowestRate, + media_source_rate_support_GetFastestRate, + media_source_rate_support_IsRateSupported, +}; + +static HRESULT WINAPI media_source_rate_control_QueryInterface(IMFRateControl *iface, REFIID riid, void **obj) +{ + struct media_source *source = impl_from_IMFRateControl(iface); + return IMFMediaSource_QueryInterface(&source->IMFMediaSource_iface, riid, obj); +} + +static ULONG WINAPI media_source_rate_control_AddRef(IMFRateControl *iface) +{ + struct media_source *source = impl_from_IMFRateControl(iface); + return IMFMediaSource_AddRef(&source->IMFMediaSource_iface); +} + +static ULONG WINAPI media_source_rate_control_Release(IMFRateControl *iface) +{ + struct media_source *source = impl_from_IMFRateControl(iface); + return IMFMediaSource_Release(&source->IMFMediaSource_iface); +} + +static HRESULT WINAPI media_source_rate_control_SetRate(IMFRateControl *iface, BOOL thin, float rate) +{ + struct media_source *source = impl_from_IMFRateControl(iface); + HRESULT hr; + + FIXME("%p, %d, %f.\n", iface, thin, rate); + + if (rate < 0.0f) + return MF_E_REVERSE_UNSUPPORTED; + + if (thin) + return MF_E_THINNING_UNSUPPORTED; + + if (FAILED(hr = IMFRateSupport_IsRateSupported(&source->IMFRateSupport_iface, thin, rate, NULL))) + return hr; + + EnterCriticalSection(&source->cs); + source->rate = rate; + LeaveCriticalSection(&source->cs); + + return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MESourceRateChanged, &GUID_NULL, S_OK, NULL); +} + +static HRESULT WINAPI media_source_rate_control_GetRate(IMFRateControl *iface, BOOL *thin, float *rate) +{ + struct media_source *source = impl_from_IMFRateControl(iface); + + TRACE("%p, %p, %p.\n", iface, thin, rate); + + if (thin) + *thin = FALSE; + + EnterCriticalSection(&source->cs); + *rate = source->rate; + LeaveCriticalSection(&source->cs); + + return S_OK; +} + +static const IMFRateControlVtbl media_source_rate_control_vtbl = +{ + media_source_rate_control_QueryInterface, + media_source_rate_control_AddRef, + media_source_rate_control_Release, + media_source_rate_control_SetRate, + media_source_rate_control_GetRate, +}; + +static HRESULT WINAPI media_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), out); + + if (IsEqualIID(riid, &IID_IMFMediaSource) || + IsEqualIID(riid, &IID_IMFMediaEventGenerator) || + IsEqualIID(riid, &IID_IUnknown)) + { + *out = &source->IMFMediaSource_iface; + } + else if (IsEqualIID(riid, &IID_IMFGetService)) + { + *out = &source->IMFGetService_iface; + } + else + { + FIXME("%s, %p.\n", debugstr_guid(riid), out); + *out = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*out); + return S_OK; +} + +static ULONG WINAPI media_source_AddRef(IMFMediaSource *iface) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + ULONG ref = InterlockedIncrement(&source->ref); + + TRACE("%p, refcount %lu.\n", iface, ref); + + return ref; +} + +static ULONG WINAPI media_source_Release(IMFMediaSource *iface) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + ULONG ref = InterlockedDecrement(&source->ref); + + TRACE("%p, refcount %lu.\n", iface, ref); + + if (!ref) + { + IMFMediaSource_Shutdown(iface); + IMFMediaEventQueue_Release(source->event_queue); + IMFByteStream_Release(source->byte_stream); + wg_parser_destroy(source->wg_parser); + source->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&source->cs); + free(source); + } + + return ref; +} + +static HRESULT WINAPI media_source_GetEvent(IMFMediaSource *iface, DWORD flags, IMFMediaEvent **event) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("%p, %#lx, %p.\n", iface, flags, event); + + return IMFMediaEventQueue_GetEvent(source->event_queue, flags, event); +} + +static HRESULT WINAPI media_source_BeginGetEvent(IMFMediaSource *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("%p, %p, %p.\n", iface, callback, state); + + return IMFMediaEventQueue_BeginGetEvent(source->event_queue, callback, state); +} + +static HRESULT WINAPI media_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("%p, %p, %p.\n", iface, result, event); + + return IMFMediaEventQueue_EndGetEvent(source->event_queue, result, event); +} + +static HRESULT WINAPI media_source_QueueEvent(IMFMediaSource *iface, MediaEventType event_type, REFGUID ext_type, + HRESULT hr, const PROPVARIANT *value) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("%p, %lu, %s, %#lx, %p.\n", iface, event_type, debugstr_guid(ext_type), hr, value); + + return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI media_source_GetCharacteristics(IMFMediaSource *iface, DWORD *characteristics) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + HRESULT hr = S_OK; + + TRACE("%p, %p.\n", iface, characteristics); + + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else + *characteristics = MFMEDIASOURCE_CAN_SEEK | MFMEDIASOURCE_CAN_PAUSE; + + LeaveCriticalSection(&source->cs); + + return hr; +} + +static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **descriptor) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + HRESULT hr; + UINT i; + + TRACE("%p, %p.\n", iface, descriptor); + + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else if (SUCCEEDED(hr = MFCreatePresentationDescriptor(source->stream_count, source->descriptors, descriptor))) + { + if (FAILED(hr = IMFPresentationDescriptor_SetUINT64(*descriptor, &MF_PD_DURATION, source->duration))) + WARN("Failed to set presentation descriptor MF_PD_DURATION, hr %#lx\n", hr); + + for (i = 0; i < source->stream_count; ++i) + { + if (FAILED(hr = IMFPresentationDescriptor_SelectStream(*descriptor, i))) + WARN("Failed to select stream %u, hr %#lx\n", i, hr); + } + + hr = S_OK; + } + + LeaveCriticalSection(&source->cs); + + return hr; +} + +static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *descriptor, + const GUID *time_format, const PROPVARIANT *position) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + IUnknown *op; + HRESULT hr; + + TRACE("%p, %p, %p, %p.\n", iface, descriptor, time_format, position); + + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else if (!(IsEqualIID(time_format, &GUID_NULL))) + hr = MF_E_UNSUPPORTED_TIME_FORMAT; + else if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_START, &op))) + { + struct source_async_command *command = impl_from_async_command_IUnknown(op); + command->u.start.descriptor = descriptor; + IMFPresentationDescriptor_AddRef(descriptor); + command->u.start.format = *time_format; + PropVariantCopy(&command->u.start.position, position); + + hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, op); + IUnknown_Release(op); + } + + LeaveCriticalSection(&source->cs); + + return hr; +} + +static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + IUnknown *op; + HRESULT hr; + + TRACE("%p.\n", iface); + + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_STOP, &op))) + { + hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, op); + IUnknown_Release(op); + } + + LeaveCriticalSection(&source->cs); + + return hr; +} + +static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + IUnknown *op; + HRESULT hr; + + TRACE("%p.\n", iface); + + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else if (source->state != SOURCE_RUNNING) + hr = MF_E_INVALID_STATE_TRANSITION; + else if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_PAUSE, &op))) + { + hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, op); + IUnknown_Release(op); + } + + LeaveCriticalSection(&source->cs); + + return S_OK; +} + +static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("%p.\n", iface); + + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + { + LeaveCriticalSection(&source->cs); + return MF_E_SHUTDOWN; + } + + source->state = SOURCE_SHUTDOWN; + + wg_parser_disconnect(source->wg_parser); + + source->read_thread_shutdown = true; + WaitForSingleObject(source->read_thread, INFINITE); + CloseHandle(source->read_thread); + + IMFMediaEventQueue_Shutdown(source->event_queue); + IMFByteStream_Close(source->byte_stream); + + while (source->stream_count--) + { + struct media_stream *stream = source->streams[source->stream_count]; + IMFStreamDescriptor_Release(source->descriptors[source->stream_count]); + IMFMediaEventQueue_Shutdown(stream->event_queue); + IMFMediaStream_Release(&stream->IMFMediaStream_iface); + } + free(source->descriptors); + free(source->streams); + + LeaveCriticalSection(&source->cs); + + MFUnlockWorkQueue(source->async_commands_queue); + + return S_OK; +} + +static const IMFMediaSourceVtbl IMFMediaSource_vtbl = +{ + media_source_QueryInterface, + media_source_AddRef, + media_source_Release, + media_source_GetEvent, + media_source_BeginGetEvent, + media_source_EndGetEvent, + media_source_QueueEvent, + media_source_GetCharacteristics, + media_source_CreatePresentationDescriptor, + media_source_Start, + media_source_Stop, + media_source_Pause, + media_source_Shutdown, +}; + +static void media_source_init_descriptors(struct media_source *source) +{ + HRESULT hr = S_OK; + UINT i; + + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + IMFStreamDescriptor *descriptor = stream->descriptor; + + if (FAILED(hr = stream_descriptor_set_tag(descriptor, stream->wg_stream, + &MF_SD_LANGUAGE, WG_PARSER_TAG_LANGUAGE))) + WARN("Failed to set stream descriptor language, hr %#lx\n", hr); + if (FAILED(hr = stream_descriptor_set_tag(descriptor, stream->wg_stream, + &MF_SD_STREAM_NAME, WG_PARSER_TAG_NAME))) + WARN("Failed to set stream descriptor name, hr %#lx\n", hr); + } +} + +static HRESULT media_source_create(struct object_context *context, IMFMediaSource **out) +{ + unsigned int stream_count = UINT_MAX; + struct media_source *object; + wg_parser_t parser; + unsigned int i; + HRESULT hr; + + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + + object->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl; + object->IMFGetService_iface.lpVtbl = &media_source_get_service_vtbl; + object->IMFRateSupport_iface.lpVtbl = &media_source_rate_support_vtbl; + object->IMFRateControl_iface.lpVtbl = &media_source_rate_control_vtbl; + object->async_commands_callback.lpVtbl = &source_async_commands_callback_vtbl; + object->ref = 1; + object->byte_stream = context->stream; + IMFByteStream_AddRef(context->stream); + object->file_size = context->file_size; + object->rate = 1.0f; + InitializeCriticalSection(&object->cs); + object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs"); + + if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) + goto fail; + + if (FAILED(hr = MFAllocateWorkQueue(&object->async_commands_queue))) + goto fail; + + if (!(parser = wg_parser_create(WG_PARSER_DECODEBIN, FALSE, FALSE))) + { + hr = E_OUTOFMEMORY; + goto fail; + } + object->wg_parser = parser; + + object->read_thread = CreateThread(NULL, 0, read_thread, object, 0, NULL); + + object->state = SOURCE_OPENING; + + if (FAILED(hr = wg_parser_connect(parser, object->file_size, NULL))) + goto fail; + + stream_count = wg_parser_get_stream_count(parser); + + if (!(object->descriptors = calloc(stream_count, sizeof(*object->descriptors))) + || !(object->streams = calloc(stream_count, sizeof(*object->streams)))) + { + hr = E_OUTOFMEMORY; + goto fail; + } + + for (i = 0; i < stream_count; ++i) + { + wg_parser_stream_t wg_stream = wg_parser_get_stream(object->wg_parser, i); + IMFStreamDescriptor *descriptor; + struct media_stream *stream; + struct wg_format format; + + wg_parser_stream_get_preferred_format(wg_stream, &format); + if (FAILED(hr = stream_descriptor_create(i, &format, &descriptor))) + goto fail; + if (FAILED(hr = media_stream_create(&object->IMFMediaSource_iface, descriptor, wg_stream, &stream))) + { + IMFStreamDescriptor_Release(descriptor); + goto fail; + } + + object->duration = max(object->duration, wg_parser_stream_get_duration(wg_stream)); + IMFStreamDescriptor_AddRef(descriptor); + object->descriptors[i] = descriptor; + object->streams[i] = stream; + object->stream_count++; + } + + media_source_init_descriptors(object); + object->state = SOURCE_STOPPED; + + *out = &object->IMFMediaSource_iface; + TRACE("Created IMFMediaSource %p\n", *out); + return S_OK; + +fail: + WARN("Failed to construct MFMediaSource, hr %#lx.\n", hr); + + while (object->streams && object->stream_count--) + { + struct media_stream *stream = object->streams[object->stream_count]; + IMFStreamDescriptor_Release(object->descriptors[object->stream_count]); + IMFMediaStream_Release(&stream->IMFMediaStream_iface); + } + free(object->descriptors); + free(object->streams); + + if (stream_count != UINT_MAX) + wg_parser_disconnect(object->wg_parser); + if (object->read_thread) + { + object->read_thread_shutdown = true; + WaitForSingleObject(object->read_thread, INFINITE); + CloseHandle(object->read_thread); + } + if (object->wg_parser) + wg_parser_destroy(object->wg_parser); + if (object->async_commands_queue) + MFUnlockWorkQueue(object->async_commands_queue); + if (object->event_queue) + IMFMediaEventQueue_Release(object->event_queue); + IMFByteStream_Release(object->byte_stream); + free(object); + return hr; +} + +struct result_entry +{ + struct list entry; + IMFAsyncResult *result; + MF_OBJECT_TYPE type; + IUnknown *object; +}; + +static HRESULT result_entry_create(IMFAsyncResult *result, MF_OBJECT_TYPE type, + IUnknown *object, struct result_entry **out) +{ + struct result_entry *entry; + + if (!(entry = malloc(sizeof(*entry)))) + return E_OUTOFMEMORY; + + entry->result = result; + IMFAsyncResult_AddRef(entry->result); + entry->object = object; + IUnknown_AddRef(entry->object); + entry->type = type; + + *out = entry; + return S_OK; +} + +static void result_entry_destroy(struct result_entry *entry) +{ + IMFAsyncResult_Release(entry->result); + IUnknown_Release(entry->object); + free(entry); +} + +struct stream_handler +{ + IMFByteStreamHandler IMFByteStreamHandler_iface; + IMFAsyncCallback IMFAsyncCallback_iface; + LONG refcount; + struct list results; + CRITICAL_SECTION cs; +}; + +static struct result_entry *handler_find_result_entry(struct stream_handler *handler, IMFAsyncResult *result) +{ + struct result_entry *entry; + + EnterCriticalSection(&handler->cs); + LIST_FOR_EACH_ENTRY(entry, &handler->results, struct result_entry, entry) + { + if (result == entry->result) + { + list_remove(&entry->entry); + LeaveCriticalSection(&handler->cs); + return entry; + } + } + LeaveCriticalSection(&handler->cs); + + return NULL; +} + +static struct stream_handler *impl_from_IMFByteStreamHandler(IMFByteStreamHandler *iface) +{ + return CONTAINING_RECORD(iface, struct stream_handler, IMFByteStreamHandler_iface); +} + +static struct stream_handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct stream_handler, IMFAsyncCallback_iface); +} + +static HRESULT WINAPI stream_handler_QueryInterface(IMFByteStreamHandler *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFByteStreamHandler) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFByteStreamHandler_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI stream_handler_AddRef(IMFByteStreamHandler *iface) +{ + struct stream_handler *handler = impl_from_IMFByteStreamHandler(iface); + ULONG refcount = InterlockedIncrement(&handler->refcount); + + TRACE("%p, refcount %lu.\n", handler, refcount); + + return refcount; +} + +static ULONG WINAPI stream_handler_Release(IMFByteStreamHandler *iface) +{ + struct stream_handler *handler = impl_from_IMFByteStreamHandler(iface); + ULONG refcount = InterlockedDecrement(&handler->refcount); + struct result_entry *result, *next; + + TRACE("%p, refcount %lu.\n", iface, refcount); + + if (!refcount) + { + LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct result_entry, entry) + result_entry_destroy(result); + DeleteCriticalSection(&handler->cs); + free(handler); + } + + return refcount; +} + +static HRESULT WINAPI stream_handler_BeginCreateObject(IMFByteStreamHandler *iface, IMFByteStream *stream, const WCHAR *url, DWORD flags, + IPropertyStore *props, IUnknown **cancel_cookie, IMFAsyncCallback *callback, IUnknown *state) +{ + struct stream_handler *handler = impl_from_IMFByteStreamHandler(iface); + IMFAsyncResult *result; + IUnknown *context; + QWORD file_size; + HRESULT hr; + DWORD caps; + + TRACE("%p, %s, %#lx, %p, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, cancel_cookie, callback, state); + + if (cancel_cookie) + *cancel_cookie = NULL; + + if (!stream) + return E_INVALIDARG; + if (flags != MF_RESOLUTION_MEDIASOURCE) + FIXME("Unimplemented flags %#lx\n", flags); + + if (FAILED(hr = IMFByteStream_GetCapabilities(stream, &caps))) + return hr; + if (!(caps & MFBYTESTREAM_IS_SEEKABLE)) + { + FIXME("Non-seekable bytestreams not supported.\n"); + return MF_E_BYTESTREAM_NOT_SEEKABLE; + } + if (FAILED(hr = IMFByteStream_GetLength(stream, &file_size))) + { + FIXME("Failed to get byte stream length, hr %#lx.\n", hr); + return hr; + } + + if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &result))) + return hr; + if (FAILED(hr = object_context_create(flags, stream, url, file_size, result, &context))) + { + IMFAsyncResult_Release(result); + return hr; + } + + hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_IO, &handler->IMFAsyncCallback_iface, context); + IUnknown_Release(context); + + if (SUCCEEDED(hr) && cancel_cookie) + { + *cancel_cookie = (IUnknown *)result; + IUnknown_AddRef(*cancel_cookie); + } + + IMFAsyncResult_Release(result); + + return hr; +} + +static HRESULT WINAPI stream_handler_EndCreateObject(IMFByteStreamHandler *iface, IMFAsyncResult *result, + MF_OBJECT_TYPE *type, IUnknown **object) +{ + struct stream_handler *handler = impl_from_IMFByteStreamHandler(iface); + struct result_entry *entry; + HRESULT hr; + + TRACE("%p, %p, %p, %p.\n", iface, result, type, object); + + if (!(entry = handler_find_result_entry(handler, result))) + { + *type = MF_OBJECT_INVALID; + *object = NULL; + return MF_E_UNEXPECTED; + } + + hr = IMFAsyncResult_GetStatus(entry->result); + *type = entry->type; + *object = entry->object; + IUnknown_AddRef(*object); + result_entry_destroy(entry); + return hr; +} + +static HRESULT WINAPI stream_handler_CancelObjectCreation(IMFByteStreamHandler *iface, IUnknown *cookie) +{ + struct stream_handler *handler = impl_from_IMFByteStreamHandler(iface); + IMFAsyncResult *result = (IMFAsyncResult *)cookie; + struct result_entry *entry; + + TRACE("%p, %p.\n", iface, cookie); + + if (!(entry = handler_find_result_entry(handler, result))) + return MF_E_UNEXPECTED; + + result_entry_destroy(entry); + return S_OK; +} + +static HRESULT WINAPI stream_handler_GetMaxNumberOfBytesRequiredForResolution(IMFByteStreamHandler *iface, QWORD *bytes) +{ + FIXME("stub (%p %p)\n", iface, bytes); + return E_NOTIMPL; +} + +static const IMFByteStreamHandlerVtbl stream_handler_vtbl = +{ + stream_handler_QueryInterface, + stream_handler_AddRef, + stream_handler_Release, + stream_handler_BeginCreateObject, + stream_handler_EndCreateObject, + stream_handler_CancelObjectCreation, + stream_handler_GetMaxNumberOfBytesRequiredForResolution, +}; + +static HRESULT WINAPI stream_handler_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFAsyncCallback) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFAsyncCallback_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI stream_handler_callback_AddRef(IMFAsyncCallback *iface) +{ + struct stream_handler *handler = impl_from_IMFAsyncCallback(iface); + return IMFByteStreamHandler_AddRef(&handler->IMFByteStreamHandler_iface); +} + +static ULONG WINAPI stream_handler_callback_Release(IMFAsyncCallback *iface) +{ + struct stream_handler *handler = impl_from_IMFAsyncCallback(iface); + return IMFByteStreamHandler_Release(&handler->IMFByteStreamHandler_iface); +} + +static HRESULT WINAPI stream_handler_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI stream_handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct stream_handler *handler = impl_from_IMFAsyncCallback(iface); + IUnknown *object, *state = IMFAsyncResult_GetStateNoAddRef(result); + struct object_context *context; + struct result_entry *entry; + HRESULT hr; + + if (!state || !(context = impl_from_IUnknown(state))) + return E_INVALIDARG; + + if (FAILED(hr = media_source_create(context, (IMFMediaSource **)&object))) + WARN("Failed to create media source, hr %#lx\n", hr); + else + { + if (FAILED(hr = result_entry_create(context->result, MF_OBJECT_MEDIASOURCE, object, &entry))) + WARN("Failed to create handler result, hr %#lx\n", hr); + else + { + EnterCriticalSection(&handler->cs); + list_add_tail(&handler->results, &entry->entry); + LeaveCriticalSection(&handler->cs); + } + + IUnknown_Release(object); + } + + IMFAsyncResult_SetStatus(context->result, hr); + MFInvokeCallback(context->result); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl stream_handler_callback_vtbl = +{ + stream_handler_callback_QueryInterface, + stream_handler_callback_AddRef, + stream_handler_callback_Release, + stream_handler_callback_GetParameters, + stream_handler_callback_Invoke, +}; + +HRESULT gstreamer_byte_stream_handler_2_create(REFIID riid, void **obj) +{ + struct stream_handler *handler; + HRESULT hr; + + TRACE("%s, %p.\n", debugstr_guid(riid), obj); + + if (!(handler = calloc(1, sizeof(*handler)))) + return E_OUTOFMEMORY; + + list_init(&handler->results); + InitializeCriticalSection(&handler->cs); + + handler->IMFByteStreamHandler_iface.lpVtbl = &stream_handler_vtbl; + handler->IMFAsyncCallback_iface.lpVtbl = &stream_handler_callback_vtbl; + handler->refcount = 1; + + hr = IMFByteStreamHandler_QueryInterface(&handler->IMFByteStreamHandler_iface, riid, obj); + IMFByteStreamHandler_Release(&handler->IMFByteStreamHandler_iface); + + return hr; +} diff --git a/dlls/winegstreamer/winegstreamer_classes.idl b/dlls/winegstreamer/winegstreamer_classes.idl index 16b24a23261..493e6cff2b0 100644 --- a/dlls/winegstreamer/winegstreamer_classes.idl +++ b/dlls/winegstreamer/winegstreamer_classes.idl @@ -83,6 +83,12 @@ coclass VideoProcessorMFT {} ] coclass GStreamerByteStreamHandler {} +[ + threading(both), + uuid(317df619-5e5a-468a-9f15-d827a9a08162) +] +coclass GStreamerByteStreamHandler2 {} + [ threading(both), uuid(2eeb4adf-4578-4d10-bca7-bb955f56320a) From f3bd809680eb03b94611fd36886a2af475ce04fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 22 Nov 2023 22:37:04 +0100 Subject: [PATCH 037/413] HACK: winegstreamer/new_media_source: Fallback to the old media source on failure. CW-Bug-Id: #21953 --- dlls/winegstreamer/new_media_source.c | 97 ++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c index 2250a5faab8..860a14f7bb5 100644 --- a/dlls/winegstreamer/new_media_source.c +++ b/dlls/winegstreamer/new_media_source.c @@ -119,6 +119,93 @@ static HRESULT object_context_create(DWORD flags, IMFByteStream *stream, const W return S_OK; } +struct media_source_fallback_callback +{ + IMFAsyncCallback IMFAsyncCallback_iface; + IMFAsyncResult *result; + HANDLE event; +}; + +static HRESULT WINAPI media_source_fallback_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFAsyncCallback) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFAsyncCallback_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI media_source_fallback_callback_AddRef(IMFAsyncCallback *iface) +{ + return 2; +} + +static ULONG WINAPI media_source_fallback_callback_Release(IMFAsyncCallback *iface) +{ + return 1; +} + +static HRESULT WINAPI media_source_fallback_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI media_source_fallback_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct media_source_fallback_callback *impl = CONTAINING_RECORD(iface, struct media_source_fallback_callback, IMFAsyncCallback_iface); + + IMFAsyncResult_AddRef((impl->result = result)); + SetEvent(impl->event); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl media_source_fallback_callback_vtbl = +{ + media_source_fallback_callback_QueryInterface, + media_source_fallback_callback_AddRef, + media_source_fallback_callback_Release, + media_source_fallback_callback_GetParameters, + media_source_fallback_callback_Invoke, +}; + +static HRESULT create_media_source_fallback(struct object_context *context, IUnknown **object) +{ + static const GUID CLSID_GStreamerByteStreamHandler = {0x317df618, 0x5e5a, 0x468a, {0x9f, 0x15, 0xd8, 0x27, 0xa9, 0xa0, 0x81, 0x62}}; + struct media_source_fallback_callback callback = {{&media_source_fallback_callback_vtbl}}; + IMFByteStreamHandler *handler; + MF_OBJECT_TYPE type; + HRESULT hr; + + if (!(callback.event = CreateEventW(NULL, FALSE, FALSE, NULL))) + return HRESULT_FROM_WIN32(GetLastError()); + + if (FAILED(hr = CoCreateInstance(&CLSID_GStreamerByteStreamHandler, NULL, CLSCTX_INPROC_SERVER, + &IID_IMFByteStreamHandler, (void **)&handler))) + { + CloseHandle(callback.event); + return hr; + } + + if (SUCCEEDED(hr = IMFByteStreamHandler_BeginCreateObject(handler, context->stream, NULL, MF_RESOLUTION_MEDIASOURCE, + NULL, NULL, &callback.IMFAsyncCallback_iface, NULL))) + { + WaitForSingleObject(callback.event, INFINITE); + hr = IMFByteStreamHandler_EndCreateObject(handler, callback.result, &type, object); + IMFAsyncResult_Release(callback.result); + } + + IMFByteStreamHandler_Release(handler); + CloseHandle(callback.event); + return hr; +} + struct media_stream { IMFMediaStream IMFMediaStream_iface; @@ -1724,6 +1811,7 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc IMFMediaEventQueue_Release(object->event_queue); IMFByteStream_Release(object->byte_stream); free(object); + return hr; } @@ -2000,7 +2088,14 @@ static HRESULT WINAPI stream_handler_callback_Invoke(IMFAsyncCallback *iface, IM if (FAILED(hr = media_source_create(context, (IMFMediaSource **)&object))) WARN("Failed to create media source, hr %#lx\n", hr); - else + + if (FAILED(hr)) + { + FIXME("Falling back to old media source, hr %#lx\n", hr); + hr = create_media_source_fallback(context, (IUnknown **)&object); + } + + if (SUCCEEDED(hr)) { if (FAILED(hr = result_entry_create(context->result, MF_OBJECT_MEDIASOURCE, object, &entry))) WARN("Failed to create handler result, hr %#lx\n", hr); From ed9b2a421941850e9a72e913142a3d35c889ef79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 18 Aug 2023 10:59:46 +0200 Subject: [PATCH 038/413] winegstreamer/new_media_source: Introduce a new wg_source interface. CW-Bug-Id: #21953 --- MAINTAINERS | 1 + dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_private.h | 3 + dlls/winegstreamer/main.c | 25 +++++++ dlls/winegstreamer/new_media_source.c | 15 ++++- dlls/winegstreamer/unix_private.h | 5 ++ dlls/winegstreamer/unixlib.h | 9 +++ dlls/winegstreamer/wg_parser.c | 6 ++ dlls/winegstreamer/wg_source.c | 94 +++++++++++++++++++++++++++ 9 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 dlls/winegstreamer/wg_source.c diff --git a/MAINTAINERS b/MAINTAINERS index 4bab36bfdf9..59873f6804c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -223,6 +223,7 @@ F: dlls/winegstreamer/mfplat.c F: dlls/winegstreamer/resampler.c F: dlls/winegstreamer/video_decoder.c F: dlls/winegstreamer/video_processor.c +F: dlls/winegstreamer/wg_source.c F: dlls/winegstreamer/wg_sample.c F: dlls/winegstreamer/wg_transform.c F: dlls/winegstreamer/wma_decoder.c diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index af64d13b7b7..49cfafc2a8e 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -27,6 +27,7 @@ SOURCES = \ wg_muxer.c \ wg_parser.c \ wg_sample.c \ + wg_source.c \ wg_transform.c \ winegstreamer_classes.idl \ wm_reader.c \ diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index e258a5ce532..13fda53b0be 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -103,6 +103,9 @@ 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); +HRESULT wg_source_create(wg_source_t *out); +void wg_source_destroy(wg_source_t source); + wg_transform_t wg_transform_create(const struct wg_format *input_format, const struct wg_format *output_format, const struct wg_transform_attrs *attrs); void wg_transform_destroy(wg_transform_t transform); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index c0d4d69c3f3..81f4ead23b6 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -342,6 +342,31 @@ void wg_parser_stream_seek(wg_parser_stream_t stream, double rate, WINE_UNIX_CALL(unix_wg_parser_stream_seek, ¶ms); } +HRESULT wg_source_create(wg_source_t *out) +{ + struct wg_source_create_params params = {0}; + NTSTATUS status; + + TRACE("out %p\n", out); + + if ((status = WINE_UNIX_CALL(unix_wg_source_create, ¶ms))) + WARN("wg_source_create returned status %#lx\n", status); + else + { + TRACE("Returning source %#I64x.\n", params.source); + *out = params.source; + } + + return HRESULT_FROM_NT(status); +} + +void wg_source_destroy(wg_source_t source) +{ + TRACE("source %#I64x.\n", source); + + WINE_UNIX_CALL(unix_wg_source_destroy, &source); +} + wg_transform_t wg_transform_create(const struct wg_format *input_format, const struct wg_format *output_format, const struct wg_transform_attrs *attrs) { diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c index 860a14f7bb5..842df5e690d 100644 --- a/dlls/winegstreamer/new_media_source.c +++ b/dlls/winegstreamer/new_media_source.c @@ -36,6 +36,8 @@ struct object_context IMFByteStream *stream; UINT64 file_size; WCHAR *url; + + wg_source_t wg_source; }; static struct object_context *impl_from_IUnknown(IUnknown *iface) @@ -78,6 +80,8 @@ static ULONG WINAPI object_context_Release(IUnknown *iface) if (!refcount) { + if (context->wg_source) + wg_source_destroy(context->wg_source); IMFAsyncResult_Release(context->result); IMFByteStream_Release(context->stream); free(context->url); @@ -269,6 +273,7 @@ struct media_source CRITICAL_SECTION cs; + wg_source_t wg_source; UINT64 file_size; wg_parser_t wg_parser; UINT64 duration; @@ -1446,6 +1451,7 @@ static ULONG WINAPI media_source_Release(IMFMediaSource *iface) IMFMediaSource_Shutdown(iface); IMFMediaEventQueue_Release(source->event_queue); IMFByteStream_Release(source->byte_stream); + wg_source_destroy(source->wg_source); wg_parser_destroy(source->wg_parser); source->cs.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&source->cs); @@ -1724,6 +1730,9 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc InitializeCriticalSection(&object->cs); object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs"); + object->wg_source = context->wg_source; + context->wg_source = 0; + if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) goto fail; @@ -1803,6 +1812,8 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc WaitForSingleObject(object->read_thread, INFINITE); CloseHandle(object->read_thread); } + if (object->wg_source) + wg_source_destroy(object->wg_source); if (object->wg_parser) wg_parser_destroy(object->wg_parser); if (object->async_commands_queue) @@ -2086,7 +2097,9 @@ static HRESULT WINAPI stream_handler_callback_Invoke(IMFAsyncCallback *iface, IM if (!state || !(context = impl_from_IUnknown(state))) return E_INVALIDARG; - if (FAILED(hr = media_source_create(context, (IMFMediaSource **)&object))) + if (FAILED(hr = wg_source_create(&context->wg_source))) + WARN("Failed to create wg_source, hr %#lx\n", hr); + else if (FAILED(hr = media_source_create(context, (IMFMediaSource **)&object))) WARN("Failed to create media source, hr %#lx\n", hr); if (FAILED(hr)) diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 55c850bc1a3..83d18ebbf92 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -57,6 +57,11 @@ 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); +/* wg_source.c */ + +extern NTSTATUS wg_source_create(void *args); +extern NTSTATUS wg_source_destroy(void *args); + /* wg_transform.c */ extern NTSTATUS wg_transform_create(void *args); diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index c8fabe6574b..addf1d8cb7e 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -218,6 +218,7 @@ enum wg_parser_type typedef UINT64 wg_parser_t; typedef UINT64 wg_parser_stream_t; +typedef UINT64 wg_source_t; typedef UINT64 wg_transform_t; typedef UINT64 wg_muxer_t; @@ -347,6 +348,11 @@ struct wg_parser_stream_seek_params DWORD start_flags, stop_flags; }; +struct wg_source_create_params +{ + wg_source_t source; +}; + struct wg_transform_attrs { UINT32 output_plane_align; @@ -457,6 +463,9 @@ enum unix_funcs unix_wg_parser_stream_get_tag, unix_wg_parser_stream_seek, + unix_wg_source_create, + unix_wg_source_destroy, + unix_wg_transform_create, unix_wg_transform_destroy, unix_wg_transform_set_output_format, diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 7326cbc27cb..b47cf9b852c 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -2206,6 +2206,9 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_parser_stream_get_tag), X(wg_parser_stream_seek), + X(wg_source_create), + X(wg_source_destroy), + X(wg_transform_create), X(wg_transform_destroy), X(wg_transform_set_output_format), @@ -2539,6 +2542,9 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = X64(wg_parser_stream_get_tag), X(wg_parser_stream_seek), + X(wg_source_create), + X(wg_source_destroy), + X64(wg_transform_create), X(wg_transform_destroy), X64(wg_transform_set_output_format), diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c new file mode 100644 index 00000000000..52a54196ea9 --- /dev/null +++ b/dlls/winegstreamer/wg_source.c @@ -0,0 +1,94 @@ +/* + * Copyright 2023 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 +#include +#include + +#include +#include +#include +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "winternl.h" +#include "mferror.h" + +#include "unix_private.h" + +struct wg_source +{ + GstElement *container; +}; + +static struct wg_source *get_source(wg_source_t source) +{ + return (struct wg_source *)(ULONG_PTR)source; +} + +NTSTATUS wg_source_create(void *args) +{ + struct wg_source_create_params *params = args; + struct wg_source *source; + + if (!(source = calloc(1, sizeof(*source)))) + return STATUS_UNSUCCESSFUL; + + if (!(source->container = gst_bin_new("wg_source"))) + goto error; + + gst_element_set_state(source->container, GST_STATE_PAUSED); + if (!gst_element_get_state(source->container, NULL, NULL, -1)) + goto error; + + params->source = (wg_source_t)(ULONG_PTR)source; + GST_INFO("Created winegstreamer source %p.", source); + return STATUS_SUCCESS; + +error: + if (source->container) + { + gst_element_set_state(source->container, GST_STATE_NULL); + gst_object_unref(source->container); + } + free(source); + + GST_ERROR("Failed to create winegstreamer source."); + return STATUS_UNSUCCESSFUL; +} + +NTSTATUS wg_source_destroy(void *args) +{ + struct wg_source *source = get_source(*(wg_source_t *)args); + + GST_TRACE("source %p", source); + + gst_element_set_state(source->container, GST_STATE_NULL); + gst_object_unref(source->container); + free(source); + + return STATUS_SUCCESS; +} From d57cf8f8ff3d17b0761a5d161cf2c3b940cf37b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 18 Aug 2023 11:00:42 +0200 Subject: [PATCH 039/413] winegstreamer/new_media_source: Call (Begin|End)Read to read the byte stream header. CW-Bug-Id: #21953 --- dlls/winegstreamer/new_media_source.c | 35 +++++++++++++++++++-------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c index 842df5e690d..74aec7c8ee6 100644 --- a/dlls/winegstreamer/new_media_source.c +++ b/dlls/winegstreamer/new_media_source.c @@ -27,6 +27,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(mfplat); +#define SOURCE_BUFFER_SIZE (32 * 1024) + struct object_context { IUnknown IUnknown_iface; @@ -37,6 +39,7 @@ struct object_context UINT64 file_size; WCHAR *url; + BYTE *buffer; wg_source_t wg_source; }; @@ -84,6 +87,7 @@ static ULONG WINAPI object_context_Release(IUnknown *iface) wg_source_destroy(context->wg_source); IMFAsyncResult_Release(context->result); IMFByteStream_Release(context->stream); + free(context->buffer); free(context->url); free(context); } @@ -99,14 +103,16 @@ static const IUnknownVtbl object_context_vtbl = }; static HRESULT object_context_create(DWORD flags, IMFByteStream *stream, const WCHAR *url, - QWORD file_size, IMFAsyncResult *result, IUnknown **out) + QWORD file_size, IMFAsyncResult *result, IUnknown **out, BYTE **out_buf) { WCHAR *tmp_url = url ? wcsdup(url) : NULL; struct object_context *context; - if (!(context = calloc(1, sizeof(*context)))) + if (!(context = calloc(1, sizeof(*context))) + || !(context->buffer = malloc(SOURCE_BUFFER_SIZE))) { free(tmp_url); + free(context); return E_OUTOFMEMORY; } @@ -120,6 +126,7 @@ static HRESULT object_context_create(DWORD flags, IMFByteStream *stream, const W IMFAsyncResult_AddRef(context->result); *out = &context->IUnknown_iface; + *out_buf = context->buffer; return S_OK; } @@ -1949,7 +1956,8 @@ static HRESULT WINAPI stream_handler_BeginCreateObject(IMFByteStreamHandler *ifa struct stream_handler *handler = impl_from_IMFByteStreamHandler(iface); IMFAsyncResult *result; IUnknown *context; - QWORD file_size; + QWORD file_size, position; + BYTE *buffer; HRESULT hr; DWORD caps; @@ -1978,13 +1986,16 @@ static HRESULT WINAPI stream_handler_BeginCreateObject(IMFByteStreamHandler *ifa if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &result))) return hr; - if (FAILED(hr = object_context_create(flags, stream, url, file_size, result, &context))) - { - IMFAsyncResult_Release(result); - return hr; - } + if (FAILED(hr = object_context_create(flags, stream, url, file_size, result, &context, &buffer))) + goto done; - hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_IO, &handler->IMFAsyncCallback_iface, context); + if (FAILED(hr = IMFByteStream_GetCurrentPosition(stream, &position))) + WARN("Failed to get byte stream position, hr %#lx\n", hr); + else if (position != 0 && FAILED(hr = IMFByteStream_SetCurrentPosition(stream, 0))) + WARN("Failed to set byte stream position, hr %#lx\n", hr); + else if (FAILED(hr = IMFByteStream_BeginRead(stream, buffer, min(SOURCE_BUFFER_SIZE, file_size), + &handler->IMFAsyncCallback_iface, context))) + WARN("Failed to queue byte stream async read, hr %#lx\n", hr); IUnknown_Release(context); if (SUCCEEDED(hr) && cancel_cookie) @@ -1993,6 +2004,7 @@ static HRESULT WINAPI stream_handler_BeginCreateObject(IMFByteStreamHandler *ifa IUnknown_AddRef(*cancel_cookie); } +done: IMFAsyncResult_Release(result); return hr; @@ -2092,12 +2104,15 @@ static HRESULT WINAPI stream_handler_callback_Invoke(IMFAsyncCallback *iface, IM IUnknown *object, *state = IMFAsyncResult_GetStateNoAddRef(result); struct object_context *context; struct result_entry *entry; + DWORD size = 0; HRESULT hr; if (!state || !(context = impl_from_IUnknown(state))) return E_INVALIDARG; - if (FAILED(hr = wg_source_create(&context->wg_source))) + if (FAILED(hr = IMFByteStream_EndRead(context->stream, result, &size))) + WARN("Failed to complete stream read, hr %#lx\n", hr); + else if (FAILED(hr = wg_source_create(&context->wg_source))) WARN("Failed to create wg_source, hr %#lx\n", hr); else if (FAILED(hr = media_source_create(context, (IMFMediaSource **)&object))) WARN("Failed to create media source, hr %#lx\n", hr); From 218436703db9d7a2b6ec64cbf4e531cb2ee3b38c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 18 Aug 2023 11:06:25 +0200 Subject: [PATCH 040/413] winegstreamer/new_media_source: Provide first block of data and stream URL to wg_source. CW-Bug-Id: #21953 --- dlls/winegstreamer/gst_private.h | 2 +- dlls/winegstreamer/main.c | 15 ++++++++--- dlls/winegstreamer/new_media_source.c | 2 +- dlls/winegstreamer/unixlib.h | 3 +++ dlls/winegstreamer/wg_parser.c | 24 +++++++++++++++++- dlls/winegstreamer/wg_source.c | 36 +++++++++++++++++++++++++++ 6 files changed, 76 insertions(+), 6 deletions(-) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 13fda53b0be..c8d1ec0cc7e 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -103,7 +103,7 @@ 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); -HRESULT wg_source_create(wg_source_t *out); +HRESULT wg_source_create(const WCHAR *url, const void *data, uint32_t size, wg_source_t *out); void wg_source_destroy(wg_source_t source); wg_transform_t wg_transform_create(const struct wg_format *input_format, diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 81f4ead23b6..27a277d05c4 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -342,12 +342,20 @@ void wg_parser_stream_seek(wg_parser_stream_t stream, double rate, WINE_UNIX_CALL(unix_wg_parser_stream_seek, ¶ms); } -HRESULT wg_source_create(wg_source_t *out) +HRESULT wg_source_create(const WCHAR *url, const void *data, uint32_t size, wg_source_t *out) { - struct wg_source_create_params params = {0}; + struct wg_source_create_params params = + { + .data = data, .size = size, + }; + UINT len = url ? WideCharToMultiByte(CP_ACP, 0, url, -1, NULL, 0, NULL, NULL) : 0; + char *tmp = url ? malloc(len) : NULL; NTSTATUS status; - TRACE("out %p\n", out); + TRACE("url %s, data %p, size %#x\n", debugstr_w(url), data, size); + + if ((params.url = tmp)) + WideCharToMultiByte(CP_ACP, 0, url, -1, tmp, len, NULL, NULL); if ((status = WINE_UNIX_CALL(unix_wg_source_create, ¶ms))) WARN("wg_source_create returned status %#lx\n", status); @@ -357,6 +365,7 @@ HRESULT wg_source_create(wg_source_t *out) *out = params.source; } + free(tmp); return HRESULT_FROM_NT(status); } diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c index 74aec7c8ee6..a7d4b76c200 100644 --- a/dlls/winegstreamer/new_media_source.c +++ b/dlls/winegstreamer/new_media_source.c @@ -2112,7 +2112,7 @@ static HRESULT WINAPI stream_handler_callback_Invoke(IMFAsyncCallback *iface, IM if (FAILED(hr = IMFByteStream_EndRead(context->stream, result, &size))) WARN("Failed to complete stream read, hr %#lx\n", hr); - else if (FAILED(hr = wg_source_create(&context->wg_source))) + else if (FAILED(hr = wg_source_create(context->url, context->buffer, size, &context->wg_source))) WARN("Failed to create wg_source, hr %#lx\n", hr); else if (FAILED(hr = media_source_create(context, (IMFMediaSource **)&object))) WARN("Failed to create media source, hr %#lx\n", hr); diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index addf1d8cb7e..b68a08b571f 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -350,6 +350,9 @@ struct wg_parser_stream_seek_params struct wg_source_create_params { + const char *url; + const void *data; + UINT32 size; wg_source_t source; }; diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index b47cf9b852c..dc5c5e6b246 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -2356,6 +2356,28 @@ static NTSTATUS wow64_wg_parser_stream_get_tag(void *args) return wg_parser_stream_get_tag(¶ms); } +NTSTATUS wow64_wg_source_create(void *args) +{ + struct + { + PTR32 url; + PTR32 data; + UINT32 size; + wg_source_t source; + } *params32 = args; + struct wg_source_create_params params = + { + .url = ULongToPtr(params32->url), + .data = ULongToPtr(params32->data), + .size = params32->size, + }; + NTSTATUS ret; + + ret = wg_source_create(¶ms); + params32->source = params.source; + return ret; +} + NTSTATUS wow64_wg_transform_create(void *args) { struct @@ -2542,7 +2564,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = X64(wg_parser_stream_get_tag), X(wg_parser_stream_seek), - X(wg_source_create), + X64(wg_source_create), X(wg_source_destroy), X64(wg_transform_create), diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 52a54196ea9..47f67ea8dae 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -49,13 +49,45 @@ static struct wg_source *get_source(wg_source_t source) return (struct wg_source *)(ULONG_PTR)source; } +static GstCaps *detect_caps_from_data(const char *url, const void *data, guint size) +{ + const char *extension = url ? strrchr(url, '.') : NULL; + GstTypeFindProbability probability; + GstCaps *caps; + gchar *str; + + if (!(caps = gst_type_find_helper_for_data_with_extension(NULL, data, size, + extension ? extension + 1 : NULL, &probability))) + { + GST_ERROR("Failed to detect caps for url %s, data %p, size %u", url, data, size); + return NULL; + } + + str = gst_caps_to_string(caps); + if (probability > GST_TYPE_FIND_POSSIBLE) + GST_INFO("Detected caps %s with probability %u for url %s, data %p, size %u", + str, probability, url, data, size); + else + GST_FIXME("Detected caps %s with probability %u for url %s, data %p, size %u", + str, probability, url, data, size); + g_free(str); + + return caps; +} + NTSTATUS wg_source_create(void *args) { struct wg_source_create_params *params = args; struct wg_source *source; + GstCaps *src_caps; + if (!(src_caps = detect_caps_from_data(params->url, params->data, params->size))) + return STATUS_UNSUCCESSFUL; if (!(source = calloc(1, sizeof(*source)))) + { + gst_caps_unref(src_caps); return STATUS_UNSUCCESSFUL; + } if (!(source->container = gst_bin_new("wg_source"))) goto error; @@ -64,6 +96,8 @@ NTSTATUS wg_source_create(void *args) if (!gst_element_get_state(source->container, NULL, NULL, -1)) goto error; + gst_caps_unref(src_caps); + params->source = (wg_source_t)(ULONG_PTR)source; GST_INFO("Created winegstreamer source %p.", source); return STATUS_SUCCESS; @@ -76,6 +110,8 @@ NTSTATUS wg_source_create(void *args) } free(source); + gst_caps_unref(src_caps); + GST_ERROR("Failed to create winegstreamer source."); return STATUS_UNSUCCESSFUL; } From 58da8495af0428cac01023a7396e9e8d79b98a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 27 Jul 2023 17:43:51 +0200 Subject: [PATCH 041/413] winegstreamer/new_media_source: Prefer MF_BYTESTREAM_ORIGIN_NAME for source URL. CW-Bug-Id: #21953 --- dlls/winegstreamer/new_media_source.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c index a7d4b76c200..e3c329bccfd 100644 --- a/dlls/winegstreamer/new_media_source.c +++ b/dlls/winegstreamer/new_media_source.c @@ -102,10 +102,30 @@ static const IUnknownVtbl object_context_vtbl = object_context_Release, }; +static WCHAR *byte_stream_get_url(IMFByteStream *stream, const WCHAR *url) +{ + IMFAttributes *attributes; + WCHAR buffer[MAX_PATH]; + UINT32 size; + HRESULT hr; + + if (SUCCEEDED(hr = IMFByteStream_QueryInterface(stream, &IID_IMFAttributes, (void **)&attributes))) + { + if (FAILED(hr = IMFAttributes_GetString(attributes, &MF_BYTESTREAM_ORIGIN_NAME, + buffer, ARRAY_SIZE(buffer), &size))) + WARN("Failed to get MF_BYTESTREAM_ORIGIN_NAME got size %#x, hr %#lx\n", size, hr); + else + url = buffer; + IMFAttributes_Release(attributes); + } + + return url ? wcsdup(url) : NULL; +} + static HRESULT object_context_create(DWORD flags, IMFByteStream *stream, const WCHAR *url, QWORD file_size, IMFAsyncResult *result, IUnknown **out, BYTE **out_buf) { - WCHAR *tmp_url = url ? wcsdup(url) : NULL; + WCHAR *tmp_url = byte_stream_get_url(stream, url); struct object_context *context; if (!(context = calloc(1, sizeof(*context))) From eef3113e3b6ce6c71826735f6fc0c5c9c38c67c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 26 Apr 2023 18:06:11 +0200 Subject: [PATCH 042/413] winegstreamer/new_media_source: Create a source pad on the wg_source. CW-Bug-Id: #21953 --- dlls/winegstreamer/wg_source.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 47f67ea8dae..295e1138840 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -41,6 +41,7 @@ struct wg_source { + GstPad *src_pad; GstElement *container; }; @@ -75,6 +76,21 @@ static GstCaps *detect_caps_from_data(const char *url, const void *data, guint s return caps; } +static GstPad *create_pad_with_caps(GstPadDirection direction, GstCaps *caps) +{ + GstCaps *pad_caps = caps ? gst_caps_ref(caps) : gst_caps_new_any(); + const char *name = direction == GST_PAD_SRC ? "src" : "sink"; + GstPadTemplate *template; + GstPad *pad; + + if (!pad_caps || !(template = gst_pad_template_new(name, direction, GST_PAD_ALWAYS, pad_caps))) + return NULL; + pad = gst_pad_new_from_template(template, "src"); + g_object_unref(template); + gst_caps_unref(pad_caps); + return pad; +} + NTSTATUS wg_source_create(void *args) { struct wg_source_create_params *params = args; @@ -91,6 +107,9 @@ NTSTATUS wg_source_create(void *args) if (!(source->container = gst_bin_new("wg_source"))) goto error; + if (!(source->src_pad = create_pad_with_caps(GST_PAD_SRC, src_caps))) + goto error; + gst_pad_set_element_private(source->src_pad, source); gst_element_set_state(source->container, GST_STATE_PAUSED); if (!gst_element_get_state(source->container, NULL, NULL, -1)) @@ -108,6 +127,8 @@ NTSTATUS wg_source_create(void *args) gst_element_set_state(source->container, GST_STATE_NULL); gst_object_unref(source->container); } + if (source->src_pad) + gst_object_unref(source->src_pad); free(source); gst_caps_unref(src_caps); @@ -124,6 +145,7 @@ NTSTATUS wg_source_destroy(void *args) gst_element_set_state(source->container, GST_STATE_NULL); gst_object_unref(source->container); + gst_object_unref(source->src_pad); free(source); return STATUS_SUCCESS; From 76f464636b3d08ac509232a9ede61ecbad0c20c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 27 Apr 2023 15:22:44 +0200 Subject: [PATCH 043/413] winegstreamer/new_media_source: Create a demuxer element in wg_source_create. CW-Bug-Id: #21953 --- dlls/winegstreamer/wg_source.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 295e1138840..6f63ac0822f 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -94,8 +94,9 @@ static GstPad *create_pad_with_caps(GstPadDirection direction, GstCaps *caps) NTSTATUS wg_source_create(void *args) { struct wg_source_create_params *params = args; + GstElement *first = NULL, *last = NULL, *element; + GstCaps *src_caps, *any_caps; struct wg_source *source; - GstCaps *src_caps; if (!(src_caps = detect_caps_from_data(params->url, params->data, params->size))) return STATUS_UNSUCCESSFUL; @@ -111,6 +112,21 @@ NTSTATUS wg_source_create(void *args) goto error; gst_pad_set_element_private(source->src_pad, source); + if (!(any_caps = gst_caps_new_any())) + goto error; + if (!(element = find_element(GST_ELEMENT_FACTORY_TYPE_DECODABLE, src_caps, any_caps)) + || !append_element(source->container, element, &first, &last)) + { + gst_caps_unref(any_caps); + goto error; + } + gst_caps_unref(any_caps); + + if (!link_src_to_element(source->src_pad, first)) + goto error; + if (!gst_pad_set_active(source->src_pad, true)) + goto error; + gst_element_set_state(source->container, GST_STATE_PAUSED); if (!gst_element_get_state(source->container, NULL, NULL, -1)) goto error; From f86e0a5a5d845178f87db02f2472da5b205d62c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 22 Nov 2023 23:11:45 +0100 Subject: [PATCH 044/413] winegstreamer/new_media_source: Push a stream and segment event to the wg_source. CW-Bug-Id: #21953 --- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/main.c | 14 +++++++++ dlls/winegstreamer/new_media_source.c | 2 ++ dlls/winegstreamer/unix_private.h | 1 + dlls/winegstreamer/unixlib.h | 8 +++++ dlls/winegstreamer/wg_parser.c | 20 +++++++++++++ dlls/winegstreamer/wg_source.c | 42 +++++++++++++++++++++++++++ 7 files changed, 88 insertions(+) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index c8d1ec0cc7e..dc50549f82d 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -105,6 +105,7 @@ void wg_parser_stream_seek(wg_parser_stream_t stream, double rate, HRESULT wg_source_create(const WCHAR *url, const void *data, uint32_t size, wg_source_t *out); void wg_source_destroy(wg_source_t source); +HRESULT wg_source_push_data(wg_source_t source, const void *data, uint32_t size); wg_transform_t wg_transform_create(const struct wg_format *input_format, const struct wg_format *output_format, const struct wg_transform_attrs *attrs); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 27a277d05c4..8250b3305e3 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -376,6 +376,20 @@ void wg_source_destroy(wg_source_t source) WINE_UNIX_CALL(unix_wg_source_destroy, &source); } +HRESULT wg_source_push_data(wg_source_t source, const void *data, uint32_t size) +{ + struct wg_source_push_data_params params = + { + .source = source, + .data = data, + .size = size, + }; + + TRACE("source %#I64x, data %p, size %#x\n", source, data, size); + + return HRESULT_FROM_NT(WINE_UNIX_CALL(unix_wg_source_push_data, ¶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) { diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c index e3c329bccfd..d63550e1b8d 100644 --- a/dlls/winegstreamer/new_media_source.c +++ b/dlls/winegstreamer/new_media_source.c @@ -2134,6 +2134,8 @@ static HRESULT WINAPI stream_handler_callback_Invoke(IMFAsyncCallback *iface, IM WARN("Failed to complete stream read, hr %#lx\n", hr); else if (FAILED(hr = wg_source_create(context->url, context->buffer, size, &context->wg_source))) WARN("Failed to create wg_source, hr %#lx\n", hr); + else if (FAILED(hr = wg_source_push_data(context->wg_source, context->buffer, size))) + WARN("Failed to push initial data, hr %#lx\n", hr); else if (FAILED(hr = media_source_create(context, (IMFMediaSource **)&object))) WARN("Failed to create media source, hr %#lx\n", hr); diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 83d18ebbf92..6aa7ef92f38 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -61,6 +61,7 @@ extern GstCaps *wg_format_to_caps(const struct wg_format *format); extern NTSTATUS wg_source_create(void *args); extern NTSTATUS wg_source_destroy(void *args); +extern NTSTATUS wg_source_push_data(void *args); /* wg_transform.c */ diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index b68a08b571f..e8164f4b01e 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -356,6 +356,13 @@ struct wg_source_create_params wg_source_t source; }; +struct wg_source_push_data_params +{ + wg_source_t source; + const void *data; + UINT32 size; +}; + struct wg_transform_attrs { UINT32 output_plane_align; @@ -468,6 +475,7 @@ enum unix_funcs unix_wg_source_create, unix_wg_source_destroy, + unix_wg_source_push_data, unix_wg_transform_create, unix_wg_transform_destroy, diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index dc5c5e6b246..5c613a66f45 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -2208,6 +2208,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_source_create), X(wg_source_destroy), + X(wg_source_push_data), X(wg_transform_create), X(wg_transform_destroy), @@ -2378,6 +2379,24 @@ NTSTATUS wow64_wg_source_create(void *args) return ret; } +NTSTATUS wow64_wg_source_push_data(void *args) +{ + struct + { + wg_source_t source; + PTR32 data; + UINT32 size; + } *params32 = args; + struct wg_source_push_data_params params = + { + .source = params32->source, + .data = ULongToPtr(params32->data), + .size = params32->size, + }; + + return wg_source_push_data(¶ms); +} + NTSTATUS wow64_wg_transform_create(void *args) { struct @@ -2566,6 +2585,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = X64(wg_source_create), X(wg_source_destroy), + X64(wg_source_push_data), X64(wg_transform_create), X(wg_transform_destroy), diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 6f63ac0822f..34dae4d8cda 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -43,6 +43,8 @@ struct wg_source { GstPad *src_pad; GstElement *container; + GstSegment segment; + bool valid_segment; }; static struct wg_source *get_source(wg_source_t source) @@ -91,12 +93,29 @@ static GstPad *create_pad_with_caps(GstPadDirection direction, GstCaps *caps) return pad; } +static GstEvent *create_stream_start_event(const char *stream_id) +{ + GstStream *stream; + GstEvent *event; + + if (!(stream = gst_stream_new(stream_id, NULL, GST_STREAM_TYPE_UNKNOWN, 0))) + return NULL; + if ((event = gst_event_new_stream_start(stream_id))) + { + gst_event_set_stream(event, stream); + gst_object_unref(stream); + } + + return event; +} + NTSTATUS wg_source_create(void *args) { struct wg_source_create_params *params = args; GstElement *first = NULL, *last = NULL, *element; GstCaps *src_caps, *any_caps; struct wg_source *source; + GstEvent *event; if (!(src_caps = detect_caps_from_data(params->url, params->data, params->size))) return STATUS_UNSUCCESSFUL; @@ -105,6 +124,7 @@ NTSTATUS wg_source_create(void *args) gst_caps_unref(src_caps); return STATUS_UNSUCCESSFUL; } + gst_segment_init(&source->segment, GST_FORMAT_BYTES); if (!(source->container = gst_bin_new("wg_source"))) goto error; @@ -131,6 +151,9 @@ NTSTATUS wg_source_create(void *args) if (!gst_element_get_state(source->container, NULL, NULL, -1)) goto error; + if (!(event = create_stream_start_event("wg_source")) + || !push_event(source->src_pad, event)) + goto error; gst_caps_unref(src_caps); params->source = (wg_source_t)(ULONG_PTR)source; @@ -166,3 +189,22 @@ NTSTATUS wg_source_destroy(void *args) return STATUS_SUCCESS; } + +NTSTATUS wg_source_push_data(void *args) +{ + struct wg_source_push_data_params *params = args; + struct wg_source *source = get_source(params->source); + GstEvent *event; + + GST_TRACE("source %p, data %p, size %#x", source, params->data, params->size); + + if (!source->valid_segment) + { + if (!(event = gst_event_new_segment(&source->segment)) + || !gst_pad_push_event(source->src_pad, event)) + GST_ERROR("Failed to push new segment event"); + source->valid_segment = true; + } + + return STATUS_SUCCESS; +} From 50194bd5f7bf33e4be907ae0f458e02a98fe5697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 22 Nov 2023 23:12:14 +0100 Subject: [PATCH 045/413] winegstreamer/new_media_source: Handle GST_QUERY_URI on wg_source src pad. CW-Bug-Id: #21953 --- dlls/winegstreamer/wg_source.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 34dae4d8cda..3847f06eb3d 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -41,6 +41,7 @@ struct wg_source { + gchar *url; GstPad *src_pad; GstElement *container; GstSegment segment; @@ -93,6 +94,32 @@ static GstPad *create_pad_with_caps(GstPadDirection direction, GstCaps *caps) return pad; } +static gboolean src_query_uri(struct wg_source *source, GstQuery *query) +{ + gchar *uri; + + gst_query_parse_uri(query, &uri); + GST_TRACE("source %p, uri %s", source, uri); + gst_query_set_uri(query, source->url); + + return true; +} + +static gboolean src_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) +{ + struct wg_source *source = gst_pad_get_element_private(pad); + + switch (GST_QUERY_TYPE(query)) + { + case GST_QUERY_URI: + if (!source->url) + return false; + return src_query_uri(source, query); + default: + return gst_pad_query_default(pad, parent, query); + } +} + static GstEvent *create_stream_start_event(const char *stream_id) { GstStream *stream; @@ -124,6 +151,7 @@ NTSTATUS wg_source_create(void *args) gst_caps_unref(src_caps); return STATUS_UNSUCCESSFUL; } + source->url = params->url ? strdup(params->url) : NULL; gst_segment_init(&source->segment, GST_FORMAT_BYTES); if (!(source->container = gst_bin_new("wg_source"))) @@ -131,6 +159,7 @@ NTSTATUS wg_source_create(void *args) if (!(source->src_pad = create_pad_with_caps(GST_PAD_SRC, src_caps))) goto error; gst_pad_set_element_private(source->src_pad, source); + gst_pad_set_query_function(source->src_pad, src_query_cb); if (!(any_caps = gst_caps_new_any())) goto error; @@ -168,6 +197,7 @@ NTSTATUS wg_source_create(void *args) } if (source->src_pad) gst_object_unref(source->src_pad); + free(source->url); free(source); gst_caps_unref(src_caps); @@ -185,6 +215,7 @@ NTSTATUS wg_source_destroy(void *args) gst_element_set_state(source->container, GST_STATE_NULL); gst_object_unref(source->container); gst_object_unref(source->src_pad); + free(source->url); free(source); return STATUS_SUCCESS; From 2fccfa6709936b264834122c366d68ac6407835d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 22 Nov 2023 23:12:29 +0100 Subject: [PATCH 046/413] winegstreamer/new_media_source: Handle GST_QUERY_DURATION on wg_source src pad. CW-Bug-Id: #21953 --- dlls/winegstreamer/gst_private.h | 3 ++- dlls/winegstreamer/main.c | 7 +++++-- dlls/winegstreamer/new_media_source.c | 3 ++- dlls/winegstreamer/unixlib.h | 1 + dlls/winegstreamer/wg_source.c | 16 ++++++++++++++++ 5 files changed, 26 insertions(+), 4 deletions(-) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index dc50549f82d..c09c367a66b 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -103,7 +103,8 @@ 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); -HRESULT wg_source_create(const WCHAR *url, const void *data, uint32_t size, wg_source_t *out); +HRESULT wg_source_create(const WCHAR *url, uint64_t file_size, + const void *data, uint32_t size, wg_source_t *out); void wg_source_destroy(wg_source_t source); HRESULT wg_source_push_data(wg_source_t source, const void *data, uint32_t size); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 8250b3305e3..8c6a5fb385e 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -342,17 +342,20 @@ void wg_parser_stream_seek(wg_parser_stream_t stream, double rate, WINE_UNIX_CALL(unix_wg_parser_stream_seek, ¶ms); } -HRESULT wg_source_create(const WCHAR *url, const void *data, uint32_t size, wg_source_t *out) +HRESULT wg_source_create(const WCHAR *url, uint64_t file_size, + const void *data, uint32_t size, wg_source_t *out) { struct wg_source_create_params params = { + .file_size = file_size, .data = data, .size = size, }; UINT len = url ? WideCharToMultiByte(CP_ACP, 0, url, -1, NULL, 0, NULL, NULL) : 0; char *tmp = url ? malloc(len) : NULL; NTSTATUS status; - TRACE("url %s, data %p, size %#x\n", debugstr_w(url), data, size); + TRACE("url %s, file_size %#I64x, data %p, size %#x\n", debugstr_w(url), + file_size, data, size); if ((params.url = tmp)) WideCharToMultiByte(CP_ACP, 0, url, -1, tmp, len, NULL, NULL); diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c index d63550e1b8d..d4c66fa3599 100644 --- a/dlls/winegstreamer/new_media_source.c +++ b/dlls/winegstreamer/new_media_source.c @@ -2132,7 +2132,8 @@ static HRESULT WINAPI stream_handler_callback_Invoke(IMFAsyncCallback *iface, IM if (FAILED(hr = IMFByteStream_EndRead(context->stream, result, &size))) WARN("Failed to complete stream read, hr %#lx\n", hr); - else if (FAILED(hr = wg_source_create(context->url, context->buffer, size, &context->wg_source))) + else if (FAILED(hr = wg_source_create(context->url, context->file_size, + context->buffer, size, &context->wg_source))) WARN("Failed to create wg_source, hr %#lx\n", hr); else if (FAILED(hr = wg_source_push_data(context->wg_source, context->buffer, size))) WARN("Failed to push initial data, hr %#lx\n", hr); diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index e8164f4b01e..4e21a818427 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -351,6 +351,7 @@ struct wg_parser_stream_seek_params struct wg_source_create_params { const char *url; + UINT64 file_size; const void *data; UINT32 size; wg_source_t source; diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 3847f06eb3d..70502295537 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -94,6 +94,19 @@ static GstPad *create_pad_with_caps(GstPadDirection direction, GstCaps *caps) return pad; } +static gboolean src_query_duration(struct wg_source *source, GstQuery *query) +{ + GstFormat format; + + gst_query_parse_duration(query, &format, NULL); + GST_TRACE("source %p, format %s", source, gst_format_get_name(format)); + if (format != GST_FORMAT_BYTES) + return false; + + gst_query_set_duration(query, format, source->segment.stop); + return true; +} + static gboolean src_query_uri(struct wg_source *source, GstQuery *query) { gchar *uri; @@ -111,6 +124,8 @@ static gboolean src_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) switch (GST_QUERY_TYPE(query)) { + case GST_QUERY_DURATION: + return src_query_duration(source, query); case GST_QUERY_URI: if (!source->url) return false; @@ -153,6 +168,7 @@ NTSTATUS wg_source_create(void *args) } source->url = params->url ? strdup(params->url) : NULL; gst_segment_init(&source->segment, GST_FORMAT_BYTES); + source->segment.stop = params->file_size; if (!(source->container = gst_bin_new("wg_source"))) goto error; From a12f7607c7806d33e0ea774e81f6b0cd2ef33dc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 26 Apr 2023 18:49:44 +0200 Subject: [PATCH 047/413] winegstreamer/new_media_source: Handle GST_QUERY_SCHEDULING on wg_source src pad. CW-Bug-Id: #21953 --- dlls/winegstreamer/wg_source.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 70502295537..41fc4af3efd 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -107,6 +107,14 @@ static gboolean src_query_duration(struct wg_source *source, GstQuery *query) return true; } +static gboolean src_query_scheduling(struct wg_source *source, GstQuery *query) +{ + GST_TRACE("source %p", source); + gst_query_set_scheduling(query, GST_SCHEDULING_FLAG_SEEKABLE, 1, -1, 0); + gst_query_add_scheduling_mode(query, GST_PAD_MODE_PUSH); + return true; +} + static gboolean src_query_uri(struct wg_source *source, GstQuery *query) { gchar *uri; @@ -126,6 +134,8 @@ static gboolean src_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) { case GST_QUERY_DURATION: return src_query_duration(source, query); + case GST_QUERY_SCHEDULING: + return src_query_scheduling(source, query); case GST_QUERY_URI: if (!source->url) return false; From 1f3a79b16e0f84e53d277dcd7be2a263ee23a3b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 29 Apr 2023 18:31:16 +0200 Subject: [PATCH 048/413] winegstreamer/new_media_source: Create and link sink pads in the wg_source. CW-Bug-Id: #21953 --- dlls/winegstreamer/wg_source.c | 68 ++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 41fc4af3efd..3d8c60ebf7f 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -39,6 +39,13 @@ #include "unix_private.h" +#define WG_SOURCE_MAX_STREAMS 32 + +struct source_stream +{ + GstPad *pad; +}; + struct wg_source { gchar *url; @@ -46,6 +53,9 @@ struct wg_source GstElement *container; GstSegment segment; bool valid_segment; + + guint stream_count; + struct source_stream streams[WG_SOURCE_MAX_STREAMS]; }; static struct wg_source *get_source(wg_source_t source) @@ -145,6 +155,14 @@ static gboolean src_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) } } +static GstFlowReturn sink_chain_cb(GstPad *pad, GstObject *parent, GstBuffer *buffer) +{ + struct wg_soutce *source = gst_pad_get_element_private(pad); + GST_TRACE("source %p, pad %p, buffer %p.", source, pad, buffer); + gst_buffer_unref(buffer); + return GST_FLOW_EOS; +} + static GstEvent *create_stream_start_event(const char *stream_id) { GstStream *stream; @@ -161,6 +179,24 @@ static GstEvent *create_stream_start_event(const char *stream_id) return event; } +static void pad_added_cb(GstElement *element, GstPad *pad, gpointer user) +{ + struct wg_source *source = user; + GstPad *sink_pad; + guint index; + + GST_TRACE("source %p, element %p, pad %p.", source, element, pad); + if ((index = source->stream_count++) >= ARRAY_SIZE(source->streams)) + { + GST_FIXME("Not enough sink pads, need %u", source->stream_count); + return; + } + + sink_pad = source->streams[index].pad; + if (gst_pad_link(pad, sink_pad) < 0 || !gst_pad_set_active(sink_pad, true)) + GST_ERROR("Failed to link new pad to sink pad %p", sink_pad); +} + NTSTATUS wg_source_create(void *args) { struct wg_source_create_params *params = args; @@ -168,6 +204,8 @@ NTSTATUS wg_source_create(void *args) GstCaps *src_caps, *any_caps; struct wg_source *source; GstEvent *event; + GstPad *peer; + guint i; if (!(src_caps = detect_caps_from_data(params->url, params->data, params->size))) return STATUS_UNSUCCESSFUL; @@ -182,11 +220,21 @@ NTSTATUS wg_source_create(void *args) if (!(source->container = gst_bin_new("wg_source"))) goto error; + GST_OBJECT_FLAG_SET(source->container, GST_BIN_FLAG_STREAMS_AWARE); + if (!(source->src_pad = create_pad_with_caps(GST_PAD_SRC, src_caps))) goto error; gst_pad_set_element_private(source->src_pad, source); gst_pad_set_query_function(source->src_pad, src_query_cb); + for (i = 0; i < ARRAY_SIZE(source->streams); i++) + { + if (!(source->streams[i].pad = create_pad_with_caps(GST_PAD_SINK, NULL))) + goto error; + gst_pad_set_element_private(source->streams[i].pad, source); + gst_pad_set_chain_function(source->streams[i].pad, sink_chain_cb); + } + if (!(any_caps = gst_caps_new_any())) goto error; if (!(element = find_element(GST_ELEMENT_FACTORY_TYPE_DECODABLE, src_caps, any_caps)) @@ -195,6 +243,7 @@ NTSTATUS wg_source_create(void *args) gst_caps_unref(any_caps); goto error; } + g_signal_connect(element, "pad-added", G_CALLBACK(pad_added_cb), source); gst_caps_unref(any_caps); if (!link_src_to_element(source->src_pad, first)) @@ -202,6 +251,17 @@ NTSTATUS wg_source_create(void *args) if (!gst_pad_set_active(source->src_pad, true)) goto error; + /* try to link the first output pad, some demuxers only have static pads */ + if ((peer = gst_element_get_static_pad(last, "src"))) + { + GstPad *sink_pad = source->streams[0].pad; + if (gst_pad_link(peer, sink_pad) < 0 || !gst_pad_set_active(sink_pad, true)) + GST_ERROR("Failed to link static source pad %p", peer); + else + source->stream_count++; + gst_object_unref(peer); + } + gst_element_set_state(source->container, GST_STATE_PAUSED); if (!gst_element_get_state(source->container, NULL, NULL, -1)) goto error; @@ -221,6 +281,11 @@ NTSTATUS wg_source_create(void *args) gst_element_set_state(source->container, GST_STATE_NULL); gst_object_unref(source->container); } + for (i = 0; i < ARRAY_SIZE(source->streams); i++) + { + if (source->streams[i].pad) + gst_object_unref(source->streams[i].pad); + } if (source->src_pad) gst_object_unref(source->src_pad); free(source->url); @@ -235,11 +300,14 @@ NTSTATUS wg_source_create(void *args) NTSTATUS wg_source_destroy(void *args) { struct wg_source *source = get_source(*(wg_source_t *)args); + guint i; GST_TRACE("source %p", source); gst_element_set_state(source->container, GST_STATE_NULL); gst_object_unref(source->container); + for (i = 0; i < ARRAY_SIZE(source->streams); i++) + gst_object_unref(source->streams[i].pad); gst_object_unref(source->src_pad); free(source->url); free(source); From 0ca40dbc7184d7ceddb142231b4bc354bd84aa1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 22 Nov 2023 23:13:14 +0100 Subject: [PATCH 049/413] winegstreamer/new_media_source: Push the initial data from to the wg_source. CW-Bug-Id: #21953 --- dlls/winegstreamer/new_media_source.c | 2 +- dlls/winegstreamer/wg_source.c | 31 +++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c index d4c66fa3599..1d177cca6f9 100644 --- a/dlls/winegstreamer/new_media_source.c +++ b/dlls/winegstreamer/new_media_source.c @@ -2136,7 +2136,7 @@ static HRESULT WINAPI stream_handler_callback_Invoke(IMFAsyncCallback *iface, IM context->buffer, size, &context->wg_source))) WARN("Failed to create wg_source, hr %#lx\n", hr); else if (FAILED(hr = wg_source_push_data(context->wg_source, context->buffer, size))) - WARN("Failed to push initial data, hr %#lx\n", hr); + WARN("Failed to push wg_source data, hr %#lx\n", hr); else if (FAILED(hr = media_source_create(context, (IMFMediaSource **)&object))) WARN("Failed to create media source, hr %#lx\n", hr); diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 3d8c60ebf7f..54cdd14668d 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -104,6 +104,21 @@ static GstPad *create_pad_with_caps(GstPadDirection direction, GstCaps *caps) return pad; } +static GstBuffer *create_buffer_from_bytes(const void *data, guint size) +{ + GstBuffer *buffer; + + if (!(buffer = gst_buffer_new_and_alloc(size))) + GST_ERROR("Failed to allocate buffer for %#x bytes\n", size); + else + { + gst_buffer_fill(buffer, 0, data, size); + gst_buffer_set_size(buffer, size); + } + + return buffer; +} + static gboolean src_query_duration(struct wg_source *source, GstQuery *query) { GstFormat format; @@ -319,6 +334,8 @@ NTSTATUS wg_source_push_data(void *args) { struct wg_source_push_data_params *params = args; struct wg_source *source = get_source(params->source); + GstFlowReturn ret = GST_FLOW_OK; + GstBuffer *buffer; GstEvent *event; GST_TRACE("source %p, data %p, size %#x", source, params->data, params->size); @@ -331,5 +348,19 @@ NTSTATUS wg_source_push_data(void *args) source->valid_segment = true; } + if (!(buffer = create_buffer_from_bytes(params->data, params->size))) + { + GST_WARNING("Failed to allocate buffer for data"); + return STATUS_UNSUCCESSFUL; + } + + source->segment.start += params->size; + if ((ret = gst_pad_push(source->src_pad, buffer)) && ret != GST_FLOW_EOS) + { + GST_WARNING("Failed to push data buffer, ret %d", ret); + source->segment.start -= params->size; + return STATUS_UNSUCCESSFUL; + } + return STATUS_SUCCESS; } From e1b8bfb3c000b9c6db43856af59bd739b54e0833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 28 Apr 2023 22:44:15 +0200 Subject: [PATCH 050/413] winegstreamer/new_media_source: Push EOS event on stream or segment end. CW-Bug-Id: #21953 --- dlls/winegstreamer/wg_source.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 54cdd14668d..ee36e777e62 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -348,6 +348,13 @@ NTSTATUS wg_source_push_data(void *args) source->valid_segment = true; } + if (!params->size) + { + if (source->segment.start != source->segment.stop) + goto eos; + return STATUS_SUCCESS; + } + if (!(buffer = create_buffer_from_bytes(params->data, params->size))) { GST_WARNING("Failed to allocate buffer for data"); @@ -362,5 +369,14 @@ NTSTATUS wg_source_push_data(void *args) return STATUS_UNSUCCESSFUL; } + if (source->segment.start != source->segment.stop) + return STATUS_SUCCESS; + +eos: + if (!(event = gst_event_new_eos()) + || !gst_pad_push_event(source->src_pad, event)) + GST_WARNING("Failed to push EOS event"); + source->segment.start = source->segment.stop; + return STATUS_SUCCESS; } From 3ade96bdda5aee900203b29df63f656815189289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 24 Jul 2023 14:03:53 +0200 Subject: [PATCH 051/413] winegstreamer/new_media_source: Handle GST_EVENT_SEEK on wg_source src pad. CW-Bug-Id: #21953 --- dlls/winegstreamer/wg_source.c | 77 ++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index ee36e777e62..be40a20f3af 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -119,6 +119,67 @@ static GstBuffer *create_buffer_from_bytes(const void *data, guint size) return buffer; } +static gboolean src_event_seek(struct wg_source *source, GstEvent *event) +{ + guint32 seqnum = gst_event_get_seqnum(event); + GstSeekType cur_type, stop_type; + GstSeekFlags flags; + GstFormat format; + gint64 cur, stop; + gdouble rate; + + gst_event_parse_seek(event, &rate, &format, &flags, &cur_type, &cur, &stop_type, &stop); + gst_event_unref(event); + if (format != GST_FORMAT_BYTES) + return false; + + GST_TRACE("source %p, rate %f, format %s, flags %#x, cur_type %u, cur %#" G_GINT64_MODIFIER "x, " + "stop_type %u, stop %#" G_GINT64_MODIFIER "x.", source, rate, gst_format_get_name(format), + flags, cur_type, cur, stop_type, stop); + + if (flags & GST_SEEK_FLAG_FLUSH) + { + if (!(event = gst_event_new_flush_start())) + GST_ERROR("Failed to allocate flush_start event"); + else + { + gst_event_set_seqnum(event, seqnum); + if (!gst_pad_push_event(source->src_pad, event)) + GST_ERROR("Failed to push flush_start event"); + } + } + + source->segment.start = cur; + + if (flags & GST_SEEK_FLAG_FLUSH) + { + if (!(event = gst_event_new_flush_stop(true))) + GST_ERROR("Failed to allocate flush_stop event"); + else + { + gst_event_set_seqnum(event, seqnum); + if (!gst_pad_push_event(source->src_pad, event)) + GST_ERROR("Failed to push flush_stop event"); + } + source->valid_segment = false; + } + + return true; +} + +static gboolean src_event_cb(GstPad *pad, GstObject *parent, GstEvent *event) +{ + struct wg_source *source = gst_pad_get_element_private(pad); + + switch (GST_EVENT_TYPE(event)) + { + case GST_EVENT_SEEK: + return src_event_seek(source, event); + default: + return gst_pad_event_default(pad, parent, event); + } +} + static gboolean src_query_duration(struct wg_source *source, GstQuery *query) { GstFormat format; @@ -140,6 +201,19 @@ static gboolean src_query_scheduling(struct wg_source *source, GstQuery *query) return true; } +static gboolean src_query_seeking(struct wg_source *source, GstQuery *query) +{ + GstFormat format; + + gst_query_parse_seeking(query, &format, NULL, NULL, NULL); + GST_TRACE("source %p, format %s", source, gst_format_get_name(format)); + if (format != GST_FORMAT_BYTES) + return false; + + gst_query_set_seeking(query, GST_FORMAT_BYTES, 1, 0, source->segment.stop); + return true; +} + static gboolean src_query_uri(struct wg_source *source, GstQuery *query) { gchar *uri; @@ -161,6 +235,8 @@ static gboolean src_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) return src_query_duration(source, query); case GST_QUERY_SCHEDULING: return src_query_scheduling(source, query); + case GST_QUERY_SEEKING: + return src_query_seeking(source, query); case GST_QUERY_URI: if (!source->url) return false; @@ -241,6 +317,7 @@ NTSTATUS wg_source_create(void *args) goto error; gst_pad_set_element_private(source->src_pad, source); gst_pad_set_query_function(source->src_pad, src_query_cb); + gst_pad_set_event_function(source->src_pad, src_event_cb); for (i = 0; i < ARRAY_SIZE(source->streams); i++) { From 571fd401a9847456adaaea7f0f91fc1adab6c4bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 28 Apr 2023 11:53:27 +0200 Subject: [PATCH 052/413] winegstreamer/new_media_source: Handle GST_EVENT_STREAM_START and create wg_source streams. CW-Bug-Id: #21953 --- dlls/winegstreamer/wg_source.c | 48 ++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index be40a20f3af..f4481166d26 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -254,6 +254,38 @@ static GstFlowReturn sink_chain_cb(GstPad *pad, GstObject *parent, GstBuffer *bu return GST_FLOW_EOS; } +static gboolean sink_event_stream_start(struct wg_source *source, GstPad *pad, GstEvent *event) +{ + guint group, flags; + GstStream *stream; + const gchar *id; + + gst_event_parse_stream_start(event, &id); + gst_event_parse_stream(event, &stream); + gst_event_parse_stream_flags(event, &flags); + if (!gst_event_parse_group_id(event, &group)) + group = -1; + + GST_TRACE("source %p, pad %p, stream %p, id %s, flags %#x, group %d", + source, pad, stream, id, flags, group); + + gst_event_unref(event); + return true; +} + +static gboolean sink_event_cb(GstPad *pad, GstObject *parent, GstEvent *event) +{ + struct wg_source *source = gst_pad_get_element_private(pad); + + switch (GST_EVENT_TYPE(event)) + { + case GST_EVENT_STREAM_START: + return sink_event_stream_start(source, pad, event); + default: + return gst_pad_event_default(pad, parent, event); + } +} + static GstEvent *create_stream_start_event(const char *stream_id) { GstStream *stream; @@ -273,7 +305,10 @@ static GstEvent *create_stream_start_event(const char *stream_id) static void pad_added_cb(GstElement *element, GstPad *pad, gpointer user) { struct wg_source *source = user; + char stream_id[256]; + GstFlowReturn ret; GstPad *sink_pad; + GstEvent *event; guint index; GST_TRACE("source %p, element %p, pad %p.", source, element, pad); @@ -286,6 +321,18 @@ static void pad_added_cb(GstElement *element, GstPad *pad, gpointer user) sink_pad = source->streams[index].pad; if (gst_pad_link(pad, sink_pad) < 0 || !gst_pad_set_active(sink_pad, true)) GST_ERROR("Failed to link new pad to sink pad %p", sink_pad); + + snprintf(stream_id, ARRAY_SIZE(stream_id), "wg_source/%03u", index); + if (!(event = create_stream_start_event(stream_id))) + GST_ERROR("Failed to create stream event for sink pad %p", sink_pad); + else + { + if ((ret = gst_pad_store_sticky_event(pad, event)) < 0) + GST_ERROR("Failed to create pad %p stream, ret %d", sink_pad, ret); + if ((ret = gst_pad_store_sticky_event(sink_pad, event)) < 0) + GST_ERROR("Failed to create pad %p stream, ret %d", sink_pad, ret); + gst_event_unref(event); + } } NTSTATUS wg_source_create(void *args) @@ -325,6 +372,7 @@ NTSTATUS wg_source_create(void *args) goto error; gst_pad_set_element_private(source->streams[i].pad, source); gst_pad_set_chain_function(source->streams[i].pad, sink_chain_cb); + gst_pad_set_event_function(source->streams[i].pad, sink_event_cb); } if (!(any_caps = gst_caps_new_any())) From 3a2659b17a3976b0eb27bb65b425fa9b1fdfde87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 18 Jun 2023 22:56:43 +0200 Subject: [PATCH 053/413] winegstreamer/new_media_source: Query stream duration from GST_EVENT_STREAM_START. CW-Bug-Id: #21953 --- dlls/winegstreamer/wg_source.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index f4481166d26..2353d5f3201 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -53,6 +53,7 @@ struct wg_source GstElement *container; GstSegment segment; bool valid_segment; + guint64 max_duration; guint stream_count; struct source_stream streams[WG_SOURCE_MAX_STREAMS]; @@ -258,6 +259,7 @@ static gboolean sink_event_stream_start(struct wg_source *source, GstPad *pad, G { guint group, flags; GstStream *stream; + gint64 duration; const gchar *id; gst_event_parse_stream_start(event, &id); @@ -266,8 +268,11 @@ static gboolean sink_event_stream_start(struct wg_source *source, GstPad *pad, G if (!gst_event_parse_group_id(event, &group)) group = -1; - GST_TRACE("source %p, pad %p, stream %p, id %s, flags %#x, group %d", - source, pad, stream, id, flags, group); + if (gst_pad_peer_query_duration(pad, GST_FORMAT_TIME, &duration) && GST_CLOCK_TIME_IS_VALID(duration)) + source->max_duration = max(source->max_duration, duration); + + GST_TRACE("source %p, pad %p, stream %p, id %s, flags %#x, group %d, duration %" GST_TIME_FORMAT, + source, pad, stream, id, flags, group, GST_TIME_ARGS(duration)); gst_event_unref(event); return true; From 041a12747d0a96a9e1c12a99b524bcc2b7b0d1d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 28 Apr 2023 12:20:15 +0200 Subject: [PATCH 054/413] winegstreamer/new_media_source: Handle GST_EVENT_CAPS and update wg_source streams caps. CW-Bug-Id: #21953 --- dlls/winegstreamer/wg_source.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 2353d5f3201..40d75332975 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -255,6 +255,28 @@ static GstFlowReturn sink_chain_cb(GstPad *pad, GstObject *parent, GstBuffer *bu return GST_FLOW_EOS; } +static gboolean sink_event_caps(struct wg_source *source, GstPad *pad, GstEvent *event) +{ + GstStream *stream; + GstCaps *caps; + gchar *str; + + gst_event_parse_caps(event, &caps); + str = gst_caps_to_string(caps); + GST_TRACE("source %p, pad %p, caps %s", source, pad, str); + g_free(str); + + if ((stream = gst_pad_get_stream(pad))) + { + gst_stream_set_caps(stream, gst_caps_copy(caps)); + gst_stream_set_stream_type(stream, stream_type_from_caps(caps)); + gst_object_unref(stream); + } + + gst_event_unref(event); + return !!stream; +} + static gboolean sink_event_stream_start(struct wg_source *source, GstPad *pad, GstEvent *event) { guint group, flags; @@ -284,6 +306,8 @@ static gboolean sink_event_cb(GstPad *pad, GstObject *parent, GstEvent *event) switch (GST_EVENT_TYPE(event)) { + case GST_EVENT_CAPS: + return sink_event_caps(source, pad, event); case GST_EVENT_STREAM_START: return sink_event_stream_start(source, pad, event); default: From 3913f3ceb47047f5382e4710d0065d6b6dee6e18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 18 Jun 2023 15:13:06 +0200 Subject: [PATCH 055/413] winegstreamer/new_media_source: Introduce a new wg_source_get_stream_count unix call. CW-Bug-Id: #21953 --- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/main.c | 22 ++++++++++++++++ dlls/winegstreamer/new_media_source.c | 7 ++++-- dlls/winegstreamer/unix_private.h | 1 + dlls/winegstreamer/unixlib.h | 7 ++++++ dlls/winegstreamer/wg_parser.c | 2 ++ dlls/winegstreamer/wg_source.c | 36 +++++++++++++++++++++++++++ 7 files changed, 74 insertions(+), 2 deletions(-) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index c09c367a66b..1a87e8600b4 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -106,6 +106,7 @@ void wg_parser_stream_seek(wg_parser_stream_t stream, double rate, HRESULT wg_source_create(const WCHAR *url, uint64_t file_size, const void *data, uint32_t size, wg_source_t *out); void wg_source_destroy(wg_source_t source); +HRESULT wg_source_get_stream_count(wg_source_t source, uint32_t *stream_count); HRESULT wg_source_push_data(wg_source_t source, const void *data, uint32_t size); wg_transform_t wg_transform_create(const struct wg_format *input_format, diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 8c6a5fb385e..620f62daf06 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -379,6 +379,28 @@ void wg_source_destroy(wg_source_t source) WINE_UNIX_CALL(unix_wg_source_destroy, &source); } +HRESULT wg_source_get_stream_count(wg_source_t source, uint32_t *stream_count) +{ + struct wg_source_get_stream_count_params params = + { + .source = source, + }; + NTSTATUS status; + + TRACE("source %#I64x, stream_count %p\n", source, stream_count); + + if ((status = WINE_UNIX_CALL(unix_wg_source_get_stream_count, ¶ms)) + && status != STATUS_PENDING) + { + WARN("wg_source_get_stream_count returned status %#lx\n", status); + return HRESULT_FROM_NT(status); + } + + *stream_count = params.stream_count; + TRACE("source %#I64x, stream_count %u\n", source, *stream_count); + return S_OK; +} + HRESULT wg_source_push_data(wg_source_t source, const void *data, uint32_t size) { struct wg_source_push_data_params params = diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c index 1d177cca6f9..45893b397d4 100644 --- a/dlls/winegstreamer/new_media_source.c +++ b/dlls/winegstreamer/new_media_source.c @@ -41,6 +41,7 @@ struct object_context BYTE *buffer; wg_source_t wg_source; + UINT32 stream_count; }; static struct object_context *impl_from_IUnknown(IUnknown *iface) @@ -1735,7 +1736,7 @@ static void media_source_init_descriptors(struct media_source *source) static HRESULT media_source_create(struct object_context *context, IMFMediaSource **out) { - unsigned int stream_count = UINT_MAX; + UINT32 stream_count; struct media_source *object; wg_parser_t parser; unsigned int i; @@ -1831,7 +1832,7 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc free(object->descriptors); free(object->streams); - if (stream_count != UINT_MAX) + if (object->state == SOURCE_OPENING) wg_parser_disconnect(object->wg_parser); if (object->read_thread) { @@ -2137,6 +2138,8 @@ static HRESULT WINAPI stream_handler_callback_Invoke(IMFAsyncCallback *iface, IM WARN("Failed to create wg_source, hr %#lx\n", hr); else if (FAILED(hr = wg_source_push_data(context->wg_source, context->buffer, size))) WARN("Failed to push wg_source data, hr %#lx\n", hr); + else if (FAILED(hr = wg_source_get_stream_count(context->wg_source, &context->stream_count))) + WARN("Failed to get wg_source status, hr %#lx\n", hr); else if (FAILED(hr = media_source_create(context, (IMFMediaSource **)&object))) WARN("Failed to create media source, hr %#lx\n", hr); diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 6aa7ef92f38..e2f56dbd55b 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -61,6 +61,7 @@ extern GstCaps *wg_format_to_caps(const struct wg_format *format); extern NTSTATUS wg_source_create(void *args); extern NTSTATUS wg_source_destroy(void *args); +extern NTSTATUS wg_source_get_stream_count(void *args); extern NTSTATUS wg_source_push_data(void *args); /* wg_transform.c */ diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 4e21a818427..03b0c9d8f28 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -357,6 +357,12 @@ struct wg_source_create_params wg_source_t source; }; +struct wg_source_get_stream_count_params +{ + wg_source_t source; + UINT32 stream_count; +}; + struct wg_source_push_data_params { wg_source_t source; @@ -476,6 +482,7 @@ enum unix_funcs unix_wg_source_create, unix_wg_source_destroy, + unix_wg_source_get_stream_count, unix_wg_source_push_data, unix_wg_transform_create, diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 5c613a66f45..e14a1f52643 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -2208,6 +2208,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_source_create), X(wg_source_destroy), + X(wg_source_get_stream_count), X(wg_source_push_data), X(wg_transform_create), @@ -2585,6 +2586,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = X64(wg_source_create), X(wg_source_destroy), + X(wg_source_get_stream_count), X64(wg_source_push_data), X64(wg_transform_create), diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 40d75332975..82cfd6acfbb 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -120,6 +120,22 @@ static GstBuffer *create_buffer_from_bytes(const void *data, guint size) return buffer; } +static GstStream *source_get_stream(struct wg_source *source, guint index) +{ + return index >= source->stream_count ? NULL : gst_pad_get_stream(source->streams[index].pad); +} + +static GstCaps *source_get_stream_caps(struct wg_source *source, guint index) +{ + GstStream *stream; + GstCaps *caps; + if (!(stream = source_get_stream(source, index))) + return NULL; + caps = gst_stream_get_caps(stream); + gst_object_unref(stream); + return caps; +} + static gboolean src_event_seek(struct wg_source *source, GstEvent *event) { guint32 seqnum = gst_event_get_seqnum(event); @@ -484,6 +500,26 @@ NTSTATUS wg_source_destroy(void *args) return STATUS_SUCCESS; } +NTSTATUS wg_source_get_stream_count(void *args) +{ + struct wg_source_get_stream_count_params *params = args; + struct wg_source *source = get_source(params->source); + UINT i, stream_count; + GstCaps *caps; + + GST_TRACE("source %p", source); + + for (i = 0, stream_count = source->stream_count; i < stream_count; i++) + { + if (!(caps = source_get_stream_caps(source, i))) + return STATUS_PENDING; + gst_caps_unref(caps); + } + + params->stream_count = stream_count; + return STATUS_SUCCESS; +} + NTSTATUS wg_source_push_data(void *args) { struct wg_source_push_data_params *params = args; From ce9f89b1cf50376346b11870773a715b4bb76f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 18 Jun 2023 15:14:14 +0200 Subject: [PATCH 056/413] winegstreamer/new_media_source: Set the MF_PD_TOTAL_FILE_SIZE presentation descriptor attribute. CW-Bug-Id: #21953 --- dlls/winegstreamer/new_media_source.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c index 45893b397d4..db2a661b6ff 100644 --- a/dlls/winegstreamer/new_media_source.c +++ b/dlls/winegstreamer/new_media_source.c @@ -1559,6 +1559,8 @@ static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource * hr = MF_E_SHUTDOWN; else if (SUCCEEDED(hr = MFCreatePresentationDescriptor(source->stream_count, source->descriptors, descriptor))) { + if (FAILED(hr = IMFPresentationDescriptor_SetUINT64(*descriptor, &MF_PD_TOTAL_FILE_SIZE, source->file_size))) + WARN("Failed to set presentation descriptor MF_PD_TOTAL_FILE_SIZE, hr %#lx\n", hr); if (FAILED(hr = IMFPresentationDescriptor_SetUINT64(*descriptor, &MF_PD_DURATION, source->duration))) WARN("Failed to set presentation descriptor MF_PD_DURATION, hr %#lx\n", hr); From 25b5ea3f64ec254bd981869dd5ce32e84fdffd3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 24 Jul 2023 14:06:25 +0200 Subject: [PATCH 057/413] winegstreamer/new_media_source: Read stream data and provide samples to wg_source. CW-Bug-Id: #21953 --- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/main.c | 22 ++++++++++++++++++++++ dlls/winegstreamer/new_media_source.c | 20 +++++++++++++++++++- dlls/winegstreamer/unix_private.h | 1 + dlls/winegstreamer/unixlib.h | 7 +++++++ dlls/winegstreamer/wg_parser.c | 2 ++ dlls/winegstreamer/wg_source.c | 11 +++++++++++ 7 files changed, 63 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 1a87e8600b4..dd7e2d18817 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -107,6 +107,7 @@ HRESULT wg_source_create(const WCHAR *url, uint64_t file_size, const void *data, uint32_t size, wg_source_t *out); void wg_source_destroy(wg_source_t source); HRESULT wg_source_get_stream_count(wg_source_t source, uint32_t *stream_count); +HRESULT wg_source_get_position(wg_source_t source, uint64_t *read_offset); HRESULT wg_source_push_data(wg_source_t source, const void *data, uint32_t size); wg_transform_t wg_transform_create(const struct wg_format *input_format, diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 620f62daf06..60cdc715470 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -401,6 +401,28 @@ HRESULT wg_source_get_stream_count(wg_source_t source, uint32_t *stream_count) return S_OK; } +HRESULT wg_source_get_position(wg_source_t source, uint64_t *read_offset) +{ + struct wg_source_get_position_params params = + { + .source = source, + }; + NTSTATUS status; + + TRACE("source %#I64x, read_offset %p\n", source, read_offset); + + if ((status = WINE_UNIX_CALL(unix_wg_source_get_position, ¶ms)) + && status != STATUS_PENDING) + { + WARN("wg_source_get_position returned status %#lx\n", status); + return HRESULT_FROM_NT(status); + } + + *read_offset = params.read_offset; + TRACE("source %#I64x, read_offset %#I64x\n", source, *read_offset); + return S_OK; +} + HRESULT wg_source_push_data(wg_source_t source, const void *data, uint32_t size) { struct wg_source_push_data_params params = diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c index db2a661b6ff..03def5550de 100644 --- a/dlls/winegstreamer/new_media_source.c +++ b/dlls/winegstreamer/new_media_source.c @@ -2127,6 +2127,7 @@ static HRESULT WINAPI stream_handler_callback_Invoke(IMFAsyncCallback *iface, IM IUnknown *object, *state = IMFAsyncResult_GetStateNoAddRef(result); struct object_context *context; struct result_entry *entry; + UINT64 read_offset; DWORD size = 0; HRESULT hr; @@ -2135,13 +2136,30 @@ static HRESULT WINAPI stream_handler_callback_Invoke(IMFAsyncCallback *iface, IM if (FAILED(hr = IMFByteStream_EndRead(context->stream, result, &size))) WARN("Failed to complete stream read, hr %#lx\n", hr); - else if (FAILED(hr = wg_source_create(context->url, context->file_size, + else if (!context->wg_source && FAILED(hr = wg_source_create(context->url, context->file_size, context->buffer, size, &context->wg_source))) WARN("Failed to create wg_source, hr %#lx\n", hr); else if (FAILED(hr = wg_source_push_data(context->wg_source, context->buffer, size))) WARN("Failed to push wg_source data, hr %#lx\n", hr); else if (FAILED(hr = wg_source_get_stream_count(context->wg_source, &context->stream_count))) WARN("Failed to get wg_source status, hr %#lx\n", hr); + else if (!context->stream_count) + { + QWORD position, offset; + if (FAILED(hr = wg_source_get_position(context->wg_source, &read_offset))) + WARN("Failed to get wg_source position, hr %#lx\n", hr); + else if (FAILED(hr = IMFByteStream_GetCurrentPosition(context->stream, &position))) + WARN("Failed to get current byte stream position, hr %#lx\n", hr); + else if (position != (offset = min(read_offset, context->file_size)) + && FAILED(hr = IMFByteStream_SetCurrentPosition(context->stream, offset))) + WARN("Failed to set current byte stream position, hr %#lx\n", hr); + else + { + UINT32 read_size = min(SOURCE_BUFFER_SIZE, context->file_size - offset); + return IMFByteStream_BeginRead(context->stream, context->buffer, read_size, + &handler->IMFAsyncCallback_iface, state); + } + } else if (FAILED(hr = media_source_create(context, (IMFMediaSource **)&object))) WARN("Failed to create media source, hr %#lx\n", hr); diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index e2f56dbd55b..e6167fef1df 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -62,6 +62,7 @@ extern GstCaps *wg_format_to_caps(const struct wg_format *format); extern NTSTATUS wg_source_create(void *args); extern NTSTATUS wg_source_destroy(void *args); extern NTSTATUS wg_source_get_stream_count(void *args); +extern NTSTATUS wg_source_get_position(void *args); extern NTSTATUS wg_source_push_data(void *args); /* wg_transform.c */ diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 03b0c9d8f28..9abeda1c3c7 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -363,6 +363,12 @@ struct wg_source_get_stream_count_params UINT32 stream_count; }; +struct wg_source_get_position_params +{ + wg_source_t source; + UINT64 read_offset; +}; + struct wg_source_push_data_params { wg_source_t source; @@ -483,6 +489,7 @@ enum unix_funcs unix_wg_source_create, unix_wg_source_destroy, unix_wg_source_get_stream_count, + unix_wg_source_get_position, unix_wg_source_push_data, unix_wg_transform_create, diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index e14a1f52643..043af8a32d2 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -2209,6 +2209,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_source_create), X(wg_source_destroy), X(wg_source_get_stream_count), + X(wg_source_get_position), X(wg_source_push_data), X(wg_transform_create), @@ -2587,6 +2588,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = X64(wg_source_create), X(wg_source_destroy), X(wg_source_get_stream_count), + X(wg_source_get_position), X64(wg_source_push_data), X64(wg_transform_create), diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 82cfd6acfbb..9906e8c51c6 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -520,6 +520,17 @@ NTSTATUS wg_source_get_stream_count(void *args) return STATUS_SUCCESS; } +NTSTATUS wg_source_get_position(void *args) +{ + struct wg_source_get_position_params *params = args; + struct wg_source *source = get_source(params->source); + + GST_TRACE("source %p", source); + + params->read_offset = source->segment.start; + return STATUS_SUCCESS; +} + NTSTATUS wg_source_push_data(void *args) { struct wg_source_push_data_params *params = args; From bcc131b531bc806688d2b5cd4b46ccad471b2962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 29 Nov 2023 18:02:55 +0100 Subject: [PATCH 058/413] winegstreamer/new_media_source: Query the stream max duration from the wg_source. CW-Bug-Id: #21953 --- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/main.c | 22 ++++++++++++++++++++++ dlls/winegstreamer/new_media_source.c | 4 ++-- dlls/winegstreamer/unix_private.h | 1 + dlls/winegstreamer/unixlib.h | 7 +++++++ dlls/winegstreamer/wg_parser.c | 2 ++ dlls/winegstreamer/wg_source.c | 11 +++++++++++ 7 files changed, 46 insertions(+), 2 deletions(-) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index dd7e2d18817..5c23483e332 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -107,6 +107,7 @@ HRESULT wg_source_create(const WCHAR *url, uint64_t file_size, const void *data, uint32_t size, wg_source_t *out); void wg_source_destroy(wg_source_t source); HRESULT wg_source_get_stream_count(wg_source_t source, uint32_t *stream_count); +HRESULT wg_source_get_duration(wg_source_t source, uint64_t *duration); HRESULT wg_source_get_position(wg_source_t source, uint64_t *read_offset); HRESULT wg_source_push_data(wg_source_t source, const void *data, uint32_t size); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 60cdc715470..1e7f40f1c7a 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -401,6 +401,28 @@ HRESULT wg_source_get_stream_count(wg_source_t source, uint32_t *stream_count) return S_OK; } +HRESULT wg_source_get_duration(wg_source_t source, uint64_t *duration) +{ + struct wg_source_get_duration_params params = + { + .source = source, + }; + NTSTATUS status; + + TRACE("source %#I64x, duration %p\n", source, duration); + + if ((status = WINE_UNIX_CALL(unix_wg_source_get_duration, ¶ms)) + && status != STATUS_PENDING) + { + WARN("wg_source_get_duration returned status %#lx\n", status); + return HRESULT_FROM_NT(status); + } + + *duration = params.duration; + TRACE("source %#I64x, duration %s\n", source, debugstr_time(*duration)); + return S_OK; +} + HRESULT wg_source_get_position(wg_source_t source, uint64_t *read_offset) { struct wg_source_get_position_params params = diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c index 03def5550de..1ecc6cba040 100644 --- a/dlls/winegstreamer/new_media_source.c +++ b/dlls/winegstreamer/new_media_source.c @@ -1765,9 +1765,10 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) goto fail; - if (FAILED(hr = MFAllocateWorkQueue(&object->async_commands_queue))) goto fail; + if (FAILED(hr = wg_source_get_duration(object->wg_source, &object->duration))) + goto fail; if (!(parser = wg_parser_create(WG_PARSER_DECODEBIN, FALSE, FALSE))) { @@ -1808,7 +1809,6 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc goto fail; } - object->duration = max(object->duration, wg_parser_stream_get_duration(wg_stream)); IMFStreamDescriptor_AddRef(descriptor); object->descriptors[i] = descriptor; object->streams[i] = stream; diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index e6167fef1df..df3bda1cad3 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -62,6 +62,7 @@ extern GstCaps *wg_format_to_caps(const struct wg_format *format); extern NTSTATUS wg_source_create(void *args); extern NTSTATUS wg_source_destroy(void *args); extern NTSTATUS wg_source_get_stream_count(void *args); +extern NTSTATUS wg_source_get_duration(void *args); extern NTSTATUS wg_source_get_position(void *args); extern NTSTATUS wg_source_push_data(void *args); diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 9abeda1c3c7..c61b5190d4c 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -363,6 +363,12 @@ struct wg_source_get_stream_count_params UINT32 stream_count; }; +struct wg_source_get_duration_params +{ + wg_source_t source; + UINT64 duration; +}; + struct wg_source_get_position_params { wg_source_t source; @@ -489,6 +495,7 @@ enum unix_funcs unix_wg_source_create, unix_wg_source_destroy, unix_wg_source_get_stream_count, + unix_wg_source_get_duration, unix_wg_source_get_position, unix_wg_source_push_data, diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 043af8a32d2..1f6ca1a5ccd 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -2209,6 +2209,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_source_create), X(wg_source_destroy), X(wg_source_get_stream_count), + X(wg_source_get_duration), X(wg_source_get_position), X(wg_source_push_data), @@ -2588,6 +2589,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = X64(wg_source_create), X(wg_source_destroy), X(wg_source_get_stream_count), + X(wg_source_get_duration), X(wg_source_get_position), X64(wg_source_push_data), diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 9906e8c51c6..ee9d7f2c9a0 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -520,6 +520,17 @@ NTSTATUS wg_source_get_stream_count(void *args) return STATUS_SUCCESS; } +NTSTATUS wg_source_get_duration(void *args) +{ + struct wg_source_get_duration_params *params = args; + struct wg_source *source = get_source(params->source); + + GST_TRACE("source %p", source); + + params->duration = source->max_duration / 100; + return STATUS_SUCCESS; +} + NTSTATUS wg_source_get_position(void *args) { struct wg_source_get_position_params *params = args; From 08523935dcd13e2df79d96c280fa7028ffdf0de2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 24 Jul 2023 14:12:21 +0200 Subject: [PATCH 059/413] winegstreamer/new_media_source: Set the MF_PD_MIME_TYPE presentation descriptor attribute. CW-Bug-Id: #21953 --- dlls/winegstreamer/gst_private.h | 3 ++- dlls/winegstreamer/main.c | 8 +++++--- dlls/winegstreamer/new_media_source.c | 7 ++++++- dlls/winegstreamer/unixlib.h | 1 + dlls/winegstreamer/wg_parser.c | 2 ++ dlls/winegstreamer/wg_source.c | 9 +++++++++ 6 files changed, 25 insertions(+), 5 deletions(-) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 5c23483e332..4af76cc4371 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -104,7 +104,8 @@ 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); HRESULT wg_source_create(const WCHAR *url, uint64_t file_size, - const void *data, uint32_t size, wg_source_t *out); + const void *data, uint32_t size, WCHAR mime_type[256], + wg_source_t *out); void wg_source_destroy(wg_source_t source); HRESULT wg_source_get_stream_count(wg_source_t source, uint32_t *stream_count); HRESULT wg_source_get_duration(wg_source_t source, uint64_t *duration); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 1e7f40f1c7a..e15dc0e543b 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -343,7 +343,8 @@ void wg_parser_stream_seek(wg_parser_stream_t stream, double rate, } HRESULT wg_source_create(const WCHAR *url, uint64_t file_size, - const void *data, uint32_t size, wg_source_t *out) + const void *data, uint32_t size, WCHAR mime_type[256], + wg_source_t *out) { struct wg_source_create_params params = { @@ -354,8 +355,8 @@ HRESULT wg_source_create(const WCHAR *url, uint64_t file_size, char *tmp = url ? malloc(len) : NULL; NTSTATUS status; - TRACE("url %s, file_size %#I64x, data %p, size %#x\n", debugstr_w(url), - file_size, data, size); + TRACE("url %s, file_size %#I64x, data %p, size %#x, mime_type %p\n", debugstr_w(url), + file_size, data, size, mime_type); if ((params.url = tmp)) WideCharToMultiByte(CP_ACP, 0, url, -1, tmp, len, NULL, NULL); @@ -365,6 +366,7 @@ HRESULT wg_source_create(const WCHAR *url, uint64_t file_size, else { TRACE("Returning source %#I64x.\n", params.source); + MultiByteToWideChar(CP_ACP, 0, params.mime_type, -1, mime_type, 256); *out = params.source; } diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c index 1ecc6cba040..c279cfdd2f5 100644 --- a/dlls/winegstreamer/new_media_source.c +++ b/dlls/winegstreamer/new_media_source.c @@ -41,6 +41,7 @@ struct object_context BYTE *buffer; wg_source_t wg_source; + WCHAR mime_type[256]; UINT32 stream_count; }; @@ -302,6 +303,7 @@ struct media_source CRITICAL_SECTION cs; wg_source_t wg_source; + WCHAR mime_type[256]; UINT64 file_size; wg_parser_t wg_parser; UINT64 duration; @@ -1559,6 +1561,8 @@ static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource * hr = MF_E_SHUTDOWN; else if (SUCCEEDED(hr = MFCreatePresentationDescriptor(source->stream_count, source->descriptors, descriptor))) { + if (FAILED(hr = IMFPresentationDescriptor_SetString(*descriptor, &MF_PD_MIME_TYPE, source->mime_type))) + WARN("Failed to set presentation descriptor MF_PD_MIME_TYPE, hr %#lx\n", hr); if (FAILED(hr = IMFPresentationDescriptor_SetUINT64(*descriptor, &MF_PD_TOTAL_FILE_SIZE, source->file_size))) WARN("Failed to set presentation descriptor MF_PD_TOTAL_FILE_SIZE, hr %#lx\n", hr); if (FAILED(hr = IMFPresentationDescriptor_SetUINT64(*descriptor, &MF_PD_DURATION, source->duration))) @@ -1755,6 +1759,7 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc object->ref = 1; object->byte_stream = context->stream; IMFByteStream_AddRef(context->stream); + wcscpy(object->mime_type, context->mime_type); object->file_size = context->file_size; object->rate = 1.0f; InitializeCriticalSection(&object->cs); @@ -2137,7 +2142,7 @@ static HRESULT WINAPI stream_handler_callback_Invoke(IMFAsyncCallback *iface, IM if (FAILED(hr = IMFByteStream_EndRead(context->stream, result, &size))) WARN("Failed to complete stream read, hr %#lx\n", hr); else if (!context->wg_source && FAILED(hr = wg_source_create(context->url, context->file_size, - context->buffer, size, &context->wg_source))) + context->buffer, size, context->mime_type, &context->wg_source))) WARN("Failed to create wg_source, hr %#lx\n", hr); else if (FAILED(hr = wg_source_push_data(context->wg_source, context->buffer, size))) WARN("Failed to push wg_source data, hr %#lx\n", hr); diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index c61b5190d4c..9db7c76a2af 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -354,6 +354,7 @@ struct wg_source_create_params UINT64 file_size; const void *data; UINT32 size; + char mime_type[256]; wg_source_t source; }; diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 1f6ca1a5ccd..826fa4168bb 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -2367,6 +2367,7 @@ NTSTATUS wow64_wg_source_create(void *args) PTR32 url; PTR32 data; UINT32 size; + char mime_type[256]; wg_source_t source; } *params32 = args; struct wg_source_create_params params = @@ -2378,6 +2379,7 @@ NTSTATUS wow64_wg_source_create(void *args) NTSTATUS ret; ret = wg_source_create(¶ms); + strcpy(params32->mime_type, params.mime_type); params32->source = params.source; return ret; } diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index ee9d7f2c9a0..ba82c2f107e 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -386,6 +386,7 @@ NTSTATUS wg_source_create(void *args) GstElement *first = NULL, *last = NULL, *element; GstCaps *src_caps, *any_caps; struct wg_source *source; + const gchar *media_type; GstEvent *event; GstPad *peer; guint i; @@ -401,6 +402,14 @@ NTSTATUS wg_source_create(void *args) gst_segment_init(&source->segment, GST_FORMAT_BYTES); source->segment.stop = params->file_size; + media_type = gst_structure_get_name(gst_caps_get_structure(src_caps, 0)); + if (!strcmp(media_type, "video/quicktime")) + strcpy(params->mime_type, "video/mp4"); + else if (!strcmp(media_type, "video/x-msvideo")) + strcpy(params->mime_type, "video/avi"); + else + lstrcpynA(params->mime_type, media_type, ARRAY_SIZE(params->mime_type)); + if (!(source->container = gst_bin_new("wg_source"))) goto error; GST_OBJECT_FLAG_SET(source->container, GST_BIN_FLAG_STREAMS_AWARE); From bf54ba49597c60e13837f666c5b6904a9a2587ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 29 Apr 2023 18:35:22 +0200 Subject: [PATCH 060/413] winegstreamer/new_media_source: Handle GST_EVENT_TAG and udpate wg_source streams tags. CW-Bug-Id: #21953 --- dlls/winegstreamer/wg_source.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index ba82c2f107e..1ef7bb7dff9 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -293,6 +293,31 @@ static gboolean sink_event_caps(struct wg_source *source, GstPad *pad, GstEvent return !!stream; } +static gboolean sink_event_tag(struct wg_source *source, GstPad *pad, GstEvent *event) +{ + GstTagList *new_tags; + GstStream *stream; + + gst_event_parse_tag(event, &new_tags); + GST_TRACE("source %p, pad %p, new_tags %p", source, pad, new_tags); + + if ((stream = gst_pad_get_stream(pad))) + { + GstTagList *old_tags = gst_stream_get_tags(stream); + if ((new_tags = gst_tag_list_merge(old_tags, new_tags, GST_TAG_MERGE_REPLACE))) + { + gst_stream_set_tags(stream, new_tags); + gst_tag_list_unref(new_tags); + } + if (old_tags) + gst_tag_list_unref(old_tags); + gst_object_unref(stream); + } + + gst_event_unref(event); + return stream && new_tags; +} + static gboolean sink_event_stream_start(struct wg_source *source, GstPad *pad, GstEvent *event) { guint group, flags; @@ -324,6 +349,8 @@ static gboolean sink_event_cb(GstPad *pad, GstObject *parent, GstEvent *event) { case GST_EVENT_CAPS: return sink_event_caps(source, pad, event); + case GST_EVENT_TAG: + return sink_event_tag(source, pad, event); case GST_EVENT_STREAM_START: return sink_event_stream_start(source, pad, event); default: From 42b2ff4ce3f82f6c901033b3033a53361e32e082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 19 Nov 2023 16:53:47 +0100 Subject: [PATCH 061/413] winegstreamer/new_media_source: Use wg_source order to build media source streams. And map them to wg_parser streams according to their format. CW-Bug-Id: #21953 --- dlls/winegstreamer/gst_private.h | 2 + dlls/winegstreamer/main.c | 19 ++++++++ dlls/winegstreamer/new_media_source.c | 64 ++++++++++++++++++++++++--- dlls/winegstreamer/unix_private.h | 1 + dlls/winegstreamer/unixlib.h | 8 ++++ dlls/winegstreamer/wg_parser.c | 2 + dlls/winegstreamer/wg_source.c | 18 ++++++++ 7 files changed, 108 insertions(+), 6 deletions(-) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 4af76cc4371..b1c482d0250 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -111,6 +111,8 @@ HRESULT wg_source_get_stream_count(wg_source_t source, uint32_t *stream_count); HRESULT wg_source_get_duration(wg_source_t source, uint64_t *duration); HRESULT wg_source_get_position(wg_source_t source, uint64_t *read_offset); HRESULT wg_source_push_data(wg_source_t source, const void *data, uint32_t size); +bool wg_source_get_stream_format(wg_source_t source, UINT32 index, + struct wg_format *format); wg_transform_t wg_transform_create(const struct wg_format *input_format, const struct wg_format *output_format, const struct wg_transform_attrs *attrs); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index e15dc0e543b..51ce82f47fe 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -461,6 +461,25 @@ HRESULT wg_source_push_data(wg_source_t source, const void *data, uint32_t size) return HRESULT_FROM_NT(WINE_UNIX_CALL(unix_wg_source_push_data, ¶ms)); } +bool wg_source_get_stream_format(wg_source_t source, UINT32 index, + struct wg_format *format) +{ + struct wg_source_get_stream_format_params params = + { + .source = source, + .index = index, + }; + + TRACE("source %#I64x, index %u, format %p\n", source, + index, format); + + if (WINE_UNIX_CALL(unix_wg_source_get_stream_format, ¶ms)) + return false; + + *format = params.format; + return true; +} + wg_transform_t wg_transform_create(const struct wg_format *input_format, const struct wg_format *output_format, const struct wg_transform_attrs *attrs) { diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c index c279cfdd2f5..11965d58d24 100644 --- a/dlls/winegstreamer/new_media_source.c +++ b/dlls/winegstreamer/new_media_source.c @@ -1721,6 +1721,50 @@ static const IMFMediaSourceVtbl IMFMediaSource_vtbl = media_source_Shutdown, }; +static HRESULT map_stream_to_wg_parser_stream(struct media_source *source, UINT stream_count, + wg_parser_stream_t *parser_streams, wg_parser_stream_t parser_stream) +{ + struct wg_format parser_format, source_format; + HRESULT hr; + UINT i; + + wg_parser_stream_get_preferred_format(parser_stream, &parser_format); + + for (i = 0; i < stream_count; i++) + { + if (parser_streams[i]) + continue; + if (FAILED(hr = wg_source_get_stream_format(source->wg_source, i, &source_format))) + return hr; + if ((parser_format.major_type >= WG_MAJOR_TYPE_VIDEO) != (source_format.major_type >= WG_MAJOR_TYPE_VIDEO) + || (parser_format.major_type >= WG_MAJOR_TYPE_AUDIO) != (source_format.major_type >= WG_MAJOR_TYPE_AUDIO)) + continue; + + TRACE("Mapped stream %#I64x with descriptor %u\n", parser_stream, i); + parser_streams[i] = parser_stream; + return S_OK; + } + + return E_FAIL; +} + +static void media_source_init_parser_streams(struct media_source *source, UINT stream_count, + wg_parser_stream_t *parser_streams) +{ + wg_parser_stream_t parser_stream; + UINT i, parser_stream_count; + HRESULT hr; + + parser_stream_count = wg_parser_get_stream_count(source->wg_parser); + for (i = 0; i < parser_stream_count; i++) + { + if (!(parser_stream = wg_parser_get_stream(source->wg_parser, i))) + continue; + if (FAILED(hr = map_stream_to_wg_parser_stream(source, stream_count, parser_streams, parser_stream))) + WARN("Failed to map parser stream %u, hr %#lx\n", i, hr); + } +} + static void media_source_init_descriptors(struct media_source *source) { HRESULT hr = S_OK; @@ -1742,7 +1786,8 @@ static void media_source_init_descriptors(struct media_source *source) static HRESULT media_source_create(struct object_context *context, IMFMediaSource **out) { - UINT32 stream_count; + UINT32 stream_count = context->stream_count; + wg_parser_stream_t *parser_streams = NULL; struct media_source *object; wg_parser_t parser; unsigned int i; @@ -1789,26 +1834,30 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc if (FAILED(hr = wg_parser_connect(parser, object->file_size, NULL))) goto fail; - stream_count = wg_parser_get_stream_count(parser); - if (!(object->descriptors = calloc(stream_count, sizeof(*object->descriptors))) + || !(parser_streams = calloc(stream_count, sizeof(*parser_streams))) || !(object->streams = calloc(stream_count, sizeof(*object->streams)))) { hr = E_OUTOFMEMORY; goto fail; } + media_source_init_parser_streams(object, stream_count, parser_streams); + for (i = 0; i < stream_count; ++i) { - wg_parser_stream_t wg_stream = wg_parser_get_stream(object->wg_parser, i); + wg_parser_stream_t parser_stream; IMFStreamDescriptor *descriptor; struct media_stream *stream; struct wg_format format; - wg_parser_stream_get_preferred_format(wg_stream, &format); + if (!(parser_stream = parser_streams[i])) + continue; + wg_parser_stream_get_preferred_format(parser_stream, &format); + if (FAILED(hr = stream_descriptor_create(i, &format, &descriptor))) goto fail; - if (FAILED(hr = media_stream_create(&object->IMFMediaSource_iface, descriptor, wg_stream, &stream))) + if (FAILED(hr = media_stream_create(&object->IMFMediaSource_iface, descriptor, parser_stream, &stream))) { IMFStreamDescriptor_Release(descriptor); goto fail; @@ -1820,6 +1869,8 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc object->stream_count++; } + free(parser_streams); + media_source_init_descriptors(object); object->state = SOURCE_STOPPED; @@ -1836,6 +1887,7 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc IMFStreamDescriptor_Release(object->descriptors[object->stream_count]); IMFMediaStream_Release(&stream->IMFMediaStream_iface); } + free(parser_streams); free(object->descriptors); free(object->streams); diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index df3bda1cad3..646d1a90f0f 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -65,6 +65,7 @@ extern NTSTATUS wg_source_get_stream_count(void *args); extern NTSTATUS wg_source_get_duration(void *args); extern NTSTATUS wg_source_get_position(void *args); extern NTSTATUS wg_source_push_data(void *args); +extern NTSTATUS wg_source_get_stream_format(void *args); /* wg_transform.c */ diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 9db7c76a2af..28ffe20f44b 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -383,6 +383,13 @@ struct wg_source_push_data_params UINT32 size; }; +struct wg_source_get_stream_format_params +{ + wg_source_t source; + UINT32 index; + struct wg_format format; +}; + struct wg_transform_attrs { UINT32 output_plane_align; @@ -499,6 +506,7 @@ enum unix_funcs unix_wg_source_get_duration, unix_wg_source_get_position, unix_wg_source_push_data, + unix_wg_source_get_stream_format, unix_wg_transform_create, unix_wg_transform_destroy, diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 826fa4168bb..fe263ac49c4 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -2212,6 +2212,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_source_get_duration), X(wg_source_get_position), X(wg_source_push_data), + X(wg_source_get_stream_format), X(wg_transform_create), X(wg_transform_destroy), @@ -2594,6 +2595,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = X(wg_source_get_duration), X(wg_source_get_position), X64(wg_source_push_data), + X(wg_source_get_stream_format), X64(wg_transform_create), X(wg_transform_destroy), diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 1ef7bb7dff9..f6a01413c42 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -628,3 +628,21 @@ NTSTATUS wg_source_push_data(void *args) return STATUS_SUCCESS; } + +NTSTATUS wg_source_get_stream_format(void *args) +{ + struct wg_source_get_stream_format_params *params = args; + struct wg_source *source = get_source(params->source); + guint index = params->index; + GstCaps *caps; + + GST_TRACE("source %p, index %u", source, index); + + if (!(caps = source_get_stream_caps(source, index))) + return STATUS_UNSUCCESSFUL; + wg_format_from_caps(¶ms->format, caps); + + gst_caps_unref(caps); + return STATUS_SUCCESS; +} + From b4b4997eb41d9e95aba7ef26326e2ed821f94ede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 19 Nov 2023 17:35:23 +0100 Subject: [PATCH 062/413] winegstreamer/new_media_source: Sort media source "video/mp4" streams by stream type. CW-Bug-Id: #21953 --- dlls/winegstreamer/new_media_source.c | 47 ++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c index 11965d58d24..4bd379d3bb6 100644 --- a/dlls/winegstreamer/new_media_source.c +++ b/dlls/winegstreamer/new_media_source.c @@ -311,6 +311,7 @@ struct media_source IMFStreamDescriptor **descriptors; struct media_stream **streams; ULONG stream_count; + UINT *stream_map; enum { @@ -1694,6 +1695,7 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) IMFMediaEventQueue_Shutdown(stream->event_queue); IMFMediaStream_Release(&stream->IMFMediaStream_iface); } + free(source->stream_map); free(source->descriptors); free(source->streams); @@ -1734,7 +1736,7 @@ static HRESULT map_stream_to_wg_parser_stream(struct media_source *source, UINT { if (parser_streams[i]) continue; - if (FAILED(hr = wg_source_get_stream_format(source->wg_source, i, &source_format))) + if (FAILED(hr = wg_source_get_stream_format(source->wg_source, source->stream_map[i], &source_format))) return hr; if ((parser_format.major_type >= WG_MAJOR_TYPE_VIDEO) != (source_format.major_type >= WG_MAJOR_TYPE_VIDEO) || (parser_format.major_type >= WG_MAJOR_TYPE_AUDIO) != (source_format.major_type >= WG_MAJOR_TYPE_AUDIO)) @@ -1765,6 +1767,46 @@ static void media_source_init_parser_streams(struct media_source *source, UINT s } } +static void media_source_init_stream_map(struct media_source *source, UINT stream_count) +{ + struct wg_format format; + int i, n = 0; + + if (wcscmp(source->mime_type, L"video/mp4")) + { + for (i = stream_count - 1; i >= 0; i--) + { + TRACE("mapping stream %u to wg_source stream %u\n", i, i); + source->stream_map[i] = i; + } + return; + } + + for (i = stream_count - 1; i >= 0; i--) + { + wg_source_get_stream_format(source->wg_source, i, &format); + if (format.major_type == WG_MAJOR_TYPE_UNKNOWN) continue; + if (format.major_type >= WG_MAJOR_TYPE_VIDEO) continue; + TRACE("mapping stream %u to wg_source stream %u\n", n, i); + source->stream_map[n++] = i; + } + for (i = stream_count - 1; i >= 0; i--) + { + wg_source_get_stream_format(source->wg_source, i, &format); + if (format.major_type == WG_MAJOR_TYPE_UNKNOWN) continue; + if (format.major_type < WG_MAJOR_TYPE_VIDEO) continue; + TRACE("mapping stream %u to wg_source stream %u\n", n, i); + source->stream_map[n++] = i; + } + for (i = stream_count - 1; i >= 0; i--) + { + wg_source_get_stream_format(source->wg_source, i, &format); + if (format.major_type != WG_MAJOR_TYPE_UNKNOWN) continue; + TRACE("mapping stream %u to wg_source stream %u\n", n, i); + source->stream_map[n++] = i; + } +} + static void media_source_init_descriptors(struct media_source *source) { HRESULT hr = S_OK; @@ -1836,12 +1878,14 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc if (!(object->descriptors = calloc(stream_count, sizeof(*object->descriptors))) || !(parser_streams = calloc(stream_count, sizeof(*parser_streams))) + || !(object->stream_map = calloc(stream_count, sizeof(*object->stream_map))) || !(object->streams = calloc(stream_count, sizeof(*object->streams)))) { hr = E_OUTOFMEMORY; goto fail; } + media_source_init_stream_map(object, stream_count); media_source_init_parser_streams(object, stream_count, parser_streams); for (i = 0; i < stream_count; ++i) @@ -1888,6 +1932,7 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc IMFMediaStream_Release(&stream->IMFMediaStream_iface); } free(parser_streams); + free(object->stream_map); free(object->descriptors); free(object->streams); From 617011a91f16a99b03a030e2451dc03b249d4412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 13 Mar 2023 12:24:48 +0100 Subject: [PATCH 063/413] winegstreamer/new_media_source: Initialize media source descriptor tags from wg_source. CW-Bug-Id: #21953 --- dlls/mfplay/tests/mfplay.c | 2 +- dlls/mfreadwrite/tests/mfplat.c | 8 +- dlls/winegstreamer/gst_private.h | 2 + dlls/winegstreamer/main.c | 29 ++++++ dlls/winegstreamer/new_media_source.c | 15 ++- dlls/winegstreamer/unix_private.h | 1 + dlls/winegstreamer/unixlib.h | 10 ++ dlls/winegstreamer/wg_parser.c | 24 +++++ dlls/winegstreamer/wg_source.c | 143 ++++++++++++++++++++++++++ 9 files changed, 221 insertions(+), 13 deletions(-) diff --git a/dlls/mfplay/tests/mfplay.c b/dlls/mfplay/tests/mfplay.c index fa66ccae57e..db23bb7de42 100644 --- a/dlls/mfplay/tests/mfplay.c +++ b/dlls/mfplay/tests/mfplay.c @@ -493,7 +493,7 @@ static void test_media_language(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); check_media_language(player, L"test-eng.mp4", L"en"); - check_media_language(player, L"test-ang.mp4", NULL); + todo_check_media_language(player, L"test-ang.mp4", NULL); check_media_language(player, L"test-und.mp4", NULL); check_media_language(player, L"test-en-US.mp4", L"en"); diff --git a/dlls/mfreadwrite/tests/mfplat.c b/dlls/mfreadwrite/tests/mfplat.c index 08e19589ad1..2095d2f054d 100644 --- a/dlls/mfreadwrite/tests/mfplat.c +++ b/dlls/mfreadwrite/tests/mfplat.c @@ -949,7 +949,7 @@ static void test_source_reader(const char *filename, bool video) hr = IMFSourceReader_ReadSample(reader, MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, &actual_index, &stream_flags, ×tamp, &sample); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine_if (video) ok(!actual_index, "Unexpected stream index %lu.\n", actual_index); + ok(!actual_index, "Unexpected stream index %lu.\n", actual_index); ok(!(stream_flags & ~MF_SOURCE_READERF_ENDOFSTREAM), "Unexpected stream flags %#lx.\n", stream_flags); if (stream_flags & MF_SOURCE_READERF_ENDOFSTREAM) @@ -970,7 +970,7 @@ static void test_source_reader(const char *filename, bool video) hr = IMFSourceReader_ReadSample(reader, MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, &actual_index, &stream_flags, ×tamp, &sample); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(actual_index == 1, "Unexpected stream index %lu.\n", actual_index); + ok(actual_index == 1, "Unexpected stream index %lu.\n", actual_index); ok(!(stream_flags & ~MF_SOURCE_READERF_ENDOFSTREAM), "Unexpected stream flags %#lx.\n", stream_flags); if (stream_flags & MF_SOURCE_READERF_ENDOFSTREAM) @@ -1001,7 +1001,7 @@ static void test_source_reader(const char *filename, bool video) hr = IMFSourceReader_ReadSample(reader, MF_SOURCE_READER_FIRST_AUDIO_STREAM, MF_SOURCE_READER_CONTROLF_DRAIN, &actual_index, &stream_flags, ×tamp, &sample); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine_if (video) ok(actual_index == 0, "Unexpected stream index %lu.\n", actual_index); + ok(actual_index == 0, "Unexpected stream index %lu.\n", actual_index); ok(stream_flags == MF_SOURCE_READERF_ENDOFSTREAM, "Unexpected stream flags %#lx.\n", stream_flags); ok(!sample, "Unexpected sample object.\n"); @@ -1022,7 +1022,7 @@ static void test_source_reader(const char *filename, bool video) hr = IMFSourceReader_ReadSample(reader, MF_SOURCE_READER_FIRST_AUDIO_STREAM, MF_SOURCE_READER_CONTROLF_DRAIN, &actual_index, &stream_flags, NULL, &sample); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine_if (video) ok(!actual_index, "Unexpected stream index %lu.\n", actual_index); + ok(!actual_index, "Unexpected stream index %lu.\n", actual_index); ok(stream_flags == MF_SOURCE_READERF_ENDOFSTREAM, "Unexpected stream flags %#lx.\n", stream_flags); ok(!sample, "Unexpected sample object.\n"); diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index b1c482d0250..df5f64660a2 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -113,6 +113,8 @@ HRESULT wg_source_get_position(wg_source_t source, uint64_t *read_offset); HRESULT wg_source_push_data(wg_source_t source, const void *data, uint32_t size); bool wg_source_get_stream_format(wg_source_t source, UINT32 index, struct wg_format *format); +char *wg_source_get_stream_tag(wg_source_t source, UINT32 index, + wg_parser_tag tag); wg_transform_t wg_transform_create(const struct wg_format *input_format, const struct wg_format *output_format, const struct wg_transform_attrs *attrs); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 51ce82f47fe..3d2333da17f 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -480,6 +480,35 @@ bool wg_source_get_stream_format(wg_source_t source, UINT32 index, return true; } +char *wg_source_get_stream_tag(wg_source_t source, UINT32 index, wg_parser_tag tag) +{ + struct wg_source_get_stream_tag_params params = + { + .source = source, + .index = index, + .tag = tag, + }; + char *buffer; + + TRACE("source %#I64x, index %u, tag %#I64x\n", source, index, tag); + + if (WINE_UNIX_CALL(unix_wg_source_get_stream_tag, ¶ms) != STATUS_BUFFER_TOO_SMALL) + return NULL; + if (!(buffer = malloc(params.size))) + { + ERR("No memory.\n"); + return NULL; + } + params.buffer = buffer; + if (WINE_UNIX_CALL(unix_wg_source_get_stream_tag, ¶ms)) + { + ERR("wg_source_get_stream_tag failed unexpectedly.\n"); + free(buffer); + return NULL; + } + return buffer; +} + wg_transform_t wg_transform_create(const struct wg_format *input_format, const struct wg_format *output_format, const struct wg_transform_attrs *attrs) { diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c index 4bd379d3bb6..9dbdf069caf 100644 --- a/dlls/winegstreamer/new_media_source.c +++ b/dlls/winegstreamer/new_media_source.c @@ -488,15 +488,15 @@ static HRESULT wg_format_from_stream_descriptor(IMFStreamDescriptor *descriptor, return hr; } -static HRESULT stream_descriptor_set_tag(IMFStreamDescriptor *descriptor, wg_parser_stream_t stream, - const GUID *attr, enum wg_parser_tag tag) +static HRESULT stream_descriptor_set_tag(IMFStreamDescriptor *descriptor, + wg_source_t source, UINT index, const GUID *attr, enum wg_parser_tag tag) { WCHAR *strW; HRESULT hr; DWORD len; char *str; - if (!(str = wg_parser_stream_get_tag(stream, tag)) + if (!(str = wg_source_get_stream_tag(source, index, tag)) || !(len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0))) hr = S_OK; else if (!(strW = malloc(len * sizeof(*strW)))) @@ -1809,18 +1809,17 @@ static void media_source_init_stream_map(struct media_source *source, UINT strea static void media_source_init_descriptors(struct media_source *source) { - HRESULT hr = S_OK; + HRESULT hr; UINT i; for (i = 0; i < source->stream_count; i++) { - struct media_stream *stream = source->streams[i]; - IMFStreamDescriptor *descriptor = stream->descriptor; + IMFStreamDescriptor *descriptor = source->descriptors[i]; - if (FAILED(hr = stream_descriptor_set_tag(descriptor, stream->wg_stream, + if (FAILED(hr = stream_descriptor_set_tag(descriptor, source->wg_source, source->stream_map[i], &MF_SD_LANGUAGE, WG_PARSER_TAG_LANGUAGE))) WARN("Failed to set stream descriptor language, hr %#lx\n", hr); - if (FAILED(hr = stream_descriptor_set_tag(descriptor, stream->wg_stream, + if (FAILED(hr = stream_descriptor_set_tag(descriptor, source->wg_source, source->stream_map[i], &MF_SD_STREAM_NAME, WG_PARSER_TAG_NAME))) WARN("Failed to set stream descriptor name, hr %#lx\n", hr); } diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 646d1a90f0f..7116e37d57f 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -66,6 +66,7 @@ extern NTSTATUS wg_source_get_duration(void *args); extern NTSTATUS wg_source_get_position(void *args); extern NTSTATUS wg_source_push_data(void *args); extern NTSTATUS wg_source_get_stream_format(void *args); +extern NTSTATUS wg_source_get_stream_tag(void *args); /* wg_transform.c */ diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 28ffe20f44b..d37070247fc 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -390,6 +390,15 @@ struct wg_source_get_stream_format_params struct wg_format format; }; +struct wg_source_get_stream_tag_params +{ + wg_source_t source; + UINT32 index; + wg_parser_tag tag; + UINT32 size; + char *buffer; +}; + struct wg_transform_attrs { UINT32 output_plane_align; @@ -507,6 +516,7 @@ enum unix_funcs unix_wg_source_get_position, unix_wg_source_push_data, unix_wg_source_get_stream_format, + unix_wg_source_get_stream_tag, unix_wg_transform_create, unix_wg_transform_destroy, diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index fe263ac49c4..114c4d38224 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -2213,6 +2213,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_source_get_position), X(wg_source_push_data), X(wg_source_get_stream_format), + X(wg_source_get_stream_tag), X(wg_transform_create), X(wg_transform_destroy), @@ -2403,6 +2404,28 @@ NTSTATUS wow64_wg_source_push_data(void *args) return wg_source_push_data(¶ms); } +NTSTATUS wow64_wg_source_get_stream_tag(void *args) +{ + struct + { + wg_source_t source; + UINT32 index; + wg_parser_tag tag; + UINT32 size; + PTR32 buffer; + } *params32 = args; + struct wg_source_get_stream_tag_params params = + { + .source = params32->source, + .index = params32->index, + .tag = params32->tag, + .size = params32->size, + .buffer = ULongToPtr(params32->buffer), + }; + + return wg_source_get_stream_tag(¶ms); +} + NTSTATUS wow64_wg_transform_create(void *args) { struct @@ -2596,6 +2619,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = X(wg_source_get_position), X64(wg_source_push_data), X(wg_source_get_stream_format), + X64(wg_source_get_stream_tag), X64(wg_transform_create), X(wg_transform_destroy), diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index f6a01413c42..8201c12eb03 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -64,6 +64,14 @@ static struct wg_source *get_source(wg_source_t source) return (struct wg_source *)(ULONG_PTR)source; } +static const char *media_type_from_caps(GstCaps *caps) +{ + GstStructure *structure; + if (!caps || !(structure = gst_caps_get_structure(caps, 0))) + return ""; + return gst_structure_get_name(structure); +} + static GstCaps *detect_caps_from_data(const char *url, const void *data, guint size) { const char *extension = url ? strrchr(url, '.') : NULL; @@ -136,6 +144,17 @@ static GstCaps *source_get_stream_caps(struct wg_source *source, guint index) return caps; } +static GstTagList *source_get_stream_tags(struct wg_source *source, guint index) +{ + GstStream *stream; + GstTagList *tags; + if (!(stream = source_get_stream(source, index))) + return NULL; + tags = gst_stream_get_tags(stream); + gst_object_unref(stream); + return tags; +} + static gboolean src_event_seek(struct wg_source *source, GstEvent *event) { guint32 seqnum = gst_event_get_seqnum(event); @@ -646,3 +665,127 @@ NTSTATUS wg_source_get_stream_format(void *args) return STATUS_SUCCESS; } +static gchar *stream_lang_from_tags(GstTagList *tags, bool is_quicktime) +{ + gchar *value; + + if (!gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &value) || !value) + return NULL; + + if (is_quicktime) + { + /* For QuickTime media, we convert the language tags to ISO 639-1. */ + const gchar *lang_code_iso_639_1 = gst_tag_get_language_code_iso_639_1(value); + gchar *tmp = lang_code_iso_639_1 ? g_strdup(lang_code_iso_639_1) : NULL; + g_free(value); + value = tmp; + } + + return value; +} + +static gchar *stream_name_from_tags(GstTagList *tags) +{ + /* Extract stream name from Quick Time demuxer private tag where it puts unrecognized chunks. */ + guint i, tag_count = gst_tag_list_get_tag_size(tags, "private-qt-tag"); + gchar *value = NULL; + + for (i = 0; !value && i < tag_count; ++i) + { + const gchar *name; + const GValue *val; + GstSample *sample; + GstBuffer *buf; + gsize size; + + if (!(val = gst_tag_list_get_value_index(tags, "private-qt-tag", i))) + continue; + if (!GST_VALUE_HOLDS_SAMPLE(val) || !(sample = gst_value_get_sample(val))) + continue; + name = gst_structure_get_name(gst_sample_get_info(sample)); + if (!name || strcmp(name, "application/x-gst-qt-name-tag")) + continue; + if (!(buf = gst_sample_get_buffer(sample))) + continue; + if ((size = gst_buffer_get_size(buf)) < 8) + continue; + size -= 8; + if (!(value = g_malloc(size + 1))) + return NULL; + if (gst_buffer_extract(buf, 8, value, size) != size) + { + g_free(value); + value = NULL; + continue; + } + value[size] = 0; + } + + return value; +} + +NTSTATUS wg_source_get_stream_tag(void *args) +{ + struct wg_source_get_stream_tag_params *params = args; + struct wg_source *source = get_source(params->source); + enum wg_parser_tag tag = params->tag; + guint index = params->index; + GstTagList *tags; + NTSTATUS status; + uint32_t len; + gchar *value; + + GST_TRACE("source %p, index %u, tag %u", source, index, tag); + + if (params->tag >= WG_PARSER_TAG_COUNT) + return STATUS_INVALID_PARAMETER; + if (!(tags = source_get_stream_tags(source, index))) + return STATUS_UNSUCCESSFUL; + + switch (tag) + { + case WG_PARSER_TAG_LANGUAGE: + { + bool is_quicktime = false; + GstCaps *caps; + + if ((caps = gst_pad_get_current_caps(source->src_pad))) + { + is_quicktime = !strcmp(media_type_from_caps(caps), "video/quicktime"); + gst_caps_unref(caps); + } + + value = stream_lang_from_tags(tags, is_quicktime); + break; + } + case WG_PARSER_TAG_NAME: + value = stream_name_from_tags(tags); + break; + default: + GST_FIXME("Unsupported stream tag %u", tag); + value = NULL; + break; + } + + if (!value) + goto error; + + if ((len = strlen(value) + 1) > params->size) + { + params->size = len; + status = STATUS_BUFFER_TOO_SMALL; + } + else + { + memcpy(params->buffer, value, len); + status = STATUS_SUCCESS; + } + + gst_tag_list_unref(tags); + g_free(value); + return status; + +error: + gst_tag_list_unref(tags); + return STATUS_NOT_FOUND; +} From 2655398a68cd37b51909eca05c24c7ea22662e51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 28 Apr 2023 19:13:44 +0200 Subject: [PATCH 064/413] winegstreamer/new_media_source: Set MF_SD_MUTUALLY_EXCLUSIVE stream descriptor attribute. CW-Bug-Id: #21953 --- dlls/winegstreamer/new_media_source.c | 39 ++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c index 9dbdf069caf..e47b71a2e66 100644 --- a/dlls/winegstreamer/new_media_source.c +++ b/dlls/winegstreamer/new_media_source.c @@ -475,6 +475,19 @@ static HRESULT stream_descriptor_get_media_type(IMFStreamDescriptor *descriptor, return hr; } +static HRESULT stream_descriptor_get_major_type(IMFStreamDescriptor *descriptor, GUID *major) +{ + IMFMediaTypeHandler *handler; + HRESULT hr; + + if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(descriptor, &handler))) + return hr; + hr = IMFMediaTypeHandler_GetMajorType(handler, major); + IMFMediaTypeHandler_Release(handler); + + return hr; +} + static HRESULT wg_format_from_stream_descriptor(IMFStreamDescriptor *descriptor, struct wg_format *format) { IMFMediaType *media_type; @@ -1809,12 +1822,36 @@ static void media_source_init_stream_map(struct media_source *source, UINT strea static void media_source_init_descriptors(struct media_source *source) { + UINT i, last_audio = -1, last_video = -1; HRESULT hr; - UINT i; for (i = 0; i < source->stream_count; i++) { IMFStreamDescriptor *descriptor = source->descriptors[i]; + GUID major = GUID_NULL; + UINT exclude = -1; + + if (FAILED(hr = stream_descriptor_get_major_type(descriptor, &major))) + WARN("Failed to get major type from stream descriptor, hr %#lx\n", hr); + + if (IsEqualGUID(&major, &MFMediaType_Audio)) + { + exclude = last_audio; + last_audio = i; + } + else if (IsEqualGUID(&major, &MFMediaType_Video)) + { + exclude = last_video; + last_video = i; + } + + if (exclude != -1) + { + if (FAILED(IMFStreamDescriptor_SetUINT32(source->descriptors[exclude], &MF_SD_MUTUALLY_EXCLUSIVE, 1))) + WARN("Failed to set stream %u MF_SD_MUTUALLY_EXCLUSIVE\n", exclude); + else if (FAILED(IMFStreamDescriptor_SetUINT32(descriptor, &MF_SD_MUTUALLY_EXCLUSIVE, 1))) + WARN("Failed to set stream %u MF_SD_MUTUALLY_EXCLUSIVE\n", i); + } if (FAILED(hr = stream_descriptor_set_tag(descriptor, source->wg_source, source->stream_map[i], &MF_SD_LANGUAGE, WG_PARSER_TAG_LANGUAGE))) From e6d206ae1546c98d6d4fdaf4b1f6a0cc84448064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 29 Apr 2023 14:51:54 +0200 Subject: [PATCH 065/413] winegstreamer/new_media_source: Select one stream descriptor for each major type. CW-Bug-Id: #21953 --- dlls/winegstreamer/new_media_source.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c index e47b71a2e66..dcc1effe0c1 100644 --- a/dlls/winegstreamer/new_media_source.c +++ b/dlls/winegstreamer/new_media_source.c @@ -1258,9 +1258,6 @@ static HRESULT media_stream_create(IMFMediaSource *source, IMFStreamDescriptor * object->media_source = source; IMFStreamDescriptor_AddRef(descriptor); object->descriptor = descriptor; - - object->active = TRUE; - object->eos = FALSE; object->wg_stream = wg_stream; TRACE("Created stream object %p.\n", object); @@ -1584,6 +1581,8 @@ static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource * for (i = 0; i < source->stream_count; ++i) { + if (!source->streams[i]->active) + continue; if (FAILED(hr = IMFPresentationDescriptor_SelectStream(*descriptor, i))) WARN("Failed to select stream %u, hr %#lx\n", i, hr); } @@ -1822,7 +1821,7 @@ static void media_source_init_stream_map(struct media_source *source, UINT strea static void media_source_init_descriptors(struct media_source *source) { - UINT i, last_audio = -1, last_video = -1; + UINT i, last_audio = -1, last_video = -1, first_audio = -1, first_video = -1; HRESULT hr; for (i = 0; i < source->stream_count; i++) @@ -1836,11 +1835,15 @@ static void media_source_init_descriptors(struct media_source *source) if (IsEqualGUID(&major, &MFMediaType_Audio)) { + if (first_audio == -1) + first_audio = i; exclude = last_audio; last_audio = i; } else if (IsEqualGUID(&major, &MFMediaType_Video)) { + if (first_video == -1) + first_video = i; exclude = last_video; last_video = i; } @@ -1860,6 +1863,21 @@ static void media_source_init_descriptors(struct media_source *source) &MF_SD_STREAM_NAME, WG_PARSER_TAG_NAME))) WARN("Failed to set stream descriptor name, hr %#lx\n", hr); } + + if (!wcscmp(source->mime_type, L"video/mp4")) + { + if (last_audio != -1) + source->streams[last_audio]->active = TRUE; + if (last_video != -1) + source->streams[last_video]->active = TRUE; + } + else + { + if (first_audio != -1) + source->streams[first_audio]->active = TRUE; + if (first_video != -1) + source->streams[first_video]->active = TRUE; + } } static HRESULT media_source_create(struct object_context *context, IMFMediaSource **out) From 38c4129f6f8091935b7fbee091bbb55b733a947a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 29 Jun 2023 12:17:55 +0200 Subject: [PATCH 066/413] winegstreamer/new_media_source: Use 1-based ids in media source stream descriptors. CW-Bug-Id: #21953 --- dlls/winegstreamer/new_media_source.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c index dcc1effe0c1..14b2679f529 100644 --- a/dlls/winegstreamer/new_media_source.c +++ b/dlls/winegstreamer/new_media_source.c @@ -758,10 +758,10 @@ static HRESULT media_source_start(struct media_source *source, IMFPresentationDe { if (FAILED(hr = IMFStreamDescriptor_GetStreamIdentifier(stream_descriptor, &id))) WARN("Failed to get stream descriptor id, hr %#lx\n", hr); - else if (id >= source->stream_count) + else if (id > source->stream_count) WARN("Invalid stream descriptor id %lu, hr %#lx\n", id, hr); else if (selected) - IMFStreamDescriptor_AddRef((descriptors[id] = stream_descriptor)); + IMFStreamDescriptor_AddRef((descriptors[id - 1] = stream_descriptor)); IMFStreamDescriptor_Release(stream_descriptor); } @@ -1953,7 +1953,7 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc continue; wg_parser_stream_get_preferred_format(parser_stream, &format); - if (FAILED(hr = stream_descriptor_create(i, &format, &descriptor))) + if (FAILED(hr = stream_descriptor_create(i + 1, &format, &descriptor))) goto fail; if (FAILED(hr = media_stream_create(&object->IMFMediaSource_iface, descriptor, parser_stream, &stream))) { From 1f7dec879d6f53cd3ef10d843e3a90ed0edad47d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 24 Jul 2023 14:25:38 +0200 Subject: [PATCH 067/413] winegstreamer/new_media_source: Select the wg_source streams on media source start. CW-Bug-Id: #21953 --- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/main.c | 14 ++++++++++++++ dlls/winegstreamer/new_media_source.c | 2 ++ dlls/winegstreamer/unix_private.h | 1 + dlls/winegstreamer/unixlib.h | 8 ++++++++ dlls/winegstreamer/wg_parser.c | 1 + dlls/winegstreamer/wg_source.c | 28 +++++++++++++++++++++++++++ 7 files changed, 55 insertions(+) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index df5f64660a2..fc3b617772f 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -115,6 +115,7 @@ bool wg_source_get_stream_format(wg_source_t source, UINT32 index, struct wg_format *format); char *wg_source_get_stream_tag(wg_source_t source, UINT32 index, wg_parser_tag tag); +void wg_source_set_stream_flags(wg_source_t source, UINT32 index, BOOL select); wg_transform_t wg_transform_create(const struct wg_format *input_format, const struct wg_format *output_format, const struct wg_transform_attrs *attrs); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 3d2333da17f..848988f93a6 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -509,6 +509,20 @@ char *wg_source_get_stream_tag(wg_source_t source, UINT32 index, wg_parser_tag t return buffer; } +void wg_source_set_stream_flags(wg_source_t source, UINT32 index, BOOL select) +{ + struct wg_source_set_stream_flags_params params = + { + .source = source, + .index = index, + .select = select, + }; + + TRACE("source %#I64x, index %u, select %u\n", source, index, select); + + WINE_UNIX_CALL(unix_wg_source_set_stream_flags, ¶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) { diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c index 14b2679f529..8c5c30e653e 100644 --- a/dlls/winegstreamer/new_media_source.c +++ b/dlls/winegstreamer/new_media_source.c @@ -783,6 +783,8 @@ static HRESULT media_source_start(struct media_source *source, IMFPresentationDe WARN("Failed to start media stream, hr %#lx\n", hr); IMFStreamDescriptor_Release(descriptors[i]); } + + wg_source_set_stream_flags(source->wg_source, source->stream_map[i], stream->active); } free(descriptors); diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 7116e37d57f..96935676c34 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -67,6 +67,7 @@ extern NTSTATUS wg_source_get_position(void *args); extern NTSTATUS wg_source_push_data(void *args); extern NTSTATUS wg_source_get_stream_format(void *args); extern NTSTATUS wg_source_get_stream_tag(void *args); +extern NTSTATUS wg_source_set_stream_flags(void *args); /* wg_transform.c */ diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index d37070247fc..684577bab56 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -399,6 +399,13 @@ struct wg_source_get_stream_tag_params char *buffer; }; +struct wg_source_set_stream_flags_params +{ + wg_source_t source; + UINT32 index; + UINT32 select; +}; + struct wg_transform_attrs { UINT32 output_plane_align; @@ -517,6 +524,7 @@ enum unix_funcs unix_wg_source_push_data, unix_wg_source_get_stream_format, unix_wg_source_get_stream_tag, + unix_wg_source_set_stream_flags, unix_wg_transform_create, unix_wg_transform_destroy, diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 114c4d38224..fd8c5731575 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -2214,6 +2214,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_source_push_data), X(wg_source_get_stream_format), X(wg_source_get_stream_tag), + X(wg_source_set_stream_flags), X(wg_transform_create), X(wg_transform_destroy), diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 8201c12eb03..48482303b4b 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -155,6 +155,16 @@ static GstTagList *source_get_stream_tags(struct wg_source *source, guint index) return tags; } +static bool source_set_stream_flags(struct wg_source *source, guint index, GstStreamFlags flags) +{ + GstStream *stream; + if (!(stream = source_get_stream(source, index))) + return false; + gst_stream_set_stream_flags(stream, flags); + gst_object_unref(stream); + return true; +} + static gboolean src_event_seek(struct wg_source *source, GstEvent *event) { guint32 seqnum = gst_event_get_seqnum(event); @@ -384,6 +394,7 @@ static GstEvent *create_stream_start_event(const char *stream_id) if (!(stream = gst_stream_new(stream_id, NULL, GST_STREAM_TYPE_UNKNOWN, 0))) return NULL; + gst_stream_set_stream_flags(stream, GST_STREAM_FLAG_SELECT); if ((event = gst_event_new_stream_start(stream_id))) { gst_event_set_stream(event, stream); @@ -789,3 +800,20 @@ NTSTATUS wg_source_get_stream_tag(void *args) gst_tag_list_unref(tags); return STATUS_NOT_FOUND; } + +NTSTATUS wg_source_set_stream_flags(void *args) +{ + struct wg_source_set_stream_flags_params *params = args; + struct wg_source *source = get_source(params->source); + BOOL select = params->select; + guint index = params->index; + GstStreamFlags flags; + + GST_TRACE("source %p, index %u, select %u", source, index, select); + + flags = select ? GST_STREAM_FLAG_SELECT : GST_STREAM_FLAG_UNSELECT; + if (!source_set_stream_flags(source, index, flags)) + return STATUS_UNSUCCESSFUL; + + return STATUS_SUCCESS; +} From 159b2f920dfdf6ef0ff610190995fd81537f24de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 8 Dec 2023 17:12:56 +0100 Subject: [PATCH 068/413] winegstreamer/new_media_source: Seek the wg_source streams on media source start. CW-Bug-Id: #21953 --- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/main.c | 9 +++++++++ dlls/winegstreamer/new_media_source.c | 2 ++ dlls/winegstreamer/unix_private.h | 1 + dlls/winegstreamer/unixlib.h | 7 +++++++ dlls/winegstreamer/wg_parser.c | 1 + dlls/winegstreamer/wg_source.c | 17 +++++++++++++++++ 7 files changed, 38 insertions(+) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index fc3b617772f..e7393e015e7 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -109,6 +109,7 @@ HRESULT wg_source_create(const WCHAR *url, uint64_t file_size, void wg_source_destroy(wg_source_t source); HRESULT wg_source_get_stream_count(wg_source_t source, uint32_t *stream_count); HRESULT wg_source_get_duration(wg_source_t source, uint64_t *duration); +HRESULT wg_source_set_position(wg_source_t source, uint64_t time); HRESULT wg_source_get_position(wg_source_t source, uint64_t *read_offset); HRESULT wg_source_push_data(wg_source_t source, const void *data, uint32_t size); bool wg_source_get_stream_format(wg_source_t source, UINT32 index, diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 848988f93a6..992f6b3ae98 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -447,6 +447,15 @@ HRESULT wg_source_get_position(wg_source_t source, uint64_t *read_offset) return S_OK; } +HRESULT wg_source_set_position(wg_source_t source, uint64_t time) +{ + struct wg_source_set_position_params params = {.source = source, .time = time}; + + TRACE("source %#I64x, time %s\n", source, debugstr_time(time)); + + return HRESULT_FROM_NT(WINE_UNIX_CALL(unix_wg_source_set_position, ¶ms)); +} + HRESULT wg_source_push_data(wg_source_t source, const void *data, uint32_t size) { struct wg_source_push_data_params params = diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c index 8c5c30e653e..ffea4e8468f 100644 --- a/dlls/winegstreamer/new_media_source.c +++ b/dlls/winegstreamer/new_media_source.c @@ -790,6 +790,8 @@ static HRESULT media_source_start(struct media_source *source, IMFPresentationDe free(descriptors); source->state = SOURCE_RUNNING; + if (position->vt == VT_I8) + wg_source_set_position(source->wg_source, position->hVal.QuadPart); if (position->vt == VT_I8) wg_parser_stream_seek(source->streams[0]->wg_stream, 1.0, position->hVal.QuadPart, 0, diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 96935676c34..f83279b9bfa 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -64,6 +64,7 @@ extern NTSTATUS wg_source_destroy(void *args); extern NTSTATUS wg_source_get_stream_count(void *args); extern NTSTATUS wg_source_get_duration(void *args); extern NTSTATUS wg_source_get_position(void *args); +extern NTSTATUS wg_source_set_position(void *args); extern NTSTATUS wg_source_push_data(void *args); extern NTSTATUS wg_source_get_stream_format(void *args); extern NTSTATUS wg_source_get_stream_tag(void *args); diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 684577bab56..2674407e799 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -376,6 +376,12 @@ struct wg_source_get_position_params UINT64 read_offset; }; +struct wg_source_set_position_params +{ + wg_source_t source; + UINT64 time; +}; + struct wg_source_push_data_params { wg_source_t source; @@ -521,6 +527,7 @@ enum unix_funcs unix_wg_source_get_stream_count, unix_wg_source_get_duration, unix_wg_source_get_position, + unix_wg_source_set_position, unix_wg_source_push_data, unix_wg_source_get_stream_format, unix_wg_source_get_stream_tag, diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index fd8c5731575..861f202995d 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -2211,6 +2211,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_source_get_stream_count), X(wg_source_get_duration), X(wg_source_get_position), + X(wg_source_set_position), X(wg_source_push_data), X(wg_source_get_stream_format), X(wg_source_get_stream_tag), diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 48482303b4b..1c6d387635e 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -608,6 +608,23 @@ NTSTATUS wg_source_get_position(void *args) return STATUS_SUCCESS; } +NTSTATUS wg_source_set_position(void *args) +{ + struct wg_source_set_position_params *params = args; + struct wg_source *source = get_source(params->source); + guint64 time = params->time * 100; + GstEvent *event; + + GST_TRACE("source %p", source); + + if (!(event = gst_event_new_seek(1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, time, GST_SEEK_TYPE_NONE, -1)) + || !gst_pad_push_event(source->streams[0].pad, event)) + GST_WARNING("Failed to seek source %p to %" G_GINT64_MODIFIER "x", source, time); + + return S_OK; +} + NTSTATUS wg_source_push_data(void *args) { struct wg_source_push_data_params *params = args; From ed20bc48620c25abbcb5fdb7bf0727158e5203b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 8 Dec 2023 17:19:22 +0100 Subject: [PATCH 069/413] winegstreamer/new_media_source: Read and discard compressed samples from wg_source. CW-Bug-Id: #21953 --- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/mfplat.c | 3 + dlls/winegstreamer/new_media_source.c | 30 +++++ dlls/winegstreamer/unix_private.h | 1 + dlls/winegstreamer/unixlib.h | 8 ++ dlls/winegstreamer/wg_parser.c | 20 ++++ dlls/winegstreamer/wg_sample.c | 69 +++++++++++ dlls/winegstreamer/wg_source.c | 164 +++++++++++++++++++++++++- 8 files changed, 292 insertions(+), 4 deletions(-) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index e7393e015e7..84857c5adfa 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -112,6 +112,7 @@ HRESULT wg_source_get_duration(wg_source_t source, uint64_t *duration); HRESULT wg_source_set_position(wg_source_t source, uint64_t time); HRESULT wg_source_get_position(wg_source_t source, uint64_t *read_offset); HRESULT wg_source_push_data(wg_source_t source, const void *data, uint32_t size); +HRESULT wg_source_read_data(wg_source_t source, UINT32 index, IMFSample **out); bool wg_source_get_stream_format(wg_source_t source, UINT32 index, struct wg_format *format); char *wg_source_get_stream_tag(wg_source_t source, UINT32 index, diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 2537c6bf455..452289c2e6a 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -17,6 +17,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include "ntstatus.h" +#define WIN32_NO_STATUS #include "gst_private.h" #include "ks.h" @@ -26,6 +28,7 @@ #include "d3d9types.h" #include "mfapi.h" #include "mmreg.h" +#include "mferror.h" #include "wine/debug.h" #include "wine/list.h" diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c index ffea4e8468f..b4c8e91db49 100644 --- a/dlls/winegstreamer/new_media_source.c +++ b/dlls/winegstreamer/new_media_source.c @@ -307,6 +307,7 @@ struct media_source UINT64 file_size; wg_parser_t wg_parser; UINT64 duration; + BYTE *read_buffer; IMFStreamDescriptor **descriptors; struct media_stream **streams; @@ -926,9 +927,34 @@ static HRESULT wait_on_sample(struct media_stream *stream, IUnknown *token) { struct media_source *source = impl_from_IMFMediaSource(stream->media_source); struct wg_parser_buffer buffer; + DWORD id, read_size; + UINT64 read_offset; + IMFSample *sample; + HRESULT hr; TRACE("%p, %p\n", stream, token); + if (FAILED(hr = IMFStreamDescriptor_GetStreamIdentifier(stream->descriptor, &id))) + return hr; + id = source->stream_map[id - 1]; + + while (SUCCEEDED(hr) && (hr = wg_source_read_data(source->wg_source, id, &sample)) == E_PENDING) + { + if (FAILED(hr = wg_source_get_position(source->wg_source, &read_offset))) + break; + if (FAILED(hr = IMFByteStream_SetCurrentPosition(source->byte_stream, read_offset))) + WARN("Failed to seek stream to %#I64x, hr %#lx\n", read_offset, hr); + else if (FAILED(hr = IMFByteStream_Read(source->byte_stream, source->read_buffer, SOURCE_BUFFER_SIZE, &read_size))) + WARN("Failed to read %#lx bytes from stream, hr %#lx\n", read_size, hr); + else if (FAILED(hr = wg_source_push_data(source->wg_source, source->read_buffer, read_size))) + WARN("Failed to push %#lx bytes to source, hr %#lx\n", read_size, hr); + } + + if (FAILED(hr)) + WARN("Failed to read stream %lu data, hr %#lx\n", id, hr); + else + IMFSample_Release(sample); + if (wg_parser_stream_get_buffer(source->wg_parser, stream->wg_stream, &buffer)) return media_stream_send_sample(stream, &buffer, token); @@ -1497,6 +1523,7 @@ static ULONG WINAPI media_source_Release(IMFMediaSource *iface) IMFMediaEventQueue_Release(source->event_queue); IMFByteStream_Release(source->byte_stream); wg_source_destroy(source->wg_source); + free(source->read_buffer); wg_parser_destroy(source->wg_parser); source->cs.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&source->cs); @@ -1910,6 +1937,8 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc InitializeCriticalSection(&object->cs); object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs"); + object->read_buffer = context->buffer; + context->buffer = NULL; object->wg_source = context->wg_source; context->wg_source = 0; @@ -2011,6 +2040,7 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc if (object->event_queue) IMFMediaEventQueue_Release(object->event_queue); IMFByteStream_Release(object->byte_stream); + free(object->read_buffer); free(object); return hr; diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index f83279b9bfa..d15054661c7 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -66,6 +66,7 @@ extern NTSTATUS wg_source_get_duration(void *args); extern NTSTATUS wg_source_get_position(void *args); extern NTSTATUS wg_source_set_position(void *args); extern NTSTATUS wg_source_push_data(void *args); +extern NTSTATUS wg_source_read_data(void *args); extern NTSTATUS wg_source_get_stream_format(void *args); extern NTSTATUS wg_source_get_stream_tag(void *args); extern NTSTATUS wg_source_set_stream_flags(void *args); diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 2674407e799..ba227ca2c61 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -389,6 +389,13 @@ struct wg_source_push_data_params UINT32 size; }; +struct wg_source_read_data_params +{ + wg_source_t source; + UINT32 index; + struct wg_sample *sample; +}; + struct wg_source_get_stream_format_params { wg_source_t source; @@ -529,6 +536,7 @@ enum unix_funcs unix_wg_source_get_position, unix_wg_source_set_position, unix_wg_source_push_data, + unix_wg_source_read_data, unix_wg_source_get_stream_format, unix_wg_source_get_stream_tag, unix_wg_source_set_stream_flags, diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 861f202995d..d6de243f3b2 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -2213,6 +2213,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_source_get_position), X(wg_source_set_position), X(wg_source_push_data), + X(wg_source_read_data), X(wg_source_get_stream_format), X(wg_source_get_stream_tag), X(wg_source_set_stream_flags), @@ -2406,6 +2407,24 @@ NTSTATUS wow64_wg_source_push_data(void *args) return wg_source_push_data(¶ms); } +NTSTATUS wow64_wg_source_read_data(void *args) +{ + struct + { + wg_source_t source; + UINT32 index; + PTR32 sample; + } *params32 = args; + struct wg_source_read_data_params params = + { + .source = params32->source, + .index = params32->index, + .sample = ULongToPtr(params32->sample), + }; + + return wg_source_read_data(¶ms); +} + NTSTATUS wow64_wg_source_get_stream_tag(void *args) { struct @@ -2620,6 +2639,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = X(wg_source_get_duration), X(wg_source_get_position), X64(wg_source_push_data), + X64(wg_source_read_data), X(wg_source_get_stream_format), X64(wg_source_get_stream_tag), diff --git a/dlls/winegstreamer/wg_sample.c b/dlls/winegstreamer/wg_sample.c index 31281066957..513907d9d77 100644 --- a/dlls/winegstreamer/wg_sample.c +++ b/dlls/winegstreamer/wg_sample.c @@ -16,6 +16,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include "ntstatus.h" +#define WIN32_NO_STATUS #include "gst_private.h" #include "wmcodecdsp.h" @@ -534,3 +536,70 @@ HRESULT wg_transform_read_dmo(wg_transform_t transform, DMO_OUTPUT_DATA_BUFFER * wg_sample_release(wg_sample); return hr; } + +HRESULT wg_source_read_data(wg_source_t source, UINT32 index, IMFSample **out) +{ + struct wg_sample wg_sample = {0}; + struct wg_source_read_data_params params = + { + .source = source, + .index = index, + .sample = &wg_sample, + }; + IMFMediaBuffer *buffer; + IMFSample *sample; + NTSTATUS status; + HRESULT hr; + + TRACE("source %#I64x, index %u, out %p\n", source, index, out); + + status = WINE_UNIX_CALL(unix_wg_source_read_data, ¶ms); + if (status == STATUS_PENDING) return E_PENDING; + if (status == STATUS_END_OF_FILE) return MF_E_END_OF_STREAM; + if (status != STATUS_BUFFER_TOO_SMALL) return HRESULT_FROM_NT(status); + + if (FAILED(hr = MFCreateSample(&sample))) + return hr; + if (SUCCEEDED(hr = MFCreateMemoryBuffer(wg_sample.max_size, &buffer))) + { + hr = IMFSample_AddBuffer(sample, buffer); + IMFMediaBuffer_Release(buffer); + } + + if (FAILED(hr) || FAILED(hr = wg_sample_create_mf(sample, ¶ms.sample))) + goto error; + + status = WINE_UNIX_CALL(unix_wg_source_read_data, ¶ms); + if (FAILED(hr = HRESULT_FROM_NT(status))) + { + wg_sample_release(params.sample); + goto error; + } + + if (params.sample->flags & WG_SAMPLE_FLAG_HAS_PTS) + IMFSample_SetSampleTime(sample, params.sample->pts); + if (params.sample->flags & WG_SAMPLE_FLAG_HAS_DURATION) + IMFSample_SetSampleDuration(sample, params.sample->duration); + if (params.sample->flags & WG_SAMPLE_FLAG_SYNC_POINT) + IMFSample_SetUINT32(sample, &MFSampleExtension_CleanPoint, 1); + if (params.sample->flags & WG_SAMPLE_FLAG_DISCONTINUITY) + IMFSample_SetUINT32(sample, &MFSampleExtension_Discontinuity, 1); + + if (SUCCEEDED(hr = IMFSample_ConvertToContiguousBuffer(sample, &buffer))) + { + hr = IMFMediaBuffer_SetCurrentLength(buffer, params.sample->size); + IMFMediaBuffer_Release(buffer); + } + wg_sample_release(params.sample); + + if (FAILED(hr)) + goto error; + + *out = sample; + TRACE("source %#I64x, index %u, out %p\n", source, index, *out); + return S_OK; + +error: + IMFSample_Release(sample); + return hr; +} diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 1c6d387635e..6e000e36e31 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -44,6 +44,9 @@ struct source_stream { GstPad *pad; + GstAtomicQueue *queue; + GstBuffer *buffer; + gboolean eos; }; struct wg_source @@ -165,9 +168,34 @@ static bool source_set_stream_flags(struct wg_source *source, guint index, GstSt return true; } +static GstStreamFlags source_get_stream_flags(struct wg_source *source, guint index) +{ + GstStreamFlags flags; + GstStream *stream; + if (!(stream = source_get_stream(source, index))) + return 0; + flags = gst_stream_get_stream_flags(stream); + gst_object_unref(stream); + return flags; +} + +static NTSTATUS source_get_stream_buffer(struct wg_source *source, guint index, GstBuffer **buffer) +{ + GstBuffer **stream_buffer; + if (index >= source->stream_count) + return STATUS_INVALID_PARAMETER; + + stream_buffer = &source->streams[index].buffer; + if (!*stream_buffer && !(*stream_buffer = gst_atomic_queue_pop(source->streams[index].queue))) + return source->streams[index].eos ? STATUS_END_OF_FILE : STATUS_PENDING; + + *buffer = gst_buffer_ref(*stream_buffer); + return STATUS_SUCCESS; +} + static gboolean src_event_seek(struct wg_source *source, GstEvent *event) { - guint32 seqnum = gst_event_get_seqnum(event); + guint32 i, seqnum = gst_event_get_seqnum(event); GstSeekType cur_type, stop_type; GstSeekFlags flags; GstFormat format; @@ -197,6 +225,9 @@ static gboolean src_event_seek(struct wg_source *source, GstEvent *event) source->segment.start = cur; + for (i = 0; i < ARRAY_SIZE(source->streams); i++) + source->streams[i].eos = false; + if (flags & GST_SEEK_FLAG_FLUSH) { if (!(event = gst_event_new_flush_stop(true))) @@ -294,10 +325,21 @@ static gboolean src_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) static GstFlowReturn sink_chain_cb(GstPad *pad, GstObject *parent, GstBuffer *buffer) { - struct wg_soutce *source = gst_pad_get_element_private(pad); + struct wg_source *source = gst_pad_get_element_private(pad); + guint index; + GST_TRACE("source %p, pad %p, buffer %p.", source, pad, buffer); - gst_buffer_unref(buffer); - return GST_FLOW_EOS; + + for (index = 0; index < source->stream_count; index++) + if (source->streams[index].pad == pad) + break; + + if (source_get_stream_flags(source, index) & GST_STREAM_FLAG_SELECT) + gst_atomic_queue_push(source->streams[index].queue, buffer); + else + gst_buffer_unref(buffer); + + return GST_FLOW_OK; } static gboolean sink_event_caps(struct wg_source *source, GstPad *pad, GstEvent *event) @@ -370,6 +412,23 @@ static gboolean sink_event_stream_start(struct wg_source *source, GstPad *pad, G return true; } +static gboolean sink_event_eos(struct wg_source *source, GstPad *pad, GstEvent *event) +{ + guint index; + + GST_TRACE("source %p, pad %p, event %p", source, pad, event); + + for (index = 0; index < source->stream_count; index++) + if (source->streams[index].pad == pad) + break; + + if (index < source->stream_count) + source->streams[index].eos = true; + + gst_event_unref(event); + return true; +} + static gboolean sink_event_cb(GstPad *pad, GstObject *parent, GstEvent *event) { struct wg_source *source = gst_pad_get_element_private(pad); @@ -382,6 +441,8 @@ static gboolean sink_event_cb(GstPad *pad, GstObject *parent, GstEvent *event) return sink_event_tag(source, pad, event); case GST_EVENT_STREAM_START: return sink_event_stream_start(source, pad, event); + case GST_EVENT_EOS: + return sink_event_eos(source, pad, event); default: return gst_pad_event_default(pad, parent, event); } @@ -481,6 +542,8 @@ NTSTATUS wg_source_create(void *args) { if (!(source->streams[i].pad = create_pad_with_caps(GST_PAD_SINK, NULL))) goto error; + if (!(source->streams[i].queue = gst_atomic_queue_new(1))) + goto error; gst_pad_set_element_private(source->streams[i].pad, source); gst_pad_set_chain_function(source->streams[i].pad, sink_chain_cb); gst_pad_set_event_function(source->streams[i].pad, sink_event_cb); @@ -534,6 +597,10 @@ NTSTATUS wg_source_create(void *args) } for (i = 0; i < ARRAY_SIZE(source->streams); i++) { + if (source->streams[i].buffer) + gst_buffer_unref(source->streams[i].buffer); + if (source->streams[i].queue) + gst_atomic_queue_unref(source->streams[i].queue); if (source->streams[i].pad) gst_object_unref(source->streams[i].pad); } @@ -558,7 +625,12 @@ NTSTATUS wg_source_destroy(void *args) gst_element_set_state(source->container, GST_STATE_NULL); gst_object_unref(source->container); for (i = 0; i < ARRAY_SIZE(source->streams); i++) + { + if (source->streams[i].buffer) + gst_buffer_unref(source->streams[i].buffer); + gst_atomic_queue_unref(source->streams[i].queue); gst_object_unref(source->streams[i].pad); + } gst_object_unref(source->src_pad); free(source->url); free(source); @@ -614,6 +686,7 @@ NTSTATUS wg_source_set_position(void *args) struct wg_source *source = get_source(params->source); guint64 time = params->time * 100; GstEvent *event; + guint i; GST_TRACE("source %p", source); @@ -622,6 +695,18 @@ NTSTATUS wg_source_set_position(void *args) || !gst_pad_push_event(source->streams[0].pad, event)) GST_WARNING("Failed to seek source %p to %" G_GINT64_MODIFIER "x", source, time); + for (i = 0; i < source->stream_count; i++) + { + GstBuffer *buffer; + + while (!source_get_stream_buffer(source, i, &buffer)) + { + gst_buffer_unref(source->streams[i].buffer); + source->streams[i].buffer = NULL; + gst_buffer_unref(buffer); + } + } + return S_OK; } @@ -676,6 +761,65 @@ NTSTATUS wg_source_push_data(void *args) return STATUS_SUCCESS; } +NTSTATUS wg_source_read_data(void *args) +{ + struct wg_source_read_data_params *params = args; + struct wg_source *source = get_source(params->source); + struct wg_sample *sample = params->sample; + NTSTATUS status = STATUS_SUCCESS; + guint index = params->index; + GstMapInfo info = {0}; + GstBuffer *buffer; + + GST_TRACE("source %p, index %#x, sample %p", source, index, sample); + + if ((status = source_get_stream_buffer(source, index, &buffer))) + return status; + + if (!gst_buffer_map(buffer, &info, GST_MAP_READ)) + status = STATUS_UNSUCCESSFUL; + else + { + if (info.size > sample->max_size) + status = STATUS_BUFFER_TOO_SMALL; + else + { + memcpy(wg_sample_data(sample), info.data, info.size); + sample->size = info.size; + + if (GST_BUFFER_PTS_IS_VALID(buffer)) + { + sample->flags |= WG_SAMPLE_FLAG_HAS_PTS; + sample->pts = GST_BUFFER_PTS(buffer) / 100; + } + else if (GST_BUFFER_DTS_IS_VALID(buffer)) + { + sample->flags |= WG_SAMPLE_FLAG_HAS_PTS; + sample->pts = GST_BUFFER_DTS(buffer) / 100; + } + if (GST_BUFFER_DURATION_IS_VALID(buffer)) + { + GstClockTime duration = GST_BUFFER_DURATION(buffer) / 100; + sample->flags |= WG_SAMPLE_FLAG_HAS_DURATION; + sample->duration = duration; + } + if (!GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT)) + sample->flags |= WG_SAMPLE_FLAG_SYNC_POINT; + if (GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DISCONT)) + sample->flags |= WG_SAMPLE_FLAG_DISCONTINUITY; + + gst_buffer_unref(source->streams[index].buffer); + source->streams[index].buffer = NULL; + } + + sample->max_size = info.size; + gst_buffer_unmap(buffer, &info); + } + + gst_buffer_unref(buffer); + return status; +} + NTSTATUS wg_source_get_stream_format(void *args) { struct wg_source_get_stream_format_params *params = args; @@ -832,5 +976,17 @@ NTSTATUS wg_source_set_stream_flags(void *args) if (!source_set_stream_flags(source, index, flags)) return STATUS_UNSUCCESSFUL; + if (!select) + { + GstBuffer *buffer; + + while (!source_get_stream_buffer(source, index, &buffer)) + { + gst_buffer_unref(source->streams[index].buffer); + source->streams[index].buffer = NULL; + gst_buffer_unref(buffer); + } + } + return STATUS_SUCCESS; } From 0eab20368c62e0cb324b2f6c130c85b444056ce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 1 Jun 2023 09:05:58 +0200 Subject: [PATCH 070/413] winegstreamer/new_media_source: Stop using wg_parser to read samples in the media source. CW-Bug-Id: #21953 --- dlls/mfmediaengine/tests/mfmediaengine.c | 2 +- dlls/winegstreamer/new_media_source.c | 334 ++--------------------- 2 files changed, 20 insertions(+), 316 deletions(-) diff --git a/dlls/mfmediaengine/tests/mfmediaengine.c b/dlls/mfmediaengine/tests/mfmediaengine.c index 66a1f0b79e9..f9489f3dcb5 100644 --- a/dlls/mfmediaengine/tests/mfmediaengine.c +++ b/dlls/mfmediaengine/tests/mfmediaengine.c @@ -152,7 +152,7 @@ static void check_rgb32_data_(int line, const WCHAR *filename, const BYTE *data, expect_data = LockResource(LoadResource(GetModuleHandleW(NULL), resource)); diff = compare_rgb32(data, &length, rect, expect_data); - ok_(__FILE__, line)(diff == 0, "Unexpected %lu%% diff\n", diff); + ok_(__FILE__, line)(diff <= 3 /* small difference in wine */, "Unexpected %lu%% diff\n", diff); } static void init_functions(void) diff --git a/dlls/winegstreamer/new_media_source.c b/dlls/winegstreamer/new_media_source.c index b4c8e91db49..efaf6a3e087 100644 --- a/dlls/winegstreamer/new_media_source.c +++ b/dlls/winegstreamer/new_media_source.c @@ -248,13 +248,10 @@ struct media_stream IMFMediaEventQueue *event_queue; IMFStreamDescriptor *descriptor; - wg_parser_stream_t wg_stream; - IUnknown **token_queue; LONG token_queue_count; LONG token_queue_cap; - DWORD stream_id; BOOL active; BOOL eos; }; @@ -305,7 +302,6 @@ struct media_source wg_source_t wg_source; WCHAR mime_type[256]; UINT64 file_size; - wg_parser_t wg_parser; UINT64 duration; BYTE *read_buffer; @@ -323,9 +319,6 @@ struct media_source SOURCE_SHUTDOWN, } state; float rate; - - HANDLE read_thread; - bool read_thread_shutdown; }; static inline struct media_stream *impl_from_IMFMediaStream(IMFMediaStream *iface) @@ -528,122 +521,28 @@ static HRESULT stream_descriptor_set_tag(IMFStreamDescriptor *descriptor, return hr; } -static HRESULT init_video_media_types(struct wg_format *format, IMFMediaType *types[6], DWORD *types_count) -{ - /* Try to prefer YUV formats over RGB ones. Most decoders output in the - * YUV color space, and it's generally much less expensive for - * videoconvert to do YUV -> YUV transformations. */ - static const enum wg_video_format video_formats[] = - { - WG_VIDEO_FORMAT_NV12, - WG_VIDEO_FORMAT_YV12, - WG_VIDEO_FORMAT_YUY2, - WG_VIDEO_FORMAT_I420, - }; - UINT count = *types_count, i; - GUID base_subtype; - HRESULT hr; - - if (FAILED(hr = IMFMediaType_GetGUID(types[0], &MF_MT_SUBTYPE, &base_subtype))) - return hr; - - for (i = 0; i < ARRAY_SIZE(video_formats); ++i) - { - struct wg_format new_format = *format; - IMFMediaType *new_type; - - new_format.u.video.format = video_formats[i]; - - if (!(new_type = mf_media_type_from_wg_format(&new_format))) - { - hr = E_OUTOFMEMORY; - goto done; - } - types[count++] = new_type; - - if (video_formats[i] == WG_VIDEO_FORMAT_I420) - { - IMFMediaType *iyuv_type; - - if (FAILED(hr = MFCreateMediaType(&iyuv_type))) - goto done; - if (FAILED(hr = IMFMediaType_CopyAllItems(new_type, (IMFAttributes *)iyuv_type))) - goto done; - if (FAILED(hr = IMFMediaType_SetGUID(iyuv_type, &MF_MT_SUBTYPE, &MFVideoFormat_IYUV))) - goto done; - types[count++] = iyuv_type; - } - } - -done: - *types_count = count; - return hr; -} - -static HRESULT init_audio_media_types(struct wg_format *format, IMFMediaType *types[6], DWORD *types_count) -{ - /* Expose at least one PCM and one floating point type for the - consumer to pick from. */ - static const enum wg_audio_format audio_types[] = - { - WG_AUDIO_FORMAT_S16LE, - WG_AUDIO_FORMAT_F32LE, - }; - UINT count = *types_count, i; - - for (i = 0; i < ARRAY_SIZE(audio_types); i++) - { - struct wg_format new_format = *format; - if (new_format.u.audio.format == audio_types[i]) - continue; - new_format.u.audio.format = audio_types[i]; - if ((types[count] = mf_media_type_from_wg_format(&new_format))) - count++; - } - - *types_count = count; - return S_OK; -} - static HRESULT stream_descriptor_create(UINT32 id, struct wg_format *format, IMFStreamDescriptor **out) { IMFStreamDescriptor *descriptor; IMFMediaTypeHandler *handler; - IMFMediaType *types[6]; - DWORD count = 0; + IMFMediaType *type; HRESULT hr; - if (!(types[0] = mf_media_type_from_wg_format(format))) + if (!(type = mf_media_type_from_wg_format(format))) return MF_E_INVALIDMEDIATYPE; - count = 1; - - if (format->major_type == WG_MAJOR_TYPE_VIDEO) - { - if (FAILED(hr = init_video_media_types(format, types, &count))) - goto done; - } - else if (format->major_type == WG_MAJOR_TYPE_AUDIO) - { - if (FAILED(hr = init_audio_media_types(format, types, &count))) - goto done; - } - - assert(count <= ARRAY_SIZE(types)); - - if (FAILED(hr = MFCreateStreamDescriptor(id, count, types, &descriptor))) + if (FAILED(hr = MFCreateStreamDescriptor(id, 1, &type, &descriptor))) goto done; if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(descriptor, &handler))) IMFStreamDescriptor_Release(descriptor); else { - hr = IMFMediaTypeHandler_SetCurrentMediaType(handler, types[0]); + hr = IMFMediaTypeHandler_SetCurrentMediaType(handler, type); IMFMediaTypeHandler_Release(handler); } done: - while (count--) - IMFMediaType_Release(types[count]); + IMFMediaType_Release(type); *out = SUCCEEDED(hr) ? descriptor : NULL; return hr; } @@ -710,7 +609,6 @@ static HRESULT media_stream_start(struct media_stream *stream, BOOL active, BOOL if (FAILED(hr = wg_format_from_stream_descriptor(stream->descriptor, &format))) WARN("Failed to get wg_format from stream descriptor, hr %#lx\n", hr); - wg_parser_stream_enable(stream->wg_stream, &format, 0); if (FAILED(hr = IMFMediaEventQueue_QueueEventParamUnk(source->event_queue, active ? MEUpdatedStream : MENewStream, &GUID_NULL, S_OK, (IUnknown *)&stream->IMFMediaStream_iface))) @@ -777,15 +675,15 @@ static HRESULT media_source_start(struct media_source *source, IMFPresentationDe stream->eos = FALSE; if (!(stream->active = !!descriptors[i])) - wg_parser_stream_disable(stream->wg_stream); + wg_source_set_stream_flags(source->wg_source, source->stream_map[i], FALSE); else { if (FAILED(hr = media_stream_start(stream, was_active, seek_message, position))) WARN("Failed to start media stream, hr %#lx\n", hr); + else + wg_source_set_stream_flags(source->wg_source, source->stream_map[i], TRUE); IMFStreamDescriptor_Release(descriptors[i]); } - - wg_source_set_stream_flags(source->wg_source, source->stream_map[i], stream->active); } free(descriptors); @@ -794,10 +692,6 @@ static HRESULT media_source_start(struct media_source *source, IMFPresentationDe if (position->vt == VT_I8) wg_source_set_position(source->wg_source, position->hVal.QuadPart); - if (position->vt == VT_I8) - wg_parser_stream_seek(source->streams[0]->wg_stream, 1.0, position->hVal.QuadPart, 0, - AM_SEEKING_AbsolutePositioning, AM_SEEKING_NoPositioning); - for (i = 0; i < source->stream_count; i++) flush_token_queue(source->streams[i], position->vt == VT_EMPTY); @@ -853,52 +747,6 @@ static HRESULT media_source_stop(struct media_source *source) return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MESourceStopped, &GUID_NULL, S_OK, NULL); } -static HRESULT media_stream_send_sample(struct media_stream *stream, const struct wg_parser_buffer *wg_buffer, IUnknown *token) -{ - IMFSample *sample = NULL; - IMFMediaBuffer *buffer; - HRESULT hr; - BYTE *data; - - if (FAILED(hr = MFCreateMemoryBuffer(wg_buffer->size, &buffer))) - return hr; - if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(buffer, wg_buffer->size))) - goto out; - if (FAILED(hr = IMFMediaBuffer_Lock(buffer, &data, NULL, NULL))) - goto out; - - if (!wg_parser_stream_copy_buffer(stream->wg_stream, data, 0, wg_buffer->size)) - { - wg_parser_stream_release_buffer(stream->wg_stream); - IMFMediaBuffer_Unlock(buffer); - goto out; - } - wg_parser_stream_release_buffer(stream->wg_stream); - - if (FAILED(hr = IMFMediaBuffer_Unlock(buffer))) - goto out; - - if (FAILED(hr = MFCreateSample(&sample))) - goto out; - if (FAILED(hr = IMFSample_AddBuffer(sample, buffer))) - goto out; - if (FAILED(hr = IMFSample_SetSampleTime(sample, wg_buffer->pts))) - goto out; - if (FAILED(hr = IMFSample_SetSampleDuration(sample, wg_buffer->duration))) - goto out; - if (token && FAILED(hr = IMFSample_SetUnknown(sample, &MFSampleExtension_Token, token))) - goto out; - - hr = IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, - &GUID_NULL, S_OK, (IUnknown *)sample); - -out: - if (sample) - IMFSample_Release(sample); - IMFMediaBuffer_Release(buffer); - return hr; -} - static HRESULT media_stream_send_eos(struct media_source *source, struct media_stream *stream) { PROPVARIANT empty = {.vt = VT_EMPTY}; @@ -926,7 +774,6 @@ static HRESULT media_stream_send_eos(struct media_source *source, struct media_s static HRESULT wait_on_sample(struct media_stream *stream, IUnknown *token) { struct media_source *source = impl_from_IMFMediaSource(stream->media_source); - struct wg_parser_buffer buffer; DWORD id, read_size; UINT64 read_offset; IMFSample *sample; @@ -953,10 +800,13 @@ static HRESULT wait_on_sample(struct media_stream *stream, IUnknown *token) if (FAILED(hr)) WARN("Failed to read stream %lu data, hr %#lx\n", id, hr); else + { + if (!token || SUCCEEDED(hr = IMFSample_SetUnknown(sample, &MFSampleExtension_Token, token))) + hr = IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, + &GUID_NULL, S_OK, (IUnknown *)sample); IMFSample_Release(sample); - - if (wg_parser_stream_get_buffer(source->wg_parser, stream->wg_stream, &buffer)) - return media_stream_send_sample(stream, &buffer, token); + return hr; + } return media_stream_send_eos(source, stream); } @@ -1021,66 +871,6 @@ static const IMFAsyncCallbackVtbl source_async_commands_callback_vtbl = source_async_commands_Invoke, }; -static DWORD CALLBACK read_thread(void *arg) -{ - struct media_source *source = arg; - IMFByteStream *byte_stream = source->byte_stream; - size_t buffer_size = 4096; - QWORD file_size; - void *data; - - if (!(data = malloc(buffer_size))) - return 0; - - IMFByteStream_GetLength(byte_stream, &file_size); - - TRACE("Starting read thread for media source %p.\n", source); - - while (!source->read_thread_shutdown) - { - uint64_t offset; - ULONG ret_size; - uint32_t size; - HRESULT hr; - - if (!wg_parser_get_next_read_offset(source->wg_parser, &offset, &size)) - continue; - - if (offset >= file_size) - size = 0; - else if (offset + size >= file_size) - size = file_size - offset; - - /* Some IMFByteStreams (including the standard file-based stream) return - * an error when reading past the file size. */ - if (!size) - { - wg_parser_push_data(source->wg_parser, data, 0); - continue; - } - - if (!array_reserve(&data, &buffer_size, size, 1)) - { - free(data); - return 0; - } - - ret_size = 0; - - if (SUCCEEDED(hr = IMFByteStream_SetCurrentPosition(byte_stream, offset))) - hr = IMFByteStream_Read(byte_stream, data, size, &ret_size); - if (FAILED(hr)) - ERR("Failed to read %u bytes at offset %I64u, hr %#lx.\n", size, offset, hr); - else if (ret_size != size) - ERR("Unexpected short read: requested %u bytes, got %lu.\n", size, ret_size); - wg_parser_push_data(source->wg_parser, SUCCEEDED(hr) ? data : NULL, ret_size); - } - - free(data); - TRACE("Media source is shutting down; exiting.\n"); - return 0; -} - static HRESULT WINAPI media_stream_QueryInterface(IMFMediaStream *iface, REFIID riid, void **out) { struct media_stream *stream = impl_from_IMFMediaStream(iface); @@ -1264,13 +1054,12 @@ static const IMFMediaStreamVtbl media_stream_vtbl = media_stream_RequestSample }; -static HRESULT media_stream_create(IMFMediaSource *source, IMFStreamDescriptor *descriptor, - wg_parser_stream_t wg_stream, struct media_stream **out) +static HRESULT media_stream_create(IMFMediaSource *source, IMFStreamDescriptor *descriptor, struct media_stream **out) { struct media_stream *object; HRESULT hr; - TRACE("source %p, descriptor %p, wg_stream %#I64x.\n", source, descriptor, wg_stream); + TRACE("source %p, descriptor %p.\n", source, descriptor); if (!(object = calloc(1, sizeof(*object)))) return E_OUTOFMEMORY; @@ -1288,7 +1077,6 @@ static HRESULT media_stream_create(IMFMediaSource *source, IMFStreamDescriptor * object->media_source = source; IMFStreamDescriptor_AddRef(descriptor); object->descriptor = descriptor; - object->wg_stream = wg_stream; TRACE("Created stream object %p.\n", object); @@ -1524,7 +1312,6 @@ static ULONG WINAPI media_source_Release(IMFMediaSource *iface) IMFByteStream_Release(source->byte_stream); wg_source_destroy(source->wg_source); free(source->read_buffer); - wg_parser_destroy(source->wg_parser); source->cs.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&source->cs); free(source); @@ -1722,12 +1509,6 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) source->state = SOURCE_SHUTDOWN; - wg_parser_disconnect(source->wg_parser); - - source->read_thread_shutdown = true; - WaitForSingleObject(source->read_thread, INFINITE); - CloseHandle(source->read_thread); - IMFMediaEventQueue_Shutdown(source->event_queue); IMFByteStream_Close(source->byte_stream); @@ -1766,50 +1547,6 @@ static const IMFMediaSourceVtbl IMFMediaSource_vtbl = media_source_Shutdown, }; -static HRESULT map_stream_to_wg_parser_stream(struct media_source *source, UINT stream_count, - wg_parser_stream_t *parser_streams, wg_parser_stream_t parser_stream) -{ - struct wg_format parser_format, source_format; - HRESULT hr; - UINT i; - - wg_parser_stream_get_preferred_format(parser_stream, &parser_format); - - for (i = 0; i < stream_count; i++) - { - if (parser_streams[i]) - continue; - if (FAILED(hr = wg_source_get_stream_format(source->wg_source, source->stream_map[i], &source_format))) - return hr; - if ((parser_format.major_type >= WG_MAJOR_TYPE_VIDEO) != (source_format.major_type >= WG_MAJOR_TYPE_VIDEO) - || (parser_format.major_type >= WG_MAJOR_TYPE_AUDIO) != (source_format.major_type >= WG_MAJOR_TYPE_AUDIO)) - continue; - - TRACE("Mapped stream %#I64x with descriptor %u\n", parser_stream, i); - parser_streams[i] = parser_stream; - return S_OK; - } - - return E_FAIL; -} - -static void media_source_init_parser_streams(struct media_source *source, UINT stream_count, - wg_parser_stream_t *parser_streams) -{ - wg_parser_stream_t parser_stream; - UINT i, parser_stream_count; - HRESULT hr; - - parser_stream_count = wg_parser_get_stream_count(source->wg_parser); - for (i = 0; i < parser_stream_count; i++) - { - if (!(parser_stream = wg_parser_get_stream(source->wg_parser, i))) - continue; - if (FAILED(hr = map_stream_to_wg_parser_stream(source, stream_count, parser_streams, parser_stream))) - WARN("Failed to map parser stream %u, hr %#lx\n", i, hr); - } -} - static void media_source_init_stream_map(struct media_source *source, UINT stream_count) { struct wg_format format; @@ -1914,9 +1651,7 @@ static void media_source_init_descriptors(struct media_source *source) static HRESULT media_source_create(struct object_context *context, IMFMediaSource **out) { UINT32 stream_count = context->stream_count; - wg_parser_stream_t *parser_streams = NULL; struct media_source *object; - wg_parser_t parser; unsigned int i; HRESULT hr; @@ -1948,23 +1683,9 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc goto fail; if (FAILED(hr = wg_source_get_duration(object->wg_source, &object->duration))) goto fail; - - if (!(parser = wg_parser_create(WG_PARSER_DECODEBIN, FALSE, FALSE))) - { - hr = E_OUTOFMEMORY; - goto fail; - } - object->wg_parser = parser; - - object->read_thread = CreateThread(NULL, 0, read_thread, object, 0, NULL); - object->state = SOURCE_OPENING; - if (FAILED(hr = wg_parser_connect(parser, object->file_size, NULL))) - goto fail; - if (!(object->descriptors = calloc(stream_count, sizeof(*object->descriptors))) - || !(parser_streams = calloc(stream_count, sizeof(*parser_streams))) || !(object->stream_map = calloc(stream_count, sizeof(*object->stream_map))) || !(object->streams = calloc(stream_count, sizeof(*object->streams)))) { @@ -1973,22 +1694,18 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc } media_source_init_stream_map(object, stream_count); - media_source_init_parser_streams(object, stream_count, parser_streams); for (i = 0; i < stream_count; ++i) { - wg_parser_stream_t parser_stream; IMFStreamDescriptor *descriptor; struct media_stream *stream; struct wg_format format; - if (!(parser_stream = parser_streams[i])) - continue; - wg_parser_stream_get_preferred_format(parser_stream, &format); - + if (FAILED(hr = wg_source_get_stream_format(object->wg_source, object->stream_map[i], &format))) + goto fail; if (FAILED(hr = stream_descriptor_create(i + 1, &format, &descriptor))) goto fail; - if (FAILED(hr = media_stream_create(&object->IMFMediaSource_iface, descriptor, parser_stream, &stream))) + if (FAILED(hr = media_stream_create(&object->IMFMediaSource_iface, descriptor, &stream))) { IMFStreamDescriptor_Release(descriptor); goto fail; @@ -2000,8 +1717,6 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc object->stream_count++; } - free(parser_streams); - media_source_init_descriptors(object); object->state = SOURCE_STOPPED; @@ -2018,23 +1733,12 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc IMFStreamDescriptor_Release(object->descriptors[object->stream_count]); IMFMediaStream_Release(&stream->IMFMediaStream_iface); } - free(parser_streams); free(object->stream_map); free(object->descriptors); free(object->streams); - if (object->state == SOURCE_OPENING) - wg_parser_disconnect(object->wg_parser); - if (object->read_thread) - { - object->read_thread_shutdown = true; - WaitForSingleObject(object->read_thread, INFINITE); - CloseHandle(object->read_thread); - } if (object->wg_source) wg_source_destroy(object->wg_source); - if (object->wg_parser) - wg_parser_destroy(object->wg_parser); if (object->async_commands_queue) MFUnlockWorkQueue(object->async_commands_queue); if (object->event_queue) From 0063fe72c9f2b5e0a92dffd0c55f548e06184ab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 15 Nov 2023 18:47:17 +0100 Subject: [PATCH 071/413] winegstreamer: Call gst_audio_info_from_caps for all audio formats. CW-Bug-Id: #21953 --- dlls/winegstreamer/wg_format.c | 54 +++++++++++++--------------------- 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/dlls/winegstreamer/wg_format.c b/dlls/winegstreamer/wg_format.c index cfa1c187672..16862998bc2 100644 --- a/dlls/winegstreamer/wg_format.c +++ b/dlls/winegstreamer/wg_format.c @@ -396,43 +396,31 @@ void wg_format_from_caps(struct wg_format *format, const GstCaps *caps) { const GstStructure *structure = gst_caps_get_structure(caps, 0); const char *name = gst_structure_get_name(structure); + GstAudioInfo audio_info; + GstVideoInfo video_info; gboolean parsed; memset(format, 0, sizeof(*format)); - if (!strcmp(name, "audio/x-raw")) - { - GstAudioInfo info; - - if (gst_audio_info_from_caps(&info, caps)) - wg_format_from_audio_info(format, &info); - } - else if (!strcmp(name, "video/x-raw")) - { - GstVideoInfo info; - - if (gst_video_info_from_caps(&info, caps)) - wg_format_from_video_info(format, &info); - } - else if (!strcmp(name, "audio/mpeg") && gst_structure_get_boolean(structure, "parsed", &parsed) && parsed) - { - wg_format_from_caps_audio_mpeg1(format, caps); - } - else if (!strcmp(name, "audio/x-wma")) - { - wg_format_from_caps_audio_wma(format, caps); - } - else if (!strcmp(name, "video/x-cinepak")) - { - wg_format_from_caps_video_cinepak(format, caps); - } - else if (!strcmp(name, "video/x-wmv")) - { - wg_format_from_caps_video_wmv(format, caps); - } - else if (!strcmp(name, "video/mpeg") && gst_structure_get_boolean(structure, "parsed", &parsed) && parsed) - { - wg_format_from_caps_video_mpeg1(format, caps); + if (g_str_has_prefix(name, "audio/") && gst_audio_info_from_caps(&audio_info, caps)) + { + if (GST_AUDIO_INFO_FORMAT(&audio_info) != GST_AUDIO_FORMAT_ENCODED) + wg_format_from_audio_info(format, &audio_info); + else if (!strcmp(name, "audio/mpeg") && gst_structure_get_boolean(structure, "parsed", &parsed) && parsed) + wg_format_from_caps_audio_mpeg1(format, caps); + else if (!strcmp(name, "audio/x-wma")) + wg_format_from_caps_audio_wma(format, caps); + } + else if (g_str_has_prefix(name, "video/") && gst_video_info_from_caps(&video_info, caps)) + { + if (GST_VIDEO_INFO_FORMAT(&video_info) != GST_VIDEO_FORMAT_ENCODED) + wg_format_from_video_info(format, &video_info); + else if (!strcmp(name, "video/x-cinepak")) + wg_format_from_caps_video_cinepak(format, caps); + else if (!strcmp(name, "video/x-wmv")) + wg_format_from_caps_video_wmv(format, caps); + else if (!strcmp(name, "video/mpeg") && gst_structure_get_boolean(structure, "parsed", &parsed) && parsed) + wg_format_from_caps_video_mpeg1(format, caps); } else { From 51d5053b9a01845e9f8dafc4c9e68e87fcb6d99d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 15 Nov 2023 18:13:17 +0100 Subject: [PATCH 072/413] winegstreamer: Support generic audio / video encoded format. CW-Bug-Id: #21953 --- dlls/winegstreamer/mfplat.c | 2 ++ dlls/winegstreamer/quartz_parser.c | 4 +++ dlls/winegstreamer/unixlib.h | 14 ++++++++ dlls/winegstreamer/wg_format.c | 56 ++++++++++++++++++++++++++++++ dlls/winegstreamer/wg_transform.c | 6 +++- dlls/winegstreamer/wm_reader.c | 8 +++++ 6 files changed, 89 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 452289c2e6a..8c8dcc32383 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -576,11 +576,13 @@ IMFMediaType *mf_media_type_from_wg_format(const struct wg_format *format) case WG_MAJOR_TYPE_AUDIO_MPEG1: case WG_MAJOR_TYPE_AUDIO_MPEG4: case WG_MAJOR_TYPE_AUDIO_WMA: + case WG_MAJOR_TYPE_AUDIO_ENCODED: case WG_MAJOR_TYPE_VIDEO_CINEPAK: case WG_MAJOR_TYPE_VIDEO_H264: case WG_MAJOR_TYPE_VIDEO_WMV: case WG_MAJOR_TYPE_VIDEO_INDEO: case WG_MAJOR_TYPE_VIDEO_MPEG1: + case WG_MAJOR_TYPE_VIDEO_ENCODED: FIXME("Format %u not implemented!\n", format->major_type); /* fallthrough */ case WG_MAJOR_TYPE_UNKNOWN: diff --git a/dlls/winegstreamer/quartz_parser.c b/dlls/winegstreamer/quartz_parser.c index dcd29d1c9fd..569a7010ebb 100644 --- a/dlls/winegstreamer/quartz_parser.c +++ b/dlls/winegstreamer/quartz_parser.c @@ -467,8 +467,10 @@ unsigned int wg_format_get_max_size(const struct wg_format *format) return format->u.audio_wma.rate * format->u.audio_wma.channels * format->u.audio_wma.depth / 8; case WG_MAJOR_TYPE_AUDIO_MPEG4: + case WG_MAJOR_TYPE_AUDIO_ENCODED: case WG_MAJOR_TYPE_VIDEO_H264: case WG_MAJOR_TYPE_VIDEO_INDEO: + case WG_MAJOR_TYPE_VIDEO_ENCODED: FIXME("Format %u not implemented!\n", format->major_type); return 0; @@ -729,8 +731,10 @@ bool amt_from_wg_format(AM_MEDIA_TYPE *mt, const struct wg_format *format, bool switch (format->major_type) { case WG_MAJOR_TYPE_AUDIO_MPEG4: + case WG_MAJOR_TYPE_AUDIO_ENCODED: case WG_MAJOR_TYPE_VIDEO_H264: case WG_MAJOR_TYPE_VIDEO_INDEO: + case WG_MAJOR_TYPE_VIDEO_ENCODED: FIXME("Format %u not implemented!\n", format->major_type); /* fallthrough */ case WG_MAJOR_TYPE_UNKNOWN: diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index ba227ca2c61..fe21a24f6f5 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -37,12 +37,14 @@ enum wg_major_type WG_MAJOR_TYPE_AUDIO_MPEG1, WG_MAJOR_TYPE_AUDIO_MPEG4, WG_MAJOR_TYPE_AUDIO_WMA, + WG_MAJOR_TYPE_AUDIO_ENCODED, WG_MAJOR_TYPE_VIDEO, WG_MAJOR_TYPE_VIDEO_CINEPAK, WG_MAJOR_TYPE_VIDEO_H264, WG_MAJOR_TYPE_VIDEO_WMV, WG_MAJOR_TYPE_VIDEO_INDEO, WG_MAJOR_TYPE_VIDEO_MPEG1, + WG_MAJOR_TYPE_VIDEO_ENCODED, }; typedef UINT32 wg_audio_format; @@ -128,6 +130,12 @@ struct wg_format unsigned char codec_data[64]; UINT8 is_xma; } audio_wma; + struct + { + uint32_t channels; + uint32_t rate; + char caps[512]; + } audio_encoded; struct { @@ -173,6 +181,12 @@ struct wg_format int32_t width, height; uint32_t fps_n, fps_d; } video_mpeg1; + struct + { + int32_t width, height; + uint32_t fps_n, fps_d; + char caps[512]; + } video_encoded; } u; }; diff --git a/dlls/winegstreamer/wg_format.c b/dlls/winegstreamer/wg_format.c index 16862998bc2..6a1e6ac15b4 100644 --- a/dlls/winegstreamer/wg_format.c +++ b/dlls/winegstreamer/wg_format.c @@ -267,6 +267,25 @@ static void wg_format_from_caps_audio_wma(struct wg_format *format, const GstCap gst_buffer_unmap(codec_data, &map); } +static void wg_format_from_caps_audio_encoded(struct wg_format *format, const GstCaps *caps, + const GstAudioInfo *info) +{ + gchar *str; + gint len; + + format->major_type = WG_MAJOR_TYPE_AUDIO_ENCODED; + format->u.audio_encoded.rate = info->rate; + format->u.audio_encoded.channels = info->channels; + + str = gst_caps_to_string(caps); + len = strlen(str) + 1; + if (len >= ARRAY_SIZE(format->u.audio_encoded.caps)) + GST_FIXME("wg_format.audio_encoded.caps buffer is too small, need %u bytes", len); + else + memcpy(format->u.audio_encoded.caps, str, len); + g_free(str); +} + static void wg_format_from_caps_video_cinepak(struct wg_format *format, const GstCaps *caps) { const GstStructure *structure = gst_caps_get_structure(caps, 0); @@ -392,6 +411,27 @@ static void wg_format_from_caps_video_mpeg1(struct wg_format *format, const GstC format->u.video_mpeg1.fps_d = fps_d; } +static void wg_format_from_caps_video_encoded(struct wg_format *format, const GstCaps *caps, + const GstVideoInfo *info) +{ + gchar *str; + gint len; + + format->major_type = WG_MAJOR_TYPE_VIDEO_ENCODED; + format->u.video_encoded.width = info->width; + format->u.video_encoded.height = info->height; + format->u.video_encoded.fps_n = info->fps_n; + format->u.video_encoded.fps_d = info->fps_d; + + str = gst_caps_to_string(caps); + len = strlen(str) + 1; + if (len >= ARRAY_SIZE(format->u.video_encoded.caps)) + GST_FIXME("wg_format.video_encoded.caps buffer is too small, need %u bytes", len); + else + memcpy(format->u.video_encoded.caps, str, len); + g_free(str); +} + void wg_format_from_caps(struct wg_format *format, const GstCaps *caps) { const GstStructure *structure = gst_caps_get_structure(caps, 0); @@ -410,6 +450,11 @@ void wg_format_from_caps(struct wg_format *format, const GstCaps *caps) wg_format_from_caps_audio_mpeg1(format, caps); else if (!strcmp(name, "audio/x-wma")) wg_format_from_caps_audio_wma(format, caps); + else + { + GST_FIXME("Using fallback for encoded audio caps %" GST_PTR_FORMAT ".", caps); + wg_format_from_caps_audio_encoded(format, caps, &audio_info); + } } else if (g_str_has_prefix(name, "video/") && gst_video_info_from_caps(&video_info, caps)) { @@ -421,6 +466,11 @@ void wg_format_from_caps(struct wg_format *format, const GstCaps *caps) wg_format_from_caps_video_wmv(format, caps); else if (!strcmp(name, "video/mpeg") && gst_structure_get_boolean(structure, "parsed", &parsed) && parsed) wg_format_from_caps_video_mpeg1(format, caps); + else + { + GST_FIXME("Using fallback for encoded video caps %" GST_PTR_FORMAT ".", caps); + wg_format_from_caps_video_encoded(format, caps, &video_info); + } } else { @@ -871,6 +921,8 @@ GstCaps *wg_format_to_caps(const struct wg_format *format) return wg_format_to_caps_audio_mpeg4(format); case WG_MAJOR_TYPE_AUDIO_WMA: return wg_format_to_caps_audio_wma(format); + case WG_MAJOR_TYPE_AUDIO_ENCODED: + return gst_caps_from_string(format->u.audio_encoded.caps); case WG_MAJOR_TYPE_VIDEO: return wg_format_to_caps_video(format); case WG_MAJOR_TYPE_VIDEO_CINEPAK: @@ -883,6 +935,8 @@ GstCaps *wg_format_to_caps(const struct wg_format *format) return wg_format_to_caps_video_indeo(format); case WG_MAJOR_TYPE_VIDEO_MPEG1: return wg_format_to_caps_video_mpeg1(format); + case WG_MAJOR_TYPE_VIDEO_ENCODED: + return gst_caps_from_string(format->u.video_encoded.caps); } assert(0); return NULL; @@ -898,9 +952,11 @@ bool wg_format_compare(const struct wg_format *a, const struct wg_format *b) case WG_MAJOR_TYPE_AUDIO_MPEG1: case WG_MAJOR_TYPE_AUDIO_MPEG4: case WG_MAJOR_TYPE_AUDIO_WMA: + case WG_MAJOR_TYPE_AUDIO_ENCODED: case WG_MAJOR_TYPE_VIDEO_H264: case WG_MAJOR_TYPE_VIDEO_INDEO: case WG_MAJOR_TYPE_VIDEO_MPEG1: + case WG_MAJOR_TYPE_VIDEO_ENCODED: GST_FIXME("Format %u not implemented!", a->major_type); /* fallthrough */ case WG_MAJOR_TYPE_UNKNOWN: diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 557d7024246..4acc449a7f5 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -404,10 +404,12 @@ NTSTATUS wg_transform_create(void *args) switch (input_format.major_type) { - case WG_MAJOR_TYPE_VIDEO_H264: + case WG_MAJOR_TYPE_AUDIO_ENCODED: case WG_MAJOR_TYPE_AUDIO_MPEG1: case WG_MAJOR_TYPE_AUDIO_MPEG4: case WG_MAJOR_TYPE_AUDIO_WMA: + case WG_MAJOR_TYPE_VIDEO_ENCODED: + case WG_MAJOR_TYPE_VIDEO_H264: case WG_MAJOR_TYPE_VIDEO_CINEPAK: case WG_MAJOR_TYPE_VIDEO_INDEO: case WG_MAJOR_TYPE_VIDEO_WMV: @@ -486,11 +488,13 @@ NTSTATUS wg_transform_create(void *args) case WG_MAJOR_TYPE_AUDIO_MPEG1: case WG_MAJOR_TYPE_AUDIO_MPEG4: case WG_MAJOR_TYPE_AUDIO_WMA: + case WG_MAJOR_TYPE_AUDIO_ENCODED: 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: + case WG_MAJOR_TYPE_VIDEO_ENCODED: GST_FIXME("Format %u not implemented!", output_format.major_type); goto out; } diff --git a/dlls/winegstreamer/wm_reader.c b/dlls/winegstreamer/wm_reader.c index c054418e2a0..a172f1cfbb3 100644 --- a/dlls/winegstreamer/wm_reader.c +++ b/dlls/winegstreamer/wm_reader.c @@ -1675,6 +1675,10 @@ static const char *get_major_type_string(enum wg_major_type type) return "indeo"; case WG_MAJOR_TYPE_VIDEO_MPEG1: return "mpeg1-video"; + case WG_MAJOR_TYPE_AUDIO_ENCODED: + return "unknown-audio"; + case WG_MAJOR_TYPE_VIDEO_ENCODED: + return "unknown-video"; case WG_MAJOR_TYPE_UNKNOWN: return "unknown"; } @@ -2043,11 +2047,13 @@ static HRESULT WINAPI reader_GetOutputFormat(IWMSyncReader2 *iface, case WG_MAJOR_TYPE_AUDIO_MPEG1: case WG_MAJOR_TYPE_AUDIO_MPEG4: case WG_MAJOR_TYPE_AUDIO_WMA: + case WG_MAJOR_TYPE_AUDIO_ENCODED: case WG_MAJOR_TYPE_VIDEO_CINEPAK: case WG_MAJOR_TYPE_VIDEO_H264: case WG_MAJOR_TYPE_VIDEO_WMV: case WG_MAJOR_TYPE_VIDEO_INDEO: case WG_MAJOR_TYPE_VIDEO_MPEG1: + case WG_MAJOR_TYPE_VIDEO_ENCODED: FIXME("Format %u not implemented!\n", format.major_type); break; case WG_MAJOR_TYPE_UNKNOWN: @@ -2086,11 +2092,13 @@ static HRESULT WINAPI reader_GetOutputFormatCount(IWMSyncReader2 *iface, DWORD o case WG_MAJOR_TYPE_AUDIO_MPEG1: case WG_MAJOR_TYPE_AUDIO_MPEG4: case WG_MAJOR_TYPE_AUDIO_WMA: + case WG_MAJOR_TYPE_AUDIO_ENCODED: case WG_MAJOR_TYPE_VIDEO_CINEPAK: case WG_MAJOR_TYPE_VIDEO_H264: case WG_MAJOR_TYPE_VIDEO_WMV: case WG_MAJOR_TYPE_VIDEO_INDEO: case WG_MAJOR_TYPE_VIDEO_MPEG1: + case WG_MAJOR_TYPE_VIDEO_ENCODED: FIXME("Format %u not implemented!\n", format.major_type); /* fallthrough */ case WG_MAJOR_TYPE_AUDIO: From 7ae422915a1d1162ad019139889cbfcd0cd39498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 19 Nov 2023 12:49:03 +0100 Subject: [PATCH 073/413] winegstreamer: Skip encoded formats in format_is_compressed. CW-Bug-Id: #21953 --- dlls/winegstreamer/wg_parser.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index d6de243f3b2..dd555a7ce19 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -147,7 +147,9 @@ static bool format_is_compressed(struct wg_format *format) { return format->major_type != WG_MAJOR_TYPE_UNKNOWN && format->major_type != WG_MAJOR_TYPE_VIDEO - && format->major_type != WG_MAJOR_TYPE_AUDIO; + && format->major_type != WG_MAJOR_TYPE_AUDIO + && format->major_type != WG_MAJOR_TYPE_VIDEO_ENCODED + && format->major_type != WG_MAJOR_TYPE_AUDIO_ENCODED; } static NTSTATUS wg_parser_get_stream_count(void *args) From 704fcf7226324bfac3631534ebf49225f1c37ca0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 15 Nov 2023 18:13:17 +0100 Subject: [PATCH 074/413] winegstreamer: Translate generic audio / video encoded media types. CW-Bug-Id: #21953 --- dlls/winegstreamer/mfplat.c | 138 +++++++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 2 deletions(-) diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 8c8dcc32383..a5325061b66 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -45,6 +45,8 @@ DEFINE_MEDIATYPE_GUID(MFAudioFormat_RAW_AAC,WAVE_FORMAT_RAW_AAC1); DEFINE_MEDIATYPE_GUID(MFAudioFormat_XMAudio2, 0x0166); DEFINE_MEDIATYPE_GUID(MFVideoFormat_VC1S,MAKEFOURCC('V','C','1','S')); DEFINE_MEDIATYPE_GUID(MFVideoFormat_IV50,MAKEFOURCC('I','V','5','0')); +DEFINE_MEDIATYPE_GUID(MFAudioFormat_GStreamer,MAKEFOURCC('G','S','T','a')); +DEFINE_MEDIATYPE_GUID(MFVideoFormat_GStreamer,MAKEFOURCC('G','S','T','v')); extern GUID MEDIASUBTYPE_VC1S; @@ -569,6 +571,74 @@ static IMFMediaType *mf_media_type_from_wg_format_video(const struct wg_format * return NULL; } +static IMFMediaType *mf_media_type_from_wg_format_audio_encoded(const struct wg_format *format) +{ + IMFMediaType *type; + UINT32 value; + HRESULT hr; + + if (FAILED(MFCreateMediaType(&type))) + return NULL; + if (FAILED(hr = IMFMediaType_SetGUID(type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio))) + goto done; + if (FAILED(hr = IMFMediaType_SetGUID(type, &MF_MT_SUBTYPE, &MFAudioFormat_GStreamer))) + goto done; + + value = format->u.audio_encoded.rate; + if (value && FAILED(hr = IMFMediaType_SetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, value))) + goto done; + value = format->u.audio_encoded.channels; + if (value && FAILED(hr = IMFMediaType_SetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, value))) + goto done; + if (FAILED(hr = IMFMediaType_SetBlob(type, &MF_MT_USER_DATA, (BYTE *)format->u.audio_encoded.caps, + strlen(format->u.audio_encoded.caps) + 1))) + goto done; + +done: + if (FAILED(hr)) + { + IMFMediaType_Release(type); + return NULL; + } + return type; +} + +static IMFMediaType *mf_media_type_from_wg_format_video_encoded(const struct wg_format *format) +{ + UINT64 frame_rate, frame_size; + IMFMediaType *type; + HRESULT hr; + + if (FAILED(MFCreateMediaType(&type))) + return NULL; + if (FAILED(hr = IMFMediaType_SetGUID(type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video))) + goto done; + if (FAILED(hr = IMFMediaType_SetGUID(type, &MF_MT_SUBTYPE, &MFVideoFormat_GStreamer))) + goto done; + if (FAILED(hr = IMFMediaType_SetUINT32(type, &MF_MT_VIDEO_ROTATION, MFVideoRotationFormat_0))) + goto done; + if (FAILED(hr = IMFMediaType_SetUINT32(type, &MF_MT_COMPRESSED, TRUE))) + goto done; + + frame_size = (UINT64)format->u.video_encoded.width << 32 | format->u.video_encoded.height; + if (FAILED(hr = IMFMediaType_SetUINT64(type, &MF_MT_FRAME_SIZE, frame_size))) + goto done; + frame_rate = (UINT64)format->u.video_encoded.fps_n << 32 | format->u.video_encoded.fps_d; + if (FAILED(hr = IMFMediaType_SetUINT64(type, &MF_MT_FRAME_RATE, frame_rate))) + goto done; + if (FAILED(hr = IMFMediaType_SetBlob(type, &MF_MT_USER_DATA, (BYTE *)format->u.video_encoded.caps, + strlen(format->u.video_encoded.caps) + 1))) + goto done; + +done: + if (FAILED(hr)) + { + IMFMediaType_Release(type); + return NULL; + } + return type; +} + IMFMediaType *mf_media_type_from_wg_format(const struct wg_format *format) { switch (format->major_type) @@ -576,13 +646,11 @@ IMFMediaType *mf_media_type_from_wg_format(const struct wg_format *format) case WG_MAJOR_TYPE_AUDIO_MPEG1: case WG_MAJOR_TYPE_AUDIO_MPEG4: case WG_MAJOR_TYPE_AUDIO_WMA: - case WG_MAJOR_TYPE_AUDIO_ENCODED: case WG_MAJOR_TYPE_VIDEO_CINEPAK: case WG_MAJOR_TYPE_VIDEO_H264: case WG_MAJOR_TYPE_VIDEO_WMV: case WG_MAJOR_TYPE_VIDEO_INDEO: case WG_MAJOR_TYPE_VIDEO_MPEG1: - case WG_MAJOR_TYPE_VIDEO_ENCODED: FIXME("Format %u not implemented!\n", format->major_type); /* fallthrough */ case WG_MAJOR_TYPE_UNKNOWN: @@ -590,9 +658,13 @@ IMFMediaType *mf_media_type_from_wg_format(const struct wg_format *format) case WG_MAJOR_TYPE_AUDIO: return mf_media_type_from_wg_format_audio(format); + case WG_MAJOR_TYPE_AUDIO_ENCODED: + return mf_media_type_from_wg_format_audio_encoded(format); case WG_MAJOR_TYPE_VIDEO: return mf_media_type_from_wg_format_video(format); + case WG_MAJOR_TYPE_VIDEO_ENCODED: + return mf_media_type_from_wg_format_video_encoded(format); } assert(0); @@ -684,6 +756,29 @@ static void mf_media_type_to_wg_format_audio_mpeg4(IMFMediaType *type, const GUI format->u.audio_mpeg4.codec_data_len = codec_data_size; } +static void mf_media_type_to_wg_format_audio_encoded(IMFMediaType *type, struct wg_format *format) +{ + UINT32 caps_len; + BYTE *caps; + HRESULT hr; + + memset(format, 0, sizeof(*format)); + format->major_type = WG_MAJOR_TYPE_AUDIO_ENCODED; + + if (FAILED(hr = IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &format->u.audio_encoded.rate))) + WARN("Failed to get MF_MT_AUDIO_SAMPLES_PER_SECOND for type %p, hr %#lx.\n", type, hr); + if (FAILED(hr = IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, &format->u.audio_encoded.channels))) + WARN("Failed to get MF_MT_AUDIO_NUM_CHANNELS for type %p, hr %#lx.\n", type, hr); + + if (FAILED(hr = IMFMediaType_GetAllocatedBlob(type, &MF_MT_USER_DATA, &caps, &caps_len))) + WARN("Failed to get MF_MT_USER_DATA for type %p, hr %#lx.\n", type, hr); + else + { + strcpy(format->u.audio_encoded.caps, (char *)caps); + CoTaskMemFree(caps); + } +} + static enum wg_video_format mf_video_format_to_wg(const GUID *subtype) { unsigned int i; @@ -922,6 +1017,41 @@ static void mf_media_type_to_wg_format_wmv(IMFMediaType *type, const GUID *subty format->u.video_wmv.format = WG_WMV_VIDEO_FORMAT_UNKNOWN; } +static void mf_media_type_to_wg_format_video_encoded(IMFMediaType *type, struct wg_format *format) +{ + UINT64 frame_rate, frame_size; + UINT32 caps_len; + HRESULT hr; + BYTE *caps; + + memset(format, 0, sizeof(*format)); + format->major_type = WG_MAJOR_TYPE_VIDEO_ENCODED; + + if (FAILED(hr = IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size))) + WARN("Failed to get MF_MT_FRAME_SIZE for type %p, hr %#lx.\n", type, hr); + else + { + format->u.video_encoded.width = frame_size >> 32; + format->u.video_encoded.height = (UINT32)frame_size; + } + + if (FAILED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &frame_rate)) && (UINT32)frame_rate) + WARN("Failed to get MF_MT_FRAME_RATE for type %p, hr %#lx.\n", type, hr); + else + { + format->u.video_encoded.fps_n = frame_rate >> 32; + format->u.video_encoded.fps_d = (UINT32)frame_rate; + } + + if (FAILED(hr = IMFMediaType_GetAllocatedBlob(type, &MF_MT_USER_DATA, &caps, &caps_len))) + WARN("Failed to get MF_MT_USER_DATA for type %p, hr %#lx.\n", type, hr); + else + { + strcpy(format->u.video_encoded.caps, (char *)caps); + CoTaskMemFree(caps); + } +} + void mf_media_type_to_wg_format(IMFMediaType *type, struct wg_format *format) { GUID major_type, subtype; @@ -949,6 +1079,8 @@ void mf_media_type_to_wg_format(IMFMediaType *type, struct wg_format *format) mf_media_type_to_wg_format_audio_wma(type, &subtype, format); else if (IsEqualGUID(&subtype, &MFAudioFormat_AAC) || IsEqualGUID(&subtype, &MFAudioFormat_RAW_AAC)) mf_media_type_to_wg_format_audio_mpeg4(type, &subtype, format); + else if (IsEqualGUID(&subtype, &MFAudioFormat_GStreamer)) + mf_media_type_to_wg_format_audio_encoded(type, format); else mf_media_type_to_wg_format_audio(type, &subtype, format); } @@ -968,6 +1100,8 @@ void mf_media_type_to_wg_format(IMFMediaType *type, struct wg_format *format) || IsEqualGUID(&subtype, &MFVideoFormat_WVC1) || IsEqualGUID(&subtype, &MEDIASUBTYPE_VC1S)) mf_media_type_to_wg_format_wmv(type, &subtype, format); + else if (IsEqualGUID(&subtype, &MFVideoFormat_GStreamer)) + mf_media_type_to_wg_format_video_encoded(type, format); else mf_media_type_to_wg_format_video(type, &subtype, format); } From 33e575bc29a88209699dd4287196850b573f83f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 17 Nov 2023 11:13:35 +0100 Subject: [PATCH 075/413] winegstreamer: Rename aac_decoder to audio_decoder. CW-Bug-Id: #21953 --- dlls/winegstreamer/Makefile.in | 2 +- .../{aac_decoder.c => audio_decoder.c} | 50 +++++++++---------- 2 files changed, 26 insertions(+), 26 deletions(-) rename dlls/winegstreamer/{aac_decoder.c => audio_decoder.c} (92%) diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 49cfafc2a8e..22238b3b765 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -7,7 +7,7 @@ UNIX_CFLAGS = $(GSTREAMER_CFLAGS) UNIX_LIBS = $(GSTREAMER_LIBS) $(PTHREAD_LIBS) SOURCES = \ - aac_decoder.c \ + audio_decoder.c \ color_convert.c \ h264_decoder.c \ main.c \ diff --git a/dlls/winegstreamer/aac_decoder.c b/dlls/winegstreamer/audio_decoder.c similarity index 92% rename from dlls/winegstreamer/aac_decoder.c rename to dlls/winegstreamer/audio_decoder.c index c0bc57f278e..82af33b671b 100644 --- a/dlls/winegstreamer/aac_decoder.c +++ b/dlls/winegstreamer/audio_decoder.c @@ -34,7 +34,7 @@ WINE_DECLARE_DEBUG_CHANNEL(winediag); #define NEXT_WAVEFORMATEXTENSIBLE(format) (WAVEFORMATEXTENSIBLE *)((BYTE *)(&(format)->Format + 1) + (format)->Format.cbSize) -static WAVEFORMATEXTENSIBLE const aac_decoder_output_types[] = +static WAVEFORMATEXTENSIBLE const audio_decoder_output_types[] = { {.Format = {.wFormatTag = WAVE_FORMAT_IEEE_FLOAT, .wBitsPerSample = 32, .nSamplesPerSec = 48000, .nChannels = 2}}, {.Format = {.wFormatTag = WAVE_FORMAT_PCM, .wBitsPerSample = 16, .nSamplesPerSec = 48000, .nChannels = 2}}, @@ -51,7 +51,7 @@ static const UINT32 default_channel_mask[7] = KSAUDIO_SPEAKER_5POINT1, }; -struct aac_decoder +struct audio_decoder { IMFTransform IMFTransform_iface; LONG refcount; @@ -66,12 +66,12 @@ struct aac_decoder struct wg_sample_queue *wg_sample_queue; }; -static struct aac_decoder *impl_from_IMFTransform(IMFTransform *iface) +static struct audio_decoder *impl_from_IMFTransform(IMFTransform *iface) { - return CONTAINING_RECORD(iface, struct aac_decoder, IMFTransform_iface); + return CONTAINING_RECORD(iface, struct audio_decoder, IMFTransform_iface); } -static HRESULT try_create_wg_transform(struct aac_decoder *decoder) +static HRESULT try_create_wg_transform(struct audio_decoder *decoder) { struct wg_format input_format, output_format; struct wg_transform_attrs attrs = {0}; @@ -96,7 +96,7 @@ static HRESULT try_create_wg_transform(struct aac_decoder *decoder) static HRESULT WINAPI transform_QueryInterface(IMFTransform *iface, REFIID iid, void **out) { - struct aac_decoder *decoder = impl_from_IMFTransform(iface); + struct audio_decoder *decoder = impl_from_IMFTransform(iface); TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); @@ -115,7 +115,7 @@ static HRESULT WINAPI transform_QueryInterface(IMFTransform *iface, REFIID iid, static ULONG WINAPI transform_AddRef(IMFTransform *iface) { - struct aac_decoder *decoder = impl_from_IMFTransform(iface); + struct audio_decoder *decoder = impl_from_IMFTransform(iface); ULONG refcount = InterlockedIncrement(&decoder->refcount); TRACE("iface %p increasing refcount to %lu.\n", decoder, refcount); return refcount; @@ -123,7 +123,7 @@ static ULONG WINAPI transform_AddRef(IMFTransform *iface) static ULONG WINAPI transform_Release(IMFTransform *iface) { - struct aac_decoder *decoder = impl_from_IMFTransform(iface); + struct audio_decoder *decoder = impl_from_IMFTransform(iface); ULONG refcount = InterlockedDecrement(&decoder->refcount); TRACE("iface %p decreasing refcount to %lu.\n", decoder, refcount); @@ -228,7 +228,7 @@ static HRESULT WINAPI transform_AddInputStreams(IMFTransform *iface, DWORD strea static HRESULT WINAPI transform_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, IMFMediaType **type) { - struct aac_decoder *decoder = impl_from_IMFTransform(iface); + struct audio_decoder *decoder = impl_from_IMFTransform(iface); const WAVEFORMATEXTENSIBLE *format = decoder->input_types; UINT count = decoder->input_type_count; @@ -245,7 +245,7 @@ static HRESULT WINAPI transform_GetInputAvailableType(IMFTransform *iface, DWORD static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWORD id, DWORD index, IMFMediaType **type) { - struct aac_decoder *decoder = impl_from_IMFTransform(iface); + struct audio_decoder *decoder = impl_from_IMFTransform(iface); UINT32 channel_count, sample_rate; WAVEFORMATEXTENSIBLE wfx = {{0}}; IMFMediaType *media_type; @@ -260,7 +260,7 @@ static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWOR if (!decoder->input_type) return MF_E_TRANSFORM_TYPE_NOT_SET; - wfx = aac_decoder_output_types[index % ARRAY_SIZE(aac_decoder_output_types)]; + wfx = audio_decoder_output_types[index % ARRAY_SIZE(audio_decoder_output_types)]; if (FAILED(hr = IMFMediaType_GetUINT32(decoder->input_type, &MF_MT_AUDIO_NUM_CHANNELS, &channel_count)) || !channel_count) @@ -271,15 +271,15 @@ static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWOR if (channel_count >= ARRAY_SIZE(default_channel_mask)) return MF_E_INVALIDMEDIATYPE; - if (channel_count > 2 && index >= ARRAY_SIZE(aac_decoder_output_types)) + if (channel_count > 2 && index >= ARRAY_SIZE(audio_decoder_output_types)) { /* If there are more than two channels in the input type GetOutputAvailableType additionally lists * types with 2 channels. */ - index -= ARRAY_SIZE(aac_decoder_output_types); + index -= ARRAY_SIZE(audio_decoder_output_types); channel_count = 2; } - if (index >= ARRAY_SIZE(aac_decoder_output_types)) + if (index >= ARRAY_SIZE(audio_decoder_output_types)) return MF_E_NO_MORE_TYPES; wfx.Format.nChannels = channel_count; @@ -321,7 +321,7 @@ static BOOL matches_format(const WAVEFORMATEXTENSIBLE *a, const WAVEFORMATEXTENS static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) { - struct aac_decoder *decoder = impl_from_IMFTransform(iface); + struct audio_decoder *decoder = impl_from_IMFTransform(iface); UINT32 size, count = decoder->input_type_count; WAVEFORMATEXTENSIBLE *format, wfx; HRESULT hr; @@ -361,7 +361,7 @@ static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFM static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) { - struct aac_decoder *decoder = impl_from_IMFTransform(iface); + struct audio_decoder *decoder = impl_from_IMFTransform(iface); WAVEFORMATEXTENSIBLE *format, wfx; UINT32 size; HRESULT hr; @@ -380,10 +380,10 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF wfx = *format; CoTaskMemFree(format); - for (i = 0; i < ARRAY_SIZE(aac_decoder_output_types); ++i) - if (matches_format(&aac_decoder_output_types[i], &wfx)) + for (i = 0; i < ARRAY_SIZE(audio_decoder_output_types); ++i) + if (matches_format(&audio_decoder_output_types[i], &wfx)) break; - if (i == ARRAY_SIZE(aac_decoder_output_types)) + if (i == ARRAY_SIZE(audio_decoder_output_types)) return MF_E_INVALIDMEDIATYPE; if (!wfx.Format.wBitsPerSample || !wfx.Format.nChannels || !wfx.Format.nSamplesPerSec) @@ -410,7 +410,7 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF static HRESULT WINAPI transform_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **out) { - struct aac_decoder *decoder = impl_from_IMFTransform(iface); + struct audio_decoder *decoder = impl_from_IMFTransform(iface); IMFMediaType *type; HRESULT hr; @@ -432,7 +432,7 @@ static HRESULT WINAPI transform_GetInputCurrentType(IMFTransform *iface, DWORD i static HRESULT WINAPI transform_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **out) { - struct aac_decoder *decoder = impl_from_IMFTransform(iface); + struct audio_decoder *decoder = impl_from_IMFTransform(iface); IMFMediaType *type; HRESULT hr; @@ -454,7 +454,7 @@ static HRESULT WINAPI transform_GetOutputCurrentType(IMFTransform *iface, DWORD static HRESULT WINAPI transform_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags) { - struct aac_decoder *decoder = impl_from_IMFTransform(iface); + struct audio_decoder *decoder = impl_from_IMFTransform(iface); bool accepts_input; TRACE("iface %p, id %#lx, flags %p.\n", iface, id, flags); @@ -495,7 +495,7 @@ static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_ static HRESULT WINAPI transform_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags) { - struct aac_decoder *decoder = impl_from_IMFTransform(iface); + struct audio_decoder *decoder = impl_from_IMFTransform(iface); TRACE("iface %p, id %#lx, sample %p, flags %#lx.\n", iface, id, sample, flags); @@ -508,7 +508,7 @@ static HRESULT WINAPI transform_ProcessInput(IMFTransform *iface, DWORD id, IMFS static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) { - struct aac_decoder *decoder = impl_from_IMFTransform(iface); + struct audio_decoder *decoder = impl_from_IMFTransform(iface); MFT_OUTPUT_STREAM_INFO info; HRESULT hr; @@ -598,7 +598,7 @@ HRESULT aac_decoder_create(REFIID riid, void **ret) static const struct wg_format input_format = {.major_type = WG_MAJOR_TYPE_AUDIO_MPEG4}; struct wg_transform_attrs attrs = {0}; wg_transform_t transform; - struct aac_decoder *decoder; + struct audio_decoder *decoder; HRESULT hr; TRACE("riid %s, ret %p.\n", debugstr_guid(riid), ret); From 6e481cb1de5bc448731c6c908327e156dcf77134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 28 Jul 2023 15:46:13 +0200 Subject: [PATCH 076/413] winegstreamer: Introduce a generic audio decoder transform. CW-Bug-Id: #21953 --- dlls/winegstreamer/audio_decoder.c | 38 ++++++++++++++++++++ dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/mfplat.c | 22 ++++++++++++ dlls/winegstreamer/winegstreamer_classes.idl | 6 ++++ 4 files changed, 67 insertions(+) diff --git a/dlls/winegstreamer/audio_decoder.c b/dlls/winegstreamer/audio_decoder.c index 82af33b671b..771eae465fe 100644 --- a/dlls/winegstreamer/audio_decoder.c +++ b/dlls/winegstreamer/audio_decoder.c @@ -612,6 +612,9 @@ HRESULT aac_decoder_create(REFIID riid, void **ret) if (!(decoder = calloc(1, sizeof(*decoder)))) return E_OUTOFMEMORY; + decoder->IMFTransform_iface.lpVtbl = &transform_vtbl; + decoder->refcount = 1; + decoder->input_types = (WAVEFORMATEXTENSIBLE *)aac_decoder_input_types; decoder->input_type_count = ARRAY_SIZE(aac_decoder_input_types); @@ -621,9 +624,44 @@ HRESULT aac_decoder_create(REFIID riid, void **ret) return hr; } + *ret = &decoder->IMFTransform_iface; + TRACE("Created decoder %p\n", *ret); + return S_OK; +} + +static WAVEFORMATEXTENSIBLE audio_decoder_input_types[] = +{ +#define MAKE_WAVEFORMATEXTENSIBLE(format) \ + {.Format = {.wFormatTag = WAVE_FORMAT_EXTENSIBLE, .nChannels = 6, .nSamplesPerSec = 48000, .nAvgBytesPerSec = 1152000, \ + .nBlockAlign = 24, .wBitsPerSample = 32, .cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)}, \ + .SubFormat = {format,0x0000,0x0010,{0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71}}} + + MAKE_WAVEFORMATEXTENSIBLE(MAKEFOURCC('G','S','T','a')), + +#undef MAKE_WAVEFORMATEXTENSIBLE +}; + +HRESULT audio_decoder_create(REFIID riid, void **ret) +{ + struct audio_decoder *decoder; + HRESULT hr; + + TRACE("riid %s, ret %p.\n", debugstr_guid(riid), ret); + + if (!(decoder = calloc(1, sizeof(*decoder)))) + return E_OUTOFMEMORY; decoder->IMFTransform_iface.lpVtbl = &transform_vtbl; decoder->refcount = 1; + decoder->input_types = audio_decoder_input_types; + decoder->input_type_count = ARRAY_SIZE(audio_decoder_input_types); + + if (FAILED(hr = wg_sample_queue_create(&decoder->wg_sample_queue))) + { + free(decoder); + return hr; + } + *ret = &decoder->IMFTransform_iface; TRACE("Created decoder %p\n", *ret); return S_OK; diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 84857c5adfa..c894fd0b1ae 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -188,6 +188,7 @@ unsigned int wg_format_get_stride(const struct wg_format *format); bool wg_video_format_is_rgb(enum wg_video_format format); +HRESULT audio_decoder_create(REFIID riid, void **ret); HRESULT aac_decoder_create(REFIID riid, void **ret); HRESULT h264_decoder_create(REFIID riid, void **ret); HRESULT video_processor_create(REFIID riid, void **ret); diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index a5325061b66..ea72539d1b6 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -128,6 +128,7 @@ static const IClassFactoryVtbl class_factory_vtbl = static const GUID CLSID_GStreamerByteStreamHandler = {0x317df618, 0x5e5a, 0x468a, {0x9f, 0x15, 0xd8, 0x27, 0xa9, 0xa0, 0x81, 0x62}}; static const GUID CLSID_GStreamerByteStreamHandler2 = {0x317df619, 0x5e5a, 0x468a, {0x9f, 0x15, 0xd8, 0x27, 0xa9, 0xa0, 0x81, 0x62}}; +static const GUID CLSID_GStreamerAudioDecoder = {0x480b1517, 0xc8e9, 0x4eae, {0xb0, 0x06, 0xe6, 0x30, 0x07, 0x18, 0xd8, 0x5d}}; static const GUID CLSID_GStreamerSchemePlugin = {0x587eeb6a,0x7336,0x4ebd,{0xa4,0xf2,0x91,0xc9,0x48,0xde,0x62,0x2c}}; @@ -138,6 +139,7 @@ static const struct class_object } class_objects[] = { + { &CLSID_GStreamerAudioDecoder, &audio_decoder_create }, { &CLSID_VideoProcessorMFT, &video_processor_create }, { &CLSID_GStreamerByteStreamHandler, &gstreamer_byte_stream_handler_create }, { &CLSID_GStreamerByteStreamHandler2, &gstreamer_byte_stream_handler_2_create }, @@ -339,6 +341,16 @@ HRESULT mfplat_DllRegisterServer(void) {MFMediaType_Video, MFVideoFormat_NV11}, }; + MFT_REGISTER_TYPE_INFO audio_decoder_input_types[] = + { + {MFMediaType_Audio, MFAudioFormat_GStreamer}, + }; + MFT_REGISTER_TYPE_INFO audio_decoder_output_types[] = + { + {MFMediaType_Audio, MFAudioFormat_Float}, + {MFMediaType_Audio, MFAudioFormat_PCM}, + }; + struct mft { GUID clsid; @@ -422,6 +434,16 @@ HRESULT mfplat_DllRegisterServer(void) ARRAY_SIZE(color_convert_output_types), color_convert_output_types, }, + { + CLSID_GStreamerAudioDecoder, + MFT_CATEGORY_AUDIO_DECODER, + L"Wine Audio Decoder MFT", + MFT_ENUM_FLAG_SYNCMFT, + ARRAY_SIZE(audio_decoder_input_types), + audio_decoder_input_types, + ARRAY_SIZE(audio_decoder_output_types), + audio_decoder_output_types, + }, }; unsigned int i; diff --git a/dlls/winegstreamer/winegstreamer_classes.idl b/dlls/winegstreamer/winegstreamer_classes.idl index 493e6cff2b0..2ef6d994274 100644 --- a/dlls/winegstreamer/winegstreamer_classes.idl +++ b/dlls/winegstreamer/winegstreamer_classes.idl @@ -89,6 +89,12 @@ coclass GStreamerByteStreamHandler {} ] coclass GStreamerByteStreamHandler2 {} +[ + threading(both), + uuid(480b1517-c8e9-4eae-b006-e6300718d85d) +] +coclass GStreamerAudioDecoder {} + [ threading(both), uuid(2eeb4adf-4578-4d10-bca7-bb955f56320a) From 40ad94723104838e117399ed80dd5b6dd736e6b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 23 Jan 2024 11:44:51 +0100 Subject: [PATCH 077/413] winegstreamer: Use MFCreateVideoMediaTypeFromSubtype in GetInputAvailableType. CW-Bug-Id: #21953 --- dlls/winegstreamer/h264_decoder.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index aff9e73d051..0ba9f378373 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -398,27 +398,12 @@ static HRESULT WINAPI transform_AddInputStreams(IMFTransform *iface, DWORD strea static HRESULT WINAPI transform_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, IMFMediaType **type) { - IMFMediaType *media_type; - const GUID *subtype; - HRESULT hr; - TRACE("iface %p, id %#lx, index %#lx, type %p.\n", iface, id, index, type); *type = NULL; - if (index >= ARRAY_SIZE(h264_decoder_input_types)) return MF_E_NO_MORE_TYPES; - subtype = h264_decoder_input_types[index]; - - if (FAILED(hr = MFCreateMediaType(&media_type))) - return hr; - - if (SUCCEEDED(hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video)) && - SUCCEEDED(hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, subtype))) - IMFMediaType_AddRef((*type = media_type)); - - IMFMediaType_Release(media_type); - return hr; + return MFCreateVideoMediaTypeFromSubtype(h264_decoder_input_types[index], (IMFVideoMediaType **)type); } static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWORD id, From b8d016c3f623ba698191a0a4f0be2cd298182b57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 23 Jan 2024 11:52:09 +0100 Subject: [PATCH 078/413] winegstreamer: Use MFCreateVideoMediaTypeFromSubtype in GetOutputAvailableType. CW-Bug-Id: #21953 --- dlls/winegstreamer/h264_decoder.c | 116 +++++++++++++----------------- 1 file changed, 49 insertions(+), 67 deletions(-) diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index 0ba9f378373..8ec0671bbbe 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -122,102 +122,106 @@ static HRESULT try_create_wg_transform(struct h264_decoder *decoder) return S_OK; } -static HRESULT fill_output_media_type(struct h264_decoder *decoder, IMFMediaType *media_type) +static HRESULT create_output_media_type(struct h264_decoder *decoder, const GUID *subtype, + IMFMediaType **media_type) { IMFMediaType *default_type = decoder->output_type; + IMFVideoMediaType *video_type; UINT32 value, width, height; MFVideoArea aperture; UINT64 ratio; - GUID subtype; HRESULT hr; - if (FAILED(hr = IMFMediaType_GetGUID(media_type, &MF_MT_SUBTYPE, &subtype))) + if (FAILED(hr = MFCreateVideoMediaTypeFromSubtype(subtype, &video_type))) return hr; - if (FAILED(hr = IMFMediaType_GetUINT64(media_type, &MF_MT_FRAME_SIZE, &ratio))) + if (FAILED(IMFVideoMediaType_GetUINT64(video_type, &MF_MT_FRAME_SIZE, &ratio))) { if (FAILED(IMFMediaType_GetUINT64(decoder->stream_type, &MF_MT_FRAME_SIZE, &ratio))) ratio = (UINT64)1920 << 32 | 1080; - if (FAILED(hr = IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_SIZE, ratio))) - return hr; + if (FAILED(hr = IMFVideoMediaType_SetUINT64(video_type, &MF_MT_FRAME_SIZE, ratio))) + goto done; } width = ratio >> 32; height = ratio; - if (FAILED(hr = IMFMediaType_GetItem(media_type, &MF_MT_FRAME_RATE, NULL))) + if (FAILED(IMFVideoMediaType_GetItem(video_type, &MF_MT_FRAME_RATE, NULL))) { if (FAILED(IMFMediaType_GetUINT64(decoder->stream_type, &MF_MT_FRAME_RATE, &ratio))) ratio = (UINT64)30000 << 32 | 1001; - if (FAILED(hr = IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_RATE, ratio))) - return hr; + if (FAILED(hr = IMFVideoMediaType_SetUINT64(video_type, &MF_MT_FRAME_RATE, ratio))) + goto done; } - if (FAILED(hr = IMFMediaType_GetItem(media_type, &MF_MT_PIXEL_ASPECT_RATIO, NULL))) + if (FAILED(IMFVideoMediaType_GetItem(video_type, &MF_MT_PIXEL_ASPECT_RATIO, NULL))) { if (FAILED(IMFMediaType_GetUINT64(decoder->stream_type, &MF_MT_PIXEL_ASPECT_RATIO, &ratio))) ratio = (UINT64)1 << 32 | 1; - if (FAILED(hr = IMFMediaType_SetUINT64(media_type, &MF_MT_PIXEL_ASPECT_RATIO, ratio))) - return hr; + if (FAILED(hr = IMFVideoMediaType_SetUINT64(video_type, &MF_MT_PIXEL_ASPECT_RATIO, ratio))) + goto done; } - if (FAILED(hr = IMFMediaType_GetItem(media_type, &MF_MT_SAMPLE_SIZE, NULL))) + if (FAILED(IMFVideoMediaType_GetItem(video_type, &MF_MT_SAMPLE_SIZE, NULL))) { - if (FAILED(hr = MFCalculateImageSize(&subtype, width, height, &value))) - return hr; - if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_SAMPLE_SIZE, value))) - return hr; + if (FAILED(hr = MFCalculateImageSize(subtype, width, height, &value))) + goto done; + if (FAILED(hr = IMFVideoMediaType_SetUINT32(video_type, &MF_MT_SAMPLE_SIZE, value))) + goto done; } - if (FAILED(hr = IMFMediaType_GetItem(media_type, &MF_MT_DEFAULT_STRIDE, NULL))) + if (FAILED(IMFVideoMediaType_GetItem(video_type, &MF_MT_DEFAULT_STRIDE, NULL))) { - if (FAILED(hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, (LONG *)&value))) - return hr; - if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_DEFAULT_STRIDE, value))) - return hr; + if (FAILED(hr = MFGetStrideForBitmapInfoHeader(subtype->Data1, width, (LONG *)&value))) + goto done; + if (FAILED(hr = IMFVideoMediaType_SetUINT32(video_type, &MF_MT_DEFAULT_STRIDE, value))) + goto done; } - if (FAILED(hr = IMFMediaType_GetItem(media_type, &MF_MT_INTERLACE_MODE, NULL))) + if (FAILED(IMFVideoMediaType_GetItem(video_type, &MF_MT_INTERLACE_MODE, NULL))) { - if (!default_type || FAILED(hr = IMFMediaType_GetUINT32(default_type, &MF_MT_INTERLACE_MODE, &value))) + if (!default_type || FAILED(IMFMediaType_GetUINT32(default_type, &MF_MT_INTERLACE_MODE, &value))) value = MFVideoInterlace_MixedInterlaceOrProgressive; - if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_INTERLACE_MODE, value))) - return hr; + if (FAILED(hr = IMFVideoMediaType_SetUINT32(video_type, &MF_MT_INTERLACE_MODE, value))) + goto done; } - if (FAILED(hr = IMFMediaType_GetItem(media_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, NULL))) + if (FAILED(IMFVideoMediaType_GetItem(video_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, NULL))) { - if (!default_type || FAILED(hr = IMFMediaType_GetUINT32(default_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, &value))) + if (!default_type || FAILED(IMFMediaType_GetUINT32(default_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, &value))) value = 1; - if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, value))) - return hr; + if (FAILED(hr = IMFVideoMediaType_SetUINT32(video_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, value))) + goto done; } - if (FAILED(hr = IMFMediaType_GetItem(media_type, &MF_MT_VIDEO_ROTATION, NULL))) + if (FAILED(IMFVideoMediaType_GetItem(video_type, &MF_MT_VIDEO_ROTATION, NULL))) { - if (!default_type || FAILED(hr = IMFMediaType_GetUINT32(default_type, &MF_MT_VIDEO_ROTATION, &value))) + if (!default_type || FAILED(IMFMediaType_GetUINT32(default_type, &MF_MT_VIDEO_ROTATION, &value))) value = 0; - if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_VIDEO_ROTATION, value))) - return hr; + if (FAILED(hr = IMFVideoMediaType_SetUINT32(video_type, &MF_MT_VIDEO_ROTATION, value))) + goto done; } - if (FAILED(hr = IMFMediaType_GetItem(media_type, &MF_MT_FIXED_SIZE_SAMPLES, NULL))) + if (FAILED(IMFVideoMediaType_GetItem(video_type, &MF_MT_FIXED_SIZE_SAMPLES, NULL))) { - if (!default_type || FAILED(hr = IMFMediaType_GetUINT32(default_type, &MF_MT_FIXED_SIZE_SAMPLES, &value))) + if (!default_type || FAILED(IMFMediaType_GetUINT32(default_type, &MF_MT_FIXED_SIZE_SAMPLES, &value))) value = 1; - if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_FIXED_SIZE_SAMPLES, value))) - return hr; + if (FAILED(hr = IMFVideoMediaType_SetUINT32(video_type, &MF_MT_FIXED_SIZE_SAMPLES, value))) + goto done; } - if (FAILED(hr = IMFMediaType_GetItem(media_type, &MF_MT_MINIMUM_DISPLAY_APERTURE, NULL)) - && SUCCEEDED(hr = IMFMediaType_GetBlob(decoder->stream_type, &MF_MT_MINIMUM_DISPLAY_APERTURE, + if (FAILED(IMFVideoMediaType_GetItem(video_type, &MF_MT_MINIMUM_DISPLAY_APERTURE, NULL)) + && SUCCEEDED(IMFMediaType_GetBlob(decoder->stream_type, &MF_MT_MINIMUM_DISPLAY_APERTURE, (BYTE *)&aperture, sizeof(aperture), &value))) { - if (FAILED(hr = IMFMediaType_SetBlob(media_type, &MF_MT_MINIMUM_DISPLAY_APERTURE, + if (FAILED(hr = IMFVideoMediaType_SetBlob(video_type, &MF_MT_MINIMUM_DISPLAY_APERTURE, (BYTE *)&aperture, sizeof(aperture)))) - return hr; + goto done; } - return S_OK; + IMFMediaType_AddRef((*media_type = (IMFMediaType *)video_type)); +done: + IMFVideoMediaType_Release(video_type); + return hr; } static HRESULT init_allocator(struct h264_decoder *decoder) @@ -410,37 +414,15 @@ static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWOR DWORD index, IMFMediaType **type) { struct h264_decoder *decoder = impl_from_IMFTransform(iface); - IMFMediaType *media_type; - const GUID *output_type; - HRESULT hr; TRACE("iface %p, id %#lx, index %#lx, type %p.\n", iface, id, index, type); + *type = NULL; if (!decoder->input_type) return MF_E_TRANSFORM_TYPE_NOT_SET; - - *type = NULL; - if (index >= ARRAY_SIZE(h264_decoder_output_types)) return MF_E_NO_MORE_TYPES; - output_type = h264_decoder_output_types[index]; - - if (FAILED(hr = MFCreateMediaType(&media_type))) - return hr; - - if (FAILED(hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video))) - goto done; - if (FAILED(hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, output_type))) - goto done; - - hr = fill_output_media_type(decoder, media_type); - -done: - if (SUCCEEDED(hr)) - IMFMediaType_AddRef((*type = media_type)); - - IMFMediaType_Release(media_type); - return hr; + return create_output_media_type(decoder, h264_decoder_output_types[index], type); } static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) From 99ec397114bb0d0d0bcc0f292f80a5eb29eb4801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 23 Jan 2024 11:57:46 +0100 Subject: [PATCH 079/413] winegstreamer: Remove unnecessary create_output_media_type checks. CW-Bug-Id: #21953 --- dlls/winegstreamer/h264_decoder.c | 118 ++++++++++++------------------ 1 file changed, 45 insertions(+), 73 deletions(-) diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index 8ec0671bbbe..db36af36a20 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -135,82 +135,54 @@ static HRESULT create_output_media_type(struct h264_decoder *decoder, const GUID if (FAILED(hr = MFCreateVideoMediaTypeFromSubtype(subtype, &video_type))) return hr; - if (FAILED(IMFVideoMediaType_GetUINT64(video_type, &MF_MT_FRAME_SIZE, &ratio))) - { - if (FAILED(IMFMediaType_GetUINT64(decoder->stream_type, &MF_MT_FRAME_SIZE, &ratio))) - ratio = (UINT64)1920 << 32 | 1080; - if (FAILED(hr = IMFVideoMediaType_SetUINT64(video_type, &MF_MT_FRAME_SIZE, ratio))) - goto done; - } + if (FAILED(IMFMediaType_GetUINT64(decoder->stream_type, &MF_MT_FRAME_SIZE, &ratio))) + ratio = (UINT64)1920 << 32 | 1080; + if (FAILED(hr = IMFVideoMediaType_SetUINT64(video_type, &MF_MT_FRAME_SIZE, ratio))) + goto done; width = ratio >> 32; height = ratio; - if (FAILED(IMFVideoMediaType_GetItem(video_type, &MF_MT_FRAME_RATE, NULL))) - { - if (FAILED(IMFMediaType_GetUINT64(decoder->stream_type, &MF_MT_FRAME_RATE, &ratio))) - ratio = (UINT64)30000 << 32 | 1001; - if (FAILED(hr = IMFVideoMediaType_SetUINT64(video_type, &MF_MT_FRAME_RATE, ratio))) - goto done; - } - - if (FAILED(IMFVideoMediaType_GetItem(video_type, &MF_MT_PIXEL_ASPECT_RATIO, NULL))) - { - if (FAILED(IMFMediaType_GetUINT64(decoder->stream_type, &MF_MT_PIXEL_ASPECT_RATIO, &ratio))) - ratio = (UINT64)1 << 32 | 1; - if (FAILED(hr = IMFVideoMediaType_SetUINT64(video_type, &MF_MT_PIXEL_ASPECT_RATIO, ratio))) - goto done; - } - - if (FAILED(IMFVideoMediaType_GetItem(video_type, &MF_MT_SAMPLE_SIZE, NULL))) - { - if (FAILED(hr = MFCalculateImageSize(subtype, width, height, &value))) - goto done; - if (FAILED(hr = IMFVideoMediaType_SetUINT32(video_type, &MF_MT_SAMPLE_SIZE, value))) - goto done; - } - - if (FAILED(IMFVideoMediaType_GetItem(video_type, &MF_MT_DEFAULT_STRIDE, NULL))) - { - if (FAILED(hr = MFGetStrideForBitmapInfoHeader(subtype->Data1, width, (LONG *)&value))) - goto done; - if (FAILED(hr = IMFVideoMediaType_SetUINT32(video_type, &MF_MT_DEFAULT_STRIDE, value))) - goto done; - } - - if (FAILED(IMFVideoMediaType_GetItem(video_type, &MF_MT_INTERLACE_MODE, NULL))) - { - if (!default_type || FAILED(IMFMediaType_GetUINT32(default_type, &MF_MT_INTERLACE_MODE, &value))) - value = MFVideoInterlace_MixedInterlaceOrProgressive; - if (FAILED(hr = IMFVideoMediaType_SetUINT32(video_type, &MF_MT_INTERLACE_MODE, value))) - goto done; - } - - if (FAILED(IMFVideoMediaType_GetItem(video_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, NULL))) - { - if (!default_type || FAILED(IMFMediaType_GetUINT32(default_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, &value))) - value = 1; - if (FAILED(hr = IMFVideoMediaType_SetUINT32(video_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, value))) - goto done; - } - - if (FAILED(IMFVideoMediaType_GetItem(video_type, &MF_MT_VIDEO_ROTATION, NULL))) - { - if (!default_type || FAILED(IMFMediaType_GetUINT32(default_type, &MF_MT_VIDEO_ROTATION, &value))) - value = 0; - if (FAILED(hr = IMFVideoMediaType_SetUINT32(video_type, &MF_MT_VIDEO_ROTATION, value))) - goto done; - } - - if (FAILED(IMFVideoMediaType_GetItem(video_type, &MF_MT_FIXED_SIZE_SAMPLES, NULL))) - { - if (!default_type || FAILED(IMFMediaType_GetUINT32(default_type, &MF_MT_FIXED_SIZE_SAMPLES, &value))) - value = 1; - if (FAILED(hr = IMFVideoMediaType_SetUINT32(video_type, &MF_MT_FIXED_SIZE_SAMPLES, value))) - goto done; - } - - if (FAILED(IMFVideoMediaType_GetItem(video_type, &MF_MT_MINIMUM_DISPLAY_APERTURE, NULL)) - && SUCCEEDED(IMFMediaType_GetBlob(decoder->stream_type, &MF_MT_MINIMUM_DISPLAY_APERTURE, + if (FAILED(IMFMediaType_GetUINT64(decoder->stream_type, &MF_MT_FRAME_RATE, &ratio))) + ratio = (UINT64)30000 << 32 | 1001; + if (FAILED(hr = IMFVideoMediaType_SetUINT64(video_type, &MF_MT_FRAME_RATE, ratio))) + goto done; + + if (FAILED(IMFMediaType_GetUINT64(decoder->stream_type, &MF_MT_PIXEL_ASPECT_RATIO, &ratio))) + ratio = (UINT64)1 << 32 | 1; + if (FAILED(hr = IMFVideoMediaType_SetUINT64(video_type, &MF_MT_PIXEL_ASPECT_RATIO, ratio))) + goto done; + + if (FAILED(hr = MFCalculateImageSize(subtype, width, height, &value))) + goto done; + if (FAILED(hr = IMFVideoMediaType_SetUINT32(video_type, &MF_MT_SAMPLE_SIZE, value))) + goto done; + + if (FAILED(hr = MFGetStrideForBitmapInfoHeader(subtype->Data1, width, (LONG *)&value))) + goto done; + if (FAILED(hr = IMFVideoMediaType_SetUINT32(video_type, &MF_MT_DEFAULT_STRIDE, value))) + goto done; + + if (!default_type || FAILED(IMFMediaType_GetUINT32(default_type, &MF_MT_INTERLACE_MODE, &value))) + value = MFVideoInterlace_MixedInterlaceOrProgressive; + if (FAILED(hr = IMFVideoMediaType_SetUINT32(video_type, &MF_MT_INTERLACE_MODE, value))) + goto done; + + if (!default_type || FAILED(IMFMediaType_GetUINT32(default_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, &value))) + value = 1; + if (FAILED(hr = IMFVideoMediaType_SetUINT32(video_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, value))) + goto done; + + if (!default_type || FAILED(IMFMediaType_GetUINT32(default_type, &MF_MT_VIDEO_ROTATION, &value))) + value = 0; + if (FAILED(hr = IMFVideoMediaType_SetUINT32(video_type, &MF_MT_VIDEO_ROTATION, value))) + goto done; + + if (!default_type || FAILED(IMFMediaType_GetUINT32(default_type, &MF_MT_FIXED_SIZE_SAMPLES, &value))) + value = 1; + if (FAILED(hr = IMFVideoMediaType_SetUINT32(video_type, &MF_MT_FIXED_SIZE_SAMPLES, value))) + goto done; + + if (SUCCEEDED(IMFMediaType_GetBlob(decoder->stream_type, &MF_MT_MINIMUM_DISPLAY_APERTURE, (BYTE *)&aperture, sizeof(aperture), &value))) { if (FAILED(hr = IMFVideoMediaType_SetBlob(video_type, &MF_MT_MINIMUM_DISPLAY_APERTURE, From aa2e8a5bd5df8af10be1d0a060864e2c717b76a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 23 Jan 2024 12:00:01 +0100 Subject: [PATCH 080/413] winegstreamer: Use GUID arrays for H264 decoder media types. CW-Bug-Id: #21953 --- dlls/winegstreamer/h264_decoder.c | 108 ++++++++++++++++++------------ 1 file changed, 67 insertions(+), 41 deletions(-) diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index db36af36a20..a08dd320b57 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -33,14 +33,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(mfplat); WINE_DECLARE_DEBUG_CHANNEL(winediag); -#define ALIGN_SIZE(size, alignment) (((size) + (alignment)) & ~((alignment))) - -static const GUID *const h264_decoder_input_types[] = -{ - &MFVideoFormat_H264, - &MFVideoFormat_H264_ES, -}; -static const GUID *const h264_decoder_output_types[] = +static const GUID *const video_decoder_output_types[] = { &MFVideoFormat_NV12, &MFVideoFormat_YV12, @@ -57,6 +50,11 @@ struct h264_decoder IMFAttributes *attributes; IMFAttributes *output_attributes; + UINT input_type_count; + const GUID *const *input_types; + UINT output_type_count; + const GUID *const *output_types; + UINT64 sample_time; IMFMediaType *input_type; MFT_INPUT_STREAM_INFO input_info; @@ -374,12 +372,14 @@ static HRESULT WINAPI transform_AddInputStreams(IMFTransform *iface, DWORD strea static HRESULT WINAPI transform_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, IMFMediaType **type) { + struct h264_decoder *decoder = impl_from_IMFTransform(iface); + TRACE("iface %p, id %#lx, index %#lx, type %p.\n", iface, id, index, type); *type = NULL; - if (index >= ARRAY_SIZE(h264_decoder_input_types)) + if (index >= decoder->input_type_count) return MF_E_NO_MORE_TYPES; - return MFCreateVideoMediaTypeFromSubtype(h264_decoder_input_types[index], (IMFVideoMediaType **)type); + return MFCreateVideoMediaTypeFromSubtype(decoder->input_types[index], (IMFVideoMediaType **)type); } static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWORD id, @@ -392,9 +392,9 @@ static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWOR *type = NULL; if (!decoder->input_type) return MF_E_TRANSFORM_TYPE_NOT_SET; - if (index >= ARRAY_SIZE(h264_decoder_output_types)) + if (index >= decoder->output_type_count) return MF_E_NO_MORE_TYPES; - return create_output_media_type(decoder, h264_decoder_output_types[index], type); + return create_output_media_type(decoder, decoder->output_types[index], type); } static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) @@ -414,10 +414,10 @@ static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFM if (!IsEqualGUID(&major, &MFMediaType_Video)) return MF_E_INVALIDMEDIATYPE; - for (i = 0; i < ARRAY_SIZE(h264_decoder_input_types); ++i) - if (IsEqualGUID(&subtype, h264_decoder_input_types[i])) + for (i = 0; i < decoder->input_type_count; ++i) + if (IsEqualGUID(&subtype, decoder->input_types[i])) break; - if (i == ARRAY_SIZE(h264_decoder_input_types)) + if (i == decoder->input_type_count) return MF_E_INVALIDMEDIATYPE; if (flags & MFT_SET_TYPE_TEST_ONLY) return S_OK; @@ -462,10 +462,10 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF if (!IsEqualGUID(&major, &MFMediaType_Video)) return MF_E_INVALIDMEDIATYPE; - for (i = 0; i < ARRAY_SIZE(h264_decoder_output_types); ++i) - if (IsEqualGUID(&subtype, h264_decoder_output_types[i])) + for (i = 0; i < decoder->output_type_count; ++i) + if (IsEqualGUID(&subtype, decoder->output_types[i])) break; - if (i == ARRAY_SIZE(h264_decoder_output_types)) + if (i == decoder->output_type_count) return MF_E_INVALIDMEDIATYPE; if (FAILED(hr = IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size))) @@ -777,39 +777,23 @@ static const IMFTransformVtbl transform_vtbl = transform_ProcessOutput, }; -HRESULT h264_decoder_create(REFIID riid, void **ret) +static HRESULT video_decoder_create_with_types(const GUID *const *input_types, UINT input_type_count, + const GUID *const *output_types, UINT output_type_count, IMFTransform **ret) { - static const struct wg_format output_format = - { - .major_type = WG_MAJOR_TYPE_VIDEO, - .u.video = - { - .format = WG_VIDEO_FORMAT_I420, - .width = 1920, - .height = 1080, - }, - }; - static const struct wg_format input_format = {.major_type = WG_MAJOR_TYPE_VIDEO_H264}; - struct wg_transform_attrs attrs = {0}; - wg_transform_t transform; struct h264_decoder *decoder; HRESULT hr; - TRACE("riid %s, ret %p.\n", debugstr_guid(riid), ret); - - if (!(transform = wg_transform_create(&input_format, &output_format, &attrs))) - { - ERR_(winediag)("GStreamer doesn't support H.264 decoding, please install appropriate plugins\n"); - return E_FAIL; - } - wg_transform_destroy(transform); - if (!(decoder = calloc(1, sizeof(*decoder)))) return E_OUTOFMEMORY; decoder->IMFTransform_iface.lpVtbl = &transform_vtbl; decoder->refcount = 1; + decoder->input_type_count = input_type_count; + decoder->input_types = input_types; + decoder->output_type_count = output_type_count; + decoder->output_types = output_types; + decoder->input_info.dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_INPUT_STREAM_FIXED_SAMPLE_SIZE; decoder->input_info.cbSize = 0x1000; @@ -861,3 +845,45 @@ HRESULT h264_decoder_create(REFIID riid, void **ret) free(decoder); return hr; } + +static const GUID *const h264_decoder_input_types[] = +{ + &MFVideoFormat_H264, + &MFVideoFormat_H264_ES, +}; + +HRESULT h264_decoder_create(REFIID riid, void **out) +{ + static const struct wg_format output_format = + { + .major_type = WG_MAJOR_TYPE_VIDEO, + .u.video = + { + .format = WG_VIDEO_FORMAT_I420, + .width = 1920, + .height = 1080, + }, + }; + static const struct wg_format input_format = {.major_type = WG_MAJOR_TYPE_VIDEO_H264}; + struct wg_transform_attrs attrs = {0}; + wg_transform_t transform; + IMFTransform *iface; + HRESULT hr; + + TRACE("riid %s, out %p.\n", debugstr_guid(riid), out); + + if (!(transform = wg_transform_create(&input_format, &output_format, &attrs))) + { + ERR_(winediag)("GStreamer doesn't support H.264 decoding, please install appropriate plugins\n"); + return E_FAIL; + } + wg_transform_destroy(transform); + + if (FAILED(hr = video_decoder_create_with_types(h264_decoder_input_types, ARRAY_SIZE(h264_decoder_input_types), + video_decoder_output_types, ARRAY_SIZE(video_decoder_output_types), &iface))) + return hr; + + hr = IMFTransform_QueryInterface(iface, riid, out); + IMFTransform_Release(iface); + return hr; +} From 941f9bfb32b565e55a8df29d8919185d31d5916b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 23 Jan 2024 12:09:57 +0100 Subject: [PATCH 081/413] winegstreamer: Use MFCalculateImageSize to compute output info size. CW-Bug-Id: #21953 --- dlls/winegstreamer/h264_decoder.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index a08dd320b57..66396660a05 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -436,7 +436,7 @@ static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFM { if (FAILED(hr = IMFMediaType_SetUINT64(decoder->stream_type, &MF_MT_FRAME_SIZE, frame_size))) WARN("Failed to update stream type frame size, hr %#lx\n", hr); - decoder->output_info.cbSize = (frame_size >> 32) * (UINT32)frame_size * 2; + MFCalculateImageSize(decoder->output_types[0], frame_size >> 32, frame_size, (UINT32 *)&decoder->output_info.cbSize); } return S_OK; @@ -642,6 +642,7 @@ static HRESULT output_sample(struct h264_decoder *decoder, IMFSample **out, IMFS static HRESULT handle_stream_type_change(struct h264_decoder *decoder, const struct wg_format *format) { UINT64 frame_size, frame_rate; + GUID subtype; HRESULT hr; if (decoder->stream_type) @@ -655,7 +656,9 @@ static HRESULT handle_stream_type_change(struct h264_decoder *decoder, const str if (FAILED(hr = IMFMediaType_GetUINT64(decoder->stream_type, &MF_MT_FRAME_SIZE, &frame_size))) return hr; - decoder->output_info.cbSize = (frame_size >> 32) * (UINT32)frame_size * 2; + if (FAILED(hr = IMFMediaType_GetGUID(decoder->stream_type, &MF_MT_SUBTYPE, &subtype))) + return hr; + MFCalculateImageSize(&subtype, frame_size >> 32, frame_size, (UINT32 *)&decoder->output_info.cbSize); uninit_allocator(decoder); return MF_E_TRANSFORM_STREAM_CHANGE; From 65a943ad1417604956189932d97f8f79e8eae32d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 17 Nov 2023 00:08:42 +0100 Subject: [PATCH 082/413] ir50_32: Use the proper hr value for stream format change. CW-Bug-Id: #21953 --- dlls/ir50_32/ir50.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/ir50_32/ir50.c b/dlls/ir50_32/ir50.c index 65c93f7fe5e..69700359e99 100644 --- a/dlls/ir50_32/ir50.c +++ b/dlls/ir50_32/ir50.c @@ -252,7 +252,7 @@ static LRESULT IV50_Decompress( IMFTransform *decoder, ICDECOMPRESS *icd, DWORD mft_buf.pSample = out_sample; hr = IMFTransform_ProcessOutput( decoder, 0, 1, &mft_buf, &mft_status ); - if ( SUCCEEDED(hr) && (mft_status & MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE) ) + if ( hr == MF_E_TRANSFORM_STREAM_CHANGE ) hr = IMFTransform_ProcessOutput( decoder, 0, 1, &mft_buf, &mft_status ); if ( SUCCEEDED(hr) ) From 7016f597f3f559ea026eaa9fc6dd07ff899a9406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 23 Jan 2024 12:11:54 +0100 Subject: [PATCH 083/413] winegstreamer: Use the H264 decoder to implement the IV50 decoder. CW-Bug-Id: #21953 --- dlls/winegstreamer/Makefile.in | 1 - dlls/winegstreamer/h264_decoder.c | 35 +- dlls/winegstreamer/video_decoder.c | 502 ----------------------------- 3 files changed, 34 insertions(+), 504 deletions(-) delete mode 100644 dlls/winegstreamer/video_decoder.c diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 22238b3b765..942d6569f28 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -20,7 +20,6 @@ SOURCES = \ resampler.c \ rsrc.rc \ unixlib.c \ - video_decoder.c \ video_processor.c \ wg_allocator.c \ wg_format.c \ diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index 66396660a05..8953a61b73a 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -1,6 +1,7 @@ -/* H264 Decoder Transform +/* Generic Video Decoder Transform * * Copyright 2022 Rémi Bernon for CodeWeavers + * Copyright 2023 Shaun Ren for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -115,7 +116,10 @@ static HRESULT try_create_wg_transform(struct h264_decoder *decoder) } if (!(decoder->wg_transform = wg_transform_create(&input_format, &output_format, &attrs))) + { + ERR("Failed to create transform with input major_type %u.\n", input_format.major_type); return E_FAIL; + } return S_OK; } @@ -890,3 +894,32 @@ HRESULT h264_decoder_create(REFIID riid, void **out) IMFTransform_Release(iface); return hr; } + +extern GUID MFVideoFormat_IV50; +static const GUID *const iv50_decoder_input_types[] = +{ + &MFVideoFormat_IV50, +}; +static const GUID *const iv50_decoder_output_types[] = +{ + &MFVideoFormat_YV12, + &MFVideoFormat_YUY2, + &MFVideoFormat_NV11, + &MFVideoFormat_NV12, + &MFVideoFormat_RGB32, + &MFVideoFormat_RGB24, + &MFVideoFormat_RGB565, + &MFVideoFormat_RGB555, + &MFVideoFormat_RGB8, +}; + +HRESULT WINAPI winegstreamer_create_video_decoder(IMFTransform **out) +{ + TRACE("out %p.\n", out); + + if (!init_gstreamer()) + return E_FAIL; + + return video_decoder_create_with_types(iv50_decoder_input_types, ARRAY_SIZE(iv50_decoder_input_types), + iv50_decoder_output_types, ARRAY_SIZE(iv50_decoder_output_types), out); +} diff --git a/dlls/winegstreamer/video_decoder.c b/dlls/winegstreamer/video_decoder.c deleted file mode 100644 index f24c25e03f2..00000000000 --- a/dlls/winegstreamer/video_decoder.c +++ /dev/null @@ -1,502 +0,0 @@ -/* Generic Video Decoder Transform - * - * Copyright 2022 Rémi Bernon for CodeWeavers - * Copyright 2023 Shaun Ren 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 - */ - -#include "gst_private.h" - -#include "mfapi.h" -#include "mferror.h" -#include "mfobjects.h" -#include "mftransform.h" - -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(mfplat); - -DEFINE_MEDIATYPE_GUID(MFVideoFormat_IV50, MAKEFOURCC('I','V','5','0')); - -static const GUID *const input_types[] = -{ - &MFVideoFormat_IV50, -}; -static const GUID *const output_types[] = -{ - &MFVideoFormat_YV12, - &MFVideoFormat_YUY2, - &MFVideoFormat_NV11, - &MFVideoFormat_NV12, - &MFVideoFormat_RGB32, - &MFVideoFormat_RGB24, - &MFVideoFormat_RGB565, - &MFVideoFormat_RGB555, - &MFVideoFormat_RGB8, -}; - -struct video_decoder -{ - IMFTransform IMFTransform_iface; - LONG refcount; - - IMFMediaType *input_type; - IMFMediaType *output_type; - - struct wg_format wg_format; - wg_transform_t wg_transform; - struct wg_sample_queue *wg_sample_queue; -}; - -static struct video_decoder *impl_from_IMFTransform(IMFTransform *iface) -{ - return CONTAINING_RECORD(iface, struct video_decoder, IMFTransform_iface); -} - -static HRESULT try_create_wg_transform(struct video_decoder *decoder) -{ - struct wg_transform_attrs attrs = {0}; - struct wg_format input_format; - struct wg_format output_format; - - if (decoder->wg_transform) - wg_transform_destroy(decoder->wg_transform); - decoder->wg_transform = 0; - - 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, &attrs))) - { - ERR("Failed to create transform with input major_type %u.\n", input_format.major_type); - return E_FAIL; - } - - return S_OK; -} - -static HRESULT WINAPI transform_QueryInterface(IMFTransform *iface, REFIID iid, void **out) -{ - struct video_decoder *decoder = impl_from_IMFTransform(iface); - - TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); - - if (IsEqualGUID(iid, &IID_IUnknown) || - IsEqualGUID(iid, &IID_IMFTransform)) - *out = &decoder->IMFTransform_iface; - else - { - *out = NULL; - WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); - return E_NOINTERFACE; - } - - IUnknown_AddRef((IUnknown *)*out); - return S_OK; -} - -static ULONG WINAPI transform_AddRef(IMFTransform *iface) -{ - struct video_decoder *decoder = impl_from_IMFTransform(iface); - ULONG refcount = InterlockedIncrement(&decoder->refcount); - - TRACE("iface %p increasing refcount to %lu.\n", decoder, refcount); - - return refcount; -} - -static ULONG WINAPI transform_Release(IMFTransform *iface) -{ - struct video_decoder *decoder = impl_from_IMFTransform(iface); - ULONG refcount = InterlockedDecrement(&decoder->refcount); - - TRACE("iface %p decreasing refcount to %lu.\n", decoder, refcount); - - 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) - IMFMediaType_Release(decoder->output_type); - - wg_sample_queue_destroy(decoder->wg_sample_queue); - free(decoder); - } - - return refcount; -} - -static HRESULT WINAPI transform_GetStreamLimits(IMFTransform *iface, DWORD *input_minimum, - DWORD *input_maximum, DWORD *output_minimum, DWORD *output_maximum) -{ - FIXME("iface %p, input_minimum %p, input_maximum %p, output_minimum %p, output_maximum %p.\n", - iface, input_minimum, input_maximum, output_minimum, output_maximum); - return E_NOTIMPL; -} - -static HRESULT WINAPI transform_GetStreamCount(IMFTransform *iface, DWORD *inputs, DWORD *outputs) -{ - FIXME("iface %p, inputs %p, outputs %p.\n", iface, inputs, outputs); - return E_NOTIMPL; -} - -static HRESULT WINAPI transform_GetStreamIDs(IMFTransform *iface, DWORD input_size, DWORD *inputs, - DWORD output_size, DWORD *outputs) -{ - FIXME("iface %p, input_size %lu, inputs %p, output_size %lu, outputs %p.\n", - iface, input_size, inputs, output_size, outputs); - return E_NOTIMPL; -} - -static HRESULT WINAPI transform_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info) -{ - FIXME("iface %p, id %#lx, info %p.\n", iface, id, info); - return E_NOTIMPL; -} - -static HRESULT WINAPI transform_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info) -{ - FIXME("iface %p, id %#lx, info %p.\n", iface, id, info); - return E_NOTIMPL; -} - -static HRESULT WINAPI transform_GetAttributes(IMFTransform *iface, IMFAttributes **attributes) -{ - FIXME("iface %p, attributes %p semi-stub!\n", iface, attributes); - return E_NOTIMPL; -} - -static HRESULT WINAPI transform_GetInputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes) -{ - FIXME("iface %p, id %#lx, attributes %p.\n", iface, id, attributes); - return E_NOTIMPL; -} - -static HRESULT WINAPI transform_GetOutputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes) -{ - FIXME("iface %p, id %#lx, attributes %p stub!\n", iface, id, attributes); - return E_NOTIMPL; -} - -static HRESULT WINAPI transform_DeleteInputStream(IMFTransform *iface, DWORD id) -{ - FIXME("iface %p, id %#lx.\n", iface, id); - return E_NOTIMPL; -} - -static HRESULT WINAPI transform_AddInputStreams(IMFTransform *iface, DWORD streams, DWORD *ids) -{ - FIXME("iface %p, streams %lu, ids %p.\n", iface, streams, ids); - return E_NOTIMPL; -} - -static HRESULT WINAPI transform_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, - IMFMediaType **type) -{ - FIXME("iface %p, id %#lx, index %#lx, type %p.\n", iface, id, index, type); - return E_NOTIMPL; -} - -static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWORD id, - DWORD index, IMFMediaType **type) -{ - FIXME("iface %p, id %#lx, index %#lx, type %p.\n", iface, id, index, type); - return E_NOTIMPL; -} - -static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) -{ - struct video_decoder *decoder = impl_from_IMFTransform(iface); - GUID major, subtype; - UINT64 frame_size; - HRESULT hr; - ULONG i; - - TRACE("iface %p, id %#lx, type %p, flags %#lx.\n", iface, id, type, flags); - - if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major)) || - FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype))) - return E_INVALIDARG; - - if (!IsEqualGUID(&major, &MFMediaType_Video)) - return MF_E_INVALIDMEDIATYPE; - - for (i = 0; i < ARRAY_SIZE(input_types); ++i) - if (IsEqualGUID(&subtype, input_types[i])) - break; - if (i == ARRAY_SIZE(input_types)) - return MF_E_INVALIDMEDIATYPE; - - if (FAILED(hr = IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size)) || - (frame_size >> 32) == 0 || (UINT32)frame_size == 0) - return MF_E_INVALIDMEDIATYPE; - - if (flags & MFT_SET_TYPE_TEST_ONLY) - return S_OK; - - if (decoder->output_type) - { - IMFMediaType_Release(decoder->output_type); - decoder->output_type = NULL; - } - - if (decoder->input_type) - IMFMediaType_Release(decoder->input_type); - IMFMediaType_AddRef((decoder->input_type = type)); - - return S_OK; -} - -static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) -{ - struct video_decoder *decoder = impl_from_IMFTransform(iface); - GUID major, subtype; - UINT64 frame_size; - struct wg_format output_format; - HRESULT hr; - ULONG i; - - TRACE("iface %p, id %#lx, type %p, flags %#lx.\n", iface, id, type, flags); - - if (!decoder->input_type) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major)) || - FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype))) - return hr; - - if (!IsEqualGUID(&major, &MFMediaType_Video)) - return MF_E_INVALIDMEDIATYPE; - - for (i = 0; i < ARRAY_SIZE(output_types); ++i) - if (IsEqualGUID(&subtype, output_types[i])) - break; - if (i == ARRAY_SIZE(output_types)) - return MF_E_INVALIDMEDIATYPE; - - if (FAILED(hr = IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size))) - return hr; - - if (flags & MFT_SET_TYPE_TEST_ONLY) - return S_OK; - - if (decoder->output_type) - IMFMediaType_Release(decoder->output_type); - IMFMediaType_AddRef((decoder->output_type = type)); - - if (decoder->wg_transform) - { - mf_media_type_to_wg_format(decoder->output_type, &output_format); - - 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; - return MF_E_INVALIDMEDIATYPE; - } - } - else if (FAILED(hr = try_create_wg_transform(decoder))) - { - IMFMediaType_Release(decoder->output_type); - decoder->output_type = NULL; - return hr; - } - - decoder->wg_format.u.video.width = frame_size >> 32; - decoder->wg_format.u.video.height = (UINT32)frame_size; - - return hr; -} - -static HRESULT WINAPI transform_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) -{ - FIXME("iface %p, id %#lx, type %p stub!\n", iface, id, type); - return E_NOTIMPL; -} - -static HRESULT WINAPI transform_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) -{ - FIXME("iface %p, id %#lx, type %p stub!\n", iface, id, type); - return E_NOTIMPL; -} - -static HRESULT WINAPI transform_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags) -{ - FIXME("iface %p, id %#lx, flags %p stub!\n", iface, id, flags); - return E_NOTIMPL; -} - -static HRESULT WINAPI transform_GetOutputStatus(IMFTransform *iface, DWORD *flags) -{ - FIXME("iface %p, flags %p stub!\n", iface, flags); - return E_NOTIMPL; -} - -static HRESULT WINAPI transform_SetOutputBounds(IMFTransform *iface, LONGLONG lower, LONGLONG upper) -{ - TRACE("iface %p, lower %I64d, upper %I64d.\n", iface, lower, upper); - return E_NOTIMPL; -} - -static HRESULT WINAPI transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFMediaEvent *event) -{ - FIXME("iface %p, id %#lx, event %p stub!\n", iface, id, event); - return E_NOTIMPL; -} - -static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) -{ - FIXME("iface %p, message %#x, param %Ix stub!\n", iface, message, param); - return S_OK; -} - -static HRESULT WINAPI transform_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags) -{ - struct video_decoder *decoder = impl_from_IMFTransform(iface); - HRESULT hr; - - TRACE("iface %p, id %#lx, sample %p, flags %#lx.\n", iface, id, sample, flags); - - if (!decoder->wg_transform) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - hr = wg_transform_push_mf(decoder->wg_transform, sample, decoder->wg_sample_queue); - - return hr; -} - -static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, - MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) -{ - struct video_decoder *decoder = impl_from_IMFTransform(iface); - struct wg_format wg_format; - UINT32 sample_size; - UINT64 frame_rate; - GUID subtype; - 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 (!decoder->wg_transform) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - *status = samples->dwStatus = 0; - if (!samples->pSample) - return E_INVALIDARG; - - if (FAILED(hr = IMFMediaType_GetGUID(decoder->output_type, &MF_MT_SUBTYPE, &subtype))) - return hr; - if (FAILED(hr = MFCalculateImageSize(&subtype, decoder->wg_format.u.video.width, - decoder->wg_format.u.video.height, &sample_size))) - return hr; - - if (SUCCEEDED(hr = wg_transform_read_mf(decoder->wg_transform, samples->pSample, - sample_size, &wg_format, &samples->dwStatus))) - wg_sample_queue_flush(decoder->wg_sample_queue, false); - - if (hr == MF_E_TRANSFORM_STREAM_CHANGE) - { - decoder->wg_format = wg_format; - - if (FAILED(hr = MFCalculateImageSize(&subtype, decoder->wg_format.u.video.width, - decoder->wg_format.u.video.height, &sample_size))) - return hr; - - /* keep the frame rate that was requested, GStreamer doesn't provide any */ - if (SUCCEEDED(IMFMediaType_GetUINT64(decoder->output_type, &MF_MT_FRAME_RATE, &frame_rate))) - { - decoder->wg_format.u.video.fps_n = frame_rate >> 32; - decoder->wg_format.u.video.fps_d = (UINT32)frame_rate; - } - - samples[0].dwStatus |= MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE; - *status |= MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE; - } - - return hr; -} - -static const IMFTransformVtbl transform_vtbl = -{ - transform_QueryInterface, - transform_AddRef, - transform_Release, - transform_GetStreamLimits, - transform_GetStreamCount, - transform_GetStreamIDs, - transform_GetInputStreamInfo, - transform_GetOutputStreamInfo, - transform_GetAttributes, - transform_GetInputStreamAttributes, - transform_GetOutputStreamAttributes, - transform_DeleteInputStream, - transform_AddInputStreams, - transform_GetInputAvailableType, - transform_GetOutputAvailableType, - transform_SetInputType, - transform_SetOutputType, - transform_GetInputCurrentType, - transform_GetOutputCurrentType, - transform_GetInputStatus, - transform_GetOutputStatus, - transform_SetOutputBounds, - transform_ProcessEvent, - transform_ProcessMessage, - transform_ProcessInput, - transform_ProcessOutput, -}; - -HRESULT WINAPI winegstreamer_create_video_decoder(IMFTransform **out) -{ - struct video_decoder *decoder; - HRESULT hr; - - TRACE("out %p.\n", out); - - if (!init_gstreamer()) - return E_FAIL; - - if (!(decoder = calloc(1, sizeof(*decoder)))) - return E_OUTOFMEMORY; - - decoder->IMFTransform_iface.lpVtbl = &transform_vtbl; - decoder->refcount = 1; - - decoder->wg_format.u.video.fps_d = 1; - decoder->wg_format.u.video.fps_n = 1; - - if (FAILED(hr = wg_sample_queue_create(&decoder->wg_sample_queue))) - goto failed; - - *out = &decoder->IMFTransform_iface; - TRACE("created decoder %p.\n", *out); - return S_OK; - -failed: - free(decoder); - return hr; -} From 68e8fc1dbb4a1af903b120c884b361c39f99d219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 23 Jan 2024 12:14:03 +0100 Subject: [PATCH 084/413] winegstreamer: Rename struct h264_decoder to struct video_decoder. CW-Bug-Id: #21953 --- dlls/winegstreamer/Makefile.in | 2 +- .../{h264_decoder.c => video_decoder.c} | 54 +++++++++---------- 2 files changed, 28 insertions(+), 28 deletions(-) rename dlls/winegstreamer/{h264_decoder.c => video_decoder.c} (94%) diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 942d6569f28..3f143e355c5 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -9,7 +9,6 @@ UNIX_LIBS = $(GSTREAMER_LIBS) $(PTHREAD_LIBS) SOURCES = \ audio_decoder.c \ color_convert.c \ - h264_decoder.c \ main.c \ media_sink.c \ media_source.c \ @@ -20,6 +19,7 @@ SOURCES = \ resampler.c \ rsrc.rc \ unixlib.c \ + video_decoder.c \ video_processor.c \ wg_allocator.c \ wg_format.c \ diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/video_decoder.c similarity index 94% rename from dlls/winegstreamer/h264_decoder.c rename to dlls/winegstreamer/video_decoder.c index 8953a61b73a..3f72a3df944 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/video_decoder.c @@ -43,7 +43,7 @@ static const GUID *const video_decoder_output_types[] = &MFVideoFormat_YUY2, }; -struct h264_decoder +struct video_decoder { IMFTransform IMFTransform_iface; LONG refcount; @@ -72,12 +72,12 @@ struct h264_decoder IMFMediaBuffer *temp_buffer; }; -static struct h264_decoder *impl_from_IMFTransform(IMFTransform *iface) +static struct video_decoder *impl_from_IMFTransform(IMFTransform *iface) { - return CONTAINING_RECORD(iface, struct h264_decoder, IMFTransform_iface); + return CONTAINING_RECORD(iface, struct video_decoder, IMFTransform_iface); } -static HRESULT try_create_wg_transform(struct h264_decoder *decoder) +static HRESULT try_create_wg_transform(struct video_decoder *decoder) { /* Call of Duty: Black Ops 3 doesn't care about the ProcessInput/ProcessOutput * return values, it calls them in a specific order and expects the decoder @@ -124,7 +124,7 @@ static HRESULT try_create_wg_transform(struct h264_decoder *decoder) return S_OK; } -static HRESULT create_output_media_type(struct h264_decoder *decoder, const GUID *subtype, +static HRESULT create_output_media_type(struct video_decoder *decoder, const GUID *subtype, IMFMediaType **media_type) { IMFMediaType *default_type = decoder->output_type; @@ -198,7 +198,7 @@ static HRESULT create_output_media_type(struct h264_decoder *decoder, const GUID return hr; } -static HRESULT init_allocator(struct h264_decoder *decoder) +static HRESULT init_allocator(struct video_decoder *decoder) { HRESULT hr; @@ -217,7 +217,7 @@ static HRESULT init_allocator(struct h264_decoder *decoder) return S_OK; } -static void uninit_allocator(struct h264_decoder *decoder) +static void uninit_allocator(struct video_decoder *decoder) { IMFVideoSampleAllocatorEx_UninitializeSampleAllocator(decoder->allocator); decoder->allocator_initialized = FALSE; @@ -225,7 +225,7 @@ static void uninit_allocator(struct h264_decoder *decoder) static HRESULT WINAPI transform_QueryInterface(IMFTransform *iface, REFIID iid, void **out) { - struct h264_decoder *decoder = impl_from_IMFTransform(iface); + struct video_decoder *decoder = impl_from_IMFTransform(iface); TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); @@ -245,7 +245,7 @@ static HRESULT WINAPI transform_QueryInterface(IMFTransform *iface, REFIID iid, static ULONG WINAPI transform_AddRef(IMFTransform *iface) { - struct h264_decoder *decoder = impl_from_IMFTransform(iface); + struct video_decoder *decoder = impl_from_IMFTransform(iface); ULONG refcount = InterlockedIncrement(&decoder->refcount); TRACE("iface %p increasing refcount to %lu.\n", decoder, refcount); @@ -255,7 +255,7 @@ static ULONG WINAPI transform_AddRef(IMFTransform *iface) static ULONG WINAPI transform_Release(IMFTransform *iface) { - struct h264_decoder *decoder = impl_from_IMFTransform(iface); + struct video_decoder *decoder = impl_from_IMFTransform(iface); ULONG refcount = InterlockedDecrement(&decoder->refcount); TRACE("iface %p decreasing refcount to %lu.\n", decoder, refcount); @@ -309,7 +309,7 @@ static HRESULT WINAPI transform_GetStreamIDs(IMFTransform *iface, DWORD input_si static HRESULT WINAPI transform_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info) { - struct h264_decoder *decoder = impl_from_IMFTransform(iface); + struct video_decoder *decoder = impl_from_IMFTransform(iface); TRACE("iface %p, id %#lx, info %p.\n", iface, id, info); @@ -319,7 +319,7 @@ static HRESULT WINAPI transform_GetInputStreamInfo(IMFTransform *iface, DWORD id static HRESULT WINAPI transform_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info) { - struct h264_decoder *decoder = impl_from_IMFTransform(iface); + struct video_decoder *decoder = impl_from_IMFTransform(iface); TRACE("iface %p, id %#lx, info %p.\n", iface, id, info); @@ -329,7 +329,7 @@ static HRESULT WINAPI transform_GetOutputStreamInfo(IMFTransform *iface, DWORD i static HRESULT WINAPI transform_GetAttributes(IMFTransform *iface, IMFAttributes **attributes) { - struct h264_decoder *decoder = impl_from_IMFTransform(iface); + struct video_decoder *decoder = impl_from_IMFTransform(iface); FIXME("iface %p, attributes %p semi-stub!\n", iface, attributes); @@ -348,7 +348,7 @@ static HRESULT WINAPI transform_GetInputStreamAttributes(IMFTransform *iface, DW static HRESULT WINAPI transform_GetOutputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes) { - struct h264_decoder *decoder = impl_from_IMFTransform(iface); + struct video_decoder *decoder = impl_from_IMFTransform(iface); FIXME("iface %p, id %#lx, attributes %p semi-stub!\n", iface, id, attributes); @@ -376,7 +376,7 @@ static HRESULT WINAPI transform_AddInputStreams(IMFTransform *iface, DWORD strea static HRESULT WINAPI transform_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, IMFMediaType **type) { - struct h264_decoder *decoder = impl_from_IMFTransform(iface); + struct video_decoder *decoder = impl_from_IMFTransform(iface); TRACE("iface %p, id %#lx, index %#lx, type %p.\n", iface, id, index, type); @@ -389,7 +389,7 @@ static HRESULT WINAPI transform_GetInputAvailableType(IMFTransform *iface, DWORD static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWORD id, DWORD index, IMFMediaType **type) { - struct h264_decoder *decoder = impl_from_IMFTransform(iface); + struct video_decoder *decoder = impl_from_IMFTransform(iface); TRACE("iface %p, id %#lx, index %#lx, type %p.\n", iface, id, index, type); @@ -403,7 +403,7 @@ static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWOR static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) { - struct h264_decoder *decoder = impl_from_IMFTransform(iface); + struct video_decoder *decoder = impl_from_IMFTransform(iface); GUID major, subtype; UINT64 frame_size; HRESULT hr; @@ -448,7 +448,7 @@ static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFM static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) { - struct h264_decoder *decoder = impl_from_IMFTransform(iface); + struct video_decoder *decoder = impl_from_IMFTransform(iface); UINT64 frame_size, stream_frame_size; GUID major, subtype; HRESULT hr; @@ -508,7 +508,7 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF static HRESULT WINAPI transform_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) { - struct h264_decoder *decoder = impl_from_IMFTransform(iface); + struct video_decoder *decoder = impl_from_IMFTransform(iface); HRESULT hr; TRACE("iface %p, id %#lx, type %p\n", iface, id, type); @@ -524,7 +524,7 @@ static HRESULT WINAPI transform_GetInputCurrentType(IMFTransform *iface, DWORD i static HRESULT WINAPI transform_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) { - struct h264_decoder *decoder = impl_from_IMFTransform(iface); + struct video_decoder *decoder = impl_from_IMFTransform(iface); HRESULT hr; TRACE("iface %p, id %#lx, type %p\n", iface, id, type); @@ -540,7 +540,7 @@ static HRESULT WINAPI transform_GetOutputCurrentType(IMFTransform *iface, DWORD static HRESULT WINAPI transform_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags) { - struct h264_decoder *decoder = impl_from_IMFTransform(iface); + struct video_decoder *decoder = impl_from_IMFTransform(iface); TRACE("iface %p, id %#lx, flags %p.\n", iface, id, flags); @@ -571,7 +571,7 @@ static HRESULT WINAPI transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFM static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) { - struct h264_decoder *decoder = impl_from_IMFTransform(iface); + struct video_decoder *decoder = impl_from_IMFTransform(iface); HRESULT hr; TRACE("iface %p, message %#x, param %Ix.\n", iface, message, param); @@ -603,7 +603,7 @@ static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_ static HRESULT WINAPI transform_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags) { - struct h264_decoder *decoder = impl_from_IMFTransform(iface); + struct video_decoder *decoder = impl_from_IMFTransform(iface); TRACE("iface %p, id %#lx, sample %p, flags %#lx.\n", iface, id, sample, flags); @@ -613,7 +613,7 @@ static HRESULT WINAPI transform_ProcessInput(IMFTransform *iface, DWORD id, IMFS return wg_transform_push_mf(decoder->wg_transform, sample, decoder->wg_sample_queue); } -static HRESULT output_sample(struct h264_decoder *decoder, IMFSample **out, IMFSample *src_sample) +static HRESULT output_sample(struct video_decoder *decoder, IMFSample **out, IMFSample *src_sample) { MFT_OUTPUT_DATA_BUFFER output[1]; IMFSample *sample; @@ -643,7 +643,7 @@ static HRESULT output_sample(struct h264_decoder *decoder, IMFSample **out, IMFS return S_OK; } -static HRESULT handle_stream_type_change(struct h264_decoder *decoder, const struct wg_format *format) +static HRESULT handle_stream_type_change(struct video_decoder *decoder, const struct wg_format *format) { UINT64 frame_size, frame_rate; GUID subtype; @@ -671,7 +671,7 @@ static HRESULT handle_stream_type_change(struct h264_decoder *decoder, const str static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) { - struct h264_decoder *decoder = impl_from_IMFTransform(iface); + struct video_decoder *decoder = impl_from_IMFTransform(iface); struct wg_format wg_format; UINT32 sample_size; LONGLONG duration; @@ -787,7 +787,7 @@ static const IMFTransformVtbl transform_vtbl = static HRESULT video_decoder_create_with_types(const GUID *const *input_types, UINT input_type_count, const GUID *const *output_types, UINT output_type_count, IMFTransform **ret) { - struct h264_decoder *decoder; + struct video_decoder *decoder; HRESULT hr; if (!(decoder = calloc(1, sizeof(*decoder)))) From 048ce8dbaf12f86970ea1572fdc6ff7af4494494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 23 Jan 2024 13:51:11 +0100 Subject: [PATCH 085/413] winegstreamer: Ignore input / output fps mismatch in video_processor. CW-Bug-Id: #21953 --- dlls/winegstreamer/video_processor.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dlls/winegstreamer/video_processor.c b/dlls/winegstreamer/video_processor.c index 3075bf997d2..1cbb37dafc7 100644 --- a/dlls/winegstreamer/video_processor.c +++ b/dlls/winegstreamer/video_processor.c @@ -102,6 +102,13 @@ static HRESULT try_create_wg_transform(struct video_processor *impl) if (output_format.major_type == WG_MAJOR_TYPE_UNKNOWN) return MF_E_INVALIDMEDIATYPE; + /* prevent fps differences from failing to connect the elements */ + if (output_format.u.video.fps_d || output_format.u.video.fps_n) + { + input_format.u.video.fps_d = output_format.u.video.fps_d; + input_format.u.video.fps_n = output_format.u.video.fps_n; + } + if (!(impl->wg_transform = wg_transform_create(&input_format, &output_format, &attrs))) return E_FAIL; From f9af12ccc1b2e4a9a0192d0c14dca409ac36f38c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 23 Jan 2024 12:14:49 +0100 Subject: [PATCH 086/413] winegstreamer: Expose the generic video decoder transform. CW-Bug-Id: #21953 --- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/mfplat.c | 30 ++++++++++++++++++++ dlls/winegstreamer/video_decoder.c | 21 ++++++++++++++ dlls/winegstreamer/winegstreamer_classes.idl | 6 ++++ 4 files changed, 58 insertions(+) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index c894fd0b1ae..bce9b5d2fe4 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -189,6 +189,7 @@ unsigned int wg_format_get_stride(const struct wg_format *format); bool wg_video_format_is_rgb(enum wg_video_format format); HRESULT audio_decoder_create(REFIID riid, void **ret); +HRESULT video_decoder_create(REFIID riid, void **ret); HRESULT aac_decoder_create(REFIID riid, void **ret); HRESULT h264_decoder_create(REFIID riid, void **ret); HRESULT video_processor_create(REFIID riid, void **ret); diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index ea72539d1b6..1e5b7a38f66 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -129,6 +129,7 @@ static const IClassFactoryVtbl class_factory_vtbl = static const GUID CLSID_GStreamerByteStreamHandler = {0x317df618, 0x5e5a, 0x468a, {0x9f, 0x15, 0xd8, 0x27, 0xa9, 0xa0, 0x81, 0x62}}; static const GUID CLSID_GStreamerByteStreamHandler2 = {0x317df619, 0x5e5a, 0x468a, {0x9f, 0x15, 0xd8, 0x27, 0xa9, 0xa0, 0x81, 0x62}}; static const GUID CLSID_GStreamerAudioDecoder = {0x480b1517, 0xc8e9, 0x4eae, {0xb0, 0x06, 0xe6, 0x30, 0x07, 0x18, 0xd8, 0x5d}}; +static const GUID CLSID_GStreamerVideoDecoder = {0x480b1518, 0xc8e9, 0x4eae, {0xb0, 0x06, 0xe6, 0x30, 0x07, 0x18, 0xd8, 0x5d}}; static const GUID CLSID_GStreamerSchemePlugin = {0x587eeb6a,0x7336,0x4ebd,{0xa4,0xf2,0x91,0xc9,0x48,0xde,0x62,0x2c}}; @@ -140,6 +141,7 @@ static const struct class_object class_objects[] = { { &CLSID_GStreamerAudioDecoder, &audio_decoder_create }, + { &CLSID_GStreamerVideoDecoder, &video_decoder_create }, { &CLSID_VideoProcessorMFT, &video_processor_create }, { &CLSID_GStreamerByteStreamHandler, &gstreamer_byte_stream_handler_create }, { &CLSID_GStreamerByteStreamHandler2, &gstreamer_byte_stream_handler_2_create }, @@ -351,6 +353,24 @@ HRESULT mfplat_DllRegisterServer(void) {MFMediaType_Audio, MFAudioFormat_PCM}, }; + MFT_REGISTER_TYPE_INFO video_decoder_input_types[] = + { + {MFMediaType_Video, MFVideoFormat_GStreamer}, + {MFMediaType_Video, MFVideoFormat_IV50}, + }; + MFT_REGISTER_TYPE_INFO video_decoder_output_types[] = + { + {MFMediaType_Video, MFVideoFormat_YV12}, + {MFMediaType_Video, MFVideoFormat_YUY2}, + {MFMediaType_Video, MFVideoFormat_NV11}, + {MFMediaType_Video, MFVideoFormat_NV12}, + {MFMediaType_Video, MFVideoFormat_RGB32}, + {MFMediaType_Video, MFVideoFormat_RGB24}, + {MFMediaType_Video, MFVideoFormat_RGB565}, + {MFMediaType_Video, MFVideoFormat_RGB555}, + {MFMediaType_Video, MFVideoFormat_RGB8}, + }; + struct mft { GUID clsid; @@ -444,6 +464,16 @@ HRESULT mfplat_DllRegisterServer(void) ARRAY_SIZE(audio_decoder_output_types), audio_decoder_output_types, }, + { + CLSID_GStreamerVideoDecoder, + MFT_CATEGORY_VIDEO_DECODER, + L"Wine Video Decoder MFT", + MFT_ENUM_FLAG_SYNCMFT, + ARRAY_SIZE(video_decoder_input_types), + video_decoder_input_types, + ARRAY_SIZE(video_decoder_output_types), + video_decoder_output_types, + }, }; unsigned int i; diff --git a/dlls/winegstreamer/video_decoder.c b/dlls/winegstreamer/video_decoder.c index 3f72a3df944..f75fb51ca84 100644 --- a/dlls/winegstreamer/video_decoder.c +++ b/dlls/winegstreamer/video_decoder.c @@ -34,6 +34,11 @@ WINE_DEFAULT_DEBUG_CHANNEL(mfplat); WINE_DECLARE_DEBUG_CHANNEL(winediag); +extern GUID MFVideoFormat_GStreamer; +static const GUID *const video_decoder_input_types[] = +{ + &MFVideoFormat_GStreamer, +}; static const GUID *const video_decoder_output_types[] = { &MFVideoFormat_NV12, @@ -853,6 +858,22 @@ static HRESULT video_decoder_create_with_types(const GUID *const *input_types, U return hr; } +HRESULT video_decoder_create(REFIID riid, void **out) +{ + IMFTransform *iface; + HRESULT hr; + + TRACE("riid %s, out %p.\n", debugstr_guid(riid), out); + + if (FAILED(hr = video_decoder_create_with_types(video_decoder_input_types, ARRAY_SIZE(video_decoder_input_types), + video_decoder_output_types, ARRAY_SIZE(video_decoder_output_types), &iface))) + return hr; + + hr = IMFTransform_QueryInterface(iface, riid, out); + IMFTransform_Release(iface); + return hr; +} + static const GUID *const h264_decoder_input_types[] = { &MFVideoFormat_H264, diff --git a/dlls/winegstreamer/winegstreamer_classes.idl b/dlls/winegstreamer/winegstreamer_classes.idl index 2ef6d994274..dc56ad2b0ab 100644 --- a/dlls/winegstreamer/winegstreamer_classes.idl +++ b/dlls/winegstreamer/winegstreamer_classes.idl @@ -95,6 +95,12 @@ coclass GStreamerByteStreamHandler2 {} ] coclass GStreamerAudioDecoder {} +[ + threading(both), + uuid(480b1518-c8e9-4eae-b006-e6300718d85d) +] +coclass GStreamerVideoDecoder {} + [ threading(both), uuid(2eeb4adf-4578-4d10-bca7-bb955f56320a) From a5db407994651f5b7d7b92146aef4342f7fe81a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 23 Jan 2024 11:21:21 +0100 Subject: [PATCH 087/413] HACK: mfplat: Enable the new media source for some games. CW-Bug-Id: #21953 --- dlls/mfplat/main.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index e545fd5b999..facb39ee3d6 100644 --- a/dlls/mfplat/main.c +++ b/dlls/mfplat/main.c @@ -6298,7 +6298,13 @@ static HRESULT resolver_create_gstreamer_handler(IMFByteStreamHandler **handler) static const GUID CLSID_GStreamerByteStreamHandler = {0x317df618, 0x5e5a, 0x468a, {0x9f, 0x15, 0xd8, 0x27, 0xa9, 0xa0, 0x81, 0x62}}; static const GUID CLSID_GStreamerByteStreamHandler2 = {0x317df619, 0x5e5a, 0x468a, {0x9f, 0x15, 0xd8, 0x27, 0xa9, 0xa0, 0x81, 0x62}}; - const char *env = getenv("WINE_NEW_MEDIA_SOURCE"); + const char *env = getenv("WINE_NEW_MEDIA_SOURCE"), *sgi = getenv("SteamGameId"); + if (!env && sgi) + { + if (!strcmp(sgi, "399810") /* Call of Cthulhu */) env = "1"; + if (!strcmp(sgi, "606880") /* Greedfall */) env = "1"; + if (!strcmp(sgi, "692850") /* Bloodstained */) env = "1"; + } if (env && atoi(env)) return CoCreateInstance(&CLSID_GStreamerByteStreamHandler2, NULL, CLSCTX_INPROC_SERVER, &IID_IMFByteStreamHandler, (void **)handler); return CoCreateInstance(&CLSID_GStreamerByteStreamHandler, NULL, CLSCTX_INPROC_SERVER, &IID_IMFByteStreamHandler, (void **)handler); From 2921586a3f210ccfde9cc986f01faf3ae02e74ab Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 29 Nov 2023 17:02:43 -0600 Subject: [PATCH 088/413] ntdll: HACK: Try to load steamoverlay.so manually if LD_PRELOAD was lost. CW-Bug-Id: #23064 --- dlls/ntdll/unix/loader.c | 5 +++++ dlls/ntdll/unix/virtual.c | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 9dec5d15d80..b7d1f677203 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -598,6 +598,8 @@ NTSTATUS exec_wineloader( char **argv, int socketfd, const pe_image_info_t *pe_i if (pe_info->wine_fakedll) res_start = res_end = 0; if (pe_info->image_flags & IMAGE_FLAGS_ComPlusNativeReady) machine = native_machine; + unsetenv( "WINE_LD_PRELOAD" ); + /* HACK: Unset LD_PRELOAD before executing explorer.exe to disable buggy gameoverlayrenderer.so */ if (ld_preload && argv[2] && !strcmp( argv[2], "C:\\windows\\system32\\explorer.exe" ) && argv[3] && !strcmp( argv[3], "/desktop" )) @@ -626,8 +628,11 @@ NTSTATUS exec_wineloader( char **argv, int socketfd, const pe_image_info_t *pe_i while (*next); putenv( env ); + ld_preload = NULL; } + if (ld_preload) setenv( "WINE_LD_PRELOAD", ld_preload, 1 ); + signal( SIGPIPE, SIG_DFL ); snprintf( socket_env, sizeof(socket_env), "WINESERVERSOCKET=%u", socketfd ); diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 81b7a97ef9f..824ba75d21b 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -889,6 +889,30 @@ void *get_builtin_so_handle( void *module ) return ret; } +static void load_steam_overlay(const char *unix_lib_path) +{ + const char *preload, *p; + char path[PATH_MAX]; + unsigned int len; + void *handle; + + if (!strstr(unix_lib_path, "winex11.so")) return; + if (getenv("LD_PRELOAD") || !(preload = getenv("WINE_LD_PRELOAD"))) return; + + p = preload; + while (*(preload = p)) + { + p = strchrnul( preload, ':' ); + len = p - preload; + if (*p) ++p; + if (len + 1 > sizeof(path)) continue; + memcpy( path, preload, len ); + path[len] = 0; + if (!strstr( path, "gameoverlayrenderer.so" )) continue; + handle = dlopen( path, RTLD_NOW | RTLD_GLOBAL ); + FIXME( "HACK: tried to load %s, handle %p.\n", debugstr_a(path), handle ); + } +} /*********************************************************************** * get_builtin_unix_funcs @@ -905,7 +929,10 @@ static NTSTATUS get_builtin_unix_funcs( void *module, BOOL wow, const void **fun { if (builtin->module != module) continue; if (builtin->unix_path && !builtin->unix_handle) + { + load_steam_overlay(builtin->unix_path); builtin->unix_handle = dlopen( builtin->unix_path, RTLD_NOW ); + } if (builtin->unix_handle) { *funcs = dlsym( builtin->unix_handle, ptr_name ); @@ -933,7 +960,11 @@ NTSTATUS load_builtin_unixlib( void *module, const char *name ) if (builtin->module != module) continue; if (!builtin->unix_path) builtin->unix_path = strdup( name ); else status = STATUS_IMAGE_ALREADY_LOADED; - if (!builtin->unix_handle) builtin->unix_handle = dlopen( builtin->unix_path, RTLD_NOW ); + if (!builtin->unix_handle) + { + load_steam_overlay(builtin->unix_path); + builtin->unix_handle = dlopen( builtin->unix_path, RTLD_NOW ); + } break; } server_leave_uninterrupted_section( &virtual_mutex, &sigset ); From 5d27bbe628a6829262f96e851d69919b2535a27e Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Thu, 23 Nov 2023 10:25:57 +0200 Subject: [PATCH 089/413] winegstreamer: Support WMA with 5.1 and 7.1 audio. --- dlls/winegstreamer/mfplat.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 1e5b7a38f66..a4cf2dfcd99 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -749,6 +749,10 @@ static void mf_media_type_to_wg_format_audio(IMFMediaType *type, const GUID *sub channel_mask = KSAUDIO_SPEAKER_MONO; else if (channels == 2) channel_mask = KSAUDIO_SPEAKER_STEREO; + else if (channels == 6) + channel_mask = KSAUDIO_SPEAKER_5POINT1; + else if (channels == 8) + channel_mask = KSAUDIO_SPEAKER_7POINT1; else { FIXME("Channel mask is not set.\n"); From 4eb474281fad47bc3a0d11834fea1be53d9bdc14 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Fri, 29 Dec 2023 13:23:25 +0200 Subject: [PATCH 090/413] FAudio: Create fake codec data for WMA3. Already upstreamed, can be dropped the next bump. Link: https://github.com/FNA-XNA/FAudio/pull/327 --- libs/faudio/src/FAudio_platform_win32.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libs/faudio/src/FAudio_platform_win32.c b/libs/faudio/src/FAudio_platform_win32.c index c45b4a2f4f2..01a3a84d5e0 100644 --- a/libs/faudio/src/FAudio_platform_win32.c +++ b/libs/faudio/src/FAudio_platform_win32.c @@ -1300,6 +1300,7 @@ static void FAudio_INTERNAL_DecodeWMAMF( uint32_t FAudio_WMADEC_init(FAudioSourceVoice *voice, uint32_t type) { static const uint8_t fake_codec_data[16] = {0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t fake_codec_data_wma3[18] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 0, 0, 0}; const FAudioWaveFormatExtensible *wfx = (FAudioWaveFormatExtensible *)voice->src.format; struct FAudioWMADEC *impl; MFT_OUTPUT_STREAM_INFO info = {0}; @@ -1361,11 +1362,17 @@ uint32_t FAudio_WMADEC_init(FAudioSourceVoice *voice, uint32_t type) FAudio_assert(!FAILED(hr) && "Failed set input block align!"); break; case FAUDIO_FORMAT_WMAUDIO3: + *(uint16_t *)fake_codec_data_wma3 = voice->src.format->wBitsPerSample; + for (i = 0; i < voice->src.format->nChannels; i++) + { + fake_codec_data_wma3[2] <<= 1; + fake_codec_data_wma3[2] |= 1; + } hr = IMFMediaType_SetBlob( media_type, &MF_MT_USER_DATA, - (void *)&wfx->Samples, - wfx->Format.cbSize + (void *)fake_codec_data_wma3, + sizeof(fake_codec_data_wma3) ); FAudio_assert(!FAILED(hr) && "Failed set codec private data!"); hr = IMFMediaType_SetGUID( From 9cc829a563a7ad3cd227bf5f7be4e707c3841efd Mon Sep 17 00:00:00 2001 From: Etaash Mathamsetty Date: Sun, 10 Dec 2023 15:29:32 -0500 Subject: [PATCH 091/413] xinput: Implement XInputGetCapabilitiesEx. CW-Bug-Id: #23185 --- dlls/xinput1_3/main.c | 30 ++++++++++++++++++++++++++++++ dlls/xinput1_4/xinput1_4.spec | 1 + include/xinput.h | 11 +++++++++++ 3 files changed, 42 insertions(+) diff --git a/dlls/xinput1_3/main.c b/dlls/xinput1_3/main.c index a18f63545cc..3e6a8d0288c 100644 --- a/dlls/xinput1_3/main.c +++ b/dlls/xinput1_3/main.c @@ -1159,3 +1159,33 @@ DWORD WINAPI DECLSPEC_HOTPATCH XInputGetBatteryInformation(DWORD index, BYTE typ return ERROR_NOT_SUPPORTED; } + +DWORD WINAPI DECLSPEC_HOTPATCH XInputGetCapabilitiesEx(DWORD unk, DWORD index, DWORD flags, XINPUT_CAPABILITIES_EX *caps) +{ + DWORD ret = ERROR_SUCCESS; + HIDD_ATTRIBUTES attr; + + TRACE("unk %lu, index %lu, flags %#lx, capabilities %p.\n", unk, index, flags, caps); + + start_update_thread(); + + if (index >= XUSER_MAX_COUNT) return ERROR_BAD_ARGUMENTS; + + if (!controller_lock(&controllers[index])) return ERROR_DEVICE_NOT_CONNECTED; + + if (flags & XINPUT_FLAG_GAMEPAD && controllers[index].caps.SubType != XINPUT_DEVSUBTYPE_GAMEPAD) + ret = ERROR_DEVICE_NOT_CONNECTED; + else if (!HidD_GetAttributes(controllers[index].device, &attr)) + ret = ERROR_DEVICE_NOT_CONNECTED; + else + { + caps->Capabilities = controllers[index].caps; + caps->VendorId = attr.VendorID; + caps->ProductId = attr.ProductID; + caps->VersionNumber = attr.VersionNumber; + } + + controller_unlock(&controllers[index]); + + return ret; +} diff --git a/dlls/xinput1_4/xinput1_4.spec b/dlls/xinput1_4/xinput1_4.spec index 8c3f4c0cd73..22c511237dd 100644 --- a/dlls/xinput1_4/xinput1_4.spec +++ b/dlls/xinput1_4/xinput1_4.spec @@ -7,3 +7,4 @@ 8 stdcall XInputGetKeystroke(long long ptr) 10 stub XInputGetAudioDeviceIds(long ptr ptr ptr ptr) 100 stdcall XInputGetStateEx(long ptr) +108 stdcall XInputGetCapabilitiesEx(long long long ptr) diff --git a/include/xinput.h b/include/xinput.h index f7c291630cb..96efae1858d 100644 --- a/include/xinput.h +++ b/include/xinput.h @@ -210,6 +210,16 @@ typedef struct _XINPUT_CAPABILITIES { XINPUT_VIBRATION Vibration; } XINPUT_CAPABILITIES, *PXINPUT_CAPABILITIES; +typedef struct _XINPUT_CAPABILITIES_EX +{ + XINPUT_CAPABILITIES Capabilities; + WORD VendorId; + WORD ProductId; + WORD VersionNumber; + WORD unk1; + DWORD unk2; +} XINPUT_CAPABILITIES_EX, *PXINPUT_CAPABILITIES_EX; + /* * Defines the structure for a joystick input event which is * retrieved using the function XInputGetKeystroke @@ -237,6 +247,7 @@ DWORD WINAPI XInputSetState(DWORD, XINPUT_VIBRATION*); DWORD WINAPI XInputGetState(DWORD, XINPUT_STATE*); DWORD WINAPI XInputGetKeystroke(DWORD, DWORD, PXINPUT_KEYSTROKE); DWORD WINAPI XInputGetCapabilities(DWORD, DWORD, XINPUT_CAPABILITIES*); +DWORD WINAPI XInputGetCapabilitiesEx(DWORD, DWORD, DWORD, XINPUT_CAPABILITIES_EX*); DWORD WINAPI XInputGetDSoundAudioDeviceGuids(DWORD, GUID*, GUID*); DWORD WINAPI XInputGetBatteryInformation(DWORD, BYTE, XINPUT_BATTERY_INFORMATION*); From f1cb38f913f7de4d7300c1b0d4a0e1577814c85c Mon Sep 17 00:00:00 2001 From: Etaash Mathamsetty Date: Sun, 10 Dec 2023 15:36:26 -0500 Subject: [PATCH 092/413] xinput: Reimplement XInputGetCapabilities. CW-Bug-Id: #23185 --- dlls/xinput1_3/main.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/dlls/xinput1_3/main.c b/dlls/xinput1_3/main.c index 3e6a8d0288c..4cb9793fa30 100644 --- a/dlls/xinput1_3/main.c +++ b/dlls/xinput1_3/main.c @@ -1116,25 +1116,15 @@ DWORD WINAPI DECLSPEC_HOTPATCH XInputGetKeystroke(DWORD index, DWORD reserved, P DWORD WINAPI DECLSPEC_HOTPATCH XInputGetCapabilities(DWORD index, DWORD flags, XINPUT_CAPABILITIES *capabilities) { - TRACE("index %lu, flags %#lx, capabilities %p.\n", index, flags, capabilities); - - start_update_thread(); - - if (index >= XUSER_MAX_COUNT) return ERROR_BAD_ARGUMENTS; - - if (!controller_lock(&controllers[index])) return ERROR_DEVICE_NOT_CONNECTED; - - if (flags & XINPUT_FLAG_GAMEPAD && controllers[index].caps.SubType != XINPUT_DEVSUBTYPE_GAMEPAD) - { - controller_unlock(&controllers[index]); - return ERROR_DEVICE_NOT_CONNECTED; - } + XINPUT_CAPABILITIES_EX caps_ex; + DWORD ret; - memcpy(capabilities, &controllers[index].caps, sizeof(*capabilities)); + TRACE("index %lu, flags %#lx, capabilities %p.\n", index, flags, capabilities); - controller_unlock(&controllers[index]); + ret = XInputGetCapabilitiesEx(0, index, flags, &caps_ex); + if (!ret) *capabilities = caps_ex.Capabilities; - return ERROR_SUCCESS; + return ret; } DWORD WINAPI DECLSPEC_HOTPATCH XInputGetDSoundAudioDeviceGuids(DWORD index, GUID *render_guid, GUID *capture_guid) From 9b1f439b8e4393c282431bb30fb9efc6f19cce64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 21 Feb 2022 15:18:15 +0100 Subject: [PATCH 093/413] windows.gaming.input: Stub IRawGameController2 interface. CW-Bug-Id: #23185 --- dlls/windows.gaming.input/controller.c | 60 ++++++++++++++++++++++++++ include/windows.devices.haptics.idl | 3 ++ 2 files changed, 63 insertions(+) diff --git a/dlls/windows.gaming.input/controller.c b/dlls/windows.gaming.input/controller.c index bd3d441c445..e1749a3032d 100644 --- a/dlls/windows.gaming.input/controller.c +++ b/dlls/windows.gaming.input/controller.c @@ -61,6 +61,7 @@ struct controller IGameControllerImpl IGameControllerImpl_iface; IGameControllerInputSink IGameControllerInputSink_iface; IRawGameController IRawGameController_iface; + IRawGameController2 IRawGameController2_iface; IGameController *IGameController_outer; LONG ref; @@ -99,6 +100,12 @@ static HRESULT WINAPI controller_QueryInterface( IGameControllerImpl *iface, REF return S_OK; } + if (IsEqualGUID( iid, &IID_IRawGameController2 )) + { + IInspectable_AddRef( (*out = &impl->IRawGameController2_iface) ); + return S_OK; + } + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); *out = NULL; return E_NOINTERFACE; @@ -330,6 +337,58 @@ static const struct IRawGameControllerVtbl raw_controller_vtbl = raw_controller_GetSwitchKind, }; +DEFINE_IINSPECTABLE_OUTER( raw_controller_2, IRawGameController2, struct controller, IGameController_outer ) + +static HRESULT WINAPI raw_controller_2_get_SimpleHapticsControllers( IRawGameController2 *iface, IVectorView_SimpleHapticsController** value) +{ + static const struct vector_iids iids = + { + .vector = &IID_IVector_SimpleHapticsController, + .view = &IID_IVectorView_SimpleHapticsController, + .iterable = &IID_IIterable_SimpleHapticsController, + .iterator = &IID_IIterator_SimpleHapticsController, + }; + IVector_SimpleHapticsController *vector; + HRESULT hr; + + FIXME( "iface %p, value %p stub!\n", iface, value ); + + if (SUCCEEDED(hr = vector_create( &iids, (void **)&vector ))) + { + hr = IVector_SimpleHapticsController_GetView( vector, value ); + IVector_SimpleHapticsController_Release( vector ); + } + + return hr; +} + +static HRESULT WINAPI raw_controller_2_get_NonRoamableId( IRawGameController2 *iface, HSTRING* value ) +{ + FIXME( "iface %p, value %p stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI raw_controller_2_get_DisplayName( IRawGameController2 *iface, HSTRING* value ) +{ + FIXME( "iface %p, value %p stub!\n", iface, value ); + return E_NOTIMPL; +} + +static const struct IRawGameController2Vtbl raw_controller_2_vtbl = +{ + raw_controller_2_QueryInterface, + raw_controller_2_AddRef, + raw_controller_2_Release, + /* IInspectable methods */ + raw_controller_2_GetIids, + raw_controller_2_GetRuntimeClassName, + raw_controller_2_GetTrustLevel, + /* IRawGameController2 methods */ + raw_controller_2_get_SimpleHapticsControllers, + raw_controller_2_get_NonRoamableId, + raw_controller_2_get_DisplayName, +}; + struct controller_statics { IActivationFactory IActivationFactory_iface; @@ -525,6 +584,7 @@ static HRESULT WINAPI controller_factory_CreateGameController( ICustomGameContro impl->IGameControllerImpl_iface.lpVtbl = &controller_vtbl; impl->IGameControllerInputSink_iface.lpVtbl = &input_sink_vtbl; impl->IRawGameController_iface.lpVtbl = &raw_controller_vtbl; + impl->IRawGameController2_iface.lpVtbl = &raw_controller_2_vtbl; impl->ref = 1; TRACE( "created RawGameController %p\n", impl ); diff --git a/include/windows.devices.haptics.idl b/include/windows.devices.haptics.idl index c056ab4941c..506525a6d3c 100644 --- a/include/windows.devices.haptics.idl +++ b/include/windows.devices.haptics.idl @@ -30,7 +30,10 @@ namespace Windows.Devices.Haptics { runtimeclass SimpleHapticsController; declare { + interface Windows.Foundation.Collections.IIterator; + interface Windows.Foundation.Collections.IIterable; interface Windows.Foundation.Collections.IVectorView; + interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; } From ebf155aa02ca34a49a8251b66edb06f470e662a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 8 Jan 2024 14:35:42 +0100 Subject: [PATCH 094/413] windows.gaming.input: Forward get_NonRoamableId to Wine provider. CW-Bug-Id: #23185 --- dlls/windows.gaming.input/controller.c | 4 ++-- dlls/windows.gaming.input/provider.c | 7 +++++++ dlls/windows.gaming.input/provider.idl | 2 ++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/dlls/windows.gaming.input/controller.c b/dlls/windows.gaming.input/controller.c index e1749a3032d..1adbc5cce0b 100644 --- a/dlls/windows.gaming.input/controller.c +++ b/dlls/windows.gaming.input/controller.c @@ -364,8 +364,8 @@ static HRESULT WINAPI raw_controller_2_get_SimpleHapticsControllers( IRawGameCon static HRESULT WINAPI raw_controller_2_get_NonRoamableId( IRawGameController2 *iface, HSTRING* value ) { - FIXME( "iface %p, value %p stub!\n", iface, value ); - return E_NOTIMPL; + struct controller *impl = impl_from_IRawGameController2( iface ); + return IWineGameControllerProvider_get_NonRoamableId( impl->wine_provider, value ); } static HRESULT WINAPI raw_controller_2_get_DisplayName( IRawGameController2 *iface, HSTRING* value ) diff --git a/dlls/windows.gaming.input/provider.c b/dlls/windows.gaming.input/provider.c index d0472727224..bb84a45b874 100644 --- a/dlls/windows.gaming.input/provider.c +++ b/dlls/windows.gaming.input/provider.c @@ -148,6 +148,12 @@ static BOOL CALLBACK count_ffb_axes( const DIDEVICEOBJECTINSTANCEW *obj, void *a return DIENUM_CONTINUE; } +static HRESULT WINAPI wine_provider_get_NonRoamableId( IWineGameControllerProvider *iface, HSTRING *value ) +{ + FIXME( "iface %p, value %p stub!\n", iface, value ); + return E_NOTIMPL; +} + static HRESULT WINAPI wine_provider_get_Type( IWineGameControllerProvider *iface, WineGameControllerType *value ) { struct provider *impl = impl_from_IWineGameControllerProvider( iface ); @@ -356,6 +362,7 @@ static const struct IWineGameControllerProviderVtbl wine_provider_vtbl = wine_provider_GetRuntimeClassName, wine_provider_GetTrustLevel, /* IWineGameControllerProvider methods */ + wine_provider_get_NonRoamableId, wine_provider_get_Type, wine_provider_get_AxisCount, wine_provider_get_ButtonCount, diff --git a/dlls/windows.gaming.input/provider.idl b/dlls/windows.gaming.input/provider.idl index e7b6e96b8aa..a6fcc6e84f3 100644 --- a/dlls/windows.gaming.input/provider.idl +++ b/dlls/windows.gaming.input/provider.idl @@ -173,6 +173,8 @@ namespace Windows.Gaming.Input.Custom { interface IWineGameControllerProvider : IInspectable requires Windows.Gaming.Input.Custom.IGameControllerProvider { + [propget] HRESULT NonRoamableId([out, retval] HSTRING *value); + [propget] HRESULT Type([out, retval] WineGameControllerType *value); [propget] HRESULT AxisCount([out, retval] INT32 *value); [propget] HRESULT ButtonCount([out, retval] INT32 *value); From 85e20d984222a9593ad1b450ed844ce985c1e483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 8 Jan 2024 14:35:42 +0100 Subject: [PATCH 095/413] HACK: dinput: Emulate Steam Input native hooks. CW-Bug-Id: #23185 --- dlls/dinput/joystick_hid.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index b5fc76b9222..a28c3ba39e7 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -864,6 +864,16 @@ static HRESULT hid_joystick_get_property( IDirectInputDevice8W *iface, DWORD pro { DIPROPGUIDANDPATH *value = (DIPROPGUIDANDPATH *)header; value->guidClass = GUID_DEVCLASS_HIDCLASS; + + /* CW-Bug-Id: #23185 Emulate Steam Input native hooks for native SDL */ + if (impl->attrs.VendorID == 0x28de && impl->attrs.ProductID == 0x11ff) + { + const WCHAR *tmp; + if ((tmp = wcschr( impl->device_path, '#' ))) tmp = wcschr( tmp + 1, '#' ); + lstrcpynW( value->wszPath, impl->device_path, tmp - impl->device_path + 1 ); + return DI_OK; + } + lstrcpynW( value->wszPath, impl->device_path, MAX_PATH ); return DI_OK; } From ea4f8fe7cc524f53bb3838e2ec17c86c8f5ac44b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 8 Jan 2024 14:35:42 +0100 Subject: [PATCH 096/413] HACK: hidclass: Emulate Steam Input native hooks. CW-Bug-Id: #23185 --- dlls/hidclass.sys/device.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/hidclass.sys/device.c b/dlls/hidclass.sys/device.c index 7d1b9d2c356..135e58bf587 100644 --- a/dlls/hidclass.sys/device.c +++ b/dlls/hidclass.sys/device.c @@ -390,6 +390,8 @@ struct device_strings static const struct device_strings device_strings[] = { + /* CW-Bug-Id: #23185 Emulate Steam Input native hooks for native SDL */ + { .id = L"VID_28DE&PID_11FF", .product = L"Controller (XBOX 360 For Windows)" }, /* Microsoft controllers */ { .id = L"VID_045E&PID_028E", .product = L"Controller (XBOX 360 For Windows)" }, { .id = L"VID_045E&PID_028F", .product = L"Controller (XBOX 360 For Windows)" }, From 12b71526a2523792269b011f6b268b13c428c65c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 8 Jan 2024 14:35:42 +0100 Subject: [PATCH 097/413] HACK: ntdll: Emulate Steam Input native hooks. CW-Bug-Id: #23185 --- dlls/ntdll/unix/file.c | 68 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index a117b08010c..e5ea71fae80 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -2844,6 +2844,67 @@ static NTSTATUS find_file_in_dir( char *unix_name, int pos, const WCHAR *name, i return STATUS_OBJECT_NAME_NOT_FOUND; } +/* CW-Bug-Id: #23185 Emulate Steam Input native hooks for native SDL */ +static BOOL replace_steam_input_path( OBJECT_ATTRIBUTES *attr, UNICODE_STRING *redir ) +{ + static const WCHAR pipe_prefixW[] = + { + '\\','?','?','\\','p','i','p','e','\\','H','I','D','#','V','I','D','_','0','4','5','E', + '&','P','I','D','_','0','2','8','E','&','I','G','_','0','0', + }; + static const WCHAR hid_prefixW[] = + { + '\\','?','?','\\','h','i','d','#','v','i','d','_','2','8','d','e', + '&','p','i','d','_','1','1','f','f','&','i','g','_','0' + }; + static const WCHAR hid_midW[] = + { + '#','0', + }; + static const WCHAR hid_tailW[] = + { + '&','0','&','0','&','1','#','{','4','d','1','e','5','5','b','2','-','f','1','6','f','-', + '1','1','c','f','-','8','8','c','b','-','0','0','1','1','1','1','0','0','0','0','3','0','}' + }; + UNICODE_STRING *path = attr->ObjectName; + const WCHAR *slot = NULL, *slot_end = NULL, *serial, *serial_end = NULL; + UINT len = 0; + + if (!path || !path->Buffer || path->Length <= sizeof(pipe_prefixW)) return FALSE; + if (wcsnicmp( path->Buffer, pipe_prefixW, ARRAY_SIZE(pipe_prefixW) )) return FALSE; + + serial = path->Buffer + path->Length / sizeof(WCHAR); + while (serial > path->Buffer && *serial != '&') + { + if (*serial == '#') + { + slot_end = serial_end; + serial_end = serial; + slot = serial_end + 1; + } + serial--; + } + if (serial == path->Buffer || *serial != '&' || !slot_end || !serial_end) return FALSE; + + redir->Length = sizeof(hid_prefixW) + sizeof(hid_midW) + sizeof(hid_tailW); + redir->Length += (serial_end - serial + slot_end - slot) * sizeof(WCHAR); + redir->MaximumLength = redir->Length + sizeof(WCHAR); + if (!(redir->Buffer = malloc( redir->MaximumLength ))) return FALSE; + + memcpy( redir->Buffer, hid_prefixW, sizeof(hid_prefixW) ); + len += ARRAY_SIZE(hid_prefixW); + memcpy( redir->Buffer + len, slot, (slot_end - slot) * sizeof(WCHAR) ); + len += slot_end - slot; + memcpy( redir->Buffer + len, hid_midW, sizeof(hid_midW) ); + len += ARRAY_SIZE(hid_midW); + memcpy( redir->Buffer + len, serial, (serial_end - serial) * sizeof(WCHAR) ); + len += serial_end - serial; + memcpy( redir->Buffer + len, hid_tailW, sizeof(hid_tailW) ); + + TRACE( "HACK: %s -> %s\n", debugstr_us(attr->ObjectName), debugstr_us(redir) ); + attr->ObjectName = redir; + return TRUE; +} #ifndef _WIN64 @@ -2945,6 +3006,10 @@ BOOL get_redirect( OBJECT_ATTRIBUTES *attr, UNICODE_STRING *redir ) unsigned int i, prefix_len = 0, len = attr->ObjectName->Length / sizeof(WCHAR); redir->Buffer = NULL; + + /* CW-Bug-Id: #23185 Emulate Steam Input native hooks for native SDL */ + if (replace_steam_input_path( attr, redir )) return TRUE; + if (!NtCurrentTeb64()) return FALSE; if (!len) return FALSE; @@ -3004,7 +3069,8 @@ BOOL get_redirect( OBJECT_ATTRIBUTES *attr, UNICODE_STRING *redir ) BOOL get_redirect( OBJECT_ATTRIBUTES *attr, UNICODE_STRING *redir ) { redir->Buffer = NULL; - return FALSE; + /* CW-Bug-Id: #23185 Emulate Steam Input native hooks for native SDL */ + return replace_steam_input_path( attr, redir ); } #endif From 843daccf372b6b20af74a11e9950768fbbf6a455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 8 Jan 2024 14:35:42 +0100 Subject: [PATCH 098/413] HACK: win32u: Emulate Steam Input native hooks. CW-Bug-Id: #23185 --- dlls/win32u/rawinput.c | 43 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/dlls/win32u/rawinput.c b/dlls/win32u/rawinput.c index e5eab049fa3..c0e64c7dc3a 100644 --- a/dlls/win32u/rawinput.c +++ b/dlls/win32u/rawinput.c @@ -568,10 +568,49 @@ UINT WINAPI NtUserGetRawInputDeviceInfo( HANDLE handle, UINT command, void *data switch (command) { case RIDI_DEVICENAME: - if ((len = wcslen( device->path ) + 1) <= data_len && data) - memcpy( data, device->path, len * sizeof(WCHAR) ); + { + static const WCHAR steam_input_idW[] = + { + '\\','\\','?','\\','H','I','D','#','V','I','D','_','2','8','D','E','&','P','I','D','_','1','1','F','F','&','I','G','_',0 + }; + const WCHAR *device_path; + WCHAR bufferW[MAX_PATH]; + + /* CW-Bug-Id: #23185 Emulate Steam Input native hooks for native SDL */ + if (wcsnicmp( device->path, steam_input_idW, 29 )) device_path = device->path; + else + { + char buffer[MAX_PATH]; + UINT size = 0, slot; + const WCHAR *tmpW; + UINT16 vid, pid; + + tmpW = device->path + 29; + while (*tmpW && *tmpW != '#' && size < ARRAY_SIZE(buffer)) buffer[size++] = *tmpW++; + buffer[size] = 0; + if (sscanf( buffer, "%02u", &slot ) != 1) slot = 0; + + /* FIXME: Return the actual underlying device VID / PID somehow */ + vid = 0x045e; + pid = 0x028e; + + size = snprintf( buffer, ARRAY_SIZE(buffer), "\\\\.\\pipe\\HID#VID_045E&PID_028E&IG_00#%04X&%04X", vid, pid ); + if ((tmpW = wcschr( device->path + 29, '&' ))) + { + do buffer[size++] = *tmpW++; + while (*tmpW != '&' && size < ARRAY_SIZE(buffer)); + } + size += snprintf( buffer + size, ARRAY_SIZE(buffer) - size, "#%d#%u", slot, (UINT)GetCurrentProcessId() ); + + ntdll_umbstowcs( buffer, size + 1, bufferW, sizeof(bufferW) ); + device_path = bufferW; + } + + if ((len = wcslen( device_path ) + 1) <= data_len && data) + memcpy( data, device_path, len * sizeof(WCHAR) ); *data_size = len; break; + } case RIDI_DEVICEINFO: if ((len = sizeof(info)) <= data_len && data) From c450588c3f834849ccd546ee3f10e20bdd21f760 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 8 Jan 2024 14:35:42 +0100 Subject: [PATCH 099/413] HACK: windows.gaming.input: Emulate Steam Input native hooks. CW-Bug-Id: #23185 --- dlls/windows.gaming.input/provider.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/dlls/windows.gaming.input/provider.c b/dlls/windows.gaming.input/provider.c index bb84a45b874..9bbf42cdf0a 100644 --- a/dlls/windows.gaming.input/provider.c +++ b/dlls/windows.gaming.input/provider.c @@ -150,7 +150,34 @@ static BOOL CALLBACK count_ffb_axes( const DIDEVICEOBJECTINSTANCEW *obj, void *a static HRESULT WINAPI wine_provider_get_NonRoamableId( IWineGameControllerProvider *iface, HSTRING *value ) { + struct provider *impl = impl_from_IWineGameControllerProvider( iface ); + WCHAR buffer[1024]; + UINT16 vid, pid; + HRESULT hr; + FIXME( "iface %p, value %p stub!\n", iface, value ); + + if (FAILED(hr = IGameControllerProvider_get_HardwareVendorId( &impl->IGameControllerProvider_iface, &vid ))) return hr; + if (FAILED(hr = IGameControllerProvider_get_HardwareProductId( &impl->IGameControllerProvider_iface, &pid ))) return hr; + + /* CW-Bug-Id: #23185 Emulate Steam Input native hooks for native SDL */ + if (vid == 0x28de && pid == 0x11ff) + { + const WCHAR *tmp; + char serial[256]; + UINT32 len, slot; + + if (!(tmp = wcschr( impl->device_path + 8, '#' )) || wcsnicmp( tmp - 6, L"&XI_", 4 )) return E_NOTIMPL; + if (swscanf( tmp - 2, L"%02u#%*u&%255[^&]&", &slot, serial ) != 2) return E_NOTIMPL; + + /* FIXME: Return the actual underlying device VID / PID somehow */ + vid = 0x045e; + pid = 0x028e; + + len = swprintf( buffer, ARRAY_SIZE(buffer), L"{wgi/nrid/:steam-%04X&%04X&%s#%d#%u}", vid, pid, serial, slot, GetCurrentProcessId() ); + return WindowsCreateString( buffer, len, value ); + } + return E_NOTIMPL; } From f7b348a4a57475c075783e9dc38850302e5008c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 8 Jan 2024 14:35:42 +0100 Subject: [PATCH 100/413] HACK: xinput: Emulate Steam Input native hooks. CW-Bug-Id: #23185 --- dlls/xinput1_3/main.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/dlls/xinput1_3/main.c b/dlls/xinput1_3/main.c index 4cb9793fa30..594010f8770 100644 --- a/dlls/xinput1_3/main.c +++ b/dlls/xinput1_3/main.c @@ -519,6 +519,16 @@ static BOOL find_opened_device(const WCHAR *device_path, int *free_slot) *free_slot = i - 1; } + /* CW-Bug-Id: #23185 Emulate Steam Input native hooks for native SDL */ + if ((swscanf(device_path, L"\\\\?\\hid#vid_28de&pid_11ff&xi_%02u#", &i) == 1 || + swscanf(device_path, L"\\\\?\\HID#VID_28DE&PID_11FF&XI_%02u#", &i) == 1) && + i < XUSER_MAX_COUNT && *free_slot != i) + { + controller_destroy(&controllers[i], TRUE); + if (*free_slot != XUSER_MAX_COUNT) open_device_at_index(controllers[i].device_path, *free_slot); + *free_slot = i; + } + return FALSE; } @@ -1173,6 +1183,8 @@ DWORD WINAPI DECLSPEC_HOTPATCH XInputGetCapabilitiesEx(DWORD unk, DWORD index, D caps->VendorId = attr.VendorID; caps->ProductId = attr.ProductID; caps->VersionNumber = attr.VersionNumber; + /* CW-Bug-Id: #23185 Emulate Steam Input native hooks for native SDL */ + caps->unk2 = index; } controller_unlock(&controllers[index]); From 2b117ec184cc5ea1f6232ccae8ab0f77b0b1cf45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 8 Jan 2024 14:35:42 +0100 Subject: [PATCH 101/413] HACK: winebus: Expose virtual controller for Steam Input hooks emulation. Guarded behind a PROTON_EXPOSE_STEAM_CONTROLLER=1 variable. CW-Bug-Id: #23185 --- dlls/winebus.sys/bus_sdl.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index 7b51ae1a84b..f37c2593f2b 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -964,7 +964,8 @@ static void sdl_add_device(unsigned int index) SDL_JoystickID id; SDL_JoystickType joystick_type; SDL_GameController *controller = NULL; - const char *product, *sdl_serial, *str; + const char *product, *sdl_serial, *str, *env; + BOOL expose_steam_controller = (env = getenv("PROTON_EXPOSE_STEAM_CONTROLLER")) && atoi(env) == 1; char guid_str[33], buffer[ARRAY_SIZE(desc.product)]; int axis_count, axis_offset; @@ -1013,7 +1014,7 @@ static void sdl_add_device(unsigned int index) return; } - if (desc.vid == 0x28de && desc.pid == 0x11ff) + if (desc.vid == 0x28de && desc.pid == 0x11ff && !expose_steam_controller) { TRACE("Steam virtual controller, pretending it's an Xbox 360 controller\n"); desc.vid = 0x045e; @@ -1021,8 +1022,12 @@ static void sdl_add_device(unsigned int index) } /* CW-Bug-Id: #20528 Check steam virtual controller indexes to keep them ordered */ - if ((str = pSDL_JoystickName(joystick)) && sscanf(str, "Microsoft X-Box 360 pad %u", &desc.input) == 1) desc.input++; - else desc.input = -1; + /* CW-Bug-Id: #23185 Emulate Steam Input native hooks for native SDL */ + if ((str = pSDL_JoystickName(joystick)) && sscanf(str, "Microsoft X-Box 360 pad %u", &desc.input) == 1) + { + if (!expose_steam_controller) desc.input++; + desc.version = 0; /* keep version fixed as 0 so we can hardcode it in ntdll rawinput pipe redirection */ + } if (pSDL_JoystickGetSerial && (sdl_serial = pSDL_JoystickGetSerial(joystick))) { @@ -1033,8 +1038,15 @@ static void sdl_add_device(unsigned int index) /* Overcooked! All You Can Eat only adds controllers with unique serial numbers * Prefer keeping serial numbers unique over keeping them consistent across runs */ pSDL_JoystickGetGUIDString(pSDL_JoystickGetGUID(joystick), guid_str, sizeof(guid_str)); - snprintf(buffer, sizeof(buffer), "%s.%d", guid_str, index); - TRACE("Making up serial number for %s: %s\n", product, buffer); + + /* CW-Bug-Id: #23185 Emulate Steam Input native hooks for native SDL */ + if (desc.input != -1) snprintf(buffer, sizeof(buffer), "%s", guid_str); + else + { + snprintf(buffer, sizeof(buffer), "%s.%d", guid_str, index); + TRACE("Making up serial number for %s: %s\n", product, buffer); + } + ntdll_umbstowcs(buffer, strlen(buffer) + 1, desc.serialnumber, ARRAY_SIZE(desc.serialnumber)); } From bf1051aee5af69509453dd99e894a17050fbc8fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 9 Jan 2024 14:10:03 +0100 Subject: [PATCH 102/413] HACK: win32u: Report underlying VID/PID in Steam Input hooks emulation. CW-Bug-Id: #23185 --- dlls/win32u/rawinput.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/dlls/win32u/rawinput.c b/dlls/win32u/rawinput.c index c0e64c7dc3a..3fa187f9673 100644 --- a/dlls/win32u/rawinput.c +++ b/dlls/win32u/rawinput.c @@ -531,6 +531,30 @@ UINT WINAPI NtUserGetRawInputDeviceList( RAWINPUTDEVICELIST *device_list, UINT * return count; } +static BOOL steam_input_get_vid_pid( UINT slot, UINT16 *vid, UINT16 *pid ) +{ + const char *info = getenv( "SteamVirtualGamepadInfo" ); + char buffer[256]; + UINT current; + FILE *file; + + TRACE( "reading SteamVirtualGamepadInfo %s\n", debugstr_a(info) ); + + if (!info || !(file = fopen( info, "r" ))) return FALSE; + while (fscanf( file, "%255[^\n]\n", buffer ) == 1) + { + if (sscanf( buffer, "[slot %d]", ¤t )) continue; + if (current < slot) continue; + if (current > slot) break; + if (sscanf( buffer, "VID=0x%hx", vid )) continue; + if (sscanf( buffer, "PID=0x%hx", pid )) continue; + } + + fclose( file ); + + return TRUE; +} + /********************************************************************** * NtUserGetRawInputDeviceInfo (win32u.@) */ @@ -590,9 +614,11 @@ UINT WINAPI NtUserGetRawInputDeviceInfo( HANDLE handle, UINT command, void *data buffer[size] = 0; if (sscanf( buffer, "%02u", &slot ) != 1) slot = 0; - /* FIXME: Return the actual underlying device VID / PID somehow */ - vid = 0x045e; - pid = 0x028e; + if (!steam_input_get_vid_pid( slot, &vid, &pid )) + { + vid = 0x045e; + pid = 0x028e; + } size = snprintf( buffer, ARRAY_SIZE(buffer), "\\\\.\\pipe\\HID#VID_045E&PID_028E&IG_00#%04X&%04X", vid, pid ); if ((tmpW = wcschr( device->path + 29, '&' ))) From 8aa6dc06a3de9b047e0a7834e7a6742d43107cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 9 Jan 2024 14:10:03 +0100 Subject: [PATCH 103/413] HACK: windows.gaming.input: Report underlying VID/PID in Steam Input hooks emulation. CW-Bug-Id: #23185 --- dlls/windows.gaming.input/provider.c | 32 +++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/dlls/windows.gaming.input/provider.c b/dlls/windows.gaming.input/provider.c index 9bbf42cdf0a..3515d5c9ba6 100644 --- a/dlls/windows.gaming.input/provider.c +++ b/dlls/windows.gaming.input/provider.c @@ -148,6 +148,30 @@ static BOOL CALLBACK count_ffb_axes( const DIDEVICEOBJECTINSTANCEW *obj, void *a return DIENUM_CONTINUE; } +static BOOL steam_input_get_vid_pid( UINT slot, UINT16 *vid, UINT16 *pid ) +{ + const char *info = getenv( "SteamVirtualGamepadInfo" ); + char buffer[256]; + UINT current; + FILE *file; + + TRACE( "reading SteamVirtualGamepadInfo %s\n", debugstr_a(info) ); + + if (!info || !(file = fopen( info, "r" ))) return FALSE; + while (fscanf( file, "%255[^\n]\n", buffer ) == 1) + { + if (sscanf( buffer, "[slot %d]", ¤t )) continue; + if (current < slot) continue; + if (current > slot) break; + if (sscanf( buffer, "VID=0x%hx", vid )) continue; + if (sscanf( buffer, "PID=0x%hx", pid )) continue; + } + + fclose( file ); + + return TRUE; +} + static HRESULT WINAPI wine_provider_get_NonRoamableId( IWineGameControllerProvider *iface, HSTRING *value ) { struct provider *impl = impl_from_IWineGameControllerProvider( iface ); @@ -170,9 +194,11 @@ static HRESULT WINAPI wine_provider_get_NonRoamableId( IWineGameControllerProvid if (!(tmp = wcschr( impl->device_path + 8, '#' )) || wcsnicmp( tmp - 6, L"&XI_", 4 )) return E_NOTIMPL; if (swscanf( tmp - 2, L"%02u#%*u&%255[^&]&", &slot, serial ) != 2) return E_NOTIMPL; - /* FIXME: Return the actual underlying device VID / PID somehow */ - vid = 0x045e; - pid = 0x028e; + if (!steam_input_get_vid_pid( slot, &vid, &pid )) + { + vid = 0x045e; + pid = 0x028e; + } len = swprintf( buffer, ARRAY_SIZE(buffer), L"{wgi/nrid/:steam-%04X&%04X&%s#%d#%u}", vid, pid, serial, slot, GetCurrentProcessId() ); return WindowsCreateString( buffer, len, value ); From b701641592cb2f0a8f52da01ecd0866856532f7f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 23 Jan 2024 16:34:49 -0600 Subject: [PATCH 104/413] winhttp: Always return result at once if available in WinHttpQueryDataAvailable(). CW-Bug-Id: #23312 --- dlls/winhttp/request.c | 59 ++++++++++++++++++++++++++++--- dlls/winhttp/tests/notification.c | 7 +++- dlls/winhttp/winhttp_private.h | 13 +++++++ 3 files changed, 73 insertions(+), 6 deletions(-) diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index 84af8e58988..74278195fc3 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -257,6 +257,16 @@ static BOOL cancel_queue( struct queue *queue ) return cancelled; } +static void task_send_callback( void *ctx, BOOL abort ) +{ + struct send_callback *s = ctx; + + if (abort) return; + + TRACE( "running %p\n", ctx ); + send_callback( s->task_hdr.obj, s->status, s->info, s->buflen ); +} + static void free_header( struct header *header ) { free( header->field ); @@ -3052,9 +3062,10 @@ static DWORD query_data_ready( struct request *request ) return count; } -static BOOL skip_async_queue( struct request *request ) +static BOOL skip_async_queue( struct request *request, BOOL *data_available ) { - return request->hdr.recursion_count < 3 && (end_of_read_data( request ) || query_data_ready( request )); + *data_available = end_of_read_data( request ) || query_data_ready( request ); + return request->hdr.recursion_count < 3 && *data_available; } static DWORD query_data_available( struct request *request, DWORD *available, BOOL async ) @@ -3106,6 +3117,7 @@ BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available ) DWORD ret; struct request *request; BOOL async; + BOOL data_available = FALSE; TRACE("%p, %p\n", hrequest, available); @@ -3121,7 +3133,44 @@ BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available ) return FALSE; } - if ((async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) && !skip_async_queue( request )) + if (!(async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) || skip_async_queue( request, &data_available )) + { + ret = query_data_available( request, available, async ); + } + else if (data_available) + { + /* Data available but recursion limit reached, only queue callback. */ + struct send_callback *s; + + if (!(s = malloc( sizeof(*s) ))) + { + release_object( &request->hdr ); + SetLastError( ERROR_OUTOFMEMORY ); + return FALSE; + } + + if (!(ret = query_data_available( request, &s->count, FALSE ))) + { + if (available) *available = s->count; + s->status = WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE; + s->info = &s->count; + s->buflen = sizeof(s->count); + } + else + { + s->result.dwResult = API_QUERY_DATA_AVAILABLE; + s->result.dwError = ret; + s->status = WINHTTP_CALLBACK_STATUS_REQUEST_ERROR; + s->info = &s->result; + s->buflen = sizeof(s->result); + } + + if ((ret = queue_task( &request->queue, task_send_callback, &s->task_hdr, &request->hdr ))) + free( s ); + else + ret = ERROR_IO_PENDING; + } + else { struct query_data *q; @@ -3139,7 +3188,6 @@ BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available ) else ret = ERROR_IO_PENDING; } - else ret = query_data_available( request, available, async ); release_object( &request->hdr ); SetLastError( ret ); @@ -3165,6 +3213,7 @@ BOOL WINAPI WinHttpReadData( HINTERNET hrequest, void *buffer, DWORD to_read, DW DWORD ret; struct request *request; BOOL async; + BOOL data_available; TRACE( "%p, %p, %lu, %p\n", hrequest, buffer, to_read, read ); @@ -3180,7 +3229,7 @@ BOOL WINAPI WinHttpReadData( HINTERNET hrequest, void *buffer, DWORD to_read, DW return FALSE; } - if ((async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) && !skip_async_queue( request )) + if ((async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) && !skip_async_queue( request, &data_available )) { struct read_data *r; diff --git a/dlls/winhttp/tests/notification.c b/dlls/winhttp/tests/notification.c index 78319f0df5c..40db323fb5e 100644 --- a/dlls/winhttp/tests/notification.c +++ b/dlls/winhttp/tests/notification.c @@ -1863,6 +1863,7 @@ static void CALLBACK test_recursion_callback( HINTERNET handle, DWORD_PTR contex DWORD status, void *buffer, DWORD buflen ) { struct test_recursion_context *context = (struct test_recursion_context *)context_ptr; + static DWORD len; DWORD err; BOOL ret; BYTE b; @@ -1939,10 +1940,14 @@ static void CALLBACK test_recursion_callback( HINTERNET handle, DWORD_PTR contex context->max_recursion_read = max( context->max_recursion_read, context->recursion_count ); context->read_from_callback = TRUE; InterlockedIncrement( &context->recursion_count ); - ret = WinHttpQueryDataAvailable( context->request, NULL ); + len = 0xdeadbeef; + /* Use static variable len here so write to it doesn't destroy the stack on old Windows which + * doesn't set the value at once. */ + ret = WinHttpQueryDataAvailable( context->request, &len ); err = GetLastError(); ok( ret, "failed to query data available, GetLastError() %lu\n", err ); ok( err == ERROR_SUCCESS || err == ERROR_IO_PENDING, "got %lu\n", err ); + ok( len != 0xdeadbeef || broken( len == 0xdeadbeef ) /* Win7 */, "got %lu.\n", len ); if (err == ERROR_SUCCESS) context->have_sync_callback = TRUE; InterlockedDecrement( &context->recursion_count ); break; diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h index 0bf1c5bc6b0..80ce2614ca1 100644 --- a/dlls/winhttp/winhttp_private.h +++ b/dlls/winhttp/winhttp_private.h @@ -312,6 +312,19 @@ struct task_header volatile LONG completion_sent; }; +struct send_callback +{ + struct task_header task_hdr; + DWORD status; + void *info; + DWORD buflen; + union + { + WINHTTP_ASYNC_RESULT result; + DWORD count; + }; +}; + struct send_request { struct task_header task_hdr; From 6b5c14f9f4af6630e57c3c1c14e8e0b8f3045ed6 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 23 Jan 2024 16:56:09 -0600 Subject: [PATCH 105/413] winhttp: Always return result at once if available in WinHttpReadData(). CW-Bug-Id: #23312 --- dlls/winhttp/request.c | 42 ++++++++++++++++++++++++++++--- dlls/winhttp/tests/notification.c | 6 ++++- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index 74278195fc3..14e5b493f77 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -3213,7 +3213,7 @@ BOOL WINAPI WinHttpReadData( HINTERNET hrequest, void *buffer, DWORD to_read, DW DWORD ret; struct request *request; BOOL async; - BOOL data_available; + BOOL data_available = FALSE; TRACE( "%p, %p, %lu, %p\n", hrequest, buffer, to_read, read ); @@ -3229,7 +3229,44 @@ BOOL WINAPI WinHttpReadData( HINTERNET hrequest, void *buffer, DWORD to_read, DW return FALSE; } - if ((async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) && !skip_async_queue( request, &data_available )) + if (!(async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) || skip_async_queue( request, &data_available )) + { + ret = read_data( request, buffer, to_read, read, async ); + } + else if (data_available) + { + /* Data available but recursion limit reached, only queue callback. */ + struct send_callback *s; + + if (!(s = malloc( sizeof(*s) ))) + { + release_object( &request->hdr ); + SetLastError( ERROR_OUTOFMEMORY ); + return FALSE; + } + + if (!(ret = read_data( request, buffer, to_read, &s->count, FALSE ))) + { + if (read) *read = s->count; + s->status = WINHTTP_CALLBACK_STATUS_READ_COMPLETE; + s->info = buffer; + s->buflen = s->count; + } + else + { + s->result.dwResult = API_READ_DATA; + s->result.dwError = ret; + s->status = WINHTTP_CALLBACK_STATUS_REQUEST_ERROR; + s->info = &s->result; + s->buflen = sizeof(s->result); + } + + if ((ret = queue_task( &request->queue, task_send_callback, &s->task_hdr, &request->hdr ))) + free( s ); + else + ret = ERROR_IO_PENDING; + } + else { struct read_data *r; @@ -3248,7 +3285,6 @@ BOOL WINAPI WinHttpReadData( HINTERNET hrequest, void *buffer, DWORD to_read, DW else ret = ERROR_IO_PENDING; } - else ret = read_data( request, buffer, to_read, read, async ); release_object( &request->hdr ); SetLastError( ret ); diff --git a/dlls/winhttp/tests/notification.c b/dlls/winhttp/tests/notification.c index 40db323fb5e..ed70ed906f0 100644 --- a/dlls/winhttp/tests/notification.c +++ b/dlls/winhttp/tests/notification.c @@ -1921,10 +1921,14 @@ static void CALLBACK test_recursion_callback( HINTERNET handle, DWORD_PTR contex "got %lu, thread %#lx\n", context->recursion_count, GetCurrentThreadId() ); context->max_recursion_query = max( context->max_recursion_query, context->recursion_count ); InterlockedIncrement( &context->recursion_count ); - ret = WinHttpReadData( context->request, &b, 1, NULL ); + b = 0xff; + len = 0xdeadbeef; + ret = WinHttpReadData( context->request, &b, 1, &len ); err = GetLastError(); ok( ret, "failed to read data, GetLastError() %lu\n", err ); ok( err == ERROR_SUCCESS || err == ERROR_IO_PENDING, "got %lu\n", err ); + ok( b != 0xff, "got %#x.\n", b ); + ok( len == 1, "got %lu.\n", len ); if (err == ERROR_SUCCESS) context->have_sync_callback = TRUE; InterlockedDecrement( &context->recursion_count ); break; From 8a3ea5e39ea21ecf1415c2ff72ebd231f6d56414 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 23 Jan 2024 21:23:02 -0600 Subject: [PATCH 106/413] ntdll: HACK: Substitute different d3dcompiler version for BDO. CW-Bug-Id: #23314 --- dlls/ntdll/loader.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index 2a12f9f376e..8dfe4e21e10 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -3335,6 +3335,21 @@ static NTSTATUS find_dll_file( const WCHAR *load_path, const WCHAR *libname, UNI return status; } +static void substitute_dll( const WCHAR **libname ) +{ + static int substitute = -1; + + if (substitute == -1) + { + WCHAR env_str[32]; + + if ((substitute = (get_env( L"SteamGameId", env_str, sizeof(env_str)) && !wcsicmp( env_str, L"582660" )))) + FIXME( "HACK: substituting dll name.\n" ); + } + if (!substitute) return; + if (*libname && !wcsicmp( *libname, L"d3dcompiler_46.dll" )) + *libname = L"d3dcompiler_43.dll"; +} /*********************************************************************** * load_dll (internal) @@ -3353,6 +3368,8 @@ static NTSTATUS load_dll( const WCHAR *load_path, const WCHAR *libname, DWORD fl TRACE( "looking for %s in %s\n", debugstr_w(libname), debugstr_w(load_path) ); + substitute_dll( &libname ); + if (system && system_dll_path.Buffer) nts = search_dll_file( system_dll_path.Buffer, libname, &nt_name, pwm, &mapping, &image_info, &id ); From eac437e849c0aea1fc7596a29d630ff70a114609 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 6 Feb 2024 19:05:00 -0600 Subject: [PATCH 107/413] ntdll: Better track thread pool wait's wait_pending state. CW-Bug-Id: #21509 --- dlls/ntdll/threadpool.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dlls/ntdll/threadpool.c b/dlls/ntdll/threadpool.c index 4f22114a55e..9a8f380bf72 100644 --- a/dlls/ntdll/threadpool.c +++ b/dlls/ntdll/threadpool.c @@ -1265,6 +1265,7 @@ static void CALLBACK waitqueue_thread_proc( void *param ) u.wait.wait_entry ) { assert( wait->type == TP_OBJECT_TYPE_WAIT ); + assert( wait->u.wait.wait_pending ); if (wait->u.wait.timeout <= now.QuadPart) { /* Wait object timed out. */ @@ -1272,6 +1273,7 @@ static void CALLBACK waitqueue_thread_proc( void *param ) { list_remove( &wait->u.wait.wait_entry ); list_add_tail( &bucket->reserved, &wait->u.wait.wait_entry ); + wait->u.wait.wait_pending = FALSE; } if ((wait->u.wait.flags & (WT_EXECUTEINWAITTHREAD | WT_EXECUTEINIOTHREAD))) { @@ -1329,6 +1331,7 @@ static void CALLBACK waitqueue_thread_proc( void *param ) { list_remove( &wait->u.wait.wait_entry ); list_add_tail( &bucket->reserved, &wait->u.wait.wait_entry ); + wait->u.wait.wait_pending = FALSE; } if ((wait->u.wait.flags & (WT_EXECUTEINWAITTHREAD | WT_EXECUTEINIOTHREAD))) { From 1c12b171e7bba1fd9bb1524e767f92034aaacf61 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 6 Feb 2024 19:14:40 -0600 Subject: [PATCH 108/413] ntdll: Make sure wakeups from already unset events are ignored in waitqueue_thread_proc(). CW-Bug-Id: #21509 --- dlls/ntdll/threadpool.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/dlls/ntdll/threadpool.c b/dlls/ntdll/threadpool.c index 9a8f380bf72..2887e84b12c 100644 --- a/dlls/ntdll/threadpool.c +++ b/dlls/ntdll/threadpool.c @@ -160,6 +160,7 @@ struct threadpool_object LONG num_pending_callbacks; LONG num_running_callbacks; LONG num_associated_callbacks; + LONG update_serial; /* arguments for callback */ union { @@ -1243,6 +1244,7 @@ static void tp_timerqueue_unlock( struct threadpool_object *timer ) static void CALLBACK waitqueue_thread_proc( void *param ) { struct threadpool_object *objects[MAXIMUM_WAITQUEUE_OBJECTS]; + LONG update_serials[MAXIMUM_WAITQUEUE_OBJECTS]; HANDLE handles[MAXIMUM_WAITQUEUE_OBJECTS + 1]; struct waitqueue_bucket *bucket = param; struct threadpool_object *wait, *next; @@ -1295,6 +1297,7 @@ static void CALLBACK waitqueue_thread_proc( void *param ) InterlockedIncrement( &wait->refcount ); objects[num_handles] = wait; handles[num_handles] = wait->u.wait.handle; + update_serials[num_handles] = wait->update_serial; num_handles++; } } @@ -1323,7 +1326,7 @@ static void CALLBACK waitqueue_thread_proc( void *param ) { wait = objects[status - STATUS_WAIT_0]; assert( wait->type == TP_OBJECT_TYPE_WAIT ); - if (wait->u.wait.bucket) + if (wait->u.wait.bucket && wait->update_serial == update_serials[status - STATUS_WAIT_0]) { /* Wait object signaled. */ assert( wait->u.wait.bucket == bucket ); @@ -1344,7 +1347,10 @@ static void CALLBACK waitqueue_thread_proc( void *param ) else tp_object_submit( wait, TRUE ); } else - WARN("wait object %p triggered while object was destroyed\n", wait); + { + WARN("wait object %p triggered while object was %s.\n", + wait, wait->u.wait.bucket ? "updated" : "destroyed"); + } } /* Release temporary references to wait objects. */ @@ -1917,6 +1923,7 @@ static void tp_object_initialize( struct threadpool_object *object, struct threa object->num_pending_callbacks = 0; object->num_running_callbacks = 0; object->num_associated_callbacks = 0; + object->update_serial = 0; if (environment) { @@ -3051,12 +3058,15 @@ VOID WINAPI TpSetWait( TP_WAIT *wait, HANDLE handle, LARGE_INTEGER *timeout ) { struct threadpool_object *this = impl_from_TP_WAIT( wait ); ULONGLONG timestamp = MAXLONGLONG; + BOOL same_handle; TRACE( "%p %p %p\n", wait, handle, timeout ); RtlEnterCriticalSection( &waitqueue.cs ); assert( this->u.wait.bucket ); + + same_handle = this->u.wait.handle == handle; this->u.wait.handle = handle; if (handle || this->u.wait.wait_pending) @@ -3090,6 +3100,8 @@ VOID WINAPI TpSetWait( TP_WAIT *wait, HANDLE handle, LARGE_INTEGER *timeout ) } /* Wake up the wait queue thread. */ + if (!same_handle) + ++this->update_serial; NtSetEvent( bucket->update_event, NULL ); } From 410bdc4021dc1dbdc72c7a674dc36ccdf0b15c4c Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 9 Feb 2024 11:33:41 -0600 Subject: [PATCH 109/413] win32u: Send message with timeout in send_erase(). CW-Bug-Id: #23394 --- dlls/win32u/dce.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dlls/win32u/dce.c b/dlls/win32u/dce.c index d7aa75b3039..3f1e25ae9e2 100644 --- a/dlls/win32u/dce.c +++ b/dlls/win32u/dce.c @@ -1248,7 +1248,10 @@ static BOOL send_erase( HWND hwnd, UINT flags, HRGN client_rgn, { /* don't erase if the clip box is empty */ if (type != NULLREGION) - need_erase = !send_message( hwnd, WM_ERASEBKGND, (WPARAM)hdc, 0 ); + { + need_erase = !send_message_timeout( hwnd, WM_ERASEBKGND, (WPARAM)hdc, 0, SMTO_ABORTIFHUNG, 1000, FALSE ); + if (need_erase && RtlGetLastWin32Error() == ERROR_TIMEOUT) ERR( "timeout.\n" ); + } } if (!hdc_ret) release_dc( hwnd, hdc, TRUE ); } From 720804e6f65c479a1292e9a383c99b30b288d872 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 12 May 2023 13:34:23 +0200 Subject: [PATCH 110/413] winegstreamer: Introduce and use a new wg_task_pool helper. To better track GStreamer threads, avoiding potential task leaks in the default pool which keeps some thread alive. CW-Bug-Id: #22045 --- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/unix_private.h | 4 ++ dlls/winegstreamer/wg_parser.c | 28 +++++++++ dlls/winegstreamer/wg_task_pool.c | 98 +++++++++++++++++++++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 dlls/winegstreamer/wg_task_pool.c diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 3f143e355c5..9cae96c5ebc 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -27,6 +27,7 @@ SOURCES = \ wg_parser.c \ wg_sample.c \ wg_source.c \ + wg_task_pool.c \ wg_transform.c \ winegstreamer_classes.idl \ wm_reader.c \ diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index d15054661c7..a165b8f1cfe 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -93,6 +93,10 @@ extern NTSTATUS wg_muxer_push_sample(void *args); extern NTSTATUS wg_muxer_read_data(void *args); extern NTSTATUS wg_muxer_finalize(void *args); +/* wg_task_pool.c */ + +extern GstTaskPool *wg_task_pool_new(void); + /* wg_allocator.c */ static inline BYTE *wg_sample_data(struct wg_sample *sample) diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index dd555a7ce19..2d00f4247e0 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -73,6 +73,7 @@ struct wg_parser GstElement *container, *decodebin; GstBus *bus; + GstTaskPool *task_pool; GstPad *my_src; guint64 file_size, start_offset, next_offset, stop_offset; @@ -1613,6 +1614,24 @@ static GstBusSyncReply bus_handler_cb(GstBus *bus, GstMessage *msg, gpointer use } break; + case GST_MESSAGE_STREAM_STATUS: + { + GstStreamStatusType type; + GstElement *element; + const GValue *val; + GstTask *task; + + gst_message_parse_stream_status(msg, &type, &element); + val = gst_message_get_stream_status_object(msg); + GST_DEBUG("parser %p, message %s, type %u, value %p (%s).", parser, GST_MESSAGE_TYPE_NAME(msg), type, val, G_VALUE_TYPE_NAME(val)); + + if (G_VALUE_TYPE(val) == GST_TYPE_TASK && (task = g_value_get_object(val)) + && type == GST_STREAM_STATUS_TYPE_CREATE) + gst_task_set_pool(task, parser->task_pool); + + break; + } + default: break; } @@ -2011,6 +2030,7 @@ static NTSTATUS wg_parser_disconnect(void *args) parser->input_cache_chunks[i].data = NULL; } + gst_task_pool_cleanup(parser->task_pool); return S_OK; } @@ -2127,6 +2147,7 @@ static NTSTATUS wg_parser_create(void *args) struct wg_parser_create_params *params = args; struct wg_parser *parser; + GError *error; if (!(parser = calloc(1, sizeof(*parser)))) return E_OUTOFMEMORY; @@ -2140,6 +2161,12 @@ static NTSTATUS wg_parser_create(void *args) parser->use_opengl = FALSE; } } + if (!(parser->task_pool = wg_task_pool_new())) + { + free(parser); + return E_OUTOFMEMORY; + } + gst_task_pool_prepare(parser->task_pool, &error); pthread_mutex_init(&parser->mutex, NULL); pthread_cond_init(&parser->init_cond, NULL); @@ -2163,6 +2190,7 @@ static NTSTATUS wg_parser_destroy(void *args) gst_bus_set_sync_handler(parser->bus, NULL, NULL, NULL); gst_object_unref(parser->bus); } + gst_object_unref(parser->task_pool); if (parser->context) gst_context_unref(parser->context); diff --git a/dlls/winegstreamer/wg_task_pool.c b/dlls/winegstreamer/wg_task_pool.c new file mode 100644 index 00000000000..ec7da286d5f --- /dev/null +++ b/dlls/winegstreamer/wg_task_pool.c @@ -0,0 +1,98 @@ +/* + * GStreamer task pool + * + * Copyright 2023 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 +#include + +#include + +#include "unix_private.h" + +typedef struct +{ + GstTaskPool parent; +} WgTaskPool; + +typedef struct +{ + GstTaskPoolClass parent_class; +} WgTaskPoolClass; + +G_DEFINE_TYPE(WgTaskPool, wg_task_pool, GST_TYPE_TASK_POOL); + +static void wg_task_pool_prepare(GstTaskPool *pool, GError **error) +{ + GST_LOG("pool %p, error %p", pool, error); +} + +static void wg_task_pool_cleanup(GstTaskPool *pool) +{ + GST_LOG("pool %p", pool); +} + +static gpointer wg_task_pool_push(GstTaskPool *pool, GstTaskPoolFunction func, gpointer data, GError **error) +{ + pthread_t *tid; + gint res; + + GST_LOG("pool %p, func %p, data %p, error %p", pool, func, data, error); + + if (!(tid = malloc(sizeof(*tid))) || !(res = pthread_create(tid, NULL, (void *)func, data))) + return tid; + + g_set_error(error, G_THREAD_ERROR, G_THREAD_ERROR_AGAIN, "Error creating thread: %s", g_strerror(res)); + free(tid); + + return NULL; +} + +static void wg_task_pool_join(GstTaskPool *pool, gpointer id) +{ + pthread_t *tid = id; + + GST_LOG("pool %p, id %p", pool, id); + + pthread_join(*tid, NULL); + free(tid); +} + +static void wg_task_pool_class_init(WgTaskPoolClass *klass) +{ + GstTaskPoolClass *parent_class = (GstTaskPoolClass *)klass; + parent_class->prepare = wg_task_pool_prepare; + parent_class->cleanup = wg_task_pool_cleanup; + parent_class->push = wg_task_pool_push; + parent_class->join = wg_task_pool_join; +} + +static void wg_task_pool_init(WgTaskPool *pool) +{ +} + +GstTaskPool *wg_task_pool_new(void) +{ + return g_object_new(wg_task_pool_get_type(), NULL); +} From dda708023b98d5dc9347ae99689c394ea00fec37 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 20 Jan 2023 14:18:25 -0600 Subject: [PATCH 111/413] winegstreamer: Implement MFT_MESSAGE_COMMAND_DRAIN for aac decoder. CW-Bug-Id: #21804 CW-Bug-Id: #22299 --- dlls/winegstreamer/audio_decoder.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/audio_decoder.c b/dlls/winegstreamer/audio_decoder.c index 771eae465fe..cd63f116cbf 100644 --- a/dlls/winegstreamer/audio_decoder.c +++ b/dlls/winegstreamer/audio_decoder.c @@ -489,7 +489,18 @@ static HRESULT WINAPI transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFM static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) { - FIXME("iface %p, message %#x, param %p stub!\n", iface, message, (void *)param); + struct audio_decoder *decoder = impl_from_IMFTransform(iface); + + TRACE("iface %p, message %#x, param %p.\n", iface, message, (void *)param); + + if (!decoder->wg_transform) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (message == MFT_MESSAGE_COMMAND_DRAIN) + return wg_transform_drain(decoder->wg_transform); + + FIXME("Ignoring message %#x.\n", message); + return S_OK; } From 6358559557139e2fc95036d6fdeca17900247cc7 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 20 Jan 2023 14:47:47 -0600 Subject: [PATCH 112/413] winegstreamer: Implement MFT_MESSAGE_COMMAND_DRAIN for resampler. CW-Bug-Id: #21804 CW-Bug-Id: #22299 --- dlls/winegstreamer/resampler.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/resampler.c b/dlls/winegstreamer/resampler.c index b5b62d58800..22fb273e84c 100644 --- a/dlls/winegstreamer/resampler.c +++ b/dlls/winegstreamer/resampler.c @@ -509,7 +509,18 @@ static HRESULT WINAPI transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFM static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) { - FIXME("iface %p, message %#x, param %p stub!\n", iface, message, (void *)param); + struct resampler *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, message %#x, param %p.\n", iface, message, (void *)param); + + if (!impl->wg_transform) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (message == MFT_MESSAGE_COMMAND_DRAIN) + return wg_transform_drain(impl->wg_transform); + + FIXME("Ignoring message %#x.\n", message); + return S_OK; } From b6b53a294c99c2bafb730228e560997124c51570 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 20 Jan 2023 14:48:48 -0600 Subject: [PATCH 113/413] winegstreamer: Implement MFT_MESSAGE_COMMAND_DRAIN for video processor. CW-Bug-Id: #21804 CW-Bug-Id: #22299 --- dlls/winegstreamer/video_processor.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/video_processor.c b/dlls/winegstreamer/video_processor.c index 1cbb37dafc7..698ae498ec7 100644 --- a/dlls/winegstreamer/video_processor.c +++ b/dlls/winegstreamer/video_processor.c @@ -513,7 +513,18 @@ static HRESULT WINAPI video_processor_ProcessEvent(IMFTransform *iface, DWORD id static HRESULT WINAPI video_processor_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) { - FIXME("iface %p, message %#x, param %#Ix stub!\n", iface, message, param); + struct video_processor *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, message %#x, param %p.\n", iface, message, (void *)param); + + if (!impl->wg_transform) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (message == MFT_MESSAGE_COMMAND_DRAIN) + return wg_transform_drain(impl->wg_transform); + + FIXME("Ignoring message %#x.\n", message); + return S_OK; } From 667ebf7971874fa54279932d8f1e4a255b576f99 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 20 Jan 2023 14:50:38 -0600 Subject: [PATCH 114/413] winegstreamer: Implement MFT_MESSAGE_COMMAND_DRAIN for wma decoder. CW-Bug-Id: #21804 CW-Bug-Id: #22299 --- dlls/winegstreamer/wma_decoder.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/wma_decoder.c b/dlls/winegstreamer/wma_decoder.c index 9a9ea857e0d..a4380d3a3b5 100644 --- a/dlls/winegstreamer/wma_decoder.c +++ b/dlls/winegstreamer/wma_decoder.c @@ -501,7 +501,18 @@ static HRESULT WINAPI transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFM static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) { - FIXME("iface %p, message %#x, param %p stub!\n", iface, message, (void *)param); + struct wma_decoder *decoder = impl_from_IMFTransform(iface); + + TRACE("iface %p, message %#x, param %p.\n", iface, message, (void *)param); + + if (!decoder->wg_transform) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (message == MFT_MESSAGE_COMMAND_DRAIN) + return wg_transform_drain(decoder->wg_transform); + + FIXME("Ignoring message %#x.\n", message); + return S_OK; } From b6d45ab74cc05e3e15fd166e3e5fa9e75313296a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 17 Aug 2023 11:21:56 +0200 Subject: [PATCH 115/413] HACK: winegstreamer: Do not report live latency for some games. CW-Bug-Id: #22581 --- dlls/winegstreamer/video_decoder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winegstreamer/video_decoder.c b/dlls/winegstreamer/video_decoder.c index f75fb51ca84..35917be2951 100644 --- a/dlls/winegstreamer/video_decoder.c +++ b/dlls/winegstreamer/video_decoder.c @@ -116,7 +116,7 @@ static HRESULT try_create_wg_transform(struct video_decoder *decoder) { const char *sgi; - if ((sgi = getenv("SteamGameId")) && (!strcmp(sgi, "2009100"))) + if ((sgi = getenv("SteamGameId")) && ((!strcmp(sgi, "2009100")) || (!strcmp(sgi, "2555360")))) attrs.low_latency = FALSE; } From 047a3280e61e1be25f33c3010e288438f84de628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 21 Aug 2023 19:17:48 +0200 Subject: [PATCH 116/413] HACK: winegstreamer: Don't add unnecessary and slow? videoflip for some games. CW-Bug-Id: #22581 --- dlls/winegstreamer/wg_transform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 4acc449a7f5..3a17ac9b401 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -457,7 +457,7 @@ NTSTATUS wg_transform_create(void *args) case WG_MAJOR_TYPE_VIDEO: { const char *sgi; - if ((sgi = getenv("SteamGameId")) && (!strcmp(sgi, "2009100"))) + if ((sgi = getenv("SteamGameId")) && ((!strcmp(sgi, "2009100")) || (!strcmp(sgi, "2555360")))) { if (!(element = create_element("videoconvert", "base")) || !append_element(transform->container, element, &first, &last)) From 0e08853b937fab0bd8e89c1c891abce2a1421dc6 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 21 Aug 2023 12:52:48 -0600 Subject: [PATCH 117/413] HACK: winegstreamer: Disable MF_SA_D3D11_AWARE for some games. CW-Bug-Id: #22581 --- dlls/winegstreamer/video_decoder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winegstreamer/video_decoder.c b/dlls/winegstreamer/video_decoder.c index 35917be2951..95a014a46e0 100644 --- a/dlls/winegstreamer/video_decoder.c +++ b/dlls/winegstreamer/video_decoder.c @@ -826,7 +826,7 @@ static HRESULT video_decoder_create_with_types(const GUID *const *input_types, U { const char *sgi; - if ((sgi = getenv("SteamGameId")) && (!strcmp(sgi, "2009100"))) + if ((sgi = getenv("SteamGameId")) && ((!strcmp(sgi, "2009100")) || (!strcmp(sgi, "2555360")))) IMFAttributes_SetUINT32(decoder->attributes, &MF_SA_D3D11_AWARE, FALSE); } From af27ee26aea9b0f2c086463417384408214d0efb Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 14 Feb 2024 10:23:33 -0600 Subject: [PATCH 118/413] Revert "winhttp: Always return result at once if available in WinHttpReadData()." This reverts commit ec94ccd297c8e5f080b45b5d48a1d035a1492df6. --- dlls/winhttp/request.c | 42 +++---------------------------- dlls/winhttp/tests/notification.c | 6 +---- 2 files changed, 4 insertions(+), 44 deletions(-) diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index 14e5b493f77..74278195fc3 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -3213,7 +3213,7 @@ BOOL WINAPI WinHttpReadData( HINTERNET hrequest, void *buffer, DWORD to_read, DW DWORD ret; struct request *request; BOOL async; - BOOL data_available = FALSE; + BOOL data_available; TRACE( "%p, %p, %lu, %p\n", hrequest, buffer, to_read, read ); @@ -3229,44 +3229,7 @@ BOOL WINAPI WinHttpReadData( HINTERNET hrequest, void *buffer, DWORD to_read, DW return FALSE; } - if (!(async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) || skip_async_queue( request, &data_available )) - { - ret = read_data( request, buffer, to_read, read, async ); - } - else if (data_available) - { - /* Data available but recursion limit reached, only queue callback. */ - struct send_callback *s; - - if (!(s = malloc( sizeof(*s) ))) - { - release_object( &request->hdr ); - SetLastError( ERROR_OUTOFMEMORY ); - return FALSE; - } - - if (!(ret = read_data( request, buffer, to_read, &s->count, FALSE ))) - { - if (read) *read = s->count; - s->status = WINHTTP_CALLBACK_STATUS_READ_COMPLETE; - s->info = buffer; - s->buflen = s->count; - } - else - { - s->result.dwResult = API_READ_DATA; - s->result.dwError = ret; - s->status = WINHTTP_CALLBACK_STATUS_REQUEST_ERROR; - s->info = &s->result; - s->buflen = sizeof(s->result); - } - - if ((ret = queue_task( &request->queue, task_send_callback, &s->task_hdr, &request->hdr ))) - free( s ); - else - ret = ERROR_IO_PENDING; - } - else + if ((async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) && !skip_async_queue( request, &data_available )) { struct read_data *r; @@ -3285,6 +3248,7 @@ BOOL WINAPI WinHttpReadData( HINTERNET hrequest, void *buffer, DWORD to_read, DW else ret = ERROR_IO_PENDING; } + else ret = read_data( request, buffer, to_read, read, async ); release_object( &request->hdr ); SetLastError( ret ); diff --git a/dlls/winhttp/tests/notification.c b/dlls/winhttp/tests/notification.c index ed70ed906f0..40db323fb5e 100644 --- a/dlls/winhttp/tests/notification.c +++ b/dlls/winhttp/tests/notification.c @@ -1921,14 +1921,10 @@ static void CALLBACK test_recursion_callback( HINTERNET handle, DWORD_PTR contex "got %lu, thread %#lx\n", context->recursion_count, GetCurrentThreadId() ); context->max_recursion_query = max( context->max_recursion_query, context->recursion_count ); InterlockedIncrement( &context->recursion_count ); - b = 0xff; - len = 0xdeadbeef; - ret = WinHttpReadData( context->request, &b, 1, &len ); + ret = WinHttpReadData( context->request, &b, 1, NULL ); err = GetLastError(); ok( ret, "failed to read data, GetLastError() %lu\n", err ); ok( err == ERROR_SUCCESS || err == ERROR_IO_PENDING, "got %lu\n", err ); - ok( b != 0xff, "got %#x.\n", b ); - ok( len == 1, "got %lu.\n", len ); if (err == ERROR_SUCCESS) context->have_sync_callback = TRUE; InterlockedDecrement( &context->recursion_count ); break; From 7282f73a25ecaf463a4f15d8fb4511cb416d47ba Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 14 Feb 2024 10:23:35 -0600 Subject: [PATCH 119/413] Revert "winhttp: Always return result at once if available in WinHttpQueryDataAvailable()." This reverts commit 41ffcf40a4244b2bc23709d57aa87fa01b13e80f. --- dlls/winhttp/request.c | 59 +++---------------------------- dlls/winhttp/tests/notification.c | 7 +--- dlls/winhttp/winhttp_private.h | 13 ------- 3 files changed, 6 insertions(+), 73 deletions(-) diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index 74278195fc3..84af8e58988 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -257,16 +257,6 @@ static BOOL cancel_queue( struct queue *queue ) return cancelled; } -static void task_send_callback( void *ctx, BOOL abort ) -{ - struct send_callback *s = ctx; - - if (abort) return; - - TRACE( "running %p\n", ctx ); - send_callback( s->task_hdr.obj, s->status, s->info, s->buflen ); -} - static void free_header( struct header *header ) { free( header->field ); @@ -3062,10 +3052,9 @@ static DWORD query_data_ready( struct request *request ) return count; } -static BOOL skip_async_queue( struct request *request, BOOL *data_available ) +static BOOL skip_async_queue( struct request *request ) { - *data_available = end_of_read_data( request ) || query_data_ready( request ); - return request->hdr.recursion_count < 3 && *data_available; + return request->hdr.recursion_count < 3 && (end_of_read_data( request ) || query_data_ready( request )); } static DWORD query_data_available( struct request *request, DWORD *available, BOOL async ) @@ -3117,7 +3106,6 @@ BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available ) DWORD ret; struct request *request; BOOL async; - BOOL data_available = FALSE; TRACE("%p, %p\n", hrequest, available); @@ -3133,44 +3121,7 @@ BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available ) return FALSE; } - if (!(async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) || skip_async_queue( request, &data_available )) - { - ret = query_data_available( request, available, async ); - } - else if (data_available) - { - /* Data available but recursion limit reached, only queue callback. */ - struct send_callback *s; - - if (!(s = malloc( sizeof(*s) ))) - { - release_object( &request->hdr ); - SetLastError( ERROR_OUTOFMEMORY ); - return FALSE; - } - - if (!(ret = query_data_available( request, &s->count, FALSE ))) - { - if (available) *available = s->count; - s->status = WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE; - s->info = &s->count; - s->buflen = sizeof(s->count); - } - else - { - s->result.dwResult = API_QUERY_DATA_AVAILABLE; - s->result.dwError = ret; - s->status = WINHTTP_CALLBACK_STATUS_REQUEST_ERROR; - s->info = &s->result; - s->buflen = sizeof(s->result); - } - - if ((ret = queue_task( &request->queue, task_send_callback, &s->task_hdr, &request->hdr ))) - free( s ); - else - ret = ERROR_IO_PENDING; - } - else + if ((async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) && !skip_async_queue( request )) { struct query_data *q; @@ -3188,6 +3139,7 @@ BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available ) else ret = ERROR_IO_PENDING; } + else ret = query_data_available( request, available, async ); release_object( &request->hdr ); SetLastError( ret ); @@ -3213,7 +3165,6 @@ BOOL WINAPI WinHttpReadData( HINTERNET hrequest, void *buffer, DWORD to_read, DW DWORD ret; struct request *request; BOOL async; - BOOL data_available; TRACE( "%p, %p, %lu, %p\n", hrequest, buffer, to_read, read ); @@ -3229,7 +3180,7 @@ BOOL WINAPI WinHttpReadData( HINTERNET hrequest, void *buffer, DWORD to_read, DW return FALSE; } - if ((async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) && !skip_async_queue( request, &data_available )) + if ((async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) && !skip_async_queue( request )) { struct read_data *r; diff --git a/dlls/winhttp/tests/notification.c b/dlls/winhttp/tests/notification.c index 40db323fb5e..78319f0df5c 100644 --- a/dlls/winhttp/tests/notification.c +++ b/dlls/winhttp/tests/notification.c @@ -1863,7 +1863,6 @@ static void CALLBACK test_recursion_callback( HINTERNET handle, DWORD_PTR contex DWORD status, void *buffer, DWORD buflen ) { struct test_recursion_context *context = (struct test_recursion_context *)context_ptr; - static DWORD len; DWORD err; BOOL ret; BYTE b; @@ -1940,14 +1939,10 @@ static void CALLBACK test_recursion_callback( HINTERNET handle, DWORD_PTR contex context->max_recursion_read = max( context->max_recursion_read, context->recursion_count ); context->read_from_callback = TRUE; InterlockedIncrement( &context->recursion_count ); - len = 0xdeadbeef; - /* Use static variable len here so write to it doesn't destroy the stack on old Windows which - * doesn't set the value at once. */ - ret = WinHttpQueryDataAvailable( context->request, &len ); + ret = WinHttpQueryDataAvailable( context->request, NULL ); err = GetLastError(); ok( ret, "failed to query data available, GetLastError() %lu\n", err ); ok( err == ERROR_SUCCESS || err == ERROR_IO_PENDING, "got %lu\n", err ); - ok( len != 0xdeadbeef || broken( len == 0xdeadbeef ) /* Win7 */, "got %lu.\n", len ); if (err == ERROR_SUCCESS) context->have_sync_callback = TRUE; InterlockedDecrement( &context->recursion_count ); break; diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h index 80ce2614ca1..0bf1c5bc6b0 100644 --- a/dlls/winhttp/winhttp_private.h +++ b/dlls/winhttp/winhttp_private.h @@ -312,19 +312,6 @@ struct task_header volatile LONG completion_sent; }; -struct send_callback -{ - struct task_header task_hdr; - DWORD status; - void *info; - DWORD buflen; - union - { - WINHTTP_ASYNC_RESULT result; - DWORD count; - }; -}; - struct send_request { struct task_header task_hdr; From fe7516a6df626df501e7957ecf0eaa3048e8fc91 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 23 Jan 2024 16:34:49 -0600 Subject: [PATCH 120/413] winhttp: Always return result at once if available in WinHttpQueryDataAvailable(). (cherry picked from commit fd2534422ba66d66cc811c75d012b2b1060d1770) CW-Bug-Id: #23312 --- dlls/winhttp/request.c | 59 ++++++++++++++++++++++++++++--- dlls/winhttp/tests/notification.c | 11 +++++- dlls/winhttp/winhttp_private.h | 13 +++++++ 3 files changed, 77 insertions(+), 6 deletions(-) diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index 84af8e58988..59431e3a9f2 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -257,6 +257,16 @@ static BOOL cancel_queue( struct queue *queue ) return cancelled; } +static void task_send_callback( void *ctx, BOOL abort ) +{ + struct send_callback *s = ctx; + + if (abort) return; + + TRACE( "running %p\n", ctx ); + send_callback( s->task_hdr.obj, s->status, s->info, s->buflen ); +} + static void free_header( struct header *header ) { free( header->field ); @@ -3052,9 +3062,10 @@ static DWORD query_data_ready( struct request *request ) return count; } -static BOOL skip_async_queue( struct request *request ) +static BOOL skip_async_queue( struct request *request, BOOL *wont_block ) { - return request->hdr.recursion_count < 3 && (end_of_read_data( request ) || query_data_ready( request )); + *wont_block = end_of_read_data( request ) || query_data_ready( request ); + return request->hdr.recursion_count < 3 && *wont_block; } static DWORD query_data_available( struct request *request, DWORD *available, BOOL async ) @@ -3106,6 +3117,7 @@ BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available ) DWORD ret; struct request *request; BOOL async; + BOOL wont_block = FALSE; TRACE("%p, %p\n", hrequest, available); @@ -3121,7 +3133,44 @@ BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available ) return FALSE; } - if ((async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) && !skip_async_queue( request )) + if (!(async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) || skip_async_queue( request, &wont_block )) + { + ret = query_data_available( request, available, async ); + } + else if (wont_block) + { + /* Data available but recursion limit reached, only queue callback. */ + struct send_callback *s; + + if (!(s = malloc( sizeof(*s) ))) + { + release_object( &request->hdr ); + SetLastError( ERROR_OUTOFMEMORY ); + return FALSE; + } + + if (!(ret = query_data_available( request, &s->count, FALSE ))) + { + if (available) *available = s->count; + s->status = WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE; + s->info = &s->count; + s->buflen = sizeof(s->count); + } + else + { + s->result.dwResult = API_QUERY_DATA_AVAILABLE; + s->result.dwError = ret; + s->status = WINHTTP_CALLBACK_STATUS_REQUEST_ERROR; + s->info = &s->result; + s->buflen = sizeof(s->result); + } + + if ((ret = queue_task( &request->queue, task_send_callback, &s->task_hdr, &request->hdr ))) + free( s ); + else + ret = ERROR_IO_PENDING; + } + else { struct query_data *q; @@ -3139,7 +3188,6 @@ BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available ) else ret = ERROR_IO_PENDING; } - else ret = query_data_available( request, available, async ); release_object( &request->hdr ); SetLastError( ret ); @@ -3165,6 +3213,7 @@ BOOL WINAPI WinHttpReadData( HINTERNET hrequest, void *buffer, DWORD to_read, DW DWORD ret; struct request *request; BOOL async; + BOOL wont_block; TRACE( "%p, %p, %lu, %p\n", hrequest, buffer, to_read, read ); @@ -3180,7 +3229,7 @@ BOOL WINAPI WinHttpReadData( HINTERNET hrequest, void *buffer, DWORD to_read, DW return FALSE; } - if ((async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) && !skip_async_queue( request )) + if ((async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) && !skip_async_queue( request, &wont_block )) { struct read_data *r; diff --git a/dlls/winhttp/tests/notification.c b/dlls/winhttp/tests/notification.c index 78319f0df5c..98df1571464 100644 --- a/dlls/winhttp/tests/notification.c +++ b/dlls/winhttp/tests/notification.c @@ -1929,6 +1929,9 @@ static void CALLBACK test_recursion_callback( HINTERNET handle, DWORD_PTR contex break; case WINHTTP_CALLBACK_STATUS_READ_COMPLETE: + { + static DWORD len; + if (!buflen) { SetEvent( context->wait ); @@ -1939,13 +1942,19 @@ static void CALLBACK test_recursion_callback( HINTERNET handle, DWORD_PTR contex context->max_recursion_read = max( context->max_recursion_read, context->recursion_count ); context->read_from_callback = TRUE; InterlockedIncrement( &context->recursion_count ); - ret = WinHttpQueryDataAvailable( context->request, NULL ); + len = 0xdeadbeef; + /* Use static variable len here so write to it doesn't destroy the stack on old Windows which + * doesn't set the value at once. */ + ret = WinHttpQueryDataAvailable( context->request, &len ); err = GetLastError(); ok( ret, "failed to query data available, GetLastError() %lu\n", err ); ok( err == ERROR_SUCCESS || err == ERROR_IO_PENDING, "got %lu\n", err ); + ok( len != 0xdeadbeef || broken( len == 0xdeadbeef ) /* Win7 */, "got %lu.\n", len ); if (err == ERROR_SUCCESS) context->have_sync_callback = TRUE; InterlockedDecrement( &context->recursion_count ); break; + } + case WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE: if (!context->headers_available && context->call_receive_response_status == WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE) diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h index 0bf1c5bc6b0..80ce2614ca1 100644 --- a/dlls/winhttp/winhttp_private.h +++ b/dlls/winhttp/winhttp_private.h @@ -312,6 +312,19 @@ struct task_header volatile LONG completion_sent; }; +struct send_callback +{ + struct task_header task_hdr; + DWORD status; + void *info; + DWORD buflen; + union + { + WINHTTP_ASYNC_RESULT result; + DWORD count; + }; +}; + struct send_request { struct task_header task_hdr; From d31cc4d1af0dbd515b5ea325319d0c43cab71f42 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 23 Jan 2024 16:56:09 -0600 Subject: [PATCH 121/413] winhttp: Always return result at once if available in WinHttpReadData(). (cherry picked from commit bd2a2c25f54e33b5ffae1f28f5ead4b020f55456) CW-Bug-Id: #23312 --- dlls/winhttp/request.c | 42 ++++++++++++++++++++++++++++--- dlls/winhttp/tests/notification.c | 10 +++++++- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index 59431e3a9f2..29b30fc2583 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -3213,7 +3213,7 @@ BOOL WINAPI WinHttpReadData( HINTERNET hrequest, void *buffer, DWORD to_read, DW DWORD ret; struct request *request; BOOL async; - BOOL wont_block; + BOOL wont_block = FALSE; TRACE( "%p, %p, %lu, %p\n", hrequest, buffer, to_read, read ); @@ -3229,7 +3229,44 @@ BOOL WINAPI WinHttpReadData( HINTERNET hrequest, void *buffer, DWORD to_read, DW return FALSE; } - if ((async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) && !skip_async_queue( request, &wont_block )) + if (!(async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) || skip_async_queue( request, &wont_block )) + { + ret = read_data( request, buffer, to_read, read, async ); + } + else if (wont_block) + { + /* Data available but recursion limit reached, only queue callback. */ + struct send_callback *s; + + if (!(s = malloc( sizeof(*s) ))) + { + release_object( &request->hdr ); + SetLastError( ERROR_OUTOFMEMORY ); + return FALSE; + } + + if (!(ret = read_data( request, buffer, to_read, &s->count, FALSE ))) + { + if (read) *read = s->count; + s->status = WINHTTP_CALLBACK_STATUS_READ_COMPLETE; + s->info = buffer; + s->buflen = s->count; + } + else + { + s->result.dwResult = API_READ_DATA; + s->result.dwError = ret; + s->status = WINHTTP_CALLBACK_STATUS_REQUEST_ERROR; + s->info = &s->result; + s->buflen = sizeof(s->result); + } + + if ((ret = queue_task( &request->queue, task_send_callback, &s->task_hdr, &request->hdr ))) + free( s ); + else + ret = ERROR_IO_PENDING; + } + else { struct read_data *r; @@ -3248,7 +3285,6 @@ BOOL WINAPI WinHttpReadData( HINTERNET hrequest, void *buffer, DWORD to_read, DW else ret = ERROR_IO_PENDING; } - else ret = read_data( request, buffer, to_read, read, async ); release_object( &request->hdr ); SetLastError( ret ); diff --git a/dlls/winhttp/tests/notification.c b/dlls/winhttp/tests/notification.c index 98df1571464..3e67c439bb5 100644 --- a/dlls/winhttp/tests/notification.c +++ b/dlls/winhttp/tests/notification.c @@ -1904,6 +1904,9 @@ static void CALLBACK test_recursion_callback( HINTERNET handle, DWORD_PTR contex break; case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE: + { + DWORD len; + if (!context->read_from_callback) { SetEvent( context->wait ); @@ -1920,13 +1923,18 @@ static void CALLBACK test_recursion_callback( HINTERNET handle, DWORD_PTR contex "got %lu, thread %#lx\n", context->recursion_count, GetCurrentThreadId() ); context->max_recursion_query = max( context->max_recursion_query, context->recursion_count ); InterlockedIncrement( &context->recursion_count ); - ret = WinHttpReadData( context->request, &b, 1, NULL ); + b = 0xff; + len = 0xdeadbeef; + ret = WinHttpReadData( context->request, &b, 1, &len ); err = GetLastError(); ok( ret, "failed to read data, GetLastError() %lu\n", err ); ok( err == ERROR_SUCCESS || err == ERROR_IO_PENDING, "got %lu\n", err ); + ok( b != 0xff, "got %#x.\n", b ); + ok( len == 1, "got %lu.\n", len ); if (err == ERROR_SUCCESS) context->have_sync_callback = TRUE; InterlockedDecrement( &context->recursion_count ); break; + } case WINHTTP_CALLBACK_STATUS_READ_COMPLETE: { From 87ffe79ded23f2623e32362016eebd5bf1c749a6 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 30 Jan 2024 11:55:31 -0600 Subject: [PATCH 122/413] winex11.drv: Default swap interval to 0 for child window drawables in create_gl_drawable(). CW-Bug-Id: #23329 --- dlls/winex11.drv/opengl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index 37179ad461a..043472531d5 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -1551,6 +1551,7 @@ static struct gl_drawable *create_gl_drawable( HWND hwnd, const struct wgl_pixel (visual->class == PseudoColor || visual->class == GrayScale || visual->class == DirectColor) ? AllocAll : AllocNone ); gl->window = create_client_window( hwnd, visual, gl->colormap ); + gl->swap_interval = 0; if (gl->window) { gl->drawable = pglXCreateWindow( gdi_display, gl->format->fbconfig, gl->window, NULL ); From d3adc983e79e452e91430b11e64dc1d5fd61c685 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 22 Jan 2024 16:28:32 -0600 Subject: [PATCH 123/413] winex11.drv: Fix wglSwapBuffers() with NULL current context with child window rendering. (cherry picked from commit eb5993a7c6fbc1cd9deac0dceabc8f1c76e14ba8) CW-Bug-Id: #23302 --- dlls/opengl32/tests/opengl.c | 52 ++++++++++++++++++++++++++++++++++++ dlls/winex11.drv/opengl.c | 10 +++---- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/dlls/opengl32/tests/opengl.c b/dlls/opengl32/tests/opengl.c index e3ca82a9b03..74aff6b4bbd 100644 --- a/dlls/opengl32/tests/opengl.c +++ b/dlls/opengl32/tests/opengl.c @@ -2006,6 +2006,55 @@ static void test_copy_context(HDC hdc) ok(ret, "wglMakeCurrent failed, last error %#lx.\n", GetLastError()); } +static void test_child_window(HWND hwnd, PIXELFORMATDESCRIPTOR *pfd) +{ + int pixel_format; + DWORD t1, t; + HGLRC hglrc; + HWND child; + HDC hdc; + int res; + + child = CreateWindowA("static", "Title", WS_CHILDWINDOW | WS_VISIBLE, 50, 50, 100, 100, hwnd, NULL, NULL, NULL); + ok(!!child, "got error %lu.\n", GetLastError()); + + hdc = GetDC(child); + pixel_format = ChoosePixelFormat(hdc, pfd); + res = SetPixelFormat(hdc, pixel_format, pfd); + ok(res, "got error %lu.\n", GetLastError()); + + hglrc = wglCreateContext(hdc); + ok(!!hglrc, "got error %lu.\n", GetLastError()); + + /* Test SwapBuffers with NULL context. */ + + glDrawBuffer(GL_BACK); + + /* Currently blit happening for child window in winex11 may not be updated with the latest GL frame + * even on glXWaitForSbcOML() path. So simulate continuous present for the test purpose. */ + trace("Child window rectangle should turn from red to green now.\n"); + t1 = GetTickCount(); + while ((t = GetTickCount()) - t1 < 3000) + { + res = wglMakeCurrent(hdc, hglrc); + ok(res, "got error %lu.\n", GetLastError()); + if (t - t1 > 1500) + glClearColor(0.0f, 1.0f, 0.0f, 1.0f); + else + glClearColor(1.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + res = wglMakeCurrent(NULL, NULL); + ok(res, "got error %lu.\n", GetLastError()); + SwapBuffers(hdc); + } + + res = wglDeleteContext(hglrc); + ok(res, "got error %lu.\n", GetLastError()); + + ReleaseDC(child, hdc); + DestroyWindow(child); +} + START_TEST(opengl) { HWND hwnd; @@ -2138,6 +2187,9 @@ START_TEST(opengl) else skip("WGL_EXT_swap_control not supported, skipping test\n"); + if (winetest_interactive) + test_child_window(hwnd, &pfd); + cleanup: ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index 043472531d5..90c5dadfff6 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -4904,7 +4904,7 @@ static BOOL glxdrv_wglSwapBuffers( HDC hdc ) case DC_GL_PIXMAP_WIN: if (ctx) sync_context( ctx ); if (!gl->layered_type) escape.drawable = gl->pixmap; - if (pglXCopySubBufferMESA) { + if (ctx && pglXCopySubBufferMESA) { /* (glX)SwapBuffers has an implicit glFlush effect, however * GLX_MESA_copy_sub_buffer doesn't. Make sure GL is flushed before * copying */ @@ -4913,7 +4913,7 @@ static BOOL glxdrv_wglSwapBuffers( HDC hdc ) gl->pixmap_size.cx, gl->pixmap_size.cy ); break; } - if (pglXSwapBuffersMscOML) + if (ctx && pglXSwapBuffersMscOML) { pglFlush(); target_sbc = pglXSwapBuffersMscOML( gdi_display, gl->drawable, 0, 0, 0 ); @@ -4939,7 +4939,7 @@ static BOOL glxdrv_wglSwapBuffers( HDC hdc ) ctx->fs_hack = FALSE; fs_hack_setup_context( ctx, gl ); } - if ((escape.drawable || gl->layered_type) && pglXSwapBuffersMscOML) + if (ctx && (escape.drawable || gl->layered_type) && pglXSwapBuffersMscOML) { pglFlush(); target_sbc = pglXSwapBuffersMscOML( gdi_display, gl->drawable, 0, 0, 0 ); @@ -4949,14 +4949,14 @@ static BOOL glxdrv_wglSwapBuffers( HDC hdc ) break; } - if ((escape.drawable || gl->layered_type) && pglXWaitForSbcOML) + if (ctx && (escape.drawable || gl->layered_type) && pglXWaitForSbcOML) pglXWaitForSbcOML( gdi_display, gl->drawable, target_sbc, &ust, &msc, &sbc ); update_window_surface( gl, hwnd ); release_gl_drawable( gl ); if (escape.drawable) - NtGdiExtEscape( ctx->hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + NtGdiExtEscape( ctx ? ctx->hdc : hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); return TRUE; } From ec3d27717098d6ba4e34fab945cdd969182ca567 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Fri, 2 Feb 2024 17:55:27 +0000 Subject: [PATCH 124/413] mscoree: Update Wine Mono to 9.0.0. (cherry picked from commit e891073c9eacc391cfa1bdb65e734922742c2c17) --- dlls/appwiz.cpl/addons.c | 4 ++-- dlls/mscoree/mscoree_private.h | 2 +- tools/gitlab/test.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dlls/appwiz.cpl/addons.c b/dlls/appwiz.cpl/addons.c index a2957c3040a..12d32a46216 100644 --- a/dlls/appwiz.cpl/addons.c +++ b/dlls/appwiz.cpl/addons.c @@ -56,10 +56,10 @@ WINE_DEFAULT_DEBUG_CHANNEL(appwizcpl); #define GECKO_SHA "???" #endif -#define MONO_VERSION "8.1.0" +#define MONO_VERSION "9.0.0" #if defined(__i386__) || defined(__x86_64__) #define MONO_ARCH "x86" -#define MONO_SHA "0ed3ec533aef79b2f312155931cf7b1080009ac0c5b4c2bcfeb678ac948e0810" +#define MONO_SHA "79f6c43100675566c112f4199b9ea7b944d338164b34bd91fa11b0b0a414e1c4" #else #define MONO_ARCH "" #define MONO_SHA "???" diff --git a/dlls/mscoree/mscoree_private.h b/dlls/mscoree/mscoree_private.h index 06db53136e1..f27ff95be2d 100644 --- a/dlls/mscoree/mscoree_private.h +++ b/dlls/mscoree/mscoree_private.h @@ -45,7 +45,7 @@ extern HRESULT assembly_get_runtime_version(ASSEMBLY *assembly, LPSTR *version); extern HRESULT assembly_get_vtable_fixups(ASSEMBLY *assembly, VTableFixup **fixups, DWORD *count); extern HRESULT assembly_get_native_entrypoint(ASSEMBLY *assembly, NativeEntryPointFunc *func); -#define WINE_MONO_VERSION "8.1.0" +#define WINE_MONO_VERSION "9.0.0" /* Mono embedding */ typedef struct _MonoDomain MonoDomain; diff --git a/tools/gitlab/test.yml b/tools/gitlab/test.yml index 92c14d83996..aaad7b31c36 100644 --- a/tools/gitlab/test.yml +++ b/tools/gitlab/test.yml @@ -7,7 +7,7 @@ variables: GIT_STRATEGY: none GECKO_VER: 2.47.4 - MONO_VER: 8.1.0 + MONO_VER: 9.0.0 cache: - key: wine-gecko-$GECKO_VER paths: From ddc16cd915b8faba4f72bc76d6a7df4c023430f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 13 Feb 2023 15:05:53 +0100 Subject: [PATCH 125/413] windows.media.speech: Add Vosk checks to autoconf. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- configure.ac | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 5bcd754799e..708c8269fc2 100644 --- a/configure.ac +++ b/configure.ac @@ -60,6 +60,7 @@ AC_ARG_WITH(udev, AS_HELP_STRING([--without-udev],[do not use udev (plug an AC_ARG_WITH(unwind, AS_HELP_STRING([--without-unwind],[do not use the libunwind library (exception handling)])) AC_ARG_WITH(usb, AS_HELP_STRING([--without-usb],[do not use the libusb library])) AC_ARG_WITH(v4l2, AS_HELP_STRING([--without-v4l2],[do not use v4l2 (video capture)])) +AC_ARG_WITH(vosk, AS_HELP_STRING([--without-vosk],[do not use Vosk])) AC_ARG_WITH(vulkan, AS_HELP_STRING([--without-vulkan],[do not use Vulkan])) AC_ARG_WITH(wayland, AS_HELP_STRING([--without-wayland],[do not build the Wayland driver])) AC_ARG_WITH(xcomposite,AS_HELP_STRING([--without-xcomposite],[do not use the Xcomposite extension]), @@ -492,7 +493,8 @@ AC_CHECK_HEADERS(\ syscall.h \ utime.h \ valgrind/memcheck.h \ - valgrind/valgrind.h + valgrind/valgrind.h \ + vosk_api.h ) WINE_HEADER_MAJOR() AC_HEADER_STAT() @@ -1875,6 +1877,14 @@ then WINE_WARNING([No sound system was found. Windows applications will be silent.]) fi +dnl **** Check for Vosk **** +if test x$with_vosk != xno +then + WINE_CHECK_SONAME(vosk,vosk_recognizer_new) +fi +WINE_NOTICE_WITH(vosk,[test x$ac_cv_lib_soname_vosk = x], + [libvosk ${notice_platform}development files not found, speech recognition won't be supported.]) + dnl *** Check for Vulkan *** if test "x$with_vulkan" != "xno" then From c4000ddef689e78ccd6958b67db218dec0a43312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 13 Feb 2023 00:17:50 +0100 Subject: [PATCH 126/413] windows.media.speech: Add unixlib stub. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/Makefile.in | 2 + dlls/windows.media.speech/main.c | 26 ++++++ dlls/windows.media.speech/private.h | 4 + dlls/windows.media.speech/unixlib.c | 125 ++++++++++++++++++++++++++ dlls/windows.media.speech/unixlib.h | 39 ++++++++ 5 files changed, 196 insertions(+) create mode 100644 dlls/windows.media.speech/unixlib.c create mode 100644 dlls/windows.media.speech/unixlib.h diff --git a/dlls/windows.media.speech/Makefile.in b/dlls/windows.media.speech/Makefile.in index 5be66d8367e..11a8ad2bba8 100644 --- a/dlls/windows.media.speech/Makefile.in +++ b/dlls/windows.media.speech/Makefile.in @@ -1,4 +1,5 @@ MODULE = windows.media.speech.dll +UNIXLIB = windows.media.speech.so IMPORTS = combase uuid SOURCES = \ @@ -9,4 +10,5 @@ SOURCES = \ main.c \ recognizer.c \ synthesizer.c \ + unixlib.c \ vector.c diff --git a/dlls/windows.media.speech/main.c b/dlls/windows.media.speech/main.c index e772a791588..d53e1599eb8 100644 --- a/dlls/windows.media.speech/main.c +++ b/dlls/windows.media.speech/main.c @@ -20,10 +20,36 @@ #include "initguid.h" #include "private.h" +#include "unixlib.h" + #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(speech); +BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, void *reserved ) +{ + NTSTATUS status; + + switch (reason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(instance); + + if ((status = __wine_init_unix_call())) + ERR("loading the unixlib failed with status %#lx.\n", status); + + if ((status = WINE_UNIX_CALL(unix_process_attach, NULL))) + WARN("initializing the unixlib failed with status %#lx.\n", status); + + break; + case DLL_PROCESS_DETACH: + WINE_UNIX_CALL(unix_process_detach, NULL); + break; + } + + return TRUE; +} + HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void **out) { FIXME("clsid %s, riid %s, out %p stub!\n", debugstr_guid(clsid), debugstr_guid(riid), out); diff --git a/dlls/windows.media.speech/private.h b/dlls/windows.media.speech/private.h index d03fe0e773e..4b6a8327d6e 100644 --- a/dlls/windows.media.speech/private.h +++ b/dlls/windows.media.speech/private.h @@ -22,6 +22,10 @@ #include +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "winerror.h" +#include "winternl.h" #define COBJMACROS #include "corerror.h" #include "windef.h" diff --git a/dlls/windows.media.speech/unixlib.c b/dlls/windows.media.speech/unixlib.c new file mode 100644 index 00000000000..6a9dac6f067 --- /dev/null +++ b/dlls/windows.media.speech/unixlib.c @@ -0,0 +1,125 @@ +/* + * Unixlib for Windows.Media.Speech + * + * Copyright 2023 Bernhard Kölbl 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 +#include +#include +#include +#include + +#ifdef SONAME_LIBVOSK +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#include +#pragma GCC diagnostic pop +#endif + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "winerror.h" +#include "winternl.h" + +#include "wine/debug.h" + +#include "unixlib.h" + +WINE_DEFAULT_DEBUG_CHANNEL(speech); +#ifdef SONAME_LIBVOSK +WINE_DECLARE_DEBUG_CHANNEL(winediag); + +static void *vosk_handle; +#define MAKE_FUNCPTR( f ) static typeof(f) * p_##f +MAKE_FUNCPTR(vosk_model_new); +MAKE_FUNCPTR(vosk_model_free); +MAKE_FUNCPTR(vosk_recognizer_new); +MAKE_FUNCPTR(vosk_recognizer_free); +#undef MAKE_FUNCPTR + +static NTSTATUS process_attach( void *args ) +{ + if (!(vosk_handle = dlopen(SONAME_LIBVOSK, RTLD_NOW))) + { + ERR_(winediag)("Wine is unable to load the Unix side dependencies for speech recognition. " + "Make sure Vosk is installed and up to date on your system and try again.\n"); + return STATUS_DLL_NOT_FOUND; + } + +#define LOAD_FUNCPTR( f ) \ + if (!(p_##f = dlsym(vosk_handle, #f))) \ + { \ + ERR("failed to load %s\n", #f); \ + goto error; \ + } + LOAD_FUNCPTR(vosk_model_new) + LOAD_FUNCPTR(vosk_model_free) + LOAD_FUNCPTR(vosk_recognizer_new) + LOAD_FUNCPTR(vosk_recognizer_free) +#undef LOAD_FUNCPTR + + return STATUS_SUCCESS; + +error: + dlclose(vosk_handle); + vosk_handle = NULL; + return STATUS_DLL_NOT_FOUND; +} + +static NTSTATUS process_detach( void *args ) +{ + if (vosk_handle) + { + dlclose(vosk_handle); + vosk_handle = NULL; + } + return STATUS_SUCCESS; +} + +#else /* SONAME_LIBVOSK */ + +#define MAKE_UNSUPPORTED_FUNC( f ) \ + static NTSTATUS f( void *args ) \ + { \ + ERR("wine was compiled without Vosk support. Speech recognition won't work.\n"); \ + return STATUS_NOT_SUPPORTED; \ + } + +MAKE_UNSUPPORTED_FUNC(process_attach) +MAKE_UNSUPPORTED_FUNC(process_detach) +#undef MAKE_UNSUPPORTED_FUNC + +#endif /* SONAME_LIBVOSK */ + +const unixlib_entry_t __wine_unix_call_funcs[] = +{ + process_attach, + process_detach, +}; + +const unixlib_entry_t __wine_unix_call_wow64_funcs[] = +{ + process_attach, + process_detach, +}; diff --git a/dlls/windows.media.speech/unixlib.h b/dlls/windows.media.speech/unixlib.h new file mode 100644 index 00000000000..6c337e54511 --- /dev/null +++ b/dlls/windows.media.speech/unixlib.h @@ -0,0 +1,39 @@ +/* + * Unix library interface for Windows.Media.Speech + * + * Copyright 2023 Bernhard Kölbl 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 + */ + +#ifndef __WINE_WINDOWS_MEDIA_SPEECH_UNIXLIB_H +#define __WINE_WINDOWS_MEDIA_SPEECH_UNIXLIB_H + +#include +#include + +#include "windef.h" +#include "winternl.h" +#include "wtypes.h" + +#include "wine/unixlib.h" + +enum unix_funcs +{ + unix_process_attach, + unix_process_detach, +}; + +#endif From 17b07df56ccc9fca38c86617cb90590f8dc84350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 6 Feb 2023 21:24:26 +0100 Subject: [PATCH 127/413] windows.media.speech/tests: Get rid of duplicated hresult. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/tests/speech.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index ade056a0a39..9be4fa462df 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -42,7 +42,6 @@ #define AsyncStatus_Closed 4 #define SPERR_WINRT_INTERNAL_ERROR 0x800455a0 -#define SPERR_WINRT_INCORRECT_FORMAT 0x80131537 #define IHandler_RecognitionResult ITypedEventHandler_SpeechContinuousRecognitionSession_SpeechContinuousRecognitionResultGeneratedEventArgs #define IHandler_RecognitionResultVtbl ITypedEventHandler_SpeechContinuousRecognitionSession_SpeechContinuousRecognitionResultGeneratedEventArgsVtbl @@ -1082,7 +1081,7 @@ static void test_SpeechSynthesizer(void) operation_ss_stream = (void *)0xdeadbeef; hr = ISpeechSynthesizer_SynthesizeSsmlToStreamAsync(synthesizer, str, &operation_ss_stream); /* Broken on Win 8 + 8.1 */ - ok(hr == S_OK || broken(hr == SPERR_WINRT_INCORRECT_FORMAT), "ISpeechSynthesizer_SynthesizeSsmlToStreamAsync failed, hr %#lx\n", hr); + ok(hr == S_OK || broken(hr == COR_E_FORMAT), "ISpeechSynthesizer_SynthesizeSsmlToStreamAsync failed, hr %#lx\n", hr); if (hr == S_OK) { From 54ce7876de11fb4c4ae8467246f6d3797ad3a6af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 20 Feb 2023 23:10:16 +0100 Subject: [PATCH 128/413] windows.media.speech/tests: Allow the SpeechRecognizer creation to fail in Wine. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To allow for error handling of missing Unix-side dependencies. Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/tests/speech.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 9be4fa462df..743955ab9bd 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1307,7 +1307,7 @@ static void test_SpeechRecognizer(void) ok(ref == 1, "Got unexpected ref %lu.\n", ref); hr = RoActivateInstance(hstr, &inspectable); - ok(hr == S_OK || broken(hr == SPERR_WINRT_INTERNAL_ERROR), "Got unexpected hr %#lx.\n", hr); + ok(hr == S_OK || hr == SPERR_WINRT_INTERNAL_ERROR, "Got unexpected hr %#lx.\n", hr); if (hr == S_OK) { @@ -1526,7 +1526,7 @@ static void test_SpeechRecognizer(void) } else if (hr == SPERR_WINRT_INTERNAL_ERROR) /* Not sure when this triggers. Probably if a language pack is not installed. */ { - win_skip("Could not init SpeechRecognizer with default language!\n"); + skip("Could not init SpeechRecognizer with default language!\n"); } done: @@ -1743,12 +1743,12 @@ static void test_Recognition(void) ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr); hr = RoActivateInstance(hstr, &inspectable); - ok(hr == S_OK || broken(hr == SPERR_WINRT_INTERNAL_ERROR || hr == REGDB_E_CLASSNOTREG), "Got unexpected hr %#lx.\n", hr); + ok(hr == S_OK || hr == SPERR_WINRT_INTERNAL_ERROR || broken(hr == REGDB_E_CLASSNOTREG), "Got unexpected hr %#lx.\n", hr); WindowsDeleteString(hstr); - if (FAILED(hr)) /* Win 8 and 8.1 and Win10 without enabled SR. */ + if (FAILED(hr)) /* Win 8 and 8.1 and Win10 without enabled SR. Wine with missing Unix side dependencies. */ { - win_skip("SpeechRecognizer cannot be activated!\n"); + skip("SpeechRecognizer cannot be activated!\n"); goto done; } From 903036c31c6d34ad129dcca58a421e60cf216f0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 20 Feb 2023 23:11:02 +0100 Subject: [PATCH 129/413] windows.media.speech: Implement Vosk create and release functions in the unixlib. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/Makefile.in | 2 +- dlls/windows.media.speech/private.h | 3 + dlls/windows.media.speech/recognizer.c | 42 ++++++ dlls/windows.media.speech/unixlib.c | 169 ++++++++++++++++++++++++- dlls/windows.media.speech/unixlib.h | 16 +++ 5 files changed, 230 insertions(+), 2 deletions(-) diff --git a/dlls/windows.media.speech/Makefile.in b/dlls/windows.media.speech/Makefile.in index 11a8ad2bba8..ea1e4272139 100644 --- a/dlls/windows.media.speech/Makefile.in +++ b/dlls/windows.media.speech/Makefile.in @@ -1,6 +1,6 @@ MODULE = windows.media.speech.dll UNIXLIB = windows.media.speech.so -IMPORTS = combase uuid +IMPORTS = combase uuid user32 SOURCES = \ async.c \ diff --git a/dlls/windows.media.speech/private.h b/dlls/windows.media.speech/private.h index 4b6a8327d6e..60d09c9f7d1 100644 --- a/dlls/windows.media.speech/private.h +++ b/dlls/windows.media.speech/private.h @@ -31,6 +31,7 @@ #include "windef.h" #include "winbase.h" #include "winstring.h" +#include "winuser.h" #include "objbase.h" #include "activation.h" @@ -47,6 +48,8 @@ #include "wine/list.h" +#define SPERR_WINRT_INTERNAL_ERROR 0x800455a0 + /* * * Windows.Media.SpeechRecognition diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 790d127fc64..7f45605d23a 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -25,6 +25,9 @@ #include "wine/debug.h" +#include "unixlib.h" +#include "wine/unixlib.h" + WINE_DEFAULT_DEBUG_CHANNEL(speech); /* @@ -171,6 +174,8 @@ struct session IAudioCaptureClient *capture_client; WAVEFORMATEX capture_wfx; + speech_recognizer_handle unix_handle; + HANDLE worker_thread, worker_control_event, audio_buf_event; BOOLEAN worker_running, worker_paused; CRITICAL_SECTION cs; @@ -319,7 +324,9 @@ static ULONG WINAPI session_AddRef( ISpeechContinuousRecognitionSession *iface ) static ULONG WINAPI session_Release( ISpeechContinuousRecognitionSession *iface ) { struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); + struct speech_release_recognizer_params release_params; ULONG ref = InterlockedDecrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); if (!ref) @@ -345,6 +352,9 @@ static ULONG WINAPI session_Release( ISpeechContinuousRecognitionSession *iface impl->cs.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&impl->cs); + release_params.handle = impl->unix_handle; + WINE_UNIX_CALL(unix_speech_release_recognizer, &release_params); + IVector_ISpeechRecognitionConstraint_Release(impl->constraints); free(impl); } @@ -1080,6 +1090,35 @@ static HRESULT recognizer_factory_create_audio_capture(struct session *session) return hr; } +static HRESULT recognizer_factory_create_unix_instance( struct session *session ) +{ + struct speech_create_recognizer_params create_params = { 0 }; + WCHAR locale[LOCALE_NAME_MAX_LENGTH]; + NTSTATUS status; + INT len; + + if (!(len = GetUserDefaultLocaleName(locale, LOCALE_NAME_MAX_LENGTH))) + return E_FAIL; + + if (CharLowerBuffW(locale, len) != len) + return E_FAIL; + + if (!WideCharToMultiByte(CP_ACP, 0, locale, len, create_params.locale, ARRAY_SIZE(create_params.locale), NULL, NULL)) + return HRESULT_FROM_WIN32(GetLastError()); + + create_params.sample_rate = (FLOAT)session->capture_wfx.nSamplesPerSec; + + if ((status = WINE_UNIX_CALL(unix_speech_create_recognizer, &create_params))) + { + ERR("Unable to create Vosk instance for locale %s, status %#lx. Speech recognition won't work.\n", debugstr_a(create_params.locale), status); + return SPERR_WINRT_INTERNAL_ERROR; + } + + session->unix_handle = create_params.handle; + + return S_OK; +} + static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface, ILanguage *language, ISpeechRecognizer **speechrecognizer ) { struct recognizer *impl; @@ -1126,6 +1165,9 @@ static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface if (FAILED(hr = recognizer_factory_create_audio_capture(session))) goto error; + if (FAILED(hr = recognizer_factory_create_unix_instance(session))) + goto error; + InitializeCriticalSection(&session->cs); session->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": recognition_session.cs"); diff --git a/dlls/windows.media.speech/unixlib.c b/dlls/windows.media.speech/unixlib.c index 6a9dac6f067..155780a3e4b 100644 --- a/dlls/windows.media.speech/unixlib.c +++ b/dlls/windows.media.speech/unixlib.c @@ -26,9 +26,12 @@ #include #include -#include #include +#include +#include #include +#include +#include #ifdef SONAME_LIBVOSK #pragma GCC diagnostic push @@ -97,6 +100,164 @@ static NTSTATUS process_detach( void *args ) return STATUS_SUCCESS; } +static inline speech_recognizer_handle vosk_recognizer_to_handle( VoskRecognizer *recognizer ) +{ + return (speech_recognizer_handle)(UINT_PTR)recognizer; +} + +static inline VoskRecognizer *vosk_recognizer_from_handle( speech_recognizer_handle handle ) +{ + return (VoskRecognizer *)(UINT_PTR)handle; +} + +static NTSTATUS find_model_by_locale_and_path( const char *path, const char *locale, VoskModel **model ) +{ + static const char *vosk_model_identifier_small = "vosk-model-small-"; + static const char *vosk_model_identifier = "vosk-model-"; + size_t ident_small_len = strlen(vosk_model_identifier_small); + size_t ident_len = strlen(vosk_model_identifier); + char *ent_name, *model_path, *best_match, *delim; + NTSTATUS status = STATUS_UNSUCCESSFUL; + struct dirent *dirent; + size_t path_len, len; + DIR *dir; + + TRACE("path %s, locale %s, model %p.\n", path, debugstr_a(locale), model); + + if (!path || !locale || (len = strlen(locale)) < 4) + return STATUS_UNSUCCESSFUL; + + if (!(dir = opendir(path))) + return STATUS_UNSUCCESSFUL; + + delim = strchr(locale, '-'); + path_len = strlen(path); + best_match = NULL; + *model = NULL; + + while ((dirent = readdir(dir))) + { + ent_name = dirent->d_name; + + if (!strncmp(ent_name, vosk_model_identifier_small, ident_small_len)) + ent_name += ident_small_len; + else if (!strncmp(ent_name, vosk_model_identifier, ident_len)) + ent_name += ident_len; + else + continue; + + /* + * Find the first matching model for lang and region (en-us). + * If there isn't any, pick the first one just matching lang (en). + */ + if (!strncmp(ent_name, locale, len)) + { + if (best_match) free(best_match); + best_match = strdup(dirent->d_name); + break; + } + + if (!best_match && !strncmp(ent_name, locale, delim - locale)) + best_match = strdup(dirent->d_name); + } + + closedir(dir); + + if (!best_match) + return STATUS_UNSUCCESSFUL; + + if (!(model_path = malloc(path_len + 1 /* '/' */ + strlen(best_match) + 1))) + { + status = STATUS_NO_MEMORY; + goto done; + } + + sprintf(model_path, "%s/%s", path, best_match); + + TRACE("trying to load Vosk model %s.\n", debugstr_a(model_path)); + + if ((*model = p_vosk_model_new(model_path)) != NULL) + status = STATUS_SUCCESS; + +done: + free(model_path); + free(best_match); + + return status; +} + +static NTSTATUS find_model_by_locale( const char *locale, VoskModel **model ) +{ + const char *suffix = NULL; + char *env, *path = NULL; + NTSTATUS status; + + TRACE("locale %s, model %p.\n", debugstr_a(locale), model); + + if (!model) + return STATUS_UNSUCCESSFUL; + + if (!find_model_by_locale_and_path(getenv("VOSK_MODEL_PATH"), locale, model)) + return STATUS_SUCCESS; + if (!find_model_by_locale_and_path("/usr/share/vosk", locale, model)) + return STATUS_SUCCESS; + + if ((env = getenv("XDG_CACHE_HOME"))) + suffix = "/vosk"; + else if ((env = getenv("HOME"))) + suffix = "/.cache/vosk"; + else + return STATUS_UNSUCCESSFUL; + + if (!(path = malloc(strlen(env) + strlen(suffix) + 1))) + return STATUS_NO_MEMORY; + + sprintf(path, "%s%s", env, suffix); + status = find_model_by_locale_and_path(path, locale, model); + free(path); + + return status; +} + +static NTSTATUS speech_create_recognizer( void *args ) +{ + struct speech_create_recognizer_params *params = args; + VoskRecognizer *recognizer = NULL; + VoskModel *model = NULL; + NTSTATUS status = STATUS_SUCCESS; + + TRACE("args %p.\n", args); + + if (!vosk_handle) + return STATUS_NOT_SUPPORTED; + + if ((status = find_model_by_locale(params->locale, &model))) + return status; + + if (!(recognizer = p_vosk_recognizer_new(model, params->sample_rate))) + status = STATUS_UNSUCCESSFUL; + + /* VoskModel is reference-counted. A VoskRecognizer keeps a reference to its model. */ + p_vosk_model_free(model); + + params->handle = vosk_recognizer_to_handle(recognizer); + return status; +} + +static NTSTATUS speech_release_recognizer( void *args ) +{ + struct speech_release_recognizer_params *params = args; + + TRACE("args %p.\n", args); + + if (!vosk_handle) + return STATUS_NOT_SUPPORTED; + + p_vosk_recognizer_free(vosk_recognizer_from_handle(params->handle)); + + return STATUS_SUCCESS; +} + #else /* SONAME_LIBVOSK */ #define MAKE_UNSUPPORTED_FUNC( f ) \ @@ -108,6 +269,8 @@ static NTSTATUS process_detach( void *args ) MAKE_UNSUPPORTED_FUNC(process_attach) MAKE_UNSUPPORTED_FUNC(process_detach) +MAKE_UNSUPPORTED_FUNC(speech_create_recognizer) +MAKE_UNSUPPORTED_FUNC(speech_release_recognizer) #undef MAKE_UNSUPPORTED_FUNC #endif /* SONAME_LIBVOSK */ @@ -116,10 +279,14 @@ const unixlib_entry_t __wine_unix_call_funcs[] = { process_attach, process_detach, + speech_create_recognizer, + speech_release_recognizer, }; const unixlib_entry_t __wine_unix_call_wow64_funcs[] = { process_attach, process_detach, + speech_create_recognizer, + speech_release_recognizer, }; diff --git a/dlls/windows.media.speech/unixlib.h b/dlls/windows.media.speech/unixlib.h index 6c337e54511..974e8d5f797 100644 --- a/dlls/windows.media.speech/unixlib.h +++ b/dlls/windows.media.speech/unixlib.h @@ -30,10 +30,26 @@ #include "wine/unixlib.h" +typedef UINT64 speech_recognizer_handle; + +struct speech_create_recognizer_params +{ + speech_recognizer_handle handle; + CHAR locale[LOCALE_NAME_MAX_LENGTH]; + FLOAT sample_rate; +}; + +struct speech_release_recognizer_params +{ + speech_recognizer_handle handle; +}; + enum unix_funcs { unix_process_attach, unix_process_detach, + unix_speech_create_recognizer, + unix_speech_release_recognizer, }; #endif From f63b53812cb62897fe2ceb218deb55b66969c7ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 6 Mar 2023 23:51:31 +0100 Subject: [PATCH 130/413] windows.media.speech: Move unix side recognizer creation to recognizer_CompileConstraintsAsync(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 76 ++++++++++++++------------ 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 7f45605d23a..77fb38455f7 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -735,17 +735,55 @@ static HRESULT WINAPI recognizer_get_UIOptions( ISpeechRecognizer *iface, ISpeec return E_NOTIMPL; } +static HRESULT recognizer_create_unix_instance( struct session *session ) +{ + struct speech_create_recognizer_params create_params = { 0 }; + WCHAR locale[LOCALE_NAME_MAX_LENGTH]; + NTSTATUS status; + INT len; + + if (!(len = GetUserDefaultLocaleName(locale, LOCALE_NAME_MAX_LENGTH))) + return E_FAIL; + + if (CharLowerBuffW(locale, len) != len) + return E_FAIL; + + if (!WideCharToMultiByte(CP_ACP, 0, locale, len, create_params.locale, ARRAY_SIZE(create_params.locale), NULL, NULL)) + return HRESULT_FROM_WIN32(GetLastError()); + + create_params.sample_rate = (FLOAT)session->capture_wfx.nSamplesPerSec; + + if ((status = WINE_UNIX_CALL(unix_speech_create_recognizer, &create_params))) + { + ERR("Unable to create Vosk instance for locale %s, status %#lx. Speech recognition won't work.\n", debugstr_a(create_params.locale), status); + return SPERR_WINRT_INTERNAL_ERROR; + } + + session->unix_handle = create_params.handle; + + return S_OK; +} + static HRESULT recognizer_compile_constraints_async( IInspectable *invoker, IInspectable **result ) { - return compilation_result_create(SpeechRecognitionResultStatus_Success, (ISpeechRecognitionCompilationResult **) result); + struct recognizer *impl = impl_from_ISpeechRecognizer((ISpeechRecognizer *)invoker); + struct session *session = impl_from_ISpeechContinuousRecognitionSession(impl->session); + HRESULT hr; + + if (FAILED(hr = recognizer_create_unix_instance(session))) + { + WARN("Failed to created recognizer instance.\n"); + return compilation_result_create(SpeechRecognitionResultStatus_GrammarCompilationFailure, (ISpeechRecognitionCompilationResult **) result); + } + else return compilation_result_create(SpeechRecognitionResultStatus_Success, (ISpeechRecognitionCompilationResult **) result); } static HRESULT WINAPI recognizer_CompileConstraintsAsync( ISpeechRecognizer *iface, IAsyncOperation_SpeechRecognitionCompilationResult **operation ) { IAsyncOperation_IInspectable **value = (IAsyncOperation_IInspectable **)operation; - FIXME("iface %p, operation %p semi-stub!\n", iface, operation); - return async_operation_inspectable_create(&IID_IAsyncOperation_SpeechRecognitionCompilationResult, NULL, recognizer_compile_constraints_async, value); + TRACE("iface %p, operation %p semi-stub!\n", iface, operation); + return async_operation_inspectable_create(&IID_IAsyncOperation_SpeechRecognitionCompilationResult, (IInspectable *)iface, recognizer_compile_constraints_async, value); } static HRESULT WINAPI recognizer_RecognizeAsync( ISpeechRecognizer *iface, @@ -1090,35 +1128,6 @@ static HRESULT recognizer_factory_create_audio_capture(struct session *session) return hr; } -static HRESULT recognizer_factory_create_unix_instance( struct session *session ) -{ - struct speech_create_recognizer_params create_params = { 0 }; - WCHAR locale[LOCALE_NAME_MAX_LENGTH]; - NTSTATUS status; - INT len; - - if (!(len = GetUserDefaultLocaleName(locale, LOCALE_NAME_MAX_LENGTH))) - return E_FAIL; - - if (CharLowerBuffW(locale, len) != len) - return E_FAIL; - - if (!WideCharToMultiByte(CP_ACP, 0, locale, len, create_params.locale, ARRAY_SIZE(create_params.locale), NULL, NULL)) - return HRESULT_FROM_WIN32(GetLastError()); - - create_params.sample_rate = (FLOAT)session->capture_wfx.nSamplesPerSec; - - if ((status = WINE_UNIX_CALL(unix_speech_create_recognizer, &create_params))) - { - ERR("Unable to create Vosk instance for locale %s, status %#lx. Speech recognition won't work.\n", debugstr_a(create_params.locale), status); - return SPERR_WINRT_INTERNAL_ERROR; - } - - session->unix_handle = create_params.handle; - - return S_OK; -} - static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface, ILanguage *language, ISpeechRecognizer **speechrecognizer ) { struct recognizer *impl; @@ -1165,9 +1174,6 @@ static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface if (FAILED(hr = recognizer_factory_create_audio_capture(session))) goto error; - if (FAILED(hr = recognizer_factory_create_unix_instance(session))) - goto error; - InitializeCriticalSection(&session->cs); session->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": recognition_session.cs"); From 4a58b0826cc7a38452f7791e1111197c8a63b669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 27 Feb 2023 13:22:00 +0100 Subject: [PATCH 131/413] windows.media.speech: Implement recognition in the unixlib. CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 35 +++++++++++- dlls/windows.media.speech/unixlib.c | 79 ++++++++++++++++++++++++++ dlls/windows.media.speech/unixlib.h | 26 ++++++++- 3 files changed, 138 insertions(+), 2 deletions(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 77fb38455f7..ea3b1b1859a 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -196,10 +196,13 @@ static DWORD CALLBACK session_worker_thread_cb( void *args ) { ISpeechContinuousRecognitionSession *iface = args; struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); + struct speech_get_recognition_result_params recognition_result_params; + struct speech_recognize_audio_params recognize_audio_params; BOOLEAN running = TRUE, paused = FALSE; UINT32 frame_count, tmp_buf_size; BYTE *audio_buf, *tmp_buf = NULL; DWORD flags, status; + NTSTATUS nt_status; HANDLE events[2]; HRESULT hr; @@ -269,7 +272,37 @@ static DWORD CALLBACK session_worker_thread_cb( void *args ) IAudioCaptureClient_ReleaseBuffer(impl->capture_client, frames_available); } - /* TODO: Send mic data to recognizer and handle results. */ + recognize_audio_params.handle = impl->unix_handle; + recognize_audio_params.samples = tmp_buf; + recognize_audio_params.samples_size = tmp_buf_offset; + recognize_audio_params.status = RECOGNITION_STATUS_EXCEPTION; + + if (NT_ERROR(nt_status = WINE_UNIX_CALL(unix_speech_recognize_audio, &recognize_audio_params))) + WARN("unix_speech_recognize_audio failed with status %#lx.\n", nt_status); + + if (recognize_audio_params.status != RECOGNITION_STATUS_RESULT_AVAILABLE) + continue; + + recognition_result_params.handle = impl->unix_handle; + recognition_result_params.result_buf = NULL; + recognition_result_params.result_buf_size = 512; + + do + { + recognition_result_params.result_buf = realloc(recognition_result_params.result_buf, recognition_result_params.result_buf_size); + } + while (WINE_UNIX_CALL(unix_speech_get_recognition_result, &recognition_result_params) == STATUS_BUFFER_TOO_SMALL && + recognition_result_params.result_buf); + + if (!recognition_result_params.result_buf) + { + WARN("memory allocation failed.\n"); + break; + } + + /* TODO: Compare recognized text to available options. */ + + free(recognition_result_params.result_buf); } else { diff --git a/dlls/windows.media.speech/unixlib.c b/dlls/windows.media.speech/unixlib.c index 155780a3e4b..c41bb4cc445 100644 --- a/dlls/windows.media.speech/unixlib.c +++ b/dlls/windows.media.speech/unixlib.c @@ -59,6 +59,9 @@ MAKE_FUNCPTR(vosk_model_new); MAKE_FUNCPTR(vosk_model_free); MAKE_FUNCPTR(vosk_recognizer_new); MAKE_FUNCPTR(vosk_recognizer_free); +MAKE_FUNCPTR(vosk_recognizer_accept_waveform); +MAKE_FUNCPTR(vosk_recognizer_final_result); +MAKE_FUNCPTR(vosk_recognizer_reset); #undef MAKE_FUNCPTR static NTSTATUS process_attach( void *args ) @@ -80,6 +83,9 @@ static NTSTATUS process_attach( void *args ) LOAD_FUNCPTR(vosk_model_free) LOAD_FUNCPTR(vosk_recognizer_new) LOAD_FUNCPTR(vosk_recognizer_free) + LOAD_FUNCPTR(vosk_recognizer_accept_waveform) + LOAD_FUNCPTR(vosk_recognizer_final_result) + LOAD_FUNCPTR(vosk_recognizer_reset) #undef LOAD_FUNCPTR return STATUS_SUCCESS; @@ -258,6 +264,73 @@ static NTSTATUS speech_release_recognizer( void *args ) return STATUS_SUCCESS; } +static NTSTATUS speech_recognize_audio( void *args ) +{ + struct speech_recognize_audio_params *params = args; + VoskRecognizer *recognizer = vosk_recognizer_from_handle(params->handle); + + if (!vosk_handle) + return STATUS_NOT_SUPPORTED; + + if (!recognizer) + return STATUS_UNSUCCESSFUL; + + params->status = p_vosk_recognizer_accept_waveform(recognizer, (const char *)params->samples, params->samples_size); + + return STATUS_SUCCESS; +} + +static NTSTATUS speech_get_recognition_result( void* args ) +{ + struct speech_get_recognition_result_params *params = args; + VoskRecognizer *recognizer = vosk_recognizer_from_handle(params->handle); + static const char *result_json_start = "{\n \"text\" : \""; + const size_t json_start_len = strlen(result_json_start); + static size_t last_result_len = 0; + static char *last_result = NULL; + const char *tmp = NULL; + + TRACE("args %p.\n", args); + + if (!vosk_handle) + return STATUS_NOT_SUPPORTED; + + if (!recognizer) + return STATUS_UNSUCCESSFUL; + + if (!last_result) + { + if ((tmp = p_vosk_recognizer_final_result(recognizer))) + { + last_result = strdup(tmp); + tmp = last_result; + + /* Operations to remove the JSON wrapper "{\n \"text\" : \"some recognized text\"\n}" -> "some recognized text\0" */ + memmove(last_result, last_result + json_start_len, strlen(last_result) - json_start_len + 1); + last_result = strrchr(last_result, '\"'); + last_result[0] = '\0'; + + last_result = (char *)tmp; + last_result_len = strlen(last_result); + TRACE("last_result %s.\n", debugstr_a(last_result)); + } + else return STATUS_NOT_FOUND; + } + else if (params->result_buf_size >= last_result_len + 1) + { + memcpy(params->result_buf, last_result, last_result_len + 1); + p_vosk_recognizer_reset(recognizer); + + free (last_result); + last_result = NULL; + + return STATUS_SUCCESS; + } + + params->result_buf_size = last_result_len + 1; + return STATUS_BUFFER_TOO_SMALL; +} + #else /* SONAME_LIBVOSK */ #define MAKE_UNSUPPORTED_FUNC( f ) \ @@ -271,6 +344,8 @@ MAKE_UNSUPPORTED_FUNC(process_attach) MAKE_UNSUPPORTED_FUNC(process_detach) MAKE_UNSUPPORTED_FUNC(speech_create_recognizer) MAKE_UNSUPPORTED_FUNC(speech_release_recognizer) +MAKE_UNSUPPORTED_FUNC(speech_recognize_audio) +MAKE_UNSUPPORTED_FUNC(speech_get_recognition_result) #undef MAKE_UNSUPPORTED_FUNC #endif /* SONAME_LIBVOSK */ @@ -281,6 +356,8 @@ const unixlib_entry_t __wine_unix_call_funcs[] = process_detach, speech_create_recognizer, speech_release_recognizer, + speech_recognize_audio, + speech_get_recognition_result, }; const unixlib_entry_t __wine_unix_call_wow64_funcs[] = @@ -289,4 +366,6 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = process_detach, speech_create_recognizer, speech_release_recognizer, + speech_recognize_audio, + speech_get_recognition_result, }; diff --git a/dlls/windows.media.speech/unixlib.h b/dlls/windows.media.speech/unixlib.h index 974e8d5f797..a07d76705d5 100644 --- a/dlls/windows.media.speech/unixlib.h +++ b/dlls/windows.media.speech/unixlib.h @@ -44,12 +44,36 @@ struct speech_release_recognizer_params speech_recognizer_handle handle; }; -enum unix_funcs +enum speech_recognition_status +{ + RECOGNITION_STATUS_CONTINUING, + RECOGNITION_STATUS_RESULT_AVAILABLE, + RECOGNITION_STATUS_EXCEPTION, +}; + +struct speech_recognize_audio_params +{ + speech_recognizer_handle handle; + const BYTE *samples; + UINT32 samples_size; + enum speech_recognition_status status; +}; + +struct speech_get_recognition_result_params +{ + speech_recognizer_handle handle; + char *result_buf; + UINT32 result_buf_size; +}; + +enum vosk_funcs { unix_process_attach, unix_process_detach, unix_speech_create_recognizer, unix_speech_release_recognizer, + unix_speech_recognize_audio, + unix_speech_get_recognition_result, }; #endif From 2f5d3032f3b110fad9d63d2cf29aaf97af4e0e6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Wed, 25 May 2022 14:14:18 +0200 Subject: [PATCH 132/413] windows.media.speech: Compare recognized words with available constraints. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 115 ++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 1 deletion(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index ea3b1b1859a..3d6d85336ef 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -192,18 +192,105 @@ static inline struct session *impl_from_ISpeechContinuousRecognitionSession( ISp return CONTAINING_RECORD(iface, struct session, ISpeechContinuousRecognitionSession_iface); } +static HRESULT session_find_constraint_by_string(struct session *session, WCHAR *str, HSTRING *hstr_out, ISpeechRecognitionConstraint **out) +{ + ISpeechRecognitionListConstraint *list_constraint; + IIterable_IInspectable *constraints_iterable; + IIterator_IInspectable *constraints_iterator; + ISpeechRecognitionConstraint *constraint; + IIterable_HSTRING *commands_iterable; + IIterator_HSTRING *commands_iterator; + BOOLEAN has_constraint, has_command; + IVector_HSTRING *commands; + const WCHAR *command_str; + HSTRING command; + HRESULT hr; + + TRACE("session %p, str %s, out %p.\n", session, debugstr_w(str), out); + + if (FAILED(hr = IVector_ISpeechRecognitionConstraint_QueryInterface(session->constraints, &IID_IIterable_ISpeechRecognitionConstraint, (void **)&constraints_iterable))) + return hr; + + if (FAILED(hr = IIterable_IInspectable_First(constraints_iterable, &constraints_iterator))) + { + IIterable_IInspectable_Release(constraints_iterable); + return hr; + } + + *out = NULL; + + for (hr = IIterator_IInspectable_get_HasCurrent(constraints_iterator, &has_constraint); SUCCEEDED(hr) && has_constraint && !(*out); hr = IIterator_IInspectable_MoveNext(constraints_iterator, &has_constraint)) + { + list_constraint = NULL; + commands_iterable = NULL; + commands_iterator = NULL; + commands = NULL; + + if (FAILED(IIterator_IInspectable_get_Current(constraints_iterator, (IInspectable **)&constraint))) + goto skip; + + if (FAILED(ISpeechRecognitionConstraint_QueryInterface(constraint, &IID_ISpeechRecognitionListConstraint, (void**)&list_constraint))) + goto skip; + + if (FAILED(ISpeechRecognitionListConstraint_get_Commands(list_constraint, &commands))) + goto skip; + + if (FAILED(IVector_HSTRING_QueryInterface(commands, &IID_IIterable_HSTRING, (void **)&commands_iterable))) + goto skip; + + if (FAILED(IIterable_HSTRING_First(commands_iterable, &commands_iterator))) + goto skip; + + for (hr = IIterator_HSTRING_get_HasCurrent(commands_iterator, &has_command); SUCCEEDED(hr) && has_command && !(*out); hr = IIterator_HSTRING_MoveNext(commands_iterator, &has_command)) + { + if (FAILED(IIterator_HSTRING_get_Current(commands_iterator, &command))) + continue; + + command_str = WindowsGetStringRawBuffer(command, NULL); + + TRACE("Comparing str %s to command_str %s.\n", debugstr_w(str), debugstr_w(command_str)); + + if (!wcsicmp(str, command_str)) + { + TRACE("constraint %p has str %s.\n", constraint, debugstr_w(str)); + ISpeechRecognitionConstraint_AddRef((*out = constraint)); + WindowsDuplicateString(command, hstr_out); + } + + WindowsDeleteString(command); + } + +skip: + if (commands_iterator) IIterator_HSTRING_Release(commands_iterator); + if (commands_iterable) IIterable_HSTRING_Release(commands_iterable); + if (commands) IVector_HSTRING_Release(commands); + + if (list_constraint) ISpeechRecognitionListConstraint_Release(list_constraint); + if (constraint) ISpeechRecognitionConstraint_Release(constraint); + } + + IIterator_IInspectable_Release(constraints_iterator); + IIterable_IInspectable_Release(constraints_iterable); + + hr = (*out) ? S_OK : COR_E_KEYNOTFOUND; + return hr; +} + static DWORD CALLBACK session_worker_thread_cb( void *args ) { ISpeechContinuousRecognitionSession *iface = args; struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); struct speech_get_recognition_result_params recognition_result_params; struct speech_recognize_audio_params recognize_audio_params; + ISpeechRecognitionConstraint *constraint; BOOLEAN running = TRUE, paused = FALSE; UINT32 frame_count, tmp_buf_size; BYTE *audio_buf, *tmp_buf = NULL; + WCHAR *recognized_text; DWORD flags, status; NTSTATUS nt_status; HANDLE events[2]; + HSTRING hstring; HRESULT hr; SetThreadDescription(GetCurrentThread(), L"wine_speech_recognition_session_worker"); @@ -253,6 +340,7 @@ static DWORD CALLBACK session_worker_thread_cb( void *args ) { SIZE_T packet_size = 0, tmp_buf_offset = 0; UINT32 frames_available = 0; + INT recognized_text_len = 0; while (tmp_buf_offset < tmp_buf_size && IAudioCaptureClient_GetBuffer(impl->capture_client, &audio_buf, &frames_available, &flags, NULL, NULL) == S_OK) @@ -300,8 +388,33 @@ static DWORD CALLBACK session_worker_thread_cb( void *args ) break; } - /* TODO: Compare recognized text to available options. */ + /* Silence was recognized. */ + if (!strcmp(recognition_result_params.result_buf, "")) + { + free(recognition_result_params.result_buf); + continue; + } + + recognized_text_len = MultiByteToWideChar(CP_UTF8, 0, recognition_result_params.result_buf, -1, NULL, 0); + + if (!(recognized_text = malloc(recognized_text_len * sizeof(WCHAR)))) + { + free(recognition_result_params.result_buf); + WARN("memory allocation failed.\n"); + break; + } + + MultiByteToWideChar(CP_UTF8, 0, recognition_result_params.result_buf, -1, recognized_text, recognized_text_len); + + if (SUCCEEDED(hr = session_find_constraint_by_string(impl, recognized_text, &hstring, &constraint))) + { + /* TODO: Send event. */ + + WindowsDeleteString(hstring); + ISpeechRecognitionConstraint_Release(constraint); + } + free(recognized_text); free(recognition_result_params.result_buf); } else From 615140c355393514523340fb7a6454b3b752d463 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 30 May 2022 18:36:51 +0200 Subject: [PATCH 133/413] windows.media.speech: Send event on recognition success. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 368 ++++++++++++++++++++++++- 1 file changed, 367 insertions(+), 1 deletion(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 3d6d85336ef..7721764f351 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -30,6 +30,363 @@ WINE_DEFAULT_DEBUG_CHANNEL(speech); +struct recognition_result +{ + ISpeechRecognitionResult ISpeechRecognitionResult_iface; + ISpeechRecognitionResult2 ISpeechRecognitionResult2_iface; + LONG ref; + + ISpeechRecognitionConstraint *constraint; + HSTRING text; +}; + +static inline struct recognition_result *impl_from_ISpeechRecognitionResult( ISpeechRecognitionResult *iface ) +{ + return CONTAINING_RECORD(iface, struct recognition_result, ISpeechRecognitionResult_iface); +} + +static HRESULT WINAPI recognition_result_QueryInterface( ISpeechRecognitionResult *iface, REFIID iid, void **out ) +{ + struct recognition_result *impl = impl_from_ISpeechRecognitionResult(iface); + + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) || + IsEqualGUID(iid, &IID_IInspectable) || + IsEqualGUID(iid, &IID_IAgileObject) || + IsEqualGUID(iid, &IID_ISpeechRecognitionResult)) + { + IInspectable_AddRef((*out = &impl->ISpeechRecognitionResult_iface)); + return S_OK; + } + + if (IsEqualGUID(iid, &IID_ISpeechRecognitionResult2)) + { + IInspectable_AddRef((*out = &impl->ISpeechRecognitionResult2_iface)); + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI recognition_result_AddRef( ISpeechRecognitionResult *iface ) +{ + struct recognition_result *impl = impl_from_ISpeechRecognitionResult(iface); + ULONG ref = InterlockedIncrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + return ref; +} + +static ULONG WINAPI recognition_result_Release( ISpeechRecognitionResult *iface ) +{ + struct recognition_result *impl = impl_from_ISpeechRecognitionResult(iface); + + ULONG ref = InterlockedDecrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + + if(!ref) + { + ISpeechRecognitionConstraint_Release(impl->constraint); + WindowsDeleteString(impl->text); + free(impl); + } + + return ref; +} + +static HRESULT WINAPI recognition_result_GetIids( ISpeechRecognitionResult *iface, ULONG *iid_count, IID **iids ) +{ + FIXME("iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids); + return E_NOTIMPL; +} + +static HRESULT WINAPI recognition_result_GetRuntimeClassName( ISpeechRecognitionResult *iface, HSTRING *class_name ) +{ + FIXME("iface %p, class_name %p stub!\n", iface, class_name); + return E_NOTIMPL; +} + +static HRESULT WINAPI recognition_result_GetTrustLevel( ISpeechRecognitionResult *iface, TrustLevel *trust_level ) +{ + FIXME("iface %p, trust_level %p stub!\n", iface, trust_level); + return E_NOTIMPL; +} + +static HRESULT WINAPI recognition_result_get_Status( ISpeechRecognitionResult *iface, SpeechRecognitionResultStatus *value ) +{ + FIXME("iface %p, operation %p stub!\n", iface, value); + *value = SpeechRecognitionResultStatus_Success; + return S_OK; +} + +static HRESULT WINAPI recognition_result_get_Text( ISpeechRecognitionResult *iface, HSTRING *value ) +{ + struct recognition_result *impl = impl_from_ISpeechRecognitionResult(iface); + TRACE("iface %p, operation %p, text: %s.\n", iface, value, debugstr_hstring(impl->text)); + return WindowsDuplicateString(impl->text, value); +} + +static HRESULT WINAPI recognition_result_get_Confidence( ISpeechRecognitionResult *iface, SpeechRecognitionConfidence *value ) +{ + FIXME("iface %p, operation %p semi stub!\n", iface, value); + *value = SpeechRecognitionConfidence_High; + return S_OK; +} + +static HRESULT WINAPI recognition_result_get_SemanticInterpretation( ISpeechRecognitionResult *iface, + ISpeechRecognitionSemanticInterpretation **value ) +{ + FIXME("iface %p, operation %p stub!\n", iface, value); + return E_NOTIMPL; +} + +static HRESULT WINAPI recognition_result_GetAlternates( ISpeechRecognitionResult *iface, + UINT32 max_amount, + IVectorView_SpeechRecognitionResult **results ) +{ + IVector_IInspectable *vector; + struct vector_iids constraints_iids = + { + .iterable = &IID_IVectorView_SpeechRecognitionResult, + .iterator = &IID_IVectorView_SpeechRecognitionResult, + .vector = &IID_IVector_IInspectable, + .view = &IID_IVectorView_SpeechRecognitionResult, + }; + + FIXME("iface %p, max_amount %u, results %p stub!\n", iface, max_amount, results); + + vector_inspectable_create(&constraints_iids, (IVector_IInspectable **)&vector); + IVector_IInspectable_GetView(vector, (IVectorView_IInspectable **)results); + IVector_IInspectable_Release(vector); + return S_OK; +} + +static HRESULT WINAPI recognition_result_get_Constraint( ISpeechRecognitionResult *iface, ISpeechRecognitionConstraint **value ) +{ + struct recognition_result *impl = impl_from_ISpeechRecognitionResult(iface); + TRACE("iface %p, operation %p.\n", iface, value); + ISpeechRecognitionConstraint_AddRef((*value = impl->constraint)); + return S_OK; +} + +static HRESULT WINAPI recognition_result_get_RulePath( ISpeechRecognitionResult *iface, IVectorView_HSTRING **value ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +static HRESULT WINAPI recognition_result_get_RawConfidence( ISpeechRecognitionResult *iface, DOUBLE *value ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +static const struct ISpeechRecognitionResultVtbl recognition_result_vtbl = +{ + /* IUnknown methods */ + recognition_result_QueryInterface, + recognition_result_AddRef, + recognition_result_Release, + /* IInspectable methods */ + recognition_result_GetIids, + recognition_result_GetRuntimeClassName, + recognition_result_GetTrustLevel, + /* ISpeechRecognitionResult methods */ + recognition_result_get_Status, + recognition_result_get_Text, + recognition_result_get_Confidence, + recognition_result_get_SemanticInterpretation, + recognition_result_GetAlternates, + recognition_result_get_Constraint, + recognition_result_get_RulePath, + recognition_result_get_RawConfidence +}; + +DEFINE_IINSPECTABLE(recognition_result2, ISpeechRecognitionResult2, struct recognition_result, ISpeechRecognitionResult_iface) + +static HRESULT WINAPI recognition_result2_get_PhraseStartTime( ISpeechRecognitionResult2 *iface, DateTime *value ) +{ + DateTime dt = { .UniversalTime = 0 }; + FIXME("iface %p, value %p stub!\n", iface, value); + *value = dt; + return S_OK; +} + + +static HRESULT WINAPI recognition_result2_get_PhraseDuration( ISpeechRecognitionResult2 *iface, TimeSpan *value ) +{ + TimeSpan ts = { .Duration = 50000000LL }; /* Use 5 seconds as stub value. */ + FIXME("iface %p, value %p stub!\n", iface, value); + *value = ts; + return S_OK; +} + +static const struct ISpeechRecognitionResult2Vtbl recognition_result2_vtbl = +{ + /* IUnknown methods */ + recognition_result2_QueryInterface, + recognition_result2_AddRef, + recognition_result2_Release, + /* IInspectable methods */ + recognition_result2_GetIids, + recognition_result2_GetRuntimeClassName, + recognition_result2_GetTrustLevel, + /* ISpeechRecognitionResult2 methods */ + recognition_result2_get_PhraseStartTime, + recognition_result2_get_PhraseDuration +}; + +static HRESULT WINAPI recognition_result_create( ISpeechRecognitionConstraint *constraint, + HSTRING result_text, + ISpeechRecognitionResult **out ) +{ + struct recognition_result *impl; + + TRACE("out %p.\n", out); + + if (!(impl = calloc(1, sizeof(*impl)))) + { + *out = NULL; + return E_OUTOFMEMORY; + } + + impl->ISpeechRecognitionResult_iface.lpVtbl = &recognition_result_vtbl; + impl->ISpeechRecognitionResult2_iface.lpVtbl = &recognition_result2_vtbl; + impl->ref = 1; + + if (constraint) ISpeechRecognitionConstraint_AddRef((impl->constraint = constraint)); + WindowsDuplicateString(result_text, &impl->text); + + *out = &impl->ISpeechRecognitionResult_iface; + + TRACE("created %p.\n", *out); + + return S_OK; +} + +struct recognition_result_event_args +{ + ISpeechContinuousRecognitionResultGeneratedEventArgs ISpeechContinuousRecognitionResultGeneratedEventArgs_iface; + LONG ref; + + ISpeechRecognitionResult *result; +}; + +static inline struct recognition_result_event_args *impl_from_ISpeechContinuousRecognitionResultGeneratedEventArgs( ISpeechContinuousRecognitionResultGeneratedEventArgs *iface ) +{ + return CONTAINING_RECORD(iface, struct recognition_result_event_args, ISpeechContinuousRecognitionResultGeneratedEventArgs_iface); +} + +static HRESULT WINAPI recognition_result_event_args_QueryInterface( ISpeechContinuousRecognitionResultGeneratedEventArgs *iface, REFIID iid, void **out ) +{ + struct recognition_result_event_args *impl = impl_from_ISpeechContinuousRecognitionResultGeneratedEventArgs(iface); + + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) || + IsEqualGUID(iid, &IID_IInspectable) || + IsEqualGUID(iid, &IID_IAgileObject) || + IsEqualGUID(iid, &IID_ISpeechContinuousRecognitionResultGeneratedEventArgs)) + { + IInspectable_AddRef((*out = &impl->ISpeechContinuousRecognitionResultGeneratedEventArgs_iface)); + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI recognition_result_event_args_AddRef( ISpeechContinuousRecognitionResultGeneratedEventArgs *iface ) +{ + struct recognition_result_event_args *impl = impl_from_ISpeechContinuousRecognitionResultGeneratedEventArgs(iface); + ULONG ref = InterlockedIncrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + return ref; +} + +static ULONG WINAPI recognition_result_event_args_Release( ISpeechContinuousRecognitionResultGeneratedEventArgs *iface ) +{ + struct recognition_result_event_args *impl = impl_from_ISpeechContinuousRecognitionResultGeneratedEventArgs(iface); + + ULONG ref = InterlockedDecrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + + if (!ref) + { + if (impl->result) ISpeechRecognitionResult_Release(impl->result); + free(impl); + } + + return ref; +} + +static HRESULT WINAPI recognition_result_event_args_GetIids( ISpeechContinuousRecognitionResultGeneratedEventArgs *iface, ULONG *iid_count, IID **iids ) +{ + FIXME("iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids); + return E_NOTIMPL; +} + +static HRESULT WINAPI recognition_result_event_args_GetRuntimeClassName( ISpeechContinuousRecognitionResultGeneratedEventArgs *iface, HSTRING *class_name ) +{ + FIXME("iface %p, class_name %p stub!\n", iface, class_name); + return E_NOTIMPL; +} + +static HRESULT WINAPI recognition_result_event_args_GetTrustLevel( ISpeechContinuousRecognitionResultGeneratedEventArgs *iface, TrustLevel *trust_level ) +{ + FIXME("iface %p, trust_level %p stub!\n", iface, trust_level); + return E_NOTIMPL; +} + +static HRESULT WINAPI recognition_result_event_args_get_Result( ISpeechContinuousRecognitionResultGeneratedEventArgs *iface, + ISpeechRecognitionResult **value ) +{ + struct recognition_result_event_args *impl = impl_from_ISpeechContinuousRecognitionResultGeneratedEventArgs(iface); + FIXME("iface %p value %p stub!\n", iface, value); + ISpeechRecognitionResult_AddRef((*value = impl->result)); + return S_OK; +} + +static const struct ISpeechContinuousRecognitionResultGeneratedEventArgsVtbl recognition_result_event_args_vtbl = +{ + /* IUnknown methods */ + recognition_result_event_args_QueryInterface, + recognition_result_event_args_AddRef, + recognition_result_event_args_Release, + /* IInspectable methods */ + recognition_result_event_args_GetIids, + recognition_result_event_args_GetRuntimeClassName, + recognition_result_event_args_GetTrustLevel, + /* ISpeechContinuousRecognitionResultGeneratedEventArgs methods */ + recognition_result_event_args_get_Result +}; + +static HRESULT WINAPI recognition_result_event_args_create( ISpeechRecognitionResult *result, + ISpeechContinuousRecognitionResultGeneratedEventArgs **out ) +{ + struct recognition_result_event_args *impl; + + TRACE("out %p.\n", out); + + if (!(impl = calloc(1, sizeof(*impl)))) + { + *out = NULL; + return E_OUTOFMEMORY; + } + + impl->ISpeechContinuousRecognitionResultGeneratedEventArgs_iface.lpVtbl = &recognition_result_event_args_vtbl; + impl->ref = 1; + if (result) ISpeechRecognitionResult_AddRef((impl->result = result)); + + *out = &impl->ISpeechContinuousRecognitionResultGeneratedEventArgs_iface; + + TRACE("created %p.\n", *out); + return S_OK; +} + /* * * ISpeechRecognitionCompilationResult @@ -282,7 +639,9 @@ static DWORD CALLBACK session_worker_thread_cb( void *args ) struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); struct speech_get_recognition_result_params recognition_result_params; struct speech_recognize_audio_params recognize_audio_params; + ISpeechContinuousRecognitionResultGeneratedEventArgs *event_args; ISpeechRecognitionConstraint *constraint; + ISpeechRecognitionResult *result; BOOLEAN running = TRUE, paused = FALSE; UINT32 frame_count, tmp_buf_size; BYTE *audio_buf, *tmp_buf = NULL; @@ -408,8 +767,15 @@ static DWORD CALLBACK session_worker_thread_cb( void *args ) if (SUCCEEDED(hr = session_find_constraint_by_string(impl, recognized_text, &hstring, &constraint))) { - /* TODO: Send event. */ + recognition_result_create(constraint, hstring, &result); + recognition_result_event_args_create(result, &event_args); + + typed_event_handlers_notify(&impl->result_handlers, + (IInspectable *)&impl->ISpeechContinuousRecognitionSession_iface, + (IInspectable *)event_args); + ISpeechContinuousRecognitionResultGeneratedEventArgs_Release(event_args); + ISpeechRecognitionResult_Release(result); WindowsDeleteString(hstring); ISpeechRecognitionConstraint_Release(constraint); } From 3858cf7a32f158fdc91994ef4bebb679ca919060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 30 May 2022 18:37:41 +0200 Subject: [PATCH 134/413] windows.media.speech: Add ISpeechRecognitionSemanticInterpretation stub. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 112 ++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 7721764f351..af66a783e78 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -30,6 +30,116 @@ WINE_DEFAULT_DEBUG_CHANNEL(speech); +struct semantic_interpretation +{ + ISpeechRecognitionSemanticInterpretation ISpeechRecognitionSemanticInterpretation_iface; + LONG ref; +}; + +static inline struct semantic_interpretation *impl_from_ISpeechRecognitionSemanticInterpretation( ISpeechRecognitionSemanticInterpretation *iface ) +{ + return CONTAINING_RECORD(iface, struct semantic_interpretation, ISpeechRecognitionSemanticInterpretation_iface); +} + +HRESULT WINAPI semantic_interpretation_QueryInterface( ISpeechRecognitionSemanticInterpretation *iface, REFIID iid, void **out ) +{ + struct semantic_interpretation *impl = impl_from_ISpeechRecognitionSemanticInterpretation(iface); + + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) || + IsEqualGUID(iid, &IID_IInspectable) || + IsEqualGUID(iid, &IID_ISpeechRecognitionSemanticInterpretation)) + { + IInspectable_AddRef((*out = &impl->ISpeechRecognitionSemanticInterpretation_iface)); + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + *out = NULL; + return E_NOINTERFACE; +} + +ULONG WINAPI semantic_interpretation_AddRef( ISpeechRecognitionSemanticInterpretation *iface ) +{ + struct semantic_interpretation *impl = impl_from_ISpeechRecognitionSemanticInterpretation(iface); + ULONG ref = InterlockedIncrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + return ref; +} + +ULONG WINAPI semantic_interpretation_Release( ISpeechRecognitionSemanticInterpretation *iface ) +{ + struct semantic_interpretation *impl = impl_from_ISpeechRecognitionSemanticInterpretation(iface); + + ULONG ref = InterlockedDecrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + + if(!ref) + free(impl); + + return ref; +} + +HRESULT WINAPI semantic_interpretation_GetIids( ISpeechRecognitionSemanticInterpretation *iface, ULONG *iid_count, IID **iids ) +{ + FIXME("iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids); + return E_NOTIMPL; +} + +HRESULT WINAPI semantic_interpretation_GetRuntimeClassName( ISpeechRecognitionSemanticInterpretation *iface, HSTRING *class_name ) +{ + FIXME("iface %p, class_name %p stub!\n", iface, class_name); + return E_NOTIMPL; +} + +HRESULT WINAPI semantic_interpretation_GetTrustLevel( ISpeechRecognitionSemanticInterpretation *iface, TrustLevel *trust_level ) +{ + FIXME("iface %p, trust_level %p stub!\n", iface, trust_level); + return E_NOTIMPL; +} + +HRESULT WINAPI semantic_interpretation_get_Properties( ISpeechRecognitionSemanticInterpretation *iface, IMapView_HSTRING_IVectorView_HSTRING **value ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +static const struct ISpeechRecognitionSemanticInterpretationVtbl semantic_interpretation_vtbl = +{ + /* IUnknown methods */ + semantic_interpretation_QueryInterface, + semantic_interpretation_AddRef, + semantic_interpretation_Release, + /* IInspectable methods */ + semantic_interpretation_GetIids, + semantic_interpretation_GetRuntimeClassName, + semantic_interpretation_GetTrustLevel, + /* ISpeechRecognitionSemanticInterpretation methods */ + semantic_interpretation_get_Properties +}; + + +static HRESULT semantic_interpretation_create( ISpeechRecognitionSemanticInterpretation **out ) +{ + struct semantic_interpretation *impl; + + TRACE("out %p.\n", out); + + if (!(impl = calloc(1, sizeof(*impl)))) + { + *out = NULL; + return E_OUTOFMEMORY; + } + + impl->ISpeechRecognitionSemanticInterpretation_iface.lpVtbl = &semantic_interpretation_vtbl; + impl->ref = 1; + + *out = &impl->ISpeechRecognitionSemanticInterpretation_iface; + TRACE("created %p\n", *out); + return S_OK; +} + struct recognition_result { ISpeechRecognitionResult ISpeechRecognitionResult_iface; @@ -139,7 +249,7 @@ static HRESULT WINAPI recognition_result_get_SemanticInterpretation( ISpeechReco ISpeechRecognitionSemanticInterpretation **value ) { FIXME("iface %p, operation %p stub!\n", iface, value); - return E_NOTIMPL; + return semantic_interpretation_create(value); } static HRESULT WINAPI recognition_result_GetAlternates( ISpeechRecognitionResult *iface, From 5ac801d73a028bc8e20266542f65807e024d209c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 20 Feb 2024 09:40:32 +0100 Subject: [PATCH 135/413] HACK: windows.media.speech: Stub semantic_interpretation_get_Properties. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 134 ++++++++++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index af66a783e78..b35fa692d2f 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -30,6 +30,138 @@ WINE_DEFAULT_DEBUG_CHANNEL(speech); +struct map_view_hstring_vector_view_hstring +{ + IMapView_HSTRING_IVectorView_HSTRING IMapView_HSTRING_IVectorView_HSTRING_iface; + LONG ref; +}; + +static inline struct map_view_hstring_vector_view_hstring *impl_from_IMapView_HSTRING_IVectorView_HSTRING( IMapView_HSTRING_IVectorView_HSTRING *iface ) +{ + return CONTAINING_RECORD(iface, struct map_view_hstring_vector_view_hstring, IMapView_HSTRING_IVectorView_HSTRING_iface); +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_QueryInterface( IMapView_HSTRING_IVectorView_HSTRING *iface, REFIID iid, void **out ) +{ + struct map_view_hstring_vector_view_hstring *impl = impl_from_IMapView_HSTRING_IVectorView_HSTRING(iface); + + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) || + IsEqualGUID(iid, &IID_IInspectable) || + IsEqualGUID(iid, &IID_IMapView_HSTRING_IVectorView_HSTRING)) + { + IInspectable_AddRef((*out = &impl->IMapView_HSTRING_IVectorView_HSTRING_iface)); + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + *out = NULL; + return E_NOINTERFACE; +} + +ULONG WINAPI map_view_hstring_vector_view_hstring_AddRef( IMapView_HSTRING_IVectorView_HSTRING *iface ) +{ + struct map_view_hstring_vector_view_hstring *impl = impl_from_IMapView_HSTRING_IVectorView_HSTRING(iface); + ULONG ref = InterlockedIncrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + return ref; +} + +ULONG WINAPI map_view_hstring_vector_view_hstring_Release( IMapView_HSTRING_IVectorView_HSTRING *iface ) +{ + struct map_view_hstring_vector_view_hstring *impl = impl_from_IMapView_HSTRING_IVectorView_HSTRING(iface); + + ULONG ref = InterlockedDecrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + + if(!ref) + free(impl); + + return ref; +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_GetIids( IMapView_HSTRING_IVectorView_HSTRING *iface, ULONG *iidCount, IID **iids ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_GetRuntimeClassName( IMapView_HSTRING_IVectorView_HSTRING *iface, HSTRING *className ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_GetTrustLevel( IMapView_HSTRING_IVectorView_HSTRING *iface, TrustLevel *trustLevel ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_Lookup( IMapView_HSTRING_IVectorView_HSTRING *iface, HSTRING key, IVectorView_HSTRING **value ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_get_Size( IMapView_HSTRING_IVectorView_HSTRING *iface, unsigned int *size ) +{ + FIXME("iface %p stub!\n", iface); + *size = 0; + return S_OK; +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_HasKey( IMapView_HSTRING_IVectorView_HSTRING *iface, HSTRING key, boolean *found ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_Split( IMapView_HSTRING_IVectorView_HSTRING *iface, IMapView_HSTRING_IVectorView_HSTRING **first, IMapView_HSTRING_IVectorView_HSTRING **second ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +static const struct IMapView_HSTRING_IVectorView_HSTRINGVtbl map_view_hstring_vector_view_hstring_vtbl = +{ + /* IUnknown methods */ + map_view_hstring_vector_view_hstring_QueryInterface, + map_view_hstring_vector_view_hstring_AddRef, + map_view_hstring_vector_view_hstring_Release, + /* IInspectable methods */ + map_view_hstring_vector_view_hstring_GetIids, + map_view_hstring_vector_view_hstring_GetRuntimeClassName, + map_view_hstring_vector_view_hstring_GetTrustLevel, + /* IMapView* > methods */ + map_view_hstring_vector_view_hstring_Lookup, + map_view_hstring_vector_view_hstring_get_Size, + map_view_hstring_vector_view_hstring_HasKey, + map_view_hstring_vector_view_hstring_Split +}; + + +static HRESULT map_view_hstring_vector_view_hstring_create( IMapView_HSTRING_IVectorView_HSTRING **out ) +{ + struct map_view_hstring_vector_view_hstring *impl; + + TRACE("out %p.\n", out); + + if (!(impl = calloc(1, sizeof(*impl)))) + { + *out = NULL; + return E_OUTOFMEMORY; + } + + impl->IMapView_HSTRING_IVectorView_HSTRING_iface.lpVtbl = &map_view_hstring_vector_view_hstring_vtbl; + impl->ref = 1; + + *out = &impl->IMapView_HSTRING_IVectorView_HSTRING_iface; + TRACE("created %p\n", *out); + return S_OK; +} + struct semantic_interpretation { ISpeechRecognitionSemanticInterpretation ISpeechRecognitionSemanticInterpretation_iface; @@ -102,7 +234,7 @@ HRESULT WINAPI semantic_interpretation_GetTrustLevel( ISpeechRecognitionSemantic HRESULT WINAPI semantic_interpretation_get_Properties( ISpeechRecognitionSemanticInterpretation *iface, IMapView_HSTRING_IVectorView_HSTRING **value ) { FIXME("iface %p stub!\n", iface); - return E_NOTIMPL; + return map_view_hstring_vector_view_hstring_create(value); } static const struct ISpeechRecognitionSemanticInterpretationVtbl semantic_interpretation_vtbl = From ab94cce654132ffe652afd6257aabefc06b8855d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 28 Feb 2023 16:11:36 +0100 Subject: [PATCH 136/413] WIP: windows.media.speech: Implement grammar. CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 106 ++++++++++++++++++++++++- dlls/windows.media.speech/unixlib.c | 61 +++++++++++++- dlls/windows.media.speech/unixlib.h | 2 + 3 files changed, 164 insertions(+), 5 deletions(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index b35fa692d2f..c898ea7723f 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -1489,7 +1489,7 @@ static HRESULT WINAPI recognizer_get_UIOptions( ISpeechRecognizer *iface, ISpeec return E_NOTIMPL; } -static HRESULT recognizer_create_unix_instance( struct session *session ) +static HRESULT recognizer_create_unix_instance( struct session *session, const char **grammar, UINT32 grammar_size ) { struct speech_create_recognizer_params create_params = { 0 }; WCHAR locale[LOCALE_NAME_MAX_LENGTH]; @@ -1506,6 +1506,8 @@ static HRESULT recognizer_create_unix_instance( struct session *session ) return HRESULT_FROM_WIN32(GetLastError()); create_params.sample_rate = (FLOAT)session->capture_wfx.nSamplesPerSec; + create_params.grammar = grammar; + create_params.grammar_size = grammar_size; if ((status = WINE_UNIX_CALL(unix_speech_create_recognizer, &create_params))) { @@ -1522,11 +1524,109 @@ static HRESULT recognizer_compile_constraints_async( IInspectable *invoker, IIns { struct recognizer *impl = impl_from_ISpeechRecognizer((ISpeechRecognizer *)invoker); struct session *session = impl_from_ISpeechContinuousRecognitionSession(impl->session); + struct speech_release_recognizer_params release_params; + ISpeechRecognitionListConstraint *list_constraint; + IIterable_IInspectable *constraints_iterable; + IIterator_IInspectable *constraints_iterator; + ISpeechRecognitionConstraint *constraint; + IIterable_HSTRING *commands_iterable; + IIterator_HSTRING *commands_iterator; + BOOLEAN has_constraint, has_command; + IVector_HSTRING *commands; + const WCHAR *command_str; + UINT32 grammar_size = 0, i = 0; + char **grammar = NULL; + HSTRING command; + UINT32 size = 0; HRESULT hr; - if (FAILED(hr = recognizer_create_unix_instance(session))) + if (FAILED(hr = IVector_ISpeechRecognitionConstraint_QueryInterface(session->constraints, &IID_IIterable_ISpeechRecognitionConstraint, (void **)&constraints_iterable))) + return hr; + + if (FAILED(hr = IIterable_IInspectable_First(constraints_iterable, &constraints_iterator))) + { + IIterable_IInspectable_Release(constraints_iterable); + return hr; + } + + for (hr = IIterator_IInspectable_get_HasCurrent(constraints_iterator, &has_constraint); SUCCEEDED(hr) && has_constraint; hr = IIterator_IInspectable_MoveNext(constraints_iterator, &has_constraint)) + { + list_constraint = NULL; + commands_iterable = NULL; + commands_iterator = NULL; + commands = NULL; + + if (FAILED(IIterator_IInspectable_get_Current(constraints_iterator, (IInspectable **)&constraint))) + goto skip; + + if (FAILED(ISpeechRecognitionConstraint_QueryInterface(constraint, &IID_ISpeechRecognitionListConstraint, (void**)&list_constraint))) + goto skip; + + if (FAILED(ISpeechRecognitionListConstraint_get_Commands(list_constraint, &commands))) + goto skip; + + if (FAILED(IVector_HSTRING_QueryInterface(commands, &IID_IIterable_HSTRING, (void **)&commands_iterable))) + goto skip; + + if (FAILED(IIterable_HSTRING_First(commands_iterable, &commands_iterator))) + goto skip; + + if (FAILED(IVector_HSTRING_get_Size(commands, &size))) + goto skip; + + grammar_size += size; + grammar = realloc(grammar, grammar_size * sizeof(char *)); + + for (hr = IIterator_HSTRING_get_HasCurrent(commands_iterator, &has_command); SUCCEEDED(hr) && has_command; hr = IIterator_HSTRING_MoveNext(commands_iterator, &has_command)) + { + if (FAILED(IIterator_HSTRING_get_Current(commands_iterator, &command))) + continue; + + command_str = WindowsGetStringRawBuffer(command, NULL); + + if (command_str) + { + WCHAR *wstr = wcsdup(command_str); + size_t len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, grammar[i], 0, NULL, NULL); + grammar[i] = malloc(len * sizeof(char)); + + CharLowerW(wstr); + WideCharToMultiByte(CP_UTF8, 0, wstr, -1, grammar[i], len, NULL, NULL); + free(wstr); + i++; + } + + WindowsDeleteString(command); + } + +skip: + if (commands_iterator) IIterator_HSTRING_Release(commands_iterator); + if (commands_iterable) IIterable_HSTRING_Release(commands_iterable); + if (commands) IVector_HSTRING_Release(commands); + + if (list_constraint) ISpeechRecognitionListConstraint_Release(list_constraint); + if (constraint) ISpeechRecognitionConstraint_Release(constraint); + } + + IIterator_IInspectable_Release(constraints_iterator); + IIterable_IInspectable_Release(constraints_iterable); + + if (session->unix_handle) + { + release_params.handle = session->unix_handle; + WINE_UNIX_CALL(unix_speech_release_recognizer, &release_params); + session->unix_handle = 0; + } + + hr = recognizer_create_unix_instance(session, (const char **)grammar, grammar_size); + + for(i = 0; i < grammar_size; ++i) + free(grammar[i]); + free(grammar); + + if (FAILED(hr)) { - WARN("Failed to created recognizer instance.\n"); + WARN("Failed to created recognizer instance with grammar.\n"); return compilation_result_create(SpeechRecognitionResultStatus_GrammarCompilationFailure, (ISpeechRecognitionCompilationResult **) result); } else return compilation_result_create(SpeechRecognitionResultStatus_Success, (ISpeechRecognitionCompilationResult **) result); diff --git a/dlls/windows.media.speech/unixlib.c b/dlls/windows.media.speech/unixlib.c index c41bb4cc445..d8f59b99b76 100644 --- a/dlls/windows.media.speech/unixlib.c +++ b/dlls/windows.media.speech/unixlib.c @@ -58,6 +58,7 @@ static void *vosk_handle; MAKE_FUNCPTR(vosk_model_new); MAKE_FUNCPTR(vosk_model_free); MAKE_FUNCPTR(vosk_recognizer_new); +MAKE_FUNCPTR(vosk_recognizer_new_grm); MAKE_FUNCPTR(vosk_recognizer_free); MAKE_FUNCPTR(vosk_recognizer_accept_waveform); MAKE_FUNCPTR(vosk_recognizer_final_result); @@ -80,6 +81,8 @@ static NTSTATUS process_attach( void *args ) goto error; \ } LOAD_FUNCPTR(vosk_model_new) + LOAD_FUNCPTR(vosk_recognizer_new) + LOAD_FUNCPTR(vosk_recognizer_new_grm) LOAD_FUNCPTR(vosk_model_free) LOAD_FUNCPTR(vosk_recognizer_new) LOAD_FUNCPTR(vosk_recognizer_free) @@ -225,12 +228,58 @@ static NTSTATUS find_model_by_locale( const char *locale, VoskModel **model ) return status; } +static NTSTATUS grammar_to_json_array(const char **grammar, UINT32 grammar_size, const char **array) +{ + size_t buf_size = strlen("[]") + 1, len; + char *buf; + UINT32 i; + + for (i = 0; i < grammar_size; ++i) + { + buf_size += strlen(grammar[i]) + 4; /* (4) - two double quotes, a comma and a space */ + } + + if (!(buf = malloc(buf_size))) + return STATUS_NO_MEMORY; + + *array = buf; + + *buf = '['; + buf++; + + for (i = 0; i < grammar_size; ++i) + { + *buf = '\"'; + buf++; + len = strlen(grammar[i]); + memcpy(buf, grammar[i], len); + buf += len; + *buf = '\"'; + buf++; + if (i < (grammar_size - 1)) + { + *buf = ','; + buf++; + *buf = ' '; + buf++; + } + } + + *buf = ']'; + buf++; + *buf = '\0'; + + TRACE("created json array %s.\n", debugstr_a(*array)); + return STATUS_SUCCESS; +} + static NTSTATUS speech_create_recognizer( void *args ) { struct speech_create_recognizer_params *params = args; VoskRecognizer *recognizer = NULL; VoskModel *model = NULL; NTSTATUS status = STATUS_SUCCESS; + const char *grammar_json; TRACE("args %p.\n", args); @@ -240,8 +289,16 @@ static NTSTATUS speech_create_recognizer( void *args ) if ((status = find_model_by_locale(params->locale, &model))) return status; - if (!(recognizer = p_vosk_recognizer_new(model, params->sample_rate))) - status = STATUS_UNSUCCESSFUL; + if (params->grammar && grammar_to_json_array(params->grammar, params->grammar_size, &grammar_json) == STATUS_SUCCESS) + { + if (!(recognizer = p_vosk_recognizer_new_grm(model, params->sample_rate, grammar_json))) + status = STATUS_UNSUCCESSFUL; + } + else + { + if (!(recognizer = p_vosk_recognizer_new(model, params->sample_rate))) + status = STATUS_UNSUCCESSFUL; + } /* VoskModel is reference-counted. A VoskRecognizer keeps a reference to its model. */ p_vosk_model_free(model); diff --git a/dlls/windows.media.speech/unixlib.h b/dlls/windows.media.speech/unixlib.h index a07d76705d5..ad2fab738b9 100644 --- a/dlls/windows.media.speech/unixlib.h +++ b/dlls/windows.media.speech/unixlib.h @@ -37,6 +37,8 @@ struct speech_create_recognizer_params speech_recognizer_handle handle; CHAR locale[LOCALE_NAME_MAX_LENGTH]; FLOAT sample_rate; + const char **grammar; + unsigned int grammar_size; }; struct speech_release_recognizer_params From 07f319e2918c00f21f933ec4d2a1d959c0ee0848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 28 Feb 2023 22:06:23 +0100 Subject: [PATCH 137/413] HACK: windows.media.speech/tests: Add tests for manual recognition testing. Speak during the delay, to test the code. CW-Bug-Id: #20134 --- dlls/windows.media.speech/tests/speech.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 743955ab9bd..a6c7d56e1e1 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -202,7 +202,23 @@ HRESULT WINAPI recognition_result_handler_Invoke( IHandler_RecognitionResult *if ISpeechContinuousRecognitionSession *sender, ISpeechContinuousRecognitionResultGeneratedEventArgs *args ) { - trace("iface %p, sender %p, args %p.\n", iface, sender, args); + ISpeechRecognitionResult *result; + HSTRING hstring; + HRESULT hr; + + if (!args) return S_OK; + + hr = ISpeechContinuousRecognitionResultGeneratedEventArgs_get_Result(args, &result); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + hr = ISpeechRecognitionResult_get_Text(result, &hstring); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + trace("iface %p, sender %p, args %p, text %s.\n", iface, sender, args, debugstr_w(WindowsGetStringRawBuffer(hstring, NULL))); + + WindowsDeleteString(hstring); + ISpeechRecognitionResult_Release(result); + return S_OK; } @@ -1702,7 +1718,7 @@ static void test_Recognition(void) static const WCHAR *list_constraint_name = L"Windows.Media.SpeechRecognition.SpeechRecognitionListConstraint"; static const WCHAR *recognizer_name = L"Windows.Media.SpeechRecognition.SpeechRecognizer"; static const WCHAR *speech_constraint_tag = L"test_message"; - static const WCHAR *speech_constraints[] = { L"This is a test.", L"Number 5!", L"What time is it?" }; + static const WCHAR *speech_constraints[] = { L"This is a test", L"Number 5", L"What time is it" }; ISpeechRecognitionListConstraintFactory *listconstraint_factory = NULL; IAsyncOperation_SpeechRecognitionCompilationResult *operation = NULL; IVector_ISpeechRecognitionConstraint *constraints = NULL; @@ -1865,6 +1881,8 @@ static void test_Recognition(void) ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); ok(recog_state == SpeechRecognizerState_Capturing || broken(recog_state == SpeechRecognizerState_Idle), "recog_state was %u.\n", recog_state); + + Sleep(10000); /* * TODO: Use a loopback device together with prerecorded audio files to test the recognizer's functionality. */ From 01b8aa2f8322e318a0b77fbbeb254e5e407d9fc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 7 Mar 2023 15:31:08 +0100 Subject: [PATCH 138/413] HACK: windows.media.speech: Load Vosk models from Phasmophobia. CW-Bug-Id: #20134 --- dlls/windows.media.speech/unixlib.c | 67 +++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/dlls/windows.media.speech/unixlib.c b/dlls/windows.media.speech/unixlib.c index d8f59b99b76..33f16e652e8 100644 --- a/dlls/windows.media.speech/unixlib.c +++ b/dlls/windows.media.speech/unixlib.c @@ -119,13 +119,55 @@ static inline VoskRecognizer *vosk_recognizer_from_handle( speech_recognizer_han return (VoskRecognizer *)(UINT_PTR)handle; } +static const char* map_lang_to_phasmophobia_dir(const char* lang, size_t len) +{ + if (!strncmp(lang, "ar", len)) + return "Arabic"; + if (!strncmp(lang, "ca", len)) + return "Catalan"; + if (!strncmp(lang, "zn", len)) + return "Chinese"; + if (!strncmp(lang, "cs", len)) + return "Czech"; + if (!strncmp(lang, "nl", len)) + return "Dutch"; + if (!strncmp(lang, "en", len)) + return "English"; + if (!strncmp(lang, "fr", len)) + return "French"; + if (!strncmp(lang, "de", len)) + return "German"; + if (!strncmp(lang, "de", len)) + return "German"; + if (!strncmp(lang, "el", len)) + return "Greek"; + if (!strncmp(lang, "it", len)) + return "Italian"; + if (!strncmp(lang, "ja", len)) + return "Japanese"; + if (!strncmp(lang, "pt", len)) + return "Portuguese"; + if (!strncmp(lang, "ru", len)) + return "Russian"; + if (!strncmp(lang, "es", len)) + return "Spanish"; + if (!strncmp(lang, "sw", len)) + return "Swedish"; + if (!strncmp(lang, "tr", len)) + return "Turkish"; + if (!strncmp(lang, "uk", len)) + return "Ukrainian"; + + return ""; +} + static NTSTATUS find_model_by_locale_and_path( const char *path, const char *locale, VoskModel **model ) { static const char *vosk_model_identifier_small = "vosk-model-small-"; static const char *vosk_model_identifier = "vosk-model-"; size_t ident_small_len = strlen(vosk_model_identifier_small); size_t ident_len = strlen(vosk_model_identifier); - char *ent_name, *model_path, *best_match, *delim; + char *ent_name, *model_path, *best_match, *delim, *appid = getenv("SteamAppId"), *str = NULL; NTSTATUS status = STATUS_UNSUCCESSFUL; struct dirent *dirent; size_t path_len, len; @@ -152,7 +194,7 @@ static NTSTATUS find_model_by_locale_and_path( const char *path, const char *loc ent_name += ident_small_len; else if (!strncmp(ent_name, vosk_model_identifier, ident_len)) ent_name += ident_len; - else + else if (strcmp(appid, "739630") != 0) continue; /* @@ -168,6 +210,12 @@ static NTSTATUS find_model_by_locale_and_path( const char *path, const char *loc if (!best_match && !strncmp(ent_name, locale, delim - locale)) best_match = strdup(dirent->d_name); + + if (!best_match && !strcmp(appid, "739630")) + { + if ((str = (char *)map_lang_to_phasmophobia_dir(locale, delim - locale))) + best_match = strdup(str); + } } closedir(dir); @@ -198,7 +246,7 @@ static NTSTATUS find_model_by_locale_and_path( const char *path, const char *loc static NTSTATUS find_model_by_locale( const char *locale, VoskModel **model ) { const char *suffix = NULL; - char *env, *path = NULL; + char *env, *path = NULL, *appid = getenv("SteamAppId"); NTSTATUS status; TRACE("locale %s, model %p.\n", debugstr_a(locale), model); @@ -225,6 +273,19 @@ static NTSTATUS find_model_by_locale( const char *locale, VoskModel **model ) status = find_model_by_locale_and_path(path, locale, model); free(path); + /* Hack to load Vosk models from Phasmophobia, so they don't need to be downloaded separately.*/ + if (status && appid && !strcmp(appid, "739630") && (env = getenv("PWD"))) + { + suffix = "/Phasmophobia_Data/StreamingAssets/LanguageModels"; + + if (!(path = malloc(strlen(env) + strlen(suffix) + 1))) + return STATUS_NO_MEMORY; + + sprintf(path, "%s%s", env, suffix); + status = find_model_by_locale_and_path(path, locale, model); + free(path); + } + return status; } From dded9485cd3fe2da80e2a7933dccfb995f63d5de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 7 Mar 2023 16:36:36 +0100 Subject: [PATCH 139/413] windows.media.speech: Suppress verbose Unixlib traces. CW-Bug-Id: #20134 --- dlls/windows.media.speech/unixlib.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dlls/windows.media.speech/unixlib.c b/dlls/windows.media.speech/unixlib.c index 33f16e652e8..55e48a550ff 100644 --- a/dlls/windows.media.speech/unixlib.c +++ b/dlls/windows.media.speech/unixlib.c @@ -330,7 +330,6 @@ static NTSTATUS grammar_to_json_array(const char **grammar, UINT32 grammar_size, buf++; *buf = '\0'; - TRACE("created json array %s.\n", debugstr_a(*array)); return STATUS_SUCCESS; } @@ -408,8 +407,6 @@ static NTSTATUS speech_get_recognition_result( void* args ) static char *last_result = NULL; const char *tmp = NULL; - TRACE("args %p.\n", args); - if (!vosk_handle) return STATUS_NOT_SUPPORTED; @@ -430,7 +427,6 @@ static NTSTATUS speech_get_recognition_result( void* args ) last_result = (char *)tmp; last_result_len = strlen(last_result); - TRACE("last_result %s.\n", debugstr_a(last_result)); } else return STATUS_NOT_FOUND; } From 31af139551be5501124c78e3845fbcb814f64124 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 7 Dec 2023 11:49:06 +0100 Subject: [PATCH 140/413] winegstreamer: Register more VIDEO_EFFECT DMO classes. CW-Bug-Id: #20427 --- dlls/winegstreamer/mfplat.c | 33 +++++++++++++++++++++++++++++++++ include/wmcodecdsp.idl | 15 +++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index a4cf2dfcd99..0e77afde3d3 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -444,6 +444,39 @@ HRESULT mfplat_DllRegisterServer(void) ARRAY_SIZE(resampler_types), resampler_types, }, + { + CLSID_CFrameRateConvertDmo, + MFT_CATEGORY_VIDEO_EFFECT, + L"Frame Rate Converter", + MFT_ENUM_FLAG_SYNCMFT, + /* FIXME: check the actual media types */ + ARRAY_SIZE(color_convert_input_types), + color_convert_input_types, + ARRAY_SIZE(color_convert_output_types), + color_convert_output_types, + }, + { + CLSID_CResizerDMO, + MFT_CATEGORY_VIDEO_EFFECT, + L"Resizer MFT", + MFT_ENUM_FLAG_SYNCMFT, + /* FIXME: check the actual media types */ + ARRAY_SIZE(color_convert_input_types), + color_convert_input_types, + ARRAY_SIZE(color_convert_output_types), + color_convert_output_types, + }, + { + CLSID_CColorControlDmo, + MFT_CATEGORY_VIDEO_EFFECT, + L"Color Control", + MFT_ENUM_FLAG_SYNCMFT, + /* FIXME: check the actual media types */ + ARRAY_SIZE(color_convert_input_types), + color_convert_input_types, + ARRAY_SIZE(color_convert_output_types), + color_convert_output_types, + }, { CLSID_CColorConvertDMO, MFT_CATEGORY_VIDEO_EFFECT, diff --git a/include/wmcodecdsp.idl b/include/wmcodecdsp.idl index a6f350ea2fc..7e91015f19e 100644 --- a/include/wmcodecdsp.idl +++ b/include/wmcodecdsp.idl @@ -72,6 +72,21 @@ coclass CWMAEncMediaObject {} ] coclass AACMFTEncoder {} +[ + uuid(01f36ce2-0907-4d8b-979d-f151be91c883) +] +coclass CFrameRateConvertDmo {} + +[ + uuid(1ea1ea14-48f4-4054-ad1a-e8aee10ac805) +] +coclass CResizerDMO {} + +[ + uuid(798059f0-89ca-4160-b325-aeb48efe4f9a) +] +coclass CColorControlDmo {} + [ uuid(98230571-0087-4204-b020-3282538e57d3) ] From 42de3e9231918273ff99fa77395713115bdedbe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 7 Dec 2023 11:49:49 +0100 Subject: [PATCH 141/413] HACK: winegstreamer: Register the video processor MFT as a decoder. CW-Bug-Id: #20427 --- dlls/winegstreamer/mfplat.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 0e77afde3d3..0c2e37abe8a 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -507,6 +507,19 @@ HRESULT mfplat_DllRegisterServer(void) ARRAY_SIZE(video_decoder_output_types), video_decoder_output_types, }, + { + /* HACK: Register the video processor as a decoder too as + * the media source currently always decodes. + */ + CLSID_VideoProcessorMFT, + MFT_CATEGORY_VIDEO_DECODER, + L"Null Decoder", + MFT_ENUM_FLAG_SYNCMFT, + ARRAY_SIZE(video_processor_input_types), + video_processor_input_types, + ARRAY_SIZE(video_processor_output_types), + video_processor_output_types, + }, }; unsigned int i; From d64cb2c1263b4f26444a59a4348a114792b4e5e4 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 15 Feb 2024 12:41:28 -0600 Subject: [PATCH 142/413] powershell: Read input command from stdin. CW-Bug-Id: #23416 --- programs/powershell/main.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/programs/powershell/main.c b/programs/powershell/main.c index eb9a64ff0ec..25a4d13d340 100644 --- a/programs/powershell/main.c +++ b/programs/powershell/main.c @@ -24,10 +24,25 @@ int __cdecl wmain(int argc, WCHAR *argv[]) { int i; - WINE_FIXME("stub:"); + WINE_FIXME("stub.\n"); for (i = 0; i < argc; i++) - WINE_FIXME(" %s", wine_dbgstr_w(argv[i])); - WINE_FIXME("\n"); + { + WINE_FIXME("argv[%d] %s\n", i, wine_dbgstr_w(argv[i])); + if (!wcsicmp(argv[i], L"-command") && i < argc - 1 && !wcscmp(argv[i + 1], L"-")) + { + char command[4096], *p; + ++i; + while (fgets(command, sizeof(command), stdin)) + { + WINE_FIXME("command %s.\n", debugstr_a(command)); + p = command; + while (*p && !isspace(*p)) ++p; + *p = 0; + if (!stricmp(command, "exit")) + break; + } + } + } return 0; } From bce3392d6b8db8d82de291ec35c37996405d487e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 13 Mar 2023 21:28:03 +0100 Subject: [PATCH 143/413] mfmediaengine: Return S_OK in SetBalance(). --- dlls/mfmediaengine/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index baa6e8540ff..3e05626f238 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -2588,7 +2588,7 @@ static HRESULT WINAPI media_engine_SetBalance(IMFMediaEngineEx *iface, double ba { FIXME("%p, %f stub.\n", iface, balance); - return E_NOTIMPL; + return S_OK; } static BOOL WINAPI media_engine_IsPlaybackRateSupported(IMFMediaEngineEx *iface, double rate) From d719878e794ade71dd491af103e14962878a5a40 Mon Sep 17 00:00:00 2001 From: Tatsuyuki Ishi Date: Sat, 17 Feb 2024 22:11:43 +0900 Subject: [PATCH 144/413] ntdll: Don't leak objattr allocation in NtCreateSemaphore. https://github.com/ValveSoftware/wine/pull/219 --- dlls/ntdll/unix/sync.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index ed30388b026..dd08632e21c 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -272,7 +272,6 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ *handle = 0; if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER; - if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; if (do_fsync()) return fsync_create_semaphore( handle, access, attr, initial, max ); @@ -280,6 +279,8 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ if (do_esync()) return esync_create_semaphore( handle, access, attr, initial, max ); + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + SERVER_START_REQ( create_semaphore ) { req->access = access; From 6dac9f85a1ef844134fb143c35f185e8d5445ee7 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 20 Feb 2024 16:03:44 -0600 Subject: [PATCH 145/413] winhttp: Mind read size when skipping async read in WinHttpReadData(). CW-Bug-Id: #21085 --- dlls/winhttp/request.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index 29b30fc2583..c75d6fd513f 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -3062,9 +3062,11 @@ static DWORD query_data_ready( struct request *request ) return count; } -static BOOL skip_async_queue( struct request *request, BOOL *wont_block ) +static BOOL skip_async_queue( struct request *request, BOOL *wont_block, DWORD to_read ) { - *wont_block = end_of_read_data( request ) || query_data_ready( request ); + if (!request->read_chunked) + to_read = min( to_read, request->content_length - request->content_read ); + *wont_block = end_of_read_data( request ) || query_data_ready( request ) >= to_read; return request->hdr.recursion_count < 3 && *wont_block; } @@ -3133,7 +3135,7 @@ BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available ) return FALSE; } - if (!(async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) || skip_async_queue( request, &wont_block )) + if (!(async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) || skip_async_queue( request, &wont_block, 1 )) { ret = query_data_available( request, available, async ); } @@ -3229,7 +3231,7 @@ BOOL WINAPI WinHttpReadData( HINTERNET hrequest, void *buffer, DWORD to_read, DW return FALSE; } - if (!(async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) || skip_async_queue( request, &wont_block )) + if (!(async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) || skip_async_queue( request, &wont_block, to_read )) { ret = read_data( request, buffer, to_read, read, async ); } From daff46e60e3d043df5a962f5121a5e09bf5aea7f Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Tue, 20 Feb 2024 22:39:56 -0300 Subject: [PATCH 146/413] HACK: gdiplus: Add HDC parameter back to gdi+ internal functions. This reverts commit e8babc9c97f8d5d7ea41c52e10bd6a8e0a2aa976 (Replace HDC with GpGraphics in parameters.) without having to revert the font linking implementation. This is due to graphics->hdc member being NULL when the GpGraphics object is created with GdipGetImageGraphicsContext, so we need to be able to pass a temporal HDC object when graphics->hdc is null. --- dlls/gdiplus/gdiplus_private.h | 4 +-- dlls/gdiplus/graphics.c | 52 +++++++++++++++++----------------- dlls/gdiplus/graphicspath.c | 8 +++--- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index 2b5dbee43e9..95d19080ff1 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -620,13 +620,13 @@ struct gdip_font_link_info { }; -typedef GpStatus (*gdip_format_string_callback)(GpGraphics *graphics, +typedef GpStatus (*gdip_format_string_callback)(GpGraphics *graphics, HDC hdc, GDIPCONST WCHAR *string, INT index, INT length, struct gdip_font_link_info *sections, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, INT underlined_index_count, void *user_data); -GpStatus gdip_format_string(GpGraphics *graphics, +GpStatus gdip_format_string(GpGraphics *graphics, HDC hdc, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, int ignore_empty_clip, gdip_format_string_callback callback, void *user_data); diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index 707fa55cfda..4b942bfaa09 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -5163,7 +5163,7 @@ GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT w } /* Populates gdip_font_link_info struct based on the base_font and input string */ -static void generate_font_link_info(GpGraphics *graphics, WCHAR *string, DWORD length, GDIPCONST GpFont *base_font, +static void generate_font_link_info(GpGraphics *graphics, HDC hdc, WCHAR *string, DWORD length, GDIPCONST GpFont *base_font, struct gdip_font_link_info *font_link_info) { IUnknown *unk; @@ -5182,7 +5182,7 @@ static void generate_font_link_info(GpGraphics *graphics, WCHAR *string, DWORD l IUnknown_Release(unk); get_font_hfont(graphics, base_font, NULL, &hfont, NULL, NULL); - IMLangFontLink_GetFontCodePages(iMLFL, graphics->hdc, hfont, &font_codepages); + IMLangFontLink_GetFontCodePages(iMLFL, hdc, hfont, &font_codepages); while (progress < length) { @@ -5197,10 +5197,10 @@ static void generate_font_link_info(GpGraphics *graphics, WCHAR *string, DWORD l } else { - IMLangFontLink_MapFont(iMLFL, graphics->hdc, string_codepages, hfont, &map_hfont); - old_font = SelectObject(graphics->hdc, map_hfont); - GdipCreateFontFromDC(graphics->hdc, &gpfont); - SelectObject(graphics->hdc, old_font); + IMLangFontLink_MapFont(iMLFL, hdc, string_codepages, hfont, &map_hfont); + old_font = SelectObject(hdc, map_hfont); + GdipCreateFontFromDC(hdc, &gpfont); + SelectObject(hdc, old_font); IMLangFontLink_ReleaseFont(iMLFL, map_hfont); section->font = gpfont; } @@ -5214,7 +5214,7 @@ static void generate_font_link_info(GpGraphics *graphics, WCHAR *string, DWORD l IMLangFontLink_Release(iMLFL); } -static void font_link_get_text_extent_point(struct gdip_font_link_info *font_link_info, GpGraphics *graphics, LPCWSTR string, +static void font_link_get_text_extent_point(struct gdip_font_link_info *font_link_info, GpGraphics *graphics, HDC hdc, LPCWSTR string, INT index, int length, int max_ext, LPINT fit, SIZE *size) { DWORD to_measure_length; @@ -5236,9 +5236,9 @@ static void font_link_get_text_extent_point(struct gdip_font_link_info *font_lin to_measure_length = min(length - (i - index), section->end - i); get_font_hfont(graphics, section->font, NULL, &hfont, NULL, NULL); - oldhfont = SelectObject(graphics->hdc, hfont); - GetTextExtentExPointW(graphics->hdc, &string[i], to_measure_length, max_ext, &fitaux, NULL, &sizeaux); - SelectObject(graphics->hdc, oldhfont); + oldhfont = SelectObject(hdc, hfont); + GetTextExtentExPointW(hdc, &string[i], to_measure_length, max_ext, &fitaux, NULL, &sizeaux); + SelectObject(hdc, oldhfont); DeleteObject(hfont); max_ext -= sizeaux.cx; @@ -5266,7 +5266,7 @@ static void release_font_link_info(struct gdip_font_link_info *font_link_info) } } -GpStatus gdip_format_string(GpGraphics *graphics, +GpStatus gdip_format_string(GpGraphics *graphics, HDC hdc, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, int ignore_empty_clip, gdip_format_string_callback callback, void *user_data) @@ -5348,10 +5348,10 @@ GpStatus gdip_format_string(GpGraphics *graphics, halign = format->align; - generate_font_link_info(graphics, stringdup, length, font, &font_link_info); + generate_font_link_info(graphics, hdc, stringdup, length, font, &font_link_info); while(sum < length){ - font_link_get_text_extent_point(&font_link_info, graphics, stringdup, sum, length - sum, nwidth, &fit, &size); + font_link_get_text_extent_point(&font_link_info, graphics, hdc, stringdup, sum, length - sum, nwidth, &fit, &size); fitcpy = fit; if(fit == 0) @@ -5399,7 +5399,7 @@ GpStatus gdip_format_string(GpGraphics *graphics, else lineend = fit; - font_link_get_text_extent_point(&font_link_info, graphics, stringdup, sum, lineend, nwidth, &j, &size); + font_link_get_text_extent_point(&font_link_info, graphics, hdc, stringdup, sum, lineend, nwidth, &j, &size); bounds.Width = size.cx; @@ -5432,7 +5432,7 @@ GpStatus gdip_format_string(GpGraphics *graphics, if (hotkeyprefix_offsets[hotkeyprefix_end_pos] >= sum + lineend) break; - stat = callback(graphics, stringdup, sum, lineend, + stat = callback(graphics, hdc, stringdup, sum, lineend, &font_link_info, rect, format, lineno, &bounds, &hotkeyprefix_offsets[hotkeyprefix_pos], hotkeyprefix_end_pos-hotkeyprefix_pos, user_data); @@ -5499,7 +5499,7 @@ struct measure_ranges_args { REAL rel_width, rel_height; }; -static GpStatus measure_ranges_callback(GpGraphics *graphics, +static GpStatus measure_ranges_callback(GpGraphics *graphics, HDC hdc, GDIPCONST WCHAR *string, INT index, INT length, struct gdip_font_link_info *font_link_info, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, @@ -5522,10 +5522,10 @@ static GpStatus measure_ranges_callback(GpGraphics *graphics, range_rect.Y = bounds->Y / args->rel_height; range_rect.Height = bounds->Height / args->rel_height; - font_link_get_text_extent_point(font_link_info, graphics, string, index, range_start - index, INT_MAX, NULL, &range_size); + font_link_get_text_extent_point(font_link_info, graphics, hdc, string, index, range_start - index, INT_MAX, NULL, &range_size); range_rect.X = (bounds->X + range_size.cx) / args->rel_width; - font_link_get_text_extent_point(font_link_info, graphics, string, index, range_end - index, INT_MAX, NULL, &range_size); + font_link_get_text_extent_point(font_link_info, graphics, hdc, string, index, range_end - index, INT_MAX, NULL, &range_size); range_rect.Width = (bounds->X + range_size.cx) / args->rel_width - range_rect.X; stat = GdipCombineRegionRect(args->regions[i], &range_rect, CombineModeUnion); @@ -5602,7 +5602,7 @@ GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics, gdi_transform_acquire(graphics); - stat = gdip_format_string(graphics, string, length, font, &scaled_rect, stringFormat, + stat = gdip_format_string(graphics, hdc, string, length, font, &scaled_rect, stringFormat, (stringFormat->attr & StringFormatFlagsNoClip) != 0, measure_ranges_callback, &args); gdi_transform_release(graphics); @@ -5623,7 +5623,7 @@ struct measure_string_args { REAL rel_width, rel_height; }; -static GpStatus measure_string_callback(GpGraphics *graphics, +static GpStatus measure_string_callback(GpGraphics *graphics, HDC hdc, GDIPCONST WCHAR *string, INT index, INT length, struct gdip_font_link_info *font_link_info, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, @@ -5726,7 +5726,7 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics, gdi_transform_acquire(graphics); - gdip_format_string(graphics, string, length, font, &scaled_rect, format, TRUE, + gdip_format_string(graphics, hdc, string, length, font, &scaled_rect, format, TRUE, measure_string_callback, &args); gdi_transform_release(graphics); @@ -5751,7 +5751,7 @@ struct draw_string_args { REAL x, y, rel_width, rel_height, ascent; }; -static GpStatus draw_string_callback(GpGraphics *graphics, +static GpStatus draw_string_callback(GpGraphics *graphics, HDC hdc, GDIPCONST WCHAR *string, INT index, INT length, struct gdip_font_link_info *font_link_info, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, @@ -5775,7 +5775,7 @@ static GpStatus draw_string_callback(GpGraphics *graphics, to_draw_length = min(length - (i - index), section->end - i); TRACE("index %d, todraw %ld, used %s\n", i, to_draw_length, section->font == font_link_info->base_font ? "base font" : "map"); - font_link_get_text_extent_point(font_link_info, graphics, string, i, to_draw_length, 0, NULL, &size); + font_link_get_text_extent_point(font_link_info, graphics, hdc, string, i, to_draw_length, 0, NULL, &size); stat = draw_driver_string(graphics, &string[i], to_draw_length, section->font, format, args->brush, &position, DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL); @@ -5801,10 +5801,10 @@ static GpStatus draw_string_callback(GpGraphics *graphics, SIZE text_size; INT ofs = underlined_indexes[i] - index; - font_link_get_text_extent_point(font_link_info, graphics, string, index, ofs, INT_MAX, NULL, &text_size); + font_link_get_text_extent_point(font_link_info, graphics, hdc, string, index, ofs, INT_MAX, NULL, &text_size); start_x = text_size.cx / args->rel_width; - font_link_get_text_extent_point(font_link_info, graphics, string, index, ofs+1, INT_MAX, NULL, &text_size); + font_link_get_text_extent_point(font_link_info, graphics, hdc, string, index, ofs+1, INT_MAX, NULL, &text_size); end_x = text_size.cx / args->rel_width; GdipFillRectangle(graphics, (GpBrush*)args->brush, position.X+start_x, underline_y, end_x-start_x, underline_height); @@ -5918,7 +5918,7 @@ GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string GetTextMetricsW(hdc, &textmetric); args.ascent = textmetric.tmAscent / rel_height; - gdip_format_string(graphics, string, length, font, &scaled_rect, format, TRUE, + gdip_format_string(graphics, hdc, string, length, font, &scaled_rect, format, TRUE, draw_string_callback, &args); gdi_transform_release(graphics); diff --git a/dlls/gdiplus/graphicspath.c b/dlls/gdiplus/graphicspath.c index 20e0787de16..0d1cfb2f9c5 100644 --- a/dlls/gdiplus/graphicspath.c +++ b/dlls/gdiplus/graphicspath.c @@ -949,7 +949,7 @@ struct format_string_args float ascent; }; -static GpStatus format_string_callback(GpGraphics *graphics, +static GpStatus format_string_callback(GpGraphics *graphics, HDC hdc, GDIPCONST WCHAR *string, INT index, INT length, struct gdip_font_link_info *font_link_info, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, @@ -975,7 +975,7 @@ static GpStatus format_string_callback(GpGraphics *graphics, TTPOLYGONHEADER *ph = NULL, *origph; char *start; DWORD len, ofs = 0; - len = GetGlyphOutlineW(graphics->hdc, string[i], GGO_BEZIER, &gm, 0, NULL, &identity); + len = GetGlyphOutlineW(hdc, string[i], GGO_BEZIER, &gm, 0, NULL, &identity); if (len == GDI_ERROR) { status = GenericError; @@ -989,7 +989,7 @@ static GpStatus format_string_callback(GpGraphics *graphics, status = OutOfMemory; break; } - GetGlyphOutlineW(graphics->hdc, string[i], GGO_BEZIER, &gm, len, start, &identity); + GetGlyphOutlineW(hdc, string[i], GGO_BEZIER, &gm, len, start, &identity); ofs = 0; while (ofs < len) @@ -1117,7 +1117,7 @@ GpStatus WINGDIPAPI GdipAddPathString(GpPath* path, GDIPCONST WCHAR* string, INT args.maxY = 0; args.scale = emSize / native_height; args.ascent = textmetric.tmAscent * args.scale; - status = gdip_format_string(graphics, string, length, NULL, &scaled_layout_rect, + status = gdip_format_string(graphics, dc, string, length, NULL, &scaled_layout_rect, format, TRUE, format_string_callback, &args); DeleteDC(dc); From 8b4c52b70f6a0aa41011fbcb36cafe49ec8f99c1 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Mon, 22 Jan 2024 16:35:45 -0500 Subject: [PATCH 147/413] sapi: Create a new engine only when needed in ISpVoice. Instead of creating the TTS voice engine directly in SetVoice, we save the specified voice token, and only create them in ISpVoice::Speak when necessary. (cherry picked from commit 2a56d4e6e9803183873aa84be29eab1f1ce8acc3) --- dlls/sapi/tts.c | 75 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 24 deletions(-) diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index 410a57b4bfc..c2e36fc274d 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -43,6 +43,7 @@ struct speech_voice LONG ref; ISpStreamFormat *output; + ISpObjectToken *engine_token; ISpTTSEngine *engine; LONG cur_stream_num; DWORD actions; @@ -163,6 +164,7 @@ static ULONG WINAPI speech_voice_Release(ISpeechVoice *iface) { async_cancel_queue(&This->queue); if (This->output) ISpStreamFormat_Release(This->output); + if (This->engine_token) ISpObjectToken_Release(This->engine_token); if (This->engine) ISpTTSEngine_Release(This->engine); DeleteCriticalSection(&This->cs); @@ -660,7 +662,7 @@ static HRESULT WINAPI spvoice_Resume(ISpVoice *iface) static HRESULT WINAPI spvoice_SetVoice(ISpVoice *iface, ISpObjectToken *token) { struct speech_voice *This = impl_from_ISpVoice(iface); - ISpTTSEngine *engine; + WCHAR *id = NULL, *old_id = NULL; HRESULT hr; TRACE("(%p, %p).\n", iface, token); @@ -673,27 +675,37 @@ static HRESULT WINAPI spvoice_SetVoice(ISpVoice *iface, ISpObjectToken *token) else ISpObjectToken_AddRef(token); - hr = ISpObjectToken_CreateInstance(token, NULL, CLSCTX_ALL, &IID_ISpTTSEngine, (void **)&engine); - ISpObjectToken_Release(token); - if (FAILED(hr)) - return hr; - EnterCriticalSection(&This->cs); + if (This->engine_token && + SUCCEEDED(ISpObjectToken_GetId(token, &id)) && + SUCCEEDED(ISpObjectToken_GetId(This->engine_token, &old_id)) && + !wcscmp(id, old_id)) + { + ISpObjectToken_Release(token); + goto done; + } + + if (This->engine_token) + ISpObjectToken_Release(This->engine_token); + This->engine_token = token; + if (This->engine) + { ISpTTSEngine_Release(This->engine); - This->engine = engine; + This->engine = NULL; + } +done: LeaveCriticalSection(&This->cs); - + CoTaskMemFree(id); + CoTaskMemFree(old_id); return S_OK; } static HRESULT WINAPI spvoice_GetVoice(ISpVoice *iface, ISpObjectToken **token) { struct speech_voice *This = impl_from_ISpVoice(iface); - ISpObjectWithToken *engine_token_iface; - HRESULT hr; TRACE("(%p, %p).\n", iface, token); @@ -702,21 +714,18 @@ static HRESULT WINAPI spvoice_GetVoice(ISpVoice *iface, ISpObjectToken **token) EnterCriticalSection(&This->cs); - if (!This->engine) + if (!This->engine_token) { LeaveCriticalSection(&This->cs); return create_default_token(SPCAT_VOICES, token); } - if (SUCCEEDED(hr = ISpTTSEngine_QueryInterface(This->engine, &IID_ISpObjectWithToken, (void **)&engine_token_iface))) - { - hr = ISpObjectWithToken_GetObjectToken(engine_token_iface, token); - ISpObjectWithToken_Release(engine_token_iface); - } + ISpObjectToken_AddRef(This->engine_token); + *token = This->engine_token; LeaveCriticalSection(&This->cs); - return hr; + return S_OK; } struct async_result @@ -731,6 +740,7 @@ struct speak_task struct async_result *result; struct speech_voice *voice; + ISpTTSEngine *engine; SPVTEXTFRAG *frag_list; ISpTTSEngineSite *site; DWORD flags; @@ -773,7 +783,6 @@ static void speak_proc(struct async_task *task) struct speech_voice *This = speak_task->voice; GUID fmtid; WAVEFORMATEX *wfx = NULL; - ISpTTSEngine *engine = NULL; ISpAudio *audio = NULL; HRESULT hr; @@ -794,8 +803,6 @@ static void speak_proc(struct async_task *task) ERR("failed setting output format: %#lx.\n", hr); goto done; } - engine = This->engine; - ISpTTSEngine_AddRef(engine); if (SUCCEEDED(ISpStreamFormat_QueryInterface(This->output, &IID_ISpAudio, (void **)&audio))) ISpAudio_SetState(audio, SPAS_RUN, 0); @@ -804,7 +811,7 @@ static void speak_proc(struct async_task *task) LeaveCriticalSection(&This->cs); - hr = ISpTTSEngine_Speak(engine, speak_task->flags, &fmtid, wfx, speak_task->frag_list, speak_task->site); + hr = ISpTTSEngine_Speak(speak_task->engine, speak_task->flags, &fmtid, wfx, speak_task->frag_list, speak_task->site); if (SUCCEEDED(hr)) { ISpStreamFormat_Commit(This->output, STGC_DEFAULT); @@ -821,7 +828,7 @@ static void speak_proc(struct async_task *task) ISpAudio_Release(audio); } CoTaskMemFree(wfx); - if (engine) ISpTTSEngine_Release(engine); + ISpTTSEngine_Release(speak_task->engine); free(speak_task->frag_list); ISpTTSEngineSite_Release(speak_task->site); @@ -838,6 +845,7 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR { struct speech_voice *This = impl_from_ISpVoice(iface); ISpTTSEngineSite *site = NULL; + ISpTTSEngine *engine = NULL; SPVTEXTFRAG *frag; struct speak_task *speak_task = NULL; struct async_result *result = NULL; @@ -891,12 +899,28 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR return hr; } - if (!This->engine) + EnterCriticalSection(&This->cs); + + if (!This->engine_token) { - /* Create a new engine with the default voice. */ + /* Set the engine token to default. */ if (FAILED(hr = ISpVoice_SetVoice(iface, NULL))) + { + LeaveCriticalSection(&This->cs); return hr; + } } + if (!This->engine && + FAILED(hr = ISpObjectToken_CreateInstance(This->engine_token, NULL, CLSCTX_ALL, &IID_ISpTTSEngine, (void **)&This->engine))) + { + LeaveCriticalSection(&This->cs); + ERR("Failed to create engine: %#lx.\n", hr); + return hr; + } + engine = This->engine; + ISpTTSEngine_AddRef(engine); + + LeaveCriticalSection(&This->cs); if (!(frag = malloc(sizeof(*frag) + contents_size))) return E_OUTOFMEMORY; @@ -920,6 +944,7 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR speak_task->task.proc = speak_proc; speak_task->result = NULL; speak_task->voice = This; + speak_task->engine = engine; speak_task->frag_list = frag; speak_task->site = site; speak_task->flags = flags & SPF_NLP_SPEAK_PUNC; @@ -958,6 +983,7 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR fail: if (site) ISpTTSEngineSite_Release(site); + if (engine) ISpTTSEngine_Release(engine); free(frag); free(speak_task); if (result) @@ -1397,6 +1423,7 @@ HRESULT speech_voice_create(IUnknown *outer, REFIID iid, void **obj) This->ref = 1; This->output = NULL; + This->engine_token = NULL; This->engine = NULL; This->cur_stream_num = 0; This->actions = SPVES_CONTINUE; From 75bf2dae1d0ef93c74d55f1fbb6897bed62c3071 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 2 Feb 2024 01:58:10 -0500 Subject: [PATCH 148/413] sapi: Add ISpeechObjectToken stub. (cherry picked from commit c45bedbf23ddf619b2aa034e843dc010f47b4f58) CW-Bug-Id: #22520 --- dlls/sapi/tests/token.c | 27 +++++ dlls/sapi/token.c | 225 ++++++++++++++++++++++++++++++++++++++-- include/sapi.idl | 2 +- 3 files changed, 247 insertions(+), 7 deletions(-) diff --git a/dlls/sapi/tests/token.c b/dlls/sapi/tests/token.c index a7d164389d9..0fe142f89a8 100644 --- a/dlls/sapi/tests/token.c +++ b/dlls/sapi/tests/token.c @@ -604,6 +604,8 @@ static void test_object_token(void) static const WCHAR test_token_id[] = L"HKEY_LOCAL_MACHINE\\Software\\Wine\\Winetest\\sapi\\TestToken"; ISpObjectToken *token; + IDispatch *disp; + ISpeechObjectToken *speech_token; ISpDataKey *sub_key; HRESULT hr; LPWSTR tempW, token_id; @@ -611,6 +613,31 @@ static void test_object_token(void) DWORD regid; IUnknown *obj; + hr = CoCreateInstance( &CLSID_SpObjectToken, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpObjectToken, (void **)&token ); + ok( hr == S_OK, "got %08lx\n", hr ); + + hr = ISpObjectToken_QueryInterface( token, &IID_IDispatch, (void **)&disp ); + ok( hr == S_OK, "got %08lx\n", hr ); + IDispatch_Release( disp ); + + hr = ISpObjectToken_QueryInterface( token, &IID_ISpeechObjectToken, (void **)&speech_token ); + ok( hr == S_OK, "got %08lx\n", hr ); + ISpeechObjectToken_Release( speech_token ); + + ISpObjectToken_Release( token ); + + hr = CoCreateInstance( &CLSID_SpObjectToken, NULL, CLSCTX_INPROC_SERVER, + &IID_IDispatch, (void **)&disp ); + ok( hr == S_OK, "got %08lx\n", hr ); + IDispatch_Release( disp ); + + hr = CoCreateInstance( &CLSID_SpObjectToken, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpeechObjectToken, (void **)&speech_token ); + ok( hr == S_OK, "got %08lx\n", hr ); + ISpeechObjectToken_Release( speech_token ); + + hr = CoCreateInstance( &CLSID_SpObjectToken, NULL, CLSCTX_INPROC_SERVER, &IID_ISpObjectToken, (void **)&token ); ok( hr == S_OK, "got %08lx\n", hr ); diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c index f599bdb6b14..fd4efb9a95a 100644 --- a/dlls/sapi/token.c +++ b/dlls/sapi/token.c @@ -51,6 +51,7 @@ static struct data_key *impl_from_ISpRegDataKey( ISpRegDataKey *iface ) struct object_token { ISpObjectToken ISpObjectToken_iface; + ISpeechObjectToken ISpeechObjectToken_iface; LONG ref; ISpRegDataKey *data_key; @@ -62,6 +63,11 @@ static struct object_token *impl_from_ISpObjectToken( ISpObjectToken *iface ) return CONTAINING_RECORD( iface, struct object_token, ISpObjectToken_iface ); } +static struct object_token *impl_from_ISpeechObjectToken( ISpeechObjectToken *iface ) +{ + return CONTAINING_RECORD( iface, struct object_token, ISpeechObjectToken_iface ); +} + static HRESULT WINAPI data_key_QueryInterface( ISpRegDataKey *iface, REFIID iid, void **obj ) { struct data_key *This = impl_from_ISpRegDataKey( iface ); @@ -1160,15 +1166,19 @@ static HRESULT WINAPI token_QueryInterface( ISpObjectToken *iface, if (IsEqualIID( iid, &IID_IUnknown ) || IsEqualIID( iid, &IID_ISpDataKey ) || IsEqualIID( iid, &IID_ISpObjectToken )) + *obj = &This->ISpObjectToken_iface; + else if (IsEqualIID( iid, &IID_IDispatch ) || + IsEqualIID( iid, &IID_ISpeechObjectToken )) + *obj = &This->ISpeechObjectToken_iface; + else { - ISpObjectToken_AddRef( iface ); - *obj = iface; - return S_OK; + *obj = NULL; + FIXME( "interface %s not implemented\n", debugstr_guid( iid ) ); + return E_NOINTERFACE; } - FIXME( "interface %s not implemented\n", debugstr_guid( iid ) ); - *obj = NULL; - return E_NOINTERFACE; + IUnknown_AddRef( (IUnknown *)*obj ); + return S_OK; } static ULONG WINAPI token_AddRef( ISpObjectToken *iface ) @@ -1495,6 +1505,208 @@ const struct ISpObjectTokenVtbl token_vtbl = token_MatchesAttributes }; +static HRESULT WINAPI speech_token_QueryInterface( ISpeechObjectToken *iface, + REFIID iid, void **obj ) +{ + struct object_token *This = impl_from_ISpeechObjectToken( iface ); + + TRACE( "(%p)->(%s %p)\n", This, debugstr_guid( iid ), obj ); + + return ISpObjectToken_QueryInterface( &This->ISpObjectToken_iface, iid, obj ); +} + +static ULONG WINAPI speech_token_AddRef( ISpeechObjectToken *iface ) +{ + struct object_token *This = impl_from_ISpeechObjectToken( iface ); + + TRACE( "(%p)\n", This ); + + return ISpObjectToken_AddRef( &This->ISpObjectToken_iface ); +} + +static ULONG WINAPI speech_token_Release( ISpeechObjectToken *iface ) +{ + struct object_token *This = impl_from_ISpeechObjectToken( iface ); + + TRACE( "(%p)\n", This ); + + return ISpObjectToken_Release( &This->ISpObjectToken_iface ); +} + +static HRESULT WINAPI speech_token_GetTypeInfoCount( ISpeechObjectToken *iface, + UINT *count ) +{ + + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_token_GetTypeInfo( ISpeechObjectToken *iface, + UINT index, + LCID lcid, + ITypeInfo **type_info ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_token_GetIDsOfNames( ISpeechObjectToken *iface, + REFIID iid, + LPOLESTR *names, + UINT count, + LCID lcid, + DISPID *dispids ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_token_Invoke( ISpeechObjectToken *iface, + DISPID dispid, + REFIID iid, + LCID lcid, + WORD flags, + DISPPARAMS *params, + VARIANT *result, + EXCEPINFO *excepinfo, + UINT *argerr ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_token_get_Id( ISpeechObjectToken *iface, + BSTR *id ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_token_get_DataKey( ISpeechObjectToken *iface, + ISpeechDataKey **key ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_token_get_Category( ISpeechObjectToken *iface, + ISpeechObjectTokenCategory **cat ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_token_GetDescription( ISpeechObjectToken *iface, + LONG locale, BSTR *desc ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_token_SetId( ISpeechObjectToken *iface, + BSTR id, BSTR category_id, + VARIANT_BOOL create ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_token_GetAttribute( ISpeechObjectToken *iface, + BSTR name, BSTR *value ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_token_CreateInstance( ISpeechObjectToken *iface, + IUnknown *outer, + SpeechTokenContext clsctx, + IUnknown **object ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_token_Remove( ISpeechObjectToken *iface, + BSTR clsid ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_token_GetStorageFileName( ISpeechObjectToken *iface, + BSTR clsid, + BSTR key, + BSTR name, + SpeechTokenShellFolder folder, + BSTR *path ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_token_RemoveStorageFileName( ISpeechObjectToken *iface, + BSTR clsid, + BSTR key, + VARIANT_BOOL remove ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_token_IsUISupported( ISpeechObjectToken *iface, + const BSTR type, + const VARIANT *data, + IUnknown *object, + VARIANT_BOOL *supported ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_token_DisplayUI( ISpeechObjectToken *iface, + LONG hwnd, + BSTR title, + const BSTR type, + const VARIANT *data, + IUnknown *object ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_token_MatchesAttributes( ISpeechObjectToken *iface, + const BSTR attributes, + VARIANT_BOOL *matches ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +const struct ISpeechObjectTokenVtbl speech_token_vtbl = +{ + speech_token_QueryInterface, + speech_token_AddRef, + speech_token_Release, + speech_token_GetTypeInfoCount, + speech_token_GetTypeInfo, + speech_token_GetIDsOfNames, + speech_token_Invoke, + speech_token_get_Id, + speech_token_get_DataKey, + speech_token_get_Category, + speech_token_GetDescription, + speech_token_SetId, + speech_token_GetAttribute, + speech_token_CreateInstance, + speech_token_Remove, + speech_token_GetStorageFileName, + speech_token_RemoveStorageFileName, + speech_token_IsUISupported, + speech_token_DisplayUI, + speech_token_MatchesAttributes +}; + HRESULT token_create( IUnknown *outer, REFIID iid, void **obj ) { struct object_token *This = malloc( sizeof(*This) ); @@ -1502,6 +1714,7 @@ HRESULT token_create( IUnknown *outer, REFIID iid, void **obj ) if (!This) return E_OUTOFMEMORY; This->ISpObjectToken_iface.lpVtbl = &token_vtbl; + This->ISpeechObjectToken_iface.lpVtbl = &speech_token_vtbl; This->ref = 1; This->data_key = NULL; diff --git a/include/sapi.idl b/include/sapi.idl index 16b8348d73b..6a593eb6b18 100644 --- a/include/sapi.idl +++ b/include/sapi.idl @@ -1293,7 +1293,7 @@ library SpeechLib coclass SpObjectToken { interface ISpObjectToken; - [default] interface ISpDataKey; + [default] interface ISpeechObjectToken; } [ From 981fc608916edecba5eed9bf053155031eceb741 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 2 Feb 2024 02:19:38 -0500 Subject: [PATCH 149/413] sapi: Add ISpeechObjectTokens stub. (cherry picked from commit ca6f3a7af87ded6f54c23aa5a983bbe9ee74667f) CW-Bug-Id: #22520 --- dlls/sapi/tests/token.c | 30 ++++++++++ dlls/sapi/token.c | 129 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 153 insertions(+), 6 deletions(-) diff --git a/dlls/sapi/tests/token.c b/dlls/sapi/tests/token.c index 0fe142f89a8..9350c304e41 100644 --- a/dlls/sapi/tests/token.c +++ b/dlls/sapi/tests/token.c @@ -246,12 +246,42 @@ static void test_token_enum(void) { ISpObjectTokenEnumBuilder *token_enum; HRESULT hr; + IDispatch *disp; + ISpeechObjectTokens *speech_tokens; ISpObjectToken *tokens[5]; ISpObjectToken *out_tokens[5]; WCHAR token_id[MAX_PATH]; ULONG count; int i; + hr = CoCreateInstance( &CLSID_SpObjectTokenEnum, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpObjectTokenEnumBuilder, (void **)&token_enum ); + ok( hr == S_OK, "got %08lx\n", hr ); + + hr = ISpObjectTokenEnumBuilder_QueryInterface( token_enum, + &IID_IDispatch, (void **)&disp ); + ok( hr == S_OK, "got %08lx\n", hr ); + IDispatch_Release( disp ); + + hr = ISpObjectTokenEnumBuilder_QueryInterface( token_enum, + &IID_ISpeechObjectTokens, + (void **)&speech_tokens ); + ok( hr == S_OK, "got %08lx\n", hr ); + ISpeechObjectTokens_Release( speech_tokens ); + + ISpObjectTokenEnumBuilder_Release( token_enum ); + + hr = CoCreateInstance( &CLSID_SpObjectTokenEnum, NULL, CLSCTX_INPROC_SERVER, + &IID_IDispatch, (void **)&disp ); + ok( hr == S_OK, "got %08lx\n", hr ); + IDispatch_Release( disp ); + + hr = CoCreateInstance( &CLSID_SpObjectTokenEnum, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpeechObjectTokens, (void **)&speech_tokens ); + ok( hr == S_OK, "got %08lx\n", hr ); + ISpeechObjectTokens_Release( speech_tokens ); + + hr = CoCreateInstance( &CLSID_SpObjectTokenEnum, NULL, CLSCTX_INPROC_SERVER, &IID_ISpObjectTokenEnumBuilder, (void **)&token_enum ); ok( hr == S_OK, "got %08lx\n", hr ); diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c index fd4efb9a95a..4688e6a07b9 100644 --- a/dlls/sapi/token.c +++ b/dlls/sapi/token.c @@ -579,6 +579,7 @@ struct token_with_score struct token_enum { ISpObjectTokenEnumBuilder ISpObjectTokenEnumBuilder_iface; + ISpeechObjectTokens ISpeechObjectTokens_iface; LONG ref; BOOL init; @@ -593,6 +594,11 @@ static struct token_enum *impl_from_ISpObjectTokenEnumBuilder( ISpObjectTokenEnu return CONTAINING_RECORD( iface, struct token_enum, ISpObjectTokenEnumBuilder_iface ); } +static struct token_enum *impl_from_ISpeechObjectTokens( ISpeechObjectTokens *iface ) +{ + return CONTAINING_RECORD( iface, struct token_enum, ISpeechObjectTokens_iface ); +} + static HRESULT WINAPI token_category_EnumTokens( ISpObjectTokenCategory *iface, LPCWSTR req, LPCWSTR opt, IEnumSpObjectTokens **enum_tokens ) @@ -765,15 +771,19 @@ static HRESULT WINAPI token_enum_QueryInterface( ISpObjectTokenEnumBuilder *ifac if (IsEqualIID( iid, &IID_IUnknown ) || IsEqualIID( iid, &IID_IEnumSpObjectTokens ) || IsEqualIID( iid, &IID_ISpObjectTokenEnumBuilder )) + *obj = &This->ISpObjectTokenEnumBuilder_iface; + else if (IsEqualIID( iid, &IID_IDispatch ) || + IsEqualIID( iid, &IID_ISpeechObjectTokens )) + *obj = &This->ISpeechObjectTokens_iface; + else { - ISpObjectTokenEnumBuilder_AddRef( iface ); - *obj = iface; - return S_OK; + *obj = NULL; + FIXME( "interface %s not implemented\n", debugstr_guid( iid ) ); + return E_NOINTERFACE; } - FIXME( "interface %s not implemented\n", debugstr_guid( iid ) ); - *obj = NULL; - return E_NOINTERFACE; + IUnknown_AddRef( (IUnknown *)*obj ); + return S_OK; } static ULONG WINAPI token_enum_AddRef( ISpObjectTokenEnumBuilder *iface ) @@ -1135,6 +1145,112 @@ const struct ISpObjectTokenEnumBuilderVtbl token_enum_vtbl = token_enum_Sort }; +static HRESULT WINAPI speech_tokens_QueryInterface( ISpeechObjectTokens *iface, + REFIID iid, void **obj ) +{ + struct token_enum *This = impl_from_ISpeechObjectTokens( iface ); + + TRACE( "(%p)->(%s %p)\n", This, debugstr_guid( iid ), obj ); + + return ISpObjectTokenEnumBuilder_QueryInterface( + &This->ISpObjectTokenEnumBuilder_iface, iid, obj ); +} + +static ULONG WINAPI speech_tokens_AddRef( ISpeechObjectTokens *iface ) +{ + struct token_enum *This = impl_from_ISpeechObjectTokens( iface ); + + TRACE( "(%p)\n", This ); + + return ISpObjectTokenEnumBuilder_AddRef( &This->ISpObjectTokenEnumBuilder_iface ); +} + +static ULONG WINAPI speech_tokens_Release( ISpeechObjectTokens *iface ) +{ + struct token_enum *This = impl_from_ISpeechObjectTokens( iface ); + + TRACE( "(%p)\n", This ); + + return ISpObjectTokenEnumBuilder_Release( &This->ISpObjectTokenEnumBuilder_iface ); +} + +static HRESULT WINAPI speech_tokens_GetTypeInfoCount( ISpeechObjectTokens *iface, + UINT *count ) +{ + + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_tokens_GetTypeInfo( ISpeechObjectTokens *iface, + UINT index, + LCID lcid, + ITypeInfo **type_info ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_tokens_GetIDsOfNames( ISpeechObjectTokens *iface, + REFIID iid, + LPOLESTR *names, + UINT count, + LCID lcid, + DISPID *dispids ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_tokens_Invoke( ISpeechObjectTokens *iface, + DISPID dispid, + REFIID iid, + LCID lcid, + WORD flags, + DISPPARAMS *params, + VARIANT *result, + EXCEPINFO *excepinfo, + UINT *argerr ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_tokens_get_Count( ISpeechObjectTokens *iface, + LONG *count ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_tokens_Item( ISpeechObjectTokens *iface, + LONG index, ISpeechObjectToken **token ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_tokens_get__NewEnum( ISpeechObjectTokens *iface, + IUnknown **new_enum ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static const ISpeechObjectTokensVtbl speech_tokens_vtbl = +{ + speech_tokens_QueryInterface, + speech_tokens_AddRef, + speech_tokens_Release, + speech_tokens_GetTypeInfoCount, + speech_tokens_GetTypeInfo, + speech_tokens_GetIDsOfNames, + speech_tokens_Invoke, + speech_tokens_get_Count, + speech_tokens_Item, + speech_tokens_get__NewEnum +}; + HRESULT token_enum_create( IUnknown *outer, REFIID iid, void **obj ) { struct token_enum *This = malloc( sizeof(*This) ); @@ -1142,6 +1258,7 @@ HRESULT token_enum_create( IUnknown *outer, REFIID iid, void **obj ) if (!This) return E_OUTOFMEMORY; This->ISpObjectTokenEnumBuilder_iface.lpVtbl = &token_enum_vtbl; + This->ISpeechObjectTokens_iface.lpVtbl = &speech_tokens_vtbl; This->ref = 1; This->req = NULL; This->opt = NULL; From 05357941e9e8cc95dcc8ca8ca6217c05789c1c17 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 2 Feb 2024 14:24:34 -0500 Subject: [PATCH 150/413] sapi: Add stub implementation for ISpeechObjectTokens::get__NewEnum. (cherry picked from commit 26498d93cf9354237eef3ef5a2b3612527068923) CW-Bug-Id: #22520 --- dlls/sapi/tests/token.c | 26 +++++++++ dlls/sapi/token.c | 117 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 141 insertions(+), 2 deletions(-) diff --git a/dlls/sapi/tests/token.c b/dlls/sapi/tests/token.c index 9350c304e41..543b19ec69a 100644 --- a/dlls/sapi/tests/token.c +++ b/dlls/sapi/tests/token.c @@ -248,6 +248,8 @@ static void test_token_enum(void) HRESULT hr; IDispatch *disp; ISpeechObjectTokens *speech_tokens; + IUnknown *unk; + IEnumVARIANT *enumvar; ISpObjectToken *tokens[5]; ISpObjectToken *out_tokens[5]; WCHAR token_id[MAX_PATH]; @@ -348,6 +350,30 @@ static void test_token_enum(void) &IID_ISpObjectTokenEnumBuilder, (void **)&token_enum ); ok( hr == S_OK, "got %08lx\n", hr ); + hr = ISpObjectTokenEnumBuilder_QueryInterface( token_enum, + &IID_ISpeechObjectTokens, + (void **)&speech_tokens ); + ok( hr == S_OK, "got %08lx\n", hr ); + + hr = ISpObjectTokenEnumBuilder_SetAttribs( token_enum, NULL, NULL ); + ok( hr == S_OK, "got %08lx\n", hr ); + hr = ISpObjectTokenEnumBuilder_AddTokens( token_enum, 3, tokens ); + ok( hr == S_OK, "got %08lx\n", hr ); + + hr = ISpeechObjectTokens_get__NewEnum( speech_tokens, &unk ); + ok( hr == S_OK, "got %08lx\n", hr ); + hr = IUnknown_QueryInterface( unk, &IID_IEnumVARIANT, (void **)&enumvar ); + ok( hr == S_OK, "got %08lx\n", hr ); + IUnknown_Release( unk ); + IEnumVARIANT_Release( enumvar ); + + ISpeechObjectTokens_Release( speech_tokens ); + ISpObjectTokenEnumBuilder_Release( token_enum ); + + hr = CoCreateInstance( &CLSID_SpObjectTokenEnum, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpObjectTokenEnumBuilder, (void **)&token_enum ); + ok( hr == S_OK, "got %08lx\n", hr ); + /* Vendor attribute must exist */ hr = ISpObjectTokenEnumBuilder_SetAttribs( token_enum, L"Vendor", NULL ); ok( hr == S_OK, "got %08lx\n", hr ); diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c index 4688e6a07b9..53b64a4383f 100644 --- a/dlls/sapi/token.c +++ b/dlls/sapi/token.c @@ -599,6 +599,20 @@ static struct token_enum *impl_from_ISpeechObjectTokens( ISpeechObjectTokens *if return CONTAINING_RECORD( iface, struct token_enum, ISpeechObjectTokens_iface ); } +struct enum_var +{ + IEnumVARIANT IEnumVARIANT_iface; + LONG ref; + + ISpObjectTokenEnumBuilder *token_enum; + ULONG index; +}; + +static struct enum_var *impl_from_IEnumVARIANT( IEnumVARIANT *iface ) +{ + return CONTAINING_RECORD( iface, struct enum_var, IEnumVARIANT_iface ); +} + static HRESULT WINAPI token_category_EnumTokens( ISpObjectTokenCategory *iface, LPCWSTR req, LPCWSTR opt, IEnumSpObjectTokens **enum_tokens ) @@ -1145,6 +1159,86 @@ const struct ISpObjectTokenEnumBuilderVtbl token_enum_vtbl = token_enum_Sort }; +static HRESULT WINAPI enum_var_QueryInterface( IEnumVARIANT *iface, + REFIID iid, void **obj ) +{ + struct enum_var *This = impl_from_IEnumVARIANT( iface ); + + TRACE( "(%p)->(%s %p)\n", This, debugstr_guid( iid ), obj ); + + if (IsEqualIID( iid, &IID_IUnknown ) || + IsEqualIID( iid, &IID_IEnumVARIANT )) + { + IEnumVARIANT_AddRef( iface ); + *obj = iface; + return S_OK; + } + + *obj = NULL; + FIXME( "interface %s not implemented\n", debugstr_guid( iid ) ); + return E_NOINTERFACE; +} + +static ULONG WINAPI enum_var_AddRef( IEnumVARIANT *iface ) +{ + struct enum_var *This = impl_from_IEnumVARIANT( iface ); + ULONG ref = InterlockedIncrement( &This->ref ); + + TRACE( "(%p) ref = %lu\n", This, ref ); + return ref; +} + +static ULONG WINAPI enum_var_Release( IEnumVARIANT *iface ) +{ + struct enum_var *This = impl_from_IEnumVARIANT( iface ); + ULONG ref = InterlockedDecrement( &This->ref ); + + TRACE( "(%p) ref = %lu\n", This, ref ); + + if (!ref) + { + ISpObjectTokenEnumBuilder_Release( This->token_enum ); + free( This ); + } + return ref; +} + +static HRESULT WINAPI enum_var_Next( IEnumVARIANT *iface, ULONG count, + VARIANT *vars, ULONG *fetched ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI enum_var_Skip( IEnumVARIANT *iface, ULONG count ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI enum_var_Reset( IEnumVARIANT *iface ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI enum_var_Clone( IEnumVARIANT *iface, IEnumVARIANT **new_enum ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static const IEnumVARIANTVtbl enum_var_vtbl = +{ + enum_var_QueryInterface, + enum_var_AddRef, + enum_var_Release, + enum_var_Next, + enum_var_Skip, + enum_var_Reset, + enum_var_Clone +}; + static HRESULT WINAPI speech_tokens_QueryInterface( ISpeechObjectTokens *iface, REFIID iid, void **obj ) { @@ -1233,8 +1327,27 @@ static HRESULT WINAPI speech_tokens_Item( ISpeechObjectTokens *iface, static HRESULT WINAPI speech_tokens_get__NewEnum( ISpeechObjectTokens *iface, IUnknown **new_enum ) { - FIXME( "stub\n" ); - return E_NOTIMPL; + struct enum_var *enum_var; + HRESULT hr; + + TRACE( "(%p)->(%p)\n", iface, new_enum ); + + if (!new_enum) return E_POINTER; + if (!(enum_var = malloc( sizeof(*enum_var) ))) return E_OUTOFMEMORY; + + enum_var->IEnumVARIANT_iface.lpVtbl = &enum_var_vtbl; + enum_var->ref = 1; + enum_var->index = 0; + if (FAILED(hr = ISpeechObjectTokens_QueryInterface( iface, &IID_ISpObjectTokenEnumBuilder, + (void **)&enum_var->token_enum ))) + { + free( enum_var ); + return hr; + } + + *new_enum = (IUnknown *)&enum_var->IEnumVARIANT_iface; + + return S_OK; } static const ISpeechObjectTokensVtbl speech_tokens_vtbl = From cd57f3f327fda1ea5014766d281dd6e27ee59c8e Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 8 Feb 2024 20:20:36 -0500 Subject: [PATCH 151/413] sapi: Implement ISpeechObjectToken::GetDescription. (cherry picked from commit b6c1760727dae433570ef0dffb985c751d4dc0bc) CW-Bug-Id: #22520 --- dlls/sapi/Makefile.in | 2 +- dlls/sapi/tests/Makefile.in | 2 +- dlls/sapi/tests/token.c | 58 +++++++++++++++++++++++++++++++++++-- dlls/sapi/token.c | 23 +++++++++++++-- 4 files changed, 79 insertions(+), 6 deletions(-) diff --git a/dlls/sapi/Makefile.in b/dlls/sapi/Makefile.in index 008d0f74545..24a1e055766 100644 --- a/dlls/sapi/Makefile.in +++ b/dlls/sapi/Makefile.in @@ -1,5 +1,5 @@ MODULE = sapi.dll -IMPORTS = uuid ole32 user32 advapi32 +IMPORTS = uuid ole32 oleaut32 user32 advapi32 DELAYIMPORTS = winmm SOURCES = \ diff --git a/dlls/sapi/tests/Makefile.in b/dlls/sapi/tests/Makefile.in index 4c107d5e994..4e792f2daae 100644 --- a/dlls/sapi/tests/Makefile.in +++ b/dlls/sapi/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = sapi.dll -IMPORTS = ole32 user32 advapi32 winmm +IMPORTS = ole32 oleaut32 user32 advapi32 winmm SOURCES = \ automation.c \ diff --git a/dlls/sapi/tests/token.c b/dlls/sapi/tests/token.c index 543b19ec69a..a651eb47c62 100644 --- a/dlls/sapi/tests/token.c +++ b/dlls/sapi/tests/token.c @@ -657,7 +657,7 @@ static IClassFactory test_class_cf = { &ClassFactoryVtbl }; static void test_object_token(void) { - static const WCHAR test_token_id[] = L"HKEY_LOCAL_MACHINE\\Software\\Wine\\Winetest\\sapi\\TestToken"; + static const WCHAR test_token_id[] = L"HKEY_LOCAL_MACHINE\\Software\\Winetest\\sapi\\TestToken"; ISpObjectToken *token; IDispatch *disp; @@ -665,6 +665,7 @@ static void test_object_token(void) ISpDataKey *sub_key; HRESULT hr; LPWSTR tempW, token_id; + BSTR tempB; ISpObjectTokenCategory *cat; DWORD regid; IUnknown *obj; @@ -863,11 +864,64 @@ static void test_object_token(void) CoTaskMemFree( tempW ); } + hr = ISpObjectToken_SetStringValue( token, L"409", L"409 - TestToken" ); + ok( hr == S_OK, "got %08lx\n", hr ); + hr = ISpObjectToken_SetStringValue( token, L"407", L"407 - TestToken" ); + ok( hr == S_OK, "got %08lx\n", hr ); + hr = ISpObjectToken_SetStringValue( token, L"E40C", L"E40C - TestToken" ); + ok( hr == S_OK, "got %08lx\n", hr ); + + hr = ISpObjectToken_QueryInterface( token, &IID_ISpeechObjectToken, (void **)&speech_token ); + ok( hr == S_OK, "got %08lx\n", hr ); + + hr = ISpeechObjectToken_GetDescription( speech_token, 0x409, NULL ); + ok( hr == E_POINTER, "got %08lx\n", hr ); + + tempB = NULL; + hr = ISpeechObjectToken_GetDescription( speech_token, 0x409, &tempB ); + ok( hr == S_OK, "got %08lx\n", hr ); + ok( tempB && !wcscmp( tempB, L"409 - TestToken" ), "got %s\n", wine_dbgstr_w( tempB ) ); + SysFreeString( tempB ); + + tempB = NULL; + hr = ISpeechObjectToken_GetDescription( speech_token, 0x10407, &tempB ); + ok( hr == S_OK, "got %08lx\n", hr ); + ok( tempB && !wcscmp( tempB, L"407 - TestToken" ), "got %s\n", wine_dbgstr_w( tempB ) ); + SysFreeString( tempB ); + + tempB = NULL; + hr = ISpeechObjectToken_GetDescription( speech_token, 0xE40C, &tempB ); + ok( hr == S_OK, "got %08lx\n", hr ); + ok( tempB && !wcscmp( tempB, L"E40C - TestToken" ), "got %s\n", wine_dbgstr_w( tempB ) ); + SysFreeString( tempB ); + + tempB = (BSTR)0xdeadbeef; + hr = ISpeechObjectToken_GetDescription( speech_token, 0x406, &tempB ); + ok( hr == SPERR_NOT_FOUND, "got %08lx\n", hr ); + ok( tempB == (BSTR)0xdeadbeef || broken(tempB == NULL) /* < win7 */, "got %p\n", tempB ); + + hr = ISpObjectToken_SetStringValue( token, NULL, L"TestToken" ); + ok( hr == S_OK, "got %08lx\n", hr ); + + tempB = NULL; + hr = ISpeechObjectToken_GetDescription( speech_token, 0x406, &tempB ); + ok( hr == S_OK, "got %08lx\n", hr ); + ok( tempB && !wcscmp( tempB, L"TestToken" ), "got %s\n", wine_dbgstr_w( tempB ) ); + SysFreeString( tempB ); + + tempB = NULL; + hr = ISpeechObjectToken_GetDescription( speech_token, 0x0, &tempB ); + ok( hr == S_OK, "got %08lx\n", hr ); + ok( tempB && !wcscmp( tempB, L"TestToken" ), "got %s\n", wine_dbgstr_w( tempB ) ); + SysFreeString( tempB ); + + ISpeechObjectToken_Release( speech_token ); + ISpObjectToken_Release( test_class_token ); IUnknown_Release( obj ); ISpObjectToken_Release( token ); - RegDeleteTreeA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Winetest\\sapi" ); + RegDeleteTreeA( HKEY_LOCAL_MACHINE, "Software\\Winetest\\sapi" ); } START_TEST(token) diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c index 53b64a4383f..e5d87505e34 100644 --- a/dlls/sapi/token.c +++ b/dlls/sapi/token.c @@ -1829,8 +1829,27 @@ static HRESULT WINAPI speech_token_get_Category( ISpeechObjectToken *iface, static HRESULT WINAPI speech_token_GetDescription( ISpeechObjectToken *iface, LONG locale, BSTR *desc ) { - FIXME( "stub\n" ); - return E_NOTIMPL; + struct object_token *This = impl_from_ISpeechObjectToken( iface ); + WCHAR langid[5]; + WCHAR *desc_wstr = NULL; + HRESULT hr; + + TRACE( "(%p)->(%#lx %p)\n", This, locale, desc ); + + if (!desc) return E_POINTER; + + swprintf( langid, ARRAY_SIZE( langid ), L"%X", LANGIDFROMLCID( locale ) ); + + hr = ISpObjectToken_GetStringValue( &This->ISpObjectToken_iface, langid, &desc_wstr ); + if (hr == SPERR_NOT_FOUND) + hr = ISpObjectToken_GetStringValue( &This->ISpObjectToken_iface, NULL, &desc_wstr ); + if (FAILED(hr)) + return hr; + + *desc = SysAllocString( desc_wstr ); + + CoTaskMemFree( desc_wstr ); + return *desc ? S_OK : E_OUTOFMEMORY; } static HRESULT WINAPI speech_token_SetId( ISpeechObjectToken *iface, From 4af47d885832d4289f07468b96c1f3f905fefcd0 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 7 Feb 2024 20:01:11 -0500 Subject: [PATCH 152/413] sapi: Implement ISpeechObjectToken::Invoke. (cherry picked from commit 001d1a4319dfb46a4383758a446203350c91edc1) CW-Bug-Id: #22520 --- dlls/sapi/Makefile.in | 1 + dlls/sapi/dispatch.c | 85 ++++++++++++++++++++++++++++++++++++++++ dlls/sapi/sapi_private.h | 9 +++++ dlls/sapi/tests/token.c | 20 ++++++++++ dlls/sapi/token.c | 14 ++++++- 5 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 dlls/sapi/dispatch.c diff --git a/dlls/sapi/Makefile.in b/dlls/sapi/Makefile.in index 24a1e055766..a6c4d86037e 100644 --- a/dlls/sapi/Makefile.in +++ b/dlls/sapi/Makefile.in @@ -5,6 +5,7 @@ DELAYIMPORTS = winmm SOURCES = \ async.c \ automation.c \ + dispatch.c \ main.c \ mmaudio.c \ resource.c \ diff --git a/dlls/sapi/dispatch.c b/dlls/sapi/dispatch.c new file mode 100644 index 00000000000..df0c6c9bf9c --- /dev/null +++ b/dlls/sapi/dispatch.c @@ -0,0 +1,85 @@ +/* + * ITypeInfo cache for IDispatch + * + * Copyright 2019 Zebediah Figura + * + * 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 + */ + +#include "wine/debug.h" + +#define COBJMACROS + +#include "objbase.h" +#include "sapiddk.h" + +#include "sapi_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(sapi); + +static ITypeLib *typelib; +static ITypeInfo *typeinfos[last_tid]; + +static REFIID tid_id[] = +{ + &IID_ISpeechObjectToken, +}; + +HRESULT get_typeinfo(enum type_id tid, ITypeInfo **ret) +{ + HRESULT hr; + + if (!typelib) + { + ITypeLib *tl; + + hr = LoadRegTypeLib(&LIBID_SpeechLib, 5, 4, LOCALE_SYSTEM_DEFAULT, &tl); + if (FAILED(hr)) + { + ERR("Failed to load typelib, hr %#lx.\n", hr); + return hr; + } + if (InterlockedCompareExchangePointer((void **)&typelib, tl, NULL)) + ITypeLib_Release(tl); + } + if (!typeinfos[tid]) + { + ITypeInfo *typeinfo; + + hr = ITypeLib_GetTypeInfoOfGuid(typelib, tid_id[tid], &typeinfo); + if (FAILED(hr)) + { + ERR("Failed to get type info for %s, hr %#lx.\n", debugstr_guid(tid_id[tid]), hr); + return hr; + } + if (InterlockedCompareExchangePointer((void **)(typeinfos + tid), typeinfo, NULL)) + ITypeInfo_Release(typeinfo); + } + ITypeInfo_AddRef(*ret = typeinfos[tid]); + return S_OK; +} + +void release_typelib(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(typeinfos); ++i) + { + if (typeinfos[i]) + ITypeInfo_Release(typeinfos[i]); + } + if (typelib) + ITypeLib_Release(typelib); +} diff --git a/dlls/sapi/sapi_private.h b/dlls/sapi/sapi_private.h index f6a5e966461..9a3c92cc4dc 100644 --- a/dlls/sapi/sapi_private.h +++ b/dlls/sapi/sapi_private.h @@ -52,3 +52,12 @@ HRESULT mmaudio_out_create( IUnknown *outer, REFIID iid, void **obj ); HRESULT token_category_create( IUnknown *outer, REFIID iid, void **obj ); HRESULT token_enum_create( IUnknown *outer, REFIID iid, void **obj ); HRESULT token_create( IUnknown *outer, REFIID iid, void **obj ); + +enum type_id +{ + ISpeechObjectToken_tid, + last_tid +}; + +HRESULT get_typeinfo( enum type_id tid, ITypeInfo **typeinfo ); +void release_typelib( void ); diff --git a/dlls/sapi/tests/token.c b/dlls/sapi/tests/token.c index a651eb47c62..ec8a101fea4 100644 --- a/dlls/sapi/tests/token.c +++ b/dlls/sapi/tests/token.c @@ -26,6 +26,8 @@ #include "wine/test.h" +DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0); + static void test_data_key(void) { ISpRegDataKey *data_key; @@ -669,6 +671,8 @@ static void test_object_token(void) ISpObjectTokenCategory *cat; DWORD regid; IUnknown *obj; + DISPPARAMS params; + VARIANT arg, ret; hr = CoCreateInstance( &CLSID_SpObjectToken, NULL, CLSCTX_INPROC_SERVER, &IID_ISpObjectToken, (void **)&token ); @@ -915,6 +919,22 @@ static void test_object_token(void) ok( tempB && !wcscmp( tempB, L"TestToken" ), "got %s\n", wine_dbgstr_w( tempB ) ); SysFreeString( tempB ); + memset( ¶ms, 0, sizeof(params) ); + params.cArgs = 1; + params.cNamedArgs = 0; + params.rgvarg = &arg; + VariantInit( &arg ); + V_VT( &arg ) = VT_I4; + V_I4( &arg ) = 0x409; + VariantInit( &ret ); + hr = ISpeechObjectToken_Invoke( speech_token, DISPID_SOTGetDescription, &IID_NULL, + 0, DISPATCH_METHOD, ¶ms, &ret, NULL, NULL ); + ok( hr == S_OK, "got %08lx\n", hr ); + ok( V_VT( &ret ) == VT_BSTR, "got %#x\n", V_VT( &ret ) ); + ok( V_BSTR( &ret ) && !wcscmp( V_BSTR( &ret ), L"409 - TestToken" ), + "got %s\n", wine_dbgstr_w( V_BSTR( &ret ) ) ); + VariantClear( &ret ); + ISpeechObjectToken_Release( speech_token ); ISpObjectToken_Release( test_class_token ); diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c index e5d87505e34..c8cb806bbae 100644 --- a/dlls/sapi/token.c +++ b/dlls/sapi/token.c @@ -1801,8 +1801,18 @@ static HRESULT WINAPI speech_token_Invoke( ISpeechObjectToken *iface, EXCEPINFO *excepinfo, UINT *argerr ) { - FIXME( "stub\n" ); - return E_NOTIMPL; + ITypeInfo *ti; + HRESULT hr; + + TRACE( "(%p)->(%ld %s %#lx %#x %p %p %p %p)\n", iface, dispid, + debugstr_guid( iid ), lcid, flags, params, result, excepinfo, argerr ); + + if (FAILED(hr = get_typeinfo( ISpeechObjectToken_tid, &ti ))) + return hr; + hr = ITypeInfo_Invoke( ti, iface, dispid, flags, params, result, excepinfo, argerr ); + ITypeInfo_Release( ti ); + + return hr; } static HRESULT WINAPI speech_token_get_Id( ISpeechObjectToken *iface, From 0e3859456102a2bc87a90b398f3b2c15430d82d5 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Sun, 11 Feb 2024 14:21:50 -0500 Subject: [PATCH 153/413] sapi: Implement ISpeechObjectToken::GetIDsOfNames. (cherry picked from commit 59a7ee1a2cf8b0b399e3c4201706197b7b035224) CW-Bug-Id: #22520 --- dlls/sapi/tests/token.c | 7 +++++++ dlls/sapi/token.c | 14 ++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/dlls/sapi/tests/token.c b/dlls/sapi/tests/token.c index ec8a101fea4..6265cde2bc3 100644 --- a/dlls/sapi/tests/token.c +++ b/dlls/sapi/tests/token.c @@ -660,6 +660,7 @@ static IClassFactory test_class_cf = { &ClassFactoryVtbl }; static void test_object_token(void) { static const WCHAR test_token_id[] = L"HKEY_LOCAL_MACHINE\\Software\\Winetest\\sapi\\TestToken"; + static const WCHAR *get_description = L"GetDescription"; ISpObjectToken *token; IDispatch *disp; @@ -671,6 +672,7 @@ static void test_object_token(void) ISpObjectTokenCategory *cat; DWORD regid; IUnknown *obj; + DISPID dispid; DISPPARAMS params; VARIANT arg, ret; @@ -919,6 +921,11 @@ static void test_object_token(void) ok( tempB && !wcscmp( tempB, L"TestToken" ), "got %s\n", wine_dbgstr_w( tempB ) ); SysFreeString( tempB ); + dispid = 0xdeadbeef; + hr = ISpeechObjectToken_GetIDsOfNames( speech_token, &IID_NULL, (WCHAR **)&get_description, 1, 0x409, &dispid ); + ok( hr == S_OK, "got %08lx\n", hr ); + ok( dispid == DISPID_SOTGetDescription, "got %08lx\n", dispid ); + memset( ¶ms, 0, sizeof(params) ); params.cArgs = 1; params.cNamedArgs = 0; diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c index c8cb806bbae..888da86d7a4 100644 --- a/dlls/sapi/token.c +++ b/dlls/sapi/token.c @@ -1787,8 +1787,18 @@ static HRESULT WINAPI speech_token_GetIDsOfNames( ISpeechObjectToken *iface, LCID lcid, DISPID *dispids ) { - FIXME( "stub\n" ); - return E_NOTIMPL; + ITypeInfo *ti; + HRESULT hr; + + TRACE( "(%p)->(%s %p %u %#lx %p)\n", + iface, debugstr_guid( iid ), names, count, lcid, dispids ); + + if (FAILED(hr = get_typeinfo( ISpeechObjectToken_tid, &ti ))) + return hr; + hr = ITypeInfo_GetIDsOfNames( ti, names, count, dispids ); + ITypeInfo_Release( ti ); + + return hr; } static HRESULT WINAPI speech_token_Invoke( ISpeechObjectToken *iface, From 865fa534383a902a82a5bbf723c0ef725b3aa4cc Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Tue, 6 Feb 2024 16:37:07 -0500 Subject: [PATCH 154/413] sapi: Implement ISpeechObjectTokens::get_Count. (cherry picked from commit 261d7c3de4af85f7b2269f5b0e2809f11a102894) CW-Bug-Id: #22520 --- dlls/sapi/tests/token.c | 5 +++++ dlls/sapi/token.c | 7 +++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/dlls/sapi/tests/token.c b/dlls/sapi/tests/token.c index 6265cde2bc3..a68a0b7cd46 100644 --- a/dlls/sapi/tests/token.c +++ b/dlls/sapi/tests/token.c @@ -362,6 +362,11 @@ static void test_token_enum(void) hr = ISpObjectTokenEnumBuilder_AddTokens( token_enum, 3, tokens ); ok( hr == S_OK, "got %08lx\n", hr ); + count = 0xdeadbeef; + hr = ISpeechObjectTokens_get_Count( speech_tokens, (LONG *)&count ); + ok( hr == S_OK, "got %08lx\n", hr ); + ok( count == 3, "got %lu\n", count ); + hr = ISpeechObjectTokens_get__NewEnum( speech_tokens, &unk ); ok( hr == S_OK, "got %08lx\n", hr ); hr = IUnknown_QueryInterface( unk, &IID_IEnumVARIANT, (void **)&enumvar ); diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c index 888da86d7a4..2cfc34304c9 100644 --- a/dlls/sapi/token.c +++ b/dlls/sapi/token.c @@ -1313,8 +1313,11 @@ static HRESULT WINAPI speech_tokens_Invoke( ISpeechObjectTokens *iface, static HRESULT WINAPI speech_tokens_get_Count( ISpeechObjectTokens *iface, LONG *count ) { - FIXME( "stub\n" ); - return E_NOTIMPL; + struct token_enum *This = impl_from_ISpeechObjectTokens( iface ); + + TRACE( "(%p)->(%p)\n", This, count ); + + return ISpObjectTokenEnumBuilder_GetCount( &This->ISpObjectTokenEnumBuilder_iface, (ULONG *)count ); } static HRESULT WINAPI speech_tokens_Item( ISpeechObjectTokens *iface, From ca653c002f0a52f6b5ea495a01feafa3fb5fdacd Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Tue, 6 Feb 2024 17:17:02 -0500 Subject: [PATCH 155/413] sapi: Implement IEnumVARIANT::Next for ISpeechObjectTokens. (cherry picked from commit 6f2a0c412fb5d09cf40b13e773529f4b9acd9ad3) CW-Bug-Id: #22520 --- dlls/sapi/tests/token.c | 28 ++++++++++++++++++++++++++++ dlls/sapi/token.c | 37 +++++++++++++++++++++++++++++++++++-- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/dlls/sapi/tests/token.c b/dlls/sapi/tests/token.c index a68a0b7cd46..89cbb5150a4 100644 --- a/dlls/sapi/tests/token.c +++ b/dlls/sapi/tests/token.c @@ -256,6 +256,7 @@ static void test_token_enum(void) ISpObjectToken *out_tokens[5]; WCHAR token_id[MAX_PATH]; ULONG count; + VARIANT vars[3]; int i; hr = CoCreateInstance( &CLSID_SpObjectTokenEnum, NULL, CLSCTX_INPROC_SERVER, @@ -372,6 +373,33 @@ static void test_token_enum(void) hr = IUnknown_QueryInterface( unk, &IID_IEnumVARIANT, (void **)&enumvar ); ok( hr == S_OK, "got %08lx\n", hr ); IUnknown_Release( unk ); + + V_VT( &vars[0] ) = VT_ILLEGAL; + V_DISPATCH( &vars[0] ) = (IDispatch *)0xdeadbeef; + hr = IEnumVARIANT_Next( enumvar, 1, vars, NULL ); + ok( hr == S_OK, "got %08lx\n", hr ); + ok( V_VT( &vars[0] ) == VT_DISPATCH, "got %#x\n", V_VT( &vars[0] ) ); + ok( V_DISPATCH( &vars[0] ) != (IDispatch *)0xdeadbeef && V_DISPATCH( &vars[0] ) != NULL, + "got %p\n", V_DISPATCH( &vars[0] ) ); + VariantClear( &vars[0] ); + + for ( i = 0; i < 3; i++ ) { + V_VT( &vars[i] ) = VT_ILLEGAL; + V_DISPATCH( &vars[i] ) = (IDispatch *)0xdeadbeef; + } + count = 0xdeadbeef; + + hr = IEnumVARIANT_Next( enumvar, 3, vars, &count ); + ok( hr == S_FALSE, "got %08lx\n", hr ); + ok( count == 2, "got %lu\n", count ); + for ( i = 0; i < 2; i++ ) { + ok( V_VT( &vars[i] ) == VT_DISPATCH, "got %#x\n", V_VT( &vars[i] ) ); + ok( V_DISPATCH( &vars[i] ) != (IDispatch *)0xdeadbeef && V_DISPATCH( &vars[i] ) != NULL, + "got %p\n", V_DISPATCH( &vars[i] ) ); + VariantClear( &vars[i] ); + } + ok( V_VT( &vars[2] ) == VT_ILLEGAL, "got %#x\n", V_VT( &vars[2] ) ); + IEnumVARIANT_Release( enumvar ); ISpeechObjectTokens_Release( speech_tokens ); diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c index 2cfc34304c9..b7f78fe98bd 100644 --- a/dlls/sapi/token.c +++ b/dlls/sapi/token.c @@ -1206,8 +1206,41 @@ static ULONG WINAPI enum_var_Release( IEnumVARIANT *iface ) static HRESULT WINAPI enum_var_Next( IEnumVARIANT *iface, ULONG count, VARIANT *vars, ULONG *fetched ) { - FIXME( "stub\n" ); - return E_NOTIMPL; + struct enum_var *This = impl_from_IEnumVARIANT( iface ); + ULONG i, total; + HRESULT hr; + + TRACE( "(%p)->(%lu %p %p)\n", This, count, vars, fetched ); + + if (fetched) *fetched = 0; + + if (FAILED(hr = ISpObjectTokenEnumBuilder_GetCount( This->token_enum, &total ))) + return hr; + + for ( i = 0; i < count && This->index < total; i++, This->index++ ) + { + ISpObjectToken *token; + IDispatch *disp; + + if (FAILED(hr = ISpObjectTokenEnumBuilder_Item( This->token_enum, This->index, &token ))) + goto fail; + + hr = ISpObjectToken_QueryInterface( token, &IID_IDispatch, (void **)&disp ); + ISpObjectToken_Release( token ); + if (FAILED(hr)) goto fail; + + VariantInit( &vars[i] ); + V_VT( &vars[i] ) = VT_DISPATCH; + V_DISPATCH( &vars[i] ) = disp; + } + + if (fetched) *fetched = i; + return i == count ? S_OK : S_FALSE; + +fail: + while (i--) + VariantClear( &vars[i] ); + return hr; } static HRESULT WINAPI enum_var_Skip( IEnumVARIANT *iface, ULONG count ) From c138fa70513491aef72d3d3c8c79149fe7158407 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Sun, 11 Feb 2024 00:29:46 -0500 Subject: [PATCH 156/413] sapi: Implement ISpeechObjectTokens::Invoke. (cherry picked from commit 6e8d4508048941015e62c42372a584d9e0dd4931) CW-Bug-Id: #22520 --- dlls/sapi/dispatch.c | 1 + dlls/sapi/sapi_private.h | 1 + dlls/sapi/tests/token.c | 11 ++++++++++- dlls/sapi/token.c | 14 ++++++++++++-- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/dlls/sapi/dispatch.c b/dlls/sapi/dispatch.c index df0c6c9bf9c..731ae9ae48f 100644 --- a/dlls/sapi/dispatch.c +++ b/dlls/sapi/dispatch.c @@ -35,6 +35,7 @@ static ITypeInfo *typeinfos[last_tid]; static REFIID tid_id[] = { &IID_ISpeechObjectToken, + &IID_ISpeechObjectTokens, }; HRESULT get_typeinfo(enum type_id tid, ITypeInfo **ret) diff --git a/dlls/sapi/sapi_private.h b/dlls/sapi/sapi_private.h index 9a3c92cc4dc..55e1460ad38 100644 --- a/dlls/sapi/sapi_private.h +++ b/dlls/sapi/sapi_private.h @@ -56,6 +56,7 @@ HRESULT token_create( IUnknown *outer, REFIID iid, void **obj ); enum type_id { ISpeechObjectToken_tid, + ISpeechObjectTokens_tid, last_tid }; diff --git a/dlls/sapi/tests/token.c b/dlls/sapi/tests/token.c index 89cbb5150a4..6d2c40ea154 100644 --- a/dlls/sapi/tests/token.c +++ b/dlls/sapi/tests/token.c @@ -256,7 +256,8 @@ static void test_token_enum(void) ISpObjectToken *out_tokens[5]; WCHAR token_id[MAX_PATH]; ULONG count; - VARIANT vars[3]; + VARIANT vars[3], ret; + DISPPARAMS params; int i; hr = CoCreateInstance( &CLSID_SpObjectTokenEnum, NULL, CLSCTX_INPROC_SERVER, @@ -402,6 +403,14 @@ static void test_token_enum(void) IEnumVARIANT_Release( enumvar ); + memset( ¶ms, 0, sizeof(params) ); + VariantInit( &ret ); + hr = ISpeechObjectTokens_Invoke( speech_tokens, DISPID_SOTsCount, &IID_NULL, 0, + DISPATCH_PROPERTYGET, ¶ms, &ret, NULL, NULL ); + ok( hr == S_OK, "got %08lx\n", hr ); + ok( V_VT( &ret ) == VT_I4, "got %#x\n", V_VT( &ret ) ); + ok( V_I4( &ret ) == 3, "got %ld\n", V_I4( &ret ) ); + ISpeechObjectTokens_Release( speech_tokens ); ISpObjectTokenEnumBuilder_Release( token_enum ); diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c index b7f78fe98bd..ad8163c9567 100644 --- a/dlls/sapi/token.c +++ b/dlls/sapi/token.c @@ -1339,8 +1339,18 @@ static HRESULT WINAPI speech_tokens_Invoke( ISpeechObjectTokens *iface, EXCEPINFO *excepinfo, UINT *argerr ) { - FIXME( "stub\n" ); - return E_NOTIMPL; + ITypeInfo *ti; + HRESULT hr; + + TRACE( "(%p)->(%ld %s %#lx %#x %p %p %p %p)\n", iface, dispid, + debugstr_guid( iid ), lcid, flags, params, result, excepinfo, argerr ); + + if (FAILED(hr = get_typeinfo( ISpeechObjectTokens_tid, &ti ))) + return hr; + hr = ITypeInfo_Invoke( ti, iface, dispid, flags, params, result, excepinfo, argerr ); + ITypeInfo_Release( ti ); + + return hr; } static HRESULT WINAPI speech_tokens_get_Count( ISpeechObjectTokens *iface, From 1127032bc89c27f8389c65d384c40b64b2b5678a Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 14 Feb 2024 20:07:41 -0500 Subject: [PATCH 157/413] sapi: Free typelib on DLL detach. (cherry picked from commit afac7d7e3ffc67c3575512634fd757f81ce839b8) --- dlls/sapi/main.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/dlls/sapi/main.c b/dlls/sapi/main.c index 108db7d13d8..8adcc7449fb 100644 --- a/dlls/sapi/main.c +++ b/dlls/sapi/main.c @@ -145,3 +145,21 @@ HRESULT WINAPI DllGetClassObject( REFCLSID clsid, REFIID iid, void **obj ) return IClassFactory_QueryInterface( cf, iid, obj ); } + +/****************************************************************** + * DllMain + */ +BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, void *reserved ) +{ + switch (reason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls( hinst ); + break; + case DLL_PROCESS_DETACH: + if (reserved) break; + release_typelib(); + break; + } + return TRUE; +} From 33d3de91393d4443bb87def2cf8bd0de69075d49 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 16 Feb 2024 14:28:42 -0500 Subject: [PATCH 158/413] sapi: Implement ISpeechVoice::Speak. (cherry picked from commit 5243f2e82cace7234a66e2036146a32f7aa62d80) CW-Bug-Id: #22520 --- dlls/sapi/tests/tts.c | 9 +++++++++ dlls/sapi/tts.c | 6 ++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 38ec1d31bfc..87711eac61a 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -431,6 +431,7 @@ static void test_spvoice(void) ULONG stream_num; DWORD regid; DWORD start, duration; + ISpeechVoice *speech_voice; HRESULT hr; if (waveOutGetNumDevs() == 0) { @@ -681,6 +682,14 @@ static void test_spvoice(void) ok(hr == S_OK, "got %#lx.\n", hr); ok(duration < 300, "took %lu ms.\n", duration); + hr = ISpVoice_QueryInterface(voice, &IID_ISpeechVoice, (void **)&speech_voice); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpeechVoice_Speak(speech_voice, NULL, SVSFPurgeBeforeSpeak, NULL); + ok(hr == S_OK, "got %#lx.\n", hr); + + ISpeechVoice_Release(speech_voice); + done: reset_engine_params(&test_engine); ISpVoice_Release(voice); diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index c2e36fc274d..e719a544a01 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -357,9 +357,11 @@ static HRESULT WINAPI speech_voice_get_SynchronousSpeakTimeout(ISpeechVoice *ifa static HRESULT WINAPI speech_voice_Speak(ISpeechVoice *iface, BSTR text, SpeechVoiceSpeakFlags flags, LONG *number) { - FIXME("(%p, %s, %#x, %p): stub.\n", iface, debugstr_w(text), flags, number); + struct speech_voice *This = impl_from_ISpeechVoice(iface); - return E_NOTIMPL; + TRACE("(%p, %s, %#x, %p).\n", iface, debugstr_w(text), flags, number); + + return ISpVoice_Speak(&This->ISpVoice_iface, text, flags, (ULONG *)number); } static HRESULT WINAPI speech_voice_SpeakStream(ISpeechVoice *iface, ISpeechBaseStream *stream, From 47133d6ca03505670316a69c5e614ce83d0cac11 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 16 Feb 2024 17:08:18 -0500 Subject: [PATCH 159/413] sapi: Handle zero-length attributes correctly in ISpObjectTokenCategory::EnumTokens. (cherry picked from commit 4bbfd838983505ce4120ebfe94899049b071fbb8) CW-Bug-Id: #22520 --- dlls/sapi/tests/token.c | 10 ++++++++++ dlls/sapi/token.c | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/dlls/sapi/tests/token.c b/dlls/sapi/tests/token.c index 6d2c40ea154..c7067718c45 100644 --- a/dlls/sapi/tests/token.c +++ b/dlls/sapi/tests/token.c @@ -205,6 +205,16 @@ static void test_token_category(void) IEnumSpObjectTokens_Release( enum_tokens ); + hr = ISpObjectTokenCategory_EnumTokens( cat, L"", NULL, &enum_tokens ); + ok( hr == S_OK, "got %08lx\n", hr ); + + count = 0xdeadbeef; + hr = IEnumSpObjectTokens_GetCount( enum_tokens, &count ); + ok( hr == S_OK, "got %08lx\n", hr ); + ok( count == 5, "got %lu\n", count ); + + IEnumSpObjectTokens_Release( enum_tokens ); + hr = ISpObjectTokenCategory_EnumTokens( cat, L"Language=409", NULL, &enum_tokens ); ok( hr == S_OK, "got %08lx\n", hr ); diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c index ad8163c9567..65d35dc96ff 100644 --- a/dlls/sapi/token.c +++ b/dlls/sapi/token.c @@ -947,7 +947,7 @@ static HRESULT score_attributes( ISpObjectToken *token, const WCHAR *attrs, unsigned int i, j; HRESULT hr; - if (!attrs) + if (!attrs || !*attrs) { *score = 1; return S_OK; From 7f29a7288e3a935ac5ac90d35f5022afc7154007 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 15 Feb 2024 17:13:55 -0500 Subject: [PATCH 160/413] sapi: Introduce create_token_category helper in tts. (cherry picked from commit 0f8b59a245bc85e45c0fc8987d6f42794165c4d3) CW-Bug-Id: #22520 --- dlls/sapi/tts.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index e719a544a01..381558659b8 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -82,6 +82,15 @@ static inline struct tts_engine_site *impl_from_ISpTTSEngineSite(ISpTTSEngineSit return CONTAINING_RECORD(iface, struct tts_engine_site, ISpTTSEngineSite_iface); } +static HRESULT create_token_category(const WCHAR *cat_id, ISpObjectTokenCategory **cat) +{ + HRESULT hr; + if (FAILED(hr = CoCreateInstance(&CLSID_SpObjectTokenCategory, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpObjectTokenCategory, (void **)cat))) + return hr; + return ISpObjectTokenCategory_SetId(*cat, cat_id, FALSE); +} + static HRESULT create_default_token(const WCHAR *cat_id, ISpObjectToken **token) { ISpObjectTokenCategory *cat; @@ -90,17 +99,13 @@ static HRESULT create_default_token(const WCHAR *cat_id, ISpObjectToken **token) TRACE("(%s, %p).\n", debugstr_w(cat_id), token); - if (FAILED(hr = CoCreateInstance(&CLSID_SpObjectTokenCategory, NULL, CLSCTX_INPROC_SERVER, - &IID_ISpObjectTokenCategory, (void **)&cat))) + if (FAILED(hr = create_token_category(cat_id, &cat))) return hr; - if (FAILED(hr = ISpObjectTokenCategory_SetId(cat, cat_id, FALSE)) || - FAILED(hr = ISpObjectTokenCategory_GetDefaultTokenId(cat, &default_token_id))) - { - ISpObjectTokenCategory_Release(cat); - return hr; - } + hr = ISpObjectTokenCategory_GetDefaultTokenId(cat, &default_token_id); ISpObjectTokenCategory_Release(cat); + if (FAILED(hr)) + return hr; if (FAILED(hr = CoCreateInstance(&CLSID_SpObjectToken, NULL, CLSCTX_INPROC_SERVER, &IID_ISpObjectToken, (void **)token))) From 391484ce2ec5992a386be617d4cc6b803ae3380e Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 16 Feb 2024 15:21:28 -0500 Subject: [PATCH 161/413] sapi: Implement ISpeechVoice::GetVoices. (cherry picked from commit 62aec0318b958655a86d25a8baad8b37525fa564) CW-Bug-Id: #22520 --- dlls/sapi/tests/tts.c | 31 +++++++++++++++++++++++++++++-- dlls/sapi/tts.c | 21 +++++++++++++++++++-- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 87711eac61a..8f88d74b9ef 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -416,7 +416,7 @@ static IClassFactory test_engine_cf = { &ClassFactoryVtbl }; static void test_spvoice(void) { - static const WCHAR test_token_id[] = L"HKEY_LOCAL_MACHINE\\Software\\Wine\\Winetest\\sapi\\tts\\TestEngine"; + static const WCHAR test_token_id[] = L"HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Speech\\Voices\\Tokens\\WinetestVoice"; static const WCHAR test_text[] = L"Hello! This is a test sentence."; ISpVoice *voice; @@ -432,6 +432,9 @@ static void test_spvoice(void) DWORD regid; DWORD start, duration; ISpeechVoice *speech_voice; + ISpeechObjectTokens *speech_tokens; + LONG count; + BSTR req = NULL, opt = NULL; HRESULT hr; if (waveOutGetNumDevs() == 0) { @@ -439,6 +442,8 @@ static void test_spvoice(void) return; } + RegDeleteTreeA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Speech\\Voices\\WinetestVoice"); + check_apttype(); ok(test_apt_data.type == APTTYPE_UNITIALIZED, "got apt type %d.\n", test_apt_data.type); @@ -588,6 +593,7 @@ static void test_spvoice(void) hr = ISpObjectToken_CreateKey(token, L"Attributes", &attrs_key); ok(hr == S_OK, "got %#lx.\n", hr); ISpDataKey_SetStringValue(attrs_key, L"Language", L"409"); + ISpDataKey_SetStringValue(attrs_key, L"Vendor", L"Winetest"); ISpDataKey_Release(attrs_key); hr = ISpVoice_SetVoice(voice, token); @@ -685,6 +691,25 @@ static void test_spvoice(void) hr = ISpVoice_QueryInterface(voice, &IID_ISpeechVoice, (void **)&speech_voice); ok(hr == S_OK, "got %#lx.\n", hr); + count = -1; + hr = ISpeechVoice_GetVoices(speech_voice, NULL, NULL, &speech_tokens); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = ISpeechObjectTokens_get_Count(speech_tokens, &count); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(count > 0, "got %ld.\n", count); + ISpeechObjectTokens_Release(speech_tokens); + + req = SysAllocString(L"Vendor=Winetest"); + opt = SysAllocString(L"Language=409;Gender=Male"); + + count = 0xdeadbeef; + hr = ISpeechVoice_GetVoices(speech_voice, req, opt, &speech_tokens); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = ISpeechObjectTokens_get_Count(speech_tokens, &count); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(count == 1, "got %ld.\n", count); + ISpeechObjectTokens_Release(speech_tokens); + hr = ISpeechVoice_Speak(speech_voice, NULL, SVSFPurgeBeforeSpeak, NULL); ok(hr == S_OK, "got %#lx.\n", hr); @@ -695,8 +720,10 @@ static void test_spvoice(void) ISpVoice_Release(voice); ISpObjectToken_Release(token); ISpMMSysAudio_Release(audio_out); + SysFreeString(req); + SysFreeString(opt); - RegDeleteTreeA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Winetest\\sapi" ); + RegDeleteTreeA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Speech\\Voices\\WinetestVoice"); } START_TEST(tts) diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index 381558659b8..6fe01373e56 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -401,9 +401,26 @@ static HRESULT WINAPI speech_voice_Skip(ISpeechVoice *iface, const BSTR type, LO static HRESULT WINAPI speech_voice_GetVoices(ISpeechVoice *iface, BSTR required, BSTR optional, ISpeechObjectTokens **tokens) { - FIXME("(%p, %s, %s, %p): stub.\n", iface, debugstr_w(required), debugstr_w(optional), tokens); - return E_NOTIMPL; + ISpObjectTokenCategory *cat; + IEnumSpObjectTokens *token_enum; + HRESULT hr; + + TRACE("(%p, %s, %s, %p).\n", iface, debugstr_w(required), debugstr_w(optional), tokens); + + if (!tokens) return E_POINTER; + + if (FAILED(hr = create_token_category(SPCAT_VOICES, &cat))) + return hr; + + if (SUCCEEDED(hr = ISpObjectTokenCategory_EnumTokens(cat, required, optional, &token_enum))) + { + hr = IEnumSpObjectTokens_QueryInterface(token_enum, &IID_ISpeechObjectTokens, (void **)tokens); + IEnumSpObjectTokens_Release(token_enum); + } + + ISpObjectTokenCategory_Release(cat); + return hr; } static HRESULT WINAPI speech_voice_GetAudioOutputs(ISpeechVoice *iface, BSTR required, BSTR optional, From 182f0b64a98e947b53ecde827843e0ffc5323dca Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 16 Feb 2024 20:04:07 -0500 Subject: [PATCH 162/413] sapi: Implement ISpeechVoice::GetTypeInfoCount. (cherry picked from commit 58087358d1038365c168772eeea9628f72c56df2) CW-Bug-Id: #22520 --- dlls/sapi/tests/tts.c | 6 ++++++ dlls/sapi/tts.c | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 8f88d74b9ef..8f18934fce3 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -435,6 +435,7 @@ static void test_spvoice(void) ISpeechObjectTokens *speech_tokens; LONG count; BSTR req = NULL, opt = NULL; + UINT info_count; HRESULT hr; if (waveOutGetNumDevs() == 0) { @@ -713,6 +714,11 @@ static void test_spvoice(void) hr = ISpeechVoice_Speak(speech_voice, NULL, SVSFPurgeBeforeSpeak, NULL); ok(hr == S_OK, "got %#lx.\n", hr); + info_count = 0xdeadbeef; + hr = ISpeechVoice_GetTypeInfoCount(speech_voice, &info_count); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(info_count == 1, "got %u.\n", info_count); + ISpeechVoice_Release(speech_voice); done: diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index 6fe01373e56..2493a2804f2 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -179,11 +179,11 @@ static ULONG WINAPI speech_voice_Release(ISpeechVoice *iface) return ref; } -static HRESULT WINAPI speech_voice_GetTypeInfoCount(ISpeechVoice *iface, UINT *info) +static HRESULT WINAPI speech_voice_GetTypeInfoCount(ISpeechVoice *iface, UINT *count) { - FIXME("(%p, %p): stub.\n", iface, info); - - return E_NOTIMPL; + TRACE("(%p, %p).\n", iface, count); + *count = 1; + return S_OK; } static HRESULT WINAPI speech_voice_GetTypeInfo(ISpeechVoice *iface, UINT info, LCID lcid, From 64ec697bb0ff3685d8b54ac137a60564c1522ad7 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 16 Feb 2024 19:52:33 -0500 Subject: [PATCH 163/413] sapi: Implement ISpeechVoice::GetTypeInfo. (cherry picked from commit dd083a6195df6b1b8a98940a76e6d2c7ae99b414) CW-Bug-Id: #22520 --- dlls/sapi/dispatch.c | 1 + dlls/sapi/sapi_private.h | 1 + dlls/sapi/tests/tts.c | 13 +++++++++++++ dlls/sapi/tts.c | 8 ++++---- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/dlls/sapi/dispatch.c b/dlls/sapi/dispatch.c index 731ae9ae48f..c1f0e842788 100644 --- a/dlls/sapi/dispatch.c +++ b/dlls/sapi/dispatch.c @@ -36,6 +36,7 @@ static REFIID tid_id[] = { &IID_ISpeechObjectToken, &IID_ISpeechObjectTokens, + &IID_ISpeechVoice, }; HRESULT get_typeinfo(enum type_id tid, ITypeInfo **ret) diff --git a/dlls/sapi/sapi_private.h b/dlls/sapi/sapi_private.h index 55e1460ad38..219436f88c1 100644 --- a/dlls/sapi/sapi_private.h +++ b/dlls/sapi/sapi_private.h @@ -57,6 +57,7 @@ enum type_id { ISpeechObjectToken_tid, ISpeechObjectTokens_tid, + ISpeechVoice_tid, last_tid }; diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 8f18934fce3..111d0a0bc6c 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -436,6 +436,8 @@ static void test_spvoice(void) LONG count; BSTR req = NULL, opt = NULL; UINT info_count; + ITypeInfo *typeinfo; + TYPEATTR *typeattr; HRESULT hr; if (waveOutGetNumDevs() == 0) { @@ -719,6 +721,17 @@ static void test_spvoice(void) ok(hr == S_OK, "got %#lx.\n", hr); ok(info_count == 1, "got %u.\n", info_count); + typeinfo = NULL; + typeattr = NULL; + hr = ISpeechVoice_GetTypeInfo(speech_voice, 0, 0, &typeinfo); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = ITypeInfo_GetTypeAttr(typeinfo, &typeattr); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(typeattr->typekind == TKIND_DISPATCH, "got %u.\n", typeattr->typekind); + ok(IsEqualGUID(&typeattr->guid, &IID_ISpeechVoice), "got %s.\n", wine_dbgstr_guid(&typeattr->guid)); + ITypeInfo_ReleaseTypeAttr(typeinfo, typeattr); + ITypeInfo_Release(typeinfo); + ISpeechVoice_Release(speech_voice); done: diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index 2493a2804f2..8e597252b31 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -186,12 +186,12 @@ static HRESULT WINAPI speech_voice_GetTypeInfoCount(ISpeechVoice *iface, UINT *c return S_OK; } -static HRESULT WINAPI speech_voice_GetTypeInfo(ISpeechVoice *iface, UINT info, LCID lcid, +static HRESULT WINAPI speech_voice_GetTypeInfo(ISpeechVoice *iface, UINT index, LCID lcid, ITypeInfo **type_info) { - FIXME("(%p, %u, %lu, %p): stub.\n", iface, info, lcid, type_info); - - return E_NOTIMPL; + TRACE("(%p, %u, %#lx, %p).\n", iface, index, lcid, type_info); + if (index != 0) return DISP_E_BADINDEX; + return get_typeinfo(ISpeechVoice_tid, type_info); } static HRESULT WINAPI speech_voice_GetIDsOfNames(ISpeechVoice *iface, REFIID riid, LPOLESTR *names, From b737934023b43b32e3634ffaa55614d22fdbc2d1 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 16 Feb 2024 20:04:41 -0500 Subject: [PATCH 164/413] sapi: Implement ISpeechVoice::GetIDsOfNames. (cherry picked from commit 9d044669f3a50fa154cbe4d1d804dee319dbbf09) CW-Bug-Id: #22520 --- dlls/sapi/tests/tts.c | 7 +++++++ dlls/sapi/tts.c | 13 ++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 111d0a0bc6c..7566bd5c5cb 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -418,6 +418,7 @@ static void test_spvoice(void) { static const WCHAR test_token_id[] = L"HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Speech\\Voices\\Tokens\\WinetestVoice"; static const WCHAR test_text[] = L"Hello! This is a test sentence."; + static const WCHAR *get_voices = L"GetVoices"; ISpVoice *voice; IUnknown *dummy; @@ -438,6 +439,7 @@ static void test_spvoice(void) UINT info_count; ITypeInfo *typeinfo; TYPEATTR *typeattr; + DISPID dispid; HRESULT hr; if (waveOutGetNumDevs() == 0) { @@ -732,6 +734,11 @@ static void test_spvoice(void) ITypeInfo_ReleaseTypeAttr(typeinfo, typeattr); ITypeInfo_Release(typeinfo); + dispid = 0xdeadbeef; + hr = ISpeechVoice_GetIDsOfNames(speech_voice, &IID_NULL, (WCHAR **)&get_voices, 1, 0x409, &dispid); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(dispid == DISPID_SVGetVoices, "got %#lx.\n", dispid); + ISpeechVoice_Release(speech_voice); done: diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index 8e597252b31..d08b0c5de22 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -195,11 +195,18 @@ static HRESULT WINAPI speech_voice_GetTypeInfo(ISpeechVoice *iface, UINT index, } static HRESULT WINAPI speech_voice_GetIDsOfNames(ISpeechVoice *iface, REFIID riid, LPOLESTR *names, - UINT count, LCID lcid, DISPID *dispid) + UINT count, LCID lcid, DISPID *dispids) { - FIXME("(%p, %s, %p, %u, %lu, %p): stub.\n", iface, debugstr_guid(riid), names, count, lcid, dispid); + ITypeInfo *typeinfo; + HRESULT hr; - return E_NOTIMPL; + TRACE("(%p, %s, %p, %u, %#lx, %p).\n", iface, debugstr_guid(riid), names, count, lcid, dispids); + + if (FAILED(hr = get_typeinfo(ISpeechVoice_tid, &typeinfo))) + return hr; + hr = ITypeInfo_GetIDsOfNames(typeinfo, names, count, dispids); + ITypeInfo_Release(typeinfo); + return hr; } static HRESULT WINAPI speech_voice_Invoke(ISpeechVoice *iface, DISPID dispid, REFIID riid, LCID lcid, From f8db8cc25cf223f0e34ceec87d0a27816433fec3 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Sat, 17 Feb 2024 00:36:26 -0500 Subject: [PATCH 165/413] sapi: Implement ISpeechVoice::Invoke. (cherry picked from commit c72f0ec0f06f19c5b1b443677702e867ee61f361) CW-Bug-Id: #22520 --- dlls/sapi/tests/tts.c | 25 +++++++++++++++++++++++++ dlls/sapi/tts.c | 11 +++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 7566bd5c5cb..4d565b35294 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -440,6 +440,8 @@ static void test_spvoice(void) ITypeInfo *typeinfo; TYPEATTR *typeattr; DISPID dispid; + DISPPARAMS params; + VARIANT args[2], ret; HRESULT hr; if (waveOutGetNumDevs() == 0) { @@ -739,6 +741,29 @@ static void test_spvoice(void) ok(hr == S_OK, "got %#lx.\n", hr); ok(dispid == DISPID_SVGetVoices, "got %#lx.\n", dispid); + memset(¶ms, 0, sizeof(params)); + params.cArgs = 2; + params.cNamedArgs = 0; + params.rgvarg = args; + VariantInit(&args[0]); + VariantInit(&args[1]); + V_VT(&args[0]) = VT_BSTR; + V_VT(&args[1]) = VT_BSTR; + V_BSTR(&args[0]) = opt; + V_BSTR(&args[1]) = req; + VariantInit(&ret); + hr = ISpeechVoice_Invoke(speech_voice, dispid, &IID_NULL, 0, DISPATCH_METHOD, ¶ms, &ret, NULL, NULL); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(V_VT(&ret) == VT_DISPATCH, "got %#x.\n", V_VT(&ret)); + hr = IDispatch_QueryInterface(V_DISPATCH(&ret), &IID_ISpeechObjectTokens, (void **)&speech_tokens); + ok(hr == S_OK, "got %#lx.\n", hr); + count = -1; + hr = ISpeechObjectTokens_get_Count(speech_tokens, &count); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(count == 1, "got %ld.\n", count); + ISpeechObjectTokens_Release(speech_tokens); + VariantClear(&ret); + ISpeechVoice_Release(speech_voice); done: diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index d08b0c5de22..d22ffd7a0da 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -213,10 +213,17 @@ static HRESULT WINAPI speech_voice_Invoke(ISpeechVoice *iface, DISPID dispid, RE WORD flags, DISPPARAMS *params, VARIANT *result, EXCEPINFO *excepinfo, UINT *argerr) { - FIXME("(%p, %ld, %s, %#lx, %#x, %p, %p, %p, %p): stub.\n", iface, dispid, debugstr_guid(riid), + ITypeInfo *typeinfo; + HRESULT hr; + + TRACE("(%p, %ld, %s, %#lx, %#x, %p, %p, %p, %p).\n", iface, dispid, debugstr_guid(riid), lcid, flags, params, result, excepinfo, argerr); - return E_NOTIMPL; + if (FAILED(hr = get_typeinfo(ISpeechVoice_tid, &typeinfo))) + return hr; + hr = ITypeInfo_Invoke(typeinfo, iface, dispid, flags, params, result, excepinfo, argerr); + ITypeInfo_Release(typeinfo); + return hr; } static HRESULT WINAPI speech_voice_get_Status(ISpeechVoice *iface, ISpeechVoiceStatus **status) From 0e810c7e61ff8d6147c252094f91fadfe29ca92c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 21 Feb 2024 17:25:37 +0100 Subject: [PATCH 166/413] winegstreamer/video_decoder: Reset sample timestamp on stream start. CW-Bug-Id: #23248 --- dlls/winegstreamer/video_decoder.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dlls/winegstreamer/video_decoder.c b/dlls/winegstreamer/video_decoder.c index 95a014a46e0..18ac6c0858f 100644 --- a/dlls/winegstreamer/video_decoder.c +++ b/dlls/winegstreamer/video_decoder.c @@ -600,6 +600,10 @@ static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_ case MFT_MESSAGE_COMMAND_FLUSH: return wg_transform_flush(decoder->wg_transform); + case MFT_MESSAGE_NOTIFY_START_OF_STREAM: + decoder->sample_time = 0; + return S_OK; + default: FIXME("Ignoring message %#x.\n", message); return S_OK; From 5a3c506345230799f1d4951d49671a44e6c68654 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Tue, 20 Feb 2024 10:54:45 +0800 Subject: [PATCH 167/413] dsound/tests: Test that formats with more than two channels require WAVEFORMATEXTENSIBLE. (cherry picked from commit b3ec5bc7ea3138d7fe8b6a1a5fa441ae305ab413) CW-Bug-Id: #19927 --- dlls/dsound/tests/dsound.c | 45 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/dlls/dsound/tests/dsound.c b/dlls/dsound/tests/dsound.c index 9656a458415..c957f3a8a4b 100644 --- a/dlls/dsound/tests/dsound.c +++ b/dlls/dsound/tests/dsound.c @@ -1481,6 +1481,51 @@ static void perform_invalid_fmt_tests(const char *testname, IDirectSound *dso, I fmtex.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; rc = do_invalid_fmt_test(dso, buf, (WAVEFORMATEX*)&fmtex, &got_buf); ok(rc == E_INVALIDARG, "%s: SetFormat: %08lx\n", testname, rc); + + /* The following 4 tests show that formats with more than two channels require WAVEFORMATEXTENSIBLE */ + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 2; + wfx.nSamplesPerSec = 44100; + wfx.wBitsPerSample = 16; + wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + rc = do_invalid_fmt_test(dso, buf, &wfx, &got_buf); + ok(rc == S_OK, "%s: SetFormat: %08lx\n", testname, rc); + IDirectSoundBuffer_Release(got_buf); + + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 4; + wfx.nSamplesPerSec = 44100; + wfx.wBitsPerSample = 16; + wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + rc = do_invalid_fmt_test(dso, buf, &wfx, &got_buf); + todo_wine + ok(rc == (buf ? DSERR_ALLOCATED : DSERR_INVALIDPARAM), "%s: SetFormat: %08lx\n", testname, rc); + + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 6; + wfx.nSamplesPerSec = 44100; + wfx.wBitsPerSample = 16; + wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + rc = do_invalid_fmt_test(dso, buf, &wfx, &got_buf); + todo_wine + ok(rc == (buf ? DSERR_ALLOCATED : DSERR_INVALIDPARAM), "%s: SetFormat: %08lx\n", testname, rc); + + fmtex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); + fmtex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + fmtex.Format.nChannels = 6; + fmtex.Format.nSamplesPerSec = 44100; + fmtex.Format.wBitsPerSample = 16; + fmtex.Format.nBlockAlign = fmtex.Format.nChannels * fmtex.Format.wBitsPerSample / 8; + fmtex.Format.nAvgBytesPerSec = fmtex.Format.nSamplesPerSec * fmtex.Format.nBlockAlign; + fmtex.Samples.wValidBitsPerSample = fmtex.Format.wBitsPerSample; + fmtex.dwChannelMask = KSAUDIO_SPEAKER_5POINT1; + fmtex.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + rc = do_invalid_fmt_test(dso, buf, (WAVEFORMATEX *)&fmtex, &got_buf); + ok(rc == S_OK, "%s: SetFormat: %08lx\n", testname, rc); + IDirectSoundBuffer_Release(got_buf); } static HRESULT test_invalid_fmts(LPGUID lpGuid) From dc7f506791492099ab12e75ca56f6e8c1b7c8015 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Tue, 20 Feb 2024 11:04:13 +0800 Subject: [PATCH 168/413] dsound: Reject WAVEFORMATEX formats with more than two channels. Formats with more than two channels require WAVEFORMATEXTENSIBLE according to tests. Fix Viking: Battle for Asgard (211160) audio cracking in its intro video. (cherry picked from commit 7c7b2e8e7e8c0be1a756e6d7c705b68017459e26) CW-Bug-Id: #19927 --- dlls/dsound/dsound.c | 3 +++ dlls/dsound/primary.c | 3 +++ dlls/dsound/tests/dsound.c | 2 -- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c index b1c0c76c367..247d1edcf6b 100644 --- a/dlls/dsound/dsound.c +++ b/dlls/dsound/dsound.c @@ -489,6 +489,9 @@ static HRESULT DirectSoundDevice_CreateSoundBuffer( return DSERR_INVALIDPARAM; } + if (dsbd->lpwfxFormat->nChannels > 2 && dsbd->lpwfxFormat->wFormatTag != WAVE_FORMAT_EXTENSIBLE) + return DSERR_INVALIDPARAM; + if (dsbd->lpwfxFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { WAVEFORMATEXTENSIBLE *pwfxe = (WAVEFORMATEXTENSIBLE*)dsbd->lpwfxFormat; diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c index 8cfcd1d8a28..22cf475a674 100644 --- a/dlls/dsound/primary.c +++ b/dlls/dsound/primary.c @@ -538,6 +538,9 @@ HRESULT primarybuffer_SetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX passe return DSERR_INVALIDPARAM; } + if (passed_fmt->nChannels > 2 && passed_fmt->wFormatTag != WAVE_FORMAT_EXTENSIBLE) + return DSERR_ALLOCATED; + /* **** */ AcquireSRWLockExclusive(&device->buffer_list_lock); EnterCriticalSection(&(device->mixlock)); diff --git a/dlls/dsound/tests/dsound.c b/dlls/dsound/tests/dsound.c index c957f3a8a4b..500bc19f655 100644 --- a/dlls/dsound/tests/dsound.c +++ b/dlls/dsound/tests/dsound.c @@ -1500,7 +1500,6 @@ static void perform_invalid_fmt_tests(const char *testname, IDirectSound *dso, I wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8; wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; rc = do_invalid_fmt_test(dso, buf, &wfx, &got_buf); - todo_wine ok(rc == (buf ? DSERR_ALLOCATED : DSERR_INVALIDPARAM), "%s: SetFormat: %08lx\n", testname, rc); wfx.wFormatTag = WAVE_FORMAT_PCM; @@ -1510,7 +1509,6 @@ static void perform_invalid_fmt_tests(const char *testname, IDirectSound *dso, I wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8; wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; rc = do_invalid_fmt_test(dso, buf, &wfx, &got_buf); - todo_wine ok(rc == (buf ? DSERR_ALLOCATED : DSERR_INVALIDPARAM), "%s: SetFormat: %08lx\n", testname, rc); fmtex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); From bad0bc0502aa0459abba3062c37f84c3918f68a9 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 23 Feb 2024 19:13:16 -0600 Subject: [PATCH 169/413] nsiproxy.sys: Fix ipv6 route table parsing on Linux. CW-Bug-Id: #23460 --- dlls/nsiproxy.sys/ip.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/dlls/nsiproxy.sys/ip.c b/dlls/nsiproxy.sys/ip.c index fe49caa6e57..09f033ac634 100644 --- a/dlls/nsiproxy.sys/ip.c +++ b/dlls/nsiproxy.sys/ip.c @@ -1544,7 +1544,7 @@ struct in6_addr str_to_in6_addr(char *nptr, char **endptr) for (int i = 0; i < sizeof(ret); i++) { - if (!isxdigit( *nptr ) || !isxdigit( *nptr + 1 )) + if (!isxdigit( *nptr ) || !isxdigit( *(nptr + 1) )) { /* invalid hex string */ if (endptr) *endptr = nptr; @@ -1574,7 +1574,7 @@ static NTSTATUS ipv6_forward_enumerate_all( void *key_data, UINT key_size, void #ifdef __linux__ { - char buf[512], *ptr; + char buf[512], *ptr, *end; UINT rtf_flags; FILE *fp; @@ -1582,9 +1582,6 @@ static NTSTATUS ipv6_forward_enumerate_all( void *key_data, UINT key_size, void while ((ptr = fgets( buf, sizeof(buf), fp ))) { - while (!isspace( *ptr )) ptr++; - *ptr++ = '\0'; - entry.prefix = str_to_in6_addr( ptr, &ptr ); entry.prefix_len = strtoul( ptr + 1, &ptr, 16 ); str_to_in6_addr( ptr + 1, &ptr ); /* source network, skip */ @@ -1597,6 +1594,10 @@ static NTSTATUS ipv6_forward_enumerate_all( void *key_data, UINT key_size, void entry.protocol = (rtf_flags & RTF_GATEWAY) ? MIB_IPPROTO_NETMGMT : MIB_IPPROTO_LOCAL; entry.loopback = entry.protocol == MIB_IPPROTO_LOCAL && entry.prefix_len == 32; + while (isspace( *ptr )) ptr++; + end = ptr; + while (*end && !isspace(*end)) ++end; + *end = 0; if (!convert_unix_name_to_luid( ptr, &entry.luid )) continue; if (!convert_luid_to_index( &entry.luid, &entry.if_index )) continue; From 892506aeb3f3802ebe4c883c161e1ea789badc24 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 23 Feb 2024 17:49:43 -0600 Subject: [PATCH 170/413] iphlpapi: Partially fill Ipv4 / Ipv6 metric in GetAdaptersAddresses(). CW-Bug-Id: #23460 --- dlls/iphlpapi/iphlpapi_main.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 9c7582b71fb..207c2ae20d0 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -993,6 +993,7 @@ static DWORD gateway_and_prefix_addresses_alloc( IP_ADAPTER_ADDRESSES *aa, ULONG { struct nsi_ipv4_forward_key *key4; struct nsi_ipv6_forward_key *key6; + struct nsi_ip_forward_rw *rw; IP_ADAPTER_GATEWAY_ADDRESS *gw, **gw_next; IP_ADAPTER_PREFIX *prefix, **prefix_next; DWORD err, count, i, prefix_len, key_size = (family == AF_INET) ? sizeof(*key4) : sizeof(*key6); @@ -1002,11 +1003,14 @@ static DWORD gateway_and_prefix_addresses_alloc( IP_ADAPTER_ADDRESSES *aa, ULONG void *key; err = NsiAllocateAndGetTable( 1, ip_module_id( family ), NSI_IP_FORWARD_TABLE, &key, key_size, - NULL, 0, NULL, 0, NULL, 0, &count, 0 ); + (void **)&rw, sizeof(*rw), NULL, 0, NULL, 0, &count, 0 ); if (err) return err; while (aa) { + if (family == AF_INET) aa->Ipv4Metric = ~0u; + else aa->Ipv6Metric = ~0u; + for (gw_next = &aa->FirstGatewayAddress; *gw_next; gw_next = &(*gw_next)->Next) ; for (prefix_next = &aa->FirstPrefix; *prefix_next; prefix_next = &(*prefix_next)->Next) @@ -1019,6 +1023,12 @@ static DWORD gateway_and_prefix_addresses_alloc( IP_ADAPTER_ADDRESSES *aa, ULONG luid = (family == AF_INET) ? &key4->luid : &key6->luid; if (luid->Value != aa->Luid.Value) continue; + if (rw[i].metric) + { + if (family == AF_INET) aa->Ipv4Metric = min( aa->Ipv4Metric, rw[i].metric ); + else aa->Ipv6Metric = min( aa->Ipv6Metric, rw[i].metric ); + } + if (flags & GAA_FLAG_INCLUDE_GATEWAYS) { memset( &sockaddr, 0, sizeof(sockaddr) ); @@ -1103,7 +1113,7 @@ static DWORD gateway_and_prefix_addresses_alloc( IP_ADAPTER_ADDRESSES *aa, ULONG } err: - NsiFreeTable( key, NULL, NULL, NULL ); + NsiFreeTable( key, rw, NULL, NULL ); return err; } @@ -1269,11 +1279,8 @@ static DWORD adapters_addresses_alloc( ULONG family, ULONG flags, IP_ADAPTER_ADD if (err) goto err; } - if (flags & (GAA_FLAG_INCLUDE_GATEWAYS | GAA_FLAG_INCLUDE_PREFIX)) - { - err = call_families( gateway_and_prefix_addresses_alloc, aa, family, flags ); - if (err) goto err; - } + err = call_families( gateway_and_prefix_addresses_alloc, aa, family, flags ); + if (err) goto err; err = dns_info_alloc( aa, family, flags ); if (err) goto err; From f127395cc1c6dfd3b345de3f5bd4e610bbea9391 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 26 Feb 2024 13:22:41 -0600 Subject: [PATCH 171/413] Revert "win32u: Use font AA flags when querying glyph outline with GGO_METRICS." This reverts commit 7a433892887fb907c94d67ccbb018ccfae08d25f. CW-Bug-Id: #23452 --- dlls/win32u/freetype.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/dlls/win32u/freetype.c b/dlls/win32u/freetype.c index b6f85bf2594..558adb69f9d 100644 --- a/dlls/win32u/freetype.c +++ b/dlls/win32u/freetype.c @@ -3469,7 +3469,6 @@ static UINT freetype_get_glyph_outline( struct gdi_font *font, UINT glyph, UINT FT_Int load_flags; FT_Matrix transform_matrices[3], *matrices = NULL; BOOL vertical_metrics; - UINT effective_format = format; TRACE("%p, %04x, %08x, %p, %08x, %p, %p\n", font, glyph, format, lpgm, buflen, buf, lpmat); @@ -3479,22 +3478,14 @@ static UINT freetype_get_glyph_outline( struct gdi_font *font, UINT glyph, UINT matrices = get_transform_matrices( font, tategaki, lpmat, transform_matrices ); - if ((format & ~GGO_GLYPH_INDEX) == GGO_METRICS) - effective_format = font->aa_flags | (format & GGO_GLYPH_INDEX); vertical_metrics = (tategaki && FT_HAS_VERTICAL(ft_face)); /* there is a freetype bug where vertical metrics are only properly scaled and correct in 2.4.0 or greater */ if (vertical_metrics && FT_SimpleVersion < FT_VERSION_VALUE(2, 4, 0)) vertical_metrics = FALSE; - load_flags = get_load_flags(effective_format, vertical_metrics, !!matrices); + load_flags = get_load_flags(format, vertical_metrics, !!matrices); err = pFT_Load_Glyph(ft_face, glyph, load_flags); - if (err && format != effective_format) - { - WARN("Failed to load glyph %#x, retrying with GGO_METRICS. Error %#x.\n", glyph, err); - load_flags = get_load_flags(effective_format, vertical_metrics, !!matrices); - err = pFT_Load_Glyph(ft_face, glyph, load_flags); - } if (err && !(load_flags & FT_LOAD_NO_HINTING)) { WARN("Failed to load glyph %#x, retrying without hinting. Error %#x.\n", glyph, err); From a93c9e5fa105885798b341dae25d708e50b9be53 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 26 Feb 2024 13:22:43 -0600 Subject: [PATCH 172/413] Revert "win32u: Store effective AA flags in gdi_font." This reverts commit d098d5cf8708edb184f221acb4c19d496b64fa14. CW-Bug-Id: #23452 --- dlls/win32u/font.c | 1 - 1 file changed, 1 deletion(-) diff --git a/dlls/win32u/font.c b/dlls/win32u/font.c index 6094ea0261b..c5cf09f56f7 100644 --- a/dlls/win32u/font.c +++ b/dlls/win32u/font.c @@ -4689,7 +4689,6 @@ static HFONT font_SelectFont( PHYSDEV dev, HFONT hfont, UINT *aa_flags ) *aa_flags = font_smoothing; } *aa_flags = font_funcs->get_aa_flags( font, *aa_flags, antialias_fakes ); - font->aa_flags = *aa_flags; } TRACE( "%p %s %d aa %x\n", hfont, debugstr_w(lf.lfFaceName), (int)lf.lfHeight, *aa_flags ); pthread_mutex_unlock( &font_lock ); From d1786359697e0c72bdf48c7fea590cbc7f6e2e20 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 26 Feb 2024 13:37:03 -0600 Subject: [PATCH 173/413] win32u: Store effective AA flags in font_physdev. CW-Bug-Id: #22992 --- dlls/win32u/font.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/win32u/font.c b/dlls/win32u/font.c index c5cf09f56f7..48e839a47f3 100644 --- a/dlls/win32u/font.c +++ b/dlls/win32u/font.c @@ -55,6 +55,7 @@ struct font_physdev { struct gdi_physdev dev; struct gdi_font *font; + UINT aa_flags; }; static inline struct font_physdev *get_font_dev( PHYSDEV dev ) @@ -4689,6 +4690,7 @@ static HFONT font_SelectFont( PHYSDEV dev, HFONT hfont, UINT *aa_flags ) *aa_flags = font_smoothing; } *aa_flags = font_funcs->get_aa_flags( font, *aa_flags, antialias_fakes ); + physdev->aa_flags = *aa_flags; } TRACE( "%p %s %d aa %x\n", hfont, debugstr_w(lf.lfFaceName), (int)lf.lfHeight, *aa_flags ); pthread_mutex_unlock( &font_lock ); From e9e3dc2f294cf2d9c66d7ce42ffffcd7b4531c74 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 15 Nov 2023 21:30:12 -0600 Subject: [PATCH 174/413] win32u: Use font AA flags when querying glyph outline with GGO_METRICS. CW-Bug-Id: #22992 --- dlls/win32u/font.c | 16 ++++++++-------- dlls/win32u/freetype.c | 13 +++++++++++-- dlls/win32u/ntgdi_private.h | 2 +- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/dlls/win32u/font.c b/dlls/win32u/font.c index 48e839a47f3..08060e0a9b7 100644 --- a/dlls/win32u/font.c +++ b/dlls/win32u/font.c @@ -3851,7 +3851,7 @@ static UINT get_glyph_index_linked( struct gdi_font **font, UINT glyph ) static DWORD get_glyph_outline( struct gdi_font *font, UINT glyph, UINT format, GLYPHMETRICS *gm_ret, ABC *abc_ret, DWORD buflen, void *buf, - const MAT2 *mat ) + const MAT2 *mat, UINT aa_flags ) { GLYPHMETRICS gm; ABC abc; @@ -3885,7 +3885,7 @@ static DWORD get_glyph_outline( struct gdi_font *font, UINT glyph, UINT format, if (format == GGO_METRICS && !mat && get_gdi_font_glyph_metrics( font, index, &gm, &abc )) goto done; - ret = font_funcs->get_glyph_outline( font, index, format, &gm, &abc, buflen, buf, mat, tategaki ); + ret = font_funcs->get_glyph_outline( font, index, format, &gm, &abc, buflen, buf, mat, tategaki, aa_flags ); if (ret == GDI_ERROR) return ret; if (format == GGO_METRICS && !mat) @@ -3934,7 +3934,7 @@ static BOOL font_GetCharABCWidths( PHYSDEV dev, UINT first, UINT count, WCHAR *c for (i = 0; i < count; i++) { c = chars ? chars[i] : first + i; - get_glyph_outline( physdev->font, c, GGO_METRICS, NULL, &buffer[i], 0, NULL, NULL ); + get_glyph_outline( physdev->font, c, GGO_METRICS, NULL, &buffer[i], 0, NULL, NULL, physdev->aa_flags ); } pthread_mutex_unlock( &font_lock ); return TRUE; @@ -3960,7 +3960,7 @@ static BOOL font_GetCharABCWidthsI( PHYSDEV dev, UINT first, UINT count, WORD *g pthread_mutex_lock( &font_lock ); for (c = 0; c < count; c++, buffer++) get_glyph_outline( physdev->font, gi ? gi[c] : first + c, GGO_METRICS | GGO_GLYPH_INDEX, - NULL, buffer, 0, NULL, NULL ); + NULL, buffer, 0, NULL, NULL, physdev->aa_flags ); pthread_mutex_unlock( &font_lock ); return TRUE; } @@ -3987,7 +3987,7 @@ static BOOL font_GetCharWidth( PHYSDEV dev, UINT first, UINT count, const WCHAR for (i = 0; i < count; i++) { c = chars ? chars[i] : i + first; - if (get_glyph_outline( physdev->font, c, GGO_METRICS, NULL, &abc, 0, NULL, NULL ) == GDI_ERROR) + if (get_glyph_outline( physdev->font, c, GGO_METRICS, NULL, &abc, 0, NULL, NULL, physdev->aa_flags ) == GDI_ERROR) buffer[i] = 0; else buffer[i] = abc.abcA + abc.abcB + abc.abcC; @@ -4166,7 +4166,7 @@ static DWORD font_GetGlyphOutline( PHYSDEV dev, UINT glyph, UINT format, return dev->funcs->pGetGlyphOutline( dev, glyph, format, gm, buflen, buf, mat ); } pthread_mutex_lock( &font_lock ); - ret = get_glyph_outline( physdev->font, glyph, format, gm, NULL, buflen, buf, mat ); + ret = get_glyph_outline( physdev->font, glyph, format, gm, NULL, buflen, buf, mat, physdev->aa_flags ); pthread_mutex_unlock( &font_lock ); return ret; } @@ -4346,7 +4346,7 @@ static BOOL font_GetTextExtentExPoint( PHYSDEV dev, const WCHAR *str, INT count, pthread_mutex_lock( &font_lock ); for (i = pos = 0; i < count; i++) { - get_glyph_outline( physdev->font, str[i], GGO_METRICS, NULL, &abc, 0, NULL, NULL ); + get_glyph_outline( physdev->font, str[i], GGO_METRICS, NULL, &abc, 0, NULL, NULL, physdev->aa_flags ); pos += abc.abcA + abc.abcB + abc.abcC; dxs[i] = pos; } @@ -4376,7 +4376,7 @@ static BOOL font_GetTextExtentExPointI( PHYSDEV dev, const WORD *indices, INT co for (i = pos = 0; i < count; i++) { get_glyph_outline( physdev->font, indices[i], GGO_METRICS | GGO_GLYPH_INDEX, - NULL, &abc, 0, NULL, NULL ); + NULL, &abc, 0, NULL, NULL, physdev->aa_flags ); pos += abc.abcA + abc.abcB + abc.abcC; dxs[i] = pos; } diff --git a/dlls/win32u/freetype.c b/dlls/win32u/freetype.c index 558adb69f9d..880b1f8603b 100644 --- a/dlls/win32u/freetype.c +++ b/dlls/win32u/freetype.c @@ -3459,7 +3459,7 @@ static FT_Int get_load_flags( UINT format, BOOL vertical_metrics, BOOL force_no_ */ static UINT freetype_get_glyph_outline( struct gdi_font *font, UINT glyph, UINT format, GLYPHMETRICS *lpgm, ABC *abc, UINT buflen, void *buf, - const MAT2 *lpmat, BOOL tategaki ) + const MAT2 *lpmat, BOOL tategaki, UINT aa_flags ) { struct gdi_font *base_font = font->base_font ? font->base_font : font; FT_Face ft_face = get_ft_face( font ); @@ -3469,6 +3469,7 @@ static UINT freetype_get_glyph_outline( struct gdi_font *font, UINT glyph, UINT FT_Int load_flags; FT_Matrix transform_matrices[3], *matrices = NULL; BOOL vertical_metrics; + UINT effective_format = format; TRACE("%p, %04x, %08x, %p, %08x, %p, %p\n", font, glyph, format, lpgm, buflen, buf, lpmat); @@ -3478,14 +3479,22 @@ static UINT freetype_get_glyph_outline( struct gdi_font *font, UINT glyph, UINT matrices = get_transform_matrices( font, tategaki, lpmat, transform_matrices ); + if (aa_flags && (format & ~GGO_GLYPH_INDEX) == GGO_METRICS) + effective_format = aa_flags | (format & GGO_GLYPH_INDEX); vertical_metrics = (tategaki && FT_HAS_VERTICAL(ft_face)); /* there is a freetype bug where vertical metrics are only properly scaled and correct in 2.4.0 or greater */ if (vertical_metrics && FT_SimpleVersion < FT_VERSION_VALUE(2, 4, 0)) vertical_metrics = FALSE; - load_flags = get_load_flags(format, vertical_metrics, !!matrices); + load_flags = get_load_flags(effective_format, vertical_metrics, !!matrices); err = pFT_Load_Glyph(ft_face, glyph, load_flags); + if (err && format != effective_format) + { + WARN("Failed to load glyph %#x, retrying with GGO_METRICS. Error %#x.\n", glyph, err); + load_flags = get_load_flags(effective_format, vertical_metrics, !!matrices); + err = pFT_Load_Glyph(ft_face, glyph, load_flags); + } if (err && !(load_flags & FT_LOAD_NO_HINTING)) { WARN("Failed to load glyph %#x, retrying without hinting. Error %#x.\n", glyph, err); diff --git a/dlls/win32u/ntgdi_private.h b/dlls/win32u/ntgdi_private.h index 5c4f8279c87..eb82df750ba 100644 --- a/dlls/win32u/ntgdi_private.h +++ b/dlls/win32u/ntgdi_private.h @@ -327,7 +327,7 @@ struct font_backend_funcs UINT (*get_default_glyph)( struct gdi_font *gdi_font ); UINT (*get_glyph_outline)( struct gdi_font *font, UINT glyph, UINT format, GLYPHMETRICS *gm, ABC *abc, UINT buflen, void *buf, - const MAT2 *mat, BOOL tategaki ); + const MAT2 *mat, BOOL tategaki, UINT aa_flags ); UINT (*get_unicode_ranges)( struct gdi_font *font, GLYPHSET *gs ); BOOL (*get_char_width_info)( struct gdi_font *font, struct char_width_info *info ); BOOL (*set_outline_text_metrics)( struct gdi_font *font ); From 2760c85d3b99edc1e938e74937590615774d1bd7 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 26 Feb 2024 19:33:28 -0600 Subject: [PATCH 175/413] faudio: Take lock before accessing list in LinkedList_RemoveEntry(). CW-Bug-Id: #23472 --- libs/faudio/src/FAudio_internal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/faudio/src/FAudio_internal.c b/libs/faudio/src/FAudio_internal.c index f0d46cfa4b6..7ce57d52a16 100644 --- a/libs/faudio/src/FAudio_internal.c +++ b/libs/faudio/src/FAudio_internal.c @@ -212,9 +212,9 @@ void LinkedList_RemoveEntry( FAudioFreeFunc pFree ) { LinkedList *latest, *prev; + FAudio_PlatformLockMutex(lock); latest = *start; prev = latest; - FAudio_PlatformLockMutex(lock); while (latest != NULL) { if (latest->entry == toRemove) From fde7629b3d15465cd57a8b4df0ba209a6167765d Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 26 Feb 2024 19:34:12 -0600 Subject: [PATCH 176/413] faudio: Don't destroy voice when it is output to other voices. CW-Bug-Id: #23472 --- libs/faudio/include/FAudio.h | 2 +- libs/faudio/src/FAudio.c | 61 +++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/libs/faudio/include/FAudio.h b/libs/faudio/include/FAudio.h index 1a873ca2064..1994cd7543b 100644 --- a/libs/faudio/include/FAudio.h +++ b/libs/faudio/include/FAudio.h @@ -1062,7 +1062,7 @@ FAUDIOAPI void FAudioVoice_GetOutputMatrix( ); /* Removes this voice from the audio graph and frees memory. */ -FAUDIOAPI void FAudioVoice_DestroyVoice(FAudioVoice *voice); +FAUDIOAPI uint32_t FAudioVoice_DestroyVoice(FAudioVoice *voice); /* FAudioSourceVoice Interface */ diff --git a/libs/faudio/src/FAudio.c b/libs/faudio/src/FAudio.c index 9366ad58208..2bb1c1fd40d 100644 --- a/libs/faudio/src/FAudio.c +++ b/libs/faudio/src/FAudio.c @@ -2265,11 +2265,69 @@ void FAudioVoice_GetOutputMatrix( LOG_API_EXIT(voice->audio) } -void FAudioVoice_DestroyVoice(FAudioVoice *voice) +static uint32_t check_for_sends_to_voice(FAudioVoice *voice) +{ + FAudio *audio = voice->audio; + uint32_t ret = 1; + FAudioSourceVoice *source; + FAudioSubmixVoice *submix; + LinkedList *list; + uint32_t i; + + FAudio_PlatformLockMutex(audio->sourceLock); + list = audio->sources; + while (list != NULL) + { + source = (FAudioSourceVoice*) list->entry; + for (i = 0; i < source->sends.SendCount; i += 1) + if (source->sends.pSends[i].pOutputVoice == voice) + { + ret = 0; + break; + } + if (!ret) + break; + list = list->next; + } + FAudio_PlatformUnlockMutex(audio->sourceLock); + + if (!ret) + return ret; + + FAudio_PlatformLockMutex(audio->submixLock); + list = audio->submixes; + while (list != NULL) + { + submix = (FAudioSubmixVoice*) list->entry; + for (i = 0; i < submix->sends.SendCount; i += 1) + if (submix->sends.pSends[i].pOutputVoice == voice) + { + ret = 0; + break; + } + list = list->next; + } + FAudio_PlatformUnlockMutex(audio->submixLock); + + return ret; +} + +uint32_t FAudioVoice_DestroyVoice(FAudioVoice *voice) { uint32_t i; LOG_API_ENTER(voice->audio) + if (!check_for_sends_to_voice(voice)) + { + LOG_ERROR( + voice->audio, + "Voice %p is an output for other voice(s)", + voice + ) + LOG_API_EXIT(voice->audio) + return 0; + } + /* TODO: Check for dependencies and remove from audio graph first! */ FAudio_OPERATIONSET_ClearAllForVoice(voice); @@ -2443,6 +2501,7 @@ void FAudioVoice_DestroyVoice(FAudioVoice *voice) LOG_API_EXIT(voice->audio) FAudio_Release(voice->audio); voice->audio->pFree(voice); + return 1; } /* FAudioSourceVoice Interface */ From 7ee1ccaeb34c59086bfedd8cdcb9335ed734eb22 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 26 Feb 2024 19:34:40 -0600 Subject: [PATCH 177/413] xaudio2: Check FAudioVoice_DestroyVoice result in destroy_voice(). CW-Bug-Id: #23472 --- dlls/xaudio2_7/xaudio_dll.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dlls/xaudio2_7/xaudio_dll.c b/dlls/xaudio2_7/xaudio_dll.c index b821e9667b7..b1ef81b632e 100644 --- a/dlls/xaudio2_7/xaudio_dll.c +++ b/dlls/xaudio2_7/xaudio_dll.c @@ -499,7 +499,11 @@ static const FAudioEngineCallback FAudioEngineCallback_Vtbl = { static inline void destroy_voice(XA2VoiceImpl *This) { - FAudioVoice_DestroyVoice(This->faudio_voice); + if (!FAudioVoice_DestroyVoice(This->faudio_voice)) + { + ERR("Destroying voice %p failed.\n", This); + return; + } free_effect_chain(This->effect_chain); This->effect_chain = NULL; This->in_use = FALSE; From 718a43b67adaed0bd8ca72b8431d51f2f30c6a84 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 26 Feb 2024 19:38:10 -0600 Subject: [PATCH 178/413] xaudio2/tests: Test destroying a voice which is an output to another one. CW-Bug-Id: #23472 --- dlls/xaudio2_7/tests/xaudio2.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/dlls/xaudio2_7/tests/xaudio2.c b/dlls/xaudio2_7/tests/xaudio2.c index 2b3102d21fe..70d9ec78aee 100644 --- a/dlls/xaudio2_7/tests/xaudio2.c +++ b/dlls/xaudio2_7/tests/xaudio2.c @@ -837,7 +837,9 @@ static void test_submix(IXAudio2 *xa) HRESULT hr; IXAudio2MasteringVoice *master; XAUDIO2_VOICE_DETAILS details; - IXAudio2SubmixVoice *sub; + IXAudio2SubmixVoice *sub, *sub2; + XAUDIO2_SEND_DESCRIPTOR send_desc = { 0 }; + XAUDIO2_VOICE_SENDS sends = {1, &send_desc }; IXAudio2_StopEngine(xa); @@ -855,6 +857,26 @@ static void test_submix(IXAudio2 *xa) ok(details.InputChannels == 2, "Got wrong channel count: 0x%x\n", details.InputChannels); ok(details.InputSampleRate == 44100, "Got wrong sample rate: 0x%x\n", details.InputSampleRate); + hr = IXAudio2_CreateSubmixVoice(xa, &sub2, 2, 44100, 0, 0, NULL, NULL); + ok(hr == S_OK, "CreateSubmixVoice failed: %08lx\n", hr); + + send_desc.pOutputVoice = (IXAudio2Voice *)sub2; + hr = IXAudio2SubmixVoice_SetOutputVoices(sub, &sends); + ok(hr == S_OK, "CreateSubmixVoice failed: %08lx\n", hr); + + IXAudio2SubmixVoice_DestroyVoice(sub2); + IXAudio2SubmixVoice_GetVoiceDetails(sub2, &details); + + sends.SendCount = 0; + hr = IXAudio2SubmixVoice_SetOutputVoices(sub, &sends); + ok(hr == S_OK, "CreateSubmixVoice failed: %08lx\n", hr); + + IXAudio2SubmixVoice_DestroyVoice(sub2); + if (0) + { + IXAudio2SubmixVoice_GetVoiceDetails(sub2, &details); + } + IXAudio2SubmixVoice_DestroyVoice(sub); IXAudio2MasteringVoice_DestroyVoice(master); } From 270b45cda973e02d2a530759e229886535f85b67 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 27 Feb 2024 11:09:41 -0600 Subject: [PATCH 179/413] Revert "faudio: Don't destroy voice when it is output to other voices." This reverts commit d497be23aa6940fcb269831cf3f92bc76e3f0444. --- libs/faudio/include/FAudio.h | 2 +- libs/faudio/src/FAudio.c | 61 +----------------------------------- 2 files changed, 2 insertions(+), 61 deletions(-) diff --git a/libs/faudio/include/FAudio.h b/libs/faudio/include/FAudio.h index 1994cd7543b..1a873ca2064 100644 --- a/libs/faudio/include/FAudio.h +++ b/libs/faudio/include/FAudio.h @@ -1062,7 +1062,7 @@ FAUDIOAPI void FAudioVoice_GetOutputMatrix( ); /* Removes this voice from the audio graph and frees memory. */ -FAUDIOAPI uint32_t FAudioVoice_DestroyVoice(FAudioVoice *voice); +FAUDIOAPI void FAudioVoice_DestroyVoice(FAudioVoice *voice); /* FAudioSourceVoice Interface */ diff --git a/libs/faudio/src/FAudio.c b/libs/faudio/src/FAudio.c index 2bb1c1fd40d..9366ad58208 100644 --- a/libs/faudio/src/FAudio.c +++ b/libs/faudio/src/FAudio.c @@ -2265,69 +2265,11 @@ void FAudioVoice_GetOutputMatrix( LOG_API_EXIT(voice->audio) } -static uint32_t check_for_sends_to_voice(FAudioVoice *voice) -{ - FAudio *audio = voice->audio; - uint32_t ret = 1; - FAudioSourceVoice *source; - FAudioSubmixVoice *submix; - LinkedList *list; - uint32_t i; - - FAudio_PlatformLockMutex(audio->sourceLock); - list = audio->sources; - while (list != NULL) - { - source = (FAudioSourceVoice*) list->entry; - for (i = 0; i < source->sends.SendCount; i += 1) - if (source->sends.pSends[i].pOutputVoice == voice) - { - ret = 0; - break; - } - if (!ret) - break; - list = list->next; - } - FAudio_PlatformUnlockMutex(audio->sourceLock); - - if (!ret) - return ret; - - FAudio_PlatformLockMutex(audio->submixLock); - list = audio->submixes; - while (list != NULL) - { - submix = (FAudioSubmixVoice*) list->entry; - for (i = 0; i < submix->sends.SendCount; i += 1) - if (submix->sends.pSends[i].pOutputVoice == voice) - { - ret = 0; - break; - } - list = list->next; - } - FAudio_PlatformUnlockMutex(audio->submixLock); - - return ret; -} - -uint32_t FAudioVoice_DestroyVoice(FAudioVoice *voice) +void FAudioVoice_DestroyVoice(FAudioVoice *voice) { uint32_t i; LOG_API_ENTER(voice->audio) - if (!check_for_sends_to_voice(voice)) - { - LOG_ERROR( - voice->audio, - "Voice %p is an output for other voice(s)", - voice - ) - LOG_API_EXIT(voice->audio) - return 0; - } - /* TODO: Check for dependencies and remove from audio graph first! */ FAudio_OPERATIONSET_ClearAllForVoice(voice); @@ -2501,7 +2443,6 @@ uint32_t FAudioVoice_DestroyVoice(FAudioVoice *voice) LOG_API_EXIT(voice->audio) FAudio_Release(voice->audio); voice->audio->pFree(voice); - return 1; } /* FAudioSourceVoice Interface */ From 969c656dd34468c95e10ad0f5e7b967da3bfa478 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 27 Feb 2024 11:09:53 -0600 Subject: [PATCH 180/413] Revert "xaudio2: Check FAudioVoice_DestroyVoice result in destroy_voice()." This reverts commit 431fdeb1e1537d04452a14c11df83569848b2a53. --- dlls/xaudio2_7/xaudio_dll.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/dlls/xaudio2_7/xaudio_dll.c b/dlls/xaudio2_7/xaudio_dll.c index b1ef81b632e..b821e9667b7 100644 --- a/dlls/xaudio2_7/xaudio_dll.c +++ b/dlls/xaudio2_7/xaudio_dll.c @@ -499,11 +499,7 @@ static const FAudioEngineCallback FAudioEngineCallback_Vtbl = { static inline void destroy_voice(XA2VoiceImpl *This) { - if (!FAudioVoice_DestroyVoice(This->faudio_voice)) - { - ERR("Destroying voice %p failed.\n", This); - return; - } + FAudioVoice_DestroyVoice(This->faudio_voice); free_effect_chain(This->effect_chain); This->effect_chain = NULL; This->in_use = FALSE; From c77c3ab951ddf6237395eb4fa497f1803c42a9fc Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 28 Feb 2024 18:19:35 -0600 Subject: [PATCH 181/413] faudio: Don't destroy voice when it is output to other voices (cherry-picked from FAudio commit c00b01caa6e3f0dce09af6676ff55936e97b451c) CW-Bug-Id: #23472 --- libs/faudio/include/FAudio.h | 5 +++ libs/faudio/src/FAudio.c | 68 +++++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/libs/faudio/include/FAudio.h b/libs/faudio/include/FAudio.h index 1a873ca2064..16837c39dbf 100644 --- a/libs/faudio/include/FAudio.h +++ b/libs/faudio/include/FAudio.h @@ -1064,6 +1064,11 @@ FAUDIOAPI void FAudioVoice_GetOutputMatrix( /* Removes this voice from the audio graph and frees memory. */ FAUDIOAPI void FAudioVoice_DestroyVoice(FAudioVoice *voice); +/* + * Returns S_OK on success and E_FAIL if voice could not be destroyed (e. g., because it is in use). + */ +FAUDIOAPI uint32_t FAudioVoice_DestroyVoiceSafeEXT(FAudioVoice *voice); + /* FAudioSourceVoice Interface */ /* Starts processing for a source voice. diff --git a/libs/faudio/src/FAudio.c b/libs/faudio/src/FAudio.c index 9366ad58208..67cbfb770e4 100644 --- a/libs/faudio/src/FAudio.c +++ b/libs/faudio/src/FAudio.c @@ -2265,11 +2265,71 @@ void FAudioVoice_GetOutputMatrix( LOG_API_EXIT(voice->audio) } -void FAudioVoice_DestroyVoice(FAudioVoice *voice) +static uint32_t check_for_sends_to_voice(FAudioVoice *voice) { + FAudio *audio = voice->audio; + uint32_t ret = 0; + FAudioSourceVoice *source; + FAudioSubmixVoice *submix; + LinkedList *list; uint32_t i; + + FAudio_PlatformLockMutex(audio->sourceLock); + list = audio->sources; + while (list != NULL) + { + source = (FAudioSourceVoice*) list->entry; + for (i = 0; i < source->sends.SendCount; i += 1) + if (source->sends.pSends[i].pOutputVoice == voice) + { + ret = 0x80004005; /* E_FAIL */ + break; + } + if (ret) + break; + list = list->next; + } + FAudio_PlatformUnlockMutex(audio->sourceLock); + + if (ret) + return ret; + + FAudio_PlatformLockMutex(audio->submixLock); + list = audio->submixes; + while (list != NULL) + { + submix = (FAudioSubmixVoice*) list->entry; + for (i = 0; i < submix->sends.SendCount; i += 1) + if (submix->sends.pSends[i].pOutputVoice == voice) + { + ret = 0x80004005; /* E_FAIL */ + break; + } + if (ret) + break; + list = list->next; + } + FAudio_PlatformUnlockMutex(audio->submixLock); + + return ret; +} + +uint32_t FAudioVoice_DestroyVoiceSafeEXT(FAudioVoice *voice) +{ + uint32_t i, ret; LOG_API_ENTER(voice->audio) + if ((ret = check_for_sends_to_voice(voice))) + { + LOG_ERROR( + voice->audio, + "Voice %p is an output for other voice(s)", + voice + ) + LOG_API_EXIT(voice->audio) + return ret; + } + /* TODO: Check for dependencies and remove from audio graph first! */ FAudio_OPERATIONSET_ClearAllForVoice(voice); @@ -2443,6 +2503,12 @@ void FAudioVoice_DestroyVoice(FAudioVoice *voice) LOG_API_EXIT(voice->audio) FAudio_Release(voice->audio); voice->audio->pFree(voice); + return 0; +} + +void FAudioVoice_DestroyVoice(FAudioVoice *voice) +{ + FAudioVoice_DestroyVoiceSafeEXT(voice); } /* FAudioSourceVoice Interface */ From 2efc2aecbd5c2daa099be7668383753f68ff2356 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 26 Feb 2024 19:34:40 -0600 Subject: [PATCH 182/413] xaudio2: Use FAudioVoice_DestroyVoiceSafeEXT() result in destroy_voice(). CW-Bug-Id: #23472 --- dlls/xaudio2_7/xaudio_dll.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dlls/xaudio2_7/xaudio_dll.c b/dlls/xaudio2_7/xaudio_dll.c index b821e9667b7..a58beb7b538 100644 --- a/dlls/xaudio2_7/xaudio_dll.c +++ b/dlls/xaudio2_7/xaudio_dll.c @@ -499,7 +499,11 @@ static const FAudioEngineCallback FAudioEngineCallback_Vtbl = { static inline void destroy_voice(XA2VoiceImpl *This) { - FAudioVoice_DestroyVoice(This->faudio_voice); + if (FAILED(FAudioVoice_DestroyVoiceSafeEXT(This->faudio_voice))) + { + ERR("Destroying voice %p failed.\n", This); + return; + } free_effect_chain(This->effect_chain); This->effect_chain = NULL; This->in_use = FALSE; From 0d82afcd7eb4d1428eed92e07c1a865d6a99fe96 Mon Sep 17 00:00:00 2001 From: Jactry Zeng Date: Wed, 28 Feb 2024 00:59:28 -0600 Subject: [PATCH 183/413] wine.inf: Add a font replacement for Segoe UI. CW-Bug-Id: #23020 --- loader/wine.inf.in | 1 + 1 file changed, 1 insertion(+) diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 7e8fa0a25a5..1e5b16a53a6 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -603,6 +603,7 @@ HKLM,%FontsNT%,"Webdings (TrueType)",,"webdings.ttf" HKLM,%FontsNT%,"Wingdings (TrueType)",,"wingdings.ttf" HKCU,Software\Wine\Fonts\Replacements,"Palatino Linotype",,"Times New Roman" HKCU,Software\Wine\Fonts\Replacements,"Verdana",,"Times New Roman" +HKCU,Software\Wine\Fonts\Replacements,"Segoe UI",,"Times New Roman" [MCI] HKLM,%Mci32Str%,"AVIVideo",,"mciavi32.dll" From 0a193b3bb957e3ba1d93c3314098e6261cf3c131 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 29 Feb 2024 20:02:48 -0600 Subject: [PATCH 184/413] fixup! ntdll: Read process memory on the client side in NtReadVirtualMemory(). CW-Bug-Id: #23456 --- dlls/ntdll/unix/virtual.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 824ba75d21b..a2fbc8ed0cc 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -6442,13 +6442,21 @@ NTSTATUS WINAPI NtReadVirtualMemory( HANDLE process, const void *addr, void *buf int unix_pid; ssize_t ret; - SERVER_START_REQ( read_process_memory ) + if (process == NtCurrentProcess()) { - req->handle = wine_server_obj_handle( process ); - status = wine_server_call( req ); - unix_pid = reply->unix_pid; + unix_pid = getpid(); + status = STATUS_SUCCESS; + } + else + { + SERVER_START_REQ( read_process_memory ) + { + req->handle = wine_server_obj_handle( process ); + status = wine_server_call( req ); + unix_pid = reply->unix_pid; + } + SERVER_END_REQ; } - SERVER_END_REQ; if (status) { From 11193217a50f001e5a876cdf9d21b12c14e39523 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Mon, 4 Mar 2024 12:13:46 +0800 Subject: [PATCH 185/413] advapi32: Check NULL return key pointers when creating registry keys. Fix Warlords Battlecry III (433280) crashes at launch. CW-Bug-Id: #23484 --- dlls/advapi32/registry.c | 6 ++++++ dlls/kernelbase/registry.c | 2 ++ 2 files changed, 8 insertions(+) diff --git a/dlls/advapi32/registry.c b/dlls/advapi32/registry.c index 85e883bdcc9..e3226c38d0a 100644 --- a/dlls/advapi32/registry.c +++ b/dlls/advapi32/registry.c @@ -70,6 +70,9 @@ LSTATUS WINAPI RegOverridePredefKey( HKEY hkey, HKEY override ) */ LSTATUS WINAPI RegCreateKeyW( HKEY hkey, LPCWSTR lpSubKey, PHKEY phkResult ) { + if (!phkResult) + return ERROR_INVALID_PARAMETER; + return RegCreateKeyExW( hkey, lpSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED, NULL, phkResult, NULL ); } @@ -82,6 +85,9 @@ LSTATUS WINAPI RegCreateKeyW( HKEY hkey, LPCWSTR lpSubKey, PHKEY phkResult ) */ LSTATUS WINAPI RegCreateKeyA( HKEY hkey, LPCSTR lpSubKey, PHKEY phkResult ) { + if (!phkResult) + return ERROR_INVALID_PARAMETER; + return RegCreateKeyExA( hkey, lpSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED, NULL, phkResult, NULL ); } diff --git a/dlls/kernelbase/registry.c b/dlls/kernelbase/registry.c index dcdb1cf84eb..ee57a4acdd1 100644 --- a/dlls/kernelbase/registry.c +++ b/dlls/kernelbase/registry.c @@ -592,6 +592,7 @@ LSTATUS WINAPI DECLSPEC_HOTPATCH RegCreateKeyExW( HKEY hkey, LPCWSTR name, DWORD { UNICODE_STRING nameW, classW; + if (!retkey) return ERROR_BADKEY; if (reserved) return ERROR_INVALID_PARAMETER; if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE; @@ -633,6 +634,7 @@ LSTATUS WINAPI DECLSPEC_HOTPATCH RegCreateKeyExA( HKEY hkey, LPCSTR name, DWORD ANSI_STRING nameA, classA; NTSTATUS status; + if (!retkey) return ERROR_BADKEY; if (reserved) return ERROR_INVALID_PARAMETER; if (!is_version_nt()) { From 6bf0aa2e761e643b26ceca0deb7e1ccb11d357e2 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Mon, 4 Mar 2024 12:09:45 +0800 Subject: [PATCH 186/413] advapi32/tests: Test creating registry keys with a NULL return key pointer. CW-Bug-Id: #23484 --- dlls/advapi32/tests/registry.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dlls/advapi32/tests/registry.c b/dlls/advapi32/tests/registry.c index 428dffed48d..ecfdbbe547e 100644 --- a/dlls/advapi32/tests/registry.c +++ b/dlls/advapi32/tests/registry.c @@ -1268,6 +1268,19 @@ static void test_reg_create_key(void) PACL key_acl; SECURITY_DESCRIPTOR *sd; + /* NULL return key check */ + ret = RegCreateKeyA(hkey_main, "Subkey1", NULL); + ok(ret == ERROR_INVALID_PARAMETER, "Got unexpected ret %ld.\n", ret); + + ret = RegCreateKeyW(hkey_main, L"Subkey1", NULL); + ok(ret == ERROR_INVALID_PARAMETER, "Got unexpected ret %ld.\n", ret); + + ret = RegCreateKeyExA(hkey_main, "Subkey1", 0, NULL, 0, KEY_NOTIFY, NULL, NULL, NULL); + ok(ret == ERROR_BADKEY, "Got unexpected ret %ld.\n", ret); + + ret = RegCreateKeyExW(hkey_main, L"Subkey1", 0, NULL, 0, KEY_NOTIFY, NULL, NULL, NULL); + ok(ret == ERROR_BADKEY, "Got unexpected ret %ld.\n", ret); + ret = RegCreateKeyExA(hkey_main, "Subkey1", 0, NULL, 0, KEY_NOTIFY, NULL, &hkey1, NULL); ok(!ret, "RegCreateKeyExA failed with error %ld\n", ret); /* should succeed: all versions of Windows ignore the access rights From 1573cfef6b9511b8c15912980c51121b62a7c40a Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Mon, 4 Mar 2024 18:51:54 +0100 Subject: [PATCH 187/413] dbghelp: Stop embedding unwind info in minidumps (X86_64). CW-Bug-Id: https://www.codeweavers.com/support/bugs/browse/?cmd=bug_edit;bug_id=23456 --- dlls/dbghelp/cpu_x86_64.c | 39 +-------------------------------------- 1 file changed, 1 insertion(+), 38 deletions(-) diff --git a/dlls/dbghelp/cpu_x86_64.c b/dlls/dbghelp/cpu_x86_64.c index eeae1f042b2..23ed5271128 100644 --- a/dlls/dbghelp/cpu_x86_64.c +++ b/dlls/dbghelp/cpu_x86_64.c @@ -951,44 +951,7 @@ static BOOL x86_64_fetch_minidump_thread(struct dump_context* dc, unsigned index static BOOL x86_64_fetch_minidump_module(struct dump_context* dc, unsigned index, unsigned flags) { - /* FIXME: not sure about the flags... */ - if (1) - { - /* FIXME: crop values across module boundaries, */ -#ifdef __x86_64__ - struct process* pcs; - struct module* module; - const RUNTIME_FUNCTION* rtf; - ULONG size; - - if (!(pcs = process_find_by_handle(dc->process->handle)) || - !(module = module_find_by_addr(pcs, dc->modules[index].base))) - return FALSE; - rtf = (const RUNTIME_FUNCTION*)pe_map_directory(module, IMAGE_DIRECTORY_ENTRY_EXCEPTION, &size); - if (rtf) - { - const RUNTIME_FUNCTION* end = (const RUNTIME_FUNCTION*)((const char*)rtf + size); - UNWIND_INFO ui; - - while (rtf + 1 < end) - { - while (rtf->UnwindData & 1) /* follow chained entry */ - { - FIXME("RunTime_Function outside IMAGE_DIRECTORY_ENTRY_EXCEPTION unimplemented yet!\n"); - return FALSE; - /* we need to read into the other process */ - /* rtf = (RUNTIME_FUNCTION*)(module->module.BaseOfImage + (rtf->UnwindData & ~1)); */ - } - if (read_process_memory(dc->process, dc->modules[index].base + rtf->UnwindData, &ui, sizeof(ui))) - minidump_add_memory_block(dc, dc->modules[index].base + rtf->UnwindData, - FIELD_OFFSET(UNWIND_INFO, UnwindCode) + ui.CountOfCodes * sizeof(UNWIND_CODE), 0); - rtf++; - } - } -#endif - } - - return TRUE; + return FALSE; } struct cpu cpu_x86_64 = { From 55461e6a507f6a62929517f30acf646607eca195 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 4 Mar 2024 14:37:03 -0600 Subject: [PATCH 188/413] winegstreamer: HACK: Do not enable low latency for Gungrave G.O.R.E. CW-Bug-Id: #23455 --- dlls/winegstreamer/video_decoder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winegstreamer/video_decoder.c b/dlls/winegstreamer/video_decoder.c index 18ac6c0858f..3e42d04d75a 100644 --- a/dlls/winegstreamer/video_decoder.c +++ b/dlls/winegstreamer/video_decoder.c @@ -116,7 +116,7 @@ static HRESULT try_create_wg_transform(struct video_decoder *decoder) { const char *sgi; - if ((sgi = getenv("SteamGameId")) && ((!strcmp(sgi, "2009100")) || (!strcmp(sgi, "2555360")))) + if ((sgi = getenv("SteamGameId")) && (!strcmp(sgi, "2009100") || !strcmp(sgi, "2555360") || !strcmp(sgi, "1630110"))) attrs.low_latency = FALSE; } From 9970d96014fe22e6890bcaae705821250305199c Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Thu, 29 Feb 2024 11:46:06 +1100 Subject: [PATCH 189/413] mshtml: Pass DOMEvent instead of nsIDOMEvent during handle_event. CW-Bug-Id: #23083 (cherry picked from commit 277acf61d0fb8e5d36fbadda8ecb349dd3dcc435) --- dlls/mshtml/htmlanchor.c | 8 ++++---- dlls/mshtml/htmlarea.c | 8 ++++---- dlls/mshtml/htmlelem.c | 6 +++--- dlls/mshtml/htmlevent.c | 2 +- dlls/mshtml/htmlevent.h | 4 ++-- dlls/mshtml/htmlform.c | 6 +++--- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/dlls/mshtml/htmlanchor.c b/dlls/mshtml/htmlanchor.c index 7d2ad328890..147a5b9cbe6 100644 --- a/dlls/mshtml/htmlanchor.c +++ b/dlls/mshtml/htmlanchor.c @@ -829,13 +829,13 @@ static void HTMLAnchorElement_unlink(DispatchEx *dispex) unlink_ref(&This->nsanchor); } -static HRESULT HTMLAnchorElement_handle_event(DispatchEx *dispex, eventid_t eid, nsIDOMEvent *event, BOOL *prevent_default) +static HRESULT HTMLAnchorElement_handle_event(DispatchEx *dispex, DOMEvent *event, BOOL *prevent_default) { HTMLAnchorElement *This = impl_from_DispatchEx(dispex); nsAString href_str, target_str; nsresult nsres; - if(eid == EVENTID_CLICK) { + if(event->event_id == EVENTID_CLICK) { nsAString_Init(&href_str, NULL); nsres = nsIDOMHTMLAnchorElement_GetHref(This->nsanchor, &href_str); if (NS_FAILED(nsres)) { @@ -850,14 +850,14 @@ static HRESULT HTMLAnchorElement_handle_event(DispatchEx *dispex, eventid_t eid, goto fallback; } - return handle_link_click_event(&This->element, &href_str, &target_str, event, prevent_default); + return handle_link_click_event(&This->element, &href_str, &target_str, event->nsevent, prevent_default); fallback: nsAString_Finish(&href_str); nsAString_Finish(&target_str); } - return HTMLElement_handle_event(&This->element.node.event_target.dispex, eid, event, prevent_default); + return HTMLElement_handle_event(&This->element.node.event_target.dispex, event, prevent_default); } static const NodeImplVtbl HTMLAnchorElementImplVtbl = { diff --git a/dlls/mshtml/htmlarea.c b/dlls/mshtml/htmlarea.c index c4cb109039a..1b9be40baab 100644 --- a/dlls/mshtml/htmlarea.c +++ b/dlls/mshtml/htmlarea.c @@ -439,13 +439,13 @@ static void HTMLAreaElement_unlink(DispatchEx *dispex) unlink_ref(&This->nsarea); } -static HRESULT HTMLAreaElement_handle_event(DispatchEx *dispex, eventid_t eid, nsIDOMEvent *event, BOOL *prevent_default) +static HRESULT HTMLAreaElement_handle_event(DispatchEx *dispex, DOMEvent *event, BOOL *prevent_default) { HTMLAreaElement *This = impl_from_DispatchEx(dispex); nsAString href_str, target_str; nsresult nsres; - if(eid == EVENTID_CLICK) { + if(event->event_id == EVENTID_CLICK) { nsAString_Init(&href_str, NULL); nsres = nsIDOMHTMLAreaElement_GetHref(This->nsarea, &href_str); if (NS_FAILED(nsres)) { @@ -460,14 +460,14 @@ static HRESULT HTMLAreaElement_handle_event(DispatchEx *dispex, eventid_t eid, n goto fallback; } - return handle_link_click_event(&This->element, &href_str, &target_str, event, prevent_default); + return handle_link_click_event(&This->element, &href_str, &target_str, event->nsevent, prevent_default); fallback: nsAString_Finish(&href_str); nsAString_Finish(&target_str); } - return HTMLElement_handle_event(&This->element.node.event_target.dispex, eid, event, prevent_default); + return HTMLElement_handle_event(&This->element.node.event_target.dispex, event, prevent_default); } static const NodeImplVtbl HTMLAreaElementImplVtbl = { diff --git a/dlls/mshtml/htmlelem.c b/dlls/mshtml/htmlelem.c index 6fcab0cf89f..cbec1368eaa 100644 --- a/dlls/mshtml/htmlelem.c +++ b/dlls/mshtml/htmlelem.c @@ -7083,16 +7083,16 @@ void HTMLElement_bind_event(DispatchEx *dispex, eventid_t eid) ensure_doc_nsevent_handler(This->node.doc, This->node.nsnode, eid); } -HRESULT HTMLElement_handle_event(DispatchEx *dispex, eventid_t eid, nsIDOMEvent *event, BOOL *prevent_default) +HRESULT HTMLElement_handle_event(DispatchEx *dispex, DOMEvent *event, BOOL *prevent_default) { HTMLElement *This = impl_from_DispatchEx(dispex); - switch(eid) { + switch(event->event_id) { case EVENTID_KEYDOWN: { nsIDOMKeyEvent *key_event; nsresult nsres; - nsres = nsIDOMEvent_QueryInterface(event, &IID_nsIDOMKeyEvent, (void**)&key_event); + nsres = nsIDOMEvent_QueryInterface(event->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event); if(NS_SUCCEEDED(nsres)) { UINT32 code = 0; diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c index bba0970d7ce..485fb8c3f39 100644 --- a/dlls/mshtml/htmlevent.c +++ b/dlls/mshtml/htmlevent.c @@ -5165,7 +5165,7 @@ static HRESULT dispatch_event_object(EventTarget *event_target, DOMEvent *event, vtbl = dispex_get_vtbl(&target_chain[i]->dispex); if(!vtbl->handle_event) continue; - hres = vtbl->handle_event(&target_chain[i]->dispex, event->event_id, event->nsevent, &prevent_default); + hres = vtbl->handle_event(&target_chain[i]->dispex, event, &prevent_default); if(FAILED(hres) || event->stop_propagation) break; if(prevent_default) diff --git a/dlls/mshtml/htmlevent.h b/dlls/mshtml/htmlevent.h index 2933314292e..545560a4030 100644 --- a/dlls/mshtml/htmlevent.h +++ b/dlls/mshtml/htmlevent.h @@ -133,7 +133,7 @@ typedef struct { nsISupports *(*get_gecko_target)(DispatchEx*); void (*bind_event)(DispatchEx*,eventid_t); EventTarget *(*get_parent_event_target)(DispatchEx*); - HRESULT (*handle_event)(DispatchEx*,eventid_t,nsIDOMEvent*,BOOL*); + HRESULT (*handle_event)(DispatchEx*,DOMEvent*,BOOL*); ConnectionPointContainer *(*get_cp_container)(DispatchEx*); IHTMLEventObj *(*set_current_event)(DispatchEx*,IHTMLEventObj*); } event_target_vtbl_t; @@ -143,7 +143,7 @@ IHTMLEventObj *default_set_current_event(HTMLInnerWindow*,IHTMLEventObj*); nsISupports *HTMLElement_get_gecko_target(DispatchEx*); void HTMLElement_bind_event(DispatchEx*,eventid_t); EventTarget *HTMLElement_get_parent_event_target(DispatchEx*); -HRESULT HTMLElement_handle_event(DispatchEx*,eventid_t,nsIDOMEvent*,BOOL*); +HRESULT HTMLElement_handle_event(DispatchEx*,DOMEvent*,BOOL*); ConnectionPointContainer *HTMLElement_get_cp_container(DispatchEx*); IHTMLEventObj *HTMLElement_set_current_event(DispatchEx*,IHTMLEventObj*); diff --git a/dlls/mshtml/htmlform.c b/dlls/mshtml/htmlform.c index 3b8f0cb4e43..019f5cb1a5f 100644 --- a/dlls/mshtml/htmlform.c +++ b/dlls/mshtml/htmlform.c @@ -1061,16 +1061,16 @@ static HRESULT HTMLFormElement_invoke(DispatchEx *dispex, IDispatch *this_obj, D return S_OK; } -static HRESULT HTMLFormElement_handle_event(DispatchEx *dispex, eventid_t eid, nsIDOMEvent *event, BOOL *prevent_default) +static HRESULT HTMLFormElement_handle_event(DispatchEx *dispex, DOMEvent *event, BOOL *prevent_default) { HTMLFormElement *This = impl_from_DispatchEx(dispex); - if(eid == EVENTID_SUBMIT) { + if(event->event_id == EVENTID_SUBMIT) { *prevent_default = TRUE; return IHTMLFormElement_submit(&This->IHTMLFormElement_iface); } - return HTMLElement_handle_event(&This->element.node.event_target.dispex, eid, event, prevent_default); + return HTMLElement_handle_event(&This->element.node.event_target.dispex, event, prevent_default); } static const NodeImplVtbl HTMLFormElementImplVtbl = { From cc1698d508e1289cc0e6873a17ac5080c8af5816 Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Wed, 21 Feb 2024 11:05:05 +1100 Subject: [PATCH 190/413] mshtml: Use generic event dispatcher for DOMContentLoaded. Use generic event dispatcher instead of nsevent for the DOMContentLoaded event. Also allow processing before dispatching event. Only update dom_content_loaded_event start/end time when the event is trusted. CW-Bug-Id: #23083 (cherry picked from commit 54980a786814ce3bed1f17be6e77658f9b9430ec) --- dlls/mshtml/htmldoc.c | 36 +++++++++++++++++++++++++++++++++++- dlls/mshtml/htmlevent.c | 14 +++++++++++++- dlls/mshtml/htmlevent.h | 1 + dlls/mshtml/nsevents.c | 22 ---------------------- 4 files changed, 49 insertions(+), 24 deletions(-) diff --git a/dlls/mshtml/htmldoc.c b/dlls/mshtml/htmldoc.c index 6b5d73c0604..69c739001a6 100644 --- a/dlls/mshtml/htmldoc.c +++ b/dlls/mshtml/htmldoc.c @@ -6333,6 +6333,38 @@ static HRESULT HTMLDocumentNode_location_hook(DispatchEx *dispex, WORD flags, DI DISPID_IHTMLWINDOW2_LOCATION, 0, flags, dp, res, ei, caller); } +static HRESULT HTMLDocumentNode_pre_handle_event(DispatchEx* dispex, DOMEvent *event) +{ + HTMLDocumentNode *doc = impl_from_DispatchEx(dispex); + switch(event->event_id) { + case EVENTID_DOMCONTENTLOADED: { + if(event->trusted && doc->window) + doc->window->dom_content_loaded_event_start_time = get_time_stamp(); + break; + } + default: + break; + } + + return S_OK; +} + +static HRESULT HTMLDocumentNode_handle_event(DispatchEx* dispex, DOMEvent *event, BOOL *prevent_default) +{ + HTMLDocumentNode *doc = impl_from_DispatchEx(dispex); + switch(event->event_id) { + case EVENTID_DOMCONTENTLOADED: { + if(event->trusted && doc->window) + doc->window->dom_content_loaded_event_end_time = get_time_stamp(); + break; + } + default: + break; + } + + return S_OK; +} + static const event_target_vtbl_t HTMLDocumentNode_event_target_vtbl = { { .query_interface = HTMLDocumentNode_query_interface, @@ -6348,8 +6380,10 @@ static const event_target_vtbl_t HTMLDocumentNode_event_target_vtbl = { .get_gecko_target = HTMLDocumentNode_get_gecko_target, .bind_event = HTMLDocumentNode_bind_event, .get_parent_event_target = HTMLDocumentNode_get_parent_event_target, + .pre_handle_event = HTMLDocumentNode_pre_handle_event, + .handle_event = HTMLDocumentNode_handle_event, .get_cp_container = HTMLDocumentNode_get_cp_container, - .set_current_event = HTMLDocumentNode_set_current_event + .set_current_event = HTMLDocumentNode_set_current_event, }; static const NodeImplVtbl HTMLDocumentFragmentImplVtbl = { diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c index 485fb8c3f39..f6f21d7a9eb 100644 --- a/dlls/mshtml/htmlevent.c +++ b/dlls/mshtml/htmlevent.c @@ -117,7 +117,7 @@ typedef struct { /* Keep these sorted case sensitively */ static const event_info_t event_info[] = { {L"DOMContentLoaded", EVENT_TYPE_EVENT, 0, - EVENT_BUBBLES | EVENT_CANCELABLE}, + EVENT_DEFAULTLISTENER | EVENT_HASDEFAULTHANDLERS | EVENT_BUBBLES | EVENT_CANCELABLE }, {L"abort", EVENT_TYPE_EVENT, DISPID_EVMETH_ONABORT, EVENT_BIND_TO_TARGET}, {L"afterprint", EVENT_TYPE_EVENT, DISPID_EVMETH_ONAFTERPRINT, @@ -5132,6 +5132,18 @@ static HRESULT dispatch_event_object(EventTarget *event_target, DOMEvent *event, IEventTarget_AddRef(&event_target->IEventTarget_iface); event->phase = DEP_CAPTURING_PHASE; + + if(event_info[event->event_id].flags & EVENT_HASDEFAULTHANDLERS) { + for(i = 0; i < chain_cnt; i++) { + vtbl = dispex_get_vtbl(&target_chain[i]->dispex); + if(!vtbl->pre_handle_event) + continue; + hres = vtbl->pre_handle_event(&target_chain[i]->dispex, event); + if(FAILED(hres) || event->stop_propagation) + break; + } + } + i = chain_cnt-1; while(!event->stop_propagation && i) call_event_handlers(target_chain[i--], event, dispatch_mode); diff --git a/dlls/mshtml/htmlevent.h b/dlls/mshtml/htmlevent.h index 545560a4030..f2cbfe18bf2 100644 --- a/dlls/mshtml/htmlevent.h +++ b/dlls/mshtml/htmlevent.h @@ -133,6 +133,7 @@ typedef struct { nsISupports *(*get_gecko_target)(DispatchEx*); void (*bind_event)(DispatchEx*,eventid_t); EventTarget *(*get_parent_event_target)(DispatchEx*); + HRESULT (*pre_handle_event)(DispatchEx*,DOMEvent*); HRESULT (*handle_event)(DispatchEx*,DOMEvent*,BOOL*); ConnectionPointContainer *(*get_cp_container)(DispatchEx*); IHTMLEventObj *(*set_current_event)(DispatchEx*,IHTMLEventObj*); diff --git a/dlls/mshtml/nsevents.c b/dlls/mshtml/nsevents.c index c229a2c634f..c6c6438a740 100644 --- a/dlls/mshtml/nsevents.c +++ b/dlls/mshtml/nsevents.c @@ -48,7 +48,6 @@ typedef struct { static nsresult handle_blur(HTMLDocumentNode*,nsIDOMEvent*); static nsresult handle_focus(HTMLDocumentNode*,nsIDOMEvent*); static nsresult handle_keypress(HTMLDocumentNode*,nsIDOMEvent*); -static nsresult handle_dom_content_loaded(HTMLDocumentNode*,nsIDOMEvent*); static nsresult handle_pageshow(HTMLDocumentNode*,nsIDOMEvent*); static nsresult handle_pagehide(HTMLDocumentNode*,nsIDOMEvent*); static nsresult handle_load(HTMLDocumentNode*,nsIDOMEvent*); @@ -68,7 +67,6 @@ static const struct { { EVENTID_BLUR, 0, handle_blur }, { EVENTID_FOCUS, 0, handle_focus }, { EVENTID_KEYPRESS, BUBBLES, handle_keypress }, - { EVENTID_DOMCONTENTLOADED, OVERRIDE, handle_dom_content_loaded }, { EVENTID_PAGESHOW, OVERRIDE, handle_pageshow }, { EVENTID_PAGEHIDE, OVERRIDE, handle_pagehide }, { EVENTID_LOAD, OVERRIDE, handle_load }, @@ -234,26 +232,6 @@ static nsresult handle_keypress(HTMLDocumentNode *doc, nsIDOMEvent *event) return NS_OK; } -static nsresult handle_dom_content_loaded(HTMLDocumentNode *doc, nsIDOMEvent *nsevent) -{ - DOMEvent *event; - HRESULT hres; - - if(doc->window) - doc->window->dom_content_loaded_event_start_time = get_time_stamp(); - - hres = create_event_from_nsevent(nsevent, get_inner_window(doc), dispex_compat_mode(&doc->node.event_target.dispex), &event); - if(SUCCEEDED(hres)) { - dispatch_event(&doc->node.event_target, event); - IDOMEvent_Release(&event->IDOMEvent_iface); - } - - if(doc->window) - doc->window->dom_content_loaded_event_end_time = get_time_stamp(); - - return NS_OK; -} - static nsresult handle_pageshow(HTMLDocumentNode *doc, nsIDOMEvent *nsevent) { HTMLInnerWindow *window; From af4a85db2665a60e85ea95fb205bc4840789816d Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Mon, 26 Feb 2024 14:36:50 +1100 Subject: [PATCH 191/413] mshtml/tests: Add test for document mode after InitNew and Load. CW-Bug-Id: #23083 (cherry picked from commit 34a8478da895538c4e3f2a9432e150d4a33ebe06) --- dlls/mshtml/tests/dom.c | 76 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/dlls/mshtml/tests/dom.c b/dlls/mshtml/tests/dom.c index 36863d2ac62..b56e13aebfc 100644 --- a/dlls/mshtml/tests/dom.c +++ b/dlls/mshtml/tests/dom.c @@ -12253,6 +12253,81 @@ static void test_document_mode_lock(void) IHTMLDocument2_Release(doc); } +static void test_document_mode_after_initnew(void) +{ + IHTMLDocument2 *doc; + IHTMLDocument6 *doc6; + IEventTarget *event_target; + IPersistStreamInit *init; + IStream *stream; + HRESULT hres; + HGLOBAL mem; + VARIANT var; + SIZE_T len; + MSG msg; + + notif_doc = doc = create_document(); + if(!doc) + return; + doc_complete = FALSE; + + hres = IHTMLDocument2_QueryInterface(doc, &IID_IEventTarget, (void**)&event_target); + ok(hres == E_NOINTERFACE, "QueryInterface(IID_IEventTarget) returned %08lx.\n", hres); + ok(event_target == NULL, "event_target != NULL\n"); + + len = strlen(doc_blank_ie9); + mem = GlobalAlloc(0, len); + memcpy(mem, doc_blank_ie9, len); + hres = CreateStreamOnHGlobal(mem, TRUE, &stream); + ok(hres == S_OK, "Failed to create stream: %08lx.\n", hres); + + hres = IHTMLDocument2_QueryInterface(doc, &IID_IPersistStreamInit, (void**)&init); + ok(hres == S_OK, "QueryInterface(IID_IPersistStreamInit) failed: %08lx.\n", hres); + + IPersistStreamInit_InitNew(init); + IPersistStreamInit_Load(init, stream); + IPersistStreamInit_Release(init); + IStream_Release(stream); + + set_client_site(doc, TRUE); + do_advise((IUnknown*)doc, &IID_IPropertyNotifySink, (IUnknown*)&PropertyNotifySink); + + hres = IHTMLDocument2_QueryInterface(doc, &IID_IHTMLDocument6, (void**)&doc6); + ok(hres == S_OK, "QueryInterface(IID_IHTMLDocument6) failed: %08lx\n", hres); + + V_VT(&var) = VT_EMPTY; + hres = IHTMLDocument6_get_documentMode(doc6, &var); + ok(hres == S_OK, "get_documentMode failed: %08lx\n", hres); + ok(V_VT(&var) == VT_R4, "V_VT(documentMode) = %u\n", V_VT(&var)); + ok(V_R4(&var) == 5, "documentMode = %f, expected 5\n", V_R4(&var)); + VariantClear(&var); + + while(!doc_complete && GetMessageW(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + hres = IHTMLDocument2_QueryInterface(doc, &IID_IEventTarget, (void**)&event_target); + todo_wine + ok(hres == S_OK, "QueryInterface(IID_IEventTarget) returned %08lx.\n", hres); + todo_wine + ok(event_target != NULL, "event_target == NULL\n"); + if (event_target != NULL) + IEventTarget_Release(event_target); + + V_VT(&var) = VT_EMPTY; + hres = IHTMLDocument6_get_documentMode(doc6, &var); + ok(hres == S_OK, "get_documentMode failed: %08lx\n", hres); + ok(V_VT(&var) == VT_R4, "V_VT(documentMode) = %u\n", V_VT(&var)); + todo_wine + ok(V_R4(&var) == 9, "documentMode = %f, expected 9\n", V_R4(&var)); + IHTMLDocument6_Release(doc6); + VariantClear(&var); + + set_client_site(doc, FALSE); + IHTMLDocument2_Release(doc); +} + static DWORD WINAPI create_document_proc(void *param) { IHTMLDocument2 *doc = NULL; @@ -12378,6 +12453,7 @@ START_TEST(dom) test_quirks_mode(); test_document_mode_lock(); + test_document_mode_after_initnew(); test_threads(); /* Run this last since it messes with the process-wide user agent */ From 2086bb0d686804770560c7f70464b83ba0c6bcc2 Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Mon, 26 Feb 2024 14:39:01 +1100 Subject: [PATCH 192/413] mshtml: Always use the event target dispex. The event target may be from a different document to the document associated with the event handler. CW-Bug-Id: #23083 (cherry picked from commit 74ff9f23717d2a8a8255d213099d2709e55e9c63) --- dlls/mshtml/nsevents.c | 2 +- dlls/mshtml/tests/dom.c | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/dlls/mshtml/nsevents.c b/dlls/mshtml/nsevents.c index c6c6438a740..38b3cccd951 100644 --- a/dlls/mshtml/nsevents.c +++ b/dlls/mshtml/nsevents.c @@ -444,7 +444,7 @@ static nsresult handle_htmlevent(HTMLDocumentNode *doc, nsIDOMEvent *nsevent) target = &node->event_target; } - hres = create_event_from_nsevent(nsevent, get_inner_window(doc), dispex_compat_mode(&doc->node.event_target.dispex), &event); + hres = create_event_from_nsevent(nsevent, get_inner_window(doc), dispex_compat_mode(&target->dispex), &event); if(FAILED(hres)) { IEventTarget_Release(&target->IEventTarget_iface); return NS_OK; diff --git a/dlls/mshtml/tests/dom.c b/dlls/mshtml/tests/dom.c index b56e13aebfc..ceb59ee8a78 100644 --- a/dlls/mshtml/tests/dom.c +++ b/dlls/mshtml/tests/dom.c @@ -12308,18 +12308,14 @@ static void test_document_mode_after_initnew(void) } hres = IHTMLDocument2_QueryInterface(doc, &IID_IEventTarget, (void**)&event_target); - todo_wine ok(hres == S_OK, "QueryInterface(IID_IEventTarget) returned %08lx.\n", hres); - todo_wine ok(event_target != NULL, "event_target == NULL\n"); - if (event_target != NULL) - IEventTarget_Release(event_target); + IEventTarget_Release(event_target); V_VT(&var) = VT_EMPTY; hres = IHTMLDocument6_get_documentMode(doc6, &var); ok(hres == S_OK, "get_documentMode failed: %08lx\n", hres); ok(V_VT(&var) == VT_R4, "V_VT(documentMode) = %u\n", V_VT(&var)); - todo_wine ok(V_R4(&var) == 9, "documentMode = %f, expected 9\n", V_R4(&var)); IHTMLDocument6_Release(doc6); VariantClear(&var); From b3ba4788b039a06aa162c37ee686345924f8e3cc Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Mon, 26 Feb 2024 16:15:43 +1100 Subject: [PATCH 193/413] mshtml: Don't handle special case when doc != node->doc. CW-Bug-Id: #23083 (cherry picked from commit 57c2c41d751d179dd1b5a2bb02431a072908ad6b) --- dlls/mshtml/nsevents.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/mshtml/nsevents.c b/dlls/mshtml/nsevents.c index 38b3cccd951..9a23d031d72 100644 --- a/dlls/mshtml/nsevents.c +++ b/dlls/mshtml/nsevents.c @@ -416,6 +416,7 @@ static nsresult handle_htmlevent(HTMLDocumentNode *doc, nsIDOMEvent *nsevent) nsIDOMEventTarget *event_target; EventTarget *target; nsIDOMNode *nsnode; + HTMLDOMNode *node = NULL; DOMEvent *event; nsresult nsres; HRESULT hres; @@ -436,7 +437,6 @@ static nsresult handle_htmlevent(HTMLDocumentNode *doc, nsIDOMEvent *nsevent) target = &doc->window->event_target; IHTMLWindow2_AddRef(&doc->window->base.IHTMLWindow2_iface); }else { - HTMLDOMNode *node; hres = get_node(nsnode, TRUE, &node); nsIDOMNode_Release(nsnode); if(FAILED(hres)) @@ -451,7 +451,7 @@ static nsresult handle_htmlevent(HTMLDocumentNode *doc, nsIDOMEvent *nsevent) } /* If we fine need for more special cases here, we may consider handling it in a more generic way. */ - if(event->event_id == EVENTID_FOCUS || event->event_id == EVENTID_BLUR) { + if((!node || doc == node->doc) && (event->event_id == EVENTID_FOCUS || event->event_id == EVENTID_BLUR)) { DOMEvent *focus_event; hres = create_document_event(doc, event->event_id == EVENTID_FOCUS ? EVENTID_FOCUSIN : EVENTID_FOCUSOUT, &focus_event); From c1f557fafad7bf619c945983676bd7b1a565b875 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 1 Mar 2024 20:20:13 -0500 Subject: [PATCH 194/413] sapi: Implement ISpeechVoice::{get/put}_Volume. (cherry picked from commit 7c384f361aaa38b31fa7b9ff72085eee728c52ea) CW-Bug-Id: #22520 --- dlls/sapi/tests/tts.c | 9 ++++++++- dlls/sapi/tts.c | 17 +++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 4d565b35294..a5c950aa80b 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -434,7 +434,7 @@ static void test_spvoice(void) DWORD start, duration; ISpeechVoice *speech_voice; ISpeechObjectTokens *speech_tokens; - LONG count; + LONG count, volume_long; BSTR req = NULL, opt = NULL; UINT info_count; ITypeInfo *typeinfo; @@ -717,6 +717,13 @@ static void test_spvoice(void) ok(count == 1, "got %ld.\n", count); ISpeechObjectTokens_Release(speech_tokens); + volume_long = 0xdeadbeef; + hr = ISpeechVoice_put_Volume(speech_voice, 80); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = ISpeechVoice_get_Volume(speech_voice, &volume_long); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(volume_long == 80, "got %ld.\n", volume_long); + hr = ISpeechVoice_Speak(speech_voice, NULL, SVSFPurgeBeforeSpeak, NULL); ok(hr == S_OK, "got %#lx.\n", hr); diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index d22ffd7a0da..325edaf04fb 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -291,16 +291,25 @@ static HRESULT WINAPI speech_voice_put_Rate(ISpeechVoice *iface, LONG rate) static HRESULT WINAPI speech_voice_get_Volume(ISpeechVoice *iface, LONG *volume) { - FIXME("(%p, %p): stub.\n", iface, volume); + struct speech_voice *This = impl_from_ISpeechVoice(iface); + USHORT res = 0; + HRESULT hr; - return E_NOTIMPL; + TRACE("(%p, %p).\n", iface, volume); + + if (!volume) return E_POINTER; + hr = ISpVoice_GetVolume(&This->ISpVoice_iface, &res); + *volume = res; + return hr; } static HRESULT WINAPI speech_voice_put_Volume(ISpeechVoice *iface, LONG volume) { - FIXME("(%p, %ld): stub.\n", iface, volume); + struct speech_voice *This = impl_from_ISpeechVoice(iface); - return E_NOTIMPL; + TRACE("(%p, %ld).\n", iface, volume); + + return ISpVoice_SetVolume(&This->ISpVoice_iface, (USHORT)volume); } static HRESULT WINAPI speech_voice_put_AllowAudioOutputFormatChangesOnNextSet(ISpeechVoice *iface, From b739aee0d7f2477c8931acea064a17ae23947f04 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 1 Mar 2024 20:44:43 -0500 Subject: [PATCH 195/413] sapi: Implement ISpeechVoice::{get/putref}_Voice. (cherry picked from commit 43220a6063328bc445a346e5f6849731aafcaf88) CW-Bug-Id: #22520 --- dlls/sapi/tests/tts.c | 22 +++++++++++++++++++++- dlls/sapi/tts.c | 26 ++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index a5c950aa80b..38c69e6144a 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -424,7 +424,7 @@ static void test_spvoice(void) IUnknown *dummy; ISpMMSysAudio *audio_out; ISpObjectTokenCategory *token_cat; - ISpObjectToken *token; + ISpObjectToken *token, *token2; WCHAR *token_id = NULL, *default_token_id = NULL; ISpDataKey *attrs_key; LONG rate; @@ -435,6 +435,7 @@ static void test_spvoice(void) ISpeechVoice *speech_voice; ISpeechObjectTokens *speech_tokens; LONG count, volume_long; + ISpeechObjectToken *speech_token; BSTR req = NULL, opt = NULL; UINT info_count; ITypeInfo *typeinfo; @@ -724,6 +725,25 @@ static void test_spvoice(void) ok(hr == S_OK, "got %#lx.\n", hr); ok(volume_long == 80, "got %ld.\n", volume_long); + hr = ISpObjectToken_QueryInterface(token, &IID_ISpeechObjectToken, (void **)&speech_token); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = ISpeechVoice_putref_Voice(speech_voice, speech_token); + ok(hr == S_OK, "got %#lx.\n", hr); + ISpeechObjectToken_Release(speech_token); + + speech_token = (ISpeechObjectToken *)0xdeadbeef; + hr = ISpeechVoice_get_Voice(speech_voice, &speech_token); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(speech_token && speech_token != (ISpeechObjectToken *)0xdeadbeef, "got %p.\n", speech_token); + hr = ISpeechObjectToken_QueryInterface(speech_token, &IID_ISpObjectToken, (void **)&token2); + ok(hr == S_OK, "got %#lx.\n", hr); + token_id = NULL; + hr = ISpObjectToken_GetId(token2, &token_id); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(!wcscmp(token_id, test_token_id), "got %s.\n", wine_dbgstr_w(token_id)); + CoTaskMemFree(token_id); + ISpObjectToken_Release(token2); + hr = ISpeechVoice_Speak(speech_voice, NULL, SVSFPurgeBeforeSpeak, NULL); ok(hr == S_OK, "got %#lx.\n", hr); diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index 325edaf04fb..c474a118981 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -235,16 +235,34 @@ static HRESULT WINAPI speech_voice_get_Status(ISpeechVoice *iface, ISpeechVoiceS static HRESULT WINAPI speech_voice_get_Voice(ISpeechVoice *iface, ISpeechObjectToken **voice) { - FIXME("(%p, %p): stub.\n", iface, voice); + struct speech_voice *This = impl_from_ISpeechVoice(iface); + ISpObjectToken *token; + HRESULT hr; - return E_NOTIMPL; + TRACE("(%p, %p).\n", iface, voice); + + if (!voice) return E_POINTER; + if (FAILED(hr = ISpVoice_GetVoice(&This->ISpVoice_iface, &token))) + return hr; + hr = ISpObjectToken_QueryInterface(token, &IID_ISpeechObjectToken, (void **)voice); + ISpObjectToken_Release(token); + return hr; } static HRESULT WINAPI speech_voice_putref_Voice(ISpeechVoice *iface, ISpeechObjectToken *voice) { - FIXME("(%p, %p): stub.\n", iface, voice); + struct speech_voice *This = impl_from_ISpeechVoice(iface); + ISpObjectToken *token; + HRESULT hr; - return E_NOTIMPL; + TRACE("(%p, %p).\n", iface, voice); + + if (!voice) return E_INVALIDARG; + if (FAILED(hr = ISpeechObjectToken_QueryInterface(voice, &IID_ISpObjectToken, (void **)&token))) + return hr; + hr = ISpVoice_SetVoice(&This->ISpVoice_iface, token); + ISpObjectToken_Release(token); + return hr; } static HRESULT WINAPI speech_voice_get_AudioOutput(ISpeechVoice *iface, ISpeechObjectToken **output) From 0692979895bf4718bf00c8b1cbb4517d611d6fb5 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 5 Mar 2024 09:47:35 -0600 Subject: [PATCH 196/413] Revert "winegstreamer: Implement MFT_MESSAGE_COMMAND_DRAIN for video processor." This reverts commit c6fe011aa26d50c761c932265af9d4d506ce72b4. CW-Bug-Id: #22299 --- dlls/winegstreamer/video_processor.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/dlls/winegstreamer/video_processor.c b/dlls/winegstreamer/video_processor.c index 698ae498ec7..1cbb37dafc7 100644 --- a/dlls/winegstreamer/video_processor.c +++ b/dlls/winegstreamer/video_processor.c @@ -513,18 +513,7 @@ static HRESULT WINAPI video_processor_ProcessEvent(IMFTransform *iface, DWORD id static HRESULT WINAPI video_processor_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) { - struct video_processor *impl = impl_from_IMFTransform(iface); - - TRACE("iface %p, message %#x, param %p.\n", iface, message, (void *)param); - - if (!impl->wg_transform) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - if (message == MFT_MESSAGE_COMMAND_DRAIN) - return wg_transform_drain(impl->wg_transform); - - FIXME("Ignoring message %#x.\n", message); - + FIXME("iface %p, message %#x, param %#Ix stub!\n", iface, message, param); return S_OK; } From 5dd480aeba7150e38491af11b4932d09fd24af59 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 5 Mar 2024 09:47:36 -0600 Subject: [PATCH 197/413] Revert "winegstreamer: Implement MFT_MESSAGE_COMMAND_DRAIN for resampler." This reverts commit 73410b62a84a58ecee639600a6b3fe689b2aa513. CW-Bug-Id: #22299 --- dlls/winegstreamer/resampler.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/dlls/winegstreamer/resampler.c b/dlls/winegstreamer/resampler.c index 22fb273e84c..b5b62d58800 100644 --- a/dlls/winegstreamer/resampler.c +++ b/dlls/winegstreamer/resampler.c @@ -509,18 +509,7 @@ static HRESULT WINAPI transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFM static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) { - struct resampler *impl = impl_from_IMFTransform(iface); - - TRACE("iface %p, message %#x, param %p.\n", iface, message, (void *)param); - - if (!impl->wg_transform) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - if (message == MFT_MESSAGE_COMMAND_DRAIN) - return wg_transform_drain(impl->wg_transform); - - FIXME("Ignoring message %#x.\n", message); - + FIXME("iface %p, message %#x, param %p stub!\n", iface, message, (void *)param); return S_OK; } From 43840532f4223b2c21643e2aa27ccd9ac6f84ce7 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 5 Mar 2024 09:47:37 -0600 Subject: [PATCH 198/413] Revert "winegstreamer: Implement MFT_MESSAGE_COMMAND_DRAIN for aac decoder." This reverts commit 3a3400aa407c2362a08095cb57ccb6e5aa68077d. CW-Bug-Id: #22299 --- dlls/winegstreamer/audio_decoder.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/dlls/winegstreamer/audio_decoder.c b/dlls/winegstreamer/audio_decoder.c index cd63f116cbf..771eae465fe 100644 --- a/dlls/winegstreamer/audio_decoder.c +++ b/dlls/winegstreamer/audio_decoder.c @@ -489,18 +489,7 @@ static HRESULT WINAPI transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFM static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) { - struct audio_decoder *decoder = impl_from_IMFTransform(iface); - - TRACE("iface %p, message %#x, param %p.\n", iface, message, (void *)param); - - if (!decoder->wg_transform) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - if (message == MFT_MESSAGE_COMMAND_DRAIN) - return wg_transform_drain(decoder->wg_transform); - - FIXME("Ignoring message %#x.\n", message); - + FIXME("iface %p, message %#x, param %p stub!\n", iface, message, (void *)param); return S_OK; } From 1ee1fd3379fb47b26c034f5eaf2f303b01a23d5a Mon Sep 17 00:00:00 2001 From: Georg Lehmann Date: Sat, 3 Feb 2024 17:23:52 +0100 Subject: [PATCH 199/413] winevulkan: Prepare for VK_KHR_calibrated_timestamps. (cherry picked from commit db03d2be88d80195635188cb51507f4c2d9c2b13) --- dlls/winevulkan/vulkan.c | 65 +++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 27cf80ee907..1f15aadc294 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -1673,17 +1673,19 @@ static inline uint64_t convert_timestamp(VkTimeDomainEXT host_domain, VkTimeDoma return value; } -VkResult wine_vkGetCalibratedTimestampsEXT(VkDevice handle, uint32_t timestamp_count, - const VkCalibratedTimestampInfoEXT *timestamp_infos, - uint64_t *timestamps, uint64_t *max_deviation) +static VkResult wine_vk_get_timestamps(struct wine_device *device, uint32_t timestamp_count, + const VkCalibratedTimestampInfoEXT *timestamp_infos, + uint64_t *timestamps, uint64_t *max_deviation, + VkResult (*get_timestamps)(VkDevice, uint32_t, const VkCalibratedTimestampInfoEXT *, uint64_t *, uint64_t *)) { - struct wine_device *device = wine_device_from_handle(handle); VkCalibratedTimestampInfoEXT* host_timestamp_infos; unsigned int i; VkResult res; - TRACE("%p, %u, %p, %p, %p\n", device, timestamp_count, timestamp_infos, timestamps, max_deviation); - if (!(host_timestamp_infos = malloc(sizeof(VkCalibratedTimestampInfoEXT) * timestamp_count))) + if (timestamp_count == 0) + return VK_SUCCESS; + + if (!(host_timestamp_infos = calloc(sizeof(VkCalibratedTimestampInfoEXT), timestamp_count))) return VK_ERROR_OUT_OF_HOST_MEMORY; for (i = 0; i < timestamp_count; i++) @@ -1693,24 +1695,23 @@ VkResult wine_vkGetCalibratedTimestampsEXT(VkDevice handle, uint32_t timestamp_c host_timestamp_infos[i].timeDomain = map_to_host_time_domain(timestamp_infos[i].timeDomain); } - res = device->funcs.p_vkGetCalibratedTimestampsEXT(device->host_device, timestamp_count, host_timestamp_infos, - timestamps, max_deviation); - if (res != VK_SUCCESS) - return res; - - for (i = 0; i < timestamp_count; i++) - timestamps[i] = convert_timestamp(host_timestamp_infos[i].timeDomain, timestamp_infos[i].timeDomain, timestamps[i]); + res = get_timestamps(device->host_device, timestamp_count, host_timestamp_infos, timestamps, max_deviation); + if (res == VK_SUCCESS) + { + for (i = 0; i < timestamp_count; i++) + timestamps[i] = convert_timestamp(host_timestamp_infos[i].timeDomain, timestamp_infos[i].timeDomain, timestamps[i]); + } free(host_timestamp_infos); return res; } -VkResult wine_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT(VkPhysicalDevice handle, - uint32_t *time_domain_count, - VkTimeDomainEXT *time_domains) +static VkResult wine_vk_get_time_domains(struct wine_phys_dev *phys_dev, + uint32_t *time_domain_count, + VkTimeDomainEXT *time_domains, + VkResult (*get_domains)(VkPhysicalDevice, uint32_t *, VkTimeDomainEXT *)) { - struct wine_phys_dev *phys_dev = wine_phys_dev_from_handle(handle); BOOL supports_device = FALSE, supports_monotonic = FALSE, supports_monotonic_raw = FALSE; const VkTimeDomainEXT performance_counter_domain = get_performance_counter_time_domain(); VkTimeDomainEXT *host_time_domains; @@ -1721,16 +1722,14 @@ VkResult wine_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT(VkPhysicalDevice ha VkResult res; /* Find out the time domains supported on the host */ - res = phys_dev->instance->funcs.p_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT(phys_dev->host_physical_device, - &host_time_domain_count, NULL); + res = get_domains(phys_dev->host_physical_device, &host_time_domain_count, NULL); if (res != VK_SUCCESS) return res; if (!(host_time_domains = malloc(sizeof(VkTimeDomainEXT) * host_time_domain_count))) return VK_ERROR_OUT_OF_HOST_MEMORY; - res = phys_dev->instance->funcs.p_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT(phys_dev->host_physical_device, - &host_time_domain_count, host_time_domains); + res = get_domains(phys_dev->host_physical_device, &host_time_domain_count, host_time_domains); if (res != VK_SUCCESS) { free(host_time_domains); @@ -1780,6 +1779,30 @@ VkResult wine_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT(VkPhysicalDevice ha return res; } +VkResult wine_vkGetCalibratedTimestampsEXT(VkDevice handle, uint32_t timestamp_count, + const VkCalibratedTimestampInfoEXT *timestamp_infos, + uint64_t *timestamps, uint64_t *max_deviation) +{ + struct wine_device *device = wine_device_from_handle(handle); + + TRACE("%p, %u, %p, %p, %p\n", device, timestamp_count, timestamp_infos, timestamps, max_deviation); + + return wine_vk_get_timestamps(device, timestamp_count, timestamp_infos, timestamps, max_deviation, + device->funcs.p_vkGetCalibratedTimestampsEXT); +} + +VkResult wine_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT(VkPhysicalDevice handle, + uint32_t *time_domain_count, + VkTimeDomainEXT *time_domains) +{ + struct wine_phys_dev *phys_dev = wine_phys_dev_from_handle(handle); + + TRACE("%p, %p, %p\n", phys_dev, time_domain_count, time_domains); + + return wine_vk_get_time_domains(phys_dev, time_domain_count, time_domains, + phys_dev->instance->funcs.p_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT); +} + static inline void wine_vk_normalize_semaphore_handle_types_win(VkExternalSemaphoreHandleTypeFlags *types) { *types &= From e732b992cd020cdfd4e351f3a193aa0a4b3c659a Mon Sep 17 00:00:00 2001 From: Georg Lehmann Date: Sat, 3 Feb 2024 17:34:27 +0100 Subject: [PATCH 200/413] winevulkan: Update to VK spec version 1.3.277. (cherry picked from commit ea890c4733db5119fdb63b900bb7e5c8d04b5245) --- dlls/winevulkan/make_vulkan | 5 ++++- dlls/winevulkan/vulkan.c | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index f8ac5976efe..4f5df2fc697 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -64,7 +64,7 @@ from enum import Enum LOGGER = logging.Logger("vulkan") LOGGER.addHandler(logging.StreamHandler()) -VK_XML_VERSION = "1.3.272" +VK_XML_VERSION = "1.3.277" WINE_VK_VERSION = (1, 3) # Filenames to create. @@ -101,6 +101,7 @@ UNSUPPORTED_EXTENSIONS = [ "VK_KHR_external_fence_win32", # Relates to external_semaphore and needs type conversions in bitflags. "VK_KHR_shared_presentable_image", # Needs WSI work. + "VK_KHR_video_encode_queue", "VK_KHR_video_queue", # TODO Video extensions use separate headers + xml "VK_NV_external_memory_rdma", # Needs shared resources work. @@ -278,6 +279,7 @@ MANUAL_UNIX_THUNKS = { "vkFreeCommandBuffers", "vkFreeMemory", "vkGetCalibratedTimestampsEXT", + "vkGetCalibratedTimestampsKHR", "vkGetDeviceProcAddr", "vkGetMemoryWin32HandleKHR", "vkGetMemoryWin32HandlePropertiesKHR", @@ -285,6 +287,7 @@ MANUAL_UNIX_THUNKS = { "vkGetDeviceQueue2", "vkGetInstanceProcAddr", "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT", + "vkGetPhysicalDeviceCalibrateableTimeDomainsKHR", "vkGetPhysicalDeviceExternalBufferProperties", "vkGetPhysicalDeviceExternalBufferPropertiesKHR", "vkGetPhysicalDeviceExternalFenceProperties", diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 1f15aadc294..885af7ae970 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -1791,6 +1791,18 @@ VkResult wine_vkGetCalibratedTimestampsEXT(VkDevice handle, uint32_t timestamp_c device->funcs.p_vkGetCalibratedTimestampsEXT); } +VkResult wine_vkGetCalibratedTimestampsKHR(VkDevice handle, uint32_t timestamp_count, + const VkCalibratedTimestampInfoKHR *timestamp_infos, + uint64_t *timestamps, uint64_t *max_deviation) +{ + struct wine_device *device = wine_device_from_handle(handle); + + TRACE("%p, %u, %p, %p, %p\n", device, timestamp_count, timestamp_infos, timestamps, max_deviation); + + return wine_vk_get_timestamps(device, timestamp_count, timestamp_infos, timestamps, max_deviation, + device->funcs.p_vkGetCalibratedTimestampsKHR); +} + VkResult wine_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT(VkPhysicalDevice handle, uint32_t *time_domain_count, VkTimeDomainEXT *time_domains) @@ -1803,6 +1815,18 @@ VkResult wine_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT(VkPhysicalDevice ha phys_dev->instance->funcs.p_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT); } +VkResult wine_vkGetPhysicalDeviceCalibrateableTimeDomainsKHR(VkPhysicalDevice handle, + uint32_t *time_domain_count, + VkTimeDomainKHR *time_domains) +{ + struct wine_phys_dev *phys_dev = wine_phys_dev_from_handle(handle); + + TRACE("%p, %p, %p\n", phys_dev, time_domain_count, time_domains); + + return wine_vk_get_time_domains(phys_dev, time_domain_count, time_domains, + phys_dev->instance->funcs.p_vkGetPhysicalDeviceCalibrateableTimeDomainsKHR); +} + static inline void wine_vk_normalize_semaphore_handle_types_win(VkExternalSemaphoreHandleTypeFlags *types) { *types &= From e236b1f12ae98ea50bd1792272e327c28ba8ddd4 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Tue, 5 Mar 2024 15:59:34 +0200 Subject: [PATCH 201/413] Update vk.xml to 1.3.277. --- dlls/winevulkan/vk.xml | 1757 ++++++++++++++++++++++++++++------------ 1 file changed, 1235 insertions(+), 522 deletions(-) diff --git a/dlls/winevulkan/vk.xml b/dlls/winevulkan/vk.xml index a696de6f012..fb92b7c2373 100644 --- a/dlls/winevulkan/vk.xml +++ b/dlls/winevulkan/vk.xml @@ -1,7 +1,7 @@ -Copyright 2015-2023 The Khronos Group Inc. +Copyright 2015-2024 The Khronos Group Inc. SPDX-License-Identifier: Apache-2.0 OR MIT @@ -175,11 +175,11 @@ branch of the member gitlab server. #define VKSC_API_VERSION_1_0 VK_MAKE_API_VERSION(VKSC_API_VARIANT, 1, 0, 0)// Patch version should always be set to 0 // Version of this file -#define VK_HEADER_VERSION 267 +#define VK_HEADER_VERSION 277 // Complete version of this file #define VK_HEADER_VERSION_COMPLETE VK_MAKE_API_VERSION(0, 1, 3, VK_HEADER_VERSION) // Version of this file -#define VK_HEADER_VERSION 13 +#define VK_HEADER_VERSION 14 // Complete version of this file #define VK_HEADER_VERSION_COMPLETE VK_MAKE_API_VERSION(VKSC_API_VARIANT, 1, 0, VK_HEADER_VERSION) @@ -482,6 +482,7 @@ typedef void* MTLSharedEvent_id; typedef VkFlags VkPresentScalingFlagsEXT; typedef VkFlags VkPresentGravityFlagsEXT; typedef VkFlags VkShaderCreateFlagsEXT; + typedef VkFlags64 VkPhysicalDeviceSchedulingControlsFlagsARM; Video Core extension typedef VkFlags VkVideoCodecOperationFlagsKHR; @@ -512,16 +513,16 @@ typedef void* MTLSharedEvent_id; typedef VkFlags VkVideoComponentBitDepthFlagsKHR; Video Encode H.264 extension - typedef VkFlags VkVideoEncodeH264CapabilityFlagsEXT; - typedef VkFlags VkVideoEncodeH264StdFlagsEXT; - typedef VkFlags VkVideoEncodeH264RateControlFlagsEXT; + typedef VkFlags VkVideoEncodeH264CapabilityFlagsKHR; + typedef VkFlags VkVideoEncodeH264StdFlagsKHR; + typedef VkFlags VkVideoEncodeH264RateControlFlagsKHR; Video Encode H.265 extension - typedef VkFlags VkVideoEncodeH265CapabilityFlagsEXT; - typedef VkFlags VkVideoEncodeH265StdFlagsEXT; - typedef VkFlags VkVideoEncodeH265RateControlFlagsEXT; - typedef VkFlags VkVideoEncodeH265CtbSizeFlagsEXT; - typedef VkFlags VkVideoEncodeH265TransformBlockSizeFlagsEXT; + typedef VkFlags VkVideoEncodeH265CapabilityFlagsKHR; + typedef VkFlags VkVideoEncodeH265StdFlagsKHR; + typedef VkFlags VkVideoEncodeH265RateControlFlagsKHR; + typedef VkFlags VkVideoEncodeH265CtbSizeFlagsKHR; + typedef VkFlags VkVideoEncodeH265TransformBlockSizeFlagsKHR; Types which can be void pointers or class pointers, selected at compile time VK_DEFINE_HANDLE(VkInstance) @@ -685,7 +686,8 @@ typedef void* MTLSharedEvent_id; - + + @@ -730,7 +732,8 @@ typedef void* MTLSharedEvent_id; - + + @@ -865,8 +868,10 @@ typedef void* MTLSharedEvent_id; + + Enumerated types in the header, but not used by the API @@ -903,16 +908,16 @@ typedef void* MTLSharedEvent_id; Video H.264 Encode extensions - - - + + + Video H.265 Encode extensions - - - - - + + + + + The PFN_vk*Function types are used by VkAllocationCallbacks below typedef void (VKAPI_PTR *PFN_vkInternalAllocationNotification)( @@ -1453,7 +1458,7 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext - VkPipelineCreateFlags flagsPipeline creation flags + VkPipelineCreateFlags flagsPipeline creation flags VkPipelineShaderStageCreateInfo stage VkPipelineLayout layoutInterface layout of the pipeline VkPipeline basePipelineHandleIf VK_PIPELINE_CREATE_DERIVATIVE_BIT is set and this value is nonzero, it specifies the handle of the base pipeline this is a derivative of @@ -1592,7 +1597,7 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext - VkPipelineCreateFlags flagsPipeline creation flags + VkPipelineCreateFlags flagsPipeline creation flags uint32_t stageCount const VkPipelineShaderStageCreateInfo* pStagesOne entry for each active shader stage const VkPipelineShaderStageCreateInfo* pStagesOne entry for each active shader stage @@ -1656,7 +1661,7 @@ typedef void* MTLSharedEvent_id; uint32_t offsetStart of the range, in bytes uint32_t sizeSize of the range, in bytes - + VkStructureType sType const void* pNext VkPipelineLayoutCreateFlags flags @@ -1859,7 +1864,7 @@ typedef void* MTLSharedEvent_id; VkBool32 residencyStandard2DBlockShapeSparse resources support: GPU will access all 2D (single sample) sparse resources using the standard sparse image block shapes (based on pixel format) VkBool32 residencyStandard2DMultisampleBlockShapeSparse resources support: GPU will access all 2D (multisample) sparse resources using the standard sparse image block shapes (based on pixel format) VkBool32 residencyStandard3DBlockShapeSparse resources support: GPU will access all 3D sparse resources using the standard sparse image block shapes (based on pixel format) - VkBool32 residencyAlignedMipSizeSparse resources support: Images with mip level dimensions that are NOT a multiple of the sparse image block dimensions will be placed in the mip tail + VkBool32 residencyAlignedMipSizeSparse resources support: Images with mip level dimensions that are NOT a multiple of the sparse image block dimensions will be placed in the mip tail VkBool32 residencyNonResidentStrictSparse resources support: GPU can consistently access non-resident regions of a resource, all reads return as if data is 0, writes are discarded @@ -2233,6 +2238,19 @@ typedef void* MTLSharedEvent_id; uint32_t disabledValidationFeatureCountNumber of validation features to disable const VkValidationFeatureDisableEXT* pDisabledValidationFeaturesValidation features to disable + + VkStructureType sTypeMust be VK_STRUCTURE_TYPE_LAYER_SETTINGS_CREATE_INFO_EXT + const void* pNext + uint32_t settingCountNumber of settings to configure + const VkLayerSettingEXT* pSettingsValidation features to enable + + + const char* pLayerName + const char* pSettingName + VkLayerSettingTypeEXT typeThe type of the object + uint32_t valueCountNumber of values of the setting + const void* pValuesValues to pass for a setting + VkStructureType sType const void* pNext @@ -2626,7 +2644,7 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext VkBufferCreateFlags flags - VkBufferUsageFlags usage + VkBufferUsageFlags usage VkExternalMemoryHandleTypeFlagBits handleType @@ -3701,6 +3719,18 @@ typedef void* MTLSharedEvent_id; VkBool32 nonStrictSinglePixelWideLinesUseParallelogram VkBool32 nonStrictWideLinesUseParallelogram + + VkStructureType sType + void* pNext + VkBool32 maintenance6 + + + VkStructureType sType + void* pNext + VkBool32 blockTexelViewCompatibleMultipleLayers + uint32_t maxCombinedImageSamplerDescriptorCount + VkBool32 fragmentShadingRateClampCombinerInputs + VkStructureType sType const void* pNext @@ -3853,7 +3883,7 @@ typedef void* MTLSharedEvent_id; VkDebugUtilsMessengerCallbackDataFlagsEXT flags const char* pMessageIdName int32_t messageIdNumber - const char* pMessage + const char* pMessage uint32_t queueLabelCount const VkDebugUtilsLabelEXT* pQueueLabels uint32_t cmdBufLabelCount @@ -3913,11 +3943,12 @@ typedef void* MTLSharedEvent_id; VkBool32 fullyCoveredFragmentShaderInputVariabletrue if the implementation supports the FullyCoveredEXT SPIR-V builtin fragment shader input variable VkBool32 conservativeRasterizationPostDepthCoveragetrue if the implementation supports both conservative rasterization and post depth coverage sample coverage mask - - VkStructureType sType + + VkStructureType sType const void* pNext - VkTimeDomainEXT timeDomain + VkTimeDomainKHR timeDomain + VkStructureType sType void* pNext @@ -4142,21 +4173,29 @@ typedef void* MTLSharedEvent_id; uint64_t value - + uint32_t binding uint32_t divisor - - VkStructureType sType + + + VkStructureType sType const void* pNext uint32_t vertexBindingDivisorCount - const VkVertexInputBindingDivisorDescriptionEXT* pVertexBindingDivisors + const VkVertexInputBindingDivisorDescriptionKHR* pVertexBindingDivisors + VkStructureType sType void* pNext uint32_t maxVertexAttribDivisormax value of vertex attribute divisor + + VkStructureType sType + void* pNext + uint32_t maxVertexAttribDivisormax value of vertex attribute divisor + VkBool32 supportsNonZeroFirstInstance + VkStructureType sType void* pNext @@ -4269,12 +4308,13 @@ typedef void* MTLSharedEvent_id; VkBool32 shaderImageFloat32AtomicMinMax VkBool32 sparseImageFloat32AtomicMinMax - - VkStructureType sType + + VkStructureType sType void* pNext VkBool32 vertexAttributeInstanceRateDivisor VkBool32 vertexAttributeInstanceRateZeroDivisor + VkStructureType sType void* pNext @@ -4545,7 +4585,7 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext - VkPipelineCreateFlags flagsPipeline creation flags + VkPipelineCreateFlags flagsPipeline creation flags uint32_t stageCount const VkPipelineShaderStageCreateInfo* pStagesOne entry for each active shader stage uint32_t groupCount @@ -4558,7 +4598,7 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext - VkPipelineCreateFlags flagsPipeline creation flags + VkPipelineCreateFlags flagsPipeline creation flags uint32_t stageCount const VkPipelineShaderStageCreateInfo* pStagesOne entry for each active shader stage uint32_t groupCount @@ -4977,7 +5017,7 @@ typedef void* MTLSharedEvent_id; void* pNext VkShaderStageFlags cooperativeMatrixSupportedStages - + VkStructureType sType void* pNext uint32_t MSize @@ -5192,11 +5232,12 @@ typedef void* MTLSharedEvent_id; VkBool32 shaderSubgroupClock VkBool32 shaderDeviceClock - - VkStructureType sType + + VkStructureType sType void* pNext VkBool32 indexTypeUint8 + VkStructureType sType void* pNext @@ -5362,8 +5403,8 @@ typedef void* MTLSharedEvent_id; VkDeviceMemory memory - - VkStructureType sType + + VkStructureType sType void* pNext VkBool32 rectangularLines VkBool32 bresenhamLines @@ -5372,19 +5413,22 @@ typedef void* MTLSharedEvent_id; VkBool32 stippledBresenhamLines VkBool32 stippledSmoothLines - - VkStructureType sType + + + VkStructureType sType void* pNext uint32_t lineSubPixelPrecisionBits - - VkStructureType sType - const void* pNext - VkLineRasterizationModeEXT lineRasterizationMode + + + VkStructureType sType + const void* pNext + VkLineRasterizationModeKHR lineRasterizationMode VkBool32 stippledLineEnable uint32_t lineStippleFactor uint16_t lineStipplePattern + VkStructureType sType void* pNext @@ -5994,7 +6038,12 @@ typedef void* MTLSharedEvent_id; void*pNext VkBool32 clustercullingShader VkBool32 multiviewClusterCullingShader - + + + VkStructureType sType + void*pNext + VkBool32 clusterShadingRate + VkStructureType sType const void* pNext @@ -6183,7 +6232,7 @@ typedef void* MTLSharedEvent_id; VkFragmentShadingRateNV shadingRate VkFragmentShadingRateCombinerOpKHR combinerOps[2] - + VkStructureType sType const void* pNext VkDeviceSize accelerationStructureSize @@ -6674,6 +6723,18 @@ typedef void* MTLSharedEvent_id; const VkVideoReferenceSlotInfoKHR* pSetupReferenceSlot uint32_t referenceSlotCount const VkVideoReferenceSlotInfoKHR* pReferenceSlots + + + VkStructureType sType + void* pNext + VkBool32 videoMaintenance1 + + + VkStructureType sType + const void* pNext + VkQueryPool queryPool + uint32_t firstQuery + uint32_t queryCount Video Decode Codec Standard specific structures #include "vk_video/vulkan_video_codec_h264std.h" @@ -6805,6 +6866,44 @@ typedef void* MTLSharedEvent_id; const void* pNext const StdVideoDecodeH265ReferenceInfo* pStdReferenceInfo + #include "vk_video/vulkan_video_codec_av1std.h" + + + + #include "vk_video/vulkan_video_codec_av1std_decode.h" + + + + VkStructureType sType + const void* pNext + StdVideoAV1Profile stdProfile + VkBool32 filmGrainSupport + + + VkStructureType sType + void* pNext + StdVideoAV1Level maxLevel + + + VkStructureType sType + const void* pNext + const StdVideoAV1SequenceHeader* pStdSequenceHeader + + + VkStructureType sType + const void* pNext + const StdVideoDecodeAV1PictureInfo* pStdPictureInfo + int32_t referenceNameSlotIndices[VK_MAX_VIDEO_AV1_REFERENCES_PER_FRAME_KHR] + uint32_t frameHeaderOffset + uint32_t tileCount + const uint32_t* pTileOffsets + const uint32_t* pTileSizes + + + VkStructureType sType + const void* pNext + const StdVideoDecodeAV1ReferenceInfo* pStdReferenceInfo + VkStructureType sType const void* pNext @@ -6857,7 +6956,7 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext - VkVideoCodingControlFlagsKHR flags + VkVideoCodingControlFlagsKHR flags VkStructureType sType @@ -6930,10 +7029,10 @@ typedef void* MTLSharedEvent_id; VkExtent2D encodeInputPictureGranularity VkVideoEncodeFeedbackFlagsKHR supportedEncodeFeedbackFlags - - VkStructureType sType + + VkStructureType sType void* pNext - VkVideoEncodeH264CapabilityFlagsEXT flags + VkVideoEncodeH264CapabilityFlagsKHR flags StdVideoH264LevelIdc maxLevelIdc uint32_t maxSliceCount uint32_t maxPPictureL0ReferenceCount @@ -6945,17 +7044,17 @@ typedef void* MTLSharedEvent_id; int32_t maxQp VkBool32 prefersGopRemainingFrames VkBool32 requiresGopRemainingFrames - VkVideoEncodeH264StdFlagsEXT stdSyntaxFlags + VkVideoEncodeH264StdFlagsKHR stdSyntaxFlags - - VkStructureType sType + + VkStructureType sType void* pNext - VkVideoEncodeH264RateControlFlagsEXT preferredRateControlFlags + VkVideoEncodeH264RateControlFlagsKHR preferredRateControlFlags uint32_t preferredGopFrameCount uint32_t preferredIdrPeriod uint32_t preferredConsecutiveBFrameCount uint32_t preferredTemporalLayerCount - VkVideoEncodeH264QpEXT preferredConstantQp + VkVideoEncodeH264QpKHR preferredConstantQp uint32_t preferredMaxL0ReferenceCount uint32_t preferredMaxL1ReferenceCount VkBool32 preferredStdEntropyCodingModeFlag @@ -6971,111 +7070,111 @@ typedef void* MTLSharedEvent_id; - - VkStructureType sType + + VkStructureType sType const void* pNext VkBool32 useMaxLevelIdc StdVideoH264LevelIdc maxLevelIdc - - VkStructureType sType + + VkStructureType sType const void* pNext uint32_t stdSPSCount const StdVideoH264SequenceParameterSet* pStdSPSs uint32_t stdPPSCount const StdVideoH264PictureParameterSet* pStdPPSsList of Picture Parameters associated with the spsStd, above - - VkStructureType sType + + VkStructureType sType const void* pNext uint32_t maxStdSPSCount uint32_t maxStdPPSCount - const VkVideoEncodeH264SessionParametersAddInfoEXT* pParametersAddInfo + const VkVideoEncodeH264SessionParametersAddInfoKHR* pParametersAddInfo - - VkStructureType sType + + VkStructureType sType const void* pNext VkBool32 writeStdSPS VkBool32 writeStdPPS uint32_t stdSPSId uint32_t stdPPSId - - VkStructureType sType + + VkStructureType sType void* pNext VkBool32 hasStdSPSOverrides VkBool32 hasStdPPSOverrides - - VkStructureType sType + + VkStructureType sType const void* pNext const StdVideoEncodeH264ReferenceInfo* pStdReferenceInfo - - VkStructureType sType + + VkStructureType sType const void* pNext uint32_t naluSliceEntryCount - const VkVideoEncodeH264NaluSliceInfoEXT* pNaluSliceEntries + const VkVideoEncodeH264NaluSliceInfoKHR* pNaluSliceEntries const StdVideoEncodeH264PictureInfo* pStdPictureInfo VkBool32 generatePrefixNalu - - VkStructureType sType + + VkStructureType sType const void* pNext StdVideoH264ProfileIdc stdProfileIdc - - VkStructureType sType + + VkStructureType sType const void* pNext int32_t constantQp const StdVideoEncodeH264SliceHeader* pStdSliceHeader - - VkStructureType sType + + VkStructureType sType const void* pNext - VkVideoEncodeH264RateControlFlagsEXT flags + VkVideoEncodeH264RateControlFlagsKHR flags uint32_t gopFrameCount uint32_t idrPeriod uint32_t consecutiveBFrameCount uint32_t temporalLayerCount - + int32_t qpI int32_t qpP int32_t qpB - + uint32_t frameISize uint32_t framePSize uint32_t frameBSize - - VkStructureType sType + + VkStructureType sType const void* pNext VkBool32 useGopRemainingFrames uint32_t gopRemainingI uint32_t gopRemainingP uint32_t gopRemainingB - - VkStructureType sType + + VkStructureType sType const void* pNext VkBool32 useMinQp - VkVideoEncodeH264QpEXT minQp + VkVideoEncodeH264QpKHR minQp VkBool32 useMaxQp - VkVideoEncodeH264QpEXT maxQp + VkVideoEncodeH264QpKHR maxQp VkBool32 useMaxFrameSize - VkVideoEncodeH264FrameSizeEXT maxFrameSize + VkVideoEncodeH264FrameSizeKHR maxFrameSize - - VkStructureType sType + + VkStructureType sType void* pNext - VkVideoEncodeH265CapabilityFlagsEXT flags + VkVideoEncodeH265CapabilityFlagsKHR flags StdVideoH265LevelIdc maxLevelIdc uint32_t maxSliceSegmentCount VkExtent2D maxTiles - VkVideoEncodeH265CtbSizeFlagsEXT ctbSizes - VkVideoEncodeH265TransformBlockSizeFlagsEXT transformBlockSizes + VkVideoEncodeH265CtbSizeFlagsKHR ctbSizes + VkVideoEncodeH265TransformBlockSizeFlagsKHR transformBlockSizes uint32_t maxPPictureL0ReferenceCount uint32_t maxBPictureL0ReferenceCount uint32_t maxL1ReferenceCount @@ -7085,17 +7184,17 @@ typedef void* MTLSharedEvent_id; int32_t maxQp VkBool32 prefersGopRemainingFrames VkBool32 requiresGopRemainingFrames - VkVideoEncodeH265StdFlagsEXT stdSyntaxFlags + VkVideoEncodeH265StdFlagsKHR stdSyntaxFlags - - VkStructureType sType + + VkStructureType sType void* pNext - VkVideoEncodeH265RateControlFlagsEXT preferredRateControlFlags + VkVideoEncodeH265RateControlFlagsKHR preferredRateControlFlags uint32_t preferredGopFrameCount uint32_t preferredIdrPeriod uint32_t preferredConsecutiveBFrameCount uint32_t preferredSubLayerCount - VkVideoEncodeH265QpEXT preferredConstantQp + VkVideoEncodeH265QpKHR preferredConstantQp uint32_t preferredMaxL0ReferenceCount uint32_t preferredMaxL1ReferenceCount @@ -7108,14 +7207,14 @@ typedef void* MTLSharedEvent_id; - - VkStructureType sType + + VkStructureType sType const void* pNext VkBool32 useMaxLevelIdc StdVideoH265LevelIdc maxLevelIdc - - VkStructureType sType + + VkStructureType sType const void* pNext uint32_t stdVPSCount const StdVideoH265VideoParameterSet* pStdVPSs @@ -7124,16 +7223,16 @@ typedef void* MTLSharedEvent_id; uint32_t stdPPSCount const StdVideoH265PictureParameterSet* pStdPPSsList of Picture Parameters associated with the spsStd, above - - VkStructureType sType + + VkStructureType sType const void* pNext uint32_t maxStdVPSCount uint32_t maxStdSPSCount uint32_t maxStdPPSCount - const VkVideoEncodeH265SessionParametersAddInfoEXT* pParametersAddInfo + const VkVideoEncodeH265SessionParametersAddInfoKHR* pParametersAddInfo - - VkStructureType sType + + VkStructureType sType const void* pNext VkBool32 writeStdVPS VkBool32 writeStdSPS @@ -7142,70 +7241,70 @@ typedef void* MTLSharedEvent_id; uint32_t stdSPSId uint32_t stdPPSId - - VkStructureType sType + + VkStructureType sType void* pNext VkBool32 hasStdVPSOverrides VkBool32 hasStdSPSOverrides VkBool32 hasStdPPSOverrides - - VkStructureType sType + + VkStructureType sType const void* pNext uint32_t naluSliceSegmentEntryCount - const VkVideoEncodeH265NaluSliceSegmentInfoEXT* pNaluSliceSegmentEntries + const VkVideoEncodeH265NaluSliceSegmentInfoKHR* pNaluSliceSegmentEntries const StdVideoEncodeH265PictureInfo* pStdPictureInfo - - VkStructureType sType + + VkStructureType sType const void* pNext int32_t constantQp const StdVideoEncodeH265SliceSegmentHeader* pStdSliceSegmentHeader - - VkStructureType sType + + VkStructureType sType const void* pNext - VkVideoEncodeH265RateControlFlagsEXT flags + VkVideoEncodeH265RateControlFlagsKHR flags uint32_t gopFrameCount uint32_t idrPeriod uint32_t consecutiveBFrameCount uint32_t subLayerCount - + int32_t qpI int32_t qpP int32_t qpB - + uint32_t frameISize uint32_t framePSize uint32_t frameBSize - - VkStructureType sType + + VkStructureType sType const void* pNext VkBool32 useGopRemainingFrames uint32_t gopRemainingI uint32_t gopRemainingP uint32_t gopRemainingB - - VkStructureType sType + + VkStructureType sType const void* pNext VkBool32 useMinQp - VkVideoEncodeH265QpEXT minQp + VkVideoEncodeH265QpKHR minQp VkBool32 useMaxQp - VkVideoEncodeH265QpEXT maxQp + VkVideoEncodeH265QpKHR maxQp VkBool32 useMaxFrameSize - VkVideoEncodeH265FrameSizeEXT maxFrameSize + VkVideoEncodeH265FrameSizeKHR maxFrameSize - - VkStructureType sType + + VkStructureType sType const void* pNext StdVideoH265ProfileIdc stdProfileIdc - - VkStructureType sType + + VkStructureType sType const void* pNext const StdVideoEncodeH265ReferenceInfo* pStdReferenceInfo @@ -7246,8 +7345,8 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext - size_t dataSize - const void* pData + size_t dataSize + const void* pData VkStructureType sType @@ -7332,7 +7431,7 @@ typedef void* MTLSharedEvent_id; VkStructureType sType void* pNext VkDeviceAddress address - VkBufferUsageFlags usage + VkBufferUsageFlags usage VkStructureType sType @@ -7545,7 +7644,7 @@ typedef void* MTLSharedEvent_id; const void* pNext zx_handle_t collectionToken - + VkStructureType sType void* pNext uint32_t memoryTypeBits @@ -7599,6 +7698,36 @@ typedef void* MTLSharedEvent_id; uint32_t minBufferCountForDedicatedSlack uint32_t minBufferCountForSharedSlack + VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkCudaModuleNV) + VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkCudaFunctionNV) + + VkStructureType sType + const void* pNext + size_t dataSize + const void* pData + + + VkStructureType sType + const void* pNext + VkCudaModuleNV module + const char* pName + + + VkStructureType sType + const void* pNext + VkCudaFunctionNV function + uint32_t gridDimX + uint32_t gridDimY + uint32_t gridDimZ + uint32_t blockDimX + uint32_t blockDimY + uint32_t blockDimZ + uint32_t sharedMemBytes + size_t paramCount + const void* const * pParams + size_t extraCount + const void* const * pExtras + VkStructureType sType void* pNext @@ -7998,7 +8127,7 @@ typedef void* MTLSharedEvent_id; VkMicromapEXT micromap - + VkStructureType sType void* pNext uint8_t pipelineIdentifier[VK_UUID_SIZE] @@ -8261,7 +8390,7 @@ typedef void* MTLSharedEvent_id; uint32_t vendorInfoCount VkDeviceSize vendorBinarySizeSpecified in bytes - + VkStructureType sType void* pNext char description[VK_MAX_DESCRIPTION_SIZE]Free-form description of the fault @@ -8438,7 +8567,7 @@ typedef void* MTLSharedEvent_id; VkStructureType sType - void* pNext + const void* pNext VkDirectDriverLoadingModeLUNARG mode uint32_t driverCount const VkDirectDriverLoadingInfoLUNARG* pDrivers @@ -8577,7 +8706,7 @@ typedef void* MTLSharedEvent_id; VkBool32 cooperativeMatrix VkBool32 cooperativeMatrixRobustBufferAccess - + VkStructureType sType void* pNext uint32_t MSize @@ -8612,7 +8741,7 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext - VkPipelineCreateFlags flags + VkPipelineCreateFlags flags uint32_t stageCount const VkPipelineShaderStageCreateInfo* pStages const VkPipelineLibraryCreateInfoKHR* pLibraryInfo @@ -8642,6 +8771,65 @@ typedef void* MTLSharedEvent_id; VkDeviceOrHostAddressConstAMDX infos uint64_t stride + + VkStructureType sType + const void* pNext + VkResult* pResult + + + VkStructureType sType + const void* pNext + VkShaderStageFlags stageFlags + VkPipelineLayout layout + uint32_t firstSet + uint32_t descriptorSetCount + const VkDescriptorSet* pDescriptorSets + uint32_t dynamicOffsetCount + const uint32_t* pDynamicOffsets + + + VkStructureType sType + const void* pNext + VkPipelineLayout layout + VkShaderStageFlags stageFlags + uint32_t offset + uint32_t size + const void* pValues + + + VkStructureType sType + const void* pNext + VkShaderStageFlags stageFlags + VkPipelineLayout layout + uint32_t set + uint32_t descriptorWriteCount + const VkWriteDescriptorSet* pDescriptorWrites + + + VkStructureType sType + const void* pNext + VkDescriptorUpdateTemplate descriptorUpdateTemplate + VkPipelineLayout layout + uint32_t set + const void* pData + + + VkStructureType sType + const void* pNext + VkShaderStageFlags stageFlags + VkPipelineLayout layout + uint32_t firstSet + uint32_t setCount + const uint32_t* pBufferIndices + const VkDeviceSize* pOffsets + + + VkStructureType sType + const void* pNext + VkShaderStageFlags stageFlags + VkPipelineLayout layout + uint32_t set + VkStructureType sType void* pNext @@ -8699,6 +8887,12 @@ typedef void* MTLSharedEvent_id; void* pNext VkLayeredDriverUnderlyingApiMSFT underlyingAPI + + VkStructureType sType + void* pNext + VkBool32 perStageDescriptorSet + VkBool32 dynamicPipelineLayout + VkStructureType sType void* pNext @@ -8707,7 +8901,7 @@ typedef void* MTLSharedEvent_id; VkStructureType sType void* pNext - VkBool32 nullColorAttachmentWithExternalFormatResolve + VkBool32 nullColorAttachmentWithExternalFormatResolve VkChromaLocation externalFormatResolveChromaOffsetX VkChromaLocation externalFormatResolveChromaOffsetY @@ -8738,7 +8932,8 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext - VkLatencyTimingsFrameReportNV* pTimings + uint32_t timingCount + VkLatencyTimingsFrameReportNV* pTimings VkStructureType sType @@ -8776,9 +8971,113 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext - uint32_t presentModeCount + uint32_t presentModeCount VkPresentModeKHR* pPresentModes + + VkStructureType sType + void* pNext + VkBool32 cudaKernelLaunchFeatures + + + VkStructureType sType + void* pNext + uint32_t computeCapabilityMinor + uint32_t computeCapabilityMajor + + + VkStructureType sType + void* pNext + uint32_t shaderCoreCount + + + VkStructureType sType + void* pNext + VkBool32 schedulingControls + + + VkStructureType sType + void* pNext + VkPhysicalDeviceSchedulingControlsFlagsARM schedulingControlsFlags + + + VkStructureType sType + void* pNext + VkBool32 relaxedLineRasterization + + + VkStructureType sType + void* pNext + VkBool32 renderPassStriped + + + VkStructureType sType + void* pNext + VkExtent2D renderPassStripeGranularity + uint32_t maxRenderPassStripes + + + VkStructureType sType + const void* pNext + VkRect2D stripeArea + + + VkStructureType sType + const void* pNext + uint32_t stripeInfoCount + const VkRenderPassStripeInfoARM* pStripeInfos + + + VkStructureType sType + const void* pNext + uint32_t stripeSemaphoreInfoCount + const VkSemaphoreSubmitInfo* pStripeSemaphoreInfos + + + VkStructureType sType + void* pNext + VkBool32 shaderMaximalReconvergence + + + VkStructureType sType + void* pNext + VkBool32 shaderSubgroupRotate + VkBool32 shaderSubgroupRotateClustered + + + VkStructureType sType + void* pNext + VkBool32 shaderExpectAssume + + + VkStructureType sType + void* pNext + VkBool32 shaderFloatControls2 + + + VkStructureType sType + void* pNext + VkBool32 dynamicRenderingLocalRead + + + VkStructureType sType + const void* pNext + uint32_t colorAttachmentCount + const uint32_t* pColorAttachmentLocations + + + VkStructureType sType + const void* pNext + uint32_t colorAttachmentCount + const uint32_t* pColorAttachmentInputIndices + const uint32_t* pDepthInputAttachmentIndex + const uint32_t* pStencilInputAttachmentIndex + + + VkStructureType sType + void* pNext + VkBool32 shaderQuadControl + @@ -8817,6 +9116,7 @@ typedef void* MTLSharedEvent_id; + @@ -9649,11 +9949,11 @@ typedef void* MTLSharedEvent_id; - - - - - + + + + + @@ -9745,6 +10045,16 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + @@ -10193,11 +10503,15 @@ typedef void* MTLSharedEvent_id; - - - - - + + + + + + + + + @@ -10436,6 +10750,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -10518,45 +10835,45 @@ typedef void* MTLSharedEvent_id; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -10634,58 +10951,58 @@ typedef void* MTLSharedEvent_id; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -11655,7 +11972,7 @@ typedef void* MTLSharedEvent_id; void vkCmdBindIndexBuffer VkCommandBuffer commandBuffer - VkBuffer buffer + VkBuffer buffer VkDeviceSize offset VkIndexType indexType @@ -12980,8 +13297,8 @@ typedef void* MTLSharedEvent_id; VkDevice device VkImage image int nativeFenceFd - VkSemaphore semaphore - VkFence fence + VkSemaphore semaphore + VkFence fence VkResult vkQueueSignalReleaseImageANDROID @@ -13007,19 +13324,21 @@ typedef void* MTLSharedEvent_id; VkBool32 localDimmingEnable - VkResult vkGetPhysicalDeviceCalibrateableTimeDomainsEXT + VkResult vkGetPhysicalDeviceCalibrateableTimeDomainsKHR VkPhysicalDevice physicalDevice uint32_t* pTimeDomainCount - VkTimeDomainEXT* pTimeDomains + VkTimeDomainKHR* pTimeDomains + - VkResult vkGetCalibratedTimestampsEXT + VkResult vkGetCalibratedTimestampsKHR VkDevice device uint32_t timestampCount - const VkCalibratedTimestampInfoEXT* pTimestampInfos + const VkCalibratedTimestampInfoKHR* pTimestampInfos uint64_t* pTimestamps uint64_t* pMaxDeviation + VkResult vkSetDebugUtilsObjectNameEXT VkDevice device @@ -13738,17 +14057,18 @@ typedef void* MTLSharedEvent_id; VkPipelineExecutableInternalRepresentationKHR* pInternalRepresentations - void vkCmdSetLineStippleEXT + void vkCmdSetLineStippleKHR VkCommandBuffer commandBuffer uint32_t lineStippleFactor uint16_t lineStipplePattern + VkResult vkGetFaultData VkDevice device VkFaultQueryBehavior faultQueryBehavior VkBool32* pUnrecordedFaults - uint32_t* pFaultCount + uint32_t* pFaultCount VkFaultData* pFaults @@ -13867,7 +14187,7 @@ typedef void* MTLSharedEvent_id; void vkCmdBindIndexBuffer2KHR VkCommandBuffer commandBuffer - VkBuffer buffer + VkBuffer buffer VkDeviceSize offset VkDeviceSize size VkIndexType indexType @@ -14596,6 +14916,44 @@ typedef void* MTLSharedEvent_id; VkBufferCollectionFUCHSIA collection VkBufferCollectionPropertiesFUCHSIA* pProperties + + VkResult vkCreateCudaModuleNV + VkDevice device + const VkCudaModuleCreateInfoNV* pCreateInfo + const VkAllocationCallbacks* pAllocator + VkCudaModuleNV* pModule + + + VkResult vkGetCudaModuleCacheNV + VkDevice device + VkCudaModuleNV module + size_t* pCacheSize + void* pCacheData + + + VkResult vkCreateCudaFunctionNV + VkDevice device + const VkCudaFunctionCreateInfoNV* pCreateInfo + const VkAllocationCallbacks* pAllocator + VkCudaFunctionNV* pFunction + + + void vkDestroyCudaModuleNV + VkDevice device + VkCudaModuleNV module + const VkAllocationCallbacks* pAllocator + + + void vkDestroyCudaFunctionNV + VkDevice device + VkCudaFunctionNV function + const VkAllocationCallbacks* pAllocator + + + void vkCmdCudaLaunchKernelNV + VkCommandBuffer commandBuffer + const VkCudaLaunchInfoNV* pLaunchInfo + void vkCmdBeginRendering VkCommandBuffer commandBuffer @@ -14833,7 +15191,7 @@ typedef void* MTLSharedEvent_id; void vkDestroyShaderEXT VkDevice device - VkShaderEXT shader + VkShaderEXT shader const VkAllocationCallbacks* pAllocator @@ -14907,6 +15265,36 @@ typedef void* MTLSharedEvent_id; VkDeviceAddress scratch VkDeviceAddress countInfo + + void vkCmdBindDescriptorSets2KHR + VkCommandBuffer commandBuffer + const VkBindDescriptorSetsInfoKHR* pBindDescriptorSetsInfo + + + void vkCmdPushConstants2KHR + VkCommandBuffer commandBuffer + const VkPushConstantsInfoKHR* pPushConstantsInfo + + + void vkCmdPushDescriptorSet2KHR + VkCommandBuffer commandBuffer + const VkPushDescriptorSetInfoKHR* pPushDescriptorSetInfo + + + void vkCmdPushDescriptorSetWithTemplate2KHR + VkCommandBuffer commandBuffer + const VkPushDescriptorSetWithTemplateInfoKHR* pPushDescriptorSetWithTemplateInfo + + + void vkCmdSetDescriptorBufferOffsets2EXT + VkCommandBuffer commandBuffer + const VkSetDescriptorBufferOffsetsInfoEXT* pSetDescriptorBufferOffsetsInfo + + + void vkCmdBindDescriptorBufferEmbeddedSamplers2EXT + VkCommandBuffer commandBuffer + const VkBindDescriptorBufferEmbeddedSamplersInfoEXT* pBindDescriptorBufferEmbeddedSamplersInfo + VkResult vkSetLatencySleepModeNV VkDevice device @@ -14929,7 +15317,6 @@ typedef void* MTLSharedEvent_id; void vkGetLatencyTimingsNV VkDevice device VkSwapchainKHR swapchain - uint32_t* pTimingCount VkGetLatencyMarkerInfoNV* pLatencyMarkerInfo @@ -14937,6 +15324,16 @@ typedef void* MTLSharedEvent_id; VkQueue queue const VkOutOfBandQueueTypeInfoNV* pQueueTypeInfo + + void vkCmdSetRenderingAttachmentLocationsKHR + VkCommandBuffer commandBuffer + const VkRenderingAttachmentLocationInfoKHR* pLocationInfo + + + void vkCmdSetRenderingInputAttachmentIndicesKHR + VkCommandBuffer commandBuffer + const VkRenderingInputAttachmentIndexInfoKHR* pLocationInfo + @@ -16773,7 +17170,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16950,101 +17347,101 @@ typedef void* MTLSharedEvent_id; - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -17348,9 +17745,9 @@ typedef void* MTLSharedEvent_id; - + - + @@ -17826,7 +18223,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17962,10 +18359,12 @@ typedef void* MTLSharedEvent_id; - + - - + + + + @@ -18332,10 +18731,18 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + + + + + + @@ -19058,7 +19465,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19070,7 +19477,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19078,13 +19485,13 @@ typedef void* MTLSharedEvent_id; - + - + @@ -19176,11 +19583,15 @@ typedef void* MTLSharedEvent_id; - + - + + + + + @@ -19203,7 +19614,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19246,13 +19657,13 @@ typedef void* MTLSharedEvent_id; - + - - - - - + + + + + @@ -19719,10 +20130,19 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + + + + + + + @@ -19739,10 +20159,12 @@ typedef void* MTLSharedEvent_id; - + - - + + + + @@ -19861,9 +20283,9 @@ typedef void* MTLSharedEvent_id; - + - + @@ -20031,14 +20453,14 @@ typedef void* MTLSharedEvent_id; - + - - - - + + + + @@ -20081,12 +20503,12 @@ typedef void* MTLSharedEvent_id; - + - - + + @@ -20363,9 +20785,9 @@ typedef void* MTLSharedEvent_id; - + - + @@ -20571,43 +20993,43 @@ typedef void* MTLSharedEvent_id; - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -20645,8 +21067,8 @@ typedef void* MTLSharedEvent_id; - - + + @@ -20661,47 +21083,71 @@ typedef void* MTLSharedEvent_id; - + - + - + - + - + - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -20717,7 +21163,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21134,9 +21580,9 @@ typedef void* MTLSharedEvent_id; - + - + @@ -21220,7 +21666,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21839,7 +22285,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21933,11 +22379,11 @@ typedef void* MTLSharedEvent_id; - + - - + + @@ -21961,10 +22407,11 @@ typedef void* MTLSharedEvent_id; - + + @@ -21972,6 +22419,7 @@ typedef void* MTLSharedEvent_id; + @@ -22061,16 +22509,28 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + + - + - - + + + + + + + + + + @@ -22128,10 +22588,20 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + + + + + + + + @@ -22230,10 +22700,12 @@ typedef void* MTLSharedEvent_id; - + - - + + + + @@ -22264,7 +22736,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22272,7 +22744,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22357,7 +22829,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22394,13 +22866,12 @@ typedef void* MTLSharedEvent_id; - + - @@ -22411,21 +22882,10 @@ typedef void* MTLSharedEvent_id; - - - - - - - - - - - @@ -22436,15 +22896,45 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -22683,8 +23173,8 @@ typedef void* MTLSharedEvent_id; - - + + @@ -22845,6 +23335,7 @@ typedef void* MTLSharedEvent_id; + @@ -22925,15 +23416,31 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + + + + + + + @@ -22984,7 +23491,7 @@ typedef void* MTLSharedEvent_id; - + @@ -23021,7 +23528,7 @@ typedef void* MTLSharedEvent_id; - + @@ -23121,10 +23628,14 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + + @@ -23183,9 +23694,9 @@ typedef void* MTLSharedEvent_id; - + - + @@ -23252,7 +23763,7 @@ typedef void* MTLSharedEvent_id; - + @@ -23268,10 +23779,22 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + + + + + + + + + + @@ -23286,19 +23809,26 @@ typedef void* MTLSharedEvent_id; - + - - - - + + + + + + + + + - + - - - + + + + + @@ -23307,7 +23837,7 @@ typedef void* MTLSharedEvent_id; - + @@ -23320,7 +23850,7 @@ typedef void* MTLSharedEvent_id; - + @@ -23333,7 +23863,7 @@ typedef void* MTLSharedEvent_id; - + @@ -23343,7 +23873,7 @@ typedef void* MTLSharedEvent_id; - + @@ -23374,16 +23904,25 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + + + + + - + - - + + + + @@ -23392,10 +23931,12 @@ typedef void* MTLSharedEvent_id; - + - - + + + + @@ -23437,19 +23978,31 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + - + - - + + + + + + + + + + + - + @@ -23497,22 +24050,59 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + + + - + - - + + + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -23534,7 +24124,91 @@ typedef void* MTLSharedEvent_id; - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -25156,6 +25830,30 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + + + + + + + + + + + + + + + @@ -25651,6 +26349,21 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + + + + + + From ce3c2ebf61a430e97999c9b02a77b96a63d5eb13 Mon Sep 17 00:00:00 2001 From: Jacek Caban Date: Tue, 20 Feb 2024 21:07:13 +0100 Subject: [PATCH 202/413] winevulkan: Update to VK spec version 1.3.278. The new spec uses length attribute for some static arrays. Change is_dynamic_array to account account for that. Eventually we could use the new information to improve conversion thunks. (cherry picked from commit f380e34a5e94371f691179711e6ce4697be0da61) --- dlls/winevulkan/make_vulkan | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 4f5df2fc697..e7b91b845e7 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -64,7 +64,7 @@ from enum import Enum LOGGER = logging.Logger("vulkan") LOGGER.addHandler(logging.StreamHandler()) -VK_XML_VERSION = "1.3.277" +VK_XML_VERSION = "1.3.278" WINE_VK_VERSION = (1, 3) # Filenames to create. @@ -126,6 +126,7 @@ UNSUPPORTED_EXTENSIONS = [ # winevulkan may nonetheless use, or extensions we want to generate headers for # but not expose to applications (useful for test commits) UNEXPOSED_EXTENSIONS = { + "VK_EXT_map_memory_placed", } # The Vulkan loader provides entry-points for core functionality and important @@ -1399,7 +1400,7 @@ class VkVariable(object): Vulkan uses this for dynamically sized arrays for which there is a 'count' parameter. """ - return self.dyn_array_len is not None + return self.dyn_array_len is not None and self.array_len is None def is_static_array(self): """ Returns if the member is an array. From 72123668b5f088c5cbea06d57408a6237711b622 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Wed, 6 Mar 2024 16:22:39 +0200 Subject: [PATCH 203/413] Update vk.xml to 1.3.278. --- dlls/winevulkan/vk.xml | 315 ++++++++++++++++++++++++++--------------- 1 file changed, 203 insertions(+), 112 deletions(-) diff --git a/dlls/winevulkan/vk.xml b/dlls/winevulkan/vk.xml index fb92b7c2373..c8464a2eae5 100644 --- a/dlls/winevulkan/vk.xml +++ b/dlls/winevulkan/vk.xml @@ -175,7 +175,7 @@ branch of the member gitlab server. #define VKSC_API_VERSION_1_0 VK_MAKE_API_VERSION(VKSC_API_VARIANT, 1, 0, 0)// Patch version should always be set to 0 // Version of this file -#define VK_HEADER_VERSION 277 +#define VK_HEADER_VERSION 278 // Complete version of this file #define VK_HEADER_VERSION_COMPLETE VK_MAKE_API_VERSION(0, 1, 3, VK_HEADER_VERSION) // Version of this file @@ -338,8 +338,8 @@ typedef void* MTLSharedEvent_id; typedef VkFlags VkCommandBufferResetFlags; typedef VkFlags VkCommandBufferUsageFlags; typedef VkFlags VkQueryPipelineStatisticFlags; - typedef VkFlags VkMemoryMapFlags; - typedef VkFlags VkMemoryUnmapFlagsKHR; + typedef VkFlags VkMemoryMapFlags; + typedef VkFlags VkMemoryUnmapFlagsKHR; typedef VkFlags VkImageAspectFlags; typedef VkFlags VkSparseMemoryBindFlags; typedef VkFlags VkSparseImageFormatFlags; @@ -502,7 +502,7 @@ typedef void* MTLSharedEvent_id; typedef VkFlags VkVideoDecodeH264PictureLayoutFlagsKHR; Video Encode Core extension - typedef VkFlags VkVideoEncodeFlagsKHR; + typedef VkFlags VkVideoEncodeFlagsKHR; typedef VkFlags VkVideoEncodeUsageFlagsKHR; typedef VkFlags VkVideoEncodeContentFlagsKHR; typedef VkFlags VkVideoEncodeCapabilityFlagsKHR; @@ -619,6 +619,7 @@ typedef void* MTLSharedEvent_id; + @@ -872,6 +873,7 @@ typedef void* MTLSharedEvent_id; + Enumerated types in the header, but not used by the API @@ -900,6 +902,7 @@ typedef void* MTLSharedEvent_id; Video H.265 Decode extensions Video Encode extensions + @@ -1042,20 +1045,20 @@ typedef void* MTLSharedEvent_id; uint32_t vendorID uint32_t deviceID VkPhysicalDeviceType deviceType - char deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE] + char deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE] uint8_t pipelineCacheUUID[VK_UUID_SIZE] VkPhysicalDeviceLimits limits VkPhysicalDeviceSparseProperties sparseProperties - char extensionName[VK_MAX_EXTENSION_NAME_SIZE]extension name - uint32_t specVersionversion of the extension specification implemented + char extensionName[VK_MAX_EXTENSION_NAME_SIZE]extension name + uint32_t specVersionversion of the extension specification implemented - char layerName[VK_MAX_EXTENSION_NAME_SIZE]layer name - uint32_t specVersionversion of the layer specification implemented - uint32_t implementationVersionbuild or release version of the layer's library - char description[VK_MAX_DESCRIPTION_SIZE]Free-form description of the layer + char layerName[VK_MAX_EXTENSION_NAME_SIZE]layer name + uint32_t specVersionversion of the layer specification implemented + uint32_t implementationVersionbuild or release version of the layer's library + char description[VK_MAX_DESCRIPTION_SIZE]Free-form description of the layer VkStructureType sType @@ -1111,10 +1114,10 @@ typedef void* MTLSharedEvent_id; VkExtent3D minImageTransferGranularityMinimum alignment requirement for image transfers - uint32_t memoryTypeCount - VkMemoryType memoryTypes[VK_MAX_MEMORY_TYPES] - uint32_t memoryHeapCount - VkMemoryHeap memoryHeaps[VK_MAX_MEMORY_HEAPS] + uint32_t memoryTypeCount + VkMemoryType memoryTypes[VK_MAX_MEMORY_TYPES] + uint32_t memoryHeapCount + VkMemoryHeap memoryHeaps[VK_MAX_MEMORY_HEAPS] VkStructureType sType @@ -2593,8 +2596,8 @@ typedef void* MTLSharedEvent_id; VkStructureType sType void* pNext VkDriverId driverID - char driverName[VK_MAX_DRIVER_NAME_SIZE] - char driverInfo[VK_MAX_DRIVER_INFO_SIZE] + char driverName[VK_MAX_DRIVER_NAME_SIZE] + char driverInfo[VK_MAX_DRIVER_INFO_SIZE] VkConformanceVersion conformanceVersion @@ -3025,7 +3028,7 @@ typedef void* MTLSharedEvent_id; VkStructureType sType void* pNext uint32_t physicalDeviceCount - VkPhysicalDevice physicalDevices[VK_MAX_DEVICE_GROUP_SIZE] + VkPhysicalDevice physicalDevices[VK_MAX_DEVICE_GROUP_SIZE] VkBool32 subsetAllocation @@ -3308,7 +3311,7 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext - VkSurfaceKHR surface + VkSurfaceKHR surface VkStructureType sType @@ -3839,11 +3842,11 @@ typedef void* MTLSharedEvent_id; VkBool32 globalPriorityQuery - + VkStructureType sType - void* pNext - uint32_t priorityCount - VkQueueGlobalPriorityKHR priorities[VK_MAX_GLOBAL_PRIORITY_SIZE_KHR] + void* pNext + uint32_t priorityCount + VkQueueGlobalPriorityKHR priorities[VK_MAX_GLOBAL_PRIORITY_SIZE_KHR] @@ -5118,9 +5121,9 @@ typedef void* MTLSharedEvent_id; VkStructureType sType void* pNext VkPerformanceCounterDescriptionFlagsKHR flags - char name[VK_MAX_DESCRIPTION_SIZE] - char category[VK_MAX_DESCRIPTION_SIZE] - char description[VK_MAX_DESCRIPTION_SIZE] + char name[VK_MAX_DESCRIPTION_SIZE] + char category[VK_MAX_DESCRIPTION_SIZE] + char description[VK_MAX_DESCRIPTION_SIZE] VkStructureType sType @@ -5189,7 +5192,7 @@ typedef void* MTLSharedEvent_id; VkBool32 valueBool const char* valueString - + VkPerformanceValueTypeINTEL type VkPerformanceValueDataINTEL data @@ -5295,10 +5298,10 @@ typedef void* MTLSharedEvent_id; VkStructureType sType void* pNext - VkShaderStageFlags stages - char name[VK_MAX_DESCRIPTION_SIZE] - char description[VK_MAX_DESCRIPTION_SIZE] - uint32_t subgroupSize + VkShaderStageFlags stages + char name[VK_MAX_DESCRIPTION_SIZE] + char description[VK_MAX_DESCRIPTION_SIZE] + uint32_t subgroupSize VkStructureType sType @@ -5314,19 +5317,19 @@ typedef void* MTLSharedEvent_id; VkStructureType sType - void* pNext - char name[VK_MAX_DESCRIPTION_SIZE] - char description[VK_MAX_DESCRIPTION_SIZE] + void* pNext + char name[VK_MAX_DESCRIPTION_SIZE] + char description[VK_MAX_DESCRIPTION_SIZE] VkPipelineExecutableStatisticFormatKHR format VkPipelineExecutableStatisticValueKHR value VkStructureType sType - void* pNext - char name[VK_MAX_DESCRIPTION_SIZE] - char description[VK_MAX_DESCRIPTION_SIZE] - VkBool32 isText - size_t dataSize + void* pNext + char name[VK_MAX_DESCRIPTION_SIZE] + char description[VK_MAX_DESCRIPTION_SIZE] + VkBool32 isText + size_t dataSize void* pData @@ -5372,7 +5375,7 @@ typedef void* MTLSharedEvent_id; - + VkStructureType sType void* pNext VkRenderPass renderPass @@ -5525,56 +5528,56 @@ typedef void* MTLSharedEvent_id; VkStructureType sType void* pNext VkDriverId driverID - char driverName[VK_MAX_DRIVER_NAME_SIZE] - char driverInfo[VK_MAX_DRIVER_INFO_SIZE] + char driverName[VK_MAX_DRIVER_NAME_SIZE] + char driverInfo[VK_MAX_DRIVER_INFO_SIZE] VkConformanceVersion conformanceVersion VkShaderFloatControlsIndependence denormBehaviorIndependence VkShaderFloatControlsIndependence roundingModeIndependence - VkBool32 shaderSignedZeroInfNanPreserveFloat16An implementation can preserve signed zero, nan, inf - VkBool32 shaderSignedZeroInfNanPreserveFloat32An implementation can preserve signed zero, nan, inf - VkBool32 shaderSignedZeroInfNanPreserveFloat64An implementation can preserve signed zero, nan, inf - VkBool32 shaderDenormPreserveFloat16An implementation can preserve denormals - VkBool32 shaderDenormPreserveFloat32An implementation can preserve denormals - VkBool32 shaderDenormPreserveFloat64An implementation can preserve denormals - VkBool32 shaderDenormFlushToZeroFloat16An implementation can flush to zero denormals - VkBool32 shaderDenormFlushToZeroFloat32An implementation can flush to zero denormals - VkBool32 shaderDenormFlushToZeroFloat64An implementation can flush to zero denormals - VkBool32 shaderRoundingModeRTEFloat16An implementation can support RTE - VkBool32 shaderRoundingModeRTEFloat32An implementation can support RTE - VkBool32 shaderRoundingModeRTEFloat64An implementation can support RTE - VkBool32 shaderRoundingModeRTZFloat16An implementation can support RTZ - VkBool32 shaderRoundingModeRTZFloat32An implementation can support RTZ - VkBool32 shaderRoundingModeRTZFloat64An implementation can support RTZ - uint32_t maxUpdateAfterBindDescriptorsInAllPools - VkBool32 shaderUniformBufferArrayNonUniformIndexingNative - VkBool32 shaderSampledImageArrayNonUniformIndexingNative - VkBool32 shaderStorageBufferArrayNonUniformIndexingNative - VkBool32 shaderStorageImageArrayNonUniformIndexingNative - VkBool32 shaderInputAttachmentArrayNonUniformIndexingNative - VkBool32 robustBufferAccessUpdateAfterBind - VkBool32 quadDivergentImplicitLod - uint32_t maxPerStageDescriptorUpdateAfterBindSamplers - uint32_t maxPerStageDescriptorUpdateAfterBindUniformBuffers - uint32_t maxPerStageDescriptorUpdateAfterBindStorageBuffers - uint32_t maxPerStageDescriptorUpdateAfterBindSampledImages - uint32_t maxPerStageDescriptorUpdateAfterBindStorageImages - uint32_t maxPerStageDescriptorUpdateAfterBindInputAttachments - uint32_t maxPerStageUpdateAfterBindResources - uint32_t maxDescriptorSetUpdateAfterBindSamplers - uint32_t maxDescriptorSetUpdateAfterBindUniformBuffers - uint32_t maxDescriptorSetUpdateAfterBindUniformBuffersDynamic - uint32_t maxDescriptorSetUpdateAfterBindStorageBuffers - uint32_t maxDescriptorSetUpdateAfterBindStorageBuffersDynamic - uint32_t maxDescriptorSetUpdateAfterBindSampledImages - uint32_t maxDescriptorSetUpdateAfterBindStorageImages - uint32_t maxDescriptorSetUpdateAfterBindInputAttachments - VkResolveModeFlags supportedDepthResolveModessupported depth resolve modes - VkResolveModeFlags supportedStencilResolveModessupported stencil resolve modes - VkBool32 independentResolveNonedepth and stencil resolve modes can be set independently if one of them is none - VkBool32 independentResolvedepth and stencil resolve modes can be set independently - VkBool32 filterMinmaxSingleComponentFormats - VkBool32 filterMinmaxImageComponentMapping - uint64_t maxTimelineSemaphoreValueDifference + VkBool32 shaderSignedZeroInfNanPreserveFloat16An implementation can preserve signed zero, nan, inf + VkBool32 shaderSignedZeroInfNanPreserveFloat32An implementation can preserve signed zero, nan, inf + VkBool32 shaderSignedZeroInfNanPreserveFloat64An implementation can preserve signed zero, nan, inf + VkBool32 shaderDenormPreserveFloat16An implementation can preserve denormals + VkBool32 shaderDenormPreserveFloat32An implementation can preserve denormals + VkBool32 shaderDenormPreserveFloat64An implementation can preserve denormals + VkBool32 shaderDenormFlushToZeroFloat16An implementation can flush to zero denormals + VkBool32 shaderDenormFlushToZeroFloat32An implementation can flush to zero denormals + VkBool32 shaderDenormFlushToZeroFloat64An implementation can flush to zero denormals + VkBool32 shaderRoundingModeRTEFloat16An implementation can support RTE + VkBool32 shaderRoundingModeRTEFloat32An implementation can support RTE + VkBool32 shaderRoundingModeRTEFloat64An implementation can support RTE + VkBool32 shaderRoundingModeRTZFloat16An implementation can support RTZ + VkBool32 shaderRoundingModeRTZFloat32An implementation can support RTZ + VkBool32 shaderRoundingModeRTZFloat64An implementation can support RTZ + uint32_t maxUpdateAfterBindDescriptorsInAllPools + VkBool32 shaderUniformBufferArrayNonUniformIndexingNative + VkBool32 shaderSampledImageArrayNonUniformIndexingNative + VkBool32 shaderStorageBufferArrayNonUniformIndexingNative + VkBool32 shaderStorageImageArrayNonUniformIndexingNative + VkBool32 shaderInputAttachmentArrayNonUniformIndexingNative + VkBool32 robustBufferAccessUpdateAfterBind + VkBool32 quadDivergentImplicitLod + uint32_t maxPerStageDescriptorUpdateAfterBindSamplers + uint32_t maxPerStageDescriptorUpdateAfterBindUniformBuffers + uint32_t maxPerStageDescriptorUpdateAfterBindStorageBuffers + uint32_t maxPerStageDescriptorUpdateAfterBindSampledImages + uint32_t maxPerStageDescriptorUpdateAfterBindStorageImages + uint32_t maxPerStageDescriptorUpdateAfterBindInputAttachments + uint32_t maxPerStageUpdateAfterBindResources + uint32_t maxDescriptorSetUpdateAfterBindSamplers + uint32_t maxDescriptorSetUpdateAfterBindUniformBuffers + uint32_t maxDescriptorSetUpdateAfterBindUniformBuffersDynamic + uint32_t maxDescriptorSetUpdateAfterBindStorageBuffers + uint32_t maxDescriptorSetUpdateAfterBindStorageBuffersDynamic + uint32_t maxDescriptorSetUpdateAfterBindSampledImages + uint32_t maxDescriptorSetUpdateAfterBindStorageImages + uint32_t maxDescriptorSetUpdateAfterBindInputAttachments + VkResolveModeFlags supportedDepthResolveModessupported depth resolve modes + VkResolveModeFlags supportedStencilResolveModessupported stencil resolve modes + VkBool32 independentResolveNonedepth and stencil resolve modes can be set independently if one of them is none + VkBool32 independentResolvedepth and stencil resolve modes can be set independently + VkBool32 filterMinmaxSingleComponentFormats + VkBool32 filterMinmaxImageComponentMapping + uint64_t maxTimelineSemaphoreValueDifference VkSampleCountFlags framebufferIntegerColorSampleCounts @@ -5670,12 +5673,12 @@ typedef void* MTLSharedEvent_id; VkStructureType sType - void* pNext - char name[VK_MAX_EXTENSION_NAME_SIZE] - char version[VK_MAX_EXTENSION_NAME_SIZE] - VkToolPurposeFlags purposes - char description[VK_MAX_DESCRIPTION_SIZE] - char layer[VK_MAX_EXTENSION_NAME_SIZE] + void* pNext + char name[VK_MAX_EXTENSION_NAME_SIZE] + char version[VK_MAX_EXTENSION_NAME_SIZE] + VkToolPurposeFlags purposes + char description[VK_MAX_DESCRIPTION_SIZE] + char layer[VK_MAX_EXTENSION_NAME_SIZE] @@ -7934,7 +7937,7 @@ typedef void* MTLSharedEvent_id; VkStructureType sType void* pNext uint32_t identifierSize - uint8_t identifier[VK_MAX_SHADER_MODULE_IDENTIFIER_SIZE_EXT] + uint8_t identifier[VK_MAX_SHADER_MODULE_IDENTIFIER_SIZE_EXT] VkStructureType sType @@ -7986,7 +7989,7 @@ typedef void* MTLSharedEvent_id; VkSubpassMergeStatusEXT subpassMergeStatus - char description[VK_MAX_DESCRIPTION_SIZE] + char description[VK_MAX_DESCRIPTION_SIZE] uint32_t postMergeIndex @@ -8379,9 +8382,9 @@ typedef void* MTLSharedEvent_id; VkDeviceSize addressPrecision - char description[VK_MAX_DESCRIPTION_SIZE]Free-form description of the fault - uint64_t vendorFaultCode - uint64_t vendorFaultData + char description[VK_MAX_DESCRIPTION_SIZE]Free-form description of the fault + uint64_t vendorFaultCode + uint64_t vendorFaultData VkStructureType sType @@ -8392,11 +8395,11 @@ typedef void* MTLSharedEvent_id; VkStructureType sType - void* pNext - char description[VK_MAX_DESCRIPTION_SIZE]Free-form description of the fault - VkDeviceFaultAddressInfoEXT* pAddressInfos - VkDeviceFaultVendorInfoEXT* pVendorInfos - void* pVendorBinaryData + void* pNext + char description[VK_MAX_DESCRIPTION_SIZE]Free-form description of the fault + VkDeviceFaultAddressInfoEXT* pAddressInfos + VkDeviceFaultVendorInfoEXT* pVendorInfos + void* pVendorBinaryData The fields in this structure are non-normative since structure packing is implementation-defined in C. The specification defines the normative layout. @@ -8935,7 +8938,7 @@ typedef void* MTLSharedEvent_id; uint32_t timingCount VkLatencyTimingsFrameReportNV* pTimings - + VkStructureType sType const void* pNext uint64_t presentID @@ -9078,6 +9081,28 @@ typedef void* MTLSharedEvent_id; void* pNext VkBool32 shaderQuadControl + + VkStructureType sType + void* pNext + VkBool32 shaderFloat16VectorAtomics + + + VkStructureType sType + void* pNext + VkBool32 memoryMapPlaced + VkBool32 memoryMapRangePlaced + VkBool32 memoryUnmapReserve + + + VkStructureType sType + void* pNext + VkDeviceSize minPlacedMemoryMapAlignment + + + VkStructureType sType + const void* pNext + void* pPlacedAddress + @@ -9837,6 +9862,8 @@ typedef void* MTLSharedEvent_id; + + @@ -10800,6 +10827,8 @@ typedef void* MTLSharedEvent_id; + + @@ -11211,6 +11240,8 @@ typedef void* MTLSharedEvent_id; + + @@ -11705,11 +11736,11 @@ typedef void* MTLSharedEvent_id; const VkAllocationCallbacks* pAllocator VkPipeline* pPipelines - + VkResult vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI VkDevice device VkRenderPass renderpass - VkExtent2D* pMaxWorkgroupSize + VkExtent2D* pMaxWorkgroupSize void vkDestroyPipeline @@ -15175,12 +15206,12 @@ typedef void* MTLSharedEvent_id; const VkMemoryMapInfoKHR* pMemoryMapInfo void** ppData - + VkResult vkUnmapMemory2KHR VkDevice device const VkMemoryUnmapInfoKHR* pMemoryUnmapInfo - + VkResult vkCreateShadersEXT VkDevice device uint32_t createInfoCount @@ -15491,6 +15522,7 @@ typedef void* MTLSharedEvent_id; + @@ -20638,15 +20670,24 @@ typedef void* MTLSharedEvent_id; + - + - - + + + + + + + + + + @@ -23370,7 +23411,8 @@ typedef void* MTLSharedEvent_id; - + + @@ -23590,6 +23632,7 @@ typedef void* MTLSharedEvent_id; + @@ -24155,6 +24198,16 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + @@ -24211,6 +24264,38 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -25789,6 +25874,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -25926,6 +26014,9 @@ typedef void* MTLSharedEvent_id; + + + From ce9dfe3934c841c79682a5697ea9dfaf2ebd9d48 Mon Sep 17 00:00:00 2001 From: Jacek Caban Date: Tue, 20 Feb 2024 21:26:32 +0100 Subject: [PATCH 204/413] winevulkan: Remove no longer needed spec workarounds. (cherry picked from commit 7995f3813c9df22a752d59500b3a9f3e625b1fa0) --- dlls/winevulkan/make_vulkan | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index e7b91b845e7..05ffd93d65e 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -2125,14 +2125,11 @@ class VkStruct(Sequence): # Those structs seem to be broken in spec, they are specified as # returned only, but documented as input structs. - if name in ["VkSubpassShadingPipelineCreateInfoHUAWEI", - "VkPipelineShaderStageRequiredSubgroupSizeCreateInfo"]: + if name in ["VkPipelineShaderStageRequiredSubgroupSizeCreateInfo"]: returnedonly = False # Those structs don't have returnedonly in spec, but they could (should?). - if name in ["VkSurfaceCapabilitiesPresentBarrierNV", - "VkCooperativeMatrixPropertiesNV", - "VkPerformanceValueINTEL"]: + if name in ["VkSurfaceCapabilitiesPresentBarrierNV"]: returnedonly = True structextends = struct.attrib.get("structextends") From 033ec3d925b5d1f4a5013ceda2915560fa2f1794 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 2 Mar 2024 17:12:15 +0100 Subject: [PATCH 205/413] winevulkan: Update to VK spec version 1.3.279. Signed-off-by: Philip Rebohle (cherry picked from commit ec6879b78ec58bfad9f3039fdab9498173a44f56) --- dlls/winevulkan/make_vulkan | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 05ffd93d65e..467eee61c2a 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -64,7 +64,7 @@ from enum import Enum LOGGER = logging.Logger("vulkan") LOGGER.addHandler(logging.StreamHandler()) -VK_XML_VERSION = "1.3.278" +VK_XML_VERSION = "1.3.279" WINE_VK_VERSION = (1, 3) # Filenames to create. From 2edc8b8d06f4f6711710ef04dfa02fa40156efa1 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Wed, 6 Mar 2024 16:34:43 +0200 Subject: [PATCH 206/413] Update vk.xml to 1.3.279. --- dlls/winevulkan/vk.xml | 73 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 63 insertions(+), 10 deletions(-) diff --git a/dlls/winevulkan/vk.xml b/dlls/winevulkan/vk.xml index c8464a2eae5..ee9221e9eb3 100644 --- a/dlls/winevulkan/vk.xml +++ b/dlls/winevulkan/vk.xml @@ -175,7 +175,7 @@ branch of the member gitlab server. #define VKSC_API_VERSION_1_0 VK_MAKE_API_VERSION(VKSC_API_VARIANT, 1, 0, 0)// Patch version should always be set to 0 // Version of this file -#define VK_HEADER_VERSION 278 +#define VK_HEADER_VERSION 279 // Complete version of this file #define VK_HEADER_VERSION_COMPLETE VK_MAKE_API_VERSION(0, 1, 3, VK_HEADER_VERSION) // Version of this file @@ -1467,7 +1467,7 @@ typedef void* MTLSharedEvent_id; VkPipeline basePipelineHandleIf VK_PIPELINE_CREATE_DERIVATIVE_BIT is set and this value is nonzero, it specifies the handle of the base pipeline this is a derivative of int32_t basePipelineIndexIf VK_PIPELINE_CREATE_DERIVATIVE_BIT is set and this value is not -1, it specifies an index into pCreateInfos of the base pipeline this is a derivative of - + VkStructureType sType const void* pNext VkDeviceAddress deviceAddress @@ -2488,7 +2488,7 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext VkPipelineBindPoint pipelineBindPoint - VkPipeline pipeline + VkPipeline pipeline VkIndirectCommandsLayoutNV indirectCommandsLayout uint32_t streamCount const VkIndirectCommandsStreamNV* pStreams @@ -9103,6 +9103,11 @@ typedef void* MTLSharedEvent_id; const void* pNext void* pPlacedAddress + + VkStructureType sType + void* pNext + VkBool32 shaderRawAccessChains + @@ -22708,8 +22713,10 @@ typedef void* MTLSharedEvent_id; - - + + + + @@ -23310,8 +23317,8 @@ typedef void* MTLSharedEvent_id; - - + + @@ -24216,10 +24223,12 @@ typedef void* MTLSharedEvent_id; - + - - + + + + @@ -24262,6 +24271,7 @@ typedef void* MTLSharedEvent_id; + @@ -24296,6 +24306,43 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -25942,6 +25989,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -26455,6 +26505,9 @@ typedef void* MTLSharedEvent_id; + + + From e3aca010cc9d1c6cf79a592722dcdabe8b96c057 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 6 Mar 2024 20:56:10 -0600 Subject: [PATCH 207/413] win32u: HACK: Clear queue QS_RAWINPUT if NtUserGetRawInputBuffer() got everything for Apex Legends. CW-Bug-Id: #23533 --- dlls/win32u/rawinput.c | 10 ++++++++++ server/protocol.def | 2 ++ server/queue.c | 2 ++ 3 files changed, 14 insertions(+) diff --git a/dlls/win32u/rawinput.c b/dlls/win32u/rawinput.c index 3fa187f9673..1e071e30a94 100644 --- a/dlls/win32u/rawinput.c +++ b/dlls/win32u/rawinput.c @@ -676,6 +676,7 @@ UINT WINAPI NtUserGetRawInputDeviceInfo( HANDLE handle, UINT command, void *data */ UINT WINAPI NtUserGetRawInputBuffer( RAWINPUT *data, UINT *data_size, UINT header_size ) { + static int cached_clear_qs_rawinput = -1; unsigned int count = 0, remaining, rawinput_size, next_size, overhead; struct rawinput_thread_data *thread_data; struct hardware_msg_data *msg_data; @@ -708,6 +709,7 @@ UINT WINAPI NtUserGetRawInputBuffer( RAWINPUT *data, UINT *data_size, UINT heade { req->rawinput_size = rawinput_size; req->buffer_size = 0; + req->clear_qs_rawinput = 0; if (wine_server_call( req )) return ~0u; *data_size = reply->next_size; } @@ -718,12 +720,20 @@ UINT WINAPI NtUserGetRawInputBuffer( RAWINPUT *data, UINT *data_size, UINT heade if (!(thread_data = get_rawinput_thread_data())) return ~0u; rawinput = thread_data->buffer; + if (cached_clear_qs_rawinput == -1) + { + const char *sgi; + + cached_clear_qs_rawinput = (sgi = getenv( "SteamGameId" )) && !strcmp( sgi, "1172470" ); + } + /* first RAWINPUT block in the buffer is used for WM_INPUT message data */ msg_data = (struct hardware_msg_data *)NEXTRAWINPUTBLOCK(rawinput); SERVER_START_REQ( get_rawinput_buffer ) { req->rawinput_size = rawinput_size; req->buffer_size = *data_size; + req->clear_qs_rawinput = cached_clear_qs_rawinput; wine_server_set_reply( req, msg_data, RAWINPUT_BUFFER_SIZE - rawinput->header.dwSize ); if (wine_server_call( req )) return ~0u; next_size = reply->next_size; diff --git a/server/protocol.def b/server/protocol.def index a42641babd3..ee75ae5e566 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3873,6 +3873,8 @@ struct handle_info @REQ(get_rawinput_buffer) data_size_t rawinput_size; /* size of RAWINPUT structure */ data_size_t buffer_size; /* size of output buffer */ + int clear_qs_rawinput; + int __pad; @REPLY data_size_t next_size; /* minimum size to get next message data */ unsigned int count; diff --git a/server/queue.c b/server/queue.c index 3290d28cfd9..6206857d46c 100644 --- a/server/queue.c +++ b/server/queue.c @@ -3978,6 +3978,8 @@ DECL_HANDLER(get_rawinput_buffer) count++; } + if (req->clear_qs_rawinput && !ptr) clear_queue_bits( current->queue, QS_RAWINPUT ); + reply->next_size = next_size; reply->count = count; set_reply_data_ptr( buf, pos ); From b5c73955f175af15f2fc79d9aed19936be3dfe03 Mon Sep 17 00:00:00 2001 From: Renato Pereyra Date: Thu, 7 Mar 2024 11:32:29 -0600 Subject: [PATCH 208/413] winex11.drv: Apply the GL vendor override for Intel GPUs too https://github.com/ValveSoftware/wine/pull/222 The vendor override forces wine to report an AMD GPU. Previously, the vendor override only applied for NVIDIA GPUs. Also, rename WINE_GL_HIDE_NVIDIA to WINE_GL_VENDOR_REPORT_AMD. More info: https://github.com/ValveSoftware/Proton/issues/7529 --- dlls/winex11.drv/opengl.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index 90c5dadfff6..74c8786c50b 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -3505,7 +3505,7 @@ static const GLubyte *wglGetString(GLenum name) int sz; override_vendor = 0; - if ((env = getenv("WINE_GL_HIDE_NVIDIA"))) + if ((env = getenv("WINE_GL_VENDOR_REPORT_AMD"))) { override_vendor = env[0] != '0'; } @@ -3533,13 +3533,19 @@ static const GLubyte *wglGetString(GLenum name) if (name == GL_RENDERER) { s = pglGetString(name); - if (s && strstr((const char *)s, "NVIDIA")) return (const GLubyte *)"AMD Radeon Graphics"; + if (s && (strstr((const char *)s, "NVIDIA") || strstr((const char *)s, "Intel"))) + { + return (const GLubyte *)"AMD Radeon Graphics"; + } return s; } else if (name == GL_VENDOR) { s = pglGetString(name); - if (s && strstr((const char *)s, "NVIDIA")) return (const GLubyte *)"AMD"; + if (s && (strstr((const char *)s, "NVIDIA") || strstr((const char *)s, "Intel"))) + { + return (const GLubyte *)"AMD"; + } return s; } } From b7d6d113cf1c53f47b0b298b54bb4c1a626f0d1f Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Thu, 7 Mar 2024 20:18:03 +0000 Subject: [PATCH 209/413] mscoree: Implement CLRRuntimeHost_Start. (cherry picked from commit 934cfb86b5e2b485b4b90e1eeac4021733559591) CW-Bug-Id: #23538 --- dlls/mscoree/corruntimehost.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dlls/mscoree/corruntimehost.c b/dlls/mscoree/corruntimehost.c index c8d384730c0..f931473de7a 100644 --- a/dlls/mscoree/corruntimehost.c +++ b/dlls/mscoree/corruntimehost.c @@ -814,8 +814,12 @@ static ULONG WINAPI CLRRuntimeHost_Release(ICLRRuntimeHost* iface) static HRESULT WINAPI CLRRuntimeHost_Start(ICLRRuntimeHost* iface) { - FIXME("(%p)\n", iface); - return E_NOTIMPL; + RuntimeHost *This = impl_from_ICLRRuntimeHost( iface ); + MonoDomain *dummy; + + TRACE("%p\n", This); + + return RuntimeHost_GetDefaultDomain(This, NULL, &dummy); } static HRESULT WINAPI CLRRuntimeHost_Stop(ICLRRuntimeHost* iface) From e3558b055827af11e18e742e31a66d3215b1a3ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 26 Nov 2023 18:59:54 +0100 Subject: [PATCH 210/413] loader: Expose the standard debugging symbols for GDB. CW-Bug-Id: #22176 --- loader/main.c | 3 ++- loader/preloader.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/loader/main.c b/loader/main.c index c258e025183..5a208cc048c 100644 --- a/loader/main.c +++ b/loader/main.c @@ -38,7 +38,8 @@ extern char **environ; -/* the preloader will set this variable */ +/* the preloader will set these variables */ +__attribute((visibility("default"))) struct r_debug *wine_r_debug = NULL; const __attribute((visibility("default"))) struct wine_preload_info *wine_main_preload_info = NULL; /* canonicalize path and return its directory name */ diff --git a/loader/preloader.c b/loader/preloader.c index d0551bae63a..f27d6df8c17 100644 --- a/loader/preloader.c +++ b/loader/preloader.c @@ -79,12 +79,16 @@ #ifdef HAVE_ELF_H # include #endif + +/* define _r_debug so we can re-define it as r_debug_extended */ +#define _r_debug no_r_debug; #ifdef HAVE_LINK_H # include #endif #ifdef HAVE_SYS_LINK_H # include #endif +#undef _r_debug #include "wine/asm.h" #include "main.h" @@ -1387,6 +1391,39 @@ static void set_process_name( int argc, char *argv[] ) for (i = 1; i < argc; i++) argv[i] -= off; } +struct wld_r_debug_extended +{ + struct r_debug base; + struct wld_r_debug_extended *r_next; +}; + +/* GDB integration, _r_debug_state is *required* for GDB to hook the preloader */ +__attribute((visibility("default"))) void _r_debug_state(void) {} +__attribute((visibility("default"))) struct wld_r_debug_extended _r_debug = {{0}}; + +/* sets the preloader r_debug address into DT_DEBUG */ +static void init_r_debug( struct wld_auxv *av ) +{ + ElfW(Phdr) *phdr, *ph; + ElfW(Dyn) *dyn = NULL; + char *l_addr; + int phnum; + + _r_debug.base.r_version = 2; + _r_debug.base.r_brk = (ElfW(Addr))_r_debug_state; + + if (!(phnum = get_auxiliary( av, AT_PHNUM, 0 ))) return; + if (!(phdr = (void *)get_auxiliary( av, AT_PHDR, 0 ))) return; + l_addr = (char *)phdr - sizeof(ElfW(Ehdr)); + _r_debug.base.r_ldbase = (ElfW(Addr))l_addr; + + for (ph = phdr; ph < &phdr[phnum]; ++ph) if (ph->p_type == PT_DYNAMIC) break; + if (ph >= &phdr[phnum]) return; + + dyn = (void *)(ph->p_vaddr + l_addr); + while (dyn->d_tag != DT_DEBUG && dyn->d_tag != DT_NULL) dyn++; + if (dyn->d_tag == DT_DEBUG) dyn->d_un.d_ptr = (uintptr_t)&_r_debug; +} /* * wld_start @@ -1403,6 +1440,7 @@ void* wld_start( void **stack ) struct wld_auxv new_av[8], delete_av[3], *av; struct wld_link_map main_binary_map, ld_so_map; struct wine_preload_info **wine_main_preload_info; + struct r_debug *ld_so_r_debug, **wine_r_debug; pargc = *stack; argv = (char **)pargc + 1; @@ -1432,6 +1470,8 @@ void* wld_start( void **stack ) dump_auxiliary( av ); #endif + init_r_debug( av ); + /* reserve memory that Wine needs */ if (reserve) preload_reserve( reserve ); for (i = 0; preload_info[i].size; i++) @@ -1470,6 +1510,17 @@ void* wld_start( void **stack ) interp = (char *)main_binary_map.l_addr + main_binary_map.l_interp; map_so_lib( interp, &ld_so_map ); + /* expose ld.so _r_debug as a separate namespace in r_next */ + ld_so_r_debug = find_symbol( &ld_so_map, "_r_debug", STT_OBJECT ); + if (ld_so_r_debug) _r_debug.r_next = (struct wld_r_debug_extended *)ld_so_r_debug; + else wld_printf( "_r_debug not found in ld.so\n" ); + + _r_debug_state(); /* notify GDB that _r_debug is ready */ + + wine_r_debug = find_symbol( &main_binary_map, "wine_r_debug", STT_OBJECT ); + if (wine_r_debug) *wine_r_debug = &_r_debug.base; + else wld_printf( "wine_r_debug not found\n" ); + /* store pointer to the preload info into the appropriate main binary variable */ wine_main_preload_info = find_symbol( &main_binary_map, "wine_main_preload_info", STT_OBJECT ); if (wine_main_preload_info) *wine_main_preload_info = preload_info; From cdcf675afcb1b29124c11fb64065562d6c89648a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 26 Nov 2023 18:59:54 +0100 Subject: [PATCH 211/413] ntdll: Maintain a PE module link map and expose it to GDB. CW-Bug-Id: #22176 --- dlls/ntdll/unix/virtual.c | 130 +++++++++++++++++++++++++++++++++++++- 1 file changed, 129 insertions(+), 1 deletion(-) diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index a2fbc8ed0cc..f9c4a27d654 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -51,6 +51,12 @@ #ifdef HAVE_SYS_USER_H # include #endif +#ifdef HAVE_LINK_H +# include +#endif +#ifdef HAVE_SYS_LINK_H +# include +#endif #ifdef HAVE_LIBPROCSTAT_H # include #endif @@ -86,6 +92,120 @@ WINE_DECLARE_DEBUG_CHANNEL(module); WINE_DECLARE_DEBUG_CHANNEL(virtual_ranges); WINE_DECLARE_DEBUG_CHANNEL(virtstat); +/* Gdb integration, in loader/main.c */ +static struct r_debug *wine_r_debug; + +#if defined(HAVE_LINK_H) || defined(HAVE_SYS_LINK_H) + +struct link_map_entry +{ + struct link_map map; + const void *module; +}; +static struct link_map link_map = {.l_name = (char *)""}; + +static void r_debug_set_state( int state ) +{ + wine_r_debug->r_map = &link_map; + wine_r_debug->r_state = state; + ((void (*)(void))wine_r_debug->r_brk)(); +} + +static char *get_path_from_fd( int fd, int sz ) +{ +#ifdef linux + char *ret = malloc( 32 + sz ); + + if (ret) sprintf( ret, "/proc/self/fd/%u", fd ); + return ret; +#elif defined(F_GETPATH) + char *ret = malloc( PATH_MAX + sz ); + + if (!ret) return NULL; + if (!fcntl( fd, F_GETPATH, ret )) return ret; + free( ret ); + return NULL; +#else + return NULL; +#endif +} + +static char *r_debug_path_from_fd( int fd ) +{ + char *real, *path; + if (!(path = get_path_from_fd( fd, 0 ))) return NULL; + if (!(real = realpath( path, NULL ))) return path; + free( path ); + return real; +} + +static void r_debug_add_module( void *module, int fd, INT_PTR offset ) +{ + struct link_map *ptr = link_map.l_next, *next; + struct link_map_entry *entry; + + if (!wine_r_debug) return; + + while (ptr) + { + entry = LIST_ENTRY(ptr, struct link_map_entry, map); + if (entry->module == module) break; + ptr = ptr->l_next; + } + + r_debug_set_state( RT_ADD ); + + if (ptr) entry->map.l_addr = offset; + else if ((entry = calloc( 1, sizeof(*entry) ))) + { + entry->module = module; + entry->map.l_addr = offset; + entry->map.l_name = r_debug_path_from_fd( fd ); + + entry->map.l_next = link_map.l_next; + if ((next = entry->map.l_next)) next->l_prev = &entry->map; + entry->map.l_prev = &link_map; + link_map.l_next = &entry->map; + } + + r_debug_set_state( RT_CONSISTENT ); +} + +static void r_debug_remove_module( void *module ) +{ + struct link_map *ptr = link_map.l_next, *next; + struct link_map_entry *entry; + + if (!wine_r_debug) return; + + while (ptr) + { + entry = LIST_ENTRY(ptr, struct link_map_entry, map); + if (entry->module == module) break; + ptr = ptr->l_next; + } + if (!ptr) return; + + r_debug_set_state( RT_DELETE ); + + entry->map.l_prev->l_next = entry->map.l_next; + if ((next = entry->map.l_next)) next->l_prev = entry->map.l_prev; + + r_debug_set_state( RT_CONSISTENT ); + + free( entry->map.l_name ); + free( entry ); +} + +#else /* defined(HAVE_LINK_H) || defined(HAVE_SYS_LINK_H) */ + +#define RT_CONSISTENT 0 +static void r_debug_set_state( int state ) {} +static void r_debug_add_module( void *module, int fd, INT_PTR offset ) {} +static void r_debug_remove_module( void *module ) {} + +#endif /* defined(HAVE_LINK_H) || defined(HAVE_SYS_LINK_H) */ + struct preload_info { void *addr; @@ -3272,6 +3392,7 @@ static NTSTATUS map_image_into_view( struct file_view *view, const WCHAR *filena #ifdef VALGRIND_LOAD_PDB_DEBUGINFO VALGRIND_LOAD_PDB_DEBUGINFO(fd, ptr, total_size, ptr - (char *)wine_server_get_ptr( image_info->base )); #endif + r_debug_add_module( ptr, fd, ptr - (char *)wine_server_get_ptr( image_info->base ) ); return STATUS_SUCCESS; } @@ -3625,12 +3746,15 @@ static void *alloc_virtual_heap( SIZE_T size ) void virtual_init(void) { const struct preload_info **preload_info = dlsym( RTLD_DEFAULT, "wine_main_preload_info" ); + struct r_debug **r_debug = dlsym( RTLD_DEFAULT, "wine_r_debug" ); const char *preload = getenv( "WINEPRELOADRESERVE" ); size_t size; int i; pthread_mutexattr_t attr; const char *env_var; + if (r_debug && (wine_r_debug = *r_debug)) r_debug_set_state( RT_CONSISTENT ); + pthread_mutexattr_init( &attr ); pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE ); pthread_mutex_init( &virtual_mutex, &attr ); @@ -6174,7 +6298,11 @@ static NTSTATUS unmap_view_of_section( HANDLE process, PVOID addr, ULONG flags ) SERVER_END_REQ; if (!status) { - if (view->protect & SEC_IMAGE) release_builtin_module( view->base ); + if (view->protect & SEC_IMAGE) + { + r_debug_remove_module( view->base ); + release_builtin_module( view->base ); + } if (flags & MEM_PRESERVE_PLACEHOLDER) free_pages_preserve_placeholder( view, view->base, view->size ); else delete_view( view ); } From f783e27255df845ff4dff4d8b8d77d19f905d010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 26 Nov 2023 18:35:47 +0100 Subject: [PATCH 212/413] tools: Add gdbunwind.py script with a syscall unwinder. CW-Bug-Id: #22176 --- tools/gdbunwind.py | 177 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 tools/gdbunwind.py diff --git a/tools/gdbunwind.py b/tools/gdbunwind.py new file mode 100644 index 00000000000..8611d789f1f --- /dev/null +++ b/tools/gdbunwind.py @@ -0,0 +1,177 @@ +#!/bin/env python3 + +# Copyright 2021 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 + +from gdb.unwinder import Unwinder +import gdb +import re + +ARCH_OFFSETS_X86_64 = { + "rax": (0x0000, 'void *'), + "rbx": (0x0008, 'void *'), + "rcx": (0x0010, 'void *'), + "rdx": (0x0018, 'void *'), + "rsi": (0x0020, 'void *'), + "rdi": (0x0028, 'void *'), + "r8": (0x0030, 'void *'), + "r9": (0x0038, 'void *'), + "r10": (0x0040, 'void *'), + "r11": (0x0048, 'void *'), + "r12": (0x0050, 'void *'), + "r13": (0x0058, 'void *'), + "r14": (0x0060, 'void *'), + "r15": (0x0068, 'void *'), + "rip": (0x0070, 'void *'), + "cs": (0x0078, 'void *'), + "eflags": (0x0080, 'void *'), + "rsp": (0x0088, 'void *'), + "ss": (0x0090, 'void *'), + "rbp": (0x0098, 'void *'), + "prev_frame": (0x00a0, 'void *'), + "syscall_cfa": (0x00a8, 'void *'), + # "syscall_flags": (0x00b0, 'unsigned int'), + # "restore_flags": (0x00b4, 'unsigned int'), + "teb_offset": (0x0328, 'void *'), +} + +ARCH_OFFSETS_I386 = { + # "syscall_flags": (0x000, 'unsigned short'), + # "restore_flags": (0x002, 'unsigned short'), + "eflags": (0x004, 'unsigned int'), + "eip": (0x008, 'void *'), + "esp": (0x00c, 'void *'), + "cs": (0x010, 'unsigned short'), + "ss": (0x012, 'unsigned short'), + "ds": (0x014, 'unsigned short'), + "es": (0x016, 'unsigned short'), + "fs": (0x018, 'unsigned short'), + "gs": (0x01a, 'unsigned short'), + "eax": (0x01c, 'void *'), + "ebx": (0x020, 'void *'), + "ecx": (0x024, 'void *'), + "edx": (0x028, 'void *'), + "edi": (0x02c, 'void *'), + "esi": (0x030, 'void *'), + "ebp": (0x034, 'void *'), + "syscall_cfa": (0x038, 'void *'), + "prev_frame": (0x03c, 'void *'), + "teb_offset": (0x1f8, 'void *'), +} + + +def arch_offsets(arch): + if 'x86-64' in arch: + return ARCH_OFFSETS_X86_64 + if 'i386' in arch: + return ARCH_OFFSETS_I386 + + +def find_syscall_frame(sp, arch, teb): + (off, type) = arch_offsets(arch)['teb_offset'] + frame = int(gdb.parse_and_eval(f'*(void **){teb + off}')) + + while frame: + (off, type) = arch_offsets(arch)['prev_frame'] + next_frame = int(gdb.parse_and_eval(f'*(void **){(frame + off)}')) + if not next_frame: return frame + + (off, type) = arch_offsets(arch)['ebp'] + ebp = int(gdb.parse_and_eval(f'*(void **){(next_frame + off)}')) + if ebp >= sp: return frame + + +def callback_registers(pc, sp, pending_frame): + arch = pending_frame.architecture().name() + + if 'x86-64' in arch: + frame = int(pending_frame.read_register("rbp")) + frame = (frame - 0x448) & ~0x3f + + (off, type) = arch_offsets(arch)['syscall_cfa'] + val = gdb.parse_and_eval(f'*({type} *){int(frame + off)}') + yield 'rip', gdb.parse_and_eval(f'*(void **){int(val - 8)}') + yield 'rbp', val - 16 + + elif 'i386' in arch: + teb = int(pending_frame.read_register('edx')) + frame = find_syscall_frame(sp, arch, teb) + + (off, type) = arch_offsets(arch)['syscall_cfa'] + val = gdb.parse_and_eval(f'*({type} *){int(frame + off)}') + yield 'eip', gdb.parse_and_eval(f'*(void **){int(val - 4)}') + yield 'ebp', val - 8 + + +def registers(pc, sp, pending_frame): + arch = pending_frame.architecture().name() + frame = sp + + if 'x86-64' in arch: + if 'syscall' in str(pc): + rbp = pending_frame.read_register("rbp") + frame = rbp - 0x98 + elif 'i386' in arch: + if 'syscall' in str(pc): + ebp = pending_frame.read_register("ebp") + frame = ebp - 0x34 + else: + frame += 16 + + for reg, (off, type) in arch_offsets(arch).items(): + val = gdb.parse_and_eval(f'*({type} *){int(frame + off)}') + + if reg in ('eflags', 'cs', 'ss', 'ds', 'es', 'fs', 'gs'): + int32 = gdb.lookup_type('int') + val = val.cast(int32) + if reg in ('syscall_cfa', 'prev_frame', 'teb_offset'): + continue + + yield reg, val + + +class WineSyscallFrameId(object): + def __init__(self, sp, pc): + self.sp = sp + self.pc = pc + + +class WineSyscallUnwinder(Unwinder): + def __init__(self): + super().__init__("WineSyscallUnwinder", gdb.SIGTRAMP_FRAME) + self.pattern = re.compile('__wine_(syscall|unix_call)' + '|KiUserCallbackDispatcher') + + def __call__(self, pending_frame): + pc = pending_frame.read_register("pc") + if self.pattern.search(str(pc)) is None: + return None + + sp = pending_frame.read_register("sp") + frame = WineSyscallFrameId(sp, pc) + if 'KiUserCallbackDispatcher' in str(pc): + unwind_info = pending_frame.create_unwind_info(frame) + for reg, val in callback_registers(pc, sp, pending_frame): + unwind_info.add_saved_register(reg, val) + return unwind_info + + unwind_info = pending_frame.create_unwind_info(frame) + for reg, val in registers(pc, sp, pending_frame): + unwind_info.add_saved_register(reg, val) + return unwind_info + + +gdb.unwinder.register_unwinder(None, WineSyscallUnwinder(), replace=True) From 04e5178273096850033d9334dda38f585f316f05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 22 Jan 2024 20:28:29 +0100 Subject: [PATCH 213/413] HACK: loader: Map /run/host to / in the link map. CW-Bug-Id: #22176 --- dlls/ntdll/unix/virtual.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index f9c4a27d654..b4d0cec3a21 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -135,6 +135,7 @@ static char *r_debug_path_from_fd( int fd ) char *real, *path; if (!(path = get_path_from_fd( fd, 0 ))) return NULL; if (!(real = realpath( path, NULL ))) return path; + if (!strncmp( real, "/run/host", 9 )) memmove( real, real + 9, strlen( real ) - 8 ); free( path ); return real; } From 620216ef8bb17f0ab074d3d3700eb551a548cafd Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Tue, 12 Mar 2024 14:05:22 +0200 Subject: [PATCH 214/413] HACK: winevulkan: Add option to WINE_HIDE_NVK. --- dlls/winevulkan/loader.c | 19 +++++++++++++++---- dlls/winevulkan/make_vulkan | 3 +++ dlls/winevulkan/vulkan.c | 32 +++++++++++++++++++++++++++++--- dlls/winevulkan/vulkan_loader.h | 5 +++++ dlls/winevulkan/vulkan_private.h | 5 +++++ 5 files changed, 57 insertions(+), 7 deletions(-) diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c index dbb44c556f6..a21f6b3bab8 100644 --- a/dlls/winevulkan/loader.c +++ b/dlls/winevulkan/loader.c @@ -84,6 +84,12 @@ static BOOL is_available_device_function(VkDevice device, const char *name) return UNIX_CALL(is_available_device_function, ¶ms); } +static BOOL is_nvk(VkPhysicalDevice device) +{ + struct is_nvk_params params = { .device = device }; + return UNIX_CALL(is_nvk, ¶ms); +} + static void *alloc_vk_object(size_t size) { struct wine_vk_base *object = calloc(1, size); @@ -416,7 +422,7 @@ static void fill_luid_property(VkPhysicalDeviceProperties2 *properties2) device_node_mask); } -static void fixup_device_id(UINT *vendor_id, UINT *device_id) +static void fixup_device_id(UINT *vendor_id, UINT *device_id, BOOL is_nvk) { const char *sgi; @@ -434,6 +440,11 @@ static void fixup_device_id(UINT *vendor_id, UINT *device_id) { *device_id = 0x687f; /* Radeon RX Vega 56/64 */ } + else if (is_nvk && (sgi = getenv("WINE_HIDE_NVK")) && *sgi != '0') + { + *vendor_id = 0x1002; /* AMD */ + *device_id = 0x73df; /* RX 6700XT */ + } } void WINAPI vkGetPhysicalDeviceProperties(VkPhysicalDevice physical_device, @@ -448,7 +459,7 @@ void WINAPI vkGetPhysicalDeviceProperties(VkPhysicalDevice physical_device, params.pProperties = properties; status = UNIX_CALL(vkGetPhysicalDeviceProperties, ¶ms); assert(!status); - fixup_device_id(&properties->vendorID, &properties->deviceID); + fixup_device_id(&properties->vendorID, &properties->deviceID, is_nvk(physical_device)); } void WINAPI vkGetPhysicalDeviceProperties2(VkPhysicalDevice phys_dev, @@ -464,7 +475,7 @@ void WINAPI vkGetPhysicalDeviceProperties2(VkPhysicalDevice phys_dev, status = UNIX_CALL(vkGetPhysicalDeviceProperties2, ¶ms); assert(!status); fill_luid_property(properties2); - fixup_device_id(&properties2->properties.vendorID, &properties2->properties.deviceID); + fixup_device_id(&properties2->properties.vendorID, &properties2->properties.deviceID, is_nvk(phys_dev)); } void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, @@ -480,7 +491,7 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, status = UNIX_CALL(vkGetPhysicalDeviceProperties2KHR, ¶ms); assert(!status); fill_luid_property(properties2); - fixup_device_id(&properties2->properties.vendorID, &properties2->properties.deviceID); + fixup_device_id(&properties2->properties.vendorID, &properties2->properties.deviceID, is_nvk(phys_dev)); } VkResult WINAPI vkCreateDevice(VkPhysicalDevice phys_dev, const VkDeviceCreateInfo *create_info, diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 467eee61c2a..362f0321276 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -2925,6 +2925,7 @@ class VkGenerator(object): f.write(" init_vulkan,\n") f.write(" vk_is_available_instance_function,\n") f.write(" vk_is_available_device_function,\n") + f.write(" vk_is_nvk,\n") for vk_func in self.registry.funcs.values(): if not vk_func.needs_exposing(): continue @@ -2949,6 +2950,7 @@ class VkGenerator(object): f.write(" init_vulkan,\n") f.write(" vk_is_available_instance_function32,\n") f.write(" vk_is_available_device_function32,\n") + f.write(" vk_is_nvk32,\n") for vk_func in self.registry.funcs.values(): if not vk_func.needs_exposing(): continue @@ -3133,6 +3135,7 @@ class VkGenerator(object): f.write(" unix_init,\n") f.write(" unix_is_available_instance_function,\n") f.write(" unix_is_available_device_function,\n") + f.write(" unix_is_nvk,\n") for vk_func in self.registry.funcs.values(): if not vk_func.needs_exposing(): continue diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 885af7ae970..253e3db68f5 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -278,7 +278,8 @@ static struct wine_phys_dev *wine_vk_physical_device_alloc(struct wine_instance struct wine_phys_dev *object; uint32_t num_host_properties, num_properties = 0; VkExtensionProperties *host_properties = NULL; - VkPhysicalDeviceProperties physdev_properties; + VkPhysicalDeviceProperties2 physdev_properties; + VkPhysicalDeviceVulkan12Properties vk12; BOOL have_external_memory_host = FALSE, have_external_memory_fd = FALSE, have_external_semaphore_fd = FALSE; VkResult res; unsigned int i, j; @@ -290,8 +291,16 @@ static struct wine_phys_dev *wine_vk_physical_device_alloc(struct wine_instance object->handle = handle; object->host_physical_device = phys_dev; - instance->funcs.p_vkGetPhysicalDeviceProperties(phys_dev, &physdev_properties); - object->api_version = physdev_properties.apiVersion; + memset(&vk12, 0, sizeof(vk12)); + vk12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES; + + memset(&physdev_properties, 0, sizeof(physdev_properties)); + physdev_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + physdev_properties.pNext = &vk12; + + instance->funcs.p_vkGetPhysicalDeviceProperties2(phys_dev, &physdev_properties); + object->api_version = physdev_properties.properties.apiVersion; + object->is_nvk = (vk12.driverID == VK_DRIVER_ID_MESA_NVK); handle->base.unix_handle = (uintptr_t)object; WINE_VK_ADD_DISPATCHABLE_MAPPING(instance, handle, phys_dev, object); @@ -4059,6 +4068,13 @@ NTSTATUS vk_is_available_device_function(void *arg) return !!vk_funcs->p_vkGetDeviceProcAddr(device->host_device, params->name); } +NTSTATUS vk_is_nvk(void *arg) +{ + struct is_nvk_params *params = arg; + struct wine_phys_dev *device = wine_phys_dev_from_handle(params->device); + return !!device->is_nvk; +} + #endif /* _WIN64 */ NTSTATUS vk_is_available_instance_function32(void *arg) @@ -4087,6 +4103,16 @@ NTSTATUS vk_is_available_device_function32(void *arg) return !!vk_funcs->p_vkGetDeviceProcAddr(device->host_device, name); } +NTSTATUS vk_is_nvk32(void *arg) +{ + struct + { + UINT32 device; + } *params = arg; + struct wine_phys_dev *device = wine_phys_dev_from_handle(UlongToPtr(params->device)); + return !!device->is_nvk; +} + DECLSPEC_EXPORT VkDevice __wine_get_native_VkDevice(VkDevice handle) { struct wine_device *device = wine_device_from_handle(handle); diff --git a/dlls/winevulkan/vulkan_loader.h b/dlls/winevulkan/vulkan_loader.h index 70efc2bfa73..d29ccd8ae08 100644 --- a/dlls/winevulkan/vulkan_loader.h +++ b/dlls/winevulkan/vulkan_loader.h @@ -147,6 +147,11 @@ struct is_available_device_function_params const char *name; }; +struct is_nvk_params +{ + VkPhysicalDevice device; +}; + #define wine_vk_find_struct(s, t) wine_vk_find_struct_((void *)s, VK_STRUCTURE_TYPE_##t) static inline void *wine_vk_find_struct_(void *s, VkStructureType t) { diff --git a/dlls/winevulkan/vulkan_private.h b/dlls/winevulkan/vulkan_private.h index ffae94adbe4..3a044f08dcc 100644 --- a/dlls/winevulkan/vulkan_private.h +++ b/dlls/winevulkan/vulkan_private.h @@ -211,6 +211,8 @@ struct wine_phys_dev uint32_t external_memory_align; + bool is_nvk; + struct wine_vk_mapping mapping; }; @@ -351,6 +353,9 @@ NTSTATUS vk_is_available_device_function(void *arg); NTSTATUS vk_is_available_instance_function32(void *arg); NTSTATUS vk_is_available_device_function32(void *arg); +NTSTATUS vk_is_nvk(void *arg); +NTSTATUS vk_is_nvk32(void *arg); + struct conversion_context { char buffer[2048]; From d455f1fccd1e6c8bccd900b17423351c932ee38f Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Tue, 12 Mar 2024 14:05:40 +0200 Subject: [PATCH 215/413] HACK: winex11: Add option to WINE_HIDE_NVK. --- dlls/winex11.drv/display.c | 9 +++++++-- dlls/winex11.drv/xrandr.c | 5 +++++ include/wine/gdi_driver.h | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/dlls/winex11.drv/display.c b/dlls/winex11.drv/display.c index a0cd93bac0f..07e5facb69d 100644 --- a/dlls/winex11.drv/display.c +++ b/dlls/winex11.drv/display.c @@ -530,7 +530,7 @@ static const char *debugstr_devmodew( const DEVMODEW *devmode ) position ); } -static void fixup_device_id(UINT *vendor_id, UINT *device_id) +static void fixup_device_id(UINT *vendor_id, UINT *device_id, BOOL is_nvk) { const char *sgi; @@ -548,6 +548,11 @@ static void fixup_device_id(UINT *vendor_id, UINT *device_id) { *device_id = 0x687f; /* Radeon RX Vega 56/64 */ } + else if (is_nvk && (sgi = getenv("WINE_HIDE_NVK")) && *sgi != '0') + { + *vendor_id = 0x1002; /* AMD */ + *device_id = 0x73df; /* RX 6700XT */ + } } BOOL X11DRV_UpdateDisplayDevices( const struct gdi_device_manager *device_manager, BOOL force, void *param ) @@ -571,7 +576,7 @@ BOOL X11DRV_UpdateDisplayDevices( const struct gdi_device_manager *device_manage for (gpu = 0; gpu < gpu_count; gpu++) { - fixup_device_id( &gpus[gpu].vendor_id, &gpus[gpu].device_id ); + fixup_device_id( &gpus[gpu].vendor_id, &gpus[gpu].device_id, gpus[gpu].is_nvk ); device_manager->add_gpu( &gpus[gpu], param ); diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c index 47accf1de6d..6011a198810 100644 --- a/dlls/winex11.drv/xrandr.c +++ b/dlls/winex11.drv/xrandr.c @@ -856,6 +856,7 @@ static BOOL get_gpu_properties_from_vulkan( struct gdi_gpu *gpu, const XRRProvid VkPhysicalDeviceMemoryProperties mem_properties; VkInstanceCreateInfo create_info; VkPhysicalDeviceIDProperties id; + VkPhysicalDeviceVulkan12Properties vk12; VkInstance vk_instance = NULL; VkDisplayKHR vk_display; DWORD len; @@ -920,8 +921,11 @@ static BOOL get_gpu_properties_from_vulkan( struct gdi_gpu *gpu, const XRRProvid if (X11DRV_check_error() || vr != VK_SUCCESS || vk_display == VK_NULL_HANDLE) continue; + memset( &vk12, 0, sizeof(vk12) ); + vk12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES; memset( &id, 0, sizeof(id) ); id.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES; + id.pNext = &vk12; properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; properties2.pNext = &id; @@ -944,6 +948,7 @@ static BOOL get_gpu_properties_from_vulkan( struct gdi_gpu *gpu, const XRRProvid { gpu->vendor_id = properties2.properties.vendorID; gpu->device_id = properties2.properties.deviceID; + if (vk12.driverID == VK_DRIVER_ID_MESA_NVK) gpu->is_nvk = TRUE; } RtlUTF8ToUnicodeN( gpu->name, sizeof(gpu->name), &len, properties2.properties.deviceName, strlen( properties2.properties.deviceName ) + 1 ); diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index 26562bfef2b..2b1dc3ca044 100644 --- a/include/wine/gdi_driver.h +++ b/include/wine/gdi_driver.h @@ -252,6 +252,7 @@ struct gdi_gpu UINT revision_id; GUID vulkan_uuid; /* Vulkan device UUID */ ULONGLONG memory_size; + BOOL is_nvk; }; struct gdi_adapter From 213f29157a29b2f0168b9de40977ba9891268500 Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Tue, 12 Mar 2024 12:15:53 -0300 Subject: [PATCH 216/413] Revert "HACK: gdiplus: Add HDC parameter back to gdi+ internal functions." This reverts commit 505f4dface671ca3a07ad7fc6eee818709a4b419. --- dlls/gdiplus/gdiplus_private.h | 4 +-- dlls/gdiplus/graphics.c | 52 +++++++++++++++++----------------- dlls/gdiplus/graphicspath.c | 8 +++--- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index 95d19080ff1..2b5dbee43e9 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -620,13 +620,13 @@ struct gdip_font_link_info { }; -typedef GpStatus (*gdip_format_string_callback)(GpGraphics *graphics, HDC hdc, +typedef GpStatus (*gdip_format_string_callback)(GpGraphics *graphics, GDIPCONST WCHAR *string, INT index, INT length, struct gdip_font_link_info *sections, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, INT underlined_index_count, void *user_data); -GpStatus gdip_format_string(GpGraphics *graphics, HDC hdc, +GpStatus gdip_format_string(GpGraphics *graphics, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, int ignore_empty_clip, gdip_format_string_callback callback, void *user_data); diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index 4b942bfaa09..707fa55cfda 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -5163,7 +5163,7 @@ GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT w } /* Populates gdip_font_link_info struct based on the base_font and input string */ -static void generate_font_link_info(GpGraphics *graphics, HDC hdc, WCHAR *string, DWORD length, GDIPCONST GpFont *base_font, +static void generate_font_link_info(GpGraphics *graphics, WCHAR *string, DWORD length, GDIPCONST GpFont *base_font, struct gdip_font_link_info *font_link_info) { IUnknown *unk; @@ -5182,7 +5182,7 @@ static void generate_font_link_info(GpGraphics *graphics, HDC hdc, WCHAR *string IUnknown_Release(unk); get_font_hfont(graphics, base_font, NULL, &hfont, NULL, NULL); - IMLangFontLink_GetFontCodePages(iMLFL, hdc, hfont, &font_codepages); + IMLangFontLink_GetFontCodePages(iMLFL, graphics->hdc, hfont, &font_codepages); while (progress < length) { @@ -5197,10 +5197,10 @@ static void generate_font_link_info(GpGraphics *graphics, HDC hdc, WCHAR *string } else { - IMLangFontLink_MapFont(iMLFL, hdc, string_codepages, hfont, &map_hfont); - old_font = SelectObject(hdc, map_hfont); - GdipCreateFontFromDC(hdc, &gpfont); - SelectObject(hdc, old_font); + IMLangFontLink_MapFont(iMLFL, graphics->hdc, string_codepages, hfont, &map_hfont); + old_font = SelectObject(graphics->hdc, map_hfont); + GdipCreateFontFromDC(graphics->hdc, &gpfont); + SelectObject(graphics->hdc, old_font); IMLangFontLink_ReleaseFont(iMLFL, map_hfont); section->font = gpfont; } @@ -5214,7 +5214,7 @@ static void generate_font_link_info(GpGraphics *graphics, HDC hdc, WCHAR *string IMLangFontLink_Release(iMLFL); } -static void font_link_get_text_extent_point(struct gdip_font_link_info *font_link_info, GpGraphics *graphics, HDC hdc, LPCWSTR string, +static void font_link_get_text_extent_point(struct gdip_font_link_info *font_link_info, GpGraphics *graphics, LPCWSTR string, INT index, int length, int max_ext, LPINT fit, SIZE *size) { DWORD to_measure_length; @@ -5236,9 +5236,9 @@ static void font_link_get_text_extent_point(struct gdip_font_link_info *font_lin to_measure_length = min(length - (i - index), section->end - i); get_font_hfont(graphics, section->font, NULL, &hfont, NULL, NULL); - oldhfont = SelectObject(hdc, hfont); - GetTextExtentExPointW(hdc, &string[i], to_measure_length, max_ext, &fitaux, NULL, &sizeaux); - SelectObject(hdc, oldhfont); + oldhfont = SelectObject(graphics->hdc, hfont); + GetTextExtentExPointW(graphics->hdc, &string[i], to_measure_length, max_ext, &fitaux, NULL, &sizeaux); + SelectObject(graphics->hdc, oldhfont); DeleteObject(hfont); max_ext -= sizeaux.cx; @@ -5266,7 +5266,7 @@ static void release_font_link_info(struct gdip_font_link_info *font_link_info) } } -GpStatus gdip_format_string(GpGraphics *graphics, HDC hdc, +GpStatus gdip_format_string(GpGraphics *graphics, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, int ignore_empty_clip, gdip_format_string_callback callback, void *user_data) @@ -5348,10 +5348,10 @@ GpStatus gdip_format_string(GpGraphics *graphics, HDC hdc, halign = format->align; - generate_font_link_info(graphics, hdc, stringdup, length, font, &font_link_info); + generate_font_link_info(graphics, stringdup, length, font, &font_link_info); while(sum < length){ - font_link_get_text_extent_point(&font_link_info, graphics, hdc, stringdup, sum, length - sum, nwidth, &fit, &size); + font_link_get_text_extent_point(&font_link_info, graphics, stringdup, sum, length - sum, nwidth, &fit, &size); fitcpy = fit; if(fit == 0) @@ -5399,7 +5399,7 @@ GpStatus gdip_format_string(GpGraphics *graphics, HDC hdc, else lineend = fit; - font_link_get_text_extent_point(&font_link_info, graphics, hdc, stringdup, sum, lineend, nwidth, &j, &size); + font_link_get_text_extent_point(&font_link_info, graphics, stringdup, sum, lineend, nwidth, &j, &size); bounds.Width = size.cx; @@ -5432,7 +5432,7 @@ GpStatus gdip_format_string(GpGraphics *graphics, HDC hdc, if (hotkeyprefix_offsets[hotkeyprefix_end_pos] >= sum + lineend) break; - stat = callback(graphics, hdc, stringdup, sum, lineend, + stat = callback(graphics, stringdup, sum, lineend, &font_link_info, rect, format, lineno, &bounds, &hotkeyprefix_offsets[hotkeyprefix_pos], hotkeyprefix_end_pos-hotkeyprefix_pos, user_data); @@ -5499,7 +5499,7 @@ struct measure_ranges_args { REAL rel_width, rel_height; }; -static GpStatus measure_ranges_callback(GpGraphics *graphics, HDC hdc, +static GpStatus measure_ranges_callback(GpGraphics *graphics, GDIPCONST WCHAR *string, INT index, INT length, struct gdip_font_link_info *font_link_info, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, @@ -5522,10 +5522,10 @@ static GpStatus measure_ranges_callback(GpGraphics *graphics, HDC hdc, range_rect.Y = bounds->Y / args->rel_height; range_rect.Height = bounds->Height / args->rel_height; - font_link_get_text_extent_point(font_link_info, graphics, hdc, string, index, range_start - index, INT_MAX, NULL, &range_size); + font_link_get_text_extent_point(font_link_info, graphics, string, index, range_start - index, INT_MAX, NULL, &range_size); range_rect.X = (bounds->X + range_size.cx) / args->rel_width; - font_link_get_text_extent_point(font_link_info, graphics, hdc, string, index, range_end - index, INT_MAX, NULL, &range_size); + font_link_get_text_extent_point(font_link_info, graphics, string, index, range_end - index, INT_MAX, NULL, &range_size); range_rect.Width = (bounds->X + range_size.cx) / args->rel_width - range_rect.X; stat = GdipCombineRegionRect(args->regions[i], &range_rect, CombineModeUnion); @@ -5602,7 +5602,7 @@ GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics, gdi_transform_acquire(graphics); - stat = gdip_format_string(graphics, hdc, string, length, font, &scaled_rect, stringFormat, + stat = gdip_format_string(graphics, string, length, font, &scaled_rect, stringFormat, (stringFormat->attr & StringFormatFlagsNoClip) != 0, measure_ranges_callback, &args); gdi_transform_release(graphics); @@ -5623,7 +5623,7 @@ struct measure_string_args { REAL rel_width, rel_height; }; -static GpStatus measure_string_callback(GpGraphics *graphics, HDC hdc, +static GpStatus measure_string_callback(GpGraphics *graphics, GDIPCONST WCHAR *string, INT index, INT length, struct gdip_font_link_info *font_link_info, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, @@ -5726,7 +5726,7 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics, gdi_transform_acquire(graphics); - gdip_format_string(graphics, hdc, string, length, font, &scaled_rect, format, TRUE, + gdip_format_string(graphics, string, length, font, &scaled_rect, format, TRUE, measure_string_callback, &args); gdi_transform_release(graphics); @@ -5751,7 +5751,7 @@ struct draw_string_args { REAL x, y, rel_width, rel_height, ascent; }; -static GpStatus draw_string_callback(GpGraphics *graphics, HDC hdc, +static GpStatus draw_string_callback(GpGraphics *graphics, GDIPCONST WCHAR *string, INT index, INT length, struct gdip_font_link_info *font_link_info, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, @@ -5775,7 +5775,7 @@ static GpStatus draw_string_callback(GpGraphics *graphics, HDC hdc, to_draw_length = min(length - (i - index), section->end - i); TRACE("index %d, todraw %ld, used %s\n", i, to_draw_length, section->font == font_link_info->base_font ? "base font" : "map"); - font_link_get_text_extent_point(font_link_info, graphics, hdc, string, i, to_draw_length, 0, NULL, &size); + font_link_get_text_extent_point(font_link_info, graphics, string, i, to_draw_length, 0, NULL, &size); stat = draw_driver_string(graphics, &string[i], to_draw_length, section->font, format, args->brush, &position, DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL); @@ -5801,10 +5801,10 @@ static GpStatus draw_string_callback(GpGraphics *graphics, HDC hdc, SIZE text_size; INT ofs = underlined_indexes[i] - index; - font_link_get_text_extent_point(font_link_info, graphics, hdc, string, index, ofs, INT_MAX, NULL, &text_size); + font_link_get_text_extent_point(font_link_info, graphics, string, index, ofs, INT_MAX, NULL, &text_size); start_x = text_size.cx / args->rel_width; - font_link_get_text_extent_point(font_link_info, graphics, hdc, string, index, ofs+1, INT_MAX, NULL, &text_size); + font_link_get_text_extent_point(font_link_info, graphics, string, index, ofs+1, INT_MAX, NULL, &text_size); end_x = text_size.cx / args->rel_width; GdipFillRectangle(graphics, (GpBrush*)args->brush, position.X+start_x, underline_y, end_x-start_x, underline_height); @@ -5918,7 +5918,7 @@ GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string GetTextMetricsW(hdc, &textmetric); args.ascent = textmetric.tmAscent / rel_height; - gdip_format_string(graphics, hdc, string, length, font, &scaled_rect, format, TRUE, + gdip_format_string(graphics, string, length, font, &scaled_rect, format, TRUE, draw_string_callback, &args); gdi_transform_release(graphics); diff --git a/dlls/gdiplus/graphicspath.c b/dlls/gdiplus/graphicspath.c index 0d1cfb2f9c5..20e0787de16 100644 --- a/dlls/gdiplus/graphicspath.c +++ b/dlls/gdiplus/graphicspath.c @@ -949,7 +949,7 @@ struct format_string_args float ascent; }; -static GpStatus format_string_callback(GpGraphics *graphics, HDC hdc, +static GpStatus format_string_callback(GpGraphics *graphics, GDIPCONST WCHAR *string, INT index, INT length, struct gdip_font_link_info *font_link_info, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, @@ -975,7 +975,7 @@ static GpStatus format_string_callback(GpGraphics *graphics, HDC hdc, TTPOLYGONHEADER *ph = NULL, *origph; char *start; DWORD len, ofs = 0; - len = GetGlyphOutlineW(hdc, string[i], GGO_BEZIER, &gm, 0, NULL, &identity); + len = GetGlyphOutlineW(graphics->hdc, string[i], GGO_BEZIER, &gm, 0, NULL, &identity); if (len == GDI_ERROR) { status = GenericError; @@ -989,7 +989,7 @@ static GpStatus format_string_callback(GpGraphics *graphics, HDC hdc, status = OutOfMemory; break; } - GetGlyphOutlineW(hdc, string[i], GGO_BEZIER, &gm, len, start, &identity); + GetGlyphOutlineW(graphics->hdc, string[i], GGO_BEZIER, &gm, len, start, &identity); ofs = 0; while (ofs < len) @@ -1117,7 +1117,7 @@ GpStatus WINGDIPAPI GdipAddPathString(GpPath* path, GDIPCONST WCHAR* string, INT args.maxY = 0; args.scale = emSize / native_height; args.ascent = textmetric.tmAscent * args.scale; - status = gdip_format_string(graphics, dc, string, length, NULL, &scaled_layout_rect, + status = gdip_format_string(graphics, string, length, NULL, &scaled_layout_rect, format, TRUE, format_string_callback, &args); DeleteDC(dc); From 2dd4050da0d0f51b29f71f00d693a0384763cf56 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Fri, 23 Feb 2024 19:48:59 +0000 Subject: [PATCH 217/413] gdiplus: Switch to a struct for gdip_format_string callback args. (cherry picked from commit f2c92c68a21bdda69df0bd24023a658e5e8f9032) --- dlls/gdiplus/gdiplus_private.h | 22 +++++-- dlls/gdiplus/graphics.c | 116 ++++++++++++++++----------------- dlls/gdiplus/graphicspath.c | 24 +++---- 3 files changed, 83 insertions(+), 79 deletions(-) diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index 2b5dbee43e9..c88b5407257 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -619,12 +619,22 @@ struct gdip_font_link_info { struct list sections; }; - -typedef GpStatus (*gdip_format_string_callback)(GpGraphics *graphics, - GDIPCONST WCHAR *string, INT index, INT length, struct gdip_font_link_info *sections, - GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, - INT lineno, const RectF *bounds, INT *underlined_indexes, - INT underlined_index_count, void *user_data); +struct gdip_format_string_info { + GpGraphics *graphics; + GDIPCONST WCHAR *string; + INT index; + INT length; + struct gdip_font_link_info font_link_info; + GDIPCONST RectF *rect; + GDIPCONST GpStringFormat *format; + INT lineno; + const RectF *bounds; + INT *underlined_indexes; + INT underlined_index_count; + void *user_data; +}; + +typedef GpStatus (*gdip_format_string_callback)(struct gdip_format_string_info *info); GpStatus gdip_format_string(GpGraphics *graphics, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index 707fa55cfda..a7ddb7d996c 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -5282,17 +5282,26 @@ GpStatus gdip_format_string(GpGraphics *graphics, INT *hotkeyprefix_offsets=NULL; INT hotkeyprefix_count=0; INT hotkeyprefix_pos=0, hotkeyprefix_end_pos=0; - struct gdip_font_link_info font_link_info = { 0 }; BOOL seen_prefix = FALSE, unixstyle_newline = TRUE; + struct gdip_format_string_info info; + + info.graphics = graphics; + info.rect = rect; + info.bounds = &bounds; + info.user_data = user_data; if(length == -1) length = lstrlenW(string); stringdup = calloc(length + 1, sizeof(WCHAR)); if(!stringdup) return OutOfMemory; + info.string = stringdup; + if (!format) format = &default_drawstring_format; + info.format = format; + nwidth = (int)(rect->Width + 0.005f); nheight = (int)(rect->Height + 0.005f); if (ignore_empty_clip) @@ -5348,10 +5357,10 @@ GpStatus gdip_format_string(GpGraphics *graphics, halign = format->align; - generate_font_link_info(graphics, stringdup, length, font, &font_link_info); + generate_font_link_info(graphics, stringdup, length, font, &info.font_link_info); while(sum < length){ - font_link_get_text_extent_point(&font_link_info, graphics, stringdup, sum, length - sum, nwidth, &fit, &size); + font_link_get_text_extent_point(&info.font_link_info, graphics, stringdup, sum, length - sum, nwidth, &fit, &size); fitcpy = fit; if(fit == 0) @@ -5399,7 +5408,7 @@ GpStatus gdip_format_string(GpGraphics *graphics, else lineend = fit; - font_link_get_text_extent_point(&font_link_info, graphics, stringdup, sum, lineend, nwidth, &j, &size); + font_link_get_text_extent_point(&info.font_link_info, graphics, stringdup, sum, lineend, nwidth, &j, &size); bounds.Width = size.cx; @@ -5432,10 +5441,13 @@ GpStatus gdip_format_string(GpGraphics *graphics, if (hotkeyprefix_offsets[hotkeyprefix_end_pos] >= sum + lineend) break; - stat = callback(graphics, stringdup, sum, lineend, - &font_link_info, rect, format, lineno, &bounds, - &hotkeyprefix_offsets[hotkeyprefix_pos], - hotkeyprefix_end_pos-hotkeyprefix_pos, user_data); + info.index = sum; + info.length = lineend; + info.lineno = lineno; + info.underlined_indexes = &hotkeyprefix_offsets[hotkeyprefix_pos]; + info.underlined_index_count = hotkeyprefix_end_pos-hotkeyprefix_pos; + + stat = callback(&info); if (stat != Ok) break; @@ -5464,7 +5476,7 @@ GpStatus gdip_format_string(GpGraphics *graphics, break; } - release_font_link_info(&font_link_info); + release_font_link_info(&info.font_link_info); free(stringdup); free(hotkeyprefix_offsets); @@ -5499,34 +5511,30 @@ struct measure_ranges_args { REAL rel_width, rel_height; }; -static GpStatus measure_ranges_callback(GpGraphics *graphics, - GDIPCONST WCHAR *string, INT index, INT length, - struct gdip_font_link_info *font_link_info, - GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, - INT lineno, const RectF *bounds, INT *underlined_indexes, - INT underlined_index_count, void *user_data) +static GpStatus measure_ranges_callback(struct gdip_format_string_info *info) { int i; GpStatus stat = Ok; - struct measure_ranges_args *args = user_data; + struct measure_ranges_args *args = info->user_data; + CharacterRange *ranges = info->format->character_ranges; - for (i=0; irange_count; i++) + for (i=0; i < info->format->range_count; i++) { - INT range_start = max(index, format->character_ranges[i].First); - INT range_end = min(index+length, format->character_ranges[i].First+format->character_ranges[i].Length); + INT range_start = max(info->index, ranges[i].First); + INT range_end = min(info->index + info->length, ranges[i].First + ranges[i].Length); if (range_start < range_end) { GpRectF range_rect; SIZE range_size; - range_rect.Y = bounds->Y / args->rel_height; - range_rect.Height = bounds->Height / args->rel_height; + range_rect.Y = info->bounds->Y / args->rel_height; + range_rect.Height = info->bounds->Height / args->rel_height; - font_link_get_text_extent_point(font_link_info, graphics, string, index, range_start - index, INT_MAX, NULL, &range_size); - range_rect.X = (bounds->X + range_size.cx) / args->rel_width; + font_link_get_text_extent_point(&info->font_link_info, info->graphics, info->string, info->index, range_start - info->index, INT_MAX, NULL, &range_size); + range_rect.X = (info->bounds->X + range_size.cx) / args->rel_width; - font_link_get_text_extent_point(font_link_info, graphics, string, index, range_end - index, INT_MAX, NULL, &range_size); - range_rect.Width = (bounds->X + range_size.cx) / args->rel_width - range_rect.X; + font_link_get_text_extent_point(&info->font_link_info, info->graphics, info->string, info->index, range_end - info->index, INT_MAX, NULL, &range_size); + range_rect.Width = (info->bounds->X + range_size.cx) / args->rel_width - range_rect.X; stat = GdipCombineRegionRect(args->regions[i], &range_rect, CombineModeUnion); if (stat != Ok) @@ -5623,18 +5631,13 @@ struct measure_string_args { REAL rel_width, rel_height; }; -static GpStatus measure_string_callback(GpGraphics *graphics, - GDIPCONST WCHAR *string, INT index, INT length, - struct gdip_font_link_info *font_link_info, - GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, - INT lineno, const RectF *bounds, INT *underlined_indexes, - INT underlined_index_count, void *user_data) +static GpStatus measure_string_callback(struct gdip_format_string_info *info) { - struct measure_string_args *args = user_data; + struct measure_string_args *args = info->user_data; REAL new_width, new_height; - new_width = bounds->Width / args->rel_width; - new_height = (bounds->Height + bounds->Y) / args->rel_height - args->bounds->Y; + new_width = info->bounds->Width / args->rel_width; + new_height = (info->bounds->Height + info->bounds->Y) / args->rel_height - args->bounds->Y; if (new_width > args->bounds->Width) args->bounds->Width = new_width; @@ -5643,7 +5646,7 @@ static GpStatus measure_string_callback(GpGraphics *graphics, args->bounds->Height = new_height; if (args->codepointsfitted) - *args->codepointsfitted = index + length; + *args->codepointsfitted = info->index + info->length; if (args->linesfilled) (*args->linesfilled)++; @@ -5751,63 +5754,58 @@ struct draw_string_args { REAL x, y, rel_width, rel_height, ascent; }; -static GpStatus draw_string_callback(GpGraphics *graphics, - GDIPCONST WCHAR *string, INT index, INT length, - struct gdip_font_link_info *font_link_info, - GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, - INT lineno, const RectF *bounds, INT *underlined_indexes, - INT underlined_index_count, void *user_data) +static GpStatus draw_string_callback(struct gdip_format_string_info *info) { - struct draw_string_args *args = user_data; - int i = index; + struct draw_string_args *args = info->user_data; + int i = info->index; PointF position; SIZE size; DWORD to_draw_length; struct gdip_font_link_section *section; GpStatus stat = Ok; - position.X = args->x + bounds->X / args->rel_width; - position.Y = args->y + bounds->Y / args->rel_height + args->ascent; + position.X = args->x + info->bounds->X / args->rel_width; + position.Y = args->y + info->bounds->Y / args->rel_height + args->ascent; - LIST_FOR_EACH_ENTRY(section, &font_link_info->sections, struct gdip_font_link_section, entry) + LIST_FOR_EACH_ENTRY(section, &info->font_link_info.sections, struct gdip_font_link_section, entry) { if (i >= section->end) continue; - to_draw_length = min(length - (i - index), section->end - i); - TRACE("index %d, todraw %ld, used %s\n", i, to_draw_length, section->font == font_link_info->base_font ? "base font" : "map"); - font_link_get_text_extent_point(font_link_info, graphics, string, i, to_draw_length, 0, NULL, &size); - stat = draw_driver_string(graphics, &string[i], to_draw_length, - section->font, format, args->brush, &position, + to_draw_length = min(info->length - (i - info->index), section->end - i); + TRACE("index %d, todraw %ld, used %s\n", i, to_draw_length, section->font == info->font_link_info.base_font ? "base font" : "map"); + font_link_get_text_extent_point(&info->font_link_info, info->graphics, info->string, i, to_draw_length, 0, NULL, &size); + stat = draw_driver_string(info->graphics, &info->string[i], to_draw_length, + section->font, info->format, args->brush, &position, DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL); position.X += size.cx / args->rel_width; i += to_draw_length; - if (stat != Ok || (i - index) >= length) break; + if (stat != Ok || (i - info->index) >= info->length) break; } - if (stat == Ok && underlined_index_count) + if (stat == Ok && info->underlined_index_count) { OUTLINETEXTMETRICW otm; REAL underline_y, underline_height; int i; - GetOutlineTextMetricsW(graphics->hdc, sizeof(otm), &otm); + GetOutlineTextMetricsW(info->graphics->hdc, sizeof(otm), &otm); underline_height = otm.otmsUnderscoreSize / args->rel_height; underline_y = position.Y - otm.otmsUnderscorePosition / args->rel_height - underline_height / 2; - for (i=0; iunderlined_index_count; i++) { REAL start_x, end_x; SIZE text_size; - INT ofs = underlined_indexes[i] - index; + INT ofs = info->underlined_indexes[i] - info->index; - font_link_get_text_extent_point(font_link_info, graphics, string, index, ofs, INT_MAX, NULL, &text_size); + font_link_get_text_extent_point(&info->font_link_info, info->graphics, info->string, info->index, ofs, INT_MAX, NULL, &text_size); start_x = text_size.cx / args->rel_width; - font_link_get_text_extent_point(font_link_info, graphics, string, index, ofs+1, INT_MAX, NULL, &text_size); + font_link_get_text_extent_point(&info->font_link_info, info->graphics, info->string, info->index, ofs+1, INT_MAX, NULL, &text_size); end_x = text_size.cx / args->rel_width; - GdipFillRectangle(graphics, (GpBrush*)args->brush, position.X+start_x, underline_y, end_x-start_x, underline_height); + GdipFillRectangle(info->graphics, (GpBrush*)args->brush, position.X+start_x, underline_y, end_x-start_x, underline_height); } } diff --git a/dlls/gdiplus/graphicspath.c b/dlls/gdiplus/graphicspath.c index 20e0787de16..8fbdae1a65a 100644 --- a/dlls/gdiplus/graphicspath.c +++ b/dlls/gdiplus/graphicspath.c @@ -949,33 +949,29 @@ struct format_string_args float ascent; }; -static GpStatus format_string_callback(GpGraphics *graphics, - GDIPCONST WCHAR *string, INT index, INT length, struct gdip_font_link_info *font_link_info, - GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, - INT lineno, const RectF *bounds, INT *underlined_indexes, - INT underlined_index_count, void *priv) +static GpStatus format_string_callback(struct gdip_format_string_info* info) { static const MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} }; - struct format_string_args *args = priv; + struct format_string_args *args = info->user_data; GpPath *path = args->path; GpStatus status = Ok; - float x = rect->X + (bounds->X - rect->X) * args->scale; - float y = rect->Y + (bounds->Y - rect->Y) * args->scale; + float x = info->rect->X + (info->bounds->X - info->rect->X) * args->scale; + float y = info->rect->Y + (info->bounds->Y - info->rect->Y) * args->scale; int i; - if (underlined_index_count) + if (info->underlined_index_count) FIXME("hotkey underlines not drawn yet\n"); - if (y + bounds->Height * args->scale > args->maxY) - args->maxY = y + bounds->Height * args->scale; + if (y + info->bounds->Height * args->scale > args->maxY) + args->maxY = y + info->bounds->Height * args->scale; - for (i = index; i < length + index; ++i) + for (i = info->index; i < info->length + info->index; ++i) { GLYPHMETRICS gm; TTPOLYGONHEADER *ph = NULL, *origph; char *start; DWORD len, ofs = 0; - len = GetGlyphOutlineW(graphics->hdc, string[i], GGO_BEZIER, &gm, 0, NULL, &identity); + len = GetGlyphOutlineW(info->graphics->hdc, info->string[i], GGO_BEZIER, &gm, 0, NULL, &identity); if (len == GDI_ERROR) { status = GenericError; @@ -989,7 +985,7 @@ static GpStatus format_string_callback(GpGraphics *graphics, status = OutOfMemory; break; } - GetGlyphOutlineW(graphics->hdc, string[i], GGO_BEZIER, &gm, len, start, &identity); + GetGlyphOutlineW(info->graphics->hdc, info->string[i], GGO_BEZIER, &gm, len, start, &identity); ofs = 0; while (ofs < len) From 190263569c2830b859cf58da79a758b65aec4bfd Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Fri, 23 Feb 2024 20:05:12 +0000 Subject: [PATCH 218/413] gdiplus: Pass gdip_format_string_info to font link functions. (cherry picked from commit 0d0d37bbea2c59820d35082007dbedc0bd531c65) --- dlls/gdiplus/graphics.c | 51 ++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index a7ddb7d996c..1a57a375489 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -5163,8 +5163,7 @@ GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT w } /* Populates gdip_font_link_info struct based on the base_font and input string */ -static void generate_font_link_info(GpGraphics *graphics, WCHAR *string, DWORD length, GDIPCONST GpFont *base_font, - struct gdip_font_link_info *font_link_info) +static void generate_font_link_info(struct gdip_format_string_info *info, DWORD length, GDIPCONST GpFont *base_font) { IUnknown *unk; IMLangFontLink *iMLFL; @@ -5174,21 +5173,21 @@ static void generate_font_link_info(GpGraphics *graphics, WCHAR *string, DWORD l struct gdip_font_link_section *section; DWORD font_codepages, string_codepages; - list_init(&font_link_info->sections); - font_link_info->base_font = base_font; + list_init(&info->font_link_info.sections); + info->font_link_info.base_font = base_font; GetGlobalFontLinkObject((void**)&unk); IUnknown_QueryInterface(unk, &IID_IMLangFontLink, (void**)&iMLFL); IUnknown_Release(unk); - get_font_hfont(graphics, base_font, NULL, &hfont, NULL, NULL); - IMLangFontLink_GetFontCodePages(iMLFL, graphics->hdc, hfont, &font_codepages); + get_font_hfont(info->graphics, base_font, NULL, &hfont, NULL, NULL); + IMLangFontLink_GetFontCodePages(iMLFL, info->graphics->hdc, hfont, &font_codepages); while (progress < length) { section = calloc(1, sizeof(*section)); section->start = progress; - IMLangFontLink_GetStrCodePages(iMLFL, &string[progress], length - progress, + IMLangFontLink_GetStrCodePages(iMLFL, &info->string[progress], length - progress, font_codepages, &string_codepages, &processed); if (font_codepages & string_codepages) @@ -5197,16 +5196,16 @@ static void generate_font_link_info(GpGraphics *graphics, WCHAR *string, DWORD l } else { - IMLangFontLink_MapFont(iMLFL, graphics->hdc, string_codepages, hfont, &map_hfont); - old_font = SelectObject(graphics->hdc, map_hfont); - GdipCreateFontFromDC(graphics->hdc, &gpfont); - SelectObject(graphics->hdc, old_font); + IMLangFontLink_MapFont(iMLFL, info->graphics->hdc, string_codepages, hfont, &map_hfont); + old_font = SelectObject(info->graphics->hdc, map_hfont); + GdipCreateFontFromDC(info->graphics->hdc, &gpfont); + SelectObject(info->graphics->hdc, old_font); IMLangFontLink_ReleaseFont(iMLFL, map_hfont); section->font = gpfont; } section->end = section->start + processed; - list_add_tail(&font_link_info->sections, §ion->entry); + list_add_tail(&info->font_link_info.sections, §ion->entry); progress += processed; } @@ -5214,7 +5213,7 @@ static void generate_font_link_info(GpGraphics *graphics, WCHAR *string, DWORD l IMLangFontLink_Release(iMLFL); } -static void font_link_get_text_extent_point(struct gdip_font_link_info *font_link_info, GpGraphics *graphics, LPCWSTR string, +static void font_link_get_text_extent_point(struct gdip_format_string_info *info, INT index, int length, int max_ext, LPINT fit, SIZE *size) { DWORD to_measure_length; @@ -5229,16 +5228,16 @@ static void font_link_get_text_extent_point(struct gdip_font_link_info *font_lin if (fit) *fit = 0; - LIST_FOR_EACH_ENTRY(section, &font_link_info->sections, struct gdip_font_link_section, entry) + LIST_FOR_EACH_ENTRY(section, &info->font_link_info.sections, struct gdip_font_link_section, entry) { if (i >= section->end) continue; to_measure_length = min(length - (i - index), section->end - i); - get_font_hfont(graphics, section->font, NULL, &hfont, NULL, NULL); - oldhfont = SelectObject(graphics->hdc, hfont); - GetTextExtentExPointW(graphics->hdc, &string[i], to_measure_length, max_ext, &fitaux, NULL, &sizeaux); - SelectObject(graphics->hdc, oldhfont); + get_font_hfont(info->graphics, section->font, NULL, &hfont, NULL, NULL); + oldhfont = SelectObject(info->graphics->hdc, hfont); + GetTextExtentExPointW(info->graphics->hdc, &info->string[i], to_measure_length, max_ext, &fitaux, NULL, &sizeaux); + SelectObject(info->graphics->hdc, oldhfont); DeleteObject(hfont); max_ext -= sizeaux.cx; @@ -5357,10 +5356,10 @@ GpStatus gdip_format_string(GpGraphics *graphics, halign = format->align; - generate_font_link_info(graphics, stringdup, length, font, &info.font_link_info); + generate_font_link_info(&info, length, font); while(sum < length){ - font_link_get_text_extent_point(&info.font_link_info, graphics, stringdup, sum, length - sum, nwidth, &fit, &size); + font_link_get_text_extent_point(&info, sum, length - sum, nwidth, &fit, &size); fitcpy = fit; if(fit == 0) @@ -5408,7 +5407,7 @@ GpStatus gdip_format_string(GpGraphics *graphics, else lineend = fit; - font_link_get_text_extent_point(&info.font_link_info, graphics, stringdup, sum, lineend, nwidth, &j, &size); + font_link_get_text_extent_point(&info, sum, lineend, nwidth, &j, &size); bounds.Width = size.cx; @@ -5530,10 +5529,10 @@ static GpStatus measure_ranges_callback(struct gdip_format_string_info *info) range_rect.Y = info->bounds->Y / args->rel_height; range_rect.Height = info->bounds->Height / args->rel_height; - font_link_get_text_extent_point(&info->font_link_info, info->graphics, info->string, info->index, range_start - info->index, INT_MAX, NULL, &range_size); + font_link_get_text_extent_point(info, info->index, range_start - info->index, INT_MAX, NULL, &range_size); range_rect.X = (info->bounds->X + range_size.cx) / args->rel_width; - font_link_get_text_extent_point(&info->font_link_info, info->graphics, info->string, info->index, range_end - info->index, INT_MAX, NULL, &range_size); + font_link_get_text_extent_point(info, info->index, range_end - info->index, INT_MAX, NULL, &range_size); range_rect.Width = (info->bounds->X + range_size.cx) / args->rel_width - range_rect.X; stat = GdipCombineRegionRect(args->regions[i], &range_rect, CombineModeUnion); @@ -5773,7 +5772,7 @@ static GpStatus draw_string_callback(struct gdip_format_string_info *info) to_draw_length = min(info->length - (i - info->index), section->end - i); TRACE("index %d, todraw %ld, used %s\n", i, to_draw_length, section->font == info->font_link_info.base_font ? "base font" : "map"); - font_link_get_text_extent_point(&info->font_link_info, info->graphics, info->string, i, to_draw_length, 0, NULL, &size); + font_link_get_text_extent_point(info, i, to_draw_length, 0, NULL, &size); stat = draw_driver_string(info->graphics, &info->string[i], to_draw_length, section->font, info->format, args->brush, &position, DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL); @@ -5799,10 +5798,10 @@ static GpStatus draw_string_callback(struct gdip_format_string_info *info) SIZE text_size; INT ofs = info->underlined_indexes[i] - info->index; - font_link_get_text_extent_point(&info->font_link_info, info->graphics, info->string, info->index, ofs, INT_MAX, NULL, &text_size); + font_link_get_text_extent_point(info, info->index, ofs, INT_MAX, NULL, &text_size); start_x = text_size.cx / args->rel_width; - font_link_get_text_extent_point(&info->font_link_info, info->graphics, info->string, info->index, ofs+1, INT_MAX, NULL, &text_size); + font_link_get_text_extent_point(info, info->index, ofs+1, INT_MAX, NULL, &text_size); end_x = text_size.cx / args->rel_width; GdipFillRectangle(info->graphics, (GpBrush*)args->brush, position.X+start_x, underline_y, end_x-start_x, underline_height); From b2a8b66ca0a553d48508306b76f80643823000af Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Fri, 23 Feb 2024 20:19:14 +0000 Subject: [PATCH 219/413] gdiplus: Restore hdc argument to gdip_format_string. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=56345 (cherry picked from commit c37fea89e6202e82e91d6af75f94441a76a9c130) --- dlls/gdiplus/gdiplus_private.h | 3 ++- dlls/gdiplus/graphics.c | 27 ++++++++++++++------------- dlls/gdiplus/graphicspath.c | 6 +++--- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index c88b5407257..1ae03d7126e 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -621,6 +621,7 @@ struct gdip_font_link_info { struct gdip_format_string_info { GpGraphics *graphics; + HDC hdc; GDIPCONST WCHAR *string; INT index; INT length; @@ -636,7 +637,7 @@ struct gdip_format_string_info { typedef GpStatus (*gdip_format_string_callback)(struct gdip_format_string_info *info); -GpStatus gdip_format_string(GpGraphics *graphics, +GpStatus gdip_format_string(GpGraphics *graphics, HDC hdc, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, int ignore_empty_clip, gdip_format_string_callback callback, void *user_data); diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index 1a57a375489..2389cbf4f68 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -5181,7 +5181,7 @@ static void generate_font_link_info(struct gdip_format_string_info *info, DWORD IUnknown_Release(unk); get_font_hfont(info->graphics, base_font, NULL, &hfont, NULL, NULL); - IMLangFontLink_GetFontCodePages(iMLFL, info->graphics->hdc, hfont, &font_codepages); + IMLangFontLink_GetFontCodePages(iMLFL, info->hdc, hfont, &font_codepages); while (progress < length) { @@ -5196,10 +5196,10 @@ static void generate_font_link_info(struct gdip_format_string_info *info, DWORD } else { - IMLangFontLink_MapFont(iMLFL, info->graphics->hdc, string_codepages, hfont, &map_hfont); - old_font = SelectObject(info->graphics->hdc, map_hfont); - GdipCreateFontFromDC(info->graphics->hdc, &gpfont); - SelectObject(info->graphics->hdc, old_font); + IMLangFontLink_MapFont(iMLFL, info->hdc, string_codepages, hfont, &map_hfont); + old_font = SelectObject(info->hdc, map_hfont); + GdipCreateFontFromDC(info->hdc, &gpfont); + SelectObject(info->hdc, old_font); IMLangFontLink_ReleaseFont(iMLFL, map_hfont); section->font = gpfont; } @@ -5235,9 +5235,9 @@ static void font_link_get_text_extent_point(struct gdip_format_string_info *info to_measure_length = min(length - (i - index), section->end - i); get_font_hfont(info->graphics, section->font, NULL, &hfont, NULL, NULL); - oldhfont = SelectObject(info->graphics->hdc, hfont); - GetTextExtentExPointW(info->graphics->hdc, &info->string[i], to_measure_length, max_ext, &fitaux, NULL, &sizeaux); - SelectObject(info->graphics->hdc, oldhfont); + oldhfont = SelectObject(info->hdc, hfont); + GetTextExtentExPointW(info->hdc, &info->string[i], to_measure_length, max_ext, &fitaux, NULL, &sizeaux); + SelectObject(info->hdc, oldhfont); DeleteObject(hfont); max_ext -= sizeaux.cx; @@ -5265,7 +5265,7 @@ static void release_font_link_info(struct gdip_font_link_info *font_link_info) } } -GpStatus gdip_format_string(GpGraphics *graphics, +GpStatus gdip_format_string(GpGraphics *graphics, HDC hdc, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, int ignore_empty_clip, gdip_format_string_callback callback, void *user_data) @@ -5285,6 +5285,7 @@ GpStatus gdip_format_string(GpGraphics *graphics, struct gdip_format_string_info info; info.graphics = graphics; + info.hdc = hdc; info.rect = rect; info.bounds = &bounds; info.user_data = user_data; @@ -5609,7 +5610,7 @@ GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics, gdi_transform_acquire(graphics); - stat = gdip_format_string(graphics, string, length, font, &scaled_rect, stringFormat, + stat = gdip_format_string(graphics, hdc, string, length, font, &scaled_rect, stringFormat, (stringFormat->attr & StringFormatFlagsNoClip) != 0, measure_ranges_callback, &args); gdi_transform_release(graphics); @@ -5728,7 +5729,7 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics, gdi_transform_acquire(graphics); - gdip_format_string(graphics, string, length, font, &scaled_rect, format, TRUE, + gdip_format_string(graphics, hdc, string, length, font, &scaled_rect, format, TRUE, measure_string_callback, &args); gdi_transform_release(graphics); @@ -5787,7 +5788,7 @@ static GpStatus draw_string_callback(struct gdip_format_string_info *info) REAL underline_y, underline_height; int i; - GetOutlineTextMetricsW(info->graphics->hdc, sizeof(otm), &otm); + GetOutlineTextMetricsW(info->hdc, sizeof(otm), &otm); underline_height = otm.otmsUnderscoreSize / args->rel_height; underline_y = position.Y - otm.otmsUnderscorePosition / args->rel_height - underline_height / 2; @@ -5915,7 +5916,7 @@ GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string GetTextMetricsW(hdc, &textmetric); args.ascent = textmetric.tmAscent / rel_height; - gdip_format_string(graphics, string, length, font, &scaled_rect, format, TRUE, + gdip_format_string(graphics, hdc, string, length, font, &scaled_rect, format, TRUE, draw_string_callback, &args); gdi_transform_release(graphics); diff --git a/dlls/gdiplus/graphicspath.c b/dlls/gdiplus/graphicspath.c index 8fbdae1a65a..4b272f539c4 100644 --- a/dlls/gdiplus/graphicspath.c +++ b/dlls/gdiplus/graphicspath.c @@ -971,7 +971,7 @@ static GpStatus format_string_callback(struct gdip_format_string_info* info) TTPOLYGONHEADER *ph = NULL, *origph; char *start; DWORD len, ofs = 0; - len = GetGlyphOutlineW(info->graphics->hdc, info->string[i], GGO_BEZIER, &gm, 0, NULL, &identity); + len = GetGlyphOutlineW(info->hdc, info->string[i], GGO_BEZIER, &gm, 0, NULL, &identity); if (len == GDI_ERROR) { status = GenericError; @@ -985,7 +985,7 @@ static GpStatus format_string_callback(struct gdip_format_string_info* info) status = OutOfMemory; break; } - GetGlyphOutlineW(info->graphics->hdc, info->string[i], GGO_BEZIER, &gm, len, start, &identity); + GetGlyphOutlineW(info->hdc, info->string[i], GGO_BEZIER, &gm, len, start, &identity); ofs = 0; while (ofs < len) @@ -1113,7 +1113,7 @@ GpStatus WINGDIPAPI GdipAddPathString(GpPath* path, GDIPCONST WCHAR* string, INT args.maxY = 0; args.scale = emSize / native_height; args.ascent = textmetric.tmAscent * args.scale; - status = gdip_format_string(graphics, string, length, NULL, &scaled_layout_rect, + status = gdip_format_string(graphics, dc, string, length, NULL, &scaled_layout_rect, format, TRUE, format_string_callback, &args); DeleteDC(dc); From 03da124707fd7ada06e64992056c163e7aaf3b9d Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 13 Mar 2024 18:44:31 -0600 Subject: [PATCH 220/413] ntdll: Wait for thread suspension in NtSuspendThread(). CW-Bug-Id: #23552 --- dlls/ntdll/unix/thread.c | 26 ++++++++++++++++++++++---- server/protocol.def | 6 ++++-- server/thread.c | 28 ++++++++++++++++++++++++---- 3 files changed, 50 insertions(+), 10 deletions(-) diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 64cad6e7c0e..c69d3de3e15 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -1611,22 +1611,40 @@ NTSTATUS WINAPI NtOpenThread( HANDLE *handle, ACCESS_MASK access, /****************************************************************************** * NtSuspendThread (NTDLL.@) */ -NTSTATUS WINAPI NtSuspendThread( HANDLE handle, ULONG *count ) +NTSTATUS WINAPI NtSuspendThread( HANDLE handle, ULONG *ret_count ) { BOOL self = FALSE; - unsigned int ret; + unsigned int ret, count = 0; + HANDLE wait_handle = NULL; SERVER_START_REQ( suspend_thread ) { req->handle = wine_server_obj_handle( handle ); - if (!(ret = wine_server_call( req ))) + if (!(ret = wine_server_call( req )) || ret == STATUS_PENDING) { self = reply->count & 0x80000000; - if (count) *count = reply->count & 0x7fffffff; + count = reply->count & 0x7fffffff;; + wait_handle = wine_server_ptr_handle( reply->wait_handle ); } } SERVER_END_REQ; + if (self) usleep( 0 ); + + if (ret == STATUS_PENDING && wait_handle) + { + NtWaitForSingleObject( wait_handle, FALSE, NULL ); + + SERVER_START_REQ( suspend_thread ) + { + req->handle = wine_server_obj_handle( handle ); + req->waited_handle = wine_server_obj_handle( wait_handle ); + ret = wine_server_call( req ); + } + SERVER_END_REQ; + } + + if (!ret && ret_count) *ret_count = count; return ret; } diff --git a/server/protocol.def b/server/protocol.def index ee75ae5e566..d6926784fe5 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1183,9 +1183,11 @@ typedef volatile struct input_shared_memory input_shm_t; /* Suspend a thread */ @REQ(suspend_thread) - obj_handle_t handle; /* thread handle */ + obj_handle_t handle; /* thread handle */ + obj_handle_t waited_handle; /* handle waited on */ @REPLY - int count; /* new suspend count */ + int count; /* new suspend count */ + obj_handle_t wait_handle; /* handle to wait on */ @END diff --git a/server/thread.c b/server/thread.c index a6039b35d9d..e54e83c178a 100644 --- a/server/thread.c +++ b/server/thread.c @@ -1815,12 +1815,32 @@ DECL_HANDLER(suspend_thread) { struct thread *thread; - if ((thread = get_thread_from_handle( req->handle, THREAD_SUSPEND_RESUME ))) + if (req->waited_handle) { - if (thread->state == TERMINATED) set_error( STATUS_ACCESS_DENIED ); - else reply->count = suspend_thread( thread ); - release_object( thread ); + struct context *context; + + if (!(context = (struct context *)get_handle_obj( current->process, req->waited_handle, + 0, &context_ops ))) + return; + close_handle( current->process, req->waited_handle ); /* avoid extra server call */ + set_error( context->status ); + release_object( context ); + return; } + + if (!(thread = get_thread_from_handle( req->handle, THREAD_SUSPEND_RESUME ))) return; + + if (thread->state != RUNNING) set_error( STATUS_ACCESS_DENIED ); + else + { + reply->count = suspend_thread( thread ); + if (!get_error() && thread != current && thread->context && thread->context->status == STATUS_PENDING) + { + set_error( STATUS_PENDING ); + reply->wait_handle = alloc_handle( current->process, thread->context, SYNCHRONIZE, 0 ); + } + } + release_object( thread ); } /* resume a thread */ From f7cd1adfc72d7f7c93a4201d788784ff7b7c33ca Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 15 Mar 2024 16:20:57 -0600 Subject: [PATCH 221/413] fixup! fshack: winex11: Always blit fs_hack in wglFlush and wglFinish when drawing to front buffer. CW-Bug-Id: #23582 --- dlls/winex11.drv/opengl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index 74c8786c50b..23eb26420fa 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -2739,7 +2739,9 @@ static void wglDrawBuffer( GLenum buffer ) TRACE( "buffer %#x.\n", buffer ); - ctx->drawing_to_front = (buffer == GL_FRONT); + if (!ctx->current_draw_fbo || (ctx->fs_hack && ctx->current_draw_fbo == ctx->fs_hack_fbo)) + ctx->drawing_to_front = (buffer == GL_FRONT || buffer == GL_FRONT_AND_BACK); + if (ctx->fs_hack && ctx->current_draw_fbo == ctx->fs_hack_fbo) { TRACE( "Overriding %#x with GL_COLOR_ATTACHMENT0\n", buffer ); From 79c7c4bb9859670e4c92a4f2378686873df751c7 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 15 Mar 2024 19:52:04 -0600 Subject: [PATCH 222/413] ddraw: Store material handles in ddraw object. CW-Bug-Id: #23583 --- dlls/ddraw/ddraw.c | 36 ++++++++++++++++++++++++++ dlls/ddraw/ddraw_private.h | 52 ++++++++++++++++++++------------------ dlls/ddraw/device.c | 10 +------- dlls/ddraw/material.c | 4 +-- dlls/ddraw/viewport.c | 2 +- 5 files changed, 67 insertions(+), 37 deletions(-) diff --git a/dlls/ddraw/ddraw.c b/dlls/ddraw/ddraw.c index 8603f4a1113..f729a35d1bf 100644 --- a/dlls/ddraw/ddraw.c +++ b/dlls/ddraw/ddraw.c @@ -420,6 +420,8 @@ static void ddraw_destroy_swapchain(struct ddraw *ddraw) *****************************************************************************/ static void ddraw_destroy(struct ddraw *This) { + unsigned int i; + IDirectDraw7_SetCooperativeLevel(&This->IDirectDraw7_iface, NULL, DDSCL_NORMAL); IDirectDraw7_RestoreDisplayMode(&This->IDirectDraw7_iface); @@ -441,6 +443,31 @@ static void ddraw_destroy(struct ddraw *This) wined3d_device_decref(This->wined3d_device); wined3d_decref(This->wined3d); + for (i = 0; i < This->handle_table.entry_count; ++i) + { + struct ddraw_handle_entry *entry = &This->handle_table.entries[i]; + + switch (entry->type) + { + case DDRAW_HANDLE_FREE: + break; + + case DDRAW_HANDLE_MATERIAL: + { + struct d3d_material *m = entry->object; + FIXME("Material handle %#x (%p) not unset properly.\n", i + 1, m); + m->Handle = 0; + break; + } + + default: + FIXME("Handle %#x (%p) has unknown type %#x.\n", i + 1, entry->object, entry->type); + break; + } + } + + ddraw_handle_table_destroy(&This->handle_table); + if (This->d3ddevice) This->d3ddevice->ddraw = NULL; @@ -5125,6 +5152,15 @@ HRESULT ddraw_init(struct ddraw *ddraw, DWORD flags, enum wined3d_device_type de list_init(&ddraw->surface_list); + if (!ddraw_handle_table_init(&ddraw->handle_table, 64)) + { + ERR("Failed to initialize handle table.\n"); + ddraw_handle_table_destroy(&ddraw->handle_table); + wined3d_device_decref(ddraw->wined3d_device); + wined3d_decref(ddraw->wined3d); + return DDERR_OUTOFMEMORY; + } + if (FAILED(hr = wined3d_stateblock_create(ddraw->wined3d_device, NULL, WINED3D_SBT_PRIMARY, &ddraw->state))) { ERR("Failed to create the primary stateblock, hr %#lx.\n", hr); diff --git a/dlls/ddraw/ddraw_private.h b/dlls/ddraw/ddraw_private.h index b1fd015aa14..d0f3ebc967c 100644 --- a/dlls/ddraw/ddraw_private.h +++ b/dlls/ddraw/ddraw_private.h @@ -81,6 +81,31 @@ enum ddraw_device_state DDRAW_DEVICE_STATE_NOT_RESTORED, }; +#define DDRAW_INVALID_HANDLE ~0U + +enum ddraw_handle_type +{ + DDRAW_HANDLE_FREE, + DDRAW_HANDLE_MATERIAL, + DDRAW_HANDLE_MATRIX, + DDRAW_HANDLE_STATEBLOCK, + DDRAW_HANDLE_SURFACE, +}; + +struct ddraw_handle_entry +{ + void *object; + enum ddraw_handle_type type; +}; + +struct ddraw_handle_table +{ + struct ddraw_handle_entry *entries; + struct ddraw_handle_entry *free_entries; + UINT table_size; + UINT entry_count; +}; + struct ddraw { /* Interfaces */ @@ -132,6 +157,8 @@ struct ddraw */ struct list surface_list; + struct ddraw_handle_table handle_table; + /* FVF management */ struct FvfToDecl *decls; UINT numConvertedDecls, declArraySize; @@ -285,31 +312,6 @@ struct ddraw_surface *unsafe_impl_from_IDirectDrawSurface7(IDirectDrawSurface7 * struct ddraw_surface *unsafe_impl_from_IDirect3DTexture(IDirect3DTexture *iface); struct ddraw_surface *unsafe_impl_from_IDirect3DTexture2(IDirect3DTexture2 *iface); -#define DDRAW_INVALID_HANDLE ~0U - -enum ddraw_handle_type -{ - DDRAW_HANDLE_FREE, - DDRAW_HANDLE_MATERIAL, - DDRAW_HANDLE_MATRIX, - DDRAW_HANDLE_STATEBLOCK, - DDRAW_HANDLE_SURFACE, -}; - -struct ddraw_handle_entry -{ - void *object; - enum ddraw_handle_type type; -}; - -struct ddraw_handle_table -{ - struct ddraw_handle_entry *entries; - struct ddraw_handle_entry *free_entries; - UINT table_size; - UINT entry_count; -}; - BOOL ddraw_handle_table_init(struct ddraw_handle_table *t, UINT initial_size); void ddraw_handle_table_destroy(struct ddraw_handle_table *t); DWORD ddraw_allocate_handle(struct ddraw_handle_table *t, void *object, enum ddraw_handle_type type); diff --git a/dlls/ddraw/device.c b/dlls/ddraw/device.c index bc1d91ee00b..b86ec044fa4 100644 --- a/dlls/ddraw/device.c +++ b/dlls/ddraw/device.c @@ -303,14 +303,6 @@ static ULONG WINAPI d3d_device_inner_Release(IUnknown *iface) case DDRAW_HANDLE_FREE: break; - case DDRAW_HANDLE_MATERIAL: - { - struct d3d_material *m = entry->object; - FIXME("Material handle %#lx (%p) not unset properly.\n", i + 1, m); - m->Handle = 0; - break; - } - case DDRAW_HANDLE_MATRIX: { /* No FIXME here because this might happen because of sloppy applications. */ @@ -2936,7 +2928,7 @@ static HRESULT WINAPI d3d_device3_SetLightState(IDirect3DDevice3 *iface, { struct d3d_material *m; - if (!(m = ddraw_get_object(&device->handle_table, value - 1, DDRAW_HANDLE_MATERIAL))) + if (!(m = ddraw_get_object(&device->ddraw->handle_table, value - 1, DDRAW_HANDLE_MATERIAL))) { WARN("Invalid material handle.\n"); wined3d_mutex_unlock(); diff --git a/dlls/ddraw/material.c b/dlls/ddraw/material.c index d8cb41f1733..4f8266f00d2 100644 --- a/dlls/ddraw/material.c +++ b/dlls/ddraw/material.c @@ -143,7 +143,7 @@ static ULONG WINAPI d3d_material3_Release(IDirect3DMaterial3 *iface) if (material->Handle) { wined3d_mutex_lock(); - ddraw_free_handle(&material->ddraw->d3ddevice->handle_table, material->Handle - 1, DDRAW_HANDLE_MATERIAL); + ddraw_free_handle(&material->ddraw->handle_table, material->Handle - 1, DDRAW_HANDLE_MATERIAL); wined3d_mutex_unlock(); } @@ -300,7 +300,7 @@ static HRESULT WINAPI d3d_material3_GetHandle(IDirect3DMaterial3 *iface, material->active_device = device_impl; if (!material->Handle) { - DWORD h = ddraw_allocate_handle(&device_impl->handle_table, material, DDRAW_HANDLE_MATERIAL); + DWORD h = ddraw_allocate_handle(&device_impl->ddraw->handle_table, material, DDRAW_HANDLE_MATERIAL); if (h == DDRAW_INVALID_HANDLE) { ERR("Failed to allocate a material handle.\n"); diff --git a/dlls/ddraw/viewport.c b/dlls/ddraw/viewport.c index 4eda2fe4763..db235721f75 100644 --- a/dlls/ddraw/viewport.c +++ b/dlls/ddraw/viewport.c @@ -627,7 +627,7 @@ static HRESULT WINAPI d3d_viewport_SetBackground(IDirect3DViewport3 *iface, D3DM wined3d_mutex_lock(); - if (!(m = ddraw_get_object(&viewport->ddraw->d3ddevice->handle_table, material - 1, DDRAW_HANDLE_MATERIAL))) + if (!(m = ddraw_get_object(&viewport->ddraw->handle_table, material - 1, DDRAW_HANDLE_MATERIAL))) { WARN("Invalid material handle %#lx.\n", material); wined3d_mutex_unlock(); From 06c4161e4b5d69330f4ae5d2fff0762c7b86c9a1 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 15 Mar 2024 19:56:46 -0600 Subject: [PATCH 223/413] ddraw: Store surface handles in ddraw object. CW-Bug-Id: #23583 --- dlls/ddraw/ddraw.c | 8 ++++++++ dlls/ddraw/device.c | 10 +--------- dlls/ddraw/executebuffer.c | 4 ++-- dlls/ddraw/surface.c | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/dlls/ddraw/ddraw.c b/dlls/ddraw/ddraw.c index f729a35d1bf..4d0bbc2654d 100644 --- a/dlls/ddraw/ddraw.c +++ b/dlls/ddraw/ddraw.c @@ -460,6 +460,14 @@ static void ddraw_destroy(struct ddraw *This) break; } + case DDRAW_HANDLE_SURFACE: + { + struct ddraw_surface *surf = entry->object; + FIXME("Texture handle %#x (%p) not unset properly.\n", i + 1, surf); + surf->Handle = 0; + break; + } + default: FIXME("Handle %#x (%p) has unknown type %#x.\n", i + 1, entry->object, entry->type); break; diff --git a/dlls/ddraw/device.c b/dlls/ddraw/device.c index b86ec044fa4..d34be880991 100644 --- a/dlls/ddraw/device.c +++ b/dlls/ddraw/device.c @@ -319,14 +319,6 @@ static ULONG WINAPI d3d_device_inner_Release(IUnknown *iface) break; } - case DDRAW_HANDLE_SURFACE: - { - struct ddraw_surface *surf = entry->object; - FIXME("Texture handle %#lx (%p) not unset properly.\n", i + 1, surf); - surf->Handle = 0; - break; - } - default: FIXME("Handle %#lx (%p) has unknown type %#x.\n", i + 1, entry->object, entry->type); break; @@ -2760,7 +2752,7 @@ static HRESULT WINAPI d3d_device3_SetRenderState(IDirect3DDevice3 *iface, break; } - surf = ddraw_get_object(&device->handle_table, value - 1, DDRAW_HANDLE_SURFACE); + surf = ddraw_get_object(&device->ddraw->handle_table, value - 1, DDRAW_HANDLE_SURFACE); if (!surf) { WARN("Invalid texture handle.\n"); diff --git a/dlls/ddraw/executebuffer.c b/dlls/ddraw/executebuffer.c index 320ce6649d4..f505eea37c6 100644 --- a/dlls/ddraw/executebuffer.c +++ b/dlls/ddraw/executebuffer.c @@ -347,13 +347,13 @@ HRESULT d3d_execute_buffer_execute(struct d3d_execute_buffer *buffer, struct d3d instr += size; - if (!(dst = ddraw_get_object(&device->handle_table, + if (!(dst = ddraw_get_object(&device->ddraw->handle_table, ci->hDestTexture - 1, DDRAW_HANDLE_SURFACE))) { WARN("Invalid destination texture handle %#lx.\n", ci->hDestTexture); continue; } - if (!(src = ddraw_get_object(&device->handle_table, + if (!(src = ddraw_get_object(&device->ddraw->handle_table, ci->hSrcTexture - 1, DDRAW_HANDLE_SURFACE))) { WARN("Invalid source texture handle %#lx.\n", ci->hSrcTexture); diff --git a/dlls/ddraw/surface.c b/dlls/ddraw/surface.c index f7b67c830ab..96405a9f8ea 100644 --- a/dlls/ddraw/surface.c +++ b/dlls/ddraw/surface.c @@ -5419,7 +5419,7 @@ static HRESULT WINAPI d3d_texture2_GetHandle(IDirect3DTexture2 *iface, if (!surface->Handle) { - DWORD h = ddraw_allocate_handle(&device_impl->handle_table, surface, DDRAW_HANDLE_SURFACE); + DWORD h = ddraw_allocate_handle(&device_impl->ddraw->handle_table, surface, DDRAW_HANDLE_SURFACE); if (h == DDRAW_INVALID_HANDLE) { ERR("Failed to allocate a texture handle.\n"); @@ -6030,7 +6030,7 @@ static void STDMETHODCALLTYPE ddraw_surface_wined3d_object_destroyed(void *paren /* Having a texture handle set implies that the device still exists. */ if (surface->Handle) - ddraw_free_handle(&surface->ddraw->d3ddevice->handle_table, surface->Handle - 1, DDRAW_HANDLE_SURFACE); + ddraw_free_handle(&surface->ddraw->handle_table, surface->Handle - 1, DDRAW_HANDLE_SURFACE); /* Reduce the ddraw surface count. */ list_remove(&surface->surface_list_entry); From 9104a74cb48d1d9946869ab5b8bc9c55df47c064 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 15 Mar 2024 20:12:04 -0600 Subject: [PATCH 224/413] ddraw: Support multiple devices per ddraw object. CW-Bug-Id: #23583 --- dlls/ddraw/ddraw.c | 8 ++++++-- dlls/ddraw/ddraw_private.h | 3 ++- dlls/ddraw/device.c | 13 +++++-------- dlls/ddraw/main.c | 4 ++-- dlls/ddraw/surface.c | 19 ++++++++++++------- 5 files changed, 27 insertions(+), 20 deletions(-) diff --git a/dlls/ddraw/ddraw.c b/dlls/ddraw/ddraw.c index 4d0bbc2654d..bfb7721c34d 100644 --- a/dlls/ddraw/ddraw.c +++ b/dlls/ddraw/ddraw.c @@ -420,6 +420,7 @@ static void ddraw_destroy_swapchain(struct ddraw *ddraw) *****************************************************************************/ static void ddraw_destroy(struct ddraw *This) { + struct d3d_device *device; unsigned int i; IDirectDraw7_SetCooperativeLevel(&This->IDirectDraw7_iface, NULL, DDSCL_NORMAL); @@ -476,8 +477,10 @@ static void ddraw_destroy(struct ddraw *This) ddraw_handle_table_destroy(&This->handle_table); - if (This->d3ddevice) - This->d3ddevice->ddraw = NULL; + LIST_FOR_EACH_ENTRY(device, &This->d3ddevice_list, struct d3d_device, ddraw_entry) + { + device->ddraw = NULL; + } /* Now free the object */ free(This); @@ -5159,6 +5162,7 @@ HRESULT ddraw_init(struct ddraw *ddraw, DWORD flags, enum wined3d_device_type de ddraw->immediate_context = wined3d_device_get_immediate_context(ddraw->wined3d_device); list_init(&ddraw->surface_list); + list_init(&ddraw->d3ddevice_list); if (!ddraw_handle_table_init(&ddraw->handle_table, 64)) { diff --git a/dlls/ddraw/ddraw_private.h b/dlls/ddraw/ddraw_private.h index d0f3ebc967c..eed8c737037 100644 --- a/dlls/ddraw/ddraw_private.h +++ b/dlls/ddraw/ddraw_private.h @@ -142,7 +142,7 @@ struct ddraw /* D3D things */ HWND d3d_window; - struct d3d_device *d3ddevice; + struct list d3ddevice_list; int d3dversion; /* Various HWNDs */ @@ -335,6 +335,7 @@ struct d3d_device struct wined3d_device *wined3d_device; struct wined3d_device_context *immediate_context; struct ddraw *ddraw; + struct list ddraw_entry; IUnknown *rt_iface; struct wined3d_streaming_buffer vertex_buffer, index_buffer; diff --git a/dlls/ddraw/device.c b/dlls/ddraw/device.c index d34be880991..b0f0bd910dc 100644 --- a/dlls/ddraw/device.c +++ b/dlls/ddraw/device.c @@ -343,7 +343,10 @@ static ULONG WINAPI d3d_device_inner_Release(IUnknown *iface) /* Releasing the render target above may have released the last * reference to the ddraw object. */ if (This->ddraw) - This->ddraw->d3ddevice = NULL; + { + list_remove(&This->ddraw_entry); + This->ddraw = NULL; + } /* Now free the structure */ free(This); @@ -6874,7 +6877,7 @@ static HRESULT d3d_device_init(struct d3d_device *device, struct ddraw *ddraw, c if (version != 1) IUnknown_AddRef(device->rt_iface); - ddraw->d3ddevice = device; + list_add_head(&ddraw->d3ddevice_list, &device->ddraw_entry); wined3d_stateblock_set_render_state(ddraw->state, WINED3D_RS_ZENABLE, d3d_device_update_depth_stencil(device)); @@ -6922,12 +6925,6 @@ HRESULT d3d_device_create(struct ddraw *ddraw, const GUID *guid, struct ddraw_su return DDERR_OUTOFMEMORY; } - if (ddraw->d3ddevice) - { - FIXME("Only one Direct3D device per DirectDraw object supported.\n"); - return DDERR_INVALIDPARAMS; - } - if (!(object = calloc(1, sizeof(*object)))) { ERR("Failed to allocate device memory.\n"); diff --git a/dlls/ddraw/main.c b/dlls/ddraw/main.c index 1ba2900704f..52f82e3c256 100644 --- a/dlls/ddraw/main.c +++ b/dlls/ddraw/main.c @@ -874,8 +874,8 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) WARN("DirectDraw object %p has reference counts {%lu, %lu, %lu, %lu, %lu}.\n", ddraw, ddraw->ref7, ddraw->ref4, ddraw->ref3, ddraw->ref2, ddraw->ref1); - if (ddraw->d3ddevice) - WARN("DirectDraw object %p has Direct3D device %p attached.\n", ddraw, ddraw->d3ddevice); + if (!list_empty(&ddraw->d3ddevice_list)) + WARN("DirectDraw object %p has Direct3D device(s) attached.\n", ddraw); LIST_FOR_EACH_ENTRY(surface, &ddraw->surface_list, struct ddraw_surface, surface_list_entry) { diff --git a/dlls/ddraw/surface.c b/dlls/ddraw/surface.c index 96405a9f8ea..20f79c9453d 100644 --- a/dlls/ddraw/surface.c +++ b/dlls/ddraw/surface.c @@ -1986,6 +1986,8 @@ static HRESULT WINAPI DECLSPEC_HOTPATCH ddraw_surface2_Blt(IDirectDrawSurface2 * *****************************************************************************/ static HRESULT ddraw_surface_attach_surface(struct ddraw_surface *This, struct ddraw_surface *Surf) { + struct d3d_device *device; + TRACE("surface %p, attachment %p.\n", This, Surf); if(Surf == This) @@ -2012,8 +2014,10 @@ static HRESULT ddraw_surface_attach_surface(struct ddraw_surface *This, struct d This->next_attached = Surf; /* Check if the WineD3D depth stencil needs updating */ - if (This->ddraw->d3ddevice) - d3d_device_update_depth_stencil(This->ddraw->d3ddevice); + LIST_FOR_EACH_ENTRY(device, &This->ddraw->d3ddevice_list, struct d3d_device, ddraw_entry) + { + d3d_device_update_depth_stencil(device); + } wined3d_mutex_unlock(); @@ -6733,18 +6737,19 @@ HRESULT ddraw_surface_create(struct ddraw *ddraw, const DDSURFACEDESC2 *surface_ if (ddraw->cooperative_level & DDSCL_EXCLUSIVE) { struct wined3d_swapchain_desc swapchain_desc; + struct d3d_device *device; wined3d_swapchain_get_desc(ddraw->wined3d_swapchain, &swapchain_desc); swapchain_desc.backbuffer_width = mode.width; swapchain_desc.backbuffer_height = mode.height; swapchain_desc.backbuffer_format = mode.format_id; - if (ddraw->d3ddevice) + LIST_FOR_EACH_ENTRY(device, &ddraw->d3ddevice_list, struct d3d_device, ddraw_entry) { - if (ddraw->d3ddevice->recording) - wined3d_stateblock_decref(ddraw->d3ddevice->recording); - ddraw->d3ddevice->recording = NULL; - ddraw->d3ddevice->update_state = ddraw->d3ddevice->state; + if (device->recording) + wined3d_stateblock_decref(device->recording); + device->recording = NULL; + device->update_state = device->state; } wined3d_stateblock_reset(ddraw->state); From 7f1d686fe07cc2eab9d47c303f82fad7624614a9 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 15 Mar 2024 21:40:03 -0600 Subject: [PATCH 225/413] ddraw/tests: Add tests for multiple devices. CW-Bug-Id: #23583 --- dlls/ddraw/tests/ddraw1.c | 80 +++++++++++++++++++++++++ dlls/ddraw/tests/ddraw2.c | 121 +++++++++++++++++++++++++++++++++++--- dlls/ddraw/tests/ddraw4.c | 98 +++++++++++++++++++++++++++--- dlls/ddraw/tests/ddraw7.c | 62 ++++++++++++++++--- 4 files changed, 338 insertions(+), 23 deletions(-) diff --git a/dlls/ddraw/tests/ddraw1.c b/dlls/ddraw/tests/ddraw1.c index 02d6adbb433..88569d01bf5 100644 --- a/dlls/ddraw/tests/ddraw1.c +++ b/dlls/ddraw/tests/ddraw1.c @@ -15457,6 +15457,85 @@ static void test_enum_devices(void) ok(!refcount, "Device has %lu references left.\n", refcount); } +static void test_multiple_devices(void) +{ + D3DTEXTUREHANDLE texture_handle, texture_handle2; + D3DMATERIALHANDLE mat_handle, mat_handle2; + IDirect3DViewport *viewport, *viewport2; + IDirect3DDevice *device, *device2; + IDirectDrawSurface *texture_surf; + IDirect3DMaterial *material; + DDSURFACEDESC surface_desc; + IDirect3DTexture *texture; + IDirectDraw *ddraw; + ULONG refcount; + HWND window; + HRESULT hr; + + window = create_window(); + ddraw = create_ddraw(); + ok(!!ddraw, "Failed to create a ddraw object.\n"); + + if (!(device = create_device_ex(ddraw, window, DDSCL_NORMAL, &IID_IDirect3DHALDevice))) + { + skip("Failed to create a 3D device, skipping test.\n"); + DestroyWindow(window); + return; + } + + device2 = create_device_ex(ddraw, window, DDSCL_NORMAL, &IID_IDirect3DHALDevice); + ok(!!device2, "got NULL.\n"); + + viewport = create_viewport(device, 0, 0, 640, 480); + viewport2 = create_viewport(device2, 0, 0, 640, 480); + + material = create_diffuse_material(device, 1.0f, 0.0f, 0.0f, 1.0f); + hr = IDirect3DMaterial2_GetHandle(material, device, &mat_handle); + ok(hr == D3D_OK, "got %#lx.\n", hr); + hr = IDirect3DMaterial2_GetHandle(material, device, &mat_handle2); + ok(hr == D3D_OK, "got %#lx.\n", hr); + ok(mat_handle == mat_handle2, "got different handles.\n"); + + hr = IDirect3DMaterial_GetHandle(material, device2, &mat_handle2); + ok(hr == D3D_OK, "got %#lx.\n", hr); + todo_wine ok(mat_handle != mat_handle2, "got same handles.\n"); + + hr = IDirect3DViewport_SetBackground(viewport, mat_handle); + ok(hr == D3D_OK, "got %#lx.\n", hr); + hr = IDirect3DViewport_SetBackground(viewport2, mat_handle); + ok(hr == D3D_OK, "got %#lx.\n", hr); + + memset(&surface_desc, 0, sizeof(surface_desc)); + surface_desc.dwSize = sizeof(surface_desc); + surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_TEXTURE; + surface_desc.dwWidth = 256; + surface_desc.dwHeight = 256; + hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &texture_surf, NULL); + ok(hr == D3D_OK, "got %#lx.\n", hr); + hr = IDirectDrawSurface_QueryInterface(texture_surf, &IID_IDirect3DTexture2, (void **)&texture); + ok(hr == D3D_OK, "got %#lx.\n", hr); + hr = IDirect3DTexture_GetHandle(texture, device, &texture_handle); + ok(hr == D3D_OK, "got %#lx.\n", hr); + hr = IDirect3DTexture_GetHandle(texture, device2, &texture_handle2); + ok(hr == D3D_OK, "got %#lx.\n", hr); + ok(texture_handle != texture_handle2, "got same handles.\n"); + + IDirect3DTexture_Release(texture); + IDirectDrawSurface_Release(texture_surf); + IDirect3DMaterial_Release(material); + IDirect3DViewport_Release(viewport); + IDirect3DViewport_Release(viewport2); + + refcount = IDirect3DDevice_Release(device); + ok(!refcount, "Device has %lu references left.\n", refcount); + refcount = IDirect3DDevice_Release(device2); + ok(!refcount, "Device has %lu references left.\n", refcount); + + IDirectDraw_Release(ddraw); + DestroyWindow(window); +} + START_TEST(ddraw1) { DDDEVICEIDENTIFIER identifier; @@ -15576,4 +15655,5 @@ START_TEST(ddraw1) run_for_each_device_type(test_texture_wrong_caps); test_filling_convention(); test_enum_devices(); + test_multiple_devices(); } diff --git a/dlls/ddraw/tests/ddraw2.c b/dlls/ddraw/tests/ddraw2.c index f4cc5df0558..1ddb36b3b4f 100644 --- a/dlls/ddraw/tests/ddraw2.c +++ b/dlls/ddraw/tests/ddraw2.c @@ -462,7 +462,8 @@ static IDirectDraw2 *create_ddraw(void) return ddraw2; } -static IDirect3DDevice2 *create_device_ex(IDirectDraw2 *ddraw, HWND window, DWORD coop_level, const GUID *device_guid) +static IDirect3DDevice2 *create_device_ex(IDirectDraw2 *ddraw, HWND window, DWORD coop_level, const GUID *device_guid, + IDirectDrawSurface **ret_surface) { /* Prefer 16 bit depth buffers because Nvidia gives us an unpadded D24 buffer if we ask * for 24 bit and handles such buffers incorrectly in DDBLT_DEPTHFILL. AMD only supports @@ -541,13 +542,17 @@ static IDirect3DDevice2 *create_device_ex(IDirectDraw2 *ddraw, HWND window, DWOR } IDirect3D2_Release(d3d); - IDirectDrawSurface_Release(surface); + if (ret_surface) + *ret_surface = surface; + else + IDirectDrawSurface_Release(surface); + return device; } static IDirect3DDevice2 *create_device(IDirectDraw2 *ddraw, HWND window, DWORD coop_level) { - return create_device_ex(ddraw, window, coop_level, &IID_IDirect3DHALDevice); + return create_device_ex(ddraw, window, coop_level, &IID_IDirect3DHALDevice, NULL); } static IDirect3DViewport2 *create_viewport(IDirect3DDevice2 *device, UINT x, UINT y, UINT w, UINT h) @@ -1330,7 +1335,7 @@ static void test_depth_blit(const GUID *device_guid) window = create_window(); ddraw = create_ddraw(); ok(!!ddraw, "Failed to create a ddraw object.\n"); - if (!(device = create_device_ex(ddraw, window, DDSCL_NORMAL, device_guid))) + if (!(device = create_device_ex(ddraw, window, DDSCL_NORMAL, device_guid, NULL))) { skip("Failed to create a 3D device, skipping test.\n"); IDirectDraw2_Release(ddraw); @@ -1856,7 +1861,7 @@ static void test_zenable(const GUID *device_guid) window = create_window(); ddraw = create_ddraw(); ok(!!ddraw, "Failed to create a ddraw object.\n"); - if (!(device = create_device_ex(ddraw, window, DDSCL_NORMAL, device_guid))) + if (!(device = create_device_ex(ddraw, window, DDSCL_NORMAL, device_guid, NULL))) { skip("Failed to create a 3D device, skipping test.\n"); IDirectDraw2_Release(ddraw); @@ -1971,7 +1976,7 @@ static void test_ck_rgba(const GUID *device_guid) window = create_window(); ddraw = create_ddraw(); ok(!!ddraw, "Failed to create a ddraw object.\n"); - if (!(device = create_device_ex(ddraw, window, DDSCL_NORMAL, device_guid))) + if (!(device = create_device_ex(ddraw, window, DDSCL_NORMAL, device_guid, NULL))) { skip("Failed to create a 3D device, skipping test.\n"); IDirectDraw2_Release(ddraw); @@ -5204,7 +5209,7 @@ static void test_rt_caps(const GUID *device_guid) window = create_window(); ddraw = create_ddraw(); ok(!!ddraw, "Failed to create a ddraw object.\n"); - if (!(device = create_device_ex(ddraw, window, DDSCL_NORMAL, device_guid))) + if (!(device = create_device_ex(ddraw, window, DDSCL_NORMAL, device_guid, NULL))) { skip("Failed to create a 3D device, skipping test.\n"); IDirectDraw2_Release(ddraw); @@ -15692,7 +15697,7 @@ static void test_texture_wrong_caps(const GUID *device_guid) window = create_window(); ddraw = create_ddraw(); ok(!!ddraw, "Failed to create a ddraw object.\n"); - if (!(device = create_device_ex(ddraw, window, DDSCL_NORMAL, device_guid))) + if (!(device = create_device_ex(ddraw, window, DDSCL_NORMAL, device_guid, NULL))) { skip("Failed to create a 3D device, skipping test.\n"); DestroyWindow(window); @@ -16442,6 +16447,105 @@ static void run_for_each_device_type(void (*test_func)(const GUID *)) test_func(&IID_IDirect3DRGBDevice); } +static void test_multiple_devices(void) +{ + D3DTEXTUREHANDLE texture_handle, texture_handle2; + IDirectDrawSurface *surface, *texture_surf; + D3DMATERIALHANDLE mat_handle, mat_handle2; + IDirect3DViewport2 *viewport, *viewport2; + IDirect3DDevice2 *device, *device2; + IDirect3DMaterial2 *material; + DDSURFACEDESC surface_desc; + IDirect3DTexture2 *texture; + IDirectDraw2 *ddraw; + IDirect3D2 *d3d; + ULONG refcount; + HWND window; + HRESULT hr; + + window = create_window(); + ddraw = create_ddraw(); + ok(!!ddraw, "Failed to create a ddraw object.\n"); + + if (!(device = create_device_ex(ddraw, window, DDSCL_NORMAL, &IID_IDirect3DHALDevice, &surface))) + { + skip("Failed to create a 3D device, skipping test.\n"); + DestroyWindow(window); + return; + } + + hr = IDirect3DDevice2_GetDirect3D(device, &d3d); + ok(hr == D3D_OK, "got %#lx.\n", hr); + + hr = IDirect3D2_CreateDevice(d3d, &IID_IDirect3DHALDevice, surface, &device2); + ok(hr == D3D_OK, "got %#lx.\n", hr); + + viewport = create_viewport(device, 0, 0, 640, 480); + viewport2 = create_viewport(device2, 0, 0, 640, 480); + hr = IDirect3DDevice2_SetCurrentViewport(device, viewport); + ok(hr == D3D_OK, "got %#lx.\n", hr); + hr = IDirect3DDevice2_SetCurrentViewport(device2, viewport); + ok(hr == DDERR_INVALIDPARAMS, "got %#lx.\n", hr); + hr = IDirect3DDevice2_SetCurrentViewport(device2, viewport2); + ok(hr == D3D_OK, "got %#lx.\n", hr); + + material = create_diffuse_material(device, 1.0f, 0.0f, 0.0f, 1.0f); + hr = IDirect3DMaterial2_GetHandle(material, device, &mat_handle); + ok(hr == D3D_OK, "got %#lx.\n", hr); + hr = IDirect3DMaterial2_GetHandle(material, device, &mat_handle2); + ok(hr == D3D_OK, "got %#lx.\n", hr); + ok(mat_handle == mat_handle2, "got different handles.\n"); + + hr = IDirect3DMaterial2_GetHandle(material, device2, &mat_handle2); + ok(hr == D3D_OK, "got %#lx.\n", hr); + todo_wine ok(mat_handle != mat_handle2, "got same handles.\n"); + + hr = IDirect3DDevice2_SetLightState(device, D3DLIGHTSTATE_MATERIAL, mat_handle); + ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + hr = IDirect3DDevice2_SetLightState(device2, D3DLIGHTSTATE_MATERIAL, mat_handle); + ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + hr = IDirect3DDevice2_SetLightState(device, D3DLIGHTSTATE_MATERIAL, mat_handle2); + ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + + hr = IDirect3DViewport2_SetBackground(viewport, mat_handle); + ok(hr == D3D_OK, "got %#lx.\n", hr); + hr = IDirect3DViewport2_SetBackground(viewport2, mat_handle); + ok(hr == D3D_OK, "got %#lx.\n", hr); + + memset(&surface_desc, 0, sizeof(surface_desc)); + surface_desc.dwSize = sizeof(surface_desc); + surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_TEXTURE; + surface_desc.dwWidth = 256; + surface_desc.dwHeight = 256; + hr = IDirectDraw2_CreateSurface(ddraw, &surface_desc, &texture_surf, NULL); + ok(hr == D3D_OK, "got %#lx.\n", hr); + hr = IDirectDrawSurface_QueryInterface(texture_surf, &IID_IDirect3DTexture2, (void **)&texture); + ok(hr == D3D_OK, "got %#lx.\n", hr); + hr = IDirect3DTexture2_GetHandle(texture, device, &texture_handle); + ok(hr == D3D_OK, "got %#lx.\n", hr); + hr = IDirect3DTexture2_GetHandle(texture, device2, &texture_handle2); + ok(hr == D3D_OK, "got %#lx.\n", hr); + ok(texture_handle == texture_handle2, "got different handles.\n"); + + IDirect3DTexture2_Release(texture); + IDirectDrawSurface_Release(texture_surf); + IDirect3DMaterial2_Release(material); + IDirect3DViewport2_Release(viewport); + IDirect3DViewport2_Release(viewport2); + + refcount = IDirect3DDevice2_Release(device); + ok(!refcount, "Device has %lu references left.\n", refcount); + refcount = IDirect3DDevice2_Release(device2); + ok(!refcount, "Device has %lu references left.\n", refcount); + refcount = IDirectDrawSurface_Release(surface); + ok(!refcount, "Surface has %lu references left.\n", refcount); + + IDirectDraw2_Release(ddraw); + IDirect3D2_Release(d3d); + DestroyWindow(window); +} + START_TEST(ddraw2) { DDDEVICEIDENTIFIER identifier; @@ -16567,4 +16671,5 @@ START_TEST(ddraw2) run_for_each_device_type(test_texture_wrong_caps); test_filling_convention(); test_enum_devices(); + test_multiple_devices(); } diff --git a/dlls/ddraw/tests/ddraw4.c b/dlls/ddraw/tests/ddraw4.c index 4811053a231..8c850ca2c93 100644 --- a/dlls/ddraw/tests/ddraw4.c +++ b/dlls/ddraw/tests/ddraw4.c @@ -450,7 +450,8 @@ static IDirectDraw4 *create_ddraw(void) return ddraw4; } -static IDirect3DDevice3 *create_device_ex(HWND window, DWORD coop_level, const GUID *device_guid) +static IDirect3DDevice3 *create_device_ex(HWND window, DWORD coop_level, const GUID *device_guid, + IDirectDrawSurface4 **ret_surface) { IDirectDrawSurface4 *surface, *ds; IDirect3DDevice3 *device = NULL; @@ -539,16 +540,22 @@ static IDirect3DDevice3 *create_device_ex(HWND window, DWORD coop_level, const G hr = IDirect3D3_CreateDevice(d3d3, device_guid, surface, &device, NULL); IDirect3D3_Release(d3d3); - IDirectDrawSurface4_Release(surface); if (FAILED(hr)) + { + IDirectDrawSurface4_Release(surface); return NULL; + } + if (ret_surface) + *ret_surface = surface; + else + IDirectDrawSurface4_Release(surface); return device; } static IDirect3DDevice3 *create_device(HWND window, DWORD coop_level) { - return create_device_ex(window, coop_level, &IID_IDirect3DHALDevice); + return create_device_ex(window, coop_level, &IID_IDirect3DHALDevice, NULL); } static IDirect3DViewport3 *create_viewport(IDirect3DDevice3 *device, UINT x, UINT y, UINT w, UINT h) @@ -1506,7 +1513,7 @@ static void test_depth_blit(const GUID *device_guid) D3DRECT d3drect; window = create_window(); - if (!(device = create_device_ex(window, DDSCL_NORMAL, device_guid))) + if (!(device = create_device_ex(window, DDSCL_NORMAL, device_guid, NULL))) { skip("Failed to create a 3D device, skipping test.\n"); DestroyWindow(window); @@ -2101,7 +2108,7 @@ static void test_zenable(const GUID *device_guid) HRESULT hr; window = create_window(); - if (!(device = create_device_ex(window, DDSCL_NORMAL, device_guid))) + if (!(device = create_device_ex(window, DDSCL_NORMAL, device_guid, NULL))) { skip("Failed to create a 3D device, skipping test.\n"); DestroyWindow(window); @@ -2213,7 +2220,7 @@ static void test_ck_rgba(const GUID *device_guid) HRESULT hr; window = create_window(); - if (!(device = create_device_ex(window, DDSCL_NORMAL, device_guid))) + if (!(device = create_device_ex(window, DDSCL_NORMAL, device_guid, NULL))) { skip("Failed to create a 3D device, skipping test.\n"); DestroyWindow(window); @@ -18750,7 +18757,7 @@ static void test_texture_wrong_caps(const GUID *device_guid) HRESULT hr; window = create_window(); - if (!(device = create_device_ex(window, DDSCL_NORMAL, device_guid))) + if (!(device = create_device_ex(window, DDSCL_NORMAL, device_guid, NULL))) { skip("Failed to create a 3D device, skipping test.\n"); DestroyWindow(window); @@ -19506,6 +19513,82 @@ static void test_enum_devices(void) ok(!refcount, "Device has %lu references left.\n", refcount); } +static void test_multiple_devices(void) +{ + D3DMATERIALHANDLE mat_handle, mat_handle2; + IDirect3DViewport3 *viewport, *viewport2; + IDirect3DDevice3 *device, *device2; + IDirect3DMaterial3 *material; + IDirectDrawSurface4 *surface; + IDirectDraw4 *ddraw; + IDirect3D3 *d3d; + ULONG refcount; + HWND window; + HRESULT hr; + + window = create_window(); + if (!(device = create_device_ex(window, DDSCL_NORMAL, &IID_IDirect3DHALDevice, &surface))) + { + skip("Failed to create a 3D device, skipping test.\n"); + DestroyWindow(window); + return; + } + + hr = IDirect3DDevice3_GetDirect3D(device, &d3d); + ok(hr == D3D_OK, "got %#lx.\n", hr); + hr = IDirect3DDevice3_QueryInterface(d3d, &IID_IDirectDraw4, (void **)&ddraw); + ok(hr == D3D_OK, "got %#lx.\n", hr); + + hr = IDirect3D3_CreateDevice(d3d, &IID_IDirect3DHALDevice, surface, &device2, NULL); + ok(hr == D3D_OK, "got %#lx.\n", hr); + + viewport = create_viewport(device, 0, 0, 640, 480); + viewport2 = create_viewport(device2, 0, 0, 640, 480); + hr = IDirect3DDevice3_SetCurrentViewport(device, viewport); + ok(hr == D3D_OK, "got %#lx.\n", hr); + hr = IDirect3DDevice3_SetCurrentViewport(device2, viewport); + ok(hr == DDERR_INVALIDPARAMS, "got %#lx.\n", hr); + hr = IDirect3DDevice3_SetCurrentViewport(device2, viewport2); + ok(hr == D3D_OK, "got %#lx.\n", hr); + + material = create_diffuse_material(device, 1.0f, 0.0f, 0.0f, 1.0f); + hr = IDirect3DMaterial3_GetHandle(material, device, &mat_handle); + ok(hr == D3D_OK, "got %#lx.\n", hr); + hr = IDirect3DMaterial3_GetHandle(material, device, &mat_handle2); + ok(hr == D3D_OK, "got %#lx.\n", hr); + ok(mat_handle == mat_handle2, "got different handles.\n"); + + hr = IDirect3DMaterial3_GetHandle(material, device2, &mat_handle2); + ok(hr == D3D_OK, "got %#lx.\n", hr); + todo_wine ok(mat_handle != mat_handle2, "got same handles.\n"); + + hr = IDirect3DDevice3_SetLightState(device, D3DLIGHTSTATE_MATERIAL, mat_handle); + ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + hr = IDirect3DDevice3_SetLightState(device2, D3DLIGHTSTATE_MATERIAL, mat_handle); + ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + hr = IDirect3DDevice3_SetLightState(device, D3DLIGHTSTATE_MATERIAL, mat_handle2); + ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + + hr = IDirect3DViewport3_SetBackground(viewport, mat_handle); + ok(hr == D3D_OK, "got %#lx.\n", hr); + hr = IDirect3DViewport3_SetBackground(viewport2, mat_handle); + ok(hr == D3D_OK, "got %#lx.\n", hr); + + IDirect3DMaterial3_Release(material); + IDirect3DViewport3_Release(viewport); + IDirect3DViewport3_Release(viewport2); + + refcount = IDirect3DDevice3_Release(device); + ok(!refcount, "Device has %lu references left.\n", refcount); + refcount = IDirect3DDevice3_Release(device2); + ok(!refcount, "Device has %lu references left.\n", refcount); + refcount = IDirectDrawSurface4_Release(surface); + ok(!refcount, "Surface has %lu references left.\n", refcount); + IDirectDraw4_Release(ddraw); + IDirect3D3_Release(d3d); + DestroyWindow(window); +} + START_TEST(ddraw4) { DDDEVICEIDENTIFIER identifier; @@ -19647,4 +19730,5 @@ START_TEST(ddraw4) run_for_each_device_type(test_texture_wrong_caps); test_filling_convention(); test_enum_devices(); + test_multiple_devices(); } diff --git a/dlls/ddraw/tests/ddraw7.c b/dlls/ddraw/tests/ddraw7.c index 2771a4ed368..2ec1ab611e4 100644 --- a/dlls/ddraw/tests/ddraw7.c +++ b/dlls/ddraw/tests/ddraw7.c @@ -498,7 +498,8 @@ static HRESULT WINAPI enum_devtype_cb(char *desc_str, char *name, D3DDEVICEDESC7 return DDENUMRET_OK; } -static IDirect3DDevice7 *create_device_ex(HWND window, DWORD coop_level, const GUID *device_guid) +static IDirect3DDevice7 *create_device_ex(HWND window, DWORD coop_level, const GUID *device_guid, + IDirectDrawSurface7 **ret_surface) { IDirectDrawSurface7 *surface, *ds; IDirect3DDevice7 *device = NULL; @@ -586,9 +587,16 @@ static IDirect3DDevice7 *create_device_ex(HWND window, DWORD coop_level, const G hr = IDirect3D7_CreateDevice(d3d7, device_guid, surface, &device); IDirect3D7_Release(d3d7); - IDirectDrawSurface7_Release(surface); if (FAILED(hr)) + { + IDirectDrawSurface7_Release(surface); return NULL; + } + + if (ret_surface) + *ret_surface = surface; + else + IDirectDrawSurface7_Release(surface); return device; } @@ -615,7 +623,7 @@ static IDirect3DDevice7 *create_device(HWND window, DWORD coop_level) IDirect3D7_Release(d3d7); - return create_device_ex(window, coop_level, device_guid); + return create_device_ex(window, coop_level, device_guid, NULL); } static bool init_3d_test_context_guid(struct ddraw_test_context *context, const GUID *device_guid) @@ -625,7 +633,7 @@ static bool init_3d_test_context_guid(struct ddraw_test_context *context, const memset(context, 0, sizeof(*context)); context->window = create_window(); - if (!(context->device = create_device_ex(context->window, DDSCL_NORMAL, device_guid))) + if (!(context->device = create_device_ex(context->window, DDSCL_NORMAL, device_guid, NULL))) { skip("Failed to create a D3D device.\n"); DestroyWindow(context->window); @@ -1586,7 +1594,7 @@ static void test_depth_blit(const GUID *device_guid) HWND window; window = create_window(); - if (!(device = create_device_ex(window, DDSCL_NORMAL, device_guid))) + if (!(device = create_device_ex(window, DDSCL_NORMAL, device_guid, NULL))) { skip("Failed to create a 3D device, skipping test.\n"); DestroyWindow(window); @@ -1844,7 +1852,7 @@ static void test_zenable(const GUID *device_guid) HRESULT hr; window = create_window(); - if (!(device = create_device_ex(window, DDSCL_NORMAL, device_guid))) + if (!(device = create_device_ex(window, DDSCL_NORMAL, device_guid, NULL))) { skip("Failed to create a 3D device, skipping test.\n"); DestroyWindow(window); @@ -1948,7 +1956,7 @@ static void test_ck_rgba(const GUID *device_guid) HRESULT hr; window = create_window(); - if (!(device = create_device_ex(window, DDSCL_NORMAL, device_guid))) + if (!(device = create_device_ex(window, DDSCL_NORMAL, device_guid, NULL))) { skip("Failed to create a 3D device, skipping test.\n"); DestroyWindow(window); @@ -19134,7 +19142,7 @@ static void test_texture_wrong_caps(const GUID *device_guid) HRESULT hr; window = create_window(); - if (!(device = create_device_ex(window, DDSCL_NORMAL, device_guid))) + if (!(device = create_device_ex(window, DDSCL_NORMAL, device_guid, NULL))) { skip("Failed to create a 3D device, skipping test.\n"); DestroyWindow(window); @@ -20001,6 +20009,43 @@ static void run_for_each_device_type(void (*test_func)(const GUID *)) winetest_pop_context(); } +static void test_multiple_devices(void) +{ + IDirect3DDevice7 *device, *device2; + IDirectDrawSurface7 *surface; + IDirectDraw7 *ddraw; + IDirect3D7 *d3d; + ULONG refcount; + HWND window; + HRESULT hr; + + window = create_window(); + if (!(device = create_device_ex(window, DDSCL_NORMAL, &IID_IDirect3DTnLHalDevice, &surface))) + { + skip("Failed to create a 3D device, skipping test.\n"); + DestroyWindow(window); + return; + } + + hr = IDirect3DDevice7_GetDirect3D(device, &d3d); + ok(hr == D3D_OK, "got %#lx.\n", hr); + hr = IDirect3DDevice7_QueryInterface(d3d, &IID_IDirectDraw7, (void **)&ddraw); + ok(hr == D3D_OK, "got %#lx.\n", hr); + + hr = IDirect3D7_CreateDevice(d3d, &IID_IDirect3DHALDevice, surface, &device2); + ok(hr == D3D_OK, "got %#lx.\n", hr); + + refcount = IDirect3DDevice3_Release(device); + ok(!refcount, "Device has %lu references left.\n", refcount); + refcount = IDirect3DDevice3_Release(device2); + ok(!refcount, "Device has %lu references left.\n", refcount); + refcount = IDirectDrawSurface4_Release(surface); + ok(!refcount, "Surface has %lu references left.\n", refcount); + IDirectDraw4_Release(ddraw); + IDirect3D3_Release(d3d); + DestroyWindow(window); +} + START_TEST(ddraw7) { DDDEVICEIDENTIFIER2 identifier; @@ -20176,4 +20221,5 @@ START_TEST(ddraw7) test_enum_devices(); run_for_each_device_type(test_user_memory); test_flip_3d(); + test_multiple_devices(); } From 637d92edbb2ea900737a7a8871a8f867ff23ae01 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 18 Mar 2024 13:08:40 +0200 Subject: [PATCH 226/413] Revert "HACK: winex11: Add option to WINE_HIDE_NVK." This reverts commit caa6104097b8b14001acd7ee787c8d814a9304d9. --- dlls/winex11.drv/display.c | 9 ++------- dlls/winex11.drv/xrandr.c | 5 ----- include/wine/gdi_driver.h | 1 - 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/dlls/winex11.drv/display.c b/dlls/winex11.drv/display.c index 07e5facb69d..a0cd93bac0f 100644 --- a/dlls/winex11.drv/display.c +++ b/dlls/winex11.drv/display.c @@ -530,7 +530,7 @@ static const char *debugstr_devmodew( const DEVMODEW *devmode ) position ); } -static void fixup_device_id(UINT *vendor_id, UINT *device_id, BOOL is_nvk) +static void fixup_device_id(UINT *vendor_id, UINT *device_id) { const char *sgi; @@ -548,11 +548,6 @@ static void fixup_device_id(UINT *vendor_id, UINT *device_id, BOOL is_nvk) { *device_id = 0x687f; /* Radeon RX Vega 56/64 */ } - else if (is_nvk && (sgi = getenv("WINE_HIDE_NVK")) && *sgi != '0') - { - *vendor_id = 0x1002; /* AMD */ - *device_id = 0x73df; /* RX 6700XT */ - } } BOOL X11DRV_UpdateDisplayDevices( const struct gdi_device_manager *device_manager, BOOL force, void *param ) @@ -576,7 +571,7 @@ BOOL X11DRV_UpdateDisplayDevices( const struct gdi_device_manager *device_manage for (gpu = 0; gpu < gpu_count; gpu++) { - fixup_device_id( &gpus[gpu].vendor_id, &gpus[gpu].device_id, gpus[gpu].is_nvk ); + fixup_device_id( &gpus[gpu].vendor_id, &gpus[gpu].device_id ); device_manager->add_gpu( &gpus[gpu], param ); diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c index 6011a198810..47accf1de6d 100644 --- a/dlls/winex11.drv/xrandr.c +++ b/dlls/winex11.drv/xrandr.c @@ -856,7 +856,6 @@ static BOOL get_gpu_properties_from_vulkan( struct gdi_gpu *gpu, const XRRProvid VkPhysicalDeviceMemoryProperties mem_properties; VkInstanceCreateInfo create_info; VkPhysicalDeviceIDProperties id; - VkPhysicalDeviceVulkan12Properties vk12; VkInstance vk_instance = NULL; VkDisplayKHR vk_display; DWORD len; @@ -921,11 +920,8 @@ static BOOL get_gpu_properties_from_vulkan( struct gdi_gpu *gpu, const XRRProvid if (X11DRV_check_error() || vr != VK_SUCCESS || vk_display == VK_NULL_HANDLE) continue; - memset( &vk12, 0, sizeof(vk12) ); - vk12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES; memset( &id, 0, sizeof(id) ); id.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES; - id.pNext = &vk12; properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; properties2.pNext = &id; @@ -948,7 +944,6 @@ static BOOL get_gpu_properties_from_vulkan( struct gdi_gpu *gpu, const XRRProvid { gpu->vendor_id = properties2.properties.vendorID; gpu->device_id = properties2.properties.deviceID; - if (vk12.driverID == VK_DRIVER_ID_MESA_NVK) gpu->is_nvk = TRUE; } RtlUTF8ToUnicodeN( gpu->name, sizeof(gpu->name), &len, properties2.properties.deviceName, strlen( properties2.properties.deviceName ) + 1 ); diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index 2b1dc3ca044..26562bfef2b 100644 --- a/include/wine/gdi_driver.h +++ b/include/wine/gdi_driver.h @@ -252,7 +252,6 @@ struct gdi_gpu UINT revision_id; GUID vulkan_uuid; /* Vulkan device UUID */ ULONGLONG memory_size; - BOOL is_nvk; }; struct gdi_adapter From e6655a5d616357364c30154e0fc9c2f4968e5239 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 18 Mar 2024 13:08:45 +0200 Subject: [PATCH 227/413] Revert "HACK: winevulkan: Add option to WINE_HIDE_NVK." This reverts commit febbad5b6d551348e0981042cf69dd6e0b9cca9b. --- dlls/winevulkan/loader.c | 19 ++++--------------- dlls/winevulkan/make_vulkan | 3 --- dlls/winevulkan/vulkan.c | 32 +++----------------------------- dlls/winevulkan/vulkan_loader.h | 5 ----- dlls/winevulkan/vulkan_private.h | 5 ----- 5 files changed, 7 insertions(+), 57 deletions(-) diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c index a21f6b3bab8..dbb44c556f6 100644 --- a/dlls/winevulkan/loader.c +++ b/dlls/winevulkan/loader.c @@ -84,12 +84,6 @@ static BOOL is_available_device_function(VkDevice device, const char *name) return UNIX_CALL(is_available_device_function, ¶ms); } -static BOOL is_nvk(VkPhysicalDevice device) -{ - struct is_nvk_params params = { .device = device }; - return UNIX_CALL(is_nvk, ¶ms); -} - static void *alloc_vk_object(size_t size) { struct wine_vk_base *object = calloc(1, size); @@ -422,7 +416,7 @@ static void fill_luid_property(VkPhysicalDeviceProperties2 *properties2) device_node_mask); } -static void fixup_device_id(UINT *vendor_id, UINT *device_id, BOOL is_nvk) +static void fixup_device_id(UINT *vendor_id, UINT *device_id) { const char *sgi; @@ -440,11 +434,6 @@ static void fixup_device_id(UINT *vendor_id, UINT *device_id, BOOL is_nvk) { *device_id = 0x687f; /* Radeon RX Vega 56/64 */ } - else if (is_nvk && (sgi = getenv("WINE_HIDE_NVK")) && *sgi != '0') - { - *vendor_id = 0x1002; /* AMD */ - *device_id = 0x73df; /* RX 6700XT */ - } } void WINAPI vkGetPhysicalDeviceProperties(VkPhysicalDevice physical_device, @@ -459,7 +448,7 @@ void WINAPI vkGetPhysicalDeviceProperties(VkPhysicalDevice physical_device, params.pProperties = properties; status = UNIX_CALL(vkGetPhysicalDeviceProperties, ¶ms); assert(!status); - fixup_device_id(&properties->vendorID, &properties->deviceID, is_nvk(physical_device)); + fixup_device_id(&properties->vendorID, &properties->deviceID); } void WINAPI vkGetPhysicalDeviceProperties2(VkPhysicalDevice phys_dev, @@ -475,7 +464,7 @@ void WINAPI vkGetPhysicalDeviceProperties2(VkPhysicalDevice phys_dev, status = UNIX_CALL(vkGetPhysicalDeviceProperties2, ¶ms); assert(!status); fill_luid_property(properties2); - fixup_device_id(&properties2->properties.vendorID, &properties2->properties.deviceID, is_nvk(phys_dev)); + fixup_device_id(&properties2->properties.vendorID, &properties2->properties.deviceID); } void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, @@ -491,7 +480,7 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, status = UNIX_CALL(vkGetPhysicalDeviceProperties2KHR, ¶ms); assert(!status); fill_luid_property(properties2); - fixup_device_id(&properties2->properties.vendorID, &properties2->properties.deviceID, is_nvk(phys_dev)); + fixup_device_id(&properties2->properties.vendorID, &properties2->properties.deviceID); } VkResult WINAPI vkCreateDevice(VkPhysicalDevice phys_dev, const VkDeviceCreateInfo *create_info, diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 362f0321276..467eee61c2a 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -2925,7 +2925,6 @@ class VkGenerator(object): f.write(" init_vulkan,\n") f.write(" vk_is_available_instance_function,\n") f.write(" vk_is_available_device_function,\n") - f.write(" vk_is_nvk,\n") for vk_func in self.registry.funcs.values(): if not vk_func.needs_exposing(): continue @@ -2950,7 +2949,6 @@ class VkGenerator(object): f.write(" init_vulkan,\n") f.write(" vk_is_available_instance_function32,\n") f.write(" vk_is_available_device_function32,\n") - f.write(" vk_is_nvk32,\n") for vk_func in self.registry.funcs.values(): if not vk_func.needs_exposing(): continue @@ -3135,7 +3133,6 @@ class VkGenerator(object): f.write(" unix_init,\n") f.write(" unix_is_available_instance_function,\n") f.write(" unix_is_available_device_function,\n") - f.write(" unix_is_nvk,\n") for vk_func in self.registry.funcs.values(): if not vk_func.needs_exposing(): continue diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 253e3db68f5..885af7ae970 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -278,8 +278,7 @@ static struct wine_phys_dev *wine_vk_physical_device_alloc(struct wine_instance struct wine_phys_dev *object; uint32_t num_host_properties, num_properties = 0; VkExtensionProperties *host_properties = NULL; - VkPhysicalDeviceProperties2 physdev_properties; - VkPhysicalDeviceVulkan12Properties vk12; + VkPhysicalDeviceProperties physdev_properties; BOOL have_external_memory_host = FALSE, have_external_memory_fd = FALSE, have_external_semaphore_fd = FALSE; VkResult res; unsigned int i, j; @@ -291,16 +290,8 @@ static struct wine_phys_dev *wine_vk_physical_device_alloc(struct wine_instance object->handle = handle; object->host_physical_device = phys_dev; - memset(&vk12, 0, sizeof(vk12)); - vk12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES; - - memset(&physdev_properties, 0, sizeof(physdev_properties)); - physdev_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; - physdev_properties.pNext = &vk12; - - instance->funcs.p_vkGetPhysicalDeviceProperties2(phys_dev, &physdev_properties); - object->api_version = physdev_properties.properties.apiVersion; - object->is_nvk = (vk12.driverID == VK_DRIVER_ID_MESA_NVK); + instance->funcs.p_vkGetPhysicalDeviceProperties(phys_dev, &physdev_properties); + object->api_version = physdev_properties.apiVersion; handle->base.unix_handle = (uintptr_t)object; WINE_VK_ADD_DISPATCHABLE_MAPPING(instance, handle, phys_dev, object); @@ -4068,13 +4059,6 @@ NTSTATUS vk_is_available_device_function(void *arg) return !!vk_funcs->p_vkGetDeviceProcAddr(device->host_device, params->name); } -NTSTATUS vk_is_nvk(void *arg) -{ - struct is_nvk_params *params = arg; - struct wine_phys_dev *device = wine_phys_dev_from_handle(params->device); - return !!device->is_nvk; -} - #endif /* _WIN64 */ NTSTATUS vk_is_available_instance_function32(void *arg) @@ -4103,16 +4087,6 @@ NTSTATUS vk_is_available_device_function32(void *arg) return !!vk_funcs->p_vkGetDeviceProcAddr(device->host_device, name); } -NTSTATUS vk_is_nvk32(void *arg) -{ - struct - { - UINT32 device; - } *params = arg; - struct wine_phys_dev *device = wine_phys_dev_from_handle(UlongToPtr(params->device)); - return !!device->is_nvk; -} - DECLSPEC_EXPORT VkDevice __wine_get_native_VkDevice(VkDevice handle) { struct wine_device *device = wine_device_from_handle(handle); diff --git a/dlls/winevulkan/vulkan_loader.h b/dlls/winevulkan/vulkan_loader.h index d29ccd8ae08..70efc2bfa73 100644 --- a/dlls/winevulkan/vulkan_loader.h +++ b/dlls/winevulkan/vulkan_loader.h @@ -147,11 +147,6 @@ struct is_available_device_function_params const char *name; }; -struct is_nvk_params -{ - VkPhysicalDevice device; -}; - #define wine_vk_find_struct(s, t) wine_vk_find_struct_((void *)s, VK_STRUCTURE_TYPE_##t) static inline void *wine_vk_find_struct_(void *s, VkStructureType t) { diff --git a/dlls/winevulkan/vulkan_private.h b/dlls/winevulkan/vulkan_private.h index 3a044f08dcc..ffae94adbe4 100644 --- a/dlls/winevulkan/vulkan_private.h +++ b/dlls/winevulkan/vulkan_private.h @@ -211,8 +211,6 @@ struct wine_phys_dev uint32_t external_memory_align; - bool is_nvk; - struct wine_vk_mapping mapping; }; @@ -353,9 +351,6 @@ NTSTATUS vk_is_available_device_function(void *arg); NTSTATUS vk_is_available_instance_function32(void *arg); NTSTATUS vk_is_available_device_function32(void *arg); -NTSTATUS vk_is_nvk(void *arg); -NTSTATUS vk_is_nvk32(void *arg); - struct conversion_context { char buffer[2048]; From 85b2b2b9f64cde2eccccbe6103d37f9f906c7945 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Wed, 28 Feb 2024 16:40:50 +0000 Subject: [PATCH 228/413] gdiplus: Fix crash in GdipAddPathString. Apparently, 1454ffe7ddf01226aacec07836d4afa62fecd3fa introduced the assumption that the font passed to gdip_format_string is non-NULL, and GdipAddPathString wasn't passing it in. (cherry picked from commit 1ee1f2e9d78fb3c8da21618c8fef1b4129f200f3) --- dlls/gdiplus/graphicspath.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dlls/gdiplus/graphicspath.c b/dlls/gdiplus/graphicspath.c index 4b272f539c4..61a6182613d 100644 --- a/dlls/gdiplus/graphicspath.c +++ b/dlls/gdiplus/graphicspath.c @@ -1093,7 +1093,6 @@ GpStatus WINGDIPAPI GdipAddPathString(GpPath* path, GDIPCONST WCHAR* string, INT } get_log_fontW(font, graphics, &lfw); - GdipDeleteFont(font); GdipDeleteGraphics(graphics); hfont = CreateFontIndirectW(&lfw); @@ -1102,6 +1101,7 @@ GpStatus WINGDIPAPI GdipAddPathString(GpPath* path, GDIPCONST WCHAR* string, INT WARN("Failed to create font\n"); DeleteDC(dc); GdipDeletePath(backup); + GdipDeleteFont(font); return GenericError; } @@ -1113,11 +1113,12 @@ GpStatus WINGDIPAPI GdipAddPathString(GpPath* path, GDIPCONST WCHAR* string, INT args.maxY = 0; args.scale = emSize / native_height; args.ascent = textmetric.tmAscent * args.scale; - status = gdip_format_string(graphics, dc, string, length, NULL, &scaled_layout_rect, + status = gdip_format_string(graphics, dc, string, length, font, &scaled_layout_rect, format, TRUE, format_string_callback, &args); DeleteDC(dc); DeleteObject(hfont); + GdipDeleteFont(font); if (status != Ok) /* free backup */ { From da87a708c97e8cf09cb7556f322e05db5fc147d9 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Fri, 1 Mar 2024 18:54:33 +0000 Subject: [PATCH 229/413] gdiplus: Fix use after free in GdipAddPathString. (cherry picked from commit 3ca8204837ffa56064c61981fcb5128981f17196) --- dlls/gdiplus/graphicspath.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/gdiplus/graphicspath.c b/dlls/gdiplus/graphicspath.c index 61a6182613d..430097d8ee5 100644 --- a/dlls/gdiplus/graphicspath.c +++ b/dlls/gdiplus/graphicspath.c @@ -1093,7 +1093,6 @@ GpStatus WINGDIPAPI GdipAddPathString(GpPath* path, GDIPCONST WCHAR* string, INT } get_log_fontW(font, graphics, &lfw); - GdipDeleteGraphics(graphics); hfont = CreateFontIndirectW(&lfw); if (!hfont) @@ -1119,6 +1118,7 @@ GpStatus WINGDIPAPI GdipAddPathString(GpPath* path, GDIPCONST WCHAR* string, INT DeleteDC(dc); DeleteObject(hfont); GdipDeleteFont(font); + GdipDeleteGraphics(graphics); if (status != Ok) /* free backup */ { From 614687b78b87c9c7e92845b6c65eb85a2836dd36 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Fri, 1 Mar 2024 19:27:30 +0000 Subject: [PATCH 230/413] gdiplus: Implement font linking in GdipAddPathString. (cherry picked from commit 55f71fd846423338e2029cdce3714903e601bb8e) --- dlls/gdiplus/gdiplus_private.h | 4 ++++ dlls/gdiplus/graphics.c | 2 +- dlls/gdiplus/graphicspath.c | 25 +++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index 1ae03d7126e..0b302a405ea 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -127,6 +127,10 @@ extern void calc_curve_bezier(const GpPointF *pts, REAL tension, REAL *x1, extern void calc_curve_bezier_endp(REAL xend, REAL yend, REAL xadj, REAL yadj, REAL tension, REAL *x, REAL *y); +extern void get_font_hfont(GpGraphics *graphics, GDIPCONST GpFont *font, + GDIPCONST GpStringFormat *format, HFONT *hfont, + LOGFONTW *lfw_return, GDIPCONST GpMatrix *matrix); + extern void free_installed_fonts(void); extern BOOL lengthen_path(GpPath *path, INT len); diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index 2389cbf4f68..ce852e7c940 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -2296,7 +2296,7 @@ void get_log_fontW(const GpFont *font, GpGraphics *graphics, LOGFONTW *lf) lstrcpyW(lf->lfFaceName, font->family->FamilyName); } -static void get_font_hfont(GpGraphics *graphics, GDIPCONST GpFont *font, +void get_font_hfont(GpGraphics *graphics, GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format, HFONT *hfont, LOGFONTW *lfw_return, GDIPCONST GpMatrix *matrix) { diff --git a/dlls/gdiplus/graphicspath.c b/dlls/gdiplus/graphicspath.c index 430097d8ee5..38a195a1371 100644 --- a/dlls/gdiplus/graphicspath.c +++ b/dlls/gdiplus/graphicspath.c @@ -953,6 +953,9 @@ static GpStatus format_string_callback(struct gdip_format_string_info* info) { static const MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} }; struct format_string_args *args = info->user_data; + struct gdip_font_link_section *section = LIST_ENTRY(list_head(&info->font_link_info.sections), struct gdip_font_link_section, entry); + HFONT hfont = NULL, oldhfont = NULL; + int section_start = -1; GpPath *path = args->path; GpStatus status = Ok; float x = info->rect->X + (info->bounds->X - info->rect->X) * args->scale; @@ -971,6 +974,22 @@ static GpStatus format_string_callback(struct gdip_format_string_info* info) TTPOLYGONHEADER *ph = NULL, *origph; char *start; DWORD len, ofs = 0; + + while (i >= section->end) + section = LIST_ENTRY(list_next(&info->font_link_info.sections, §ion->entry), struct gdip_font_link_section, entry); + + if (section_start != section->start) + { + if (hfont) + { + SelectObject(info->hdc, oldhfont); + DeleteObject(hfont); + } + get_font_hfont(info->graphics, section->font, NULL, &hfont, NULL, NULL); + oldhfont = SelectObject(info->hdc, hfont); + section_start = section->start; + } + len = GetGlyphOutlineW(info->hdc, info->string[i], GGO_BEZIER, &gm, 0, NULL, &identity); if (len == GDI_ERROR) { @@ -1037,6 +1056,12 @@ static GpStatus format_string_callback(struct gdip_format_string_info* info) break; } + if (hfont) + { + SelectObject(info->hdc, oldhfont); + DeleteObject(hfont); + } + return status; } From ac0db78396a789729891e0ac2be73d355b5aafe0 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 25 Mar 2024 13:43:29 -0600 Subject: [PATCH 231/413] amend! kernelbase: HACK: Force CEF swiftshader for Snares of Ruin 2. kernelbase: HACK: Force CEF swiftshader for Snares of Ruin 2. CW-Bug-Id: #23613 From 8babfa7338b8676116f28ffce34e65c623312119 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 25 Mar 2024 16:24:01 -0600 Subject: [PATCH 232/413] wintypes: Report some API contracts as present in api_information_statics_IsApiContractPresentByMajor(). CW-Bug-Id: #23614 --- dlls/wintypes/main.c | 25 +++++++++++++++++++++++-- dlls/wintypes/tests/wintypes.c | 26 ++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/dlls/wintypes/main.c b/dlls/wintypes/main.c index d5bfb5ad11c..7d59ab92c41 100644 --- a/dlls/wintypes/main.c +++ b/dlls/wintypes/main.c @@ -34,6 +34,27 @@ WINE_DEFAULT_DEBUG_CHANNEL(wintypes); +static const struct +{ + const WCHAR *name; + unsigned int max_major; +} +present_contracts[] = +{ + { L"Windows.Foundation.UniversalApiContract", 10, }, +}; + +static BOOLEAN is_api_contract_present( const HSTRING hname, unsigned int version ) +{ + const WCHAR *name = WindowsGetStringRawBuffer( hname, NULL ); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(present_contracts); ++i) + if (!wcsicmp( name, present_contracts[i].name )) return version <= present_contracts[i].max_major; + + return FALSE; +} + struct wintypes { IActivationFactory IActivationFactory_iface; @@ -280,13 +301,13 @@ static HRESULT STDMETHODCALLTYPE api_information_statics_IsEnumNamedValuePresent static HRESULT STDMETHODCALLTYPE api_information_statics_IsApiContractPresentByMajor( IApiInformationStatics *iface, HSTRING contract_name, UINT16 major_version, BOOLEAN *value) { - FIXME("iface %p, contract_name %s, major_version %u, value %p stub!\n", iface, + FIXME("iface %p, contract_name %s, major_version %u, value %p semi-stub.\n", iface, debugstr_hstring(contract_name), major_version, value); if (!contract_name) return E_INVALIDARG; - *value = FALSE; + *value = is_api_contract_present( contract_name, major_version ); return S_OK; } diff --git a/dlls/wintypes/tests/wintypes.c b/dlls/wintypes/tests/wintypes.c index dd6401c212b..0703582d325 100644 --- a/dlls/wintypes/tests/wintypes.c +++ b/dlls/wintypes/tests/wintypes.c @@ -33,12 +33,23 @@ static void test_IApiInformationStatics(void) { + static const struct + { + const WCHAR *name; + unsigned int max_major; + } + present_contracts[] = + { + { L"Windows.Foundation.UniversalApiContract", 10, }, + }; + static const WCHAR *class_name = L"Windows.Foundation.Metadata.ApiInformation"; IAgileObject *agile_object = NULL, *tmp_agile_object = NULL; IInspectable *inspectable = NULL, *tmp_inspectable = NULL; IApiInformationStatics *statics = NULL; IActivationFactory *factory = NULL; HSTRING str, str2; + unsigned int i, j; BOOLEAN ret; HRESULT hr; @@ -424,6 +435,21 @@ static void test_IApiInformationStatics(void) WindowsDeleteString(str); + /* Test API contracts presence. */ + for (i = 0; i < ARRAY_SIZE(present_contracts); ++i) + { + hr = WindowsCreateString(present_contracts[i].name, wcslen(present_contracts[i].name), &str); + ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr); + for (j = 0; j <= present_contracts[i].max_major; ++j) + { + ret = FALSE; + hr = IApiInformationStatics_IsApiContractPresentByMajor(statics, str, i, &ret); + ok(hr == S_OK, "IsApiContractPresentByMajor failed, hr %#lx, i %u, major %u.\n", hr, i, j); + ok(ret == TRUE, "IsApiContractPresentByMajor returned FALSE, i %u, major %u.\n", i, j); + } + WindowsDeleteString(str); + } + IApiInformationStatics_Release(statics); IAgileObject_Release(agile_object); IInspectable_Release(inspectable); From d32a706519359aac230b68752a57b7ca0f942f74 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 25 Mar 2024 16:02:02 -0600 Subject: [PATCH 233/413] windows.perception.stub: Add stub IHolographicSpaceInterop interface. CW-Bug-Id: #23614 --- .../holographicspace.c | 33 +++++++++++++++++++ dlls/windows.perception.stub/private.h | 1 + .../tests/perception.c | 2 ++ include/Makefile.in | 1 + include/holographicspaceinterop.idl | 27 +++++++++++++++ 5 files changed, 64 insertions(+) create mode 100644 include/holographicspaceinterop.idl diff --git a/dlls/windows.perception.stub/holographicspace.c b/dlls/windows.perception.stub/holographicspace.c index 8a12147f4a8..53e6be0e712 100644 --- a/dlls/windows.perception.stub/holographicspace.c +++ b/dlls/windows.perception.stub/holographicspace.c @@ -27,6 +27,7 @@ struct holographicspace IActivationFactory IActivationFactory_iface; IHolographicSpaceStatics2 IHolographicSpaceStatics2_iface; IHolographicSpaceStatics3 IHolographicSpaceStatics3_iface; + IHolographicSpaceInterop IHolographicSpaceInterop_iface; LONG ref; }; @@ -65,6 +66,13 @@ static HRESULT WINAPI factory_QueryInterface( IActivationFactory *iface, REFIID return S_OK; } + if (IsEqualGUID( iid, &IID_IHolographicSpaceInterop )) + { + *out = &impl->IHolographicSpaceInterop_iface; + IInspectable_AddRef( *out ); + return S_OK; + } + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); *out = NULL; return E_NOINTERFACE; @@ -193,11 +201,36 @@ static const struct IHolographicSpaceStatics3Vtbl holographicspace_statics3_vtbl holographicspace_statics3_get_IsConfigured, }; +DEFINE_IINSPECTABLE( holographicspace_interop, IHolographicSpaceInterop, struct holographicspace, IActivationFactory_iface ) + +static HRESULT WINAPI holographicspace_interop_CreateForWindow( IHolographicSpaceInterop *iface, + HWND window, REFIID iid, void **holographic_space ) +{ + FIXME( "iface %p, window %p, iid %s, holographic_space %p.\n", iface, window, debugstr_guid( iid ), holographic_space ); + + *holographic_space = NULL; + return E_NOTIMPL; +} + +static const struct IHolographicSpaceInteropVtbl holographicspace_interop_vtbl = +{ + holographicspace_interop_QueryInterface, + holographicspace_interop_AddRef, + holographicspace_interop_Release, + /* IInspectable methods */ + holographicspace_interop_GetIids, + holographicspace_interop_GetRuntimeClassName, + holographicspace_interop_GetTrustLevel, + /* IHolographicSpaceInterop methods */ + holographicspace_interop_CreateForWindow, +}; + static struct holographicspace holographicspace_statics = { {&factory_vtbl}, {&holographicspace_statics2_vtbl}, {&holographicspace_statics3_vtbl}, + {&holographicspace_interop_vtbl}, 1, }; diff --git a/dlls/windows.perception.stub/private.h b/dlls/windows.perception.stub/private.h index a6ae62916e1..ceaa2944174 100644 --- a/dlls/windows.perception.stub/private.h +++ b/dlls/windows.perception.stub/private.h @@ -36,6 +36,7 @@ #include "windows.perception.spatial.surfaces.h" #define WIDL_using_Windows_Graphics_Holographic #include "windows.graphics.holographic.h" +#include "holographicspaceinterop.h" extern IActivationFactory *observer_factory; extern IActivationFactory *holographicspace_factory; diff --git a/dlls/windows.perception.stub/tests/perception.c b/dlls/windows.perception.stub/tests/perception.c index d0bee4eb247..8714f44be4a 100644 --- a/dlls/windows.perception.stub/tests/perception.c +++ b/dlls/windows.perception.stub/tests/perception.c @@ -32,6 +32,7 @@ #include "windows.perception.spatial.surfaces.h" #define WIDL_using_Windows_Graphics_Holographic #include "windows.graphics.holographic.h" +#include "holographicspaceinterop.h" #include "wine/test.h" @@ -121,6 +122,7 @@ static void test_HolographicSpaceStatics(void) check_interface( factory, &IID_IUnknown ); check_interface( factory, &IID_IInspectable ); check_interface( factory, &IID_IAgileObject ); + check_interface( factory, &IID_IHolographicSpaceInterop ); hr = IActivationFactory_QueryInterface( factory, &IID_IHolographicSpaceStatics2, (void **)&holographicspace_statics2 ); if (hr == E_NOINTERFACE) /* win1607 */ diff --git a/include/Makefile.in b/include/Makefile.in index d4a166e265c..7149a364db8 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -345,6 +345,7 @@ SOURCES = \ highlevelmonitorconfigurationapi.h \ hlguids.h \ hlink.idl \ + holographicspaceinterop.idl \ hrtfapoapi.idl \ hstring.idl \ htiface.idl \ diff --git a/include/holographicspaceinterop.idl b/include/holographicspaceinterop.idl new file mode 100644 index 00000000000..913f60bc7a0 --- /dev/null +++ b/include/holographicspaceinterop.idl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 Paul Gofman 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 + */ + +import "inspectable.idl"; + +[ + uuid(5c4ee536-6a98-4b86-a170-587013d6fd4b), +] +interface IHolographicSpaceInterop : IInspectable +{ + HRESULT CreateForWindow( [in] HWND window, [in] REFIID iid, [out, iid_is(iid)] void **holographic_space); +} From 804365831a48f9c0eefb7daf3a72c762015288d7 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 25 Mar 2024 16:21:46 -0600 Subject: [PATCH 234/413] windows.perception.stub: HACK: Set WS_EX_NOACTIVATE for window in holographicspace_interop_CreateForWindow(). CW-Bug-Id: #23614 --- dlls/windows.perception.stub/Makefile.in | 2 +- dlls/windows.perception.stub/holographicspace.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/dlls/windows.perception.stub/Makefile.in b/dlls/windows.perception.stub/Makefile.in index 18c90e697c4..9583b64bb89 100644 --- a/dlls/windows.perception.stub/Makefile.in +++ b/dlls/windows.perception.stub/Makefile.in @@ -1,5 +1,5 @@ MODULE = windows.perception.stub.dll -IMPORTS = combase +IMPORTS = combase user32 SOURCES = \ classes.idl \ diff --git a/dlls/windows.perception.stub/holographicspace.c b/dlls/windows.perception.stub/holographicspace.c index 53e6be0e712..f51be10a9c1 100644 --- a/dlls/windows.perception.stub/holographicspace.c +++ b/dlls/windows.perception.stub/holographicspace.c @@ -208,6 +208,9 @@ static HRESULT WINAPI holographicspace_interop_CreateForWindow( IHolographicSpac { FIXME( "iface %p, window %p, iid %s, holographic_space %p.\n", iface, window, debugstr_guid( iid ), holographic_space ); + FIXME( "HACK: Setting WS_EX_NOACTIVATE for %p.\n", window ); + SetWindowLongW( window, GWL_EXSTYLE, WS_EX_NOACTIVATE ); + *holographic_space = NULL; return E_NOTIMPL; } From fa9a24cbfb5fda07f738dd2a8bc36b589dbf466a Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Mon, 7 Aug 2023 17:30:05 +0800 Subject: [PATCH 235/413] mf: Make session_get_node_object() more robust. It's possible that a state object pointer not in the topology node collection gets passed to session_get_node_object(). Instead of returning the last node when the object is not found, we should return a NULL so that the state of the last node is not changed by mistake. Signed-off-by: Zhiyi Zhang (cherry picked from commit a00b30bfbf499679cb5fc356e5fdb7f9c4334dfb) CW-Bug-Id: #22486 CW-Bug-Id: #22799 --- dlls/mf/session.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index a3cf57ab047..ef707dea4de 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -2864,10 +2864,10 @@ static struct topo_node *session_get_node_object(struct media_session *session, LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) { if (node->type == node_type && object == node->object.object) - break; + return node; } - return node; + return NULL; } static BOOL session_set_node_object_state(struct media_session *session, IUnknown *object, From 1244141e264492931d5e6d85445dc998ad649339 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Wed, 16 Aug 2023 15:17:58 +0800 Subject: [PATCH 236/413] mf: Add a session_flush_nodes() helper. Signed-off-by: Zhiyi Zhang (cherry picked from commit 8173d3b0ab3c620aaaa73b114b22826d2c4c20c4) CW-Bug-Id: #22486 CW-Bug-Id: #22799 --- dlls/mf/session.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index ef707dea4de..d38e7cc0f6b 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -912,6 +912,19 @@ static HRESULT session_subscribe_sources(struct media_session *session) return hr; } +static void session_flush_nodes(struct media_session *session) +{ + struct topo_node *node; + + LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) + { + if (node->type == MF_TOPOLOGY_OUTPUT_NODE) + IMFStreamSink_Flush(node->object.sink_stream); + else if (node->type == MF_TOPOLOGY_TRANSFORM_NODE) + IMFTransform_ProcessMessage(node->object.transform, MFT_MESSAGE_COMMAND_FLUSH, 0); + } +} + static void session_start(struct media_session *session, const GUID *time_format, const PROPVARIANT *start_position) { struct media_source *source; @@ -2892,7 +2905,6 @@ static void session_set_source_object_state(struct media_session *session, IUnkn struct media_source *src; struct media_sink *sink; enum object_state state; - struct topo_node *node; BOOL changed = FALSE; DWORD i, count; HRESULT hr; @@ -2988,21 +3000,7 @@ static void session_set_source_object_state(struct media_session *session, IUnkn if (!session_is_source_nodes_state(session, OBJ_STATE_STOPPED)) break; - LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) - { - switch (node->type) - { - case MF_TOPOLOGY_OUTPUT_NODE: - IMFStreamSink_Flush(node->object.sink_stream); - break; - case MF_TOPOLOGY_TRANSFORM_NODE: - IMFTransform_ProcessMessage(node->object.transform, MFT_MESSAGE_COMMAND_FLUSH, 0); - break; - default: - ; - } - } - + session_flush_nodes(session); session_set_caps(session, session->caps & ~MFSESSIONCAP_PAUSE); if (session->presentation.flags & SESSION_FLAG_FINALIZE_SINKS) From cab06c31aa2134212f470c4c74ebd1b2c6bf93ed Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Tue, 8 Aug 2023 15:24:34 +0800 Subject: [PATCH 237/413] mf/tests: Add a create_test_topology() helper. Signed-off-by: Zhiyi Zhang (cherry picked from commit c1223d0be0d566cc520a1fba2734877537d5058c) CW-Bug-Id: #22486 CW-Bug-Id: #22799 --- dlls/mf/tests/mf.c | 80 +++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 0a34329bd75..35d22839032 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -4995,6 +4995,48 @@ static void test_sample_grabber_is_mediatype_supported(void) IMFSampleGrabberSinkCallback_Release(grabber_callback); } +/* create a test topology with the specified source and sink */ +static IMFTopology *create_test_topology(IMFMediaSource *source, IMFActivate *sink_activate) +{ + IMFTopologyNode *src_node, *sink_node; + IMFPresentationDescriptor *pd; + IMFTopology *topology = NULL; + IMFStreamDescriptor *sd; + BOOL selected; + HRESULT hr; + + hr = MFCreateTopology(&topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &sink_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &src_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopology_AddNode(topology, sink_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopology_AddNode(topology, src_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_ConnectOutput(src_node, 0, sink_node, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSource_CreatePresentationDescriptor(source, &pd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, 0, &selected, &sd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(selected, "got selected %u.\n", !!selected); + init_source_node(source, -1, src_node, pd, sd); + hr = IMFTopologyNode_SetObject(sink_node, (IUnknown *)sink_activate); + ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); + hr = IMFTopologyNode_SetUINT32(sink_node, &MF_TOPONODE_CONNECT_METHOD, MF_CONNECT_ALLOW_DECODER); + ok(hr == S_OK, "Failed to set connect method, hr %#lx.\n", hr); + hr = IMFTopology_SetUINT32(topology, &MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES, TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFStreamDescriptor_Release(sd); + IMFPresentationDescriptor_Release(pd); + IMFTopologyNode_Release(src_node); + IMFTopologyNode_Release(sink_node); + return topology; +} + static void test_sample_grabber_orientation(GUID subtype) { media_type_desc video_rgb32_desc = @@ -5004,17 +5046,13 @@ static void test_sample_grabber_orientation(GUID subtype) }; struct test_grabber_callback *grabber_callback; - IMFTopologyNode *src_node, *sink_node; - IMFPresentationDescriptor *pd; IMFAsyncCallback *callback; IMFActivate *sink_activate; IMFMediaType *output_type; IMFMediaSession *session; - IMFStreamDescriptor *sd; IMFMediaSource *source; IMFTopology *topology; PROPVARIANT propvar; - BOOL selected; HRESULT hr; DWORD res; @@ -5038,30 +5076,6 @@ static void test_sample_grabber_orientation(GUID subtype) hr = MFCreateMediaSession(NULL, &session); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &sink_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &src_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = MFCreateTopology(&topology); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopology_AddNode(topology, sink_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopology_AddNode(topology, src_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopologyNode_ConnectOutput(src_node, 0, sink_node, 0); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSource_CreatePresentationDescriptor(source, &pd); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, 0, &selected, &sd); - ok(selected, "got selected %u.\n", !!selected); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - init_source_node(source, -1, src_node, pd, sd); - IMFTopologyNode_Release(src_node); - IMFPresentationDescriptor_Release(pd); - IMFStreamDescriptor_Release(sd); - hr = MFCreateMediaType(&output_type); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); init_media_type(output_type, video_rgb32_desc, -1); @@ -5069,15 +5083,7 @@ static void test_sample_grabber_orientation(GUID subtype) ok(hr == S_OK, "Failed to create grabber sink, hr %#lx.\n", hr); IMFMediaType_Release(output_type); - hr = IMFTopologyNode_SetObject(sink_node, (IUnknown *)sink_activate); - ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); - hr = IMFTopologyNode_SetUINT32(sink_node, &MF_TOPONODE_CONNECT_METHOD, MF_CONNECT_ALLOW_DECODER); - ok(hr == S_OK, "Failed to set connect method, hr %#lx.\n", hr); - IMFTopologyNode_Release(sink_node); - - hr = IMFTopology_SetUINT32(topology, &MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES, TRUE); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - + topology = create_test_topology(source, sink_activate); hr = IMFMediaSession_SetTopology(session, 0, topology); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); IMFTopology_Release(topology); From 66f568e0d760b99281e58540d4e2a7f274f2c772 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Tue, 24 Oct 2023 12:42:12 -0500 Subject: [PATCH 238/413] mf: Support seeking while a session is started. (cherry picked from commit 9c82ce962bc51f6386c68f96111269c7f884d55a) CW-Bug-Id: #22486 CW-Bug-Id: #22799 --- dlls/mf/session.c | 63 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index d38e7cc0f6b..6590f001a00 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -93,6 +93,7 @@ enum session_state SESSION_STATE_STARTING_SOURCES, SESSION_STATE_PREROLLING_SINKS, SESSION_STATE_STARTING_SINKS, + SESSION_STATE_RESTARTING_SOURCES, SESSION_STATE_STARTED, SESSION_STATE_PAUSING_SINKS, SESSION_STATE_PAUSING_SOURCES, @@ -929,6 +930,7 @@ static void session_start(struct media_session *session, const GUID *time_format { struct media_source *source; struct topo_node *topo_node; + MFTIME duration; HRESULT hr; UINT i; @@ -981,8 +983,36 @@ static void session_start(struct media_session *session, const GUID *time_format session->state = SESSION_STATE_STARTING_SOURCES; break; case SESSION_STATE_STARTED: - FIXME("Seeking is not implemented.\n"); - session_command_complete(session); + /* Check for invalid positions */ + LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) + { + hr = IMFPresentationDescriptor_GetUINT64(source->pd, &MF_PD_DURATION, (UINT64 *)&duration); + if (SUCCEEDED(hr) && IsEqualGUID(time_format, &GUID_NULL) + && start_position->vt == VT_I8 && start_position->hVal.QuadPart > duration) + { + WARN("Start position %s out of range, hr %#lx.\n", wine_dbgstr_longlong(start_position->hVal.QuadPart), hr); + session_command_complete_with_event(session, MESessionStarted, MF_E_INVALID_POSITION, NULL); + return; + } + } + + /* Stop sources */ + LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) + { + if (FAILED(hr = IMFMediaSource_Stop(source->source))) + { + WARN("Failed to stop media source %p, hr %#lx.\n", source->source, hr); + session_command_complete_with_event(session, MESessionStarted, hr, NULL); + return; + } + } + + session->presentation.time_format = *time_format; + session->presentation.start_position.vt = VT_EMPTY; + PropVariantCopy(&session->presentation.start_position, start_position); + + /* SESSION_STATE_STARTED -> SESSION_STATE_RESTARTING_SOURCES -> SESSION_STATE_STARTED */ + session->state = SESSION_STATE_RESTARTING_SOURCES; break; default: session_command_complete_with_event(session, MESessionStarted, MF_E_INVALIDREQUEST, NULL); @@ -2901,6 +2931,7 @@ static BOOL session_set_node_object_state(struct media_session *session, IUnknow static void session_set_source_object_state(struct media_session *session, IUnknown *object, MediaEventType event_type) { + struct media_source *source; IMFStreamSink *stream_sink; struct media_source *src; struct media_sink *sink; @@ -2952,6 +2983,15 @@ static void session_set_source_object_state(struct media_session *session, IUnkn session_set_presentation_clock(session); + /* If sinks are already started, start session immediately. This can happen when doing a + * seek from SESSION_STATE_STARTED */ + if (session_is_output_nodes_state(session, OBJ_STATE_STARTED) + && SUCCEEDED(session_start_clock(session))) + { + session_set_started(session); + return; + } + if ((session->presentation.flags & SESSION_FLAG_NEEDS_PREROLL) && session_is_output_nodes_state(session, OBJ_STATE_STOPPED)) { MFTIME preroll_time = 0; @@ -2989,6 +3029,25 @@ static void session_set_source_object_state(struct media_session *session, IUnkn else if (SUCCEEDED(session_start_clock(session))) session->state = SESSION_STATE_STARTING_SINKS; + break; + case SESSION_STATE_RESTARTING_SOURCES: + if (!session_is_source_nodes_state(session, OBJ_STATE_STOPPED)) + break; + + session_flush_nodes(session); + + /* Start sources */ + LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) + { + if (FAILED(hr = IMFMediaSource_Start(source->source, source->pd, + &session->presentation.time_format, &session->presentation.start_position))) + { + WARN("Failed to start media source %p, hr %#lx.\n", source->source, hr); + session_command_complete_with_event(session, MESessionStarted, hr, NULL); + return; + } + } + session->state = SESSION_STATE_STARTING_SOURCES; break; case SESSION_STATE_PAUSING_SOURCES: if (!session_is_source_nodes_state(session, OBJ_STATE_PAUSED)) From 68110f447f68992f38009ebd73e89b6b5dcb8153 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Mon, 7 Aug 2023 11:52:20 +0800 Subject: [PATCH 239/413] mf/tests: Test IMFMediaSession::Start(). Signed-off-by: Zhiyi Zhang (cherry picked from commit 6b853a9354dfe62df838b255b4af33b423e74642) CW-Bug-Id: #22486 CW-Bug-Id: #22799 --- dlls/mf/tests/mf.c | 960 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 952 insertions(+), 8 deletions(-) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 35d22839032..e6ae0a922d1 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -2075,6 +2075,7 @@ static IMFMediaSource *create_media_source(const WCHAR *name, const WCHAR *mime) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, stream, NULL, MF_RESOLUTION_MEDIASOURCE, NULL, &obj_type, (IUnknown **)&source); + todo_wine_if(hr == MF_E_UNEXPECTED) /* Gitlab CI Debian runner */ ok(hr == S_OK || broken(hr == MF_E_UNSUPPORTED_BYTESTREAM_TYPE), "Unexpected hr %#lx.\n", hr); IMFSourceResolver_Release(resolver); IMFByteStream_Release(stream); @@ -2086,6 +2087,626 @@ static IMFMediaSource *create_media_source(const WCHAR *name, const WCHAR *mime) return source; } +enum object_state +{ + SOURCE_START, + SOURCE_PAUSE, + SOURCE_STOP, + SOURCE_SHUTDOWN, + SINK_ON_CLOCK_START, + SINK_ON_CLOCK_PAUSE, + SINK_ON_CLOCK_STOP, + SINK_ON_CLOCK_RESTART, + SINK_ON_CLOCK_SETRATE, +}; + +#define MAX_OBJECT_STATE 1024 + +struct object_state_record +{ + enum object_state states[MAX_OBJECT_STATE]; + unsigned int state_count; +}; +static struct object_state_record actual_object_state_record; + +#define add_object_state(a, b) _add_object_state(__LINE__, a, b) +static void _add_object_state(int line, struct object_state_record *record, enum object_state state) +{ + ok_(__FILE__, line)(record->state_count < MAX_OBJECT_STATE, "exceeded state_count maximum %d.\n", MAX_OBJECT_STATE); + if (record->state_count < MAX_OBJECT_STATE) + record->states[record->state_count++] = state; +} + +#define compare_object_states(a, b) _compare_object_states(__LINE__, a, b) +static void _compare_object_states(int line, const struct object_state_record *r1, + const struct object_state_record *r2) +{ + ok_(__FILE__, line)(r1->state_count == r2->state_count, "State count not equal.\n"); + if (r1->state_count == r2->state_count) + ok_(__FILE__, line)(!memcmp(r1->states, r2->states, sizeof(enum object_state) * r1->state_count), "Got different states.\n"); +} + +enum source_state +{ + SOURCE_STOPPED, + SOURCE_RUNNING, + SOURCE_PAUSED, +}; + +struct test_media_stream +{ + IMFMediaStream IMFMediaStream_iface; + IMFMediaEventQueue *event_queue; + IMFStreamDescriptor *sd; + IMFMediaSource *source; + LONGLONG sample_duration; + LONGLONG sample_time; + BOOL is_new; + LONG refcount; +}; + +static struct test_media_stream *impl_from_IMFMediaStream(IMFMediaStream *iface) +{ + return CONTAINING_RECORD(iface, struct test_media_stream, IMFMediaStream_iface); +} + +static HRESULT WINAPI test_media_stream_QueryInterface(IMFMediaStream *iface, REFIID riid, void **out) +{ + if (IsEqualIID(riid, &IID_IMFMediaStream) + || IsEqualIID(riid, &IID_IMFMediaEventGenerator) + || IsEqualIID(riid, &IID_IUnknown)) + { + *out = iface; + } + else + { + *out = NULL; + return E_NOINTERFACE; + } + + IMFMediaStream_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI test_media_stream_AddRef(IMFMediaStream *iface) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + return InterlockedIncrement(&stream->refcount); +} + +static ULONG WINAPI test_media_stream_Release(IMFMediaStream *iface) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + ULONG refcount = InterlockedDecrement(&stream->refcount); + + if (!refcount) + { + IMFMediaEventQueue_Release(stream->event_queue); + free(stream); + } + + return refcount; +} + +static HRESULT WINAPI test_media_stream_GetEvent(IMFMediaStream *iface, DWORD flags, IMFMediaEvent **event) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + return IMFMediaEventQueue_GetEvent(stream->event_queue, flags, event); +} + +static HRESULT WINAPI test_media_stream_BeginGetEvent(IMFMediaStream *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + return IMFMediaEventQueue_BeginGetEvent(stream->event_queue, callback, state); +} + +static HRESULT WINAPI test_media_stream_EndGetEvent(IMFMediaStream *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + return IMFMediaEventQueue_EndGetEvent(stream->event_queue, result, event); +} + +static HRESULT WINAPI test_media_stream_QueueEvent(IMFMediaStream *iface, MediaEventType event_type, REFGUID ext_type, + HRESULT hr, const PROPVARIANT *value) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + return IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI test_media_stream_GetMediaSource(IMFMediaStream *iface, IMFMediaSource **source) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + + *source = stream->source; + IMFMediaSource_AddRef(*source); + + return S_OK; +} + +static HRESULT WINAPI test_media_stream_GetStreamDescriptor(IMFMediaStream *iface, IMFStreamDescriptor **sd) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + + *sd = stream->sd; + IMFStreamDescriptor_AddRef(*sd); + + return S_OK; +} + +static HRESULT WINAPI test_media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + IMFMediaBuffer *buffer; + IMFSample *sample; + HRESULT hr; + + hr = MFCreateSample(&sample); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (stream->sample_duration) + { + hr = IMFSample_SetSampleDuration(sample, stream->sample_duration); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFSample_SetSampleTime(sample, stream->sample_time); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + stream->sample_time += stream->sample_duration; + } + else + { + hr = IMFSample_SetSampleTime(sample, 123); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFSample_SetSampleDuration(sample, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + + if (token) + IMFSample_SetUnknown(sample, &MFSampleExtension_Token, token); + + /* Reader expects buffers, empty samples are considered an error. */ + hr = MFCreateMemoryBuffer(8, &buffer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFSample_AddBuffer(sample, buffer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaBuffer_Release(buffer); + + hr = IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, &GUID_NULL, S_OK, + (IUnknown *)sample); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFSample_Release(sample); + + return S_OK; +} + +static const IMFMediaStreamVtbl test_media_stream_vtbl = +{ + test_media_stream_QueryInterface, + test_media_stream_AddRef, + test_media_stream_Release, + test_media_stream_GetEvent, + test_media_stream_BeginGetEvent, + test_media_stream_EndGetEvent, + test_media_stream_QueueEvent, + test_media_stream_GetMediaSource, + test_media_stream_GetStreamDescriptor, + test_media_stream_RequestSample, +}; + +#define TEST_SOURCE_NUM_STREAMS 3 + +struct test_seek_source +{ + IMFMediaSource IMFMediaSource_iface; + IMFMediaEventQueue *event_queue; + IMFPresentationDescriptor *pd; + struct test_media_stream *streams[TEST_SOURCE_NUM_STREAMS]; + enum source_state state; + unsigned stream_count; + CRITICAL_SECTION cs; + BOOL seekable; + LONG refcount; +}; + +static struct test_seek_source *impl_test_seek_source_from_IMFMediaSource(IMFMediaSource *iface) +{ + return CONTAINING_RECORD(iface, struct test_seek_source, IMFMediaSource_iface); +} + +static HRESULT WINAPI test_seek_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out) +{ + if (IsEqualIID(riid, &IID_IMFMediaSource) + || IsEqualIID(riid, &IID_IMFMediaEventGenerator) + || IsEqualIID(riid, &IID_IUnknown)) + { + *out = iface; + } + else + { + *out = NULL; + return E_NOINTERFACE; + } + + IMFMediaSource_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI test_seek_source_AddRef(IMFMediaSource *iface) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + return InterlockedIncrement(&source->refcount); +} + +static ULONG WINAPI test_seek_source_Release(IMFMediaSource *iface) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + ULONG refcount = InterlockedDecrement(&source->refcount); + + if (!refcount) + { + IMFMediaEventQueue_Release(source->event_queue); + free(source); + } + + return refcount; +} + +static HRESULT WINAPI test_seek_source_GetEvent(IMFMediaSource *iface, DWORD flags, IMFMediaEvent **event) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + return IMFMediaEventQueue_GetEvent(source->event_queue, flags, event); +} + +static HRESULT WINAPI test_seek_source_BeginGetEvent(IMFMediaSource *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + return IMFMediaEventQueue_BeginGetEvent(source->event_queue, callback, state); +} + +static HRESULT WINAPI test_seek_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + return IMFMediaEventQueue_EndGetEvent(source->event_queue, result, event); +} + +static HRESULT WINAPI test_seek_source_QueueEvent(IMFMediaSource *iface, MediaEventType event_type, REFGUID ext_type, + HRESULT hr, const PROPVARIANT *value) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI test_seek_source_GetCharacteristics(IMFMediaSource *iface, DWORD *flags) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + + if (source->seekable) + *flags = MFMEDIASOURCE_CAN_PAUSE | MFMEDIASOURCE_CAN_SEEK; + else + *flags = MFMEDIASOURCE_CAN_PAUSE; + return S_OK; +} + +static HRESULT WINAPI test_seek_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **pd) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + IMFStreamDescriptor *sds[ARRAY_SIZE(source->streams)]; + IMFMediaType *media_type; + HRESULT hr = S_OK; + int i; + + EnterCriticalSection(&source->cs); + + if (source->pd) + { + *pd = source->pd; + IMFPresentationDescriptor_AddRef(*pd); + } + else + { + for (i = 0; i < source->stream_count; ++i) + { + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_RGB32); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_SIZE, (UINT64)640 << 32 | 480); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateStreamDescriptor(i, 1, &media_type, &sds[i]); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFMediaType_Release(media_type); + } + + hr = MFCreatePresentationDescriptor(source->stream_count, sds, &source->pd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationDescriptor_SetUINT64(source->pd, &MF_PD_DURATION, 10 * 10000000); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationDescriptor_SelectStream(source->pd, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + for (i = 0; i < source->stream_count; ++i) + IMFStreamDescriptor_Release(sds[i]); + + *pd = source->pd; + IMFPresentationDescriptor_AddRef(*pd); + } + + LeaveCriticalSection(&source->cs); + + return hr; +} + +static BOOL is_stream_selected(IMFPresentationDescriptor *pd, DWORD index) +{ + IMFStreamDescriptor *sd; + BOOL selected = FALSE; + + if (SUCCEEDED(IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, index, &selected, &sd))) + IMFStreamDescriptor_Release(sd); + + return selected; +} + +static HRESULT WINAPI test_seek_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *pd, const GUID *time_format, + const PROPVARIANT *start_position) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + MediaEventType event_type; + PROPVARIANT var; + HRESULT hr; + int i; + + add_object_state(&actual_object_state_record, SOURCE_START); + + ok(time_format && IsEqualGUID(time_format, &GUID_NULL), "Unexpected time format %s.\n", + wine_dbgstr_guid(time_format)); + ok(start_position && (start_position->vt == VT_I8 || start_position->vt == VT_EMPTY), + "Unexpected position type.\n"); + + /* This is what makes IMFMediaSession::Start() seeking fail, not the lacking of MFMEDIASOURCE_CAN_SEEK. + * Without this, IMFMediaSession::Start() seeking succeeds even with the missing MFMEDIASOURCE_CAN_SEEK. + * If this is check is not here, the first IMFMediaSession::Start() call to a non-zero position + * succeeds somehow on Windows 10, then all following seeks fails and no MESessionStarted events */ + if (!source->seekable && start_position && start_position->vt == VT_I8 && start_position->hVal.QuadPart) + return E_FAIL; + + EnterCriticalSection(&source->cs); + + event_type = source->state != SOURCE_STOPPED ? MESourceSeeked : MESourceStarted; + hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + for (i = 0; i < source->stream_count; ++i) + { + if (!is_stream_selected(pd, i)) + continue; + + var.vt = VT_UNKNOWN; + var.punkVal = (IUnknown *)&source->streams[i]->IMFMediaStream_iface; + event_type = source->streams[i]->is_new ? MENewStream : MEUpdatedStream; + source->streams[i]->is_new = FALSE; + hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, &var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + event_type = source->state != SOURCE_STOPPED ? MEStreamSeeked : MEStreamStarted; + hr = IMFMediaEventQueue_QueueEventParamVar(source->streams[i]->event_queue, event_type, &GUID_NULL, + S_OK, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + + source->state = SOURCE_RUNNING; + + LeaveCriticalSection(&source->cs); + + return S_OK; +} + +static HRESULT WINAPI test_seek_source_Stop(IMFMediaSource *iface) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + MediaEventType event_type; + HRESULT hr; + int i; + + add_object_state(&actual_object_state_record, SOURCE_STOP); + + EnterCriticalSection(&source->cs); + + event_type = MESourceStopped; + hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + for (i = 0; i < source->stream_count; ++i) + { + if (!is_stream_selected(source->pd, i)) + continue; + + event_type = MEStreamStopped; + hr = IMFMediaEventQueue_QueueEventParamVar(source->streams[i]->event_queue, event_type, &GUID_NULL, + S_OK, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + + source->state = SOURCE_STOPPED; + + LeaveCriticalSection(&source->cs); + + return S_OK; +} + +static HRESULT WINAPI test_seek_source_Pause(IMFMediaSource *iface) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + MediaEventType event_type; + HRESULT hr; + int i; + + add_object_state(&actual_object_state_record, SOURCE_PAUSE); + + EnterCriticalSection(&source->cs); + + event_type = MESourcePaused; + hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + for (i = 0; i < source->stream_count; ++i) + { + if (!is_stream_selected(source->pd, i)) + continue; + + event_type = MEStreamPaused; + hr = IMFMediaEventQueue_QueueEventParamVar(source->streams[i]->event_queue, event_type, &GUID_NULL, + S_OK, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + + source->state = SOURCE_PAUSED; + LeaveCriticalSection(&source->cs); + + return S_OK; +} + +static HRESULT WINAPI test_seek_source_Shutdown(IMFMediaSource *iface) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + HRESULT hr; + + add_object_state(&actual_object_state_record, SOURCE_SHUTDOWN); + + hr = IMFMediaEventQueue_Shutdown(source->event_queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + return S_OK; +} + +static const IMFMediaSourceVtbl test_seek_source_vtbl = +{ + test_seek_source_QueryInterface, + test_seek_source_AddRef, + test_seek_source_Release, + test_seek_source_GetEvent, + test_seek_source_BeginGetEvent, + test_seek_source_EndGetEvent, + test_seek_source_QueueEvent, + test_seek_source_GetCharacteristics, + test_seek_source_CreatePresentationDescriptor, + test_seek_source_Start, + test_seek_source_Stop, + test_seek_source_Pause, + test_seek_source_Shutdown, +}; + +static HRESULT WINAPI test_seek_clock_sink_QueryInterface(IMFClockStateSink *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFClockStateSink) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFClockStateSink_AddRef(iface); + return S_OK; + } + + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI test_seek_clock_sink_AddRef(IMFClockStateSink *iface) +{ + return 2; +} + +static ULONG WINAPI test_seek_clock_sink_Release(IMFClockStateSink *iface) +{ + return 1; +} + +static HRESULT WINAPI test_seek_clock_sink_OnClockStart(IMFClockStateSink *iface, MFTIME system_time, LONGLONG offset) +{ + add_object_state(&actual_object_state_record, SINK_ON_CLOCK_START); + return S_OK; +} + +static HRESULT WINAPI test_seek_clock_sink_OnClockStop(IMFClockStateSink *iface, MFTIME system_time) +{ + add_object_state(&actual_object_state_record, SINK_ON_CLOCK_STOP); + return S_OK; +} + +static HRESULT WINAPI test_seek_clock_sink_OnClockPause(IMFClockStateSink *iface, MFTIME system_time) +{ + add_object_state(&actual_object_state_record, SINK_ON_CLOCK_PAUSE); + return S_OK; +} + +static HRESULT WINAPI test_seek_clock_sink_OnClockRestart(IMFClockStateSink *iface, MFTIME system_time) +{ + add_object_state(&actual_object_state_record, SINK_ON_CLOCK_RESTART); + return S_OK; +} + +static HRESULT WINAPI test_seek_clock_sink_OnClockSetRate(IMFClockStateSink *iface, MFTIME system_time, float rate) +{ + add_object_state(&actual_object_state_record, SINK_ON_CLOCK_SETRATE); + return S_OK; +} + +static const IMFClockStateSinkVtbl test_seek_clock_sink_vtbl = +{ + test_seek_clock_sink_QueryInterface, + test_seek_clock_sink_AddRef, + test_seek_clock_sink_Release, + test_seek_clock_sink_OnClockStart, + test_seek_clock_sink_OnClockStop, + test_seek_clock_sink_OnClockPause, + test_seek_clock_sink_OnClockRestart, + test_seek_clock_sink_OnClockSetRate, +}; + +static struct test_media_stream *create_test_stream(DWORD stream_index, IMFMediaSource *source) +{ + struct test_media_stream *stream; + IMFPresentationDescriptor *pd; + BOOL selected; + HRESULT hr; + + stream = calloc(1, sizeof(*stream)); + stream->IMFMediaStream_iface.lpVtbl = &test_media_stream_vtbl; + stream->refcount = 1; + hr = MFCreateEventQueue(&stream->event_queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + stream->source = source; + IMFMediaSource_AddRef(stream->source); + stream->is_new = TRUE; + + IMFMediaSource_CreatePresentationDescriptor(source, &pd); + IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, stream_index, &selected, &stream->sd); + IMFPresentationDescriptor_Release(pd); + + return stream; +} + +static IMFMediaSource *create_test_seek_source(BOOL seekable) +{ + struct test_seek_source *source; + int i; + + source = calloc(1, sizeof(*source)); + source->IMFMediaSource_iface.lpVtbl = &test_seek_source_vtbl; + source->refcount = 1; + source->stream_count = 1; + source->seekable = seekable; + MFCreateEventQueue(&source->event_queue); + InitializeCriticalSection(&source->cs); + for (i = 0; i < source->stream_count; ++i) + source->streams[i] = create_test_stream(i, &source->IMFMediaSource_iface); + + return &source->IMFMediaSource_iface; +} + static void test_media_session_events(void) { static const media_type_desc audio_float_44100 = @@ -2887,27 +3508,27 @@ static ULONG WINAPI test_grabber_callback_Release(IMFSampleGrabberSinkCallback * static HRESULT WINAPI test_grabber_callback_OnClockStart(IMFSampleGrabberSinkCallback *iface, MFTIME time, LONGLONG offset) { - return E_NOTIMPL; + return S_OK; } static HRESULT WINAPI test_grabber_callback_OnClockStop(IMFSampleGrabberSinkCallback *iface, MFTIME time) { - return E_NOTIMPL; + return S_OK; } static HRESULT WINAPI test_grabber_callback_OnClockPause(IMFSampleGrabberSinkCallback *iface, MFTIME time) { - return E_NOTIMPL; + return S_OK; } static HRESULT WINAPI test_grabber_callback_OnClockRestart(IMFSampleGrabberSinkCallback *iface, MFTIME time) { - return E_NOTIMPL; + return S_OK; } static HRESULT WINAPI test_grabber_callback_OnClockSetRate(IMFSampleGrabberSinkCallback *iface, MFTIME time, float rate) { - return E_NOTIMPL; + return S_OK; } static HRESULT WINAPI test_grabber_callback_OnSetPresentationClock(IMFSampleGrabberSinkCallback *iface, @@ -4995,8 +5616,8 @@ static void test_sample_grabber_is_mediatype_supported(void) IMFSampleGrabberSinkCallback_Release(grabber_callback); } -/* create a test topology with the specified source and sink */ -static IMFTopology *create_test_topology(IMFMediaSource *source, IMFActivate *sink_activate) +/* create a test topology with the specified source and sink, and return duration if required */ +static IMFTopology *create_test_topology(IMFMediaSource *source, IMFActivate *sink_activate, UINT64 *duration) { IMFTopologyNode *src_node, *sink_node; IMFPresentationDescriptor *pd; @@ -5022,6 +5643,11 @@ static IMFTopology *create_test_topology(IMFMediaSource *source, IMFActivate *si hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, 0, &selected, &sd); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(selected, "got selected %u.\n", !!selected); + if (duration) + { + hr = IMFPresentationDescriptor_GetUINT64(pd, &MF_PD_DURATION, duration); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } init_source_node(source, -1, src_node, pd, sd); hr = IMFTopologyNode_SetObject(sink_node, (IUnknown *)sink_activate); ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); @@ -5083,7 +5709,7 @@ static void test_sample_grabber_orientation(GUID subtype) ok(hr == S_OK, "Failed to create grabber sink, hr %#lx.\n", hr); IMFMediaType_Release(output_type); - topology = create_test_topology(source, sink_activate); + topology = create_test_topology(source, sink_activate, NULL); hr = IMFMediaSession_SetTopology(session, 0, topology); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); IMFTopology_Release(topology); @@ -7282,6 +7908,323 @@ static void test_MFCreateSequencerSegmentOffset(void) PropVariantClear(&propvar); } +static void test_media_session_Start(void) +{ + static const struct object_state_record expected_object_state_records[] = + { + {{SOURCE_START, SINK_ON_CLOCK_START}, 2}, + {{SOURCE_STOP, SOURCE_START, SINK_ON_CLOCK_START}, 3}, + {{SOURCE_STOP, SOURCE_START, SINK_ON_CLOCK_START}, 3}, + }; + media_type_desc video_rgb32_desc = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), + }; + static const MFTIME allowed_error = 5000000; + IMFClockStateSink test_seek_clock_sink = {&test_seek_clock_sink_vtbl}; + struct test_grabber_callback *grabber_callback; + IMFPresentationClock *presentation_clock; + enum source_state initial_state; + IMFActivate *sink_activate; + IMFAsyncCallback *callback; + IMFMediaType *output_type; + IMFMediaSession *session; + IMFMediaSource *source; + IMFTopology *topology; + MFTIME time, old_time; + PROPVARIANT propvar; + IMFClock *clock; + UINT64 duration; + DWORD caps; + HRESULT hr; + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "Failed to start up, hr %#lx.\n", hr); + + if (!(source = create_media_source(L"test.mp4", L"video/mp4"))) + { + todo_wine /* Gitlab CI Debian runner */ + win_skip("MP4 media source is not supported, skipping tests.\n"); + MFShutdown(); + return; + } + + grabber_callback = impl_from_IMFSampleGrabberSinkCallback(create_test_grabber_callback()); + hr = MFCreateMediaType(&output_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(output_type, video_rgb32_desc, -1); + hr = MFCreateSampleGrabberSinkActivate(output_type, &grabber_callback->IMFSampleGrabberSinkCallback_iface, &sink_activate); + ok(hr == S_OK, "Failed to create grabber sink, hr %#lx.\n", hr); + IMFMediaType_Release(output_type); + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + topology = create_test_topology(source, sink_activate, &duration); + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTopology_Release(topology); + + hr = IMFMediaSession_GetClock(session, &clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFClock_QueryInterface(clock, &IID_IMFPresentationClock, (void **)&presentation_clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFClock_Release(clock); + + propvar.vt = VT_EMPTY; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + callback = create_test_callback(TRUE); + hr = wait_media_event(session, callback, MESessionStarted, 5000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Seek to 1s */ + propvar.vt = VT_I8; + propvar.hVal.QuadPart = 10000000; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationClock_GetTime(presentation_clock, &time); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(llabs(time - 10000000) <= allowed_error, "Unexpected time %I64d.\n", time); + + /* Seek to beyond duration */ + propvar.vt = VT_I8; + propvar.hVal.QuadPart = duration + 10000000; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == MF_E_INVALID_POSITION, "Unexpected hr %#lx.\n", hr); + + /* Seek to negative position */ + propvar.vt = VT_I8; + propvar.hVal.QuadPart = -10000000; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationClock_GetTime(presentation_clock, &time); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(llabs(time - (-10000000)) <= allowed_error, "Unexpected time %I64d.\n", time); + + /* Seek backwards to 0s */ + propvar.vt = VT_I8; + propvar.hVal.QuadPart = 0; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationClock_GetTime(presentation_clock, &time); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(llabs(time) <= allowed_error, "Unexpected time %I64d.\n", time); + + /* Seek to 1s while in paused state */ + hr = IMFMediaSession_Pause(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionPaused, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + propvar.vt = VT_I8; + propvar.hVal.QuadPart = 10000000; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFPresentationClock_GetTime(presentation_clock, &time); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(llabs(time - 10000000) <= allowed_error, "Unexpected time %I64d.\n", time); + old_time = time; + + /* Expected the presentation clock is running */ + Sleep(100); + hr = IMFPresentationClock_GetTime(presentation_clock, &time); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(time > old_time, "Unexpected time %I64d.\n", time); + + hr = IMFMediaSession_Stop(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Close(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Media session is shut down */ + hr = IMFMediaSource_Shutdown(source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + propvar.vt = VT_I8; + propvar.hVal.QuadPart = 10000000; + hr = IMFMediaSession_Start(session, &GUID_NULL, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + propvar.vt = VT_I8; + propvar.hVal.QuadPart = 10000000; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + + propvar.vt = VT_EMPTY; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + + IMFPresentationClock_Release(presentation_clock); + IMFMediaSource_Release(source); + IMFAsyncCallback_Release(callback); + /* sometimes briefly leaking */ + IMFMediaSession_Release(session); + IMFActivate_ShutdownObject(sink_activate); + IMFActivate_Release(sink_activate); + IMFSampleGrabberSinkCallback_Release(&grabber_callback->IMFSampleGrabberSinkCallback_iface); + + /* Unseekable media source */ + source = create_test_seek_source(FALSE); + hr = IMFMediaSource_GetCharacteristics(source, &caps); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok((caps & MFMEDIASOURCE_CAN_SEEK) == 0, "Got unexpected caps %#lx.\n", caps); + grabber_callback = impl_from_IMFSampleGrabberSinkCallback(create_test_grabber_callback()); + hr = MFCreateMediaType(&output_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(output_type, video_rgb32_desc, -1); + hr = MFCreateSampleGrabberSinkActivate(output_type, &grabber_callback->IMFSampleGrabberSinkCallback_iface, &sink_activate); + ok(hr == S_OK, "Failed to create grabber sink, hr %#lx.\n", hr); + IMFMediaType_Release(output_type); + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + topology = create_test_topology(source, sink_activate, &duration); + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTopology_Release(topology); + + hr = IMFMediaSession_GetClock(session, &clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFClock_QueryInterface(clock, &IID_IMFPresentationClock, (void **)&presentation_clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFClock_Release(clock); + + propvar.vt = VT_EMPTY; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + callback = create_test_callback(TRUE); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaSession_GetSessionCapabilities(session, &caps); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok((caps & MFSESSIONCAP_SEEK) == 0, "Got unexpected caps %#lx\n", caps); + + /* Seek to 1s */ + propvar.vt = VT_I8; + propvar.hVal.QuadPart = 10000000; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationClock_GetTime(presentation_clock, &time); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(llabs(time) <= allowed_error, "Unexpected time %I64d.\n", time); + + hr = IMFMediaSession_Stop(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Close(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSource_Shutdown(source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFPresentationClock_Release(presentation_clock); + IMFAsyncCallback_Release(callback); + IMFMediaSession_Release(session); + IMFMediaSource_Release(source); + IMFActivate_ShutdownObject(sink_activate); + IMFActivate_Release(sink_activate); + IMFSampleGrabberSinkCallback_Release(&grabber_callback->IMFSampleGrabberSinkCallback_iface); + + /* Test object state transitions */ + for (initial_state = SOURCE_STOPPED; initial_state <= SOURCE_PAUSED; initial_state++) + { + winetest_push_context("Test %d", initial_state); + + source = create_test_seek_source(TRUE); + callback = create_test_callback(TRUE); + + grabber_callback = impl_from_IMFSampleGrabberSinkCallback(create_test_grabber_callback()); + hr = MFCreateMediaType(&output_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(output_type, video_rgb32_desc, -1); + hr = MFCreateSampleGrabberSinkActivate(output_type, &grabber_callback->IMFSampleGrabberSinkCallback_iface, &sink_activate); + ok(hr == S_OK, "Failed to create grabber sink, hr %#lx.\n", hr); + IMFMediaType_Release(output_type); + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + topology = create_test_topology(source, sink_activate, &duration); + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTopology_Release(topology); + + hr = IMFMediaSession_GetClock(session, &clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFClock_QueryInterface(clock, &IID_IMFPresentationClock, (void **)&presentation_clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationClock_AddClockStateSink(presentation_clock, &test_seek_clock_sink); + ok(hr == S_OK, "Failed to add a sink, hr %#lx.\n", hr); + IMFClock_Release(clock); + + if (initial_state == SOURCE_RUNNING || initial_state == SOURCE_PAUSED) + { + propvar.vt = VT_EMPTY; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStarted, 5000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + if (initial_state == SOURCE_PAUSED) + { + hr = IMFMediaSession_Pause(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionPaused, 5000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + + /* Seek to 1s */ + memset(&actual_object_state_record, 0, sizeof(actual_object_state_record)); + + propvar.vt = VT_I8; + propvar.hVal.QuadPart = 10000000; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStarted, 5000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine_if(initial_state == SOURCE_PAUSED) + compare_object_states(&actual_object_state_record, &expected_object_state_records[initial_state]); + + hr = IMFMediaSession_Stop(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Close(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSource_Shutdown(source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFPresentationClock_RemoveClockStateSink(presentation_clock, &test_seek_clock_sink); + IMFPresentationClock_Release(presentation_clock); + IMFAsyncCallback_Release(callback); + IMFMediaSession_Release(session); + IMFMediaSource_Release(source); + IMFActivate_ShutdownObject(sink_activate); + IMFActivate_Release(sink_activate); + IMFSampleGrabberSinkCallback_Release(&grabber_callback->IMFSampleGrabberSinkCallback_iface); + winetest_pop_context(); + } + + hr = MFShutdown(); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +} + START_TEST(mf) { init_functions(); @@ -7318,4 +8261,5 @@ START_TEST(mf) test_MFRequireProtectedEnvironment(); test_mpeg4_media_sink(); test_MFCreateSequencerSegmentOffset(); + test_media_session_Start(); } From 9fe8106545f71efee3844e6e6b1e9a2b408e0398 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Fri, 28 Jul 2023 18:04:30 +0800 Subject: [PATCH 240/413] mfmediaengine: Implement IMFMediaEngineEx::SetCurrentTime(). (cherry picked from commit 2bcc87b30301bef3c77601d31d87fa93e60d79d5) CW-Bug-Id: #22486 CW-Bug-Id: #22799 --- dlls/mfmediaengine/main.c | 59 ++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index 3e05626f238..c6f82ec055b 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -94,6 +94,7 @@ enum media_engine_flags FLAGS_ENGINE_NEW_FRAME = 0x8000, FLAGS_ENGINE_SOURCE_PENDING = 0x10000, FLAGS_ENGINE_PLAY_PENDING = 0x20000, + FLAGS_ENGINE_SEEKING = 0x40000, }; struct vec3 @@ -164,6 +165,7 @@ struct media_engine { IMFMediaSource *source; IMFPresentationDescriptor *pd; + PROPVARIANT start_position; } presentation; struct effects video_effects; struct effects audio_effects; @@ -978,7 +980,14 @@ static HRESULT WINAPI media_engine_session_events_Invoke(IMFAsyncCallback *iface break; } case MESessionStarted: - + EnterCriticalSection(&engine->cs); + if (engine->flags & FLAGS_ENGINE_SEEKING) + { + media_engine_set_flag(engine, FLAGS_ENGINE_SEEKING | FLAGS_ENGINE_IS_ENDED, FALSE); + IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_SEEKED, 0, 0); + IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_TIMEUPDATE, 0, 0); + } + LeaveCriticalSection(&engine->cs); IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_PLAYING, 0, 0); break; case MESessionEnded: @@ -1339,10 +1348,9 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi static void media_engine_start_playback(struct media_engine *engine) { - PROPVARIANT var; - - var.vt = VT_EMPTY; - IMFMediaSession_Start(engine->session, &GUID_NULL, &var); + IMFMediaSession_Start(engine->session, &GUID_NULL, &engine->presentation.start_position); + /* Reset the playback position to the current position */ + engine->presentation.start_position.vt = VT_EMPTY; } static HRESULT WINAPI media_engine_load_handler_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) @@ -1770,6 +1778,10 @@ static double WINAPI media_engine_GetCurrentTime(IMFMediaEngineEx *iface) { ret = engine->duration; } + else if (engine->flags & FLAGS_ENGINE_PAUSED && engine->presentation.start_position.vt == VT_I8) + { + ret = (double)engine->presentation.start_position.hVal.QuadPart / 10000000; + } else if (SUCCEEDED(IMFPresentationClock_GetTime(engine->clock, &clocktime))) { ret = mftime_to_seconds(clocktime); @@ -1779,17 +1791,50 @@ static double WINAPI media_engine_GetCurrentTime(IMFMediaEngineEx *iface) return ret; } +static HRESULT media_engine_set_current_time(struct media_engine *engine, double seektime) +{ + PROPVARIANT position; + DWORD caps; + HRESULT hr; + + hr = IMFMediaSession_GetSessionCapabilities(engine->session, &caps); + if (FAILED(hr) || !(caps & MFSESSIONCAP_SEEK)) + return hr; + + position.vt = VT_I8; + position.hVal.QuadPart = min(max(0, seektime), engine->duration) * 10000000; + + if (IMFMediaEngineEx_IsPaused(&engine->IMFMediaEngineEx_iface)) + { + engine->presentation.start_position = position; + IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_SEEKING, 0, 0); + IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_SEEKED, 0, 0); + IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_TIMEUPDATE, 0, 0); + return S_OK; + } + + if (SUCCEEDED(hr = IMFMediaSession_Start(engine->session, &GUID_NULL, &position))) + { + media_engine_set_flag(engine, FLAGS_ENGINE_SEEKING, TRUE); + IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_SEEKING, 0, 0); + } + + return hr; +} + static HRESULT WINAPI media_engine_SetCurrentTime(IMFMediaEngineEx *iface, double time) { struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - HRESULT hr = E_NOTIMPL; + HRESULT hr; - FIXME("(%p, %f): stub.\n", iface, time); + TRACE("%p, %f.\n", iface, time); EnterCriticalSection(&engine->cs); if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) hr = MF_E_SHUTDOWN; + else + hr = media_engine_set_current_time(engine, time); LeaveCriticalSection(&engine->cs); From 3771bdb7a493857081e412558eda1392806032f3 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Tue, 20 Feb 2024 15:21:56 +0800 Subject: [PATCH 241/413] mfmediaengine: Implement IMFMediaEngineEx::SetCurrentTimeEx(). (cherry picked from commit 283d4bab2229c4accae8453f24c76319741dec96) CW-Bug-Id: #22486 CW-Bug-Id: #22799 --- dlls/mfmediaengine/main.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index c6f82ec055b..f6ea0cf3e59 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -3027,9 +3027,24 @@ static HRESULT WINAPI media_engine_SetRealTimeMode(IMFMediaEngineEx *iface, BOOL static HRESULT WINAPI media_engine_SetCurrentTimeEx(IMFMediaEngineEx *iface, double seektime, MF_MEDIA_ENGINE_SEEK_MODE mode) { - FIXME("%p, %f, %#x stub.\n", iface, seektime, mode); + struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + HRESULT hr; - return E_NOTIMPL; + TRACE("%p, %f, %#x.\n", iface, seektime, mode); + + if (mode) + FIXME("mode %#x is ignored.\n", mode); + + EnterCriticalSection(&engine->cs); + + if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) + hr = MF_E_SHUTDOWN; + else + hr = media_engine_set_current_time(engine, seektime); + + LeaveCriticalSection(&engine->cs); + + return hr; } static HRESULT WINAPI media_engine_EnableTimeUpdateTimer(IMFMediaEngineEx *iface, BOOL enable) From 74e945ff8506ce35ad64bd0c5e1ad48beba63ff6 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Tue, 20 Feb 2024 15:22:25 +0800 Subject: [PATCH 242/413] mfmediaengine/tests: Test IMFMediaEngineEx::SetCurrentTime/Ex(). (cherry picked from commit de89f777271a2b3fe2ab7fd7779bb6a0a70c09c4) CW-Bug-Id: #22486 CW-Bug-Id: #22799 --- dlls/mfmediaengine/tests/mfmediaengine.c | 201 +++++++++++++++++++++++ 1 file changed, 201 insertions(+) diff --git a/dlls/mfmediaengine/tests/mfmediaengine.c b/dlls/mfmediaengine/tests/mfmediaengine.c index f9489f3dcb5..c7fabbe218f 100644 --- a/dlls/mfmediaengine/tests/mfmediaengine.c +++ b/dlls/mfmediaengine/tests/mfmediaengine.c @@ -2183,6 +2183,11 @@ struct test_seek_notify { IMFMediaEngineNotify IMFMediaEngineNotify_iface; HANDLE playing_event; + HANDLE seeking_event; + HANDLE seeked_event; + HANDLE time_update_event; + BOOL seeking_event_received; + BOOL time_update_event_received; HRESULT expected_error; HRESULT error; LONG refcount; @@ -2222,6 +2227,9 @@ static ULONG WINAPI test_seek_notify_Release(IMFMediaEngineNotify *iface) if (!refcount) { CloseHandle(notify->playing_event); + CloseHandle(notify->seeking_event); + CloseHandle(notify->seeked_event); + CloseHandle(notify->time_update_event); free(notify); } @@ -2238,6 +2246,17 @@ static HRESULT WINAPI test_seek_notify_EventNotify(IMFMediaEngineNotify *iface, case MF_MEDIA_ENGINE_EVENT_PLAYING: SetEvent(notify->playing_event); break; + case MF_MEDIA_ENGINE_EVENT_SEEKING: + notify->seeking_event_received = TRUE; + SetEvent(notify->seeking_event); + break; + case MF_MEDIA_ENGINE_EVENT_SEEKED: + SetEvent(notify->seeked_event); + break; + case MF_MEDIA_ENGINE_EVENT_TIMEUPDATE: + notify->time_update_event_received = TRUE; + SetEvent(notify->time_update_event); + break; case MF_MEDIA_ENGINE_EVENT_ERROR: ok(param2 == notify->expected_error, "Unexpected error %#lx\n", param2); notify->error = param2; @@ -2262,7 +2281,13 @@ static struct test_seek_notify *create_seek_notify(void) object = calloc(1, sizeof(*object)); object->IMFMediaEngineNotify_iface.lpVtbl = &test_seek_notify_vtbl; object->playing_event = CreateEventW(NULL, FALSE, FALSE, NULL); + object->seeking_event = CreateEventW(NULL, FALSE, FALSE, NULL); + object->seeked_event = CreateEventW(NULL, FALSE, FALSE, NULL); + object->time_update_event = CreateEventW(NULL, FALSE, FALSE, NULL); ok(!!object->playing_event, "Failed to create an event, error %lu.\n", GetLastError()); + ok(!!object->seeking_event, "Failed to create an event, error %lu.\n", GetLastError()); + ok(!!object->seeked_event, "Failed to create an event, error %lu.\n", GetLastError()); + ok(!!object->time_update_event, "Failed to create an event, error %lu.\n", GetLastError()); object->refcount = 1; return object; } @@ -2504,6 +2529,181 @@ static void test_media_extension(void) IMFMediaEngineExtension_Release(&extension->IMFMediaEngineExtension_iface); } +#define test_seek_result(a, b, c) _test_seek_result(__LINE__, a, b, c) +static void _test_seek_result(int line, IMFMediaEngineEx *media_engine, + struct test_seek_notify *notify, double expected_time) +{ + static const double allowed_error = 0.05; + static const int timeout = 1000; + double time; + DWORD res; + + ok(notify->seeking_event_received, "Seeking event not received.\n"); + notify->seeking_event_received = FALSE; + res = WaitForSingleObject(notify->seeking_event, timeout); + ok_(__FILE__, line)(!res, "Waiting for seeking event returned %#lx.\n", res); + res = WaitForSingleObject(notify->seeked_event, timeout); + ok_(__FILE__, line)(!res, "Waiting for seeked event returned %#lx.\n", res); + res = WaitForSingleObject(notify->time_update_event, timeout); + ok_(__FILE__, line)(!res, "Waiting for ready event returned %#lx.\n", res); + time = IMFMediaEngineEx_GetCurrentTime(media_engine); + ok_(__FILE__, line)(compare_double(time, expected_time, allowed_error), "Unexpected time %lf.\n", time); +} + +static void test_SetCurrentTime(void) +{ + static const double allowed_error = 0.05; + static const int timeout = 1000; + IMFByteStream *stream, *unseekable_stream = NULL; + double time, duration, start, end; + struct test_seek_notify *notify; + IMFMediaEngineEx *media_engine; + ULONG refcount; + HRESULT hr; + DWORD res; + BOOL ret; + BSTR url; + + notify = create_seek_notify(); + hr = create_media_engine(¬ify->IMFMediaEngineNotify_iface, NULL, DXGI_FORMAT_B8G8R8X8_UNORM, + &IID_IMFMediaEngineEx, (void **)&media_engine); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface); + + stream = load_resource(L"i420-64x64.avi", L"video/avi"); + url = SysAllocString(L"i420-64x64.avi"); + hr = IMFMediaEngineEx_SetSourceFromByteStream(media_engine, stream, url); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaEngineEx_Play(media_engine); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + res = WaitForSingleObject(notify->playing_event, 5000); + ok(!res, "Unexpected res %#lx.\n", res); + + duration = IMFMediaEngineEx_GetDuration(media_engine); + ok(duration > 0, "Got invalid duration.\n"); + start = 0; + end = duration; + + /* Test playing state */ + hr = IMFMediaEngineEx_SetCurrentTime(media_engine, end); + ok(hr == S_OK || broken(hr == MF_INVALID_STATE_ERR) /* Win8 */, "Unexpected hr %#lx.\n", hr); + if (hr == S_OK) + test_seek_result(media_engine, notify, end); + + /* Test seeking with a negative position */ + hr = IMFMediaEngineEx_SetCurrentTime(media_engine, -1); + ok(hr == S_OK || broken(hr == MF_INVALID_STATE_ERR) /* Win8 */, "Unexpected hr %#lx.\n", hr); + if (hr == S_OK) + test_seek_result(media_engine, notify, 0); + + /* Test seeking beyond duration */ + hr = IMFMediaEngineEx_SetCurrentTime(media_engine, end + 1); + ok(hr == S_OK || broken(hr == MF_INVALID_STATE_ERR) /* Win8 */, "Unexpected hr %#lx.\n", hr); + if (hr == S_OK) + test_seek_result(media_engine, notify, end); + + hr = IMFMediaEngineEx_SetCurrentTimeEx(media_engine, start, MF_MEDIA_ENGINE_SEEK_MODE_NORMAL); + ok(hr == S_OK || broken(hr == MF_INVALID_STATE_ERR) /* Win8 */, "Unexpected hr %#lx.\n", hr); + if (hr == S_OK) + test_seek_result(media_engine, notify, start); + + hr = IMFMediaEngineEx_SetCurrentTimeEx(media_engine, end, MF_MEDIA_ENGINE_SEEK_MODE_APPROXIMATE); + ok(hr == S_OK || broken(hr == MF_INVALID_STATE_ERR) /* Win8 */, "Unexpected hr %#lx.\n", hr); + if (hr == S_OK) + test_seek_result(media_engine, notify, end); + + /* Test paused state */ + hr = IMFMediaEngineEx_Pause(media_engine); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaEngineEx_SetCurrentTime(media_engine, start); + ok(hr == S_OK || broken(hr == MF_INVALID_STATE_ERR) /* Win8 */, "Unexpected hr %#lx.\n", hr); + if (hr == S_OK) + { + ok(notify->seeking_event_received, "Seeking event not received.\n"); + notify->seeking_event_received = FALSE; + ok(notify->time_update_event_received, "Time update event not received.\n"); + notify->time_update_event_received = FALSE; + res = WaitForSingleObject(notify->seeking_event, timeout); + ok(!res, "Unexpected res %#lx.\n", res); + res = WaitForSingleObject(notify->seeked_event, timeout); + ok(res == WAIT_TIMEOUT || res == 0, /* No timeout sometimes on Win10+ */ + "Unexpected res %#lx.\n", res); + res = WaitForSingleObject(notify->time_update_event, timeout); + ok(!res, "Unexpected res %#lx.\n", res); + time = IMFMediaEngineEx_GetCurrentTime(media_engine); + ok(compare_double(time, start, allowed_error), "Unexpected time %lf.\n", time); + } + + Sleep(end * 1000); + + ret = IMFMediaEngineEx_IsPaused(media_engine); + ok(ret, "Unexpected ret %d.\n", ret); + time = IMFMediaEngineEx_GetCurrentTime(media_engine); + ok(compare_double(time, start, allowed_error) + || broken(time >= end) /* Windows 11 21H2 AMD GPU TestBot */, "Unexpected time %lf.\n", time); + + hr = IMFMediaEngineEx_Play(media_engine); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + res = WaitForSingleObject(notify->seeked_event, timeout); + ok(res == WAIT_TIMEOUT, "Unexpected res %#lx.\n", res); + + /* Media engine is shut down */ + hr = IMFMediaEngineEx_Shutdown(media_engine); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaEngineEx_SetCurrentTime(media_engine, start); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaEngineEx_SetCurrentTimeEx(media_engine, start, MF_MEDIA_ENGINE_SEEK_MODE_NORMAL); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + + refcount = IMFMediaEngineEx_Release(media_engine); + todo_wine + ok(!refcount, "Got unexpected refcount %lu.\n", refcount); + + /* Unseekable bytestreams */ + notify = create_seek_notify(); + hr = create_media_engine(¬ify->IMFMediaEngineNotify_iface, NULL, DXGI_FORMAT_B8G8R8X8_UNORM, + &IID_IMFMediaEngineEx, (void **)&media_engine); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface); + unseekable_stream = create_unseekable_stream(stream); + hr = IMFMediaEngineEx_SetSourceFromByteStream(media_engine, unseekable_stream, url); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (FAILED(hr)) + goto done; + + hr = IMFMediaEngineEx_Play(media_engine); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + notify->expected_error = MF_E_INVALIDREQUEST; + res = WaitForSingleObject(notify->playing_event, 5000); + ok(res == S_OK, "Unexpected res %#lx.\n", res); + + hr = IMFMediaEngineEx_SetCurrentTime(media_engine, end); + ok(hr == S_OK || broken(hr == MF_INVALID_STATE_ERR) /* Win8 */, "Unexpected hr %#lx.\n", hr); + if (hr == S_OK) + { + ok(!notify->seeking_event_received, "Seeking event received.\n"); + res = WaitForSingleObject(notify->seeking_event, timeout); + ok(res == WAIT_TIMEOUT, "Unexpected res %#lx.\n", res); + res = WaitForSingleObject(notify->seeked_event, timeout); + ok(res == WAIT_TIMEOUT, "Unexpected res %#lx.\n", res); + res = WaitForSingleObject(notify->time_update_event, timeout); + ok(!res, "Unexpected res %#lx.\n", res); + } + +done: + hr = IMFMediaEngineEx_Shutdown(media_engine); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + refcount = IMFMediaEngineEx_Release(media_engine); + ok(!refcount || broken(refcount == 1) /* Win8.1 */, "Got unexpected refcount %lu.\n", refcount); + IMFByteStream_Release(unseekable_stream); + SysFreeString(url); + IMFByteStream_Release(stream); +} + START_TEST(mfmediaengine) { HRESULT hr; @@ -2539,6 +2739,7 @@ START_TEST(mfmediaengine) test_GetDuration(); test_GetSeekable(); test_media_extension(); + test_SetCurrentTime(); IMFMediaEngineClassFactory_Release(factory); From 7354ff73a22d8eb417fd4b816cb5c387fe30c9d4 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 26 Mar 2024 17:27:42 -0600 Subject: [PATCH 243/413] Revert "ntdll: Validate xstate alignment in validate_context_xstate()." This reverts commit f3757df4074d132647aa74be1428b9948ff0a7ad. CW-Bug-Id: #23244 --- dlls/ntdll/tests/exception.c | 14 -------------- dlls/ntdll/unix/thread.c | 2 -- 2 files changed, 16 deletions(-) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index bb62d4627a5..76190aef75a 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -10895,7 +10895,6 @@ static void test_extended_context(void) CONTEXT_EX *context_ex; CONTEXT *context; unsigned data[8]; - NTSTATUS status; HANDLE thread; ULONG64 mask; XSTATE *xs; @@ -11689,19 +11688,6 @@ static void test_extended_context(void) thread = CreateThread(NULL, 0, test_extended_context_thread, 0, CREATE_SUSPENDED, NULL); ok(!!thread, "Failed to create thread.\n"); - /* Unaligned xstate. */ - length = sizeof(context_buffer); - memset(context_buffer, 0xcc, sizeof(context_buffer)); - bret = pInitializeContext(context_buffer, CONTEXT_FULL | CONTEXT_XSTATE | CONTEXT_FLOATING_POINT, - &context, &length); - ok(bret, "Got unexpected bret %#x.\n", bret); - context_ex = (CONTEXT_EX *)(context + 1); - context_ex->XState.Offset += 0x10; - status = pNtGetContextThread(thread, context); - ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %#lx.\n", status); - status = pNtGetContextThread(GetCurrentThread(), context); - ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %#lx.\n", status); - bret = pInitializeContext(context_buffer, CONTEXT_FULL | CONTEXT_XSTATE | CONTEXT_FLOATING_POINT, &context, &length); ok(bret, "Got unexpected bret %#x.\n", bret); diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index c69d3de3e15..7416de09168 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -173,8 +173,6 @@ BOOL validate_context_xstate( CONTEXT *context ) || context_ex->XState.Length > sizeof(XSTATE)) return FALSE; - if (((ULONG_PTR)context_ex + context_ex->XState.Offset) & 63) return FALSE; - return TRUE; } From 93c7162c9a619f33825e1bb7bae04aa146215400 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 26 Mar 2024 17:27:43 -0600 Subject: [PATCH 244/413] Revert "ntdll: Validate context xstate at once in NtGetContextThread()." This reverts commit 92fe92017e5b6e1cee22b2d4d6b86dbb5014e5c9. CW-Bug-Id: #23244 --- dlls/ntdll/unix/signal_i386.c | 4 ++-- dlls/ntdll/unix/signal_x86_64.c | 4 ++-- dlls/ntdll/unix/thread.c | 2 -- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index 5b7d460372c..eb23dd3dc83 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -1050,8 +1050,6 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) BOOL use_cached_debug_regs = FALSE; NTSTATUS ret; - if (!validate_context_xstate( context )) return STATUS_INVALID_PARAMETER; - if (self && needed_flags & CONTEXT_DEBUG_REGISTERS) { /* debug registers require a server call if hw breakpoints are enabled */ @@ -1151,6 +1149,8 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) XSTATE *xstate = (XSTATE *)((char *)context_ex + context_ex->XState.Offset); unsigned int mask; + if (!validate_context_xstate( context )) return STATUS_INVALID_PARAMETER; + mask = (xstate_compaction_enabled ? xstate->CompactionMask : xstate->Mask) & XSTATE_MASK_GSSE; xstate->Mask = frame->xstate.Mask & mask; xstate->CompactionMask = xstate_compaction_enabled ? (0x8000000000000000 | mask) : 0; diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index f0467def51f..df90157a474 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1088,8 +1088,6 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) BOOL use_cached_debug_regs = FALSE; BOOL self = (handle == GetCurrentThread()); - if (!validate_context_xstate( context )) return STATUS_INVALID_PARAMETER; - if (self && needed_flags & CONTEXT_DEBUG_REGISTERS) { /* debug registers require a server call if hw breakpoints are enabled */ @@ -1180,6 +1178,8 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) XSTATE *xstate = (XSTATE *)((char *)context_ex + context_ex->XState.Offset); unsigned int mask; + if (!validate_context_xstate( context )) return STATUS_INVALID_PARAMETER; + mask = (xstate_compaction_enabled ? xstate->CompactionMask : xstate->Mask) & XSTATE_MASK_GSSE; xstate->Mask = frame->xstate.Mask & mask; xstate->CompactionMask = xstate_compaction_enabled ? (0x8000000000000000 | mask) : 0; diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 7416de09168..c09e5fb9d54 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -165,8 +165,6 @@ BOOL validate_context_xstate( CONTEXT *context ) { CONTEXT_EX *context_ex; - if (!((context->ContextFlags & 0x40) && (cpu_info.ProcessorFeatureBits & CPU_FEATURE_AVX))) return TRUE; - context_ex = (CONTEXT_EX *)(context + 1); if (context_ex->XState.Length < offsetof(XSTATE, YmmContext) From ca7394ed43cbd0ae2662630e644a82cc2f206eec Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 26 Mar 2024 17:27:44 -0600 Subject: [PATCH 245/413] Revert "ntdll: Factor out validate_context_xstate() function." This reverts commit 96e3ee88b6b23d47ef6ef21adacaee0f72351e05. CW-Bug-Id: #23244 --- dlls/ntdll/unix/signal_i386.c | 4 +++- dlls/ntdll/unix/signal_x86_64.c | 4 +++- dlls/ntdll/unix/thread.c | 17 ----------------- dlls/ntdll/unix/unix_private.h | 1 - 4 files changed, 6 insertions(+), 20 deletions(-) diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index eb23dd3dc83..98e5e26ffa3 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -1149,7 +1149,9 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) XSTATE *xstate = (XSTATE *)((char *)context_ex + context_ex->XState.Offset); unsigned int mask; - if (!validate_context_xstate( context )) return STATUS_INVALID_PARAMETER; + if (context_ex->XState.Length < offsetof(XSTATE, YmmContext) + || context_ex->XState.Length > sizeof(XSTATE)) + return STATUS_INVALID_PARAMETER; mask = (xstate_compaction_enabled ? xstate->CompactionMask : xstate->Mask) & XSTATE_MASK_GSSE; xstate->Mask = frame->xstate.Mask & mask; diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index df90157a474..1953179d76b 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1178,7 +1178,9 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) XSTATE *xstate = (XSTATE *)((char *)context_ex + context_ex->XState.Offset); unsigned int mask; - if (!validate_context_xstate( context )) return STATUS_INVALID_PARAMETER; + if (context_ex->XState.Length < offsetof(XSTATE, YmmContext) + || context_ex->XState.Length > sizeof(XSTATE)) + return STATUS_INVALID_PARAMETER; mask = (xstate_compaction_enabled ? xstate->CompactionMask : xstate->Mask) & XSTATE_MASK_GSSE; xstate->Mask = frame->xstate.Mask & mask; diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index c09e5fb9d54..73f252853fe 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -158,23 +158,6 @@ void fpu_to_fpux( XMM_SAVE_AREA32 *fpux, const I386_FLOATING_SAVE_AREA *fpu ) } -/*********************************************************************** - * validate_context_xstate - */ -BOOL validate_context_xstate( CONTEXT *context ) -{ - CONTEXT_EX *context_ex; - - context_ex = (CONTEXT_EX *)(context + 1); - - if (context_ex->XState.Length < offsetof(XSTATE, YmmContext) - || context_ex->XState.Length > sizeof(XSTATE)) - return FALSE; - - return TRUE; -} - - /*********************************************************************** * get_server_context_flags */ diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index cf6b9ab0c53..8ba8950fb55 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -235,7 +235,6 @@ extern void DECLSPEC_NORETURN abort_process( int status ); extern void DECLSPEC_NORETURN exit_process( int status ); extern void wait_suspend( CONTEXT *context ); extern NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_chance ); -extern BOOL validate_context_xstate( CONTEXT *context ); extern NTSTATUS set_thread_context( HANDLE handle, const void *context, BOOL *self, USHORT machine ); extern NTSTATUS get_thread_context( HANDLE handle, void *context, BOOL *self, USHORT machine ); extern unsigned int alloc_object_attributes( const OBJECT_ATTRIBUTES *attr, struct object_attributes **ret, From f3ef0eee2e78f22cfdfa70653fdfe06610bf3f9c Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 18 Jan 2024 15:17:39 -0600 Subject: [PATCH 246/413] ntdll: Fix exception list offset in call_user_mode_callback / user_mode_callback_return. (cherry picked from commit 39304daf4d7bdf9d3e617108e4ae4bd6bd7c7d25) CW-Bug-Id: #23244 --- dlls/ntdll/unix/signal_x86_64.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 1953179d76b..403112b4ff3 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1620,7 +1620,7 @@ __ASM_GLOBAL_FUNC( call_user_mode_callback, "movq %rax,0xa8(%rsp)\n\t" /* frame->syscall_cfa */ "movq 0x328(%r8),%r10\n\t" /* amd64_thread_data()->syscall_frame */ "movq (%r8),%rax\n\t" /* NtCurrentTeb()->Tib.ExceptionList */ - "movq %rax,0x408(%rsp)\n\t" + "movq %rax,0x400(%rsp)\n\t" "movl 0xb0(%r10),%r14d\n\t" /* prev_frame->syscall_flags */ "movl %r14d,0xb0(%rsp)\n\t" /* frame->syscall_flags */ "movq %r10,0xa0(%rsp)\n\t" /* frame->prev_frame */ @@ -1653,7 +1653,7 @@ __ASM_GLOBAL_FUNC( user_mode_callback_return, __ASM_CFI(".cfi_rel_offset %r13,-0x18\n\t") __ASM_CFI(".cfi_rel_offset %r14,-0x20\n\t") __ASM_CFI(".cfi_rel_offset %r15,-0x28\n\t") - "movq 0x408(%r10),%rax\n\t" /* exception list */ + "movq 0x400(%r10),%rax\n\t" /* exception list */ "movq %rax,0(%rcx)\n\t" /* teb->Tib.ExceptionList */ "movq -0x38(%rbp),%r10\n\t" /* ret_ptr */ "movq -0x40(%rbp),%r11\n\t" /* ret_len */ From 473dd7d21c342bcdcc7a49af53a05f3a088f08c3 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 31 Jan 2024 14:24:24 -0600 Subject: [PATCH 247/413] ntdll: Don't hardcode xstate feature mask. (cherry picked from commit 1e0728c5d48d87b38c54f655702bf2e639aa4f34) CW-Bug-Id: #23244 --- dlls/ntdll/unix/signal_i386.c | 61 +++++++++++++++++---------------- dlls/ntdll/unix/signal_x86_64.c | 61 +++++++++++++++++++++------------ dlls/ntdll/unix/system.c | 4 +++ dlls/ntdll/unix/unix_private.h | 9 +++++ 4 files changed, 84 insertions(+), 51 deletions(-) diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index 98e5e26ffa3..b20303d1b52 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -525,6 +525,7 @@ struct x86_thread_data UINT dr7; /* 1f0 */ SYSTEM_SERVICE_TABLE *syscall_table; /* 1f4 syscall table */ struct syscall_frame *syscall_frame; /* 1f8 frame pointer on syscall entry */ + UINT64 xstate_features_mask; /* 1fc */ }; C_ASSERT( sizeof(struct x86_thread_data) <= sizeof(((struct ntdll_thread_data *)0)->cpu_data) ); @@ -609,8 +610,6 @@ struct xcontext ULONG64 host_compaction_mask; }; -extern BOOL xstate_compaction_enabled; - static inline XSTATE *xstate_from_context( const CONTEXT *context ) { CONTEXT_EX *xctx = (CONTEXT_EX *)(context + 1); @@ -832,7 +831,7 @@ static inline void save_context( struct xcontext *xcontext, const ucontext_t *si context->ContextFlags |= CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS; memcpy( context->ExtendedRegisters, fpux, sizeof(*fpux) ); if (!fpu) fpux_to_fpu( &context->FloatSave, fpux ); - if ((cpu_info.ProcessorFeatureBits & CPU_FEATURE_AVX) && (xs = XState_sig(fpux))) + if (xstate_extended_features() && (xs = XState_sig(fpux))) { context_init_xstate( context, xs ); xcontext->host_compaction_mask = xs->CompactionMask; @@ -936,7 +935,7 @@ NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context ) DWORD flags = context->ContextFlags & ~CONTEXT_i386; BOOL self = (handle == GetCurrentThread()); - if ((flags & CONTEXT_XSTATE) && (cpu_info.ProcessorFeatureBits & CPU_FEATURE_AVX)) + if ((flags & CONTEXT_XSTATE) && xstate_extended_features()) { CONTEXT_EX *context_ex = (CONTEXT_EX *)(context + 1); XSTATE *xs = (XSTATE *)((char *)context_ex + context_ex->XState.Offset); @@ -944,7 +943,7 @@ NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context ) if (context_ex->XState.Length < offsetof(XSTATE, YmmContext) || context_ex->XState.Length > sizeof(XSTATE)) return STATUS_INVALID_PARAMETER; - if ((xs->Mask & XSTATE_MASK_GSSE) && (context_ex->XState.Length < sizeof(XSTATE))) + if ((xs->Mask & xstate_extended_features()) && (context_ex->XState.Length < sizeof(XSTATE))) return STATUS_BUFFER_OVERFLOW; } else flags &= ~CONTEXT_XSTATE; @@ -1143,7 +1142,7 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) context->ContextFlags |= CONTEXT_EXTENDED_REGISTERS; } - if ((needed_flags & CONTEXT_XSTATE) && (cpu_info.ProcessorFeatureBits & CPU_FEATURE_AVX)) + if ((needed_flags & CONTEXT_XSTATE) && xstate_extended_features()) { CONTEXT_EX *context_ex = (CONTEXT_EX *)(context + 1); XSTATE *xstate = (XSTATE *)((char *)context_ex + context_ex->XState.Offset); @@ -1153,7 +1152,7 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) || context_ex->XState.Length > sizeof(XSTATE)) return STATUS_INVALID_PARAMETER; - mask = (xstate_compaction_enabled ? xstate->CompactionMask : xstate->Mask) & XSTATE_MASK_GSSE; + mask = (xstate_compaction_enabled ? xstate->CompactionMask : xstate->Mask) & xstate_extended_features(); xstate->Mask = frame->xstate.Mask & mask; xstate->CompactionMask = xstate_compaction_enabled ? (0x8000000000000000 | mask) : 0; memset( xstate->Reserved, 0, sizeof(xstate->Reserved) ); @@ -1502,7 +1501,7 @@ static void setup_raise_exception( ucontext_t *sigcontext, void *stack_ptr, context_init_xstate( &stack->context, dst_xs ); memset( dst_xs, 0, offsetof(XSTATE, YmmContext) ); - dst_xs->CompactionMask = xstate_compaction_enabled ? 0x8000000000000004 : 0; + dst_xs->CompactionMask = xstate_compaction_enabled ? 0x8000000000000000 | xstate_extended_features() : 0; if (src_xs->Mask & 4) { dst_xs->Mask = 4; @@ -1604,7 +1603,7 @@ NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context context_init_xstate( &stack->context, dst_xs ); memset( dst_xs, 0, offsetof(XSTATE, YmmContext) ); - dst_xs->CompactionMask = xstate_compaction_enabled ? 0x8000000000000004 : 0; + dst_xs->CompactionMask = xstate_compaction_enabled ? 0x8000000000000000 | xstate_extended_features() : 0; if (src_xs->Mask & 4) { dst_xs->Mask = 4; @@ -2518,6 +2517,7 @@ void call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, TEB ldt_set_fs( thread_data->fs, teb ); thread_data->gs = get_gs(); thread_data->syscall_table = KeServiceDescriptorTable; + thread_data->xstate_features_mask = xstate_supported_features_mask; context.SegCs = get_cs(); context.SegDs = get_ds(); @@ -2553,6 +2553,8 @@ void call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, TEB *ctx = context; ctx->ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS; memset( frame, 0, sizeof(*frame) ); + if (xstate_compaction_enabled) + frame->xstate.CompactionMask = 0x8000000000000000 | xstate_supported_features_mask; NtSetContextThread( GetCurrentThread(), ctx ); stack = (DWORD *)ctx; @@ -2654,26 +2656,27 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, "addl %fs:0x1f4,%ebx\n\t" /* x86_thread_data()->syscall_table */ "testl $3,(%ecx)\n\t" /* frame->syscall_flags & (SYSCALL_HAVE_XSAVE | SYSCALL_HAVE_XSAVEC) */ "jz 2f\n\t" - "movl $7,%eax\n\t" - "xorl %edx,%edx\n\t" - "movl %edx,0x240(%ecx)\n\t" - "movl %edx,0x244(%ecx)\n\t" - "movl %edx,0x248(%ecx)\n\t" - "movl %edx,0x24c(%ecx)\n\t" - "movl %edx,0x250(%ecx)\n\t" - "movl %edx,0x254(%ecx)\n\t" + "movl %fs:0x1fc,%eax\n\t" /* x86_thread_data()->xstate_features_mask */ + "movl %fs:0x200,%edx\n\t" /* x86_thread_data()->xstate_features_mask high dword */ + "xorl %edi,%edi\n\t" + "movl %edi,0x240(%ecx)\n\t" + "movl %edi,0x244(%ecx)\n\t" + "movl %edi,0x248(%ecx)\n\t" + "movl %edi,0x24c(%ecx)\n\t" + "movl %edi,0x250(%ecx)\n\t" + "movl %edi,0x254(%ecx)\n\t" "testl $2,(%ecx)\n\t" /* frame->syscall_flags & SYSCALL_HAVE_XSAVEC */ "jz 1f\n\t" - "movl %edx,0x258(%ecx)\n\t" - "movl %edx,0x25c(%ecx)\n\t" - "movl %edx,0x260(%ecx)\n\t" - "movl %edx,0x264(%ecx)\n\t" - "movl %edx,0x268(%ecx)\n\t" - "movl %edx,0x26c(%ecx)\n\t" - "movl %edx,0x270(%ecx)\n\t" - "movl %edx,0x274(%ecx)\n\t" - "movl %edx,0x278(%ecx)\n\t" - "movl %edx,0x27c(%ecx)\n\t" + "movl %edi,0x258(%ecx)\n\t" + "movl %edi,0x25c(%ecx)\n\t" + "movl %edi,0x260(%ecx)\n\t" + "movl %edi,0x264(%ecx)\n\t" + "movl %edi,0x268(%ecx)\n\t" + "movl %edi,0x26c(%ecx)\n\t" + "movl %edi,0x270(%ecx)\n\t" + "movl %edi,0x274(%ecx)\n\t" + "movl %edi,0x278(%ecx)\n\t" + "movl %edi,0x27c(%ecx)\n\t" /* The xsavec instruction is not supported by * binutils < 2.25. */ ".byte 0x0f, 0xc7, 0x61, 0x40\n\t" /* xsavec 0x40(%ecx) */ @@ -2718,8 +2721,8 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, "testl $3,%ecx\n\t" /* SYSCALL_HAVE_XSAVE | SYSCALL_HAVE_XSAVEC */ "jz 1f\n\t" "movl %eax,%esi\n\t" - "movl $7,%eax\n\t" - "xorl %edx,%edx\n\t" + "movl %fs:0x1fc,%eax\n\t" /* x86_thread_data()->xstate_features_mask */ + "movl %fs:0x200,%edx\n\t" /* x86_thread_data()->xstate_features_mask high dword */ "xrstor 0x40(%esp)\n\t" "movl %esi,%eax\n\t" "jmp 3f\n" diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 403112b4ff3..8a63ffdd8d6 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -452,6 +452,8 @@ struct amd64_thread_data struct syscall_frame *syscall_frame; /* 0328 syscall frame pointer */ SYSTEM_SERVICE_TABLE *syscall_table; /* 0330 syscall table */ DWORD fs; /* 0338 WOW TEB selector */ + DWORD align; + UINT64 xstate_features_mask; /* 0340 */ }; C_ASSERT( sizeof(struct amd64_thread_data) <= sizeof(((struct ntdll_thread_data *)0)->cpu_data) ); @@ -459,6 +461,7 @@ C_ASSERT( offsetof( TEB, GdiTebBatch ) + offsetof( struct amd64_thread_data, pth C_ASSERT( offsetof( TEB, GdiTebBatch ) + offsetof( struct amd64_thread_data, syscall_frame ) == 0x328 ); C_ASSERT( offsetof( TEB, GdiTebBatch ) + offsetof( struct amd64_thread_data, syscall_table ) == 0x330 ); C_ASSERT( offsetof( TEB, GdiTebBatch ) + offsetof( struct amd64_thread_data, fs ) == 0x338 ); +C_ASSERT( offsetof( TEB, GdiTebBatch ) + offsetof( struct amd64_thread_data, xstate_features_mask ) == 0x340 ); static inline struct amd64_thread_data *amd64_thread_data(void) { @@ -488,8 +491,6 @@ struct xcontext ULONG64 host_compaction_mask; }; -extern BOOL xstate_compaction_enabled; - static inline XSTATE *xstate_from_context( const CONTEXT *context ) { CONTEXT_EX *xctx = (CONTEXT_EX *)(context + 1); @@ -910,7 +911,7 @@ static void save_context( struct xcontext *xcontext, const ucontext_t *sigcontex context->ContextFlags |= CONTEXT_FLOATING_POINT; context->FltSave = *FPU_sig(sigcontext); context->MxCsr = context->FltSave.MxCsr; - if ((cpu_info.ProcessorFeatureBits & CPU_FEATURE_AVX) && (xs = XState_sig(FPU_sig(sigcontext)))) + if (xstate_extended_features() && (xs = XState_sig(FPU_sig(sigcontext)))) { /* xcontext and sigcontext are both on the signal stack, so we can * just reference sigcontext without overflowing 32 bit XState.Offset */ @@ -940,7 +941,7 @@ static void restore_context( const struct xcontext *xcontext, ucontext_t *sigcon amd64_thread_data()->dr7 = context->Dr7; set_sigcontext( context, sigcontext ); if (FPU_sig(sigcontext)) *FPU_sig(sigcontext) = context->FltSave; - if ((cpu_info.ProcessorFeatureBits & CPU_FEATURE_AVX) && (xs = XState_sig(FPU_sig(sigcontext)))) + if (xstate_extended_features() && (xs = XState_sig(FPU_sig(sigcontext)))) xs->CompactionMask = xcontext->host_compaction_mask; leave_handler( sigcontext ); } @@ -989,7 +990,7 @@ NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context ) BOOL self = (handle == GetCurrentThread()); struct syscall_frame *frame = amd64_thread_data()->syscall_frame; - if ((flags & CONTEXT_XSTATE) && (cpu_info.ProcessorFeatureBits & CPU_FEATURE_AVX)) + if ((flags & CONTEXT_XSTATE) && xstate_extended_features()) { CONTEXT_EX *context_ex = (CONTEXT_EX *)(context + 1); XSTATE *xs = (XSTATE *)((char *)context_ex + context_ex->XState.Offset); @@ -997,7 +998,7 @@ NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context ) if (context_ex->XState.Length < offsetof(XSTATE, YmmContext) || context_ex->XState.Length > sizeof(XSTATE)) return STATUS_INVALID_PARAMETER; - if ((xs->Mask & XSTATE_MASK_GSSE) && (context_ex->XState.Length < sizeof(XSTATE))) + if ((xs->Mask & xstate_extended_features()) && (context_ex->XState.Length < sizeof(XSTATE))) return STATUS_BUFFER_OVERFLOW; } else flags &= ~CONTEXT_XSTATE; @@ -1172,7 +1173,7 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) context->MxCsr = context->FltSave.MxCsr; context->ContextFlags |= CONTEXT_FLOATING_POINT; } - if ((needed_flags & CONTEXT_XSTATE) && (cpu_info.ProcessorFeatureBits & CPU_FEATURE_AVX)) + if ((needed_flags & CONTEXT_XSTATE) && xstate_extended_features()) { CONTEXT_EX *context_ex = (CONTEXT_EX *)(context + 1); XSTATE *xstate = (XSTATE *)((char *)context_ex + context_ex->XState.Offset); @@ -1182,7 +1183,7 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) || context_ex->XState.Length > sizeof(XSTATE)) return STATUS_INVALID_PARAMETER; - mask = (xstate_compaction_enabled ? xstate->CompactionMask : xstate->Mask) & XSTATE_MASK_GSSE; + mask = (xstate_compaction_enabled ? xstate->CompactionMask : xstate->Mask) & xstate_extended_features(); xstate->Mask = frame->xstate.Mask & mask; xstate->CompactionMask = xstate_compaction_enabled ? (0x8000000000000000 | mask) : 0; memset( xstate->Reserved, 0, sizeof(xstate->Reserved) ); @@ -1406,7 +1407,7 @@ NTSTATUS get_thread_wow64_context( HANDLE handle, void *ctx, ULONG size ) context_ex->XState.Length > sizeof(XSTATE)) return STATUS_INVALID_PARAMETER; - mask = (xstate_compaction_enabled ? xstate->CompactionMask : xstate->Mask) & XSTATE_MASK_GSSE; + mask = (xstate_compaction_enabled ? xstate->CompactionMask : xstate->Mask) & xstate_extended_features(); xstate->Mask = frame->xstate.Mask & mask; xstate->CompactionMask = xstate_compaction_enabled ? (0x8000000000000000 | mask) : 0; memset( xstate->Reserved, 0, sizeof(xstate->Reserved) ); @@ -1471,7 +1472,7 @@ static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec assert( !((ULONG_PTR)&stack->xstate & 63) ); context_init_xstate( &stack->context, &stack->xstate ); memset( &stack->xstate, 0, offsetof(XSTATE, YmmContext) ); - stack->xstate.CompactionMask = xstate_compaction_enabled ? 0x8000000000000004 : 0; + stack->xstate.CompactionMask = xstate_compaction_enabled ? 0x8000000000000000 | xstate_extended_features() : 0; if (src_xs->Mask & 4) { stack->xstate.Mask = 4; @@ -2832,6 +2833,7 @@ void call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, TEB I386_CONTEXT *wow_context; thread_data->syscall_table = KeServiceDescriptorTable; + thread_data->xstate_features_mask = xstate_supported_features_mask; #if defined __linux__ arch_prctl( ARCH_SET_GS, teb ); @@ -2904,6 +2906,8 @@ void call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, TEB *ctx = context; ctx->ContextFlags = CONTEXT_FULL; memset( frame, 0, sizeof(*frame) ); + if (xstate_compaction_enabled) + frame->xstate.CompactionMask = 0x8000000000000000 | xstate_supported_features_mask; NtSetContextThread( GetCurrentThread(), ctx ); frame->cs = cs64_sel; @@ -3001,18 +3005,25 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, "movl 0xb0(%rcx),%r14d\n\t" /* frame->syscall_flags */ "testl $3,%r14d\n\t" /* SYSCALL_HAVE_XSAVE | SYSCALL_HAVE_XSAVEC */ "jz 2f\n\t" - "movl $7,%eax\n\t" - "xorl %edx,%edx\n\t" - "movq %rdx,0x2c0(%rcx)\n\t" - "movq %rdx,0x2c8(%rcx)\n\t" - "movq %rdx,0x2d0(%rcx)\n\t" +#ifdef __APPLE__ + "movq %gs:0x30,%rdx\n\t" + "movl 0x340(%rdx),%eax\n\t" + "movl 0x344(%rdx),%edx\n\t" +#else + "movl %gs:0x340,%eax\n\t" /* amd64_thread_data()->xstate_features_mask */ + "movl %gs:0x344,%edx\n\t" /* amd64_thread_data()->xstate_features_mask high dword */ +#endif + "xorq %rbp,%rbp\n\t" + "movq %rbp,0x2c0(%rcx)\n\t" + "movq %rbp,0x2c8(%rcx)\n\t" + "movq %rbp,0x2d0(%rcx)\n\t" "testl $2,%r14d\n\t" /* SYSCALL_HAVE_XSAVEC */ "jz 1f\n\t" - "movq %rdx,0x2d8(%rcx)\n\t" - "movq %rdx,0x2e0(%rcx)\n\t" - "movq %rdx,0x2e8(%rcx)\n\t" - "movq %rdx,0x2f0(%rcx)\n\t" - "movq %rdx,0x2f8(%rcx)\n\t" + "movq %rbp,0x2d8(%rcx)\n\t" + "movq %rbp,0x2e0(%rcx)\n\t" + "movq %rbp,0x2e8(%rcx)\n\t" + "movq %rbp,0x2f0(%rcx)\n\t" + "movq %rbp,0x2f8(%rcx)\n\t" /* The xsavec instruction is not supported by * binutils < 2.25. */ ".byte 0x48, 0x0f, 0xc7, 0xa1, 0xc0, 0x00, 0x00, 0x00\n\t" /* xsavec64 0xc0(%rcx) */ @@ -3114,8 +3125,14 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, "2:\ttestl $3,%r14d\n\t" /* SYSCALL_HAVE_XSAVE | SYSCALL_HAVE_XSAVEC */ "jz 3f\n\t" "movq %rax,%r11\n\t" - "movl $7,%eax\n\t" - "xorl %edx,%edx\n\t" +#ifdef __APPLE__ + "movq %gs:0x30,%rdx\n\t" + "movl 0x340(%rdx),%eax\n\t" + "movl 0x344(%rdx),%edx\n\t" +#else + "movl %gs:0x340,%eax\n\t" /* amd64_thread_data()->xstate_features_mask */ + "movl %gs:0x344,%edx\n\t" /* amd64_thread_data()->xstate_features_mask high dword */ +#endif "xrstor64 0xc0(%rcx)\n\t" "movq %r11,%rax\n\t" "movl 0xb4(%rcx),%edx\n\t" /* frame->restore_flags */ diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index 69055146cda..d19f9db3450 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -255,6 +255,7 @@ cpu_override; #if defined(__i386__) || defined(__x86_64__) BOOL xstate_compaction_enabled = FALSE; +UINT64 xstate_supported_features_mask; #define AUTH 0x68747541 /* "Auth" */ #define ENTI 0x69746e65 /* "enti" */ @@ -404,6 +405,9 @@ static void get_cpuinfo( SYSTEM_CPU_INFORMATION *info ) { do_cpuid( 0x0000000d, 1, regs3 ); /* get XSAVE details */ if (regs3[0] & 2) xstate_compaction_enabled = TRUE; + xstate_supported_features_mask = 3; + if (features & CPU_FEATURE_AVX) + xstate_supported_features_mask |= (UINT64)1 << XSTATE_AVX; } if (regs[1] == AUTH && regs[3] == ENTI && regs[2] == CAMD) diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index 8ba8950fb55..7fc5932c1ff 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -227,6 +227,15 @@ extern int server_pipe( int fd[2] ); extern void fpux_to_fpu( I386_FLOATING_SAVE_AREA *fpu, const XSAVE_FORMAT *fpux ); extern void fpu_to_fpux( XSAVE_FORMAT *fpux, const I386_FLOATING_SAVE_AREA *fpu ); + +extern BOOL xstate_compaction_enabled; +extern UINT64 xstate_supported_features_mask; + +static inline UINT64 xstate_extended_features(void) +{ + return xstate_supported_features_mask & ~(UINT64)3; +} + extern void *get_cpu_area( USHORT machine ); extern void set_thread_id( TEB *teb, DWORD pid, DWORD tid ); extern NTSTATUS init_thread_stack( TEB *teb, ULONG_PTR limit, SIZE_T reserve_size, SIZE_T commit_size ); From df7bee7ebacb0d0009afbf92fec85050777184a7 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 31 Jan 2024 16:12:58 -0600 Subject: [PATCH 248/413] ntdll: Don't hardcode xstate size in syscall frame. (cherry picked from commit 7ae488a2bb58501684c6475d4942277b852475fc) CW-Bug-Id: #23244 --- dlls/ntdll/unix/signal_i386.c | 53 ++++++++-------- dlls/ntdll/unix/signal_x86_64.c | 104 +++++++++++++++++++------------- dlls/ntdll/unix/system.c | 21 +++++++ dlls/ntdll/unix/unix_private.h | 3 + include/winnt.h | 8 +++ 5 files changed, 123 insertions(+), 66 deletions(-) diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index b20303d1b52..4f192e1df63 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -508,10 +508,10 @@ struct syscall_frame /* Leave space for the whole set of YMM registers. They're not used in * 32-bit mode, but some processors fault if they're not in writable memory. */ - DECLSPEC_ALIGN(64) XSTATE xstate; /* 240 */ + DECLSPEC_ALIGN(64) XSAVE_AREA_HEADER xstate; /* 240 */ }; -C_ASSERT( sizeof(struct syscall_frame) == 0x380 ); +C_ASSERT( sizeof(struct syscall_frame) == 0x280 ); struct x86_thread_data { @@ -526,12 +526,14 @@ struct x86_thread_data SYSTEM_SERVICE_TABLE *syscall_table; /* 1f4 syscall table */ struct syscall_frame *syscall_frame; /* 1f8 frame pointer on syscall entry */ UINT64 xstate_features_mask; /* 1fc */ + UINT xstate_features_size; /* 204 */ }; C_ASSERT( sizeof(struct x86_thread_data) <= sizeof(((struct ntdll_thread_data *)0)->cpu_data) ); C_ASSERT( offsetof( TEB, GdiTebBatch ) + offsetof( struct x86_thread_data, gs ) == 0x1d8 ); C_ASSERT( offsetof( TEB, GdiTebBatch ) + offsetof( struct x86_thread_data, syscall_table ) == 0x1f4 ); C_ASSERT( offsetof( TEB, GdiTebBatch ) + offsetof( struct x86_thread_data, syscall_frame ) == 0x1f8 ); +C_ASSERT( offsetof( TEB, GdiTebBatch ) + offsetof( struct x86_thread_data, xstate_features_size ) == 0x204 ); /* flags to control the behavior of the syscall dispatcher */ #define SYSCALL_HAVE_XSAVE 1 @@ -938,12 +940,13 @@ NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context ) if ((flags & CONTEXT_XSTATE) && xstate_extended_features()) { CONTEXT_EX *context_ex = (CONTEXT_EX *)(context + 1); - XSTATE *xs = (XSTATE *)((char *)context_ex + context_ex->XState.Offset); + XSAVE_AREA_HEADER *xs = (XSAVE_AREA_HEADER *)((char *)context_ex + context_ex->XState.Offset); - if (context_ex->XState.Length < offsetof(XSTATE, YmmContext) || - context_ex->XState.Length > sizeof(XSTATE)) + if (context_ex->XState.Length < sizeof(XSAVE_AREA_HEADER) || + context_ex->XState.Length > sizeof(XSAVE_AREA_HEADER) + xstate_features_size) return STATUS_INVALID_PARAMETER; - if ((xs->Mask & xstate_extended_features()) && (context_ex->XState.Length < sizeof(XSTATE))) + if ((xs->Mask & xstate_extended_features()) + && (context_ex->XState.Length < xstate_get_size( xs->CompactionMask, xs->Mask ))) return STATUS_BUFFER_OVERFLOW; } else flags &= ~CONTEXT_XSTATE; @@ -1019,14 +1022,9 @@ NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context ) if (flags & CONTEXT_XSTATE) { CONTEXT_EX *context_ex = (CONTEXT_EX *)(context + 1); - XSTATE *xs = (XSTATE *)((char *)context_ex + context_ex->XState.Offset); + XSAVE_AREA_HEADER *xs = (XSAVE_AREA_HEADER *)((char *)context_ex + context_ex->XState.Offset); - if (xs->Mask & XSTATE_MASK_GSSE) - { - frame->xstate.Mask |= XSTATE_MASK_GSSE; - frame->xstate.YmmContext = xs->YmmContext; - } - else frame->xstate.Mask &= ~XSTATE_MASK_GSSE; + copy_xstate( &frame->xstate, xs, xs->Mask ); } frame->restore_flags |= flags & ~CONTEXT_INTEGER; @@ -1145,21 +1143,22 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) if ((needed_flags & CONTEXT_XSTATE) && xstate_extended_features()) { CONTEXT_EX *context_ex = (CONTEXT_EX *)(context + 1); - XSTATE *xstate = (XSTATE *)((char *)context_ex + context_ex->XState.Offset); - unsigned int mask; + XSAVE_AREA_HEADER *xstate = (XSAVE_AREA_HEADER *)((char *)context_ex + context_ex->XState.Offset); + UINT64 mask; - if (context_ex->XState.Length < offsetof(XSTATE, YmmContext) - || context_ex->XState.Length > sizeof(XSTATE)) + if (context_ex->XState.Length < sizeof(XSAVE_AREA_HEADER) || + context_ex->XState.Length > sizeof(XSAVE_AREA_HEADER) + xstate_features_size) return STATUS_INVALID_PARAMETER; mask = (xstate_compaction_enabled ? xstate->CompactionMask : xstate->Mask) & xstate_extended_features(); xstate->Mask = frame->xstate.Mask & mask; xstate->CompactionMask = xstate_compaction_enabled ? (0x8000000000000000 | mask) : 0; - memset( xstate->Reserved, 0, sizeof(xstate->Reserved) ); + memset( xstate->Reserved2, 0, sizeof(xstate->Reserved2) ); if (xstate->Mask) { - if (context_ex->XState.Length < sizeof(XSTATE)) return STATUS_BUFFER_OVERFLOW; - xstate->YmmContext = frame->xstate.YmmContext; + if (context_ex->XState.Length < xstate_get_size( xstate->CompactionMask, xstate->Mask )) + return STATUS_BUFFER_OVERFLOW; + copy_xstate( xstate, &frame->xstate, xstate->Mask ); } } if (context->ContextFlags & (CONTEXT_DEBUG_REGISTERS & ~CONTEXT_i386)) @@ -1500,7 +1499,7 @@ static void setup_raise_exception( ucontext_t *sigcontext, void *stack_ptr, XSTATE *dst_xs = (XSTATE *)(((ULONG_PTR)stack->xstate + 63) & ~63); context_init_xstate( &stack->context, dst_xs ); - memset( dst_xs, 0, offsetof(XSTATE, YmmContext) ); + memset( dst_xs, 0, sizeof(XSAVE_AREA_HEADER) ); dst_xs->CompactionMask = xstate_compaction_enabled ? 0x8000000000000000 | xstate_extended_features() : 0; if (src_xs->Mask & 4) { @@ -1640,7 +1639,8 @@ __ASM_GLOBAL_FUNC( call_user_mode_callback, __ASM_CFI(".cfi_rel_offset %edi,-12\n\t") "movl 0x18(%ebp),%edx\n\t" /* teb */ "pushl 0(%edx)\n\t" /* teb->Tib.ExceptionList */ - "subl $0x380,%esp\n\t" /* sizeof(struct syscall_frame) */ + "subl $0x280,%esp\n\t" /* sizeof(struct syscall_frame) */ + "subl %fs:0x204,%esp\n\t" /* x86_thread_data()->xstate_features_size */ "andl $~63,%esp\n\t" "leal 8(%ebp),%eax\n\t" "movl %eax,0x38(%esp)\n\t" /* frame->syscall_cfa */ @@ -2440,6 +2440,7 @@ NTSTATUS signal_alloc_thread( TEB *teb ) else thread_data->fs = gdt_fs_sel; teb->WOW32Reserved = __wine_syscall_dispatcher; + thread_data->xstate_features_size = xstate_features_size; return STATUS_SUCCESS; } @@ -2468,7 +2469,9 @@ void signal_init_process(void) struct sigaction sig_act; void *kernel_stack = (char *)ntdll_get_thread_data()->kernel_stack + kernel_stack_size; - x86_thread_data()->syscall_frame = (struct syscall_frame *)kernel_stack - 1; + x86_thread_data()->syscall_frame = (struct syscall_frame *)((ULONG_PTR)((char *)kernel_stack + - sizeof(struct syscall_frame) - xstate_features_size) & ~(ULONG_PTR)63); + x86_thread_data()->xstate_features_size = xstate_features_size; if (cpu_info.ProcessorFeatureBits & CPU_FEATURE_FXSR) syscall_flags |= SYSCALL_HAVE_FXSAVE; if (cpu_info.ProcessorFeatureBits & CPU_FEATURE_XSAVE) syscall_flags |= SYSCALL_HAVE_XSAVE; @@ -2518,6 +2521,7 @@ void call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, TEB thread_data->gs = get_gs(); thread_data->syscall_table = KeServiceDescriptorTable; thread_data->xstate_features_mask = xstate_supported_features_mask; + assert( thread_data->xstate_features_size == xstate_features_size ); context.SegCs = get_cs(); context.SegDs = get_ds(); @@ -2596,7 +2600,8 @@ __ASM_GLOBAL_FUNC( signal_start_thread, "movl 0x1f8(%ecx),%eax\n\t" /* x86_thread_data()->syscall_frame */ "orl %eax,%eax\n\t" "jnz 1f\n\t" - "leal -0x380(%esp),%eax\n\t" /* sizeof(struct syscall_frame) */ + "leal -0x280(%esp),%eax\n\t" /* sizeof(struct syscall_frame) */ + "subl %fs:0x204,%eax\n\t" /* x86_thread_data()->xstate_features_size */ "andl $~63,%eax\n\t" "movl %eax,0x1f8(%ecx)\n" /* x86_thread_data()->syscall_frame */ /* switch to kernel stack */ diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 8a63ffdd8d6..fb97bf4d598 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -435,10 +435,12 @@ struct syscall_frame DWORD restore_flags; /* 00b4 */ DWORD align[2]; /* 00b8 */ XMM_SAVE_AREA32 xsave; /* 00c0 */ - DECLSPEC_ALIGN(64) XSTATE xstate; /* 02c0 */ + DECLSPEC_ALIGN(64) XSAVE_AREA_HEADER xstate; /* 02c0 */ }; -C_ASSERT( sizeof( struct syscall_frame ) == 0x400); +C_ASSERT( offsetof( struct syscall_frame, xsave ) == 0xc0 ); +C_ASSERT( offsetof( struct syscall_frame, xstate ) == 0x2c0 ); +C_ASSERT( sizeof( struct syscall_frame ) == 0x300); struct amd64_thread_data { @@ -452,7 +454,7 @@ struct amd64_thread_data struct syscall_frame *syscall_frame; /* 0328 syscall frame pointer */ SYSTEM_SERVICE_TABLE *syscall_table; /* 0330 syscall table */ DWORD fs; /* 0338 WOW TEB selector */ - DWORD align; + DWORD xstate_features_size; /* 033c */ UINT64 xstate_features_mask; /* 0340 */ }; @@ -461,6 +463,7 @@ C_ASSERT( offsetof( TEB, GdiTebBatch ) + offsetof( struct amd64_thread_data, pth C_ASSERT( offsetof( TEB, GdiTebBatch ) + offsetof( struct amd64_thread_data, syscall_frame ) == 0x328 ); C_ASSERT( offsetof( TEB, GdiTebBatch ) + offsetof( struct amd64_thread_data, syscall_table ) == 0x330 ); C_ASSERT( offsetof( TEB, GdiTebBatch ) + offsetof( struct amd64_thread_data, fs ) == 0x338 ); +C_ASSERT( offsetof( TEB, GdiTebBatch ) + offsetof( struct amd64_thread_data, xstate_features_size ) == 0x33c ); C_ASSERT( offsetof( TEB, GdiTebBatch ) + offsetof( struct amd64_thread_data, xstate_features_mask ) == 0x340 ); static inline struct amd64_thread_data *amd64_thread_data(void) @@ -993,12 +996,13 @@ NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context ) if ((flags & CONTEXT_XSTATE) && xstate_extended_features()) { CONTEXT_EX *context_ex = (CONTEXT_EX *)(context + 1); - XSTATE *xs = (XSTATE *)((char *)context_ex + context_ex->XState.Offset); + XSAVE_AREA_HEADER *xs = (XSAVE_AREA_HEADER *)((char *)context_ex + context_ex->XState.Offset); - if (context_ex->XState.Length < offsetof(XSTATE, YmmContext) || - context_ex->XState.Length > sizeof(XSTATE)) + if (context_ex->XState.Length < sizeof(XSAVE_AREA_HEADER) || + context_ex->XState.Length > sizeof(XSAVE_AREA_HEADER) + xstate_features_size) return STATUS_INVALID_PARAMETER; - if ((xs->Mask & xstate_extended_features()) && (context_ex->XState.Length < sizeof(XSTATE))) + if ((xs->Mask & xstate_extended_features()) + && (context_ex->XState.Length < xstate_get_size( xs->CompactionMask, xs->Mask ))) return STATUS_BUFFER_OVERFLOW; } else flags &= ~CONTEXT_XSTATE; @@ -1063,14 +1067,9 @@ NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context ) if (flags & CONTEXT_XSTATE) { CONTEXT_EX *context_ex = (CONTEXT_EX *)(context + 1); - XSTATE *xs = (XSTATE *)((char *)context_ex + context_ex->XState.Offset); + XSAVE_AREA_HEADER *xs = (XSAVE_AREA_HEADER *)((char *)context_ex + context_ex->XState.Offset); - if (xs->Mask & XSTATE_MASK_GSSE) - { - frame->xstate.Mask |= XSTATE_MASK_GSSE; - memcpy( &frame->xstate.YmmContext, &xs->YmmContext, sizeof(xs->YmmContext) ); - } - else frame->xstate.Mask &= ~XSTATE_MASK_GSSE; + copy_xstate( &frame->xstate, xs, xs->Mask ); } frame->restore_flags |= flags & ~CONTEXT_INTEGER; @@ -1176,21 +1175,22 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) if ((needed_flags & CONTEXT_XSTATE) && xstate_extended_features()) { CONTEXT_EX *context_ex = (CONTEXT_EX *)(context + 1); - XSTATE *xstate = (XSTATE *)((char *)context_ex + context_ex->XState.Offset); - unsigned int mask; + XSAVE_AREA_HEADER *xstate = (XSAVE_AREA_HEADER *)((char *)context_ex + context_ex->XState.Offset); + UINT64 mask; - if (context_ex->XState.Length < offsetof(XSTATE, YmmContext) - || context_ex->XState.Length > sizeof(XSTATE)) + if (context_ex->XState.Length < sizeof(XSAVE_AREA_HEADER) || + context_ex->XState.Length > sizeof(XSAVE_AREA_HEADER) + xstate_features_size) return STATUS_INVALID_PARAMETER; mask = (xstate_compaction_enabled ? xstate->CompactionMask : xstate->Mask) & xstate_extended_features(); xstate->Mask = frame->xstate.Mask & mask; xstate->CompactionMask = xstate_compaction_enabled ? (0x8000000000000000 | mask) : 0; - memset( xstate->Reserved, 0, sizeof(xstate->Reserved) ); + memset( xstate->Reserved2, 0, sizeof(xstate->Reserved2) ); if (xstate->Mask) { - if (context_ex->XState.Length < sizeof(XSTATE)) return STATUS_BUFFER_OVERFLOW; - memcpy( &xstate->YmmContext, &frame->xstate.YmmContext, sizeof(xstate->YmmContext) ); + if (context_ex->XState.Length < xstate_get_size( xstate->CompactionMask, xstate->Mask )) + return STATUS_BUFFER_OVERFLOW; + copy_xstate( xstate, &frame->xstate, xstate->Mask ); } } if (context->ContextFlags & (CONTEXT_DEBUG_REGISTERS & ~CONTEXT_AMD64)) @@ -1309,14 +1309,9 @@ NTSTATUS set_thread_wow64_context( HANDLE handle, const void *ctx, ULONG size ) if (flags & CONTEXT_I386_XSTATE) { CONTEXT_EX *context_ex = (CONTEXT_EX *)(context + 1); - XSTATE *xs = (XSTATE *)((char *)context_ex + context_ex->XState.Offset); + XSAVE_AREA_HEADER *xs = (XSAVE_AREA_HEADER *)((char *)context_ex + context_ex->XState.Offset); - if (xs->Mask & XSTATE_MASK_GSSE) - { - frame->xstate.Mask |= XSTATE_MASK_GSSE; - memcpy( &frame->xstate.YmmContext, &xs->YmmContext, sizeof(xs->YmmContext) ); - } - else frame->xstate.Mask &= ~XSTATE_MASK_GSSE; + copy_xstate( &frame->xstate, xs, xs->Mask ); frame->restore_flags |= CONTEXT_XSTATE; } return STATUS_SUCCESS; @@ -1397,24 +1392,25 @@ NTSTATUS get_thread_wow64_context( HANDLE handle, void *ctx, ULONG size ) fpux_to_fpu( &context->FloatSave, &frame->xsave ); context->ContextFlags |= CONTEXT_I386_FLOATING_POINT; } - if ((needed_flags & CONTEXT_I386_XSTATE) && (cpu_info.ProcessorFeatureBits & CPU_FEATURE_AVX)) + if ((needed_flags & CONTEXT_I386_XSTATE) && xstate_extended_features()) { CONTEXT_EX *context_ex = (CONTEXT_EX *)(context + 1); - XSTATE *xstate = (XSTATE *)((char *)context_ex + context_ex->XState.Offset); - unsigned int mask; + XSAVE_AREA_HEADER *xstate = (XSAVE_AREA_HEADER *)((char *)context_ex + context_ex->XState.Offset); + UINT64 mask; - if (context_ex->XState.Length < offsetof(XSTATE, YmmContext) || - context_ex->XState.Length > sizeof(XSTATE)) + if (context_ex->XState.Length < sizeof(XSAVE_AREA_HEADER) || + context_ex->XState.Length > sizeof(XSAVE_AREA_HEADER) + xstate_features_size) return STATUS_INVALID_PARAMETER; mask = (xstate_compaction_enabled ? xstate->CompactionMask : xstate->Mask) & xstate_extended_features(); xstate->Mask = frame->xstate.Mask & mask; xstate->CompactionMask = xstate_compaction_enabled ? (0x8000000000000000 | mask) : 0; - memset( xstate->Reserved, 0, sizeof(xstate->Reserved) ); + memset( xstate->Reserved2, 0, sizeof(xstate->Reserved2) ); if (xstate->Mask) { - if (context_ex->XState.Length < sizeof(XSTATE)) return STATUS_BUFFER_OVERFLOW; - memcpy( &xstate->YmmContext, &frame->xstate.YmmContext, sizeof(xstate->YmmContext) ); + if (context_ex->XState.Length < xstate_get_size( xstate->CompactionMask, xstate->Mask )) + return STATUS_BUFFER_OVERFLOW; + copy_xstate( xstate, &frame->xstate, xstate->Mask ); } } return STATUS_SUCCESS; @@ -1573,7 +1569,7 @@ NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context { assert( !((ULONG_PTR)&stack->xstate & 63) ); context_init_xstate( &stack->context, &stack->xstate ); - memcpy( &stack->xstate, &frame->xstate, sizeof(frame->xstate) ); + memcpy( &stack->xstate, &frame->xstate, sizeof(XSAVE_AREA_HEADER) + xstate_features_size ); } else context_init_xstate( &stack->context, NULL ); @@ -1615,13 +1611,20 @@ __ASM_GLOBAL_FUNC( call_user_mode_callback, "fnstcw -0x2c(%rbp)\n\t" "movq %rsi,-0x38(%rbp)\n\t" /* ret_ptr */ "movq %rdx,-0x40(%rbp)\n\t" /* ret_len */ - "subq $0x408,%rsp\n\t" /* sizeof(struct syscall_frame) + exception */ + "subq $0x308,%rsp\n\t" /* sizeof(struct syscall_frame) + exception */ +#ifdef __APPLE__ + "movq %gs:0x30,%rsi\n\t" + "movl 0x33c(%rsi),%esi\n\t" +#else + "movl %gs:0x33c,%esi\n\t" /* amd64_thread_data()->xstate_features_size */ +#endif + "subq %rsi,%rsp\n\t" "andq $~63,%rsp\n\t" "leaq 0x10(%rbp),%rax\n\t" "movq %rax,0xa8(%rsp)\n\t" /* frame->syscall_cfa */ "movq 0x328(%r8),%r10\n\t" /* amd64_thread_data()->syscall_frame */ "movq (%r8),%rax\n\t" /* NtCurrentTeb()->Tib.ExceptionList */ - "movq %rax,0x400(%rsp)\n\t" + "movq %rax,0x300(%rsp,%rsi)\n\t" "movl 0xb0(%r10),%r14d\n\t" /* prev_frame->syscall_flags */ "movl %r14d,0xb0(%rsp)\n\t" /* frame->syscall_flags */ "movq %r10,0xa0(%rsp)\n\t" /* frame->prev_frame */ @@ -1654,7 +1657,13 @@ __ASM_GLOBAL_FUNC( user_mode_callback_return, __ASM_CFI(".cfi_rel_offset %r13,-0x18\n\t") __ASM_CFI(".cfi_rel_offset %r14,-0x20\n\t") __ASM_CFI(".cfi_rel_offset %r15,-0x28\n\t") - "movq 0x400(%r10),%rax\n\t" /* exception list */ +#ifdef __APPLE__ + "movq %gs:0x30,%rax\n\t" + "movl 0x33c(%rax),%eax\n\t" +#else + "movl %gs:0x33c,%eax\n\t" /* amd64_thread_data()->xstate_features_size */ +#endif + "movq 0x300(%r10,%rax),%rax\n\t" /* exception list */ "movq %rax,0(%rcx)\n\t" /* teb->Tib.ExceptionList */ "movq -0x38(%rbp),%r10\n\t" /* ret_ptr */ "movq -0x40(%rbp),%r11\n\t" /* ret_len */ @@ -2651,6 +2660,7 @@ NTSTATUS signal_alloc_thread( TEB *teb ) } else thread_data->fs = fs32_sel; } + thread_data->xstate_features_size = xstate_features_size; return STATUS_SUCCESS; } @@ -2737,7 +2747,9 @@ void signal_init_process(void) WOW_TEB *wow_teb = get_wow_teb( NtCurrentTeb() ); void *ptr, *kernel_stack = (char *)ntdll_get_thread_data()->kernel_stack + kernel_stack_size; - amd64_thread_data()->syscall_frame = (struct syscall_frame *)kernel_stack - 1; + amd64_thread_data()->syscall_frame = (struct syscall_frame *)((ULONG_PTR)((char *)kernel_stack + - sizeof(struct syscall_frame) - xstate_features_size) & ~(ULONG_PTR)63); + amd64_thread_data()->xstate_features_size = xstate_features_size; /* sneak in a syscall dispatcher pointer at a fixed address (7ffe1000) */ ptr = (char *)user_shared_data + page_size; @@ -2834,6 +2846,7 @@ void call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, TEB thread_data->syscall_table = KeServiceDescriptorTable; thread_data->xstate_features_mask = xstate_supported_features_mask; + assert( thread_data->xstate_features_size == xstate_features_size ); #if defined __linux__ arch_prctl( ARCH_SET_GS, teb ); @@ -2950,7 +2963,14 @@ __ASM_GLOBAL_FUNC( signal_start_thread, "movq 0x328(%rcx),%r8\n\t" /* amd64_thread_data()->syscall_frame */ "orq %r8,%r8\n\t" "jnz 1f\n\t" - "leaq -0x400(%rsp),%r8\n\t" /* sizeof(struct syscall_frame) */ + "leaq -0x300(%rsp),%r8\n\t" /* sizeof(struct syscall_frame) */ +#ifdef __APPLE__ + "movq %gs:0x30,%rax\n\t" + "movl 0x33c(%rax),%eax\n\t" +#else + "movl %gs:0x33c,%eax\n\t" /* amd64_thread_data()->xstate_features_size */ +#endif + "subq %rax,%r8\n\t" "andq $~63,%r8\n\t" "movq %r8,0x328(%rcx)\n" /* amd64_thread_data()->syscall_frame */ /* switch to kernel stack */ diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index d19f9db3450..56f86a6caa7 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -256,6 +256,23 @@ cpu_override; BOOL xstate_compaction_enabled = FALSE; UINT64 xstate_supported_features_mask; +UINT64 xstate_features_size; + +unsigned int xstate_get_size( UINT64 compaction_mask, UINT64 mask ) +{ + if (!(mask & ((UINT64)1 << XSTATE_AVX))) return sizeof(XSAVE_AREA_HEADER); + return sizeof(XSAVE_AREA_HEADER) + sizeof(YMMCONTEXT); +} + +void copy_xstate( XSAVE_AREA_HEADER *dst, XSAVE_AREA_HEADER *src, UINT64 mask ) +{ + mask &= xstate_extended_features() & src->Mask; + if (src->CompactionMask) mask &= src->CompactionMask; + if (dst->CompactionMask) mask &= dst->CompactionMask; + dst->Mask = (dst->Mask & ~xstate_extended_features()) | mask; + if (mask & ((UINT64)1 << XSTATE_AVX)) + *(YMMCONTEXT *)(dst + 1) = *(YMMCONTEXT *)(src + 1); +} #define AUTH 0x68747541 /* "Auth" */ #define ENTI 0x69746e65 /* "enti" */ @@ -408,6 +425,10 @@ static void get_cpuinfo( SYSTEM_CPU_INFORMATION *info ) xstate_supported_features_mask = 3; if (features & CPU_FEATURE_AVX) xstate_supported_features_mask |= (UINT64)1 << XSTATE_AVX; + xstate_features_size = xstate_get_size( xstate_compaction_enabled ? 0x8000000000000000 + | xstate_supported_features_mask : 0, xstate_supported_features_mask ) + - sizeof(XSAVE_AREA_HEADER); + xstate_features_size = (xstate_features_size + 15) & ~15; } if (regs[1] == AUTH && regs[3] == ENTI && regs[2] == CAMD) diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index 7fc5932c1ff..77a3de4c5d0 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -230,6 +230,9 @@ extern void fpu_to_fpux( XSAVE_FORMAT *fpux, const I386_FLOATING_SAVE_AREA *fpu extern BOOL xstate_compaction_enabled; extern UINT64 xstate_supported_features_mask; +extern UINT64 xstate_features_size; +extern unsigned int xstate_get_size( UINT64 compaction_mask, UINT64 mask ); +extern void copy_xstate( XSAVE_AREA_HEADER *dst, XSAVE_AREA_HEADER *src, UINT64 mask ); static inline UINT64 xstate_extended_features(void) { diff --git a/include/winnt.h b/include/winnt.h index 0e1a197d423..0f7c9430bfd 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -1470,6 +1470,14 @@ typedef struct _XSTATE_CONFIGURATION ULONG64 EnabledUserVisibleSupervisorFeatures; } XSTATE_CONFIGURATION, *PXSTATE_CONFIGURATION; +typedef struct _XSAVE_AREA_HEADER +{ + DWORD64 Mask; + DWORD64 CompactionMask; + DWORD64 Reserved2[6]; +} +XSAVE_AREA_HEADER, *PXSAVE_AREA_HEADER; + typedef struct _YMMCONTEXT { M128A Ymm0; From 498040f6216e19e7b5afbdd4e1942903fbc9fe2a Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 31 Jan 2024 16:26:16 -0600 Subject: [PATCH 249/413] ntdll: Don't hardcode xstate size in exception stack layout. (cherry picked from commit dfe2b717a622fd551053d880d2185281fb237a65) CW-Bug-Id: #23244 --- dlls/ntdll/unix/signal_i386.c | 63 +++++++++++++++------------------ dlls/ntdll/unix/signal_x86_64.c | 47 ++++++++++++------------ 2 files changed, 53 insertions(+), 57 deletions(-) diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index 4f192e1df63..b2bdb3a7e20 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -151,7 +151,7 @@ typedef struct ucontext #define FPU_sig(context) ((FLOATING_SAVE_AREA*)((context)->uc_mcontext.fpregs)) #define FPUX_sig(context) (FPU_sig(context) && !((context)->uc_mcontext.fpregs->status >> 16) ? (XSAVE_FORMAT *)(FPU_sig(context) + 1) : NULL) -#define XState_sig(fpu) (((unsigned int *)fpu->Reserved4)[12] == FP_XSTATE_MAGIC1 ? (XSTATE *)(fpu + 1) : NULL) +#define XState_sig(fpu) (((unsigned int *)fpu->Reserved4)[12] == FP_XSTATE_MAGIC1 ? (XSAVE_AREA_HEADER *)(fpu + 1) : NULL) #ifdef __ANDROID__ /* custom signal restorer since we may have unmapped the one in vdso, and bionic doesn't check for that */ @@ -443,12 +443,10 @@ struct exc_stack_layout EXCEPTION_RECORD rec; /* 008 */ CONTEXT context; /* 058 */ CONTEXT_EX context_ex; /* 324 */ - BYTE xstate[sizeof(XSTATE)+64]; /* 33c extra space to allow for 64-byte alignment */ - DWORD align; /* 4bc */ + DWORD align; /* 33c */ }; C_ASSERT( offsetof(struct exc_stack_layout, context) == 0x58 ); -C_ASSERT( offsetof(struct exc_stack_layout, xstate) == 0x33c ); -C_ASSERT( sizeof(struct exc_stack_layout) == 0x4c0 ); +C_ASSERT( sizeof(struct exc_stack_layout) == 0x340 ); /* stack layout when calling KiUserApcDispatcher */ struct apc_stack_layout @@ -612,12 +610,12 @@ struct xcontext ULONG64 host_compaction_mask; }; -static inline XSTATE *xstate_from_context( const CONTEXT *context ) +static inline XSAVE_AREA_HEADER *xstate_from_context( const CONTEXT *context ) { CONTEXT_EX *xctx = (CONTEXT_EX *)(context + 1); if ((context->ContextFlags & CONTEXT_XSTATE) != CONTEXT_XSTATE) return NULL; - return (XSTATE *)((char *)xctx + xctx->XState.Offset); + return (XSAVE_AREA_HEADER *)((char *)xctx + xctx->XState.Offset); } static inline void context_init_xstate( CONTEXT *context, void *xstate_buffer ) @@ -630,7 +628,7 @@ static inline void context_init_xstate( CONTEXT *context, void *xstate_buffer ) if (xstate_buffer) { - xctx->XState.Length = sizeof(XSTATE); + xctx->XState.Length = sizeof(XSAVE_AREA_HEADER) + xstate_features_size; xctx->XState.Offset = (BYTE *)xstate_buffer - (BYTE *)xctx; context->ContextFlags |= CONTEXT_XSTATE; @@ -828,7 +826,7 @@ static inline void save_context( struct xcontext *xcontext, const ucontext_t *si } if (fpux) { - XSTATE *xs; + XSAVE_AREA_HEADER *xs; context->ContextFlags |= CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS; memcpy( context->ExtendedRegisters, fpux, sizeof(*fpux) ); @@ -880,16 +878,12 @@ static inline void restore_context( const struct xcontext *xcontext, ucontext_t if (fpu) *fpu = context->FloatSave; if (fpux) { - XSTATE *src_xs, *dst_xs; + XSAVE_AREA_HEADER *xs; memcpy( fpux, context->ExtendedRegisters, sizeof(*fpux) ); - if ((dst_xs = XState_sig(fpux)) && (src_xs = xstate_from_context( context ))) - { - memcpy( &dst_xs->YmmContext, &src_xs->YmmContext, sizeof(dst_xs->YmmContext) ); - dst_xs->Mask |= src_xs->Mask; - dst_xs->CompactionMask = xcontext->host_compaction_mask; - } + if (xstate_extended_features() && (xs = XState_sig(fpux))) + xs->CompactionMask = xcontext->host_compaction_mask; } if (!fpu && !fpux) restore_fpu( context ); } @@ -1475,8 +1469,10 @@ static void setup_raise_exception( ucontext_t *sigcontext, void *stack_ptr, EXCEPTION_RECORD *rec, struct xcontext *xcontext ) { CONTEXT *context = &xcontext->c; - XSTATE *src_xs; + XSAVE_AREA_HEADER *src_xs; struct exc_stack_layout *stack; + size_t stack_size; + unsigned int xstate_size; NTSTATUS status = send_debug_event( rec, context, TRUE ); if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED) @@ -1488,7 +1484,9 @@ static void setup_raise_exception( ucontext_t *sigcontext, void *stack_ptr, /* fix up instruction pointer in context for EXCEPTION_BREAKPOINT */ if (rec->ExceptionCode == EXCEPTION_BREAKPOINT) context->Eip--; - stack = virtual_setup_exception( stack_ptr, sizeof(*stack), rec ); + xstate_size = sizeof(XSAVE_AREA_HEADER) + xstate_features_size; + stack_size = (ULONG_PTR)stack_ptr - (((ULONG_PTR)stack_ptr - sizeof(*stack) - xstate_size) & ~(ULONG_PTR)63); + stack = virtual_setup_exception( stack_ptr, stack_size, rec ); stack->rec_ptr = &stack->rec; stack->context_ptr = &stack->context; stack->rec = *rec; @@ -1496,16 +1494,13 @@ static void setup_raise_exception( ucontext_t *sigcontext, void *stack_ptr, if ((src_xs = xstate_from_context( context ))) { - XSTATE *dst_xs = (XSTATE *)(((ULONG_PTR)stack->xstate + 63) & ~63); + XSAVE_AREA_HEADER *dst_xs = (XSAVE_AREA_HEADER *)(stack + 1); + assert( !((ULONG_PTR)dst_xs & 63) ); context_init_xstate( &stack->context, dst_xs ); - memset( dst_xs, 0, sizeof(XSAVE_AREA_HEADER) ); + memset( dst_xs, 0, sizeof(*dst_xs) ); dst_xs->CompactionMask = xstate_compaction_enabled ? 0x8000000000000000 | xstate_extended_features() : 0; - if (src_xs->Mask & 4) - { - dst_xs->Mask = 4; - memcpy( &dst_xs->YmmContext, &src_xs->YmmContext, sizeof(dst_xs->YmmContext) ); - } + copy_xstate( dst_xs, src_xs, src_xs->Mask ); } else { @@ -1586,11 +1581,14 @@ NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context { struct syscall_frame *frame = x86_thread_data()->syscall_frame; ULONG esp = (frame->esp - sizeof(struct exc_stack_layout)) & ~3; - struct exc_stack_layout *stack = (struct exc_stack_layout *)esp; - XSTATE *src_xs; + struct exc_stack_layout *stack; + XSAVE_AREA_HEADER *src_xs; + unsigned int xstate_size; if (rec->ExceptionCode == EXCEPTION_BREAKPOINT) context->Eip--; + xstate_size = sizeof(XSAVE_AREA_HEADER) + xstate_features_size; + stack = (struct exc_stack_layout *)((esp - sizeof(*stack) - xstate_size) & ~(ULONG_PTR)63); stack->rec_ptr = &stack->rec; stack->context_ptr = &stack->context; stack->rec = *rec; @@ -1598,16 +1596,13 @@ NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context if ((src_xs = xstate_from_context( context ))) { - XSTATE *dst_xs = (XSTATE *)(((ULONG_PTR)stack->xstate + 63) & ~63); + XSAVE_AREA_HEADER *dst_xs = (XSAVE_AREA_HEADER *)(stack + 1); context_init_xstate( &stack->context, dst_xs ); - memset( dst_xs, 0, offsetof(XSTATE, YmmContext) ); + assert( !((ULONG_PTR)dst_xs & 63) ); + memset( dst_xs, 0, sizeof(*dst_xs) ); dst_xs->CompactionMask = xstate_compaction_enabled ? 0x8000000000000000 | xstate_extended_features() : 0; - if (src_xs->Mask & 4) - { - dst_xs->Mask = 4; - memcpy( &dst_xs->YmmContext, &src_xs->YmmContext, sizeof(dst_xs->YmmContext) ); - } + copy_xstate( dst_xs, src_xs, src_xs->Mask ); } else { diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index fb97bf4d598..8c77bcbd970 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -151,7 +151,7 @@ __ASM_GLOBAL_FUNC( alloc_fs_sel, #define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO]) #define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR]) #define FPU_sig(context) ((XMM_SAVE_AREA32 *)((context)->uc_mcontext.fpregs)) -#define XState_sig(fpu) (((unsigned int *)fpu->Reserved4)[12] == FP_XSTATE_MAGIC1 ? (XSTATE *)(fpu + 1) : NULL) +#define XState_sig(fpu) (((unsigned int *)fpu->Reserved4)[12] == FP_XSTATE_MAGIC1 ? (XSAVE_AREA_HEADER *)(fpu + 1) : NULL) #elif defined(__FreeBSD__) || defined (__FreeBSD_kernel__) @@ -370,11 +370,10 @@ struct exc_stack_layout ULONG64 align; /* 588 */ struct machine_frame machine_frame; /* 590 */ ULONG64 align2; /* 5b8 */ - XSTATE xstate; /* 5c0 */ }; C_ASSERT( offsetof(struct exc_stack_layout, rec) == 0x4f0 ); C_ASSERT( offsetof(struct exc_stack_layout, machine_frame) == 0x590 ); -C_ASSERT( sizeof(struct exc_stack_layout) == 0x700 ); +C_ASSERT( sizeof(struct exc_stack_layout) == 0x5c0 ); /* stack layout when calling KiUserApcDispatcher */ struct apc_stack_layout @@ -494,12 +493,12 @@ struct xcontext ULONG64 host_compaction_mask; }; -static inline XSTATE *xstate_from_context( const CONTEXT *context ) +static inline XSAVE_AREA_HEADER *xstate_from_context( const CONTEXT *context ) { CONTEXT_EX *xctx = (CONTEXT_EX *)(context + 1); if ((context->ContextFlags & CONTEXT_XSTATE) != CONTEXT_XSTATE) return NULL; - return (XSTATE *)((char *)xctx + xctx->XState.Offset); + return (XSAVE_AREA_HEADER *)((char *)xctx + xctx->XState.Offset); } static inline void context_init_xstate( CONTEXT *context, void *xstate_buffer ) @@ -512,7 +511,7 @@ static inline void context_init_xstate( CONTEXT *context, void *xstate_buffer ) if (xstate_buffer) { - xctx->XState.Length = sizeof(XSTATE); + xctx->XState.Length = sizeof(XSAVE_AREA_HEADER) + xstate_features_size; xctx->XState.Offset = (BYTE *)xstate_buffer - (BYTE *)xctx; context->ContextFlags |= CONTEXT_XSTATE; @@ -909,7 +908,7 @@ static void save_context( struct xcontext *xcontext, const ucontext_t *sigcontex context->Dr7 = amd64_thread_data()->dr7; if (FPU_sig(sigcontext)) { - XSTATE *xs; + XSAVE_AREA_HEADER *xs; context->ContextFlags |= CONTEXT_FLOATING_POINT; context->FltSave = *FPU_sig(sigcontext); @@ -934,7 +933,7 @@ static void save_context( struct xcontext *xcontext, const ucontext_t *sigcontex static void restore_context( const struct xcontext *xcontext, ucontext_t *sigcontext ) { const CONTEXT *context = &xcontext->c; - XSTATE *xs; + XSAVE_AREA_HEADER *xs; amd64_thread_data()->dr0 = context->Dr0; amd64_thread_data()->dr1 = context->Dr1; @@ -1427,7 +1426,8 @@ static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec struct exc_stack_layout *stack; size_t stack_size; NTSTATUS status; - XSTATE *src_xs; + XSAVE_AREA_HEADER *src_xs; + unsigned int xstate_size; if (rec->ExceptionCode == EXCEPTION_SINGLE_STEP) { @@ -1456,7 +1456,8 @@ static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec /* fix up instruction pointer in context for EXCEPTION_BREAKPOINT */ if (rec->ExceptionCode == EXCEPTION_BREAKPOINT) context->Rip--; - stack_size = (ULONG_PTR)stack_ptr - (((ULONG_PTR)stack_ptr - sizeof(*stack)) & ~(ULONG_PTR)63); + xstate_size = sizeof(XSAVE_AREA_HEADER) + xstate_features_size; + stack_size = (ULONG_PTR)stack_ptr - (((ULONG_PTR)stack_ptr - sizeof(*stack) - xstate_size) & ~(ULONG_PTR)63); stack = virtual_setup_exception( stack_ptr, stack_size, rec ); stack->rec = *rec; stack->context = *context; @@ -1465,15 +1466,12 @@ static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec if ((src_xs = xstate_from_context( context ))) { - assert( !((ULONG_PTR)&stack->xstate & 63) ); - context_init_xstate( &stack->context, &stack->xstate ); - memset( &stack->xstate, 0, offsetof(XSTATE, YmmContext) ); - stack->xstate.CompactionMask = xstate_compaction_enabled ? 0x8000000000000000 | xstate_extended_features() : 0; - if (src_xs->Mask & 4) - { - stack->xstate.Mask = 4; - memcpy( &stack->xstate.YmmContext, &src_xs->YmmContext, sizeof(stack->xstate.YmmContext) ); - } + XSAVE_AREA_HEADER *dst_xs = (XSAVE_AREA_HEADER *)(stack + 1); + assert( !((ULONG_PTR)dst_xs & 63) ); + context_init_xstate( &stack->context, dst_xs ); + memset( dst_xs, 0, sizeof(*dst_xs) ); + dst_xs->CompactionMask = xstate_compaction_enabled ? 0x8000000000000000 | xstate_extended_features() : 0; + copy_xstate( dst_xs, src_xs, src_xs->Mask ); } else { @@ -1560,16 +1558,19 @@ NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context struct syscall_frame *frame = amd64_thread_data()->syscall_frame; struct exc_stack_layout *stack; NTSTATUS status = NtSetContextThread( GetCurrentThread(), context ); + unsigned int xstate_size; if (status) return status; - stack = (struct exc_stack_layout *)((context->Rsp - sizeof(*stack)) & ~(ULONG_PTR)63); + xstate_size = sizeof(XSAVE_AREA_HEADER) + xstate_features_size; + stack = (struct exc_stack_layout *)((context->Rsp - sizeof(*stack) - xstate_size) & ~(ULONG_PTR)63); memmove( &stack->context, context, sizeof(*context) ); if ((context->ContextFlags & CONTEXT_XSTATE) == CONTEXT_XSTATE) { - assert( !((ULONG_PTR)&stack->xstate & 63) ); - context_init_xstate( &stack->context, &stack->xstate ); - memcpy( &stack->xstate, &frame->xstate, sizeof(XSAVE_AREA_HEADER) + xstate_features_size ); + XSAVE_AREA_HEADER *dst_xs = (XSAVE_AREA_HEADER *)(stack + 1); + assert( !((ULONG_PTR)dst_xs & 63) ); + context_init_xstate( &stack->context, dst_xs ); + memcpy( dst_xs, &frame->xstate, sizeof(XSAVE_AREA_HEADER) + xstate_features_size ); } else context_init_xstate( &stack->context, NULL ); From d9b3809c32d622dc85f17c4af9a18e32f5e2d355 Mon Sep 17 00:00:00 2001 From: Brendan Shanks Date: Tue, 20 Feb 2024 21:49:36 -0800 Subject: [PATCH 250/413] ntdll: On x86_64, don't access %gs in signal_start_thread(). %gs is not set until call_init_thunk(). Also, adjust other TEB accesses to be consistent with the surrounding functions. Fixes a crash on macOS introduced by 7ae488a2bb58501684c6475d4942277b852475fc ("ntdll: Don't hardcode xstate size in syscall frame.") (cherry picked from commit e628eb2c6ff84ff347d78b0c7a601665a51dbcef) CW-Bug-Id: #23244 --- dlls/ntdll/unix/signal_i386.c | 4 ++-- dlls/ntdll/unix/signal_x86_64.c | 21 +++------------------ 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index b2bdb3a7e20..bb97279fc43 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -1635,7 +1635,7 @@ __ASM_GLOBAL_FUNC( call_user_mode_callback, "movl 0x18(%ebp),%edx\n\t" /* teb */ "pushl 0(%edx)\n\t" /* teb->Tib.ExceptionList */ "subl $0x280,%esp\n\t" /* sizeof(struct syscall_frame) */ - "subl %fs:0x204,%esp\n\t" /* x86_thread_data()->xstate_features_size */ + "subl 0x204(%edx),%esp\n\t" /* x86_thread_data()->xstate_features_size */ "andl $~63,%esp\n\t" "leal 8(%ebp),%eax\n\t" "movl %eax,0x38(%esp)\n\t" /* frame->syscall_cfa */ @@ -2596,7 +2596,7 @@ __ASM_GLOBAL_FUNC( signal_start_thread, "orl %eax,%eax\n\t" "jnz 1f\n\t" "leal -0x280(%esp),%eax\n\t" /* sizeof(struct syscall_frame) */ - "subl %fs:0x204,%eax\n\t" /* x86_thread_data()->xstate_features_size */ + "subl 0x204(%ecx),%eax\n\t" /* x86_thread_data()->xstate_features_size */ "andl $~63,%eax\n\t" "movl %eax,0x1f8(%ecx)\n" /* x86_thread_data()->syscall_frame */ /* switch to kernel stack */ diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 8c77bcbd970..06a3eb6d5c7 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1613,12 +1613,7 @@ __ASM_GLOBAL_FUNC( call_user_mode_callback, "movq %rsi,-0x38(%rbp)\n\t" /* ret_ptr */ "movq %rdx,-0x40(%rbp)\n\t" /* ret_len */ "subq $0x308,%rsp\n\t" /* sizeof(struct syscall_frame) + exception */ -#ifdef __APPLE__ - "movq %gs:0x30,%rsi\n\t" - "movl 0x33c(%rsi),%esi\n\t" -#else - "movl %gs:0x33c,%esi\n\t" /* amd64_thread_data()->xstate_features_size */ -#endif + "movl 0x33c(%r8),%esi\n\t" /* amd64_thread_data()->xstate_features_size */ "subq %rsi,%rsp\n\t" "andq $~63,%rsp\n\t" "leaq 0x10(%rbp),%rax\n\t" @@ -1658,12 +1653,7 @@ __ASM_GLOBAL_FUNC( user_mode_callback_return, __ASM_CFI(".cfi_rel_offset %r13,-0x18\n\t") __ASM_CFI(".cfi_rel_offset %r14,-0x20\n\t") __ASM_CFI(".cfi_rel_offset %r15,-0x28\n\t") -#ifdef __APPLE__ - "movq %gs:0x30,%rax\n\t" - "movl 0x33c(%rax),%eax\n\t" -#else - "movl %gs:0x33c,%eax\n\t" /* amd64_thread_data()->xstate_features_size */ -#endif + "movl 0x33c(%rcx),%eax\n\t" /* amd64_thread_data()->xstate_features_size */ "movq 0x300(%r10,%rax),%rax\n\t" /* exception list */ "movq %rax,0(%rcx)\n\t" /* teb->Tib.ExceptionList */ "movq -0x38(%rbp),%r10\n\t" /* ret_ptr */ @@ -2965,12 +2955,7 @@ __ASM_GLOBAL_FUNC( signal_start_thread, "orq %r8,%r8\n\t" "jnz 1f\n\t" "leaq -0x300(%rsp),%r8\n\t" /* sizeof(struct syscall_frame) */ -#ifdef __APPLE__ - "movq %gs:0x30,%rax\n\t" - "movl 0x33c(%rax),%eax\n\t" -#else - "movl %gs:0x33c,%eax\n\t" /* amd64_thread_data()->xstate_features_size */ -#endif + "movl 0x33c(%rcx),%eax\n\t" /* amd64_thread_data()->xstate_features_size */ "subq %rax,%r8\n\t" "andq $~63,%r8\n\t" "movq %r8,0x328(%rcx)\n" /* amd64_thread_data()->syscall_frame */ From f71659d8b3851ec9b72388305c9a19c80549ffb3 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 1 Feb 2024 11:47:42 -0600 Subject: [PATCH 251/413] ntdll: Mind context compaction mask in context_from_server(). (cherry picked from commit ba1e2d5dda84f64bd784ae1797ad2825d8b8f096) CW-Bug-Id: #23244 --- dlls/ntdll/unix/signal_i386.c | 17 ++--------------- dlls/ntdll/unix/signal_x86_64.c | 5 ----- dlls/ntdll/unix/thread.c | 8 ++++---- 3 files changed, 6 insertions(+), 24 deletions(-) diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index bb97279fc43..42eb89af586 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -607,7 +607,6 @@ struct xcontext { CONTEXT c; CONTEXT_EX c_ex; - ULONG64 host_compaction_mask; }; static inline XSAVE_AREA_HEADER *xstate_from_context( const CONTEXT *context ) @@ -831,11 +830,7 @@ static inline void save_context( struct xcontext *xcontext, const ucontext_t *si context->ContextFlags |= CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS; memcpy( context->ExtendedRegisters, fpux, sizeof(*fpux) ); if (!fpu) fpux_to_fpu( &context->FloatSave, fpux ); - if (xstate_extended_features() && (xs = XState_sig(fpux))) - { - context_init_xstate( context, xs ); - xcontext->host_compaction_mask = xs->CompactionMask; - } + if (xstate_extended_features() && (xs = XState_sig(fpux))) context_init_xstate( context, xs ); } if (!fpu && !fpux) save_fpu( context ); } @@ -876,15 +871,7 @@ static inline void restore_context( const struct xcontext *xcontext, ucontext_t SS_sig(sigcontext) = context->SegSs; if (fpu) *fpu = context->FloatSave; - if (fpux) - { - XSAVE_AREA_HEADER *xs; - - memcpy( fpux, context->ExtendedRegisters, sizeof(*fpux) ); - - if (xstate_extended_features() && (xs = XState_sig(fpux))) - xs->CompactionMask = xcontext->host_compaction_mask; - } + if (fpux) memcpy( fpux, context->ExtendedRegisters, sizeof(*fpux) ); if (!fpu && !fpux) restore_fpu( context ); } diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 06a3eb6d5c7..2143d42b8c6 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -490,7 +490,6 @@ struct xcontext { CONTEXT c; CONTEXT_EX c_ex; - ULONG64 host_compaction_mask; }; static inline XSAVE_AREA_HEADER *xstate_from_context( const CONTEXT *context ) @@ -919,7 +918,6 @@ static void save_context( struct xcontext *xcontext, const ucontext_t *sigcontex * just reference sigcontext without overflowing 32 bit XState.Offset */ context_init_xstate( context, xs ); assert( xcontext->c_ex.XState.Offset == (BYTE *)xs - (BYTE *)&xcontext->c_ex ); - xcontext->host_compaction_mask = xs->CompactionMask; } } } @@ -933,7 +931,6 @@ static void save_context( struct xcontext *xcontext, const ucontext_t *sigcontex static void restore_context( const struct xcontext *xcontext, ucontext_t *sigcontext ) { const CONTEXT *context = &xcontext->c; - XSAVE_AREA_HEADER *xs; amd64_thread_data()->dr0 = context->Dr0; amd64_thread_data()->dr1 = context->Dr1; @@ -943,8 +940,6 @@ static void restore_context( const struct xcontext *xcontext, ucontext_t *sigcon amd64_thread_data()->dr7 = context->Dr7; set_sigcontext( context, sigcontext ); if (FPU_sig(sigcontext)) *FPU_sig(sigcontext) = context->FltSave; - if (xstate_extended_features() && (xs = XState_sig(FPU_sig(sigcontext)))) - xs->CompactionMask = xcontext->host_compaction_mask; leave_handler( sigcontext ); } diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 73f252853fe..244305c9df6 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -688,7 +688,7 @@ static NTSTATUS context_from_server( void *dst, const context_t *from, USHORT ma XSTATE *xs = (XSTATE *)((char *)xctx + xctx->XState.Offset); xs->Mask &= ~4; - if (user_shared_data->XState.CompactionEnabled) xs->CompactionMask = 0x8000000000000004; + if (xs->CompactionMask) xs->CompactionMask = 0x8000000000000004; for (i = 0; i < ARRAY_SIZE( from->ymm.regs.ymm_high); i++) { if (!from->ymm.regs.ymm_high[i].low && !from->ymm.regs.ymm_high[i].high) continue; @@ -762,7 +762,7 @@ static NTSTATUS context_from_server( void *dst, const context_t *from, USHORT ma XSTATE *xs = (XSTATE *)((char *)xctx + xctx->XState.Offset); xs->Mask &= ~4; - if (user_shared_data->XState.CompactionEnabled) xs->CompactionMask = 0x8000000000000004; + if (xs->CompactionMask) xs->CompactionMask = 0x8000000000000004; for (i = 0; i < ARRAY_SIZE( from->ymm.regs.ymm_high); i++) { if (!from->ymm.regs.ymm_high[i].low && !from->ymm.regs.ymm_high[i].high) continue; @@ -837,7 +837,7 @@ static NTSTATUS context_from_server( void *dst, const context_t *from, USHORT ma XSTATE *xs = (XSTATE *)((char *)xctx + xctx->XState.Offset); xs->Mask &= ~4; - if (user_shared_data->XState.CompactionEnabled) xs->CompactionMask = 0x8000000000000004; + if (xs->CompactionMask) xs->CompactionMask = 0x8000000000000004; for (i = 0; i < ARRAY_SIZE( from->ymm.regs.ymm_high); i++) { if (!from->ymm.regs.ymm_high[i].low && !from->ymm.regs.ymm_high[i].high) continue; @@ -919,7 +919,7 @@ static NTSTATUS context_from_server( void *dst, const context_t *from, USHORT ma XSTATE *xs = (XSTATE *)((char *)xctx + xctx->XState.Offset); xs->Mask &= ~4; - if (user_shared_data->XState.CompactionEnabled) xs->CompactionMask = 0x8000000000000004; + if (xs->CompactionMask) xs->CompactionMask = 0x8000000000000004; for (i = 0; i < ARRAY_SIZE( from->ymm.regs.ymm_high); i++) { if (!from->ymm.regs.ymm_high[i].low && !from->ymm.regs.ymm_high[i].high) continue; From 32dc479a25ae1d51f89c0c03adadbd09ff580f84 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 1 Feb 2024 12:17:14 -0600 Subject: [PATCH 252/413] ntdll: Don't copy xstate from / to syscall frame in usr1_handler(). (cherry picked from commit efd3d31082645fab83c6a4994705654edafa9163) CW-Bug-Id: #23244 --- dlls/ntdll/unix/signal_i386.c | 44 ++++++++++++++++++++++++--------- dlls/ntdll/unix/signal_x86_64.c | 34 +++++++++++++++++++------ 2 files changed, 60 insertions(+), 18 deletions(-) diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index 42eb89af586..35d9b1df2bb 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -2138,24 +2138,46 @@ static void quit_handler( int signal, siginfo_t *siginfo, void *sigcontext ) */ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) { - struct xcontext xcontext; + ucontext_t *ucontext = sigcontext; init_handler( sigcontext ); - if (is_inside_syscall( sigcontext )) + + if (is_inside_syscall( ucontext )) { - DECLSPEC_ALIGN(64) XSTATE xs; - xcontext.c.ContextFlags = CONTEXT_FULL; - context_init_xstate( &xcontext.c, &xs ); + struct syscall_frame *frame = x86_thread_data()->syscall_frame; + ULONG64 saved_compaction = 0; + struct xcontext *context; - NtGetContextThread( GetCurrentThread(), &xcontext.c ); - wait_suspend( &xcontext.c ); - NtSetContextThread( GetCurrentThread(), &xcontext.c ); + context = (struct xcontext *)(((ULONG_PTR)ESP_sig(ucontext) - sizeof(*context)) & ~15); + if ((char *)context < (char *)ntdll_get_thread_data()->kernel_stack) + { + ERR_(seh)( "kernel stack overflow.\n" ); + return; + } + context->c.ContextFlags = CONTEXT_FULL; + NtGetContextThread( GetCurrentThread(), &context->c ); + if (xstate_extended_features()) + { + context_init_xstate( &context->c, &frame->xstate ); + saved_compaction = frame->xstate.CompactionMask; + } + wait_suspend( &context->c ); + if (xstate_extended_features()) frame->xstate.CompactionMask = saved_compaction; + if (context->c.ContextFlags & 0x40) + { + /* xstate is updated directly in frame's xstate */ + context->c.ContextFlags &= ~0x40; + frame->restore_flags |= 0x40; + } + NtSetContextThread( GetCurrentThread(), &context->c ); } else { - save_context( &xcontext, sigcontext ); - wait_suspend( &xcontext.c ); - restore_context( &xcontext, sigcontext ); + struct xcontext context; + + save_context( &context, ucontext ); + wait_suspend( &context.c ); + restore_context( &context, ucontext ); } } diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 2143d42b8c6..2b4483644a5 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -2493,20 +2493,40 @@ static void quit_handler( int signal, siginfo_t *siginfo, void *sigcontext ) static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) { ucontext_t *ucontext = init_handler( sigcontext ); - struct xcontext context; if (is_inside_syscall( ucontext )) { - DECLSPEC_ALIGN(64) XSTATE xs; - context.c.ContextFlags = CONTEXT_FULL | CONTEXT_SEGMENTS; - context_init_xstate( &context.c, &xs ); + struct syscall_frame *frame = amd64_thread_data()->syscall_frame; + ULONG64 saved_compaction = 0; + struct xcontext *context; - NtGetContextThread( GetCurrentThread(), &context.c ); - wait_suspend( &context.c ); - NtSetContextThread( GetCurrentThread(), &context.c ); + context = (struct xcontext *)(((ULONG_PTR)RSP_sig(ucontext) - sizeof(*context)) & ~15); + if ((char *)context < (char *)ntdll_get_thread_data()->kernel_stack) + { + ERR_(seh)( "kernel stack overflow.\n" ); + return; + } + context->c.ContextFlags = CONTEXT_FULL | CONTEXT_SEGMENTS; + NtGetContextThread( GetCurrentThread(), &context->c ); + if (xstate_extended_features()) + { + context_init_xstate( &context->c, &frame->xstate ); + saved_compaction = frame->xstate.CompactionMask; + } + wait_suspend( &context->c ); + if (xstate_extended_features()) frame->xstate.CompactionMask = saved_compaction; + if (context->c.ContextFlags & 0x40) + { + /* xstate is updated directly in frame's xstate */ + context->c.ContextFlags &= ~0x40; + frame->restore_flags |= 0x40; + } + NtSetContextThread( GetCurrentThread(), &context->c ); } else { + struct xcontext context; + save_context( &context, ucontext ); wait_suspend( &context.c ); restore_context( &context, ucontext ); From 90c7f90bddc779baa57df572aa5f55eabea9f53f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 18 Jan 2024 11:51:36 -0600 Subject: [PATCH 253/413] ntdll: Support generic xstate config in context manipulation functions. (cherry picked from commit beec1b57fbaf9034a05051ea2ceeba0ffdbf0857) CW-Bug-Id: #23244 --- dlls/ntdll/exception.c | 121 ++++++++++++++++++++++++++++++++++------- 1 file changed, 102 insertions(+), 19 deletions(-) diff --git a/dlls/ntdll/exception.c b/dlls/ntdll/exception.c index 7a230237f0c..d4b5f901e10 100644 --- a/dlls/ntdll/exception.c +++ b/dlls/ntdll/exception.c @@ -797,6 +797,52 @@ static const struct context_parameters *context_get_parameters( ULONG context_fl return NULL; } +/* offset is from the start of XSAVE_AREA_HEADER. */ +static int next_compacted_xstate_offset( int off, UINT64 compaction_mask, int feature_idx ) +{ + const UINT64 feature_mask = (UINT64)1 << feature_idx; + + if (compaction_mask & feature_mask) off += user_shared_data->XState.Features[feature_idx].Size; + if (user_shared_data->XState.AlignedFeatures & (feature_mask << 1)) + off = (off + 63) & ~63; + return off; +} + +/* size includes XSAVE_AREA_HEADER but not XSAVE_FORMAT (legacy save area). */ +static int xstate_get_compacted_size( UINT64 mask ) +{ + UINT64 compaction_mask; + unsigned int i; + int off; + + compaction_mask = ((UINT64)1 << 63) | mask; + mask >>= 2; + off = sizeof(XSAVE_AREA_HEADER); + i = 2; + while (mask) + { + if (mask == 1) return off + user_shared_data->XState.Features[i].Size; + off = next_compacted_xstate_offset( off, compaction_mask, i ); + mask >>= 1; + ++i; + } + return off; +} + +static int xstate_get_size( UINT64 mask ) +{ + unsigned int i; + + mask >>= 2; + if (!mask) return sizeof(XSAVE_AREA_HEADER); + i = 2; + while (mask != 1) + { + mask >>= 1; + ++i; + } + return user_shared_data->XState.Features[i].Offset + user_shared_data->XState.Features[i].Size - sizeof(XSAVE_FORMAT); +} /********************************************************************** * RtlGetExtendedContextLength2 (NTDLL.@) @@ -822,12 +868,12 @@ NTSTATUS WINAPI RtlGetExtendedContextLength2( ULONG context_flags, ULONG *length if (!(supported_mask = RtlGetEnabledExtendedFeatures( ~(ULONG64)0) )) return STATUS_NOT_SUPPORTED; - compaction_mask &= supported_mask; - - size = p->context_size + p->context_ex_size + offsetof(XSTATE, YmmContext) + 63; + size = p->context_size + p->context_ex_size + 63; - if (compaction_mask & supported_mask & (1 << XSTATE_AVX)) - size += sizeof(YMMCONTEXT); + compaction_mask &= supported_mask & ~(ULONG64)3; + if (user_shared_data->XState.CompactionEnabled) size += xstate_get_compacted_size( compaction_mask ); + else if (compaction_mask) size += xstate_get_size( compaction_mask ); + else size += sizeof(XSAVE_AREA_HEADER); *length = size; return STATUS_SUCCESS; @@ -878,11 +924,11 @@ NTSTATUS WINAPI RtlInitializeExtendedContext2( void *context, ULONG context_flag xs = (XSTATE *)(((ULONG_PTR)c_ex + p->context_ex_size + 63) & ~(ULONG_PTR)63); c_ex->XState.Offset = (ULONG_PTR)xs - (ULONG_PTR)c_ex; - c_ex->XState.Length = offsetof(XSTATE, YmmContext); compaction_mask &= supported_mask; - if (compaction_mask & (1 << XSTATE_AVX)) - c_ex->XState.Length += sizeof(YMMCONTEXT); + if (user_shared_data->XState.CompactionEnabled) c_ex->XState.Length = xstate_get_compacted_size( compaction_mask ); + else if (compaction_mask & ~(ULONG64)3) c_ex->XState.Length = xstate_get_size( compaction_mask ); + else c_ex->XState.Length = sizeof(XSAVE_AREA_HEADER); memset( xs, 0, c_ex->XState.Length ); if (user_shared_data->XState.CompactionEnabled) @@ -916,6 +962,10 @@ NTSTATUS WINAPI RtlInitializeExtendedContext( void *context, ULONG context_flags void * WINAPI RtlLocateExtendedFeature2( CONTEXT_EX *context_ex, ULONG feature_id, XSTATE_CONFIGURATION *xstate_config, ULONG *length ) { + UINT64 feature_mask = (ULONG64)1 << feature_id; + XSAVE_AREA_HEADER *xs; + unsigned int offset, i; + TRACE( "context_ex %p, feature_id %lu, xstate_config %p, length %p.\n", context_ex, feature_id, xstate_config, length ); @@ -931,16 +981,31 @@ void * WINAPI RtlLocateExtendedFeature2( CONTEXT_EX *context_ex, ULONG feature_i return NULL; } - if (feature_id != XSTATE_AVX) + if (feature_id < 2 || feature_id >= 64) return NULL; + xs = (XSAVE_AREA_HEADER *)((BYTE *)context_ex + context_ex->XState.Offset); + if (length) - *length = sizeof(YMMCONTEXT); + *length = xstate_config->Features[feature_id].Size; + + if (xstate_config->CompactionEnabled) + { + if (!(xs->CompactionMask & feature_mask)) return NULL; + offset = sizeof(XSAVE_AREA_HEADER); + for (i = 2; i < feature_id; ++i) + offset = next_compacted_xstate_offset( offset, xs->CompactionMask, i ); + } + else + { + if (!(feature_mask & xstate_config->EnabledFeatures)) return NULL; + offset = xstate_config->Features[feature_id].Offset - sizeof(XSAVE_FORMAT); + } - if (context_ex->XState.Length < sizeof(XSTATE)) + if (context_ex->XState.Length < offset + xstate_config->Features[feature_id].Size) return NULL; - return (BYTE *)context_ex + context_ex->XState.Offset + offsetof(XSTATE, YmmContext); + return (BYTE *)xs + offset; } @@ -1070,8 +1135,9 @@ NTSTATUS WINAPI RtlCopyContext( CONTEXT *dst, DWORD context_flags, CONTEXT *src NTSTATUS WINAPI RtlCopyExtendedContext( CONTEXT_EX *dst, ULONG context_flags, CONTEXT_EX *src ) { const struct context_parameters *p; - XSTATE *dst_xs, *src_xs; + XSAVE_AREA_HEADER *dst_xs, *src_xs; ULONG64 feature_mask; + unsigned int i, off, size; TRACE( "dst %p, context_flags %#lx, src %p.\n", dst, context_flags, src ); @@ -1086,18 +1152,35 @@ NTSTATUS WINAPI RtlCopyExtendedContext( CONTEXT_EX *dst, ULONG context_flags, CO if (!(context_flags & 0x40)) return STATUS_SUCCESS; - if (dst->XState.Length < offsetof(XSTATE, YmmContext)) + if (dst->XState.Length < sizeof(XSAVE_AREA_HEADER)) return STATUS_BUFFER_OVERFLOW; - dst_xs = (XSTATE *)((BYTE *)dst + dst->XState.Offset); - src_xs = (XSTATE *)((BYTE *)src + src->XState.Offset); + dst_xs = (XSAVE_AREA_HEADER *)((BYTE *)dst + dst->XState.Offset); + src_xs = (XSAVE_AREA_HEADER *)((BYTE *)src + src->XState.Offset); - memset(dst_xs, 0, offsetof(XSTATE, YmmContext)); + memset(dst_xs, 0, sizeof(XSAVE_AREA_HEADER)); dst_xs->Mask = (src_xs->Mask & ~(ULONG64)3) & feature_mask; dst_xs->CompactionMask = user_shared_data->XState.CompactionEnabled ? ((ULONG64)1 << 63) | (src_xs->CompactionMask & feature_mask) : 0; - if (dst_xs->Mask & 4 && src->XState.Length >= sizeof(XSTATE) && dst->XState.Length >= sizeof(XSTATE)) - memcpy( &dst_xs->YmmContext, &src_xs->YmmContext, sizeof(dst_xs->YmmContext) ); + + if (dst_xs->CompactionMask) feature_mask &= dst_xs->CompactionMask; + feature_mask = dst_xs->Mask >> 2; + + i = 2; + off = sizeof(XSAVE_AREA_HEADER); + while (1) + { + if (feature_mask & 1) + { + if (!dst_xs->CompactionMask) off = user_shared_data->XState.Features[i].Offset - sizeof(XSAVE_FORMAT); + size = user_shared_data->XState.Features[i].Size; + if (src->XState.Length < off + size || dst->XState.Length < off + size) break; + memcpy( (BYTE *)dst_xs + off, (BYTE *)src_xs + off, size ); + } + if (!(feature_mask >>= 1)) break; + if (dst_xs->CompactionMask) off = next_compacted_xstate_offset( off, dst_xs->CompactionMask, i); + ++i; + } return STATUS_SUCCESS; } From 750503499ff6e86e37d146d8f66cf2df0aa48afb Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 4 Mar 2024 19:54:54 -0600 Subject: [PATCH 254/413] ntdll: Respect red zone in usr1_handler() on x64. Fixes a regression introduced by efd3d31082645fab83c6a4994705654edafa9163. (cherry picked from commit 6f55a13169797dd073575c052a6418e3cecbcfad) CW-Bug-Id: #23244 --- dlls/ntdll/unix/signal_x86_64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 2b4483644a5..3932e64678f 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -2500,7 +2500,7 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) ULONG64 saved_compaction = 0; struct xcontext *context; - context = (struct xcontext *)(((ULONG_PTR)RSP_sig(ucontext) - sizeof(*context)) & ~15); + context = (struct xcontext *)(((ULONG_PTR)RSP_sig(ucontext) - 128 /* red zone */ - sizeof(*context)) & ~15); if ((char *)context < (char *)ntdll_get_thread_data()->kernel_stack) { ERR_(seh)( "kernel stack overflow.\n" ); From ba9eabc3aa5b8cd24513392f549cb9f76a62ccda Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 18 Mar 2024 13:00:30 -0600 Subject: [PATCH 255/413] ntdll: Only save AVX xstate in wine_syscall_dispatcher. (cherry picked from commit 7ae23ad7758418012c3d2d40a3acfed5af18afaa) CW-Bug-Id: #23244 --- dlls/ntdll/unix/signal_i386.c | 6 +++++- dlls/ntdll/unix/signal_x86_64.c | 9 +++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index 35d9b1df2bb..d781bd8b12c 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -1005,6 +1005,7 @@ NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context ) CONTEXT_EX *context_ex = (CONTEXT_EX *)(context + 1); XSAVE_AREA_HEADER *xs = (XSAVE_AREA_HEADER *)((char *)context_ex + context_ex->XState.Offset); + if (xstate_compaction_enabled) frame->xstate.CompactionMask |= xstate_extended_features(); copy_xstate( &frame->xstate, xs, xs->Mask ); } @@ -1131,6 +1132,7 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) context_ex->XState.Length > sizeof(XSAVE_AREA_HEADER) + xstate_features_size) return STATUS_INVALID_PARAMETER; + if (xstate_compaction_enabled) frame->xstate.CompactionMask |= xstate_extended_features(); mask = (xstate_compaction_enabled ? xstate->CompactionMask : xstate->Mask) & xstate_extended_features(); xstate->Mask = frame->xstate.Mask & mask; xstate->CompactionMask = xstate_compaction_enabled ? (0x8000000000000000 | mask) : 0; @@ -2158,6 +2160,7 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) NtGetContextThread( GetCurrentThread(), &context->c ); if (xstate_extended_features()) { + if (xstate_compaction_enabled) frame->xstate.CompactionMask |= xstate_extended_features(); context_init_xstate( &context->c, &frame->xstate ); saved_compaction = frame->xstate.CompactionMask; } @@ -2666,7 +2669,8 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, "testl $3,(%ecx)\n\t" /* frame->syscall_flags & (SYSCALL_HAVE_XSAVE | SYSCALL_HAVE_XSAVEC) */ "jz 2f\n\t" "movl %fs:0x1fc,%eax\n\t" /* x86_thread_data()->xstate_features_mask */ - "movl %fs:0x200,%edx\n\t" /* x86_thread_data()->xstate_features_mask high dword */ + "xorl %edx,%edx\n\t" + "andl $7,%eax\n\t" "xorl %edi,%edi\n\t" "movl %edi,0x240(%ecx)\n\t" "movl %edi,0x244(%ecx)\n\t" diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 3932e64678f..1f23d166263 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1063,6 +1063,7 @@ NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context ) CONTEXT_EX *context_ex = (CONTEXT_EX *)(context + 1); XSAVE_AREA_HEADER *xs = (XSAVE_AREA_HEADER *)((char *)context_ex + context_ex->XState.Offset); + if (xstate_compaction_enabled) frame->xstate.CompactionMask |= xstate_extended_features(); copy_xstate( &frame->xstate, xs, xs->Mask ); } @@ -1176,6 +1177,7 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) context_ex->XState.Length > sizeof(XSAVE_AREA_HEADER) + xstate_features_size) return STATUS_INVALID_PARAMETER; + if (xstate_compaction_enabled) frame->xstate.CompactionMask |= xstate_extended_features(); mask = (xstate_compaction_enabled ? xstate->CompactionMask : xstate->Mask) & xstate_extended_features(); xstate->Mask = frame->xstate.Mask & mask; xstate->CompactionMask = xstate_compaction_enabled ? (0x8000000000000000 | mask) : 0; @@ -1305,6 +1307,7 @@ NTSTATUS set_thread_wow64_context( HANDLE handle, const void *ctx, ULONG size ) CONTEXT_EX *context_ex = (CONTEXT_EX *)(context + 1); XSAVE_AREA_HEADER *xs = (XSAVE_AREA_HEADER *)((char *)context_ex + context_ex->XState.Offset); + if (xstate_compaction_enabled) frame->xstate.CompactionMask |= xstate_extended_features(); copy_xstate( &frame->xstate, xs, xs->Mask ); frame->restore_flags |= CONTEXT_XSTATE; } @@ -1396,6 +1399,7 @@ NTSTATUS get_thread_wow64_context( HANDLE handle, void *ctx, ULONG size ) context_ex->XState.Length > sizeof(XSAVE_AREA_HEADER) + xstate_features_size) return STATUS_INVALID_PARAMETER; + if (xstate_compaction_enabled) frame->xstate.CompactionMask |= xstate_extended_features(); mask = (xstate_compaction_enabled ? xstate->CompactionMask : xstate->Mask) & xstate_extended_features(); xstate->Mask = frame->xstate.Mask & mask; xstate->CompactionMask = xstate_compaction_enabled ? (0x8000000000000000 | mask) : 0; @@ -2510,6 +2514,7 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) NtGetContextThread( GetCurrentThread(), &context->c ); if (xstate_extended_features()) { + if (xstate_compaction_enabled) frame->xstate.CompactionMask |= xstate_extended_features(); context_init_xstate( &context->c, &frame->xstate ); saved_compaction = frame->xstate.CompactionMask; } @@ -3029,11 +3034,11 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, #ifdef __APPLE__ "movq %gs:0x30,%rdx\n\t" "movl 0x340(%rdx),%eax\n\t" - "movl 0x344(%rdx),%edx\n\t" #else "movl %gs:0x340,%eax\n\t" /* amd64_thread_data()->xstate_features_mask */ - "movl %gs:0x344,%edx\n\t" /* amd64_thread_data()->xstate_features_mask high dword */ #endif + "xorl %edx,%edx\n\t" + "andl $7,%eax\n\t" "xorq %rbp,%rbp\n\t" "movq %rbp,0x2c0(%rcx)\n\t" "movq %rbp,0x2c8(%rcx)\n\t" From d049d287c5f718d0fd271da4ea11bc5401c1218b Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 8 Feb 2024 14:09:12 -0600 Subject: [PATCH 256/413] ntdll: Preserve untouched parts of xstate in NtSetContextThread(). (cherry picked from commit a10da8a42a25c6b72d863767461b85e1f3f0ea12) CW-Bug-Id: #23244 --- dlls/ntdll/unix/signal_i386.c | 2 ++ dlls/ntdll/unix/signal_x86_64.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index d781bd8b12c..fe0e0ed771b 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -1004,9 +1004,11 @@ NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context ) { CONTEXT_EX *context_ex = (CONTEXT_EX *)(context + 1); XSAVE_AREA_HEADER *xs = (XSAVE_AREA_HEADER *)((char *)context_ex + context_ex->XState.Offset); + UINT64 mask = frame->xstate.Mask; if (xstate_compaction_enabled) frame->xstate.CompactionMask |= xstate_extended_features(); copy_xstate( &frame->xstate, xs, xs->Mask ); + if (xs->CompactionMask) frame->xstate.Mask |= mask & ~xs->CompactionMask; } frame->restore_flags |= flags & ~CONTEXT_INTEGER; diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 1f23d166263..c7f2e627ed3 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1062,9 +1062,11 @@ NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context ) { CONTEXT_EX *context_ex = (CONTEXT_EX *)(context + 1); XSAVE_AREA_HEADER *xs = (XSAVE_AREA_HEADER *)((char *)context_ex + context_ex->XState.Offset); + UINT64 mask = frame->xstate.Mask; if (xstate_compaction_enabled) frame->xstate.CompactionMask |= xstate_extended_features(); copy_xstate( &frame->xstate, xs, xs->Mask ); + if (xs->CompactionMask) frame->xstate.Mask |= mask & ~xs->CompactionMask; } frame->restore_flags |= flags & ~CONTEXT_INTEGER; From 317f6bae362c7756f2101e8684da25bffd357761 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 17 Jan 2024 16:33:33 -0600 Subject: [PATCH 257/413] ntdll: Support generic xstate in Unix-side manipulation functions. (cherry picked from commit 6bc2c092327bae6cbf73b75fd33581ee5140a285) CW-Bug-Id: #23244 --- dlls/ntdll/unix/signal_i386.c | 2 ++ dlls/ntdll/unix/signal_x86_64.c | 4 +++ dlls/ntdll/unix/system.c | 48 ++++++++++++++++++++++++++++++--- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index fe0e0ed771b..d36636fdcf1 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -1144,6 +1144,8 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) if (context_ex->XState.Length < xstate_get_size( xstate->CompactionMask, xstate->Mask )) return STATUS_BUFFER_OVERFLOW; copy_xstate( xstate, &frame->xstate, xstate->Mask ); + /* copy_xstate may use avx in memcpy, restore xstate not to break the tests. */ + frame->restore_flags |= CONTEXT_XSTATE; } } if (context->ContextFlags & (CONTEXT_DEBUG_REGISTERS & ~CONTEXT_i386)) diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index c7f2e627ed3..d821cf1293b 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1189,6 +1189,8 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) if (context_ex->XState.Length < xstate_get_size( xstate->CompactionMask, xstate->Mask )) return STATUS_BUFFER_OVERFLOW; copy_xstate( xstate, &frame->xstate, xstate->Mask ); + /* copy_xstate may use avx in memcpy, restore xstate not to break the tests. */ + frame->restore_flags |= CONTEXT_XSTATE; } } if (context->ContextFlags & (CONTEXT_DEBUG_REGISTERS & ~CONTEXT_AMD64)) @@ -1411,6 +1413,8 @@ NTSTATUS get_thread_wow64_context( HANDLE handle, void *ctx, ULONG size ) if (context_ex->XState.Length < xstate_get_size( xstate->CompactionMask, xstate->Mask )) return STATUS_BUFFER_OVERFLOW; copy_xstate( xstate, &frame->xstate, xstate->Mask ); + /* copy_xstate may use avx in memcpy, restore xstate not to break the tests. */ + frame->restore_flags |= CONTEXT_XSTATE; } } return STATUS_SUCCESS; diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index 56f86a6caa7..ff12b13af2d 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -258,20 +258,60 @@ BOOL xstate_compaction_enabled = FALSE; UINT64 xstate_supported_features_mask; UINT64 xstate_features_size; +static int xstate_feature_offset[64] = {0, 0, 576}; +static int xstate_feature_size[64] = {0, 0, 256}; +static UINT64 xstate_aligned_features; + +static int next_xstate_offset( int off, UINT64 compaction_mask, int feature_idx ) +{ + const UINT64 feature_mask = (UINT64)1 << feature_idx; + + if (!compaction_mask) return xstate_feature_offset[feature_idx + 1] - sizeof(XSAVE_FORMAT); + + if (compaction_mask & feature_mask) off += xstate_feature_size[feature_idx]; + if (xstate_aligned_features & (feature_mask << 1)) + off = (off + 63) & ~63; + return off; +} + unsigned int xstate_get_size( UINT64 compaction_mask, UINT64 mask ) { - if (!(mask & ((UINT64)1 << XSTATE_AVX))) return sizeof(XSAVE_AREA_HEADER); - return sizeof(XSAVE_AREA_HEADER) + sizeof(YMMCONTEXT); + unsigned int i; + int off; + + mask >>= 2; + off = sizeof(XSAVE_AREA_HEADER); + i = 2; + while (mask) + { + if (mask == 1) return off + xstate_feature_size[i]; + off = next_xstate_offset( off, compaction_mask, i ); + mask >>= 1; + ++i; + } + return off; } void copy_xstate( XSAVE_AREA_HEADER *dst, XSAVE_AREA_HEADER *src, UINT64 mask ) { + unsigned int i; + int src_off, dst_off; + mask &= xstate_extended_features() & src->Mask; if (src->CompactionMask) mask &= src->CompactionMask; if (dst->CompactionMask) mask &= dst->CompactionMask; dst->Mask = (dst->Mask & ~xstate_extended_features()) | mask; - if (mask & ((UINT64)1 << XSTATE_AVX)) - *(YMMCONTEXT *)(dst + 1) = *(YMMCONTEXT *)(src + 1); + mask >>= 2; + src_off = dst_off = sizeof(XSAVE_AREA_HEADER); + i = 2; + while (1) + { + if (mask & 1) memcpy( (char *)dst + dst_off, (char *)src + src_off, xstate_feature_size[i] ); + if (!(mask >>= 1)) break; + src_off = next_xstate_offset( src_off, src->CompactionMask, i ); + dst_off = next_xstate_offset( dst_off, dst->CompactionMask, i ); + ++i; + } } #define AUTH 0x68747541 /* "Auth" */ From fccaa9842d47f92ecf6546ddd2ed44fa65a4d232 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 18 Mar 2024 15:00:06 -0600 Subject: [PATCH 258/413] ntdll: Factor out xstate_from_server() function. (cherry picked from commit 25e0a2580758282d679dafa557b2c1771410e970) CW-Bug-Id: #23244 --- dlls/ntdll/unix/thread.c | 82 +++++++++++++--------------------------- 1 file changed, 26 insertions(+), 56 deletions(-) diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 244305c9df6..7ae18a38ce1 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -610,6 +610,28 @@ static NTSTATUS context_to_server( context_t *to, USHORT to_machine, const void } +/*********************************************************************** + * xstate_from_server + * + * Copy xstate from the server format. + */ +static void xstate_from_server( CONTEXT_EX *xctx, const context_t *from ) +{ + XSTATE *xs = (XSTATE *)((char *)xctx + xctx->XState.Offset); + unsigned int i; + + xs->Mask &= ~4; + if (xs->CompactionMask) xs->CompactionMask = 0x8000000000000004; + for (i = 0; i < ARRAY_SIZE( from->ymm.regs.ymm_high); i++) + { + if (!from->ymm.regs.ymm_high[i].low && !from->ymm.regs.ymm_high[i].high) continue; + memcpy( &xs->YmmContext, &from->ymm.regs, sizeof(xs->YmmContext) ); + xs->Mask |= 4; + break; + } +} + + /*********************************************************************** * context_from_server * @@ -683,20 +705,7 @@ static NTSTATUS context_from_server( void *dst, const context_t *from, USHORT ma memcpy( to->ExtendedRegisters, from->ext.i386_regs, sizeof(to->ExtendedRegisters) ); } if ((from->flags & SERVER_CTX_YMM_REGISTERS) && (to_flags & CONTEXT_I386_XSTATE)) - { - CONTEXT_EX *xctx = (CONTEXT_EX *)(to + 1); - XSTATE *xs = (XSTATE *)((char *)xctx + xctx->XState.Offset); - - xs->Mask &= ~4; - if (xs->CompactionMask) xs->CompactionMask = 0x8000000000000004; - for (i = 0; i < ARRAY_SIZE( from->ymm.regs.ymm_high); i++) - { - if (!from->ymm.regs.ymm_high[i].low && !from->ymm.regs.ymm_high[i].high) continue; - memcpy( &xs->YmmContext, &from->ymm.regs, sizeof(xs->YmmContext) ); - xs->Mask |= 4; - break; - } - } + xstate_from_server( (CONTEXT_EX *)(to + 1), from ); return STATUS_SUCCESS; } @@ -757,20 +766,7 @@ static NTSTATUS context_from_server( void *dst, const context_t *from, USHORT ma to->Dr7 = from->debug.x86_64_regs.dr7; } if ((from->flags & SERVER_CTX_YMM_REGISTERS) && (to_flags & CONTEXT_I386_XSTATE)) - { - CONTEXT_EX *xctx = (CONTEXT_EX *)(to + 1); - XSTATE *xs = (XSTATE *)((char *)xctx + xctx->XState.Offset); - - xs->Mask &= ~4; - if (xs->CompactionMask) xs->CompactionMask = 0x8000000000000004; - for (i = 0; i < ARRAY_SIZE( from->ymm.regs.ymm_high); i++) - { - if (!from->ymm.regs.ymm_high[i].low && !from->ymm.regs.ymm_high[i].high) continue; - memcpy( &xs->YmmContext, &from->ymm.regs, sizeof(xs->YmmContext) ); - xs->Mask |= 4; - break; - } - } + xstate_from_server( (CONTEXT_EX *)(to + 1), from ); return STATUS_SUCCESS; } @@ -832,20 +828,7 @@ static NTSTATUS context_from_server( void *dst, const context_t *from, USHORT ma to->Dr7 = from->debug.x86_64_regs.dr7; } if ((from->flags & SERVER_CTX_YMM_REGISTERS) && (to_flags & CONTEXT_AMD64_XSTATE)) - { - CONTEXT_EX *xctx = (CONTEXT_EX *)(to + 1); - XSTATE *xs = (XSTATE *)((char *)xctx + xctx->XState.Offset); - - xs->Mask &= ~4; - if (xs->CompactionMask) xs->CompactionMask = 0x8000000000000004; - for (i = 0; i < ARRAY_SIZE( from->ymm.regs.ymm_high); i++) - { - if (!from->ymm.regs.ymm_high[i].low && !from->ymm.regs.ymm_high[i].high) continue; - memcpy( &xs->YmmContext, &from->ymm.regs, sizeof(xs->YmmContext) ); - xs->Mask |= 4; - break; - } - } + xstate_from_server( (CONTEXT_EX *)(to + 1), from ); return STATUS_SUCCESS; } @@ -914,20 +897,7 @@ static NTSTATUS context_from_server( void *dst, const context_t *from, USHORT ma to->Dr7 = from->debug.i386_regs.dr7; } if ((from->flags & SERVER_CTX_YMM_REGISTERS) && (to_flags & CONTEXT_AMD64_XSTATE)) - { - CONTEXT_EX *xctx = (CONTEXT_EX *)(to + 1); - XSTATE *xs = (XSTATE *)((char *)xctx + xctx->XState.Offset); - - xs->Mask &= ~4; - if (xs->CompactionMask) xs->CompactionMask = 0x8000000000000004; - for (i = 0; i < ARRAY_SIZE( from->ymm.regs.ymm_high); i++) - { - if (!from->ymm.regs.ymm_high[i].low && !from->ymm.regs.ymm_high[i].high) continue; - memcpy( &xs->YmmContext, &from->ymm.regs, sizeof(xs->YmmContext) ); - xs->Mask |= 4; - break; - } - } + xstate_from_server( (CONTEXT_EX *)(to + 1), from ); return STATUS_SUCCESS; } From af239758dba2e8b53abd68d24911bdd98d63679d Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 18 Mar 2024 15:29:50 -0600 Subject: [PATCH 259/413] ntdll: Factor out context_to_server() function. (cherry picked from commit a9d77d098928f6521bef7c1b1739429b8fe11ebd) CW-Bug-Id: #23244 --- dlls/ntdll/unix/thread.c | 46 ++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 7ae18a38ce1..d821fe848c7 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -224,6 +224,20 @@ static unsigned int get_native_context_flags( USHORT native_machine, USHORT wow_ } +/*********************************************************************** + * xstate_to_server + * + * Copy xstate to the server format. + */ +static void xstate_to_server( context_t *to, const CONTEXT_EX *xctx ) +{ + const XSTATE *xs = (const XSTATE *)((const char *)xctx + xctx->XState.Offset); + + to->flags |= SERVER_CTX_YMM_REGISTERS; + if (xs->Mask & 4) memcpy( &to->ymm.regs.ymm_high, &xs->YmmContext, sizeof(xs->YmmContext) ); +} + + /*********************************************************************** * context_to_server * @@ -300,13 +314,7 @@ static NTSTATUS context_to_server( context_t *to, USHORT to_machine, const void memcpy( to->ext.i386_regs, from->ExtendedRegisters, sizeof(to->ext.i386_regs) ); } if (flags & CONTEXT_I386_XSTATE) - { - const CONTEXT_EX *xctx = (const CONTEXT_EX *)(from + 1); - const XSTATE *xs = (const XSTATE *)((const char *)xctx + xctx->XState.Offset); - - to->flags |= SERVER_CTX_YMM_REGISTERS; - if (xs->Mask & 4) memcpy( &to->ymm.regs.ymm_high, &xs->YmmContext, sizeof(xs->YmmContext) ); - } + xstate_to_server( to, (const CONTEXT_EX *)(from + 1) ); return STATUS_SUCCESS; } @@ -364,13 +372,7 @@ static NTSTATUS context_to_server( context_t *to, USHORT to_machine, const void fpu_to_fpux( (XMM_SAVE_AREA32 *)to->fp.x86_64_regs.fpregs, &from->FloatSave ); } if (flags & CONTEXT_I386_XSTATE) - { - const CONTEXT_EX *xctx = (const CONTEXT_EX *)(from + 1); - const XSTATE *xs = (const XSTATE *)((const char *)xctx + xctx->XState.Offset); - - to->flags |= SERVER_CTX_YMM_REGISTERS; - if (xs->Mask & 4) memcpy( &to->ymm.regs.ymm_high, &xs->YmmContext, sizeof(xs->YmmContext) ); - } + xstate_to_server( to, (const CONTEXT_EX *)(from + 1) ); return STATUS_SUCCESS; } @@ -431,13 +433,7 @@ static NTSTATUS context_to_server( context_t *to, USHORT to_machine, const void to->debug.x86_64_regs.dr7 = from->Dr7; } if (flags & CONTEXT_AMD64_XSTATE) - { - const CONTEXT_EX *xctx = (const CONTEXT_EX *)(from + 1); - const XSTATE *xs = (const XSTATE *)((const char *)xctx + xctx->XState.Offset); - - to->flags |= SERVER_CTX_YMM_REGISTERS; - if (xs->Mask & 4) memcpy( &to->ymm.regs.ymm_high, &xs->YmmContext, sizeof(xs->YmmContext) ); - } + xstate_to_server( to, (const CONTEXT_EX *)(from + 1) ); return STATUS_SUCCESS; } @@ -502,13 +498,7 @@ static NTSTATUS context_to_server( context_t *to, USHORT to_machine, const void to->debug.i386_regs.dr7 = from->Dr7; } if (flags & CONTEXT_AMD64_XSTATE) - { - const CONTEXT_EX *xctx = (const CONTEXT_EX *)(from + 1); - const XSTATE *xs = (const XSTATE *)((const char *)xctx + xctx->XState.Offset); - - to->flags |= SERVER_CTX_YMM_REGISTERS; - if (xs->Mask & 4) memcpy( &to->ymm.regs.ymm_high, &xs->YmmContext, sizeof(xs->YmmContext) ); - } + xstate_to_server( to, (const CONTEXT_EX *)(from + 1) ); return STATUS_SUCCESS; } From 57170eaa823ea3a5801898c83204da8865f7d2a7 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 18 Mar 2024 15:14:50 -0600 Subject: [PATCH 260/413] ntdll: Mind generic xstate masks in server context conversion. (cherry picked from commit 3dea242677be1ca7f18ca95b6d9d0aacc5c3f1b9) CW-Bug-Id: #23244 --- dlls/ntdll/unix/thread.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index d821fe848c7..25bc747df70 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -233,6 +233,7 @@ static void xstate_to_server( context_t *to, const CONTEXT_EX *xctx ) { const XSTATE *xs = (const XSTATE *)((const char *)xctx + xctx->XState.Offset); + if (xs->CompactionMask && !(xs->CompactionMask & 4)) return; to->flags |= SERVER_CTX_YMM_REGISTERS; if (xs->Mask & 4) memcpy( &to->ymm.regs.ymm_high, &xs->YmmContext, sizeof(xs->YmmContext) ); } @@ -610,8 +611,14 @@ static void xstate_from_server( CONTEXT_EX *xctx, const context_t *from ) XSTATE *xs = (XSTATE *)((char *)xctx + xctx->XState.Offset); unsigned int i; - xs->Mask &= ~4; - if (xs->CompactionMask) xs->CompactionMask = 0x8000000000000004; + xs->Mask &= ~(ULONG64)4; + + if (xs->CompactionMask) + { + xs->CompactionMask &= ~(UINT64)3; + if (!(xs->CompactionMask & 4)) return; + } + for (i = 0; i < ARRAY_SIZE( from->ymm.regs.ymm_high); i++) { if (!from->ymm.regs.ymm_high[i].low && !from->ymm.regs.ymm_high[i].high) continue; From 5705b3e454c1519e5ac6558431dc2dfa56b96dc9 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 18 Jan 2024 12:59:26 -0600 Subject: [PATCH 261/413] ntdll: Support more xstate features. (cherry picked from commit 29c73ee17335b30f3f49c8b3562742c6a35b482c) CW-Bug-Id: #23244 --- dlls/ntdll/tests/exception.c | 2 +- dlls/ntdll/unix/system.c | 40 +++++++++++++++++++++---- programs/wineboot/wineboot.c | 58 ++++++++++++++++++++++++++---------- 3 files changed, 79 insertions(+), 21 deletions(-) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 76190aef75a..50f407c4a52 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -10600,7 +10600,7 @@ static DWORD test_extended_context_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGI } else { - ok(xs->Mask == (xsaveopt_enabled ? 0 : 4), "Got unexpected Mask %#I64x.\n", xs->Mask); + ok((xs->Mask & 7) == (xsaveopt_enabled ? 0 : 4), "Got unexpected Mask %#I64x.\n", xs->Mask); /* The save area has garbage if xsaveopt is available, so we can't test * its contents. */ diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index ff12b13af2d..0e77e7d7b14 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -258,8 +258,8 @@ BOOL xstate_compaction_enabled = FALSE; UINT64 xstate_supported_features_mask; UINT64 xstate_features_size; -static int xstate_feature_offset[64] = {0, 0, 576}; -static int xstate_feature_size[64] = {0, 0, 256}; +static int xstate_feature_offset[64]; +static int xstate_feature_size[64]; static UINT64 xstate_aligned_features; static int next_xstate_offset( int off, UINT64 compaction_mask, int feature_idx ) @@ -353,6 +353,21 @@ __ASM_GLOBAL_FUNC( do_cpuid, "ret" ) #endif +extern UINT64 do_xgetbv( unsigned int cx); +#ifdef __i386__ +__ASM_GLOBAL_FUNC( do_xgetbv, + "movl 4(%esp),%ecx\n\t" + "xgetbv\n\t" + "ret" ) +#else +__ASM_GLOBAL_FUNC( do_xgetbv, + "movl %edi,%ecx\n\t" + "xgetbv\n\t" + "shlq $32,%rdx\n\t" + "orq %rdx,%rax\n\t" + "ret" ) +#endif + #ifdef __i386__ extern int have_cpuid(void); __ASM_GLOBAL_FUNC( have_cpuid, @@ -411,8 +426,11 @@ static void get_cpuid_name( char *buffer ) static void get_cpuinfo( SYSTEM_CPU_INFORMATION *info ) { + static const ULONG64 wine_xstate_supported_features = 0xff; /* XSTATE_AVX, XSTATE_MPX_BNDREGS, XSTATE_MPX_BNDCSR, + * XSTATE_AVX512_KMASK, XSTATE_AVX512_ZMM_H, XSTATE_AVX512_ZMM */ unsigned int regs[4], regs2[4], regs3[4]; ULONGLONG features; + unsigned int i; #if defined(__i386__) info->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL; @@ -462,13 +480,25 @@ static void get_cpuinfo( SYSTEM_CPU_INFORMATION *info ) { do_cpuid( 0x0000000d, 1, regs3 ); /* get XSAVE details */ if (regs3[0] & 2) xstate_compaction_enabled = TRUE; - xstate_supported_features_mask = 3; - if (features & CPU_FEATURE_AVX) - xstate_supported_features_mask |= (UINT64)1 << XSTATE_AVX; + + do_cpuid( 0x0000000d, 0, regs3 ); /* get user xstate features */ + xstate_supported_features_mask = ((ULONG64)regs3[3] << 32) | regs3[0]; + xstate_supported_features_mask &= do_xgetbv( 0 ) & wine_xstate_supported_features; + TRACE("xstate_supported_features_mask %#llx.\n", (long long)xstate_supported_features_mask); + for (i = 2; i < 64; ++i) + { + if (!(xstate_supported_features_mask & ((ULONG64)1 << i))) continue; + do_cpuid( 0x0000000d, i, regs3 ); /* get user xstate features */ + xstate_feature_offset[i] = regs3[1]; + xstate_feature_size[i] = regs3[0]; + if (regs3[2] & 2) xstate_aligned_features |= (ULONG64)1 << i; + TRACE("xstate[%d] offset %d, size %d, aligned %d.\n", i, xstate_feature_offset[i], xstate_feature_size[i], !!(regs3[2] & 2)); + } xstate_features_size = xstate_get_size( xstate_compaction_enabled ? 0x8000000000000000 | xstate_supported_features_mask : 0, xstate_supported_features_mask ) - sizeof(XSAVE_AREA_HEADER); xstate_features_size = (xstate_features_size + 15) & ~15; + TRACE("xstate_features_size %lld.\n", (long long)xstate_features_size); } if (regs[1] == AUTH && regs[3] == ENTI && regs[2] == CAMD) diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c index 0ea276f6876..ff6bd6cc38c 100644 --- a/programs/wineboot/wineboot.c +++ b/programs/wineboot/wineboot.c @@ -195,10 +195,27 @@ static DWORD set_reg_value_dword( HKEY hkey, const WCHAR *name, DWORD value ) #if defined(__i386__) || defined(__x86_64__) +extern UINT64 WINAPI do_xgetbv( unsigned int cx); +#ifdef __i386__ +__ASM_STDCALL_FUNC( do_xgetbv, 4, + "movl 4(%esp),%ecx\n\t" + "xgetbv\n\t" + "ret $4" ) +#else +__ASM_GLOBAL_FUNC( do_xgetbv, + "xgetbv\n\t" + "shlq $32,%rdx\n\t" + "orq %rdx,%rax\n\t" + "ret" ) +#endif + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) { + static const ULONG64 wine_xstate_supported_features = 0xfc; /* XSTATE_AVX, XSTATE_MPX_BNDREGS, XSTATE_MPX_BNDCSR, + * XSTATE_AVX512_KMASK, XSTATE_AVX512_ZMM_H, XSTATE_AVX512_ZMM */ XSTATE_CONFIGURATION *xstate = &data->XState; - unsigned int i; + ULONG64 supported_mask; + unsigned int i, off; int regs[4]; if (!data->ProcessorFeatures[PF_AVX_INSTRUCTIONS_AVAILABLE]) @@ -217,29 +234,40 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) __cpuidex(regs, 0xd, 0); TRACE("XSAVE details %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); - if (!(regs[0] & XSTATE_AVX)) + supported_mask = ((ULONG64)regs[3] << 32) | regs[0]; + supported_mask &= do_xgetbv(0) & wine_xstate_supported_features; + if (!(supported_mask >> 2)) return; - xstate->EnabledFeatures = (1 << XSTATE_LEGACY_FLOATING_POINT) | (1 << XSTATE_LEGACY_SSE) | (1 << XSTATE_AVX); + xstate->EnabledFeatures = (1 << XSTATE_LEGACY_FLOATING_POINT) | (1 << XSTATE_LEGACY_SSE) | supported_mask; xstate->EnabledVolatileFeatures = xstate->EnabledFeatures; - xstate->Size = sizeof(XSAVE_FORMAT) + sizeof(XSTATE); xstate->AllFeatureSize = regs[1]; - xstate->AllFeatures[0] = offsetof(XSAVE_FORMAT, XmmRegisters); - xstate->AllFeatures[1] = sizeof(M128A) * 16; - xstate->AllFeatures[2] = sizeof(YMMCONTEXT); - - for (i = 0; i < 3; ++i) - xstate->Features[i].Size = xstate->AllFeatures[i]; - - xstate->Features[1].Offset = xstate->Features[0].Size; - xstate->Features[2].Offset = sizeof(XSAVE_FORMAT) + offsetof(XSTATE, YmmContext); __cpuidex(regs, 0xd, 1); xstate->OptimizedSave = regs[0] & 1; xstate->CompactionEnabled = !!(regs[0] & 2); - __cpuidex(regs, 0xd, 2); - TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + xstate->Features[0].Size = xstate->AllFeatures[0] = offsetof(XSAVE_FORMAT, XmmRegisters); + xstate->Features[1].Size = xstate->AllFeatures[1] = sizeof(M128A) * 16; + xstate->Features[1].Offset = xstate->Features[0].Size; + off = sizeof(XSAVE_FORMAT) + sizeof(XSAVE_AREA_HEADER); + supported_mask >>= 2; + for (i = 2; supported_mask; ++i, supported_mask >>= 1) + { + if (!(supported_mask & 1)) continue; + __cpuidex( regs, 0xd, i ); + xstate->Features[i].Offset = regs[1]; + xstate->Features[i].Size = xstate->AllFeatures[i] = regs[0]; + if (regs[2] & 2) + { + xstate->AlignedFeatures |= (ULONG64)1 << i; + off = (off + 63) & ~63; + } + off += xstate->Features[i].Size; + TRACE("xstate[%d] offset %lu, size %lu, aligned %d.\n", i, xstate->Features[i].Offset, xstate->Features[i].Size, !!(regs[2] & 2)); + } + xstate->Size = xstate->CompactionEnabled ? off : xstate->Features[i - 1].Offset + xstate->Features[i - 1].Size; + TRACE("xstate size %lu, compacted %d, optimized %d.\n", xstate->Size, xstate->CompactionEnabled, xstate->OptimizedSave); } static BOOL is_tsc_trusted_by_the_kernel(void) From 56d69bdc69d04d8da84c6ee2469fddd3343c03c2 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 18 Mar 2024 13:29:11 -0600 Subject: [PATCH 262/413] ntdll/tests: Add more tests for xstate. (cherry picked from commit bb47eb9f723053c512136362a167ddb1cc565d71) CW-Bug-Id: #23244 --- dlls/ntdll/tests/exception.c | 100 +++++++++++++++++++++++++++++++---- 1 file changed, 90 insertions(+), 10 deletions(-) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 50f407c4a52..ad806ded587 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -10546,11 +10546,12 @@ static const unsigned test_extended_context_spoil_data2[8] = {0x15, 0x25, 0x35, static BOOL test_extended_context_modified_state; static BOOL xsaveopt_enabled, compaction_enabled; +static ULONG64 xstate_supported_features; static DWORD test_extended_context_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher) { - static const ULONG64 expected_compaction_mask = 0x8000000000000004; + const ULONG64 expected_compaction_mask = (0x8000000000000000 | xstate_supported_features) & ~(ULONG64)3; CONTEXT_EX *xctx = (CONTEXT_EX *)(context + 1); unsigned int *context_ymm_data; DWORD expected_min_offset; @@ -10588,7 +10589,7 @@ static DWORD test_extended_context_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGI if (compaction_enabled) ok((xs->CompactionMask & (expected_compaction_mask | 3)) == expected_compaction_mask, - "Got compaction mask %#I64x.\n", xs->CompactionMask); + "Got compaction mask %#I64x, expected %#I64x.\n", xs->CompactionMask, expected_compaction_mask); else ok(!xs->CompactionMask, "Got compaction mask %#I64x.\n", xs->CompactionMask); @@ -10883,7 +10884,8 @@ static void test_extended_context(void) }; const struct context_parameters *context_arch; - const ULONG64 supported_features = 7, supported_compaction_mask = supported_features | ((ULONG64)1 << 63); + const ULONG64 supported_features = 0xff; + const ULONG64 supported_compaction_mask = supported_features | ((ULONG64)1 << 63); ULONG expected_length, expected_length_xstate, context_flags, expected_offset, max_xstate_length; ULONG64 enabled_features, expected_compaction; DECLSPEC_ALIGN(64) BYTE context_buffer2[4096]; @@ -10921,6 +10923,7 @@ static void test_extended_context(void) xsaveopt_enabled = regs[0] & 1; compaction_enabled = regs[0] & 2; } + xstate_supported_features = enabled_features & supported_features; /* Test context manipulation functions. */ length = 0xdeadbeef; @@ -11391,7 +11394,7 @@ static void test_extended_context(void) xs->Mask = 0xdeadbeef; xs->CompactionMask = 0xdeadbeef; - bret = pSetXStateFeaturesMask(context, 7); + bret = pSetXStateFeaturesMask(context, xstate_supported_features); ok(bret == !!(flags & CONTEXT_NATIVE), "Got unexpected bret %#x.\n", bret); context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset); ok(context_flags == (bret ? flags_fpx : flags), @@ -11405,8 +11408,8 @@ static void test_extended_context(void) mask = 0xdeadbeef; bret = pGetXStateFeaturesMask(context, &mask); if (flags & CONTEXT_NATIVE) - ok(bret && mask == (enabled_features & supported_features), - "Got unexpected bret %#x, mask %s, flags %#lx.\n", bret, wine_dbgstr_longlong(mask), flags); + ok(bret && mask == xstate_supported_features, + "Got unexpected bret %#x, mask %s, flags %#lx (enabled_features & supported_features %#I64x).\n", bret, wine_dbgstr_longlong(mask), flags, xstate_supported_features); else ok(!bret && mask == 0xdeadbeef, "Got unexpected bret %#x, mask %s, flags %#lx.\n", bret, wine_dbgstr_longlong(mask), flags); @@ -11631,6 +11634,14 @@ static void test_extended_context(void) &context, &length); memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext)); ok(bret, "Got unexpected bret %#x.\n", bret); + + /* clear potentially leftover xstate */ + pSetXStateFeaturesMask(context, 0); + context->ContextFlags = CONTEXT_XSTATE; + SetThreadContext(GetCurrentThread(), context); + + context->ContextFlags = CONTEXT_FULL | CONTEXT_XSTATE | CONTEXT_FLOATING_POINT; + pSetXStateFeaturesMask(context, ~(ULONG64)0); *(void **)(call_func_code_reset_ymm_state + call_func_offsets.func_addr) = GetThreadContext; *(void **)(call_func_code_reset_ymm_state + call_func_offsets.func_param1) = (void *)GetCurrentThread(); @@ -11648,12 +11659,12 @@ static void test_extended_context(void) ok(context->ContextFlags == expected_flags, "Got unexpected ContextFlags %#lx.\n", context->ContextFlags); - expected_compaction = compaction_enabled ? ((ULONG64)1 << 63) | 4 : 0; + expected_compaction = compaction_enabled ? ((ULONG64)1 << 63) | (xstate_supported_features & ~(UINT64)3) : 0; xs = (XSTATE *)((BYTE *)context_ex + context_ex->XState.Offset); ok((xs->Mask & supported_features) == (xsaveopt_enabled ? 0 : 4), "Got unexpected Mask %#I64x.\n", xs->Mask); ok((xs->CompactionMask & (supported_features | ((ULONG64)1 << 63))) == expected_compaction, - "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(xs->CompactionMask)); + "Got unexpected CompactionMask %s (expected %#I64x).\n", wine_dbgstr_longlong(xs->CompactionMask), expected_compaction); for (i = 4; i < 8; ++i) ok(!data[i], "Got unexpected data %#x, i %u.\n", data[i], i); @@ -11663,6 +11674,36 @@ static void test_extended_context(void) || broken(((ULONG *)&xs->YmmContext)[i] == test_extended_context_data[i + 4]), "Got unexpected data %#lx, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i); + /* Test setting context which has only part of xstate in CompactionMask. */ + if (compaction_enabled && enabled_features & ((ULONG64)1 << XSTATE_AVX512_KMASK)) + { + *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_addr) = SetThreadContext; + *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_param1) = (void *)GetCurrentThread(); + *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_param2) = context; + *(void **)(call_func_code_set_ymm0 + call_func_offsets.ymm0_save) = data; + memcpy(code_mem, call_func_code_set_ymm0, sizeof(call_func_code_set_ymm0)); + context->ContextFlags = CONTEXT_XSTATE; + xs->CompactionMask = 0x8000000000000000 | ((ULONG64)1 << XSTATE_AVX512_KMASK); + xs->Mask = 0; + memcpy(data, test_extended_context_data, sizeof(data)); + bret = func(); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + /* Setting a context with only part of xstate in CompactionMask doesn't change missing parts. */ + for (i = 4; i < 8; ++i) + ok(data[i] == test_extended_context_data[i], "Got unexpected data %#x, i %u.\n", data[i], i); + + memcpy(data, test_extended_context_data, sizeof(data)); + xs->CompactionMask |= XSTATE_MASK_GSSE; + bret = func(); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + for (i = 4; i < 8; ++i) + ok(!data[i], "Got unexpected data %#x, i %u.\n", data[i], i); + } + else + { + skip("avx512 is not available, skipping test.\n"); + } + /* Test fault exception context. */ memset(data, 0xff, sizeof(data)); xs->Mask = 0; @@ -11699,9 +11740,10 @@ static void test_extended_context(void) bret = GetThreadContext(thread, context); ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); todo_wine_if (!xsaveopt_enabled) - ok((xs->Mask & supported_features) == (xsaveopt_enabled ? 0 : 4), "Got unexpected Mask %#I64x.\n", xs->Mask); + ok((xs->Mask & supported_features) == (xsaveopt_enabled ? 0 : 4), "Got unexpected Mask %#I64x.\n", xs->Mask); ok((xs->CompactionMask & supported_compaction_mask) == expected_compaction, - "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(xs->CompactionMask)); + "Got unexpected CompactionMask %I64x, expected %I64x.\n", xs->CompactionMask, + expected_compaction); for (i = 0; i < 16 * 4; ++i) ok(((ULONG *)&xs->YmmContext)[i] == ((xs->Mask & 4) ? 0 : 0xcccccccc), @@ -11782,6 +11824,44 @@ static void test_extended_context(void) "Got unexpected value %#lx, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i); } + if (compaction_enabled && enabled_features & ((ULONG64)1 << XSTATE_AVX512_KMASK)) + { + ULONG64 saved_mask; + ULONG *d; + + saved_mask = xs->CompactionMask; + xs->Mask = XSTATE_MASK_GSSE; + xs->CompactionMask = 0x8000000000000000 | xs->Mask; + *(ULONG *)&xs->YmmContext = 0x11111111; + bret = SetThreadContext(thread, context); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + + xs->Mask = (ULONG64)1 << XSTATE_AVX512_KMASK; + xs->CompactionMask = 0x8000000000000000 | xs->Mask; + *(ULONG *)&xs->YmmContext = 0x22222222; + bret = SetThreadContext(thread, context); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + + xs->CompactionMask = saved_mask; + bret = GetThreadContext(thread, context); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + + todo_wine_if(xs->Mask == XSTATE_MASK_GSSE) + ok((xs->Mask & (XSTATE_MASK_GSSE | ((ULONG64)1 << XSTATE_AVX512_KMASK))) + == (XSTATE_MASK_GSSE | ((ULONG64)1 << XSTATE_AVX512_KMASK)), "got Mask %#I64x.\n", xs->Mask); + d = pLocateXStateFeature(context, XSTATE_AVX, NULL); + ok(!!d, "Got NULL.\n"); + ok(*d == 0x11111111, "got %#lx.\n", *d); + + d = pLocateXStateFeature(context, XSTATE_AVX512_KMASK, NULL); + ok(!!d, "Got NULL.\n"); + todo_wine ok(*d == 0x22222222, "got %#lx.\n", *d); + } + else + { + skip("avx512 is not available, skipping test.\n"); + } + bret = ResumeThread(thread); ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); From 68c286875fd43fa4bfb580176b07baa2a506a508 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 16 Jun 2022 13:41:44 -0500 Subject: [PATCH 263/413] ntdll: Factor out validate_context_xstate() function. CW-Bug-Id: #20821 --- dlls/ntdll/unix/signal_i386.c | 4 +--- dlls/ntdll/unix/signal_x86_64.c | 4 +--- dlls/ntdll/unix/thread.c | 17 +++++++++++++++++ dlls/ntdll/unix/unix_private.h | 1 + 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index d36636fdcf1..c5e7c3ffa98 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -1130,9 +1130,7 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) XSAVE_AREA_HEADER *xstate = (XSAVE_AREA_HEADER *)((char *)context_ex + context_ex->XState.Offset); UINT64 mask; - if (context_ex->XState.Length < sizeof(XSAVE_AREA_HEADER) || - context_ex->XState.Length > sizeof(XSAVE_AREA_HEADER) + xstate_features_size) - return STATUS_INVALID_PARAMETER; + if (!validate_context_xstate( context )) return STATUS_INVALID_PARAMETER; if (xstate_compaction_enabled) frame->xstate.CompactionMask |= xstate_extended_features(); mask = (xstate_compaction_enabled ? xstate->CompactionMask : xstate->Mask) & xstate_extended_features(); diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index d821cf1293b..d292c97f517 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1175,9 +1175,7 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) XSAVE_AREA_HEADER *xstate = (XSAVE_AREA_HEADER *)((char *)context_ex + context_ex->XState.Offset); UINT64 mask; - if (context_ex->XState.Length < sizeof(XSAVE_AREA_HEADER) || - context_ex->XState.Length > sizeof(XSAVE_AREA_HEADER) + xstate_features_size) - return STATUS_INVALID_PARAMETER; + if (!validate_context_xstate( context )) return STATUS_INVALID_PARAMETER; if (xstate_compaction_enabled) frame->xstate.CompactionMask |= xstate_extended_features(); mask = (xstate_compaction_enabled ? xstate->CompactionMask : xstate->Mask) & xstate_extended_features(); diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 25bc747df70..3e71286d6bf 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -158,6 +158,23 @@ void fpu_to_fpux( XMM_SAVE_AREA32 *fpux, const I386_FLOATING_SAVE_AREA *fpu ) } +/*********************************************************************** + * validate_context_xstate + */ +BOOL validate_context_xstate( CONTEXT *context ) +{ + CONTEXT_EX *context_ex; + + context_ex = (CONTEXT_EX *)(context + 1); + + if (context_ex->XState.Length < sizeof(XSAVE_AREA_HEADER) || + context_ex->XState.Length > sizeof(XSAVE_AREA_HEADER) + xstate_features_size) + return FALSE; + + return TRUE; +} + + /*********************************************************************** * get_server_context_flags */ diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index 77a3de4c5d0..967422d9288 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -247,6 +247,7 @@ extern void DECLSPEC_NORETURN abort_process( int status ); extern void DECLSPEC_NORETURN exit_process( int status ); extern void wait_suspend( CONTEXT *context ); extern NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_chance ); +extern BOOL validate_context_xstate( CONTEXT *context ); extern NTSTATUS set_thread_context( HANDLE handle, const void *context, BOOL *self, USHORT machine ); extern NTSTATUS get_thread_context( HANDLE handle, void *context, BOOL *self, USHORT machine ); extern unsigned int alloc_object_attributes( const OBJECT_ATTRIBUTES *attr, struct object_attributes **ret, From 907bbf28822b28a4fc8524fb4bac8514892793b1 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 16 Jun 2022 13:50:10 -0500 Subject: [PATCH 264/413] ntdll: Validate context xstate at once in NtGetContextThread(). CW-Bug-Id: #20821 --- dlls/ntdll/unix/signal_i386.c | 4 ++-- dlls/ntdll/unix/signal_x86_64.c | 4 ++-- dlls/ntdll/unix/thread.c | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index c5e7c3ffa98..b87fc077602 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -1031,6 +1031,8 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) BOOL use_cached_debug_regs = FALSE; NTSTATUS ret; + if (!validate_context_xstate( context )) return STATUS_INVALID_PARAMETER; + if (self && needed_flags & CONTEXT_DEBUG_REGISTERS) { /* debug registers require a server call if hw breakpoints are enabled */ @@ -1130,8 +1132,6 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) XSAVE_AREA_HEADER *xstate = (XSAVE_AREA_HEADER *)((char *)context_ex + context_ex->XState.Offset); UINT64 mask; - if (!validate_context_xstate( context )) return STATUS_INVALID_PARAMETER; - if (xstate_compaction_enabled) frame->xstate.CompactionMask |= xstate_extended_features(); mask = (xstate_compaction_enabled ? xstate->CompactionMask : xstate->Mask) & xstate_extended_features(); xstate->Mask = frame->xstate.Mask & mask; diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index d292c97f517..453be6ca1b3 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1085,6 +1085,8 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) BOOL use_cached_debug_regs = FALSE; BOOL self = (handle == GetCurrentThread()); + if (!validate_context_xstate( context )) return STATUS_INVALID_PARAMETER; + if (self && needed_flags & CONTEXT_DEBUG_REGISTERS) { /* debug registers require a server call if hw breakpoints are enabled */ @@ -1175,8 +1177,6 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) XSAVE_AREA_HEADER *xstate = (XSAVE_AREA_HEADER *)((char *)context_ex + context_ex->XState.Offset); UINT64 mask; - if (!validate_context_xstate( context )) return STATUS_INVALID_PARAMETER; - if (xstate_compaction_enabled) frame->xstate.CompactionMask |= xstate_extended_features(); mask = (xstate_compaction_enabled ? xstate->CompactionMask : xstate->Mask) & xstate_extended_features(); xstate->Mask = frame->xstate.Mask & mask; diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 3e71286d6bf..0f5fbbb9adc 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -165,6 +165,8 @@ BOOL validate_context_xstate( CONTEXT *context ) { CONTEXT_EX *context_ex; + if (!((context->ContextFlags & 0x40) && xstate_extended_features())) return TRUE; + context_ex = (CONTEXT_EX *)(context + 1); if (context_ex->XState.Length < sizeof(XSAVE_AREA_HEADER) || From 253d56c2cc6af418a2e560aa4a8e8ab3fc71e09d Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 16 Jun 2022 13:25:39 -0500 Subject: [PATCH 265/413] ntdll: Validate xstate alignment in validate_context_xstate(). CW-Bug-Id: #20821 --- dlls/ntdll/tests/exception.c | 14 ++++++++++++++ dlls/ntdll/unix/thread.c | 2 ++ 2 files changed, 16 insertions(+) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index ad806ded587..3a15c1ac7ab 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -10897,6 +10897,7 @@ static void test_extended_context(void) CONTEXT_EX *context_ex; CONTEXT *context; unsigned data[8]; + NTSTATUS status; HANDLE thread; ULONG64 mask; XSTATE *xs; @@ -11729,6 +11730,19 @@ static void test_extended_context(void) thread = CreateThread(NULL, 0, test_extended_context_thread, 0, CREATE_SUSPENDED, NULL); ok(!!thread, "Failed to create thread.\n"); + /* Unaligned xstate. */ + length = sizeof(context_buffer); + memset(context_buffer, 0xcc, sizeof(context_buffer)); + bret = pInitializeContext(context_buffer, CONTEXT_FULL | CONTEXT_XSTATE | CONTEXT_FLOATING_POINT, + &context, &length); + ok(bret, "Got unexpected bret %#x.\n", bret); + context_ex = (CONTEXT_EX *)(context + 1); + context_ex->XState.Offset += 0x10; + status = pNtGetContextThread(thread, context); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %#lx.\n", status); + status = pNtGetContextThread(GetCurrentThread(), context); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %#lx.\n", status); + bret = pInitializeContext(context_buffer, CONTEXT_FULL | CONTEXT_XSTATE | CONTEXT_FLOATING_POINT, &context, &length); ok(bret, "Got unexpected bret %#x.\n", bret); diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 0f5fbbb9adc..812302bf346 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -173,6 +173,8 @@ BOOL validate_context_xstate( CONTEXT *context ) context_ex->XState.Length > sizeof(XSAVE_AREA_HEADER) + xstate_features_size) return FALSE; + if (((ULONG_PTR)context_ex + context_ex->XState.Offset) & 63) return FALSE; + return TRUE; } From 5165a89266bb27a9f079250f37734c95f2412bd2 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 28 Mar 2024 11:18:03 -0600 Subject: [PATCH 266/413] winegstreamer: HACK: Disable MF_SA_D3D11_AWARE for The Finals. CW-Bug-Id: #23621 --- dlls/winegstreamer/video_decoder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winegstreamer/video_decoder.c b/dlls/winegstreamer/video_decoder.c index 3e42d04d75a..44e5c4821f2 100644 --- a/dlls/winegstreamer/video_decoder.c +++ b/dlls/winegstreamer/video_decoder.c @@ -830,7 +830,7 @@ static HRESULT video_decoder_create_with_types(const GUID *const *input_types, U { const char *sgi; - if ((sgi = getenv("SteamGameId")) && ((!strcmp(sgi, "2009100")) || (!strcmp(sgi, "2555360")))) + if ((sgi = getenv("SteamGameId")) && ((!strcmp(sgi, "2073850")) || (!strcmp(sgi, "2009100")) || (!strcmp(sgi, "2555360")))) IMFAttributes_SetUINT32(decoder->attributes, &MF_SA_D3D11_AWARE, FALSE); } From 5936b69cc7749a0ae72246b909af9202e34e84f4 Mon Sep 17 00:00:00 2001 From: Ziqing Hui Date: Wed, 15 Nov 2023 11:43:21 +0800 Subject: [PATCH 267/413] winegstreamer: Integrate media-converter to winegstreamer. CW-Bug-Id: #23225 --- dlls/winegstreamer/Makefile.in | 8 +- .../winegstreamer/media-converter/audioconv.c | 1102 +++++++++++++++++ .../media-converter/audioconvbin.c | 148 +++ .../winegstreamer/media-converter/fossilize.c | 722 +++++++++++ dlls/winegstreamer/media-converter/lib.c | 329 +++++ .../media-converter/media-converter.h | 287 +++++ dlls/winegstreamer/media-converter/murmur3.c | 371 ++++++ .../winegstreamer/media-converter/videoconv.c | 938 ++++++++++++++ dlls/winegstreamer/unixlib.c | 9 + 9 files changed, 3913 insertions(+), 1 deletion(-) create mode 100644 dlls/winegstreamer/media-converter/audioconv.c create mode 100644 dlls/winegstreamer/media-converter/audioconvbin.c create mode 100644 dlls/winegstreamer/media-converter/fossilize.c create mode 100644 dlls/winegstreamer/media-converter/lib.c create mode 100644 dlls/winegstreamer/media-converter/media-converter.h create mode 100644 dlls/winegstreamer/media-converter/murmur3.c create mode 100644 dlls/winegstreamer/media-converter/videoconv.c diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 9cae96c5ebc..ac38a1b8964 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -32,4 +32,10 @@ SOURCES = \ winegstreamer_classes.idl \ wm_reader.c \ wma_decoder.c \ - wmv_decoder.c + wmv_decoder.c \ + media-converter/audioconv.c \ + media-converter/audioconvbin.c \ + media-converter/fossilize.c \ + media-converter/lib.c \ + media-converter/murmur3.c \ + media-converter/videoconv.c diff --git a/dlls/winegstreamer/media-converter/audioconv.c b/dlls/winegstreamer/media-converter/audioconv.c new file mode 100644 index 00000000000..71612cbfddd --- /dev/null +++ b/dlls/winegstreamer/media-converter/audioconv.c @@ -0,0 +1,1102 @@ +/* + * Copyright 2024 Ziqing Hui 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 + */ + +/* Algorithm + * --------- + * + * The application feeds encoded audio into XAudio2 in chunks. Since we don't have access to all + * chunks in a stream on initialization (as we do with the video converter), we continuously hash + * the stream as it is sent to us. Each "chunk" is identified as the hash of the entire stream up + * to that chunk. + * + * Since chunks are small (~2 kilobytes), this leads to a significant possibility of two different + * streams having identical intro chunks (imagine two streams that start with several seconds of + * silence). This means we need a tree of chunks. Suppose two incoming streams with chunks that + * hash as shown (i.e. identical intro chunks that diverge later): + * + * Stream 1: [AA BB CC DD] + * + * Stream 2: [AA BB YY ZZ] + * + * We record a tree and the transcoder will walk it depth-first in order to reconstruct each unique + * stream: + * + * AA => aa.ptna + * AA+BB => bb.ptna + * AA+BB+CC => cc.ptna + * AA+BB+CC+DD => dd.ptna + * AA+BB+YY => yy.ptna + * AA+BB+YY+ZZ => zz.ptna + * + * Upon playback, we chain each transcoded stream chunk together as the packets come in: + * + * AA -> start stream with aa.ptna + * BB -> play bb.ptna + * CC -> play cc.ptna + * DD -> play dd.ptna + * + * or: + * + * AA -> start stream with aa.ptna + * BB -> play bb.ptna + * YY -> play yy.ptna + * ZZ -> play zz.ptna + * + * or: + * + * AA -> start stream with aa.ptna + * NN -> not recognized, instead play blank.ptna and mark this stream as needs-transcoding + * OO -> play blank.ptna + * PP -> play blank.ptna + * When the Stream is destroyed, we'll record AA+NN+OO+PP into the needs-transcode database + * for the transcoder to convert later. + * + * + * Physical Format + * --------------- + * + * All stored values are little-endian. + * + * Transcoded audio is stored in the "transcoded" Fossilize database under the + * AUDIO_CONV_FOZ_TAG_PTNADATA tag. Each chunk is stored in one entry with as many of the following + * "Proton Audio" (ptna) packets as are required to store the entire transcoded chunk: + * + * uint32_t packet_header: Information about the upcoming packet, see bitmask: + * MSB [FFFF PPPP PPPP PPPP PPPP LLLL LLLL LLLL] LSB + * L: Number of _bytes_ in this packet following this header. + * P: Number of _samples_ at the end of this packet which are padding and should be skipped. + * F: Flag bits: + * 0x1: This packet is an Opus header + * 0x2, 0x4, 0x8: Reserved for future use. + * + * If the Opus header flag is set: + * Following packet is an Opus identification header, as defined in RFC 7845 "Ogg + * Encapsulation for the Opus Audio Codec" Section 5.1. + * + * + * If the header flag is not set: + * Following packet is raw Opus data to be sent to an Opus decoder. + * + * + * If we encounter a stream which needs transcoding, we record the buffers and metadata in + * a Fossilize database. The database has three tag types: + * + * AUDIO_CONV_FOZ_TAG_STREAM: This identifies each unique stream of buffers. For example: + * [hash(AA+BB+CC+DD)] -> [AA, BB, CC, DD] + * [hash(AA+BB+XX+YY)] -> [AA, BB, XX, YY] + * + * AUDIO_CONV_FOZ_TAG_AUDIODATA: This contans the actual encoded audio data. For example: + * [AA] -> [AA's buffer data] + * [BB] -> [BB's buffer data] + * + * AUDIO_CONV_FOZ_TAG_CODECINFO: This contans the codec data required to decode the buffer. Only + * the "head" of each stream is recorded. For example: + * [AA] -> [ + * uint32_t wmaversion (from WAVEFORMATEX.wFormatTag) + * uint32_t bitrate (from WAVEFORMATEX.nAvgBytesPerSec) + * uint32_t channels (WAVEFORMATEX.nChannels) + * uint32_t rate (WAVEFORMATEX.nSamplesPerSec) + * uint32_t block_align (WAVEFORMATEX.nBlockAlign) + * uint32_t depth (WAVEFORMATEX.wBitsPerSample) + * char[remainder of entry] codec_data (codec data which follows WAVEFORMATEX) + * ] + * + */ + +#if 0 +#pragma makedep unix +#endif + +#include "media-converter.h" + +#include +#include +#include +#include + +GST_DEBUG_CATEGORY_EXTERN(media_converter_debug); +#undef GST_CAT_DEFAULT +#define GST_CAT_DEFAULT media_converter_debug + +#define AUDIO_CONV_ENCODED_LENGTH_MASK 0x00000fff /* 4kB fits in here. */ +#define AUDIO_CONV_PADDING_LENGTH_MASK 0x0ffff000 /* 120ms of samples at 48kHz fits in here. */ +#define AUDIO_CONV_PADDING_LENGTH_SHIFT 12 +#define AUDIO_CONV_FLAG_MASK 0xf0000000 +#define AUDIO_CONV_FLAG_HEADER 0x10000000 /* This chunk is the Opus header. */ +#define _AUDIO_CONV_FLAG_RESERVED1 0x20000000 /* Not yet used */ +#define _AUDIO_CONV_FLAG_RESERVED2 0x40000000 /* Not yet used */ +#define _AUDIO_CONV_FLAG_V2 0x80000000 /* Indicates a "version 2" header, process somehow differently (TBD). */ + +/* Properties of the "blank" audio file. */ +#define _BLANK_AUDIO_FILE_LENGTH_MS 10.0 +#define _BLANK_AUDIO_FILE_RATE 48000.0 + +#define AUDIO_CONV_FOZ_TAG_STREAM 0 +#define AUDIO_CONV_FOZ_TAG_CODECINFO 1 +#define AUDIO_CONV_FOZ_TAG_AUDIODATA 2 +#define AUDIO_CONV_FOZ_TAG_PTNADATA 3 +#define AUDIO_CONV_FOZ_NUM_TAGS 4 + +typedef enum +{ + NO_LOOP, + LOOPING, + LOOP_ENDED, + LOOP_ERROR, +} loop_state; + +struct buffer_entry +{ + struct payload_hash hash; + GstBuffer *buffer; +}; + +/* Followed by codec_data_size bytes codec data. */ +struct need_transcode_head +{ + size_t codec_data_size; + uint32_t wmaversion; + uint32_t bitrate; + uint32_t channels; + uint32_t rate; + uint32_t block_align; + uint32_t depth; +} __attribute__((packed)); + +/* Represents a Stream, a sequence of buffers. */ +struct stream_state +{ + struct murmur3_128_state hash_state; + struct payload_hash current_hash; + GList *buffers; /* Entry type: struct buffer_entry. */ + GList *loop_buffers; /* Entry type: struct buffer_entry. */ + struct need_transcode_head *codec_info; + bool needs_dump; +}; + +struct stream_state_serializer +{ + struct stream_state *state; + int index; +}; + +struct audio_conv_state +{ + bool sent_header; + struct need_transcode_head *codec_data; + struct murmur3_128_state hash_state; + struct murmur3_128_state loop_hash_state; + struct stream_state *stream_state; + struct fozdb *read_fozdb; +}; + +typedef struct +{ + GstElement element; + GstPad *sink_pad, *src_pad; + pthread_mutex_t state_mutex; + struct audio_conv_state *state; +} AudioConv; + +typedef struct +{ + GstElementClass class; +} AudioConvClass; + +G_DEFINE_TYPE(AudioConv, audio_conv, GST_TYPE_ELEMENT); +#define AUDIO_CONV_TYPE (audio_conv_get_type()) +#define AUDIO_CONV(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), AUDIO_CONV_TYPE, AudioConv)) +#define parent_class (audio_conv_parent_class) +GST_ELEMENT_REGISTER_DEFINE(protonaudioconverter, "protonaudioconverter", + GST_RANK_MARGINAL, AUDIO_CONV_TYPE); + +static GstStaticPadTemplate audio_conv_sink_template = GST_STATIC_PAD_TEMPLATE("sink", + GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS("audio/x-wma;")); + +static GstStaticPadTemplate audio_conv_src_template = GST_STATIC_PAD_TEMPLATE("src", + GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS("audio/x-opus;")); + +static struct dump_fozdb dump_fozdb = {PTHREAD_MUTEX_INITIALIZER, NULL, false}; + +static struct buffer_entry *buffer_entry_create(struct payload_hash *hash, GstBuffer *buffer) +{ + struct buffer_entry *entry = calloc(1, sizeof(*entry)); + entry->hash = *hash; + entry->buffer = gst_buffer_ref(buffer); + return entry; +} + +static void buffer_entry_release(void *arg) +{ + struct buffer_entry *entry = arg; + gst_buffer_unref(entry->buffer); + free(entry); +} + +static bool dumping_disabled(void) +{ + return option_enabled("MEDIACONV_AUDIO_DONT_DUMP"); +} + +static bool hash_data(const uint8_t *data, size_t size, struct murmur3_128_state *hash_state, struct payload_hash *hash) +{ + struct bytes_reader reader; + bytes_reader_init(&reader, data, size); + return murmur3_128_full(&reader, bytes_reader_read, hash_state, hash); +} + +static int dump_fozdb_open_audio(bool create) +{ + return dump_fozdb_open(&dump_fozdb, create, "MEDIACONV_AUDIO_DUMP_FILE", AUDIO_CONV_FOZ_NUM_TAGS); +} + +static void dump_fozdb_discard_transcoded(void) +{ + GList *chunks_to_discard = NULL, *chunks_to_keep = NULL, *chunks = NULL, *list_iter; + struct payload_hash chunk_id, *stream_id; + struct fozdb *read_fozdb; + char *read_fozdb_path; + GHashTableIter iter; + int ret; + + if (dump_fozdb.already_cleaned) + return; + dump_fozdb.already_cleaned = true; + + if (discarding_disabled()) + return; + if (!file_exists(getenv("MEDIACONV_AUDIO_DUMP_FILE"))) + return; + + if ((dump_fozdb_open_audio(false)) < 0) + return; + + if (!(read_fozdb_path = getenv("MEDIACONV_AUDIO_TRANSCODED_FILE"))) + { + GST_ERROR("Env MEDIACONV_AUDIO_TRANSCODED_FILE not set."); + return; + } + + if ((ret = fozdb_create(read_fozdb_path, O_RDONLY, true /* Read-only? */, AUDIO_CONV_FOZ_NUM_TAGS, &read_fozdb)) < 0) + { + GST_ERROR("Failed to create read fozdb, ret %d.", ret); + return; + } + + fozdb_iter_tag(dump_fozdb.fozdb, AUDIO_CONV_FOZ_TAG_STREAM, &iter); + while (g_hash_table_iter_next(&iter, (void *)&stream_id, NULL)) + { + uint32_t chunks_size, i; + size_t read_size; + + if (fozdb_entry_size(dump_fozdb.fozdb, AUDIO_CONV_FOZ_TAG_STREAM, stream_id, &chunks_size) == CONV_OK) + { + uint8_t *buffer = calloc(1, chunks_size); + if (fozdb_read_entry_data(dump_fozdb.fozdb, AUDIO_CONV_FOZ_TAG_STREAM, stream_id, + 0, buffer, chunks_size, &read_size, true) == CONV_OK) + { + GList *stream_chunks = NULL; + bool has_all = true; + + for (i = 0; i < read_size / sizeof(chunk_id); ++i) + { + payload_hash_from_bytes(&chunk_id, buffer + i * sizeof(chunk_id)); + if (!fozdb_has_entry(read_fozdb, AUDIO_CONV_FOZ_TAG_PTNADATA, &chunk_id)) + { + has_all = false; + break; + } + stream_chunks = g_list_append(stream_chunks, + entry_name_create(AUDIO_CONV_FOZ_TAG_AUDIODATA, &chunk_id)); + } + + for (list_iter = stream_chunks; list_iter; list_iter = list_iter->next) + { + struct entry_name *entry = list_iter->data; + if (has_all) + { + chunks_to_discard = g_list_append(chunks_to_discard, + entry_name_create(entry->tag, &entry->hash)); + chunks_to_discard = g_list_append(chunks_to_discard, + entry_name_create(AUDIO_CONV_FOZ_TAG_CODECINFO, &entry->hash)); + } + else + { + chunks_to_keep = g_list_append(chunks_to_keep, + entry_name_create(entry->tag, &entry->hash)); + chunks_to_keep = g_list_append(chunks_to_keep, + entry_name_create(AUDIO_CONV_FOZ_TAG_CODECINFO, &entry->hash)); + } + } + + if (has_all) + chunks_to_discard = g_list_append(chunks_to_discard, + entry_name_create(AUDIO_CONV_FOZ_TAG_STREAM, stream_id)); + + g_list_free_full(stream_chunks, free); + } + free(buffer); + } + } + + for (list_iter = chunks_to_discard; list_iter; list_iter = list_iter->next) + { + struct entry_name *entry = list_iter->data; + if (!g_list_find_custom(chunks_to_keep, entry, entry_name_compare)) + chunks = g_list_append(chunks, entry_name_create(entry->tag, &entry->hash)); + } + + if ((ret = fozdb_discard_entries(dump_fozdb.fozdb, chunks)) < 0) + { + GST_ERROR("Failed to discard entries, ret %d.", ret); + dump_fozdb_close(&dump_fozdb); + } + + g_list_free_full(chunks, free); + g_list_free_full(chunks_to_keep, free); + g_list_free_full(chunks_to_discard, free); +} + +static bool need_transcode_head_create_from_caps(GstCaps *caps, struct need_transcode_head **out) +{ + int wmaversion, bitrate, channels, rate, block_align, depth; + const GstStructure *structure = gst_caps_get_structure(caps, 0); + struct need_transcode_head *head; + const GValue *codec_data_value; + GstBuffer *codec_data_buffer; + gsize codec_data_size; + + GST_DEBUG("caps %"GST_PTR_FORMAT", out %p.", caps, out); + + if (!gst_structure_get_int(structure, "wmaversion", &wmaversion)) + { + GST_ERROR("Caps have no wmaversion field."); + return false; + } + if (!gst_structure_get_int(structure, "bitrate", &bitrate)) + { + GST_ERROR("Caps have no bitrate field."); + return false; + } + if (!gst_structure_get_int(structure, "channels", &channels)) + { + GST_ERROR("Caps have no channels field."); + return false; + } + if (!gst_structure_get_int(structure, "rate", &rate)) + { + GST_ERROR("Caps have no rate field."); + return false; + } + if (!gst_structure_get_int(structure, "block_align", &block_align)) + { + GST_ERROR("Caps have no block_align field."); + return false; + } + if (!gst_structure_get_int(structure, "depth", &depth)) + { + GST_ERROR("Caps have no depth field."); + return false; + } + if (!(codec_data_value = gst_structure_get_value(structure, "codec_data")) + || !(codec_data_buffer = gst_value_get_buffer(codec_data_value))) + { + GST_ERROR("Caps have no codec_data field."); + return false; + } + + codec_data_size = gst_buffer_get_size(codec_data_buffer); + head = calloc(1, sizeof(*head) + codec_data_size); + head->codec_data_size = codec_data_size; + head->wmaversion = wmaversion; + head->bitrate = bitrate; + head->channels = channels; + head->rate = rate; + head->block_align = block_align; + head->depth = depth; + gst_buffer_extract(codec_data_buffer, 0, head + 1, codec_data_size); + + *out = head; + return true; +} + +static struct need_transcode_head *need_transcode_head_dup(struct need_transcode_head *head) +{ + size_t size = sizeof(*head) + head->codec_data_size; + struct need_transcode_head *dup; + + dup = calloc(1, size); + memcpy(dup, head, size); + + return dup; +} + + +static void need_transcode_head_serialize(struct need_transcode_head *head, + uint8_t *buffer, size_t buffer_size, size_t *out_size) +{ + *out_size = sizeof(*head) - sizeof(head->codec_data_size) + head->codec_data_size; + + if (buffer_size < *out_size) + { + GST_ERROR("Buffer too small: buffer size %zu, out size %zu.", buffer_size, *out_size); + return; + } + memcpy(buffer, &head->wmaversion, *out_size); +} + +static void stream_state_serializer_init(struct stream_state_serializer *serializer, struct stream_state *state) +{ + serializer->state = state; + serializer->index = 0; +} + +static int stream_state_serializer_read(void *data_reader, uint8_t *buffer, size_t size, size_t *read_size) +{ + struct stream_state_serializer *serializer = data_reader; + struct buffer_entry *entry; + + if (!size) + { + *read_size = 0; + return CONV_OK; + } + + if (serializer->index >= g_list_length(serializer->state->buffers)) + return CONV_ERROR_DATA_END; + + entry = g_list_nth_data(serializer->state->buffers, serializer->index++); + memcpy(buffer, &entry->hash, sizeof(entry->hash)); + + *read_size = sizeof(entry->hash); + return CONV_OK; +} + +static struct stream_state *stream_state_create(void) +{ + struct stream_state *state; + state = calloc(1, sizeof(*state)); + murmur3_128_state_init(&state->hash_state, HASH_SEED); + return state; +} + +static void stream_state_release(struct stream_state *state) +{ + g_list_free_full(state->buffers, buffer_entry_release); + g_list_free_full(state->loop_buffers, buffer_entry_release); + if (state->codec_info) + free(state->codec_info); + free(state); +} + +static void stream_state_reset(struct stream_state *state) +{ + murmur3_128_state_reset(&state->hash_state); + memset(&state->current_hash, 0, sizeof(state->current_hash)); + g_list_free_full(g_steal_pointer(&state->buffers), buffer_entry_release); + g_list_free_full(g_steal_pointer(&state->loop_buffers), buffer_entry_release); + free(state->codec_info); + state->codec_info = NULL; + state->needs_dump = false; +} + +static loop_state stream_state_record_buffer(struct stream_state *state, struct payload_hash *buffer_hash, + struct payload_hash *loop_hash, GstBuffer *buffer, struct need_transcode_head *codec_info) +{ + if (!state->codec_info && codec_info) + state->codec_info = need_transcode_head_dup(codec_info); + + if (g_list_length(state->loop_buffers) < g_list_length(state->buffers)) + { + struct buffer_entry *entry = g_list_nth_data(state->buffers, g_list_length(state->loop_buffers)); + if (!memcmp(&entry->hash, loop_hash, sizeof(*loop_hash))) + { + state->loop_buffers = g_list_append(state->loop_buffers, + buffer_entry_create(buffer_hash /* Not loop_hash! */, buffer)); + if (g_list_length(state->loop_buffers) == g_list_length(state->buffers)) + { + /* Full loop, just drop them. */ + g_list_free_full(g_steal_pointer(&state->loop_buffers), buffer_entry_release); + return LOOP_ENDED; + } + + return LOOPING; + } + } + + /* Partial loop, track them and then continue */ + if (state->loop_buffers) + state->buffers = g_list_concat(state->buffers, g_steal_pointer(&state->loop_buffers)); + + state->buffers = g_list_append(state->buffers, buffer_entry_create(buffer_hash, buffer)); + + if (!hash_data((const uint8_t *)buffer_hash, sizeof(*buffer_hash), &state->hash_state, &state->current_hash)) + return LOOP_ERROR; + + return NO_LOOP; +} + +static bool stream_state_is_stream_subset(struct stream_state *state, struct fozdb *db, struct payload_hash *stream_id) +{ + uint64_t offset = 0; + GList *list_iter; + + for (list_iter = state->buffers; list_iter; list_iter = list_iter->next) + { + struct buffer_entry *entry = list_iter->data; + struct payload_hash buffer_id; + size_t read_size; + + if ((fozdb_read_entry_data(db, AUDIO_CONV_FOZ_TAG_STREAM, stream_id, + offset, (uint8_t *)&buffer_id, sizeof(buffer_id), &read_size, true)) < 0 + || read_size != sizeof(buffer_id)) + return false; + + if (memcmp(&buffer_id, &entry->hash, sizeof(buffer_id)) != 0) + return false; + + offset += sizeof(buffer_id); + } + + GST_LOG("Stream id %s is a subset of %s, so not recording stream.", + format_hash(&state->current_hash), format_hash(stream_id)); + + return true; +} + +static int stream_state_write_to_foz(struct stream_state *state) +{ + struct stream_state_serializer serializer; + struct buffer_entry *entry; + struct bytes_reader reader; + uint8_t buffer[1024]; + size_t header_size; + GList *list_iter; + bool found; + int ret; + + GST_DEBUG("state %p, current hash %s.", state, format_hash(&state->current_hash)); + + if (!state->needs_dump || !state->buffers) + return CONV_OK; + + pthread_mutex_lock(&dump_fozdb.mutex); + + if ((ret = dump_fozdb_open_audio(true)) < 0) + { + GST_ERROR("Failed to open audio dump fozdb, ret %d.", ret); + pthread_mutex_unlock(&dump_fozdb.mutex); + return ret; + } + + found = fozdb_has_entry(dump_fozdb.fozdb, AUDIO_CONV_FOZ_TAG_STREAM, &state->current_hash); + if (!found) + { + /* Are there any recorded streams of which this stream is a subset? */ + struct payload_hash *stream_id; + GHashTableIter stream_ids; + + fozdb_iter_tag(dump_fozdb.fozdb, AUDIO_CONV_FOZ_TAG_STREAM, &stream_ids); + while (g_hash_table_iter_next(&stream_ids, (void **)&stream_id, NULL)) + { + if (stream_state_is_stream_subset(state, dump_fozdb.fozdb, stream_id)) + { + found = true; + break; + } + } + } + + if (!found) + { + if (dumping_disabled()) + { + GST_LOG("Dumping disabled, so not recording stream id %s.", format_hash(&state->current_hash)); + } + else + { + GST_LOG("Recording stream id %s.", format_hash(&state->current_hash)); + + need_transcode_head_serialize(state->codec_info, buffer, sizeof(buffer), &header_size); + bytes_reader_init(&reader, buffer, header_size); + entry = state->buffers->data; + if ((ret = fozdb_write_entry(dump_fozdb.fozdb, AUDIO_CONV_FOZ_TAG_CODECINFO, &entry->hash, + &reader, bytes_reader_read, true)) < 0) + { + GST_ERROR("Unable to write stream header, ret %d.\n", ret); + pthread_mutex_unlock(&dump_fozdb.mutex); + return ret; + } + + stream_state_serializer_init(&serializer, state); + if ((ret = fozdb_write_entry(dump_fozdb.fozdb, AUDIO_CONV_FOZ_TAG_STREAM, &state->current_hash, + &serializer, stream_state_serializer_read, true)) < 0) + { + GST_ERROR("Unable to write stream, ret %d.\n", ret); + pthread_mutex_unlock(&dump_fozdb.mutex); + return ret; + } + + for (list_iter = state->buffers; list_iter; list_iter = list_iter->next) + { + struct gst_buffer_reader buffer_reader; + + entry = list_iter->data; + gst_buffer_reader_init(&buffer_reader, entry->buffer); + if ((ret = fozdb_write_entry(dump_fozdb.fozdb, AUDIO_CONV_FOZ_TAG_AUDIODATA, &entry->hash, + &buffer_reader, gst_buffer_reader_read, true)) < 0) + { + GST_ERROR("Unable to write audio data, ret %d.\n", ret); + pthread_mutex_unlock(&dump_fozdb.mutex); + return ret; + } + } + } + } + + pthread_mutex_unlock(&dump_fozdb.mutex); + return CONV_OK; +} + +static int audio_conv_state_create(struct audio_conv_state **out) +{ + struct audio_conv_state *state; + struct fozdb *fozdb = NULL; + char *read_fozdb_path; + int ret; + + if (!(read_fozdb_path = getenv("MEDIACONV_AUDIO_TRANSCODED_FILE"))) + { + GST_ERROR("Env MEDIACONV_AUDIO_TRANSCODED_FILE is not set!"); + return CONV_ERROR_ENV_NOT_SET; + } + + if ((ret = fozdb_create(read_fozdb_path, O_RDONLY, true /* Read-only? */, AUDIO_CONV_FOZ_NUM_TAGS, &fozdb)) < 0) + GST_ERROR("Failed to create fozdb from %s, ret %d.", read_fozdb_path, ret); + + state = calloc(1, sizeof(*state)); + murmur3_128_state_init(&state->hash_state, HASH_SEED); + murmur3_128_state_init(&state->loop_hash_state, HASH_SEED); + state->stream_state = stream_state_create(); + state->read_fozdb = fozdb; + + *out = state; + return CONV_OK; +} + +static void audio_conv_state_release(struct audio_conv_state *state) +{ + free(state->codec_data); + stream_state_release(state->stream_state); + if (state->read_fozdb) + fozdb_release(state->read_fozdb); + free(state); +} + +static void audio_conv_state_reset(struct audio_conv_state *state) +{ + if (stream_state_write_to_foz(state->stream_state) < 0) + GST_ERROR("Failed to write stream to dump fozdb."); + + stream_state_reset(state->stream_state); + murmur3_128_state_reset(&state->hash_state); + murmur3_128_state_reset(&state->loop_hash_state); +} + +/* Allocate a buffer on success, free it after usage. */ +static int audio_conv_state_open_transcode_file(struct audio_conv_state *state, GstBuffer *buffer, + uint8_t **out_data, size_t *out_size) +{ + struct payload_hash hash, loop_hash; + uint32_t transcoded_size; + const char *blank_audio; + bool try_loop, hash_ok; + GstMapInfo map_info; + uint64_t file_size; + loop_state loop; + uint8_t *data; + size_t size; + int fd; + + /* Hash buffer. */ + if (!gst_buffer_map(buffer, &map_info, GST_MAP_READ)) + return CONV_ERROR; + hash_ok = hash_data(map_info.data, map_info.size, &state->hash_state, &hash) + && hash_data(map_info.data, map_info.size, &state->loop_hash_state, &loop_hash); + gst_buffer_unmap(buffer, &map_info); + if (!hash_ok) + { + GST_ERROR("Failed to hash buffer."); + return CONV_ERROR; + } + + loop = stream_state_record_buffer(state->stream_state, &hash, &loop_hash, buffer, state->codec_data); + gst_buffer_unref(buffer); /* Buffer has been recorded, so unref it. */ + switch (loop) + { + case NO_LOOP: + murmur3_128_state_reset(&state->loop_hash_state); + try_loop = false; + break; + + case LOOP_ENDED: + murmur3_128_state_reset(&state->loop_hash_state); + case LOOPING: + try_loop = true; + break; + + case LOOP_ERROR: + default: + return CONV_ERROR; + } + + if (try_loop) + GST_INFO("Buffer hash: %s (Loop: %s).", format_hash(&hash), format_hash(&loop_hash)); + else + GST_INFO("Buffer hash: %s.", format_hash(&hash)); + + /* Try to read transcoded data. */ + if (state->read_fozdb) + { + if (fozdb_entry_size(state->read_fozdb, + AUDIO_CONV_FOZ_TAG_PTNADATA, &hash, &transcoded_size) == CONV_OK) + { + data = calloc(1, transcoded_size); + if (fozdb_read_entry_data(state->read_fozdb, AUDIO_CONV_FOZ_TAG_PTNADATA, &hash, 0, + data, transcoded_size, &size, false) == CONV_OK) + { + *out_data = data; + *out_size = size; + return CONV_OK; + } + free(data); + } + + if (try_loop && fozdb_entry_size(state->read_fozdb, + AUDIO_CONV_FOZ_TAG_PTNADATA, &loop_hash, &transcoded_size) == CONV_OK) + { + data = calloc(1, transcoded_size); + if (fozdb_read_entry_data(state->read_fozdb, AUDIO_CONV_FOZ_TAG_PTNADATA, &loop_hash, 0, + data, transcoded_size, &size, false) == CONV_OK) + { + *out_data = data; + *out_size = size; + return CONV_OK; + } + free(data); + } + } + + /* If we can't, return the blank file */ + state->stream_state->needs_dump = true; + if (!(blank_audio = getenv("MEDIACONV_BLANK_AUDIO_FILE"))) + { + GST_ERROR("Env MEDIACONV_BLANK_AUDIO_FILE not set."); + return CONV_ERROR_ENV_NOT_SET; + } + if (!open_file(blank_audio, O_RDONLY, &fd)) + return CONV_ERROR_OPEN_FAILED; + if (!get_file_size(fd, &file_size)) + { + close(fd); + return CONV_ERROR; + } + data = calloc(1, file_size); + if (!complete_read(fd, data, file_size)) + { + free(data); + close(fd); + return CONV_ERROR_READ_FAILED; + } + + create_placeholder_file("placeholder-audio-used"); + + *out_data = data; + *out_size = file_size; + + return CONV_OK; +} + +/* Call pthread_mutex_unlock() to unlock after usage. */ +static struct audio_conv_state *audio_conv_lock_state(AudioConv *conv) +{ + pthread_mutex_lock(&conv->state_mutex); + if (!conv->state) + pthread_mutex_unlock(&conv->state_mutex); + return conv->state; +} + +static GstStateChangeReturn audio_conv_change_state(GstElement *element, GstStateChange transition) +{ + AudioConv *conv = AUDIO_CONV(element); + struct audio_conv_state *state; + int ret; + + GST_DEBUG_OBJECT(element, "State transition %s.", gst_state_change_get_name(transition)); + + switch (transition) + { + case GST_STATE_CHANGE_NULL_TO_READY: + /* Do runtime setup. */ + + /* Open fozdb here; this is the right place to fail + * and opening may be expensive. */ + pthread_mutex_lock(&dump_fozdb.mutex); + dump_fozdb_discard_transcoded(); + ret = dump_fozdb_open_audio(true); + pthread_mutex_unlock(&dump_fozdb.mutex); + if (ret < 0) + { + GST_ERROR("Failed to open dump fozdb, ret %d.", ret); + return GST_STATE_CHANGE_FAILURE; + } + + /* Create audio conv state. */ + if ((ret = audio_conv_state_create(&state)) < 0) + { + GST_ERROR("Failed to create audio conv state, ret %d.", ret); + return GST_STATE_CHANGE_FAILURE; + } + pthread_mutex_lock(&conv->state_mutex); + assert(!conv->state); + conv->state = state; + pthread_mutex_unlock(&conv->state_mutex); + break; + + case GST_STATE_CHANGE_READY_TO_NULL: + /* Do runtime teardown. */ + pthread_mutex_lock(&conv->state_mutex); + state = conv->state; + conv->state = NULL; + pthread_mutex_unlock(&conv->state_mutex); + + if (state && (ret = stream_state_write_to_foz(state->stream_state)) < 0) + GST_WARNING("Error writing out stream data, ret %d.", ret); + audio_conv_state_release(state); + break; + + default: + break; + } + + return GST_ELEMENT_CLASS(parent_class)->change_state(element, transition); + + /* XXX on ReadyToNull, sodium drops state _again_ here... why? */ +} + +static gboolean audio_conv_sink_event(GstPad *pad, GstObject *parent, GstEvent *event) +{ + AudioConv *conv = AUDIO_CONV(parent); + struct audio_conv_state *state; + GstCaps *caps; + bool ret; + + GST_DEBUG_OBJECT(pad, "Got sink event %"GST_PTR_FORMAT".", event); + + switch (event->type) + { + case GST_EVENT_CAPS: + if ((state = audio_conv_lock_state(conv))) + { + gst_event_parse_caps(event, &caps); + if (!need_transcode_head_create_from_caps(caps, &state->codec_data)) + { + GST_ERROR("Invalid WMA caps!"); + pthread_mutex_unlock(&conv->state_mutex); + return false; + } + pthread_mutex_unlock(&conv->state_mutex); + } + + caps = gst_caps_from_string("audio/x-opus, channel-mapping-family=0"); + ret = push_event(conv->src_pad, gst_event_new_caps(caps)); + gst_caps_unref(caps); + return ret; + + case GST_EVENT_FLUSH_STOP: + if ((state = audio_conv_lock_state(conv))) + { + audio_conv_state_reset(state); + pthread_mutex_unlock(&conv->state_mutex); + } + return gst_pad_event_default(pad, parent, event); + + default: + return gst_pad_event_default(pad, parent, event); + } +} + +static GstFlowReturn audio_conv_chain(GstPad *pad, GstObject *parent, GstBuffer *buffer) +{ + size_t ptna_data_size, offset, encoded_len; + GstFlowReturn flow_ret = GST_FLOW_ERROR; + AudioConv *conv = AUDIO_CONV(parent); + struct audio_conv_state *state; + uint8_t *ptna_data = NULL; + int ret; + + GST_LOG_OBJECT(pad, "Handling buffer <%"GST_PTR_FORMAT">.", buffer); + + if (!(state = audio_conv_lock_state(conv))) + return flow_ret; + + if ((ret = audio_conv_state_open_transcode_file(state, buffer, &ptna_data, &ptna_data_size)) < 0) + { + GST_ERROR("Failed to read transcoded audio, ret %d. Things will go badly...", ret); + goto done; + } + + for (offset = 0; offset < ptna_data_size; offset += encoded_len) + { + uint32_t packet_header, flags, padding_len; + GstBuffer *new_buffer; + bool packet_is_header; + + if (offset + 4 >= ptna_data_size) + { + GST_WARNING( "Short read on ptna header?"); + break; + } + + packet_header = bytes_to_uint32(&ptna_data[offset]); + offset += 4; + + flags = packet_header & AUDIO_CONV_FLAG_MASK, + padding_len = (packet_header & AUDIO_CONV_PADDING_LENGTH_MASK) >> AUDIO_CONV_PADDING_LENGTH_SHIFT; + encoded_len = packet_header & AUDIO_CONV_ENCODED_LENGTH_MASK; + + if (offset + encoded_len > ptna_data_size) + { + GST_WARNING("Short read on ptna data?"); + break; + } + + packet_is_header = flags & AUDIO_CONV_FLAG_HEADER; + if (packet_is_header && state->sent_header) + continue; /* Only send one header. */ + + /* TODO: can we use a GstBuffer cache here? */ + new_buffer = gst_buffer_new_and_alloc(encoded_len); + if (!packet_is_header && padding_len > 0) + gst_buffer_add_audio_clipping_meta(new_buffer, GST_FORMAT_DEFAULT, 0, padding_len); + gst_buffer_fill(new_buffer, 0, ptna_data + offset, encoded_len); + + GST_LOG("Pushing one packet of len %zu.", encoded_len); + if ((flow_ret = gst_pad_push(conv->src_pad, new_buffer)) < 0) + { + GST_ERROR("Failed to push buffer <%"GST_PTR_FORMAT"> to src pad %"GST_PTR_FORMAT, + new_buffer, conv->src_pad); + goto done; + } + + if (packet_is_header) + state->sent_header = true; + } + + flow_ret = GST_FLOW_OK; + +done: + if (ptna_data) + free(ptna_data); + pthread_mutex_unlock(&conv->state_mutex); + return flow_ret; +} + +static gboolean audio_conv_src_query(GstPad *pad, GstObject *parent, GstQuery *query) +{ + AudioConv *conv = AUDIO_CONV(parent); + GstSchedulingFlags flags; + gint min, max, align; + GstQuery *peer_query; + + GST_DEBUG_OBJECT(pad, "Got query %"GST_PTR_FORMAT".", query); + + switch (query->type) + { + case GST_QUERY_SCHEDULING: + peer_query = gst_query_new_scheduling(); + if (!gst_pad_peer_query(conv->sink_pad, peer_query)) + { + gst_query_unref(peer_query); + return false; + } + gst_query_parse_scheduling(peer_query, &flags, &min, &max, &align); + gst_query_unref(peer_query); + gst_query_set_scheduling(query, flags, min, max, align); + return true; + + default: + return gst_pad_query_default(pad, parent, query); + } + +} + +static gboolean audio_conv_active_mode(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean active) +{ + AudioConv *conv = AUDIO_CONV(parent); + return gst_pad_activate_mode(conv->sink_pad, mode, active); +} + +static void audio_conv_finalize(GObject *object) +{ + AudioConv *conv = AUDIO_CONV(object); + + pthread_mutex_destroy(&conv->state_mutex); + if (conv->state) + audio_conv_state_release(conv->state); + + G_OBJECT_CLASS(parent_class)->finalize(object); +} + +static void audio_conv_class_init(AudioConvClass *klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + gst_element_class_set_metadata(element_class, + "Proton audio converter", + "Codec/Demuxer", + "Converts audio for Proton", + "Andrew Eikum , Ziqing Hui "); + + element_class->change_state = audio_conv_change_state; + object_class->finalize = audio_conv_finalize; + + gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&audio_conv_sink_template)); + gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&audio_conv_src_template)); +} + +static void audio_conv_init(AudioConv *conv) +{ + GstElement *element = GST_ELEMENT(conv); + + conv->sink_pad = gst_pad_new_from_static_template(&audio_conv_sink_template, "sink"); + gst_pad_set_event_function(conv->sink_pad, GST_DEBUG_FUNCPTR(audio_conv_sink_event)); + gst_pad_set_chain_function(conv->sink_pad, GST_DEBUG_FUNCPTR(audio_conv_chain)); + gst_element_add_pad(element, conv->sink_pad); + + conv->src_pad = gst_pad_new_from_static_template(&audio_conv_src_template, "src"); + gst_pad_set_query_function(conv->src_pad, GST_DEBUG_FUNCPTR(audio_conv_src_query)); + gst_pad_set_activatemode_function(conv->src_pad, GST_DEBUG_FUNCPTR(audio_conv_active_mode)); + gst_element_add_pad(element, conv->src_pad); + + pthread_mutex_init(&conv->state_mutex, NULL); + conv->state = NULL; +} diff --git a/dlls/winegstreamer/media-converter/audioconvbin.c b/dlls/winegstreamer/media-converter/audioconvbin.c new file mode 100644 index 00000000000..9857a0ff507 --- /dev/null +++ b/dlls/winegstreamer/media-converter/audioconvbin.c @@ -0,0 +1,148 @@ +/* + * Copyright 2024 Ziqing Hui 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 "media-converter.h" + +GST_DEBUG_CATEGORY_EXTERN(media_converter_debug); +#undef GST_CAT_DEFAULT +#define GST_CAT_DEFAULT media_converter_debug + +typedef struct +{ + GstBin bin; + GstElement *audio_conv, *opus_dec, *caps_setter; + GstPad *sink_pad, *src_pad; /* Ghost pads. */ +} AudioConvBin; + +typedef struct +{ + GstBinClass class; +} AudioConvBinClass; + +G_DEFINE_TYPE(AudioConvBin, audio_conv_bin, GST_TYPE_BIN); +#define AUDIO_CONV_BIN_TYPE (audio_conv_bin_get_type()) +#define AUDIO_CONV_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), AUDIO_CONV_BIN_TYPE, AudioConvBin)) +GST_ELEMENT_REGISTER_DEFINE(protonaudioconverterbin, "protonaudioconverterbin", + GST_RANK_MARGINAL + 1, AUDIO_CONV_BIN_TYPE); + +static GstStaticPadTemplate audio_conv_bin_sink_template = GST_STATIC_PAD_TEMPLATE("sink", + GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS("audio/x-wma;")); + +static GstStaticPadTemplate audio_conv_bin_src_template = GST_STATIC_PAD_TEMPLATE("src", + GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS("audio/x-raw, format=S16LE;")); + +static void link_elements(GstElement *src_element, GstElement *sink_element) +{ + if (!gst_element_link(src_element, sink_element)) + GST_ERROR("Failed to link src element %"GST_PTR_FORMAT" to sink element %"GST_PTR_FORMAT".", + src_element, sink_element); +} + +static gboolean audio_conv_bin_sink_event(GstPad *pad, GstObject *parent, GstEvent *event) +{ + AudioConvBin * bin = AUDIO_CONV_BIN(parent); + GstCaps *caps, *rate_caps; + GstStructure *structure; + GstPad *audio_conv_sink; + gint override_rate; + gboolean ret; + + GST_DEBUG_OBJECT(pad, "Got sink event %"GST_PTR_FORMAT".", event); + + switch (event->type) + { + case GST_EVENT_CAPS: + gst_event_parse_caps(event, &caps); + if ((structure = gst_caps_get_structure(caps, 0)) + && gst_structure_get_int(structure, "rate", &override_rate)) + { + rate_caps = gst_caps_new_simple("audio/x-raw", "rate", G_TYPE_INT, override_rate, NULL); + g_object_set(bin->caps_setter, "caps", rate_caps, NULL); + } + else + { + GST_WARNING("Event has no rate."); + } + + /* Forward on to the real pad. */ + audio_conv_sink = gst_element_get_static_pad(bin->audio_conv, "sink"); + ret = gst_pad_send_event(audio_conv_sink, event); + gst_object_unref(audio_conv_sink); + return ret; + + default: + return gst_pad_event_default(pad, parent, event); + } +} + +static void audio_conv_bin_class_init(AudioConvBinClass * klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS(klass); + + gst_element_class_set_metadata(element_class, + "Proton audio converter with rate fixup", + "Codec/Decoder/Audio", + "Converts audio for Proton, fixing up samplerates", + "Andrew Eikum , Ziqing Hui "); + + gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&audio_conv_bin_sink_template)); + gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&audio_conv_bin_src_template)); +} + +static void audio_conv_bin_init(AudioConvBin *bin) +{ + GstElement *element = GST_ELEMENT(bin); + GstPad *sink, *src; + + bin->sink_pad = gst_ghost_pad_new_no_target_from_template("sink", + gst_element_get_pad_template(element, "sink")); + bin->src_pad = gst_ghost_pad_new_no_target_from_template("src", + gst_element_get_pad_template(element, "src")); + gst_pad_set_event_function(bin->sink_pad, GST_DEBUG_FUNCPTR(audio_conv_bin_sink_event)); + + bin->audio_conv = create_element("protonaudioconverter", "protonmediaconverter"); + bin->opus_dec = create_element("opusdec", "base"); + bin->caps_setter = create_element("capssetter", "good"); + + gst_bin_add(GST_BIN(bin), bin->audio_conv); + gst_bin_add(GST_BIN(bin), bin->opus_dec); + gst_bin_add(GST_BIN(bin), bin->caps_setter); + + link_elements(bin->audio_conv, bin->opus_dec); + link_elements(bin->opus_dec, bin->caps_setter); + + sink = gst_element_get_static_pad(bin->audio_conv, "sink"); + src = gst_element_get_static_pad(bin->caps_setter, "src"); + gst_ghost_pad_set_target(GST_GHOST_PAD(bin->sink_pad), sink); + gst_ghost_pad_set_target(GST_GHOST_PAD(bin->src_pad), src); + gst_object_unref(src); + gst_object_unref(sink); + + gst_element_add_pad(element, bin->sink_pad); + gst_element_add_pad(element, bin->src_pad); + + GST_INFO("Initialized AudioConvBin %"GST_PTR_FORMAT": audio_conv %"GST_PTR_FORMAT", opus_dec %"GST_PTR_FORMAT", " + "caps_setter %"GST_PTR_FORMAT", sink_pad %"GST_PTR_FORMAT", src_pad %"GST_PTR_FORMAT".", + bin, bin->audio_conv, bin->opus_dec, bin->caps_setter, bin->sink_pad, bin->src_pad); +} diff --git a/dlls/winegstreamer/media-converter/fossilize.c b/dlls/winegstreamer/media-converter/fossilize.c new file mode 100644 index 00000000000..057825ad5b5 --- /dev/null +++ b/dlls/winegstreamer/media-converter/fossilize.c @@ -0,0 +1,722 @@ +/* + * Copyright 2024 Ziqing Hui 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 + * + * Based on "Fossilize," which is + * Copyright (c) 2018-2019 Hans-Kristian Arntzen + * https://github.com/ValveSoftware/Fossilize/ + */ + +/* This is a read/write implementation of the Fossilize database format. + * + * https://github.com/ValveSoftware/Fossilize/ + * + * That C++ implementation is specific to Vulkan, while this one tries to be generic to store any + * type of data. + */ + +#if 0 +#pragma makedep unix +#endif + +#include "media-converter.h" + +/* Fossilize StreamArchive database format version 6: + * + * The file consists of a header, followed by an unlimited series of "entries". + * + * All multi-byte entities are little-endian. + * + * The file header is as follows: + * + * Field Type Description + * ----- ---- ----------- + * magic_number uint8_t[12] Constant value: "\x81""FOSSILIZEDB" + * version uint32_t StreamArchive version: 6 + * + * + * Each entry follows this format: + * + * Field Type Description + * ----- ---- ----------- + * name unsigned char[40] Application-defined entry identifier, stored in hexadecimal big-endian + * ASCII. Usually N-char tag followed by (40 - N)-char hash. + * size uint32_t Size of the payload as stored in this file. + * flags uint32_t Flags for this entry (e.g. compression). See below. + * crc32 uint32_t CRC32 of the payload as stored in this file. + * full_size uint32_t Size of this payload after decompression. + * payload uint8_t[stored_size] Entry data. + * + * The flags field may contain: + * 0x1: No compression. + * 0x2: Deflate compression. + */ + +#define FOZDB_MIN_COMPAT_VERSION 5 +#define FOZDB_VERSION 6 + +#define FOZDB_COMPRESSION_NONE 1 +#define FOZDB_COMPRESSION_DEFLATE 2 + +#define ENTRY_NAME_SIZE 40 + +#define BUFFER_COPY_BYTES (8 * 1024 * 1024) /* Tuneable. */ + +static const uint8_t FOZDB_MAGIC[] = {0x81, 'F', 'O', 'S', 'S', 'I', 'L', 'I', 'Z', 'E', 'D', 'B'}; + +struct file_header +{ + uint8_t magic[12]; + uint8_t unused1; + uint8_t unused2; + uint8_t unused3; + uint8_t version; +} __attribute__((packed)); + +struct payload_header +{ + uint32_t size; + uint32_t compression; + uint32_t crc; + uint32_t full_size; +} __attribute__((packed)); + +struct payload_entry +{ + struct payload_hash hash; + struct payload_header header; + uint64_t offset; +}; + +static guint hash_func(gconstpointer key) +{ + const struct payload_hash *payload_hash = key; + + return payload_hash->hash[0] + ^ payload_hash->hash[1] + ^ payload_hash->hash[2] + ^ payload_hash->hash[3]; +} + +static gboolean hash_equal(gconstpointer a, gconstpointer b) +{ + return memcmp(a, b, sizeof(struct payload_hash)) == 0; +} + +static bool tag_from_ascii_bytes(uint32_t *tag, const uint8_t *ascii_bytes) +{ + char str[sizeof(*tag) * 2 + 1] = {}; + + memcpy(str, ascii_bytes, sizeof(*tag) * 2); + + *tag = strtoul(str, NULL, 16); + if (errno != 0) + { + GST_ERROR("Failed to convert string \"%s\" to tag. %s.", str, strerror(errno)); + return false; + } + + return true; +} + +static bool hash_from_ascii_bytes(struct payload_hash *hash, const uint8_t *ascii_bytes) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(hash->hash); ++i) + { + uint32_t *hash_part = &hash->hash[ARRAY_SIZE(hash->hash) - 1 - i]; + char str[sizeof(hash_part) * 2 + 1] = {}; + + memcpy(str, ascii_bytes + sizeof(*hash_part) * 2 * i, sizeof(*hash_part) * 2); + + *hash_part = strtoul(str, NULL, 16); + + if (errno != 0) + { + GST_ERROR("Failed to convert string \"%s\" to hash part %u. %s.", str, 4 - i, strerror(errno)); + return false; + } + } + + return true; +} + +static void tag_to_ascii_bytes(uint32_t tag, uint8_t *ascii_bytes) +{ + char buffer[sizeof(tag) * 2 + 1]; + sprintf(buffer, "%08x", tag); + memcpy(ascii_bytes, buffer, sizeof(tag) * 2); +} + +static void hash_to_ascii_bytes(const struct payload_hash *hash, uint8_t *ascii_bytes) +{ + char buffer[sizeof(*hash) * 2 + 1]; + sprintf(buffer, "%08x%08x%08x%08x", hash->hash[3], hash->hash[2], hash->hash[1], hash->hash[0]); + memcpy(ascii_bytes, buffer, sizeof(*hash) * 2); +} + +static void payload_header_from_bytes(struct payload_header *header, const uint8_t *bytes) +{ + header->size = bytes_to_uint32(bytes); + header->compression = bytes_to_uint32(bytes + 4); + header->crc = bytes_to_uint32(bytes + 8); + header->full_size = bytes_to_uint32(bytes + 12); +} + +static int fozdb_read_file_header(struct fozdb *db) +{ + struct file_header header; + + if (!complete_read(db->file, &header, sizeof(header))) + { + GST_ERROR("Failed to read file header."); + return CONV_ERROR_READ_FAILED; + } + if (memcmp(&header.magic, FOZDB_MAGIC, sizeof(FOZDB_MAGIC)) != 0) + { + GST_ERROR("Bad magic."); + return CONV_ERROR_CORRUPT_DATABASE; + } + if (header.version < FOZDB_MIN_COMPAT_VERSION || header.version > FOZDB_VERSION) + { + GST_ERROR("Incompatible version %u.", header.version); + return CONV_ERROR_CORRUPT_DATABASE; + } + + return CONV_OK; +} + +static int fozdb_read_entry_tag_hash_header(struct fozdb *db, + uint32_t *out_tag, struct payload_hash *out_hash, struct payload_header *out_header) +{ + uint8_t entry_name_and_header[ENTRY_NAME_SIZE + sizeof(struct payload_header)]; + struct payload_hash hash; + uint32_t tag; + + if (!complete_read(db->file, entry_name_and_header, sizeof(entry_name_and_header))) + { + GST_ERROR("Failed to read entry name and header."); + return CONV_ERROR_READ_FAILED; + } + + if (!tag_from_ascii_bytes(&tag, entry_name_and_header) + || !hash_from_ascii_bytes(&hash, entry_name_and_header + sizeof(tag) * 2)) + return CONV_ERROR_CORRUPT_DATABASE; + + payload_header_from_bytes(out_header, entry_name_and_header + ENTRY_NAME_SIZE); + + *out_tag = tag; + *out_hash = hash; + return CONV_OK; +} + +static bool fozdb_seek_to_next_entry(struct fozdb *db, struct payload_header *header, bool *truncated) +{ + uint64_t file_size = 0, data_offset = lseek(db->file, 0, SEEK_CUR); + + if (truncated) + *truncated = false; + + get_file_size(db->file, &file_size); + + if (lseek(db->file, header->size, SEEK_CUR) < 0) + { + GST_ERROR("Failed to seek to next entry. %s. " + "Entry data offset %#"PRIx64", size %#x, file size %#"PRIx64".", + strerror(errno), data_offset, header->size, file_size); + return false; + } + + if (file_size && data_offset + header->size > file_size) + { + /* Truncated chunk is not fatal. */ + GST_WARNING("Entry data larger than file, truncating database here. " + "Entry data offset %#"PRIx64", size %#x, file size %#"PRIx64".", + data_offset, header->size, file_size); + if (truncated) + *truncated = true; + } + + return true; +} + +static bool fozdb_write_entry_name(struct fozdb *db, uint32_t tag, struct payload_hash *hash) +{ + uint8_t entry_name[ENTRY_NAME_SIZE]; + + tag_to_ascii_bytes(tag, entry_name); + hash_to_ascii_bytes(hash, entry_name + sizeof(tag) * 2); + + if (!complete_write(db->file, entry_name, sizeof(entry_name))) + { + GST_ERROR("Failed to write entry name."); + return false; + } + + return true; +} + +/* Copy an entry to write_pos. */ +static int fozdb_copy_entry(struct fozdb *db, + struct entry_name *name, struct payload_header *header, uint64_t entry_data_offset) +{ + uint64_t read_offset, entry_end = entry_data_offset + header->size; + ssize_t read_size; + uint8_t *buffer; + + if (lseek(db->file, db->write_pos, SEEK_SET) < 0) + { + GST_ERROR("Failed to seek file to write_pos."); + return CONV_ERROR_SEEK_FAILED; + } + + /* Write entry name. */ + if (!fozdb_write_entry_name(db, name->tag, &name->hash)) + return CONV_ERROR_WRITE_FAILED; + db->write_pos += ENTRY_NAME_SIZE; + + /* Write entry header. */ + if (!complete_write(db->file, header, sizeof(*header))) + { + GST_ERROR("Failed to write entry header."); + return CONV_ERROR_WRITE_FAILED; + } + db->write_pos += sizeof(*header); + + /* Copy entry data. */ + buffer = calloc(1, BUFFER_COPY_BYTES); + for (read_offset = entry_data_offset; read_offset < entry_end; read_offset += read_size) + { + size_t to_read = min(entry_end - read_offset, BUFFER_COPY_BYTES); + + /* Read data from entry. */ + if (lseek(db->file, read_offset, SEEK_SET) < 0) + { + GST_ERROR("Failed to seek to read offset. %s.", strerror(errno)); + free(buffer); + return CONV_ERROR_SEEK_FAILED; + } + if ((read_size = read(db->file, buffer, to_read)) < 0) + { + GST_ERROR("Failed to read entry data. %s.", strerror(errno)); + free(buffer); + return CONV_ERROR_READ_FAILED; + } + if (read_size == 0) + break; + + /* Write data to write_pos. */ + if (lseek(db->file, db->write_pos, SEEK_SET) < 0) + { + GST_ERROR("Failed to seek to write_pos. %s.", strerror(errno)); + free(buffer); + return CONV_ERROR_SEEK_FAILED; + } + if (!complete_write(db->file, buffer, read_size)) + { + GST_ERROR("Failed to write entry data to write_pos."); + free(buffer); + return CONV_ERROR_WRITE_FAILED; + } + db->write_pos += read_size; + } + free(buffer); + + if (lseek(db->file, read_offset, SEEK_SET) < 0) + { + GST_ERROR("Failed to seek to read offset. %s.", strerror(errno)); + return CONV_ERROR_SEEK_FAILED; + } + + return CONV_OK; +} + +int fozdb_create(const char *file_name, int open_flags, bool read_only, uint32_t num_tags, struct fozdb **out) +{ + struct fozdb *db; + size_t i; + int ret; + + GST_DEBUG("file_name %s, open_flags %d, read_only %d, num_tags %u, out %p.", + file_name, open_flags, read_only, num_tags, out); + + db = calloc(1, sizeof(*db)); + + if (!open_file(file_name, open_flags, &db->file)) + { + free(db); + return CONV_ERROR_OPEN_FAILED; + } + + db->file_name = file_name; + db->num_tags = num_tags; + db->read_only = read_only; + + /* Create entry hash tables. */ + db->seen_blobs = calloc(num_tags, sizeof(*db->seen_blobs)); + for (i = 0; i < num_tags; ++i) + db->seen_blobs[i] = g_hash_table_new_full(hash_func, hash_equal, NULL, free); + + /* Load entries. */ + if ((ret = fozdb_prepare(db)) < 0) + { + GST_ERROR("Failed to prepare fozdb, ret %d.", ret); + fozdb_release(db); + return ret; + } + + GST_INFO("Created fozdb %p from %s.", db, file_name); + + *out = db; + return CONV_OK; +} + +void fozdb_release(struct fozdb *db) +{ + int i; + + GST_DEBUG("db %p.", db); + + for (i = 0; i < db->num_tags; ++i) + g_hash_table_destroy(db->seen_blobs[i]); + free(db->seen_blobs); + close(db->file); + free(db); +} + +int fozdb_prepare(struct fozdb *db) +{ + uint64_t file_size; + int ret; + + GST_DEBUG("db %p, file_name %s, read_only %d, num_tags %u.", + db, db->file_name, db->read_only, db->num_tags); + + db->write_pos = lseek(db->file, 0, SEEK_SET); + if (!get_file_size(db->file, &file_size)) + return CONV_ERROR; + + /* New file, write foz header. */ + if (!file_size) + { + struct file_header file_header; + + memcpy(file_header.magic, FOZDB_MAGIC, sizeof(FOZDB_MAGIC)); + file_header.unused1 = 0; + file_header.unused2 = 0; + file_header.unused3 = 0; + file_header.version = FOZDB_VERSION; + + if (!complete_write(db->file, &file_header, sizeof(file_header))) + { + GST_ERROR("Failed to write file header."); + return CONV_ERROR_WRITE_FAILED; + } + db->write_pos = sizeof(file_header); + + return CONV_OK; + } + + /* Read file header. */ + if ((ret = fozdb_read_file_header(db)) < 0) + return ret; + db->write_pos = lseek(db->file, 0, SEEK_CUR); + + /* Read entries to seen_blobs. */ + while (db->write_pos < file_size) + { + struct payload_entry entry, *table_entry; + uint32_t tag; + + /* Read an entry. */ + if ((ret = fozdb_read_entry_tag_hash_header(db, &tag, &entry.hash, &entry.header) < 0)) + return ret; + entry.offset = lseek(db->file, 0, SEEK_CUR); + + if (!fozdb_seek_to_next_entry(db, &entry.header, NULL)) + return CONV_ERROR_SEEK_FAILED; + db->write_pos = lseek(db->file, 0, SEEK_CUR); + + GST_INFO("Got entry: tag %u, hash %s, offset %#"PRIx64", size %#x, crc %#x.", + tag, format_hash(&entry.hash), entry.offset, entry.header.size, entry.header.crc); + + /* Insert entry to hash table. */ + if (tag >= db->num_tags) + { + GST_WARNING("Invalid tag %u.", tag); + + /* Ignore unknown tags for read-only DBs. */ + if (db->read_only) + continue; + else + return CONV_ERROR_INVALID_TAG; + } + table_entry = calloc(1, sizeof(*table_entry)); + *table_entry = entry; + g_hash_table_insert(db->seen_blobs[tag], &table_entry->hash, table_entry); + } + + return CONV_OK; +} + +bool fozdb_has_entry(struct fozdb *db, uint32_t tag, struct payload_hash *hash) +{ + if (tag >= db->num_tags) + return false; + return g_hash_table_contains(db->seen_blobs[tag], hash); +} + +int fozdb_entry_size(struct fozdb *db, uint32_t tag, struct payload_hash *hash, uint32_t *size) +{ + struct payload_entry *entry; + + if (tag >= db->num_tags) + return CONV_ERROR_INVALID_TAG; + if (!(entry = g_hash_table_lookup(db->seen_blobs[tag], hash))) + return CONV_ERROR_ENTRY_NOT_FOUND; + + *size = entry->header.full_size; + + return CONV_OK; +} + +void fozdb_iter_tag(struct fozdb *db, uint32_t tag, GHashTableIter *iter) +{ + if (tag > db->num_tags) + { + GST_ERROR("Invalid tag %u.", tag); + return; + } + g_hash_table_iter_init(iter, db->seen_blobs[tag]); +} + +int fozdb_read_entry_data(struct fozdb *db, uint32_t tag, struct payload_hash *hash, + uint64_t offset, uint8_t *buffer, size_t size, size_t *read_size, bool with_crc) +{ + struct payload_entry *entry; + size_t to_copy; + + GST_DEBUG("db %p, file_name %s, tag %u, hash %s, offset %#"PRIx64", buffer %p, size %zu, read_size %p, with_crc %d.", + db, db->file_name, tag, format_hash(hash), offset, buffer, size, read_size, with_crc); + + if (tag >= db->num_tags) + return CONV_ERROR_INVALID_TAG; + if (!(entry = g_hash_table_lookup(db->seen_blobs[tag], hash))) + return CONV_ERROR_ENTRY_NOT_FOUND; + + if (entry->header.compression != FOZDB_COMPRESSION_NONE) + return CONV_ERROR_NOT_IMPLEMENTED; + + if (offset >= entry->header.full_size) + return CONV_OK; + + if (lseek(db->file, entry->offset + offset, SEEK_SET) < 0) + return CONV_ERROR_SEEK_FAILED; + + to_copy = min(entry->header.full_size - offset, size); + if (!complete_read(db->file, buffer, to_copy)) + { + GST_ERROR("Failed to read entry data."); + return CONV_ERROR_READ_FAILED; + } + *read_size = to_copy; + + if (entry->header.crc != 0 && with_crc && entry->header.crc != crc32(0, buffer, to_copy)) + { + GST_ERROR("Wrong check sum."); + return CONV_ERROR_WRONG_CHECKSUM; + } + + return CONV_OK; +} + +int fozdb_write_entry(struct fozdb *db, uint32_t tag, struct payload_hash *hash, + void *data_src, data_read_callback read_callback, bool with_crc) +{ + struct payload_header header; + struct payload_entry *entry; + off_t header_offset; + uint32_t size = 0; + size_t read_size; + uint8_t *buffer; + uint64_t offset; + int ret; + + GST_DEBUG("db %p, file_name %s, tag %u, hash %s, data_src %p, read_callback %p, with_crc %d.", + db, db->file_name, tag, format_hash(hash), data_src, read_callback, with_crc); + + if (tag >= db->num_tags) + { + GST_ERROR("Invalid tag %u.", tag); + return CONV_ERROR_INVALID_TAG; + } + if (fozdb_has_entry(db, tag, hash)) + return CONV_OK; + + if (lseek(db->file, db->write_pos, SEEK_SET) < 0) + { + GST_ERROR("Failed to seek file to write_pos %#"PRIx64".", db->write_pos); + return CONV_ERROR_SEEK_FAILED; + } + + /* Write entry name. */ + if (!fozdb_write_entry_name(db, tag, hash)) + return CONV_ERROR_WRITE_FAILED; + + /* Write payload header first. */ + header_offset = lseek(db->file, 0, SEEK_CUR); + header.size = UINT32_MAX; /* Will be filled later. */ + header.compression = FOZDB_COMPRESSION_NONE; + header.crc = 0; /* Will be filled later. */ + header.full_size = UINT32_MAX; /* Will be filled later. */ + if (!complete_write(db->file, &header, sizeof(header))) + { + GST_ERROR("Failed to write entry header."); + return CONV_ERROR_WRITE_FAILED; + } + offset = lseek(db->file, 0, SEEK_CUR); + + /* Write data. */ + buffer = calloc(1, BUFFER_COPY_BYTES); + while ((ret = read_callback(data_src, buffer, BUFFER_COPY_BYTES, &read_size)) == CONV_OK) + { + if (size + read_size > UINT32_MAX) + { + GST_ERROR("Data too large. Fossilize format only supports 4 GB entries."); + free(buffer); + return CONV_ERROR; + } + + size += read_size; + if (!complete_write(db->file, buffer, read_size)) + { + GST_ERROR("Failed to write entry data."); + free(buffer); + return CONV_ERROR_WRITE_FAILED; + } + + if (with_crc) + header.crc = crc32(header.crc, buffer, read_size); + } + db->write_pos = lseek(db->file, 0, SEEK_CUR); + free(buffer); + if (ret != CONV_ERROR_DATA_END) + { + GST_ERROR("Failed to read data from data src, ret %d.", ret); + return ret; + } + + /* Seek back and fill in the size to header. */ + if (lseek(db->file, header_offset, SEEK_SET) < 0) + { + GST_ERROR("Failed to seek back to entry header. %s.", strerror(errno)); + return CONV_ERROR_SEEK_FAILED; + } + header.size = size; + header.full_size = size; + if (!complete_write(db->file, &header, sizeof(header))) + { + GST_ERROR("Failed to write entry header."); + return CONV_ERROR_WRITE_FAILED; + } + + /* Success. Record entry and exit. */ + entry = calloc(1, sizeof(*entry)); + entry->header = header; + entry->hash = *hash; + entry->offset = offset; + g_hash_table_insert(db->seen_blobs[tag], &entry->hash, entry); + + GST_INFO("Wrote entry: tag %u, hash %s, offset %#"PRIx64", size %#x, crc %#x.", + tag, format_hash(&entry->hash), entry->offset, entry->header.size, entry->header.crc); + + return CONV_OK; +} + +/* Rewrites the database, discarding entries listed. */ +int fozdb_discard_entries(struct fozdb *db, GList *to_discard_names) +{ + uint8_t entry_name_and_header[ENTRY_NAME_SIZE + sizeof(struct payload_header)]; + uint64_t file_size; + int i, ret; + + GST_DEBUG("db %p, file_name %s, to_discard_entries %p.", db, db->file_name, to_discard_names); + + /* Rewind the file and clear the entry tables. */ + if (lseek(db->file, 0, SEEK_SET) < 0) + { + GST_ERROR("Failed to seek to file start. %s.", strerror(errno)); + return CONV_ERROR_SEEK_FAILED; + } + for (i = 0; i < db->num_tags; ++i) + g_hash_table_remove_all(db->seen_blobs[i]); + + /* Read file header. */ + if ((ret = fozdb_read_file_header(db)) < 0) + return ret; + db->write_pos = lseek(db->file, 0, SEEK_CUR); + + /* Read each entry and see if it should be discarded. */ + if (!get_file_size(db->file, &file_size)) + return CONV_ERROR; + while (lseek(db->file, 0, SEEK_CUR) < file_size) + { + struct payload_header header; + uint64_t entry_data_offset; + struct entry_name name; + bool truncated; + + if ((ret = fozdb_read_entry_tag_hash_header(db, &name.tag, &name.hash, &header) < 0)) + return CONV_ERROR_READ_FAILED; + entry_data_offset = lseek(db->file, 0, SEEK_CUR); + + /* Check if entry should be discarded. */ + if (g_list_find_custom(to_discard_names, &name, entry_name_compare)) + { + if (!fozdb_seek_to_next_entry(db, &header, &truncated)) + return CONV_ERROR_SEEK_FAILED; + if (truncated) + break; + } + else + { + if (db->write_pos == entry_data_offset - sizeof(entry_name_and_header)) + { + /* If we haven't dropped any chunks, we can just skip it rather than rewrite it. */ + if (!fozdb_seek_to_next_entry(db, &header, &truncated)) + return CONV_ERROR_SEEK_FAILED; + if (truncated) + break; + db->write_pos = lseek(db->file, 0, SEEK_CUR); + } + else + { + /* We're offset, so we have to rewrite. */ + if ((ret = fozdb_copy_entry(db, &name, &header, entry_data_offset)) < 0) + return ret; + } + } + } + + if (ftruncate(db->file, db->write_pos) < 0) + { + GST_ERROR("Failed to truncate file. %s.", strerror(errno)); + return CONV_ERROR; + } + + return fozdb_prepare(db); +} diff --git a/dlls/winegstreamer/media-converter/lib.c b/dlls/winegstreamer/media-converter/lib.c new file mode 100644 index 00000000000..61d7fe8605e --- /dev/null +++ b/dlls/winegstreamer/media-converter/lib.c @@ -0,0 +1,329 @@ +/* + * Copyright 2024 Ziqing Hui 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 "media-converter.h" + +GST_ELEMENT_REGISTER_DECLARE(protonvideoconverter); +GST_ELEMENT_REGISTER_DECLARE(protonaudioconverter); +GST_ELEMENT_REGISTER_DECLARE(protonaudioconverterbin); + +GST_DEBUG_CATEGORY(media_converter_debug); + +static void get_dirname(const char *path, char *result) +{ + size_t i; + + for (i = strlen(path) - 1; i > 0; --i) + { + if (path[i] == '\\' || path[i] == '/') + break; + } + + memcpy(result, path, i); + result[i] = 0; +} + +static void path_concat(char *result, const char *a, const char *b) +{ + size_t a_len, b_offset; + + a_len = strlen(a); + b_offset = a_len; + memcpy(result, a, a_len); + + if (result[a_len - 1] != '/') + { + result[a_len] = '/'; + ++b_offset; + } + + strcpy(result + b_offset, b); +} + +static bool create_all_dir(const char *dir) +{ + char *ptr, buffer[4096] = {0}; + + strcpy(buffer, dir); + if (buffer[strlen(dir) - 1] != '/') + buffer[strlen(dir)] = '/'; + + ptr = strchr(buffer + 1, '/'); + while (ptr) + { + *ptr = '\0'; + + if (mkdir(buffer, 0777) < 0) + { + if (errno != EEXIST) + { + GST_ERROR("Failed to make directory %s. %s.", buffer, strerror(errno)); + return false; + } + } + *ptr = '/'; + + ptr = strchr(ptr + 1, '/'); + } + return true; +} + +static int create_file(const char *file_name) +{ + int fd, ret = CONV_OK; + char dir[4096]; + + get_dirname(file_name, dir); + if (access(dir, F_OK) < 0 && !create_all_dir(dir)) + return CONV_ERROR_PATH_NOT_FOUND; + + if ((fd = open(file_name, O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) < 0) + { + GST_WARNING("Failed to open file %s with O_CREAT. %s.", file_name, strerror(errno)); + return CONV_ERROR_OPEN_FAILED; + } + + if (futimens(fd, NULL) < 0) + { + GST_WARNING("Failed to set file %s timestamps. %s.", file_name, strerror(errno)); + ret = CONV_ERROR; + } + + close(fd); + + return ret; +} + +bool open_file(const char *file_name, int open_flags, int *out_fd) +{ + int fd; + + if ((fd = open(file_name, open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) < 0) + { + GST_WARNING("Failed to open %s with flags %d. %s.", file_name, open_flags, strerror(errno)); + return false; + } + + *out_fd = fd; + return true; +} + +bool get_file_size(int fd, uint64_t *file_size) +{ + struct stat file_info; + + if (fstat(fd, &file_info) < 0) + { + GST_WARNING("Failed to fstat fd %d. %s.", fd, strerror(errno)); + return false; + } + + *file_size = file_info.st_size; + return true; +} + +bool complete_read(int file, void *buffer, size_t size) +{ + size_t current_read = 0; + ssize_t read_size; + errno = 0; + + while (current_read < size) + { + read_size = read(file, ((char *)buffer) + current_read, size - current_read); + if(read_size <= 0) + { + if(errno != EINTR && errno != EAGAIN) + return false; + } + else + { + current_read += read_size; + } + } + + return current_read == size; +} + +bool complete_write(int file, const void *buffer, size_t size) +{ + size_t current_write = 0; + ssize_t write_size; + errno = 0; + + while (current_write < size) + { + write_size = write(file, (const char *)buffer + current_write, size - current_write); + if(write_size < 0) + { + if(errno != EINTR && errno != EAGAIN) + return false; + } + else + { + current_write += write_size; + } + } + + return current_write == size; +} + +uint32_t crc32(uint32_t crc, const uint8_t *ptr, size_t buf_len) +{ + static const uint32_t s_crc_table[256] = + { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, + 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, + 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, + 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, + 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, + 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, + 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, + 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, + 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, + 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, + 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, + 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, + 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, + 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, + 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, + 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, + 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, + 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, + 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, + 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, + 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, + 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, + 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, + 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, + 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, + }; + uint32_t crc32 = (uint32_t)crc ^ 0xffffffff; + const uint8_t *buffer = (const uint8_t *)ptr; + + while (buf_len >= 4) + { + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ buffer[0]) & 0xff]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ buffer[1]) & 0xff]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ buffer[2]) & 0xff]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ buffer[3]) & 0xff]; + buffer += 4; + buf_len -= 4; + } + + while (buf_len) + { + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ buffer[0]) & 0xff]; + ++buffer; + --buf_len; + } + + return ~crc32; +} + +int create_placeholder_file(const char *file_name) +{ + const char *shader_path; + char path[1024]; + int ret; + + if ((shader_path = getenv("STEAM_COMPAT_SHADER_PATH"))) + { + path_concat(path, shader_path, file_name); + if ((ret = create_file(path)) < 0) + GST_ERROR("Failed to create %s file, ret %d.", file_name, ret); + } + else + { + GST_ERROR("Env STEAM_COMPAT_SHADER_PATH not set."); + ret = CONV_ERROR_ENV_NOT_SET; + } + + return ret; +} + +int dump_fozdb_open(struct dump_fozdb *db, bool create, const char *file_path_env, int num_tags) +{ + char *dump_file_path; + + if (db->fozdb) + return CONV_OK; + + if (!(dump_file_path = getenv(file_path_env))) + { + GST_ERROR("Env %s not set.", file_path_env); + return CONV_ERROR_ENV_NOT_SET; + } + + if (create) + create_file(dump_file_path); + + return fozdb_create(dump_file_path, O_RDWR, false, num_tags, &db->fozdb); +} + +void dump_fozdb_close(struct dump_fozdb *db) +{ + if (db->fozdb) + { + fozdb_release(db->fozdb); + db->fozdb = NULL; + } +} + +bool media_converter_init(void) +{ + GST_DEBUG_CATEGORY_INIT(media_converter_debug, + "protonmediaconverter", GST_DEBUG_FG_YELLOW, "Proton media converter"); + + if (!GST_ELEMENT_REGISTER(protonvideoconverter, NULL)) + { + GST_ERROR("Failed to register protonvideoconverter."); + return false; + } + + if (!GST_ELEMENT_REGISTER(protonaudioconverter, NULL)) + { + GST_ERROR("Failed to register protonaudioconverter."); + return false; + } + + if (!GST_ELEMENT_REGISTER(protonaudioconverterbin, NULL)) + { + GST_ERROR("Failed to register protonaudioconverterbin."); + return false; + } + + return true; +} diff --git a/dlls/winegstreamer/media-converter/media-converter.h b/dlls/winegstreamer/media-converter/media-converter.h new file mode 100644 index 00000000000..51b54147806 --- /dev/null +++ b/dlls/winegstreamer/media-converter/media-converter.h @@ -0,0 +1,287 @@ +/* + * Copyright 2024 Ziqing Hui 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 + */ + +#ifndef __MEDIA_CONVERTER_H__ +#define __MEDIA_CONVERTER_H__ + +#define _FILE_OFFSET_BITS 64 + +#include +#include +#include +#include + +#include "unix_private.h" + +GST_DEBUG_CATEGORY_EXTERN(media_converter_debug); +#undef GST_CAT_DEFAULT +#define GST_CAT_DEFAULT media_converter_debug + +typedef int (*data_read_callback)(void *data_reader, uint8_t *buffer, size_t size, size_t *read_size); + +/* Changing this will invalidate the cache. You MUST clear it. */ +#define HASH_SEED 0x4AA61F63 + +enum conv_ret +{ + CONV_OK = 0, + CONV_ERROR = -1, + CONV_ERROR_NOT_IMPLEMENTED = -2, + CONV_ERROR_INVALID_ARGUMENT = -3, + CONV_ERROR_OPEN_FAILED = -4, + CONV_ERROR_READ_FAILED = -5, + CONV_ERROR_WRITE_FAILED = -6, + CONV_ERROR_SEEK_FAILED = -7, + CONV_ERROR_CORRUPT_DATABASE = -8, + CONV_ERROR_WRONG_CHECKSUM = -9, + CONV_ERROR_ENTRY_NOT_FOUND = -10, + CONV_ERROR_ENV_NOT_SET = -11, + CONV_ERROR_PATH_NOT_FOUND = -12, + CONV_ERROR_INVALID_TAG = -13, + CONV_ERROR_DATA_END = -14, +}; + +struct murmur3_x64_128_state +{ + uint32_t seed; + uint64_t h1; + uint64_t h2; + size_t processed; +}; + +struct murmur3_x86_128_state +{ + uint32_t seed; + uint32_t h1; + uint32_t h2; + uint32_t h3; + uint32_t h4; + size_t processed; +}; + +struct bytes_reader +{ + const uint8_t *data; + size_t size; + size_t offset; +}; + +struct gst_buffer_reader +{ + GstBuffer *buffer; /* No ref here, no need to unref. */ + size_t offset; +}; + +struct payload_hash +{ + uint32_t hash[4]; +}; + +struct entry_name +{ + uint32_t tag; + struct payload_hash hash; +}; + +struct dump_fozdb +{ + pthread_mutex_t mutex; + struct fozdb *fozdb; + bool already_cleaned; +}; + +struct fozdb +{ + const char *file_name; + int file; + bool read_only; + uint64_t write_pos; + GHashTable **seen_blobs; + uint32_t num_tags; +}; + +/* lib.c. */ +extern bool open_file(const char *file_name, int open_flags, int *out_fd); +extern bool get_file_size(int fd, uint64_t *file_size); +extern bool complete_read(int file, void *buffer, size_t size); +extern bool complete_write(int file, const void *buffer, size_t size); +extern uint32_t crc32(uint32_t crc, const uint8_t *ptr, size_t buf_len); +extern int create_placeholder_file(const char *file_name); +extern int dump_fozdb_open(struct dump_fozdb *db, bool create, const char *file_path_env, int num_tags); +extern void dump_fozdb_close(struct dump_fozdb *db); + +/* murmur3.c. */ +extern void murmur3_x64_128_state_init(struct murmur3_x64_128_state *state, uint32_t seed); +extern void murmur3_x64_128_state_reset(struct murmur3_x64_128_state *state); +extern bool murmur3_x64_128_full(void *data_src, data_read_callback read_callback, + struct murmur3_x64_128_state* state, void *out); +extern bool murmur3_x64_128(void *data_src, data_read_callback read_callback, uint32_t seed, void *out); +extern void murmur3_x86_128_state_init(struct murmur3_x86_128_state *state, uint32_t seed); +extern void murmur3_x86_128_state_reset(struct murmur3_x86_128_state *state); +extern bool murmur3_x86_128_full(void *data_src, data_read_callback read_callback, + struct murmur3_x86_128_state* state, void *out); +extern bool murmur3_x86_128(void *data_src, data_read_callback read_callback, uint32_t seed, void *out); +#ifdef __x86_64__ +#define murmur3_128_state murmur3_x64_128_state +#define murmur3_128_state_init murmur3_x64_128_state_init +#define murmur3_128_state_reset murmur3_x64_128_state_reset +#define murmur3_128_full murmur3_x64_128_full +#define murmur3_128 murmur3_x64_128 +#elif defined(__i386__) +#define murmur3_128_state murmur3_x86_128_state +#define murmur3_128_state_init murmur3_x86_128_state_init +#define murmur3_128_state_reset murmur3_x86_128_state_reset +#define murmur3_128_full murmur3_x86_128_full +#define murmur3_128 murmur3_x86_128 +#endif /* __x86_64__ */ + +/* fossilize.c. */ +extern int fozdb_create(const char *file_name, int open_flags, bool read_only, uint32_t num_tags, struct fozdb **out); +extern void fozdb_release(struct fozdb *db); +extern int fozdb_prepare(struct fozdb *db); +extern bool fozdb_has_entry(struct fozdb *db, uint32_t tag, struct payload_hash *hash); +extern int fozdb_entry_size(struct fozdb *db, uint32_t tag, struct payload_hash *hash, uint32_t *size); +extern void fozdb_iter_tag(struct fozdb *db, uint32_t tag, GHashTableIter *iter); +extern int fozdb_read_entry_data(struct fozdb *db, uint32_t tag, struct payload_hash *hash, + uint64_t offset, uint8_t *buffer, size_t size, size_t *read_size, bool with_crc); +extern int fozdb_write_entry(struct fozdb *db, uint32_t tag, struct payload_hash *hash, + void *data_src, data_read_callback read_callback, bool with_crc); +extern int fozdb_discard_entries(struct fozdb *db, GList *to_discard_entries); + +static inline bool option_enabled(const char *env) +{ + const char *env_var; + + if (!(env_var = getenv(env))) + return false; + + return strcmp(env_var, "0") != 0; +} + +static inline bool discarding_disabled(void) +{ + return option_enabled("MEDIACONV_DONT_DISCARD"); +} + +static inline const char *format_hash(struct payload_hash *hash) +{ + int hash_str_size = 2 + sizeof(*hash) * 2 + 1; + static char buffer[1024] = {}; + static int offset = 0; + char *ret; + + if (offset + hash_str_size > sizeof(buffer)) + offset = 0; + + ret = buffer + offset; + sprintf(ret, "0x%08x%08x%08x%08x", hash->hash[3], hash->hash[2], hash->hash[1], hash->hash[0]); + offset += hash_str_size; + + return ret; +} + +static inline void bytes_reader_init(struct bytes_reader *reader, const uint8_t *data, size_t size) +{ + reader->data = data; + reader->size = size; + reader->offset = 0; +} + +static inline int bytes_reader_read(void *data_reader, uint8_t *buffer, size_t size, size_t *read_size) +{ + struct bytes_reader *reader = data_reader; + size_t data_size, to_copy; + + if (!size) + { + *read_size = 0; + return CONV_OK; + } + + if (!(data_size = reader->size - reader->offset)) + return CONV_ERROR_DATA_END; + + to_copy = min(data_size, size); + memcpy(buffer, reader->data + reader->offset, to_copy); + reader->offset += to_copy; + + *read_size = to_copy; + return CONV_OK; +} + +static inline void gst_buffer_reader_init(struct gst_buffer_reader *reader, GstBuffer *buffer) +{ + reader->buffer = buffer; /* No ref here, so no need to unref. */ + reader->offset = 0; +} + +static inline int gst_buffer_reader_read(void *data_reader, uint8_t *buffer, size_t size, size_t *read_size) +{ + struct gst_buffer_reader *reader = data_reader; + + if (!size) + { + *read_size = 0; + return CONV_OK; + } + + *read_size = gst_buffer_extract(reader->buffer, reader->offset, buffer, size); + reader->offset += *read_size; + if (!*read_size) + return CONV_ERROR_DATA_END; + + return CONV_OK; +} + +static inline bool file_exists(const char *file_path) +{ + if (!file_path) + return false; + return access(file_path, F_OK) == 0; +} + +static inline struct entry_name *entry_name_create(uint32_t tag, struct payload_hash *hash) +{ + struct entry_name *entry = calloc(1, sizeof(*entry)); + entry->tag = tag; + entry->hash = *hash; + return entry; +} + +static inline gint entry_name_compare(const void *a, const void *b) +{ + return memcmp(a, b, sizeof(struct entry_name)); +} + +static inline uint32_t bytes_to_uint32(const uint8_t *bytes) +{ + return ((uint32_t)bytes[0] << 0) + | ((uint32_t)bytes[1] << 8) + | ((uint32_t)bytes[2] << 16) + | ((uint32_t)bytes[3] << 24); +} + +static inline void payload_hash_from_bytes(struct payload_hash *hash, uint8_t *bytes) +{ + hash->hash[0] = bytes_to_uint32(bytes + 0); + hash->hash[1] = bytes_to_uint32(bytes + 4); + hash->hash[2] = bytes_to_uint32(bytes + 8); + hash->hash[3] = bytes_to_uint32(bytes + 12); +} + +#endif /* __MEDIA_CONVERTER_H__ */ diff --git a/dlls/winegstreamer/media-converter/murmur3.c b/dlls/winegstreamer/media-converter/murmur3.c new file mode 100644 index 00000000000..014345e61d5 --- /dev/null +++ b/dlls/winegstreamer/media-converter/murmur3.c @@ -0,0 +1,371 @@ +/* + * Copyright 2024 Ziqing Hui 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 "media-converter.h" + +static uint64_t rotl64(uint64_t value, uint8_t shift) +{ + return (value << shift) | (value >> (64 - shift)); +} + +static uint32_t rotl32(uint32_t value, uint8_t shift) +{ + return (value << shift) | (value >> (32 - shift)); +} + +static uint64_t fmix64(uint64_t h) +{ + const uint64_t c1 = 0xff51afd7ed558ccd; + const uint64_t c2 = 0xc4ceb9fe1a85ec53; + const uint32_t r = 33; + + h ^= h >> r; + h *= c1; + h ^= h >> r; + h *= c2; + h ^= h >> r; + + return h; +} + +static uint32_t fmix32 (uint32_t h) +{ + const uint32_t c1 = 0x85ebca6b; + const uint32_t c2 = 0xc2b2ae35; + const uint32_t r1 = 16; + const uint32_t r2 = 13; + + h ^= h >> r1; + h *= c1; + h ^= h >> r2; + h *= c2; + h ^= h >> r1; + + return h; +} + +void murmur3_x64_128_state_init(struct murmur3_x64_128_state *state, uint32_t seed) +{ + state->seed = seed; + state->h1 = seed; + state->h2 = seed; + state->processed = 0; +} + +void murmur3_x64_128_state_reset(struct murmur3_x64_128_state *state) +{ + state->h1 = state->seed; + state->h2 = state->seed; + state->processed = 0; +} + +bool murmur3_x64_128_full(void *data_reader, data_read_callback read_callback, + struct murmur3_x64_128_state* state, void *out) +{ + const uint64_t c1 = 0x87c37b91114253d5, c2 = 0x4cf5ad432745937f, c3 = 0x52dce729, c4 = 0x38495ab5, m = 5; + size_t read_size, processed = state->processed; + const uint32_t r1 = 27, r2 = 31, r3 = 33; + uint64_t h1 = state->h1, h2 = state->h2; + uint8_t buffer[16] = {0}; + uint64_t k1, k2; + int ret; + + while ((ret = read_callback(data_reader, buffer, sizeof(buffer), &read_size)) == CONV_OK) + { + processed += read_size; + + if (read_size == 16) + { + k1 = *(uint64_t *)&buffer[0]; + k2 = *(uint64_t *)&buffer[8]; + + k1 *= c1; + k1 = rotl64(k1, r2); + k1 *= c2; + h1 ^= k1; + h1 = rotl64(h1, r1); + h1 += h2; + h1 = h1 * m + c3; + + k2 *= c2; + k2 = rotl64(k2, r3); + k2 *= c1; + h2 ^= k2; + h2 = rotl64(h2, r2); + h2 += h1; + h2 = h2 * m + c4; + } + else + { + k1 = 0; + k2 = 0; + + switch (read_size) + { + case 15: + k2 ^= ((uint64_t)buffer[14]) << 48; + case 14: + k2 ^= ((uint64_t)buffer[13]) << 40; + case 13: + k2 ^= ((uint64_t)buffer[12]) << 32; + case 12: + k2 ^= ((uint64_t)buffer[11]) << 24; + case 11: + k2 ^= ((uint64_t)buffer[10]) << 16; + case 10: + k2 ^= ((uint64_t)buffer[9]) << 8; + case 9: + k2 ^= ((uint64_t)buffer[8]) << 0; + k2 *= c2; + k2 = rotl64(k2, r3); + k2 *= c1; + h2 ^= k2; + case 8: + k1 ^= ((uint64_t)buffer[7]) << 56; + case 7: + k1 ^= ((uint64_t)buffer[6]) << 48; + case 6: + k1 ^= ((uint64_t)buffer[5]) << 40; + case 5: + k1 ^= ((uint64_t)buffer[4]) << 32; + case 4: + k1 ^= ((uint64_t)buffer[3]) << 24; + case 3: + k1 ^= ((uint64_t)buffer[2]) << 16; + case 2: + k1 ^= ((uint64_t)buffer[1]) << 8; + case 1: + k1 ^= ((uint64_t)buffer[0]) << 0; + k1 *= c1; + k1 = rotl64(k1, r2); + k1 *= c2; + h1 ^= k1; + } + } + } + + if (ret != CONV_ERROR_DATA_END) + return false; + + state->processed = processed; + state->h1 = h1; + state->h2 = h2; + + h1 ^= (uint64_t)processed; + h2 ^= (uint64_t)processed; + h1 += h2; + h2 += h1; + h1 = fmix64(h1); + h2 = fmix64(h2); + h1 += h2; + h2 += h1; + + ((uint64_t *)out)[0] = h1; + ((uint64_t *)out)[1] = h2; + + return true; +} + +bool murmur3_x64_128(void *data_src, data_read_callback read_callback, uint32_t seed, void *out) +{ + struct murmur3_x64_128_state state; + murmur3_x64_128_state_init(&state, seed); + return murmur3_x64_128_full(data_src, read_callback, &state, out); +} + +void murmur3_x86_128_state_init(struct murmur3_x86_128_state *state, uint32_t seed) +{ + state->seed = seed; + state->h1 = seed; + state->h2 = seed; + state->h3 = seed; + state->h4 = seed; + state->processed = 0; +} + +void murmur3_x86_128_state_reset(struct murmur3_x86_128_state *state) +{ + state->h1 = state->seed; + state->h2 = state->seed; + state->h3 = state->seed; + state->h4 = state->seed; + state->processed = 0; +} + +bool murmur3_x86_128_full(void *data_reader, data_read_callback read_callback, + struct murmur3_x86_128_state *state, void *out) +{ + const uint32_t c1 = 0x239b961b, c2 = 0xab0e9789, c3 = 0x38b34ae5, c4 = 0xa1e38b93; + const uint32_t c5 = 0x561ccd1b, c6 = 0x0bcaa747, c7 = 0x96cd1c35, c8 = 0x32ac3b17; + uint32_t h1 = state->h1, h2 = state->h2, h3 = state->h3, h4 = state->h4; + size_t read_size, processed = state->processed; + unsigned char buffer[16] = {0}; + uint64_t k1, k2, k3, k4; + const uint32_t m = 5; + int ret; + + while ((ret = read_callback(data_reader, buffer, sizeof(buffer), &read_size)) == CONV_OK) + { + processed += read_size; + + if (read_size == 16) + { + k1 = *(uint32_t*)&buffer[0]; + k2 = *(uint32_t*)&buffer[4]; + k3 = *(uint32_t*)&buffer[8]; + k4 = *(uint32_t*)&buffer[12]; + + k1 *= c1; + k1 = rotl32(k1, 15); + k1 *= c2; + h1 ^= k1; + h1 = rotl32(h1, 19); + h1 += h2; + h1 = h1 * m + c5; + + k2 *= c2; + k2 = rotl32(k2, 16); + k2 *= c3; + h2 ^= k2; + h2 = rotl32(h2, 17); + h2 += h3; + h2 = h2 * m + c6; + + k3 *= c3; + k3 = rotl32(k3, 17); + k3 *= c4; + h3 ^= k3; + h3 = rotl32(h3, 15); + h3 += h4; + h3 = h3 * m + c7; + + k4 *= c4; + k4 = rotl32(k4, 18); + k4 *= c1; + h4 ^= k4; + h4 = rotl32(h4, 13); + h4 += h1; + h4 = h4 * m + c8; + } + else + { + k1 = 0; + k2 = 0; + k3 = 0; + k4 = 0; + + switch (read_size) + { + case 15: + k4 ^= buffer[14] << 16; + case 14: + k4 ^= buffer[13] << 8; + case 13: + k4 ^= buffer[12] << 0; + k4 *= c4; + k4 = rotl32(k4,18); + k4 *= c1; + h4 ^= k4; + case 12: + k3 ^= buffer[11] << 24; + case 11: + k3 ^= buffer[10] << 16; + case 10: + k3 ^= buffer[9] << 8; + case 9: + k3 ^= buffer[8] << 0; + k3 *= c3; + k3 = rotl32(k3, 17); + k3 *= c4; + h3 ^= k3; + case 8: + k2 ^= buffer[7] << 24; + case 7: + k2 ^= buffer[6] << 16; + case 6: + k2 ^= buffer[5] << 8; + case 5: + k2 ^= buffer[4] << 0; + k2 *= c2; + k2 = rotl32(k2, 16); + k2 *= c3; + h2 ^= k2; + case 4: + k1 ^= buffer[3] << 24; + case 3: + k1 ^= buffer[2] << 16; + case 2: + k1 ^= buffer[1] << 8; + case 1: + k1 ^= buffer[0] << 0; + k1 *= c1; + k1 = rotl32(k1, 15); + k1 *= c2; + h1 ^= k1; + } + } + } + + if (ret != CONV_ERROR_DATA_END) + return false; + + state->processed = processed; + state->h1 = h1; + state->h2 = h2; + state->h3 = h3; + state->h4 = h4; + + h1 ^= processed; + h2 ^= processed; + h3 ^= processed; + h4 ^= processed; + h1 += h2; + h1 += h3; + h1 += h4; + h2 += h1; + h3 += h1; + h4 += h1; + h1 = fmix32(h1); + h2 = fmix32(h2); + h3 = fmix32(h3); + h4 = fmix32(h4); + h1 += h2; + h1 += h3; + h1 += h4; + h2 += h1; + h3 += h1; + h4 += h1; + + ((uint32_t*)out)[0] = h1; + ((uint32_t*)out)[1] = h2; + ((uint32_t*)out)[2] = h3; + ((uint32_t*)out)[3] = h4; + + return true; +} + +bool murmur3_x86_128(void *data_src, data_read_callback read_callback, uint32_t seed, void *out) +{ + struct murmur3_x86_128_state state; + murmur3_x86_128_state_init(&state, seed); + return murmur3_x86_128_full(data_src, read_callback, &state, out); +} diff --git a/dlls/winegstreamer/media-converter/videoconv.c b/dlls/winegstreamer/media-converter/videoconv.c new file mode 100644 index 00000000000..b91aa96f1bf --- /dev/null +++ b/dlls/winegstreamer/media-converter/videoconv.c @@ -0,0 +1,938 @@ +/* + * Copyright 2024 Ziqing Hui 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 + */ + +/* Algorithm + * --------- + * + * Nicely, both Quartz and Media Foundation allow us random access to the entire data stream. So we + * can easily hash the entire incoming stream and substitute it with our Ogg Theora video. If there + * is a cache miss, then we dump the entire incoming stream. In case of a cache hit, we dump + * nothing. + * + * Incoming video data is stored in the video.foz Fossilize database. + * + * Transcoded video data is stored in the transcoded_video.foz Fossilize database. + * + * + * Hashing algorithm + * ----------------- + * + * We use murmur3 hash with the seed given below. We use the x32 variant for 32-bit programs, and + * the x64 variant for 64-bit programs. + * + * For speed when hashing, we specify a stride which will skip over chunks of the input. However, + * we will always hash the first "stride" number of bytes, to try to avoid collisions on smaller + * files with size between chunk and stride. + * + * For example, the 'H's below are hashed, the 'x's are skipped: + * + * int chunk = 4; + * int stride = chunk * 3; + * H = hashed, x = skipped + * [HHHH HHHH HHHH HHHH xxxx xxxx HHHH xxxx xxxx HHHH xxxx] < data stream + * ^^^^ ^^^^ ^^^^ stride prefix, hashed + * ^^^^ chunk + * ^^^^ ^^^^ ^^^^ stride + * ^^^^ chunk + * ^^^^ ^^^^ ^^^^ stride + * ^^^^ chunk + * ^^^^ ^^^^ stride + */ + +#if 0 +#pragma makedep unix +#endif + +#include "media-converter.h" + +#include + +#define HASH_CHUNK_SIZE (8 * 1024 * 1024) /* 8 MB. */ +#define HASH_STRIDE (HASH_CHUNK_SIZE * 6) + +#define VIDEO_CONV_FOZ_TAG_VIDEODATA 0 +#define VIDEO_CONV_FOZ_TAG_OGVDATA 1 +#define VIDEO_CONV_FOZ_TAG_STREAM 2 +#define VIDEO_CONV_FOZ_TAG_MKVDATA 3 +#define VIDEO_CONV_FOZ_NUM_TAGS 4 + +#define DURATION_NONE (UINT64_MAX) + +struct pad_reader +{ + GstPad *pad; + size_t offset; + uint8_t *chunk; + size_t chunk_offset; + size_t chunk_end; + size_t stride; /* Set to SIZE_MAX to skip no bytes. */ +}; + +struct hashes_reader +{ + GList *current_hash; +}; + +struct video_conv_state +{ + struct payload_hash transcode_hash; + struct fozdb *read_fozdb; + uint64_t upstream_duration; + uint64_t our_duration; + uint32_t transcoded_tag; + bool has_transcoded, need_stream_start; +}; + +typedef struct +{ + GstElement element; + GstPad *sink_pad, *src_pad; + pthread_mutex_t state_mutex; + struct video_conv_state *state; +} VideoConv; + +typedef struct +{ + GstElementClass class; +} VideoConvClass; + +G_DEFINE_TYPE(VideoConv, video_conv, GST_TYPE_ELEMENT); +#define VIDEO_CONV_TYPE (video_conv_get_type()) +#define VIDEO_CONV(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), VIDEO_CONV_TYPE, VideoConv)) +#define parent_class (video_conv_parent_class) +GST_ELEMENT_REGISTER_DEFINE(protonvideoconverter, "protonvideoconverter", + GST_RANK_MARGINAL, VIDEO_CONV_TYPE); + +static GstStaticPadTemplate video_conv_sink_template = GST_STATIC_PAD_TEMPLATE("sink", + GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS("video/x-ms-asf; video/x-msvideo; video/mpeg; video/quicktime;")); + +static GstStaticPadTemplate video_conv_src_template = GST_STATIC_PAD_TEMPLATE("src", + GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS("video/x-matroska; application/ogg;")); + +static struct dump_fozdb dump_fozdb = {PTHREAD_MUTEX_INITIALIZER, NULL, false}; + +void hashes_reader_init(struct hashes_reader *reader, GList *hashes) +{ + reader->current_hash = hashes; +} + +static int hashes_reader_read(void *reader, uint8_t *buffer, size_t size, size_t *read_size) +{ + struct payload_hash *hash = (struct payload_hash *)buffer; + struct hashes_reader *hashes_reader = reader; + + if (!size) + { + *read_size = 0; + return CONV_OK; + } + + if (!hashes_reader->current_hash) + return CONV_ERROR_DATA_END; + + *hash = *(struct payload_hash *)(hashes_reader->current_hash->data); + hashes_reader->current_hash = hashes_reader->current_hash->next; + + *read_size = sizeof(*hash); + return CONV_OK; +} + +static int dump_fozdb_open_video(bool create) +{ + return dump_fozdb_open(&dump_fozdb, create, "MEDIACONV_VIDEO_DUMP_FILE", VIDEO_CONV_FOZ_NUM_TAGS); +} + +static void dump_fozdb_discard_transcoded(void) +{ + GList *to_discard_chunks = NULL; + struct payload_hash *stream_id; + struct fozdb *read_fozdb; + char *read_fozdb_path; + GHashTableIter iter; + int ret; + + if (dump_fozdb.already_cleaned) + return; + dump_fozdb.already_cleaned = true; + + if (discarding_disabled()) + return; + if (!file_exists(getenv("MEDIACONV_VIDEO_DUMP_FILE"))) + return; + + if (dump_fozdb_open_video(false) < 0) + return; + + if (!(read_fozdb_path = getenv("MEDIACONV_VIDEO_TRANSCODED_FILE"))) + { + GST_ERROR("Env MEDIACONV_VIDEO_TRANSCODED_FILE not set."); + return; + } + + if ((ret = fozdb_create(read_fozdb_path, O_RDONLY, true /* Read-only? */, VIDEO_CONV_FOZ_NUM_TAGS, &read_fozdb)) < 0) + { + GST_ERROR("Failed to create read fozdb, ret %d.", ret); + return; + } + + fozdb_iter_tag(dump_fozdb.fozdb, VIDEO_CONV_FOZ_TAG_STREAM, &iter); + while (g_hash_table_iter_next(&iter, (void **)&stream_id, NULL)) + { + struct payload_hash chunk_id; + uint32_t chunks_size, i; + size_t read_size; + + if (fozdb_has_entry(read_fozdb, VIDEO_CONV_FOZ_TAG_OGVDATA, stream_id)) + { + if (fozdb_entry_size(dump_fozdb.fozdb, VIDEO_CONV_FOZ_TAG_STREAM, stream_id, &chunks_size) == CONV_OK) + { + uint8_t *buffer = calloc(1, chunks_size); + if (fozdb_read_entry_data(dump_fozdb.fozdb, VIDEO_CONV_FOZ_TAG_STREAM, stream_id, + 0, buffer, chunks_size, &read_size, true) == CONV_OK) + { + for (i = 0; i < read_size / sizeof(chunk_id); ++i) + { + payload_hash_from_bytes(&chunk_id, buffer + i * sizeof(chunk_id)); + to_discard_chunks = g_list_append(to_discard_chunks, + entry_name_create(VIDEO_CONV_FOZ_TAG_VIDEODATA, &chunk_id)); + } + } + free(buffer); + } + + to_discard_chunks = g_list_append(to_discard_chunks, + entry_name_create(VIDEO_CONV_FOZ_TAG_STREAM, stream_id)); + } + } + + if ((ret = fozdb_discard_entries(dump_fozdb.fozdb, to_discard_chunks)) < 0) + { + GST_ERROR("Failed to discard entries, ret %d.", ret); + dump_fozdb_close(&dump_fozdb); + } + + g_list_free_full(to_discard_chunks, free); +} + +struct pad_reader *pad_reader_create_with_stride(GstPad *pad, size_t stride) +{ + struct pad_reader *pad_reader; + + pad_reader = calloc(1, sizeof(*pad_reader)); + pad_reader->chunk = calloc(HASH_CHUNK_SIZE, sizeof(*pad_reader->chunk)); + pad_reader->stride = stride; + gst_object_ref((pad_reader->pad = pad)); + + return pad_reader; +} + +struct pad_reader *pad_reader_create(GstPad *pad) +{ + return pad_reader_create_with_stride(pad, SIZE_MAX); +} + +void pad_reader_release(struct pad_reader *reader) +{ + gst_object_unref(reader->pad); + free(reader->chunk); + free(reader); +} + +int pad_reader_read(void *data_src, uint8_t *buffer, size_t size, size_t *read_size) +{ + struct pad_reader *reader = data_src; + GstBuffer *gst_buffer = NULL; + GstFlowReturn gst_ret; + size_t to_copy; + + if (!size) + { + *read_size = 0; + return CONV_OK; + } + + if (reader->chunk_offset >= reader->chunk_end) + { + reader->chunk_offset = 0; + reader->chunk_end = 0; + + if ((gst_ret = gst_pad_pull_range(reader->pad, + reader->offset, HASH_CHUNK_SIZE, &gst_buffer)) == GST_FLOW_OK) + { + gsize buffer_size = gst_buffer_get_size(gst_buffer); + + if (reader->offset + buffer_size < reader->stride) + { + to_copy = buffer_size; + reader->offset += to_copy; + } + else if (reader->offset < reader->stride) + { + to_copy = reader->stride - reader->offset; + reader->offset = reader->stride; + } + else + { + to_copy = buffer_size; + reader->offset += reader->stride; + } + + if (size >= to_copy) /* Copy directly into out buffer and return. */ + { + *read_size = gst_buffer_extract(gst_buffer, 0, buffer, to_copy); + gst_buffer_unref(gst_buffer); + return CONV_OK; + } + else + { + reader->chunk_end = gst_buffer_extract(gst_buffer, 0, reader->chunk, to_copy); + gst_buffer_unref(gst_buffer); + } + } + else if (gst_ret == GST_FLOW_EOS) + { + return CONV_ERROR_DATA_END; + } + else + { + GST_WARNING("Failed to pull data from %"GST_PTR_FORMAT", reason %s.", + reader->pad, gst_flow_get_name(gst_ret)); + return CONV_ERROR; + } + } + + /* Copy chunk data to output buffer. */ + to_copy = min(reader->chunk_end - reader->chunk_offset, size); + memcpy(buffer, reader->chunk + reader->chunk_offset, to_copy); + reader->chunk_offset += to_copy; + + *read_size = to_copy; + return CONV_OK; +} + +static int video_conv_state_create(struct video_conv_state **out) +{ + struct video_conv_state *state; + struct fozdb *fozdb = NULL; + char *read_fozdb_path; + int ret; + + if (!(read_fozdb_path = getenv("MEDIACONV_VIDEO_TRANSCODED_FILE"))) + { + GST_ERROR("MEDIACONV_VIDEO_TRANSCODED_FILE is not set."); + return CONV_ERROR_ENV_NOT_SET; + } + + if ((ret = fozdb_create(read_fozdb_path, O_RDONLY, true /* Read-only? */, VIDEO_CONV_FOZ_NUM_TAGS, &fozdb)) < 0) + GST_ERROR("Failed to create read fozdb from %s, ret %d.", read_fozdb_path, ret); + + state = calloc(1, sizeof(*state)); + state->read_fozdb = fozdb; + state->upstream_duration = DURATION_NONE; + state->our_duration = DURATION_NONE; + state->transcoded_tag = VIDEO_CONV_FOZ_TAG_MKVDATA; + state->need_stream_start = true; + + *out = state; + return CONV_OK; +} + +static void video_conv_state_release(struct video_conv_state *state) +{ + if (state->read_fozdb) + fozdb_release(state->read_fozdb); + free(state); +} + +/* Return true if the file is transcoded, false if not. */ +bool video_conv_state_begin_transcode(struct video_conv_state *state, struct payload_hash *hash) +{ + const char *blank_video; + uint64_t file_size = 0; + int fd; + + GST_DEBUG("state %p, hash %s.", state, format_hash(hash)); + + if (state->read_fozdb) + { + uint32_t entry_size; + + if (fozdb_entry_size(state->read_fozdb, VIDEO_CONV_FOZ_TAG_MKVDATA, hash, &entry_size) == CONV_OK) + { + GST_DEBUG("Found an MKV video for hash %s.", format_hash(hash)); + state->transcode_hash = *hash; + state->our_duration = entry_size; + state->transcoded_tag = VIDEO_CONV_FOZ_TAG_MKVDATA; + state->has_transcoded = true; + return true; + } + + if (fozdb_entry_size(state->read_fozdb, VIDEO_CONV_FOZ_TAG_OGVDATA, hash, &entry_size) == CONV_OK) + { + GST_DEBUG("Found an OGV video for hash %s.", format_hash(hash)); + state->transcode_hash = *hash; + state->our_duration = entry_size; + state->transcoded_tag = VIDEO_CONV_FOZ_TAG_OGVDATA; + state->has_transcoded = true; + return true; + } + } + + GST_INFO("No transcoded video for %s. Substituting a blank video.", format_hash(hash)); + + if (!(blank_video = getenv("MEDIACONV_BLANK_VIDEO_FILE"))) + { + GST_ERROR("Env MEDIACONV_BLANK_VIDEO_FILE not set."); + return false; + } + if (open_file(blank_video, O_RDONLY, &fd)) + { + get_file_size(fd, &file_size); + close(fd); + } + state->our_duration = file_size; + state->has_transcoded = false; + + create_placeholder_file("placeholder-video-used"); + + return false; +} + +int video_conv_state_fill_buffer(struct video_conv_state *state, uint64_t offset, + uint8_t *buffer, size_t size, size_t *fill_size) +{ + const char *blank_video; + uint64_t file_size; + size_t to_copy; + bool read_ok; + int fd, ret; + + if (state->has_transcoded) + { + if ((ret = fozdb_read_entry_data(state->read_fozdb, state->transcoded_tag, &state->transcode_hash, + offset, buffer, size, fill_size, false)) < 0) + GST_ERROR("Failed to read entry data, ret %d.", ret); + return ret; + } + else /* Fill blank video data to buffer. */ + { + if (!(blank_video = getenv("MEDIACONV_BLANK_VIDEO_FILE"))) + { + GST_ERROR("Env MEDIACONV_BLANK_VIDEO_FILE not set."); + return CONV_ERROR_ENV_NOT_SET; + } + if (!open_file(blank_video, O_RDONLY, &fd)) + return CONV_ERROR_OPEN_FAILED; + if (!get_file_size(fd, &file_size)) + { + close(fd); + return CONV_ERROR; + } + + /* Get copy size. */ + if (offset >= file_size) + { + close(fd); + return CONV_OK; + } + to_copy = min(file_size - offset, size); + + /* Copy data. */ + if (lseek(fd, offset, SEEK_SET) < 0) + { + GST_ERROR("Failed to seek %s to %#"PRIx64". %s.", blank_video, offset, strerror(errno)); + close(fd); + return CONV_ERROR; + } + read_ok = complete_read(fd, buffer, to_copy); + close(fd); + + if (!read_ok) + { + GST_ERROR("Failed to read blank video data."); + return CONV_ERROR_READ_FAILED; + } + *fill_size = to_copy; + return CONV_OK; + } +} + +/* Call pthread_mutex_unlock() to unlock after usage. */ +static struct video_conv_state *video_conv_lock_state(VideoConv *conv) +{ + pthread_mutex_lock(&conv->state_mutex); + if (!conv->state) + pthread_mutex_unlock(&conv->state_mutex); + return conv->state; +} + +static GstStateChangeReturn video_conv_change_state(GstElement *element, GstStateChange transition) +{ + VideoConv *conv = VIDEO_CONV(element); + struct video_conv_state *state; + int ret; + + GST_INFO_OBJECT(element, "State transition: %s.", gst_state_change_get_name(transition)); + + switch (transition) + { + case GST_STATE_CHANGE_NULL_TO_READY: + /* Do runtime setup. */ + if ((ret = video_conv_state_create(&state)) < 0) + { + GST_ERROR("Failed to create video conv state, ret %d.", ret); + return GST_STATE_CHANGE_FAILURE; + } + pthread_mutex_lock(&conv->state_mutex); + assert(!conv->state); + conv->state = state; + pthread_mutex_unlock(&conv->state_mutex); + break; + + case GST_STATE_CHANGE_READY_TO_NULL: + /* Do runtime teardown. */ + pthread_mutex_lock(&conv->state_mutex); + video_conv_state_release(conv->state); + conv->state = NULL; + pthread_mutex_unlock(&conv->state_mutex); + break; + + default: + break; + } + + return GST_ELEMENT_CLASS(parent_class)->change_state(element, transition); + + /* XXX on ReadyToNull, sodium drops state _again_ here... why? */ +} + +static uint64_t video_conv_duration_ours_to_upstream(VideoConv *conv, uint64_t pos) +{ + struct video_conv_state *state = conv->state; + + if (state->upstream_duration != DURATION_NONE && state->our_duration != DURATION_NONE) + return pos * state->upstream_duration / state->our_duration; + else + return DURATION_NONE; +} + +static void video_conv_query_upstream_duration(VideoConv *conv) +{ + gint64 duration; + + if (gst_pad_peer_query_duration(conv->sink_pad, GST_FORMAT_BYTES, &duration)) + conv->state->upstream_duration = duration; + else + GST_ERROR_OBJECT(conv, "Failed to query upstream duration."); +} + +static bool video_conv_get_upstream_range(VideoConv *conv, uint64_t offset, uint32_t requested_size, + uint64_t *upstream_offset, uint64_t *upstream_requested_size) +{ + struct video_conv_state *state; + + if (!(state = video_conv_lock_state(conv))) + return false; + + if (state->upstream_duration == DURATION_NONE) + video_conv_query_upstream_duration(conv); + + *upstream_offset = video_conv_duration_ours_to_upstream(conv, offset); + *upstream_requested_size = video_conv_duration_ours_to_upstream(conv, requested_size); + + pthread_mutex_unlock(&conv->state_mutex); + + return true; +} + +static bool video_conv_hash_upstream_data(VideoConv *conv, struct payload_hash *hash) +{ + struct pad_reader *reader; + bool ret; + + memset(hash, 0, sizeof(*hash)); + + reader = pad_reader_create_with_stride(conv->sink_pad, HASH_STRIDE); + ret = murmur3_128(reader, pad_reader_read, HASH_SEED, hash); + pad_reader_release(reader); + + return ret; +} + +static int video_conv_dump_upstream_data(VideoConv *conv, struct payload_hash *hash) +{ + struct hashes_reader chunk_hashes_reader; + struct pad_reader *pad_reader = NULL; + GList *chunk_hashes = NULL; + uint8_t *buffer = NULL; + size_t read_size; + int ret; + + GST_DEBUG("Dumping upstream data, hash %s.", format_hash(hash)); + + if ((ret = dump_fozdb_open_video(true)) < 0) + { + GST_ERROR("Failed to open video dump fozdb, ret %d.", ret); + goto done; + } + + buffer = calloc(1, HASH_CHUNK_SIZE); + pad_reader = pad_reader_create(conv->sink_pad); + while ((ret = pad_reader_read(pad_reader, buffer, HASH_CHUNK_SIZE, &read_size)) == CONV_OK) + { + struct bytes_reader bytes_reader; + struct payload_hash *chunk_hash; + + bytes_reader_init(&bytes_reader, buffer, read_size); + chunk_hash = calloc(1, sizeof(*chunk_hash)); + murmur3_128(&bytes_reader, bytes_reader_read, HASH_SEED, chunk_hash); + chunk_hashes = g_list_append(chunk_hashes, chunk_hash); + + bytes_reader_init(&bytes_reader, buffer, read_size); + if ((ret = fozdb_write_entry(dump_fozdb.fozdb, VIDEO_CONV_FOZ_TAG_VIDEODATA, chunk_hash, + &bytes_reader, bytes_reader_read, true)) < 0) + { + GST_ERROR("Error writing video data to fozdb, ret %d.", ret); + goto done; + } + } + + if (ret != CONV_ERROR_DATA_END) + { + GST_ERROR("Failed to read data from pad reader, ret %d.", ret); + goto done; + } + + hashes_reader_init(&chunk_hashes_reader, chunk_hashes); + if ((ret = fozdb_write_entry(dump_fozdb.fozdb, VIDEO_CONV_FOZ_TAG_STREAM, hash, + &chunk_hashes_reader, hashes_reader_read, true)) < 0) + GST_ERROR("Error writing stream data to fozdb, ret %d.", ret); + +done: + if (chunk_hashes) + g_list_free_full(chunk_hashes, free); + if (pad_reader) + pad_reader_release(pad_reader); + if (buffer) + free(buffer); + return ret; +} + +static void video_conv_init_transcode(VideoConv *conv) +{ + struct video_conv_state *state = conv->state; + struct payload_hash hash; + int ret; + + if (state->has_transcoded) + return; + + pthread_mutex_lock(&dump_fozdb.mutex); + + dump_fozdb_discard_transcoded(); + + if (video_conv_hash_upstream_data(conv, &hash)) + { + GST_INFO("Got upstream data hash: %s.", format_hash(&hash)); + if (!video_conv_state_begin_transcode(state, &hash) + && (ret = video_conv_dump_upstream_data(conv, &hash)) < 0) + GST_ERROR("Failed to dump upstream data, ret %d.", ret); + } + else + { + GST_WARNING("Failed to hash upstream data."); + } + + pthread_mutex_unlock(&dump_fozdb.mutex); +} + +static gboolean video_conv_sink_event(GstPad *pad, GstObject *parent, GstEvent *event) +{ + VideoConv *conv = VIDEO_CONV(parent); + bool ret; + + GST_DEBUG_OBJECT(pad, "Got event %"GST_PTR_FORMAT".", event); + + if (event->type == GST_EVENT_CAPS) + { + struct video_conv_state *state; + uint32_t transcode_tag; + GstCaps *caps; + + /* push_event, below, can also grab state and cause a deadlock, so make sure it's + * unlocked before calling */ + if (!(state = video_conv_lock_state(conv))) + { + GST_ERROR("VideoConv not yet in READY state?"); + return false; + } + + if (!gst_pad_activate_mode(conv->sink_pad, GST_PAD_MODE_PULL, true)) + { + GST_ERROR("Failed to activate sink pad in pull mode."); + pthread_mutex_unlock(&conv->state_mutex); + return false; + } + + video_conv_init_transcode(conv); + transcode_tag = state->transcoded_tag; + + pthread_mutex_unlock(&conv->state_mutex); + + if (transcode_tag == VIDEO_CONV_FOZ_TAG_MKVDATA) + caps = gst_caps_from_string("video/x-matroska"); + else if (transcode_tag == VIDEO_CONV_FOZ_TAG_OGVDATA) + caps = gst_caps_from_string("application/ogg"); + else + return false; + + ret = push_event(conv->src_pad, gst_event_new_caps(caps)); + gst_caps_unref(caps); + return ret; + } + + return gst_pad_event_default(pad, parent, event); +} + +static GstFlowReturn video_conv_src_get_range(GstPad *pad, GstObject *parent, + guint64 offset, guint request_size, GstBuffer **buffer) +{ + GstBuffer *my_buffer, *upstream_buffer = NULL, *new_buffer = NULL; + uint64_t upstream_offset, upstream_request_size; + GstFlowReturn flow_ret = GST_FLOW_ERROR; + VideoConv *conv = VIDEO_CONV(parent); + struct video_conv_state *state; + size_t fill_size; + GstMapInfo map; + int ret; + + if (!video_conv_get_upstream_range(conv, offset, request_size, &upstream_offset, &upstream_request_size)) + return flow_ret; + + if (!(state = video_conv_lock_state(conv))) + return flow_ret; + + /* Read and ignore upstream bytes. */ + if ((flow_ret = gst_pad_pull_range(conv->sink_pad, upstream_offset, upstream_request_size, &upstream_buffer)) < 0) + { + GST_ERROR("Failed to pull upstream data from %"GST_PTR_FORMAT", offset %#"PRIx64", size %#"PRIx64", reason %s.", + conv->sink_pad, upstream_offset, upstream_request_size, gst_flow_get_name(flow_ret)); + goto done; + } + gst_buffer_unref(upstream_buffer); + + /* Allocate and map buffer. */ + my_buffer = *buffer; + if (!my_buffer) + { + /* XXX: can we use a buffer cache here? */ + if (!(new_buffer = gst_buffer_new_and_alloc(request_size))) + { + GST_ERROR("Failed to allocate buffer of %u bytes.", request_size); + goto done; + } + my_buffer = new_buffer; + } + if (!gst_buffer_map(my_buffer, &map, GST_MAP_READWRITE)) + { + GST_ERROR("Failed to map buffer <%"GST_PTR_FORMAT">.", my_buffer); + goto done; + } + + /* Fill buffer. */ + ret = video_conv_state_fill_buffer(state, offset, map.data, map.size, &fill_size); + gst_buffer_unmap(my_buffer, &map); + if (ret < 0) + { + GST_ERROR("Failed to fill buffer, ret %d.", ret); + goto done; + } + + if (fill_size > 0 || !gst_buffer_get_size(my_buffer)) + { + gst_buffer_set_size(my_buffer, fill_size); + *buffer = my_buffer; + flow_ret = GST_FLOW_OK; + } + else + { + flow_ret = GST_FLOW_EOS; + } + +done: + if (flow_ret < 0 && new_buffer) + gst_buffer_unref(new_buffer); + pthread_mutex_unlock(&conv->state_mutex); + return flow_ret; +} + +static gboolean video_conv_src_query(GstPad *pad, GstObject *parent, GstQuery *query) +{ + VideoConv *conv = VIDEO_CONV(parent); + struct video_conv_state *state; + GstSchedulingFlags flags; + gint min, max, align; + GstQuery *peer_query; + uint64_t duration; + GstFormat format; + + GST_DEBUG_OBJECT(pad, "Got query %"GST_PTR_FORMAT".", query); + + switch (query->type) + { + case GST_QUERY_SCHEDULING: + peer_query = gst_query_new_scheduling(); + if (!gst_pad_peer_query(conv->sink_pad, peer_query)) + { + GST_ERROR_OBJECT(conv->sink_pad, "Failed to query scheduling from peer."); + gst_query_unref(peer_query); + return false; + } + gst_query_parse_scheduling(peer_query, &flags, &min, &max, &align); + gst_query_unref(peer_query); + + gst_query_set_scheduling(query, flags, min, max, align); + gst_query_add_scheduling_mode(query, GST_PAD_MODE_PULL); + + return true; + + case GST_QUERY_DURATION: + gst_query_parse_duration(query, &format, NULL); + if (format != GST_FORMAT_BYTES) + { + GST_WARNING("Duration query format is not GST_FORMAT_BYTES."); + return false; + } + + if (!(state = video_conv_lock_state(conv))) + return false; + if (state->upstream_duration == DURATION_NONE) + video_conv_query_upstream_duration(conv); + duration = state->our_duration; + pthread_mutex_unlock(&conv->state_mutex); + + if (duration == DURATION_NONE) + return false; + gst_query_set_duration(query, GST_FORMAT_BYTES, duration); + return true; + + default: + return gst_pad_query_default(pad, parent, query); + } +} + +static gboolean video_conv_src_active_mode(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean active) +{ + VideoConv *conv = VIDEO_CONV(parent); + struct video_conv_state *state; + struct payload_hash hash; + bool need_stream_start; + bool has_transcoded; + + GST_DEBUG_OBJECT(pad, "mode %s, active %d.", gst_pad_mode_get_name(mode), active); + + if (!gst_pad_activate_mode(conv->sink_pad, mode, active)) + { + GST_ERROR_OBJECT(conv->sink_pad, "Failed to active sink pad: mode %s, active %d.", + gst_pad_mode_get_name(mode), active); + return false; + } + + if (mode != GST_PAD_MODE_PULL) + return true; + + if (!(state = video_conv_lock_state(conv))) + { + GST_ERROR("VideoConv not yet in READY state?"); + return false; + } + + video_conv_init_transcode(conv); + hash = state->transcode_hash; + need_stream_start = state->need_stream_start; + has_transcoded = state->has_transcoded; + + /* push_event, below, can also grab state and cause a deadlock, so make sure it's + * unlocked before calling */ + pthread_mutex_unlock(&conv->state_mutex); + + if (need_stream_start && active && has_transcoded) + { + push_event(conv->src_pad, gst_event_new_stream_start(format_hash(&hash))); + + if (!(state = video_conv_lock_state(conv))) + { + GST_ERROR("VideoConv not yet in READY state?"); + return false; + } + state->need_stream_start = false; + pthread_mutex_unlock(&conv->state_mutex); + } + + return true; +} + + +static void video_conv_finalize(GObject *object) +{ + VideoConv *conv = VIDEO_CONV(object); + + pthread_mutex_destroy(&conv->state_mutex); + if (conv->state) + video_conv_state_release(conv->state); + + G_OBJECT_CLASS(parent_class)->finalize(object); +} + +static void video_conv_class_init(VideoConvClass * klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + gst_element_class_set_metadata(element_class, + "Proton video converter", + "Codec/Demuxer", + "Converts video for Proton", + "Andrew Eikum , Ziqing Hui "); + + element_class->change_state = video_conv_change_state; + object_class->finalize = video_conv_finalize; + + gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&video_conv_sink_template)); + gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&video_conv_src_template)); +} + +static void video_conv_init(VideoConv *conv) +{ + GstElement *element = GST_ELEMENT(conv); + + conv->sink_pad = gst_pad_new_from_static_template(&video_conv_sink_template, "sink"); + gst_pad_set_event_function(conv->sink_pad, GST_DEBUG_FUNCPTR(video_conv_sink_event)); + gst_element_add_pad(element, conv->sink_pad); + + conv->src_pad = gst_pad_new_from_static_template(&video_conv_src_template, "src"); + gst_pad_set_getrange_function(conv->src_pad, GST_DEBUG_FUNCPTR(video_conv_src_get_range)); + gst_pad_set_query_function(conv->src_pad, GST_DEBUG_FUNCPTR(video_conv_src_query)); + gst_pad_set_activatemode_function(conv->src_pad, GST_DEBUG_FUNCPTR(video_conv_src_active_mode)); + gst_element_add_pad(element, conv->src_pad); + + pthread_mutex_init(&conv->state_mutex, NULL); + conv->state = NULL; +} diff --git a/dlls/winegstreamer/unixlib.c b/dlls/winegstreamer/unixlib.c index 3c51c83b348..881566339eb 100644 --- a/dlls/winegstreamer/unixlib.c +++ b/dlls/winegstreamer/unixlib.c @@ -46,6 +46,8 @@ GST_DEBUG_CATEGORY(wine); +extern bool media_converter_init(void); + GstGLDisplay *gl_display; GstStreamType stream_type_from_caps(GstCaps *caps) @@ -323,5 +325,12 @@ NTSTATUS wg_init_gstreamer(void *arg) } } + if (!media_converter_init()) + { + GST_ERROR("Failed to init media converter."); + gst_object_unref(gl_display); + return STATUS_UNSUCCESSFUL; + } + return STATUS_SUCCESS; } From bf2bfa8ee42fb62c816a150a96212a8142f41e27 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Fri, 29 Mar 2024 17:07:08 +0800 Subject: [PATCH 268/413] Revert "mfmediaengine/tests: Test IMFMediaEngineEx::SetCurrentTime/Ex()." This reverts commit e9b0c55ec27c464482847d057cc22721121d30e9. --- dlls/mfmediaengine/tests/mfmediaengine.c | 201 ----------------------- 1 file changed, 201 deletions(-) diff --git a/dlls/mfmediaengine/tests/mfmediaengine.c b/dlls/mfmediaengine/tests/mfmediaengine.c index c7fabbe218f..f9489f3dcb5 100644 --- a/dlls/mfmediaengine/tests/mfmediaengine.c +++ b/dlls/mfmediaengine/tests/mfmediaengine.c @@ -2183,11 +2183,6 @@ struct test_seek_notify { IMFMediaEngineNotify IMFMediaEngineNotify_iface; HANDLE playing_event; - HANDLE seeking_event; - HANDLE seeked_event; - HANDLE time_update_event; - BOOL seeking_event_received; - BOOL time_update_event_received; HRESULT expected_error; HRESULT error; LONG refcount; @@ -2227,9 +2222,6 @@ static ULONG WINAPI test_seek_notify_Release(IMFMediaEngineNotify *iface) if (!refcount) { CloseHandle(notify->playing_event); - CloseHandle(notify->seeking_event); - CloseHandle(notify->seeked_event); - CloseHandle(notify->time_update_event); free(notify); } @@ -2246,17 +2238,6 @@ static HRESULT WINAPI test_seek_notify_EventNotify(IMFMediaEngineNotify *iface, case MF_MEDIA_ENGINE_EVENT_PLAYING: SetEvent(notify->playing_event); break; - case MF_MEDIA_ENGINE_EVENT_SEEKING: - notify->seeking_event_received = TRUE; - SetEvent(notify->seeking_event); - break; - case MF_MEDIA_ENGINE_EVENT_SEEKED: - SetEvent(notify->seeked_event); - break; - case MF_MEDIA_ENGINE_EVENT_TIMEUPDATE: - notify->time_update_event_received = TRUE; - SetEvent(notify->time_update_event); - break; case MF_MEDIA_ENGINE_EVENT_ERROR: ok(param2 == notify->expected_error, "Unexpected error %#lx\n", param2); notify->error = param2; @@ -2281,13 +2262,7 @@ static struct test_seek_notify *create_seek_notify(void) object = calloc(1, sizeof(*object)); object->IMFMediaEngineNotify_iface.lpVtbl = &test_seek_notify_vtbl; object->playing_event = CreateEventW(NULL, FALSE, FALSE, NULL); - object->seeking_event = CreateEventW(NULL, FALSE, FALSE, NULL); - object->seeked_event = CreateEventW(NULL, FALSE, FALSE, NULL); - object->time_update_event = CreateEventW(NULL, FALSE, FALSE, NULL); ok(!!object->playing_event, "Failed to create an event, error %lu.\n", GetLastError()); - ok(!!object->seeking_event, "Failed to create an event, error %lu.\n", GetLastError()); - ok(!!object->seeked_event, "Failed to create an event, error %lu.\n", GetLastError()); - ok(!!object->time_update_event, "Failed to create an event, error %lu.\n", GetLastError()); object->refcount = 1; return object; } @@ -2529,181 +2504,6 @@ static void test_media_extension(void) IMFMediaEngineExtension_Release(&extension->IMFMediaEngineExtension_iface); } -#define test_seek_result(a, b, c) _test_seek_result(__LINE__, a, b, c) -static void _test_seek_result(int line, IMFMediaEngineEx *media_engine, - struct test_seek_notify *notify, double expected_time) -{ - static const double allowed_error = 0.05; - static const int timeout = 1000; - double time; - DWORD res; - - ok(notify->seeking_event_received, "Seeking event not received.\n"); - notify->seeking_event_received = FALSE; - res = WaitForSingleObject(notify->seeking_event, timeout); - ok_(__FILE__, line)(!res, "Waiting for seeking event returned %#lx.\n", res); - res = WaitForSingleObject(notify->seeked_event, timeout); - ok_(__FILE__, line)(!res, "Waiting for seeked event returned %#lx.\n", res); - res = WaitForSingleObject(notify->time_update_event, timeout); - ok_(__FILE__, line)(!res, "Waiting for ready event returned %#lx.\n", res); - time = IMFMediaEngineEx_GetCurrentTime(media_engine); - ok_(__FILE__, line)(compare_double(time, expected_time, allowed_error), "Unexpected time %lf.\n", time); -} - -static void test_SetCurrentTime(void) -{ - static const double allowed_error = 0.05; - static const int timeout = 1000; - IMFByteStream *stream, *unseekable_stream = NULL; - double time, duration, start, end; - struct test_seek_notify *notify; - IMFMediaEngineEx *media_engine; - ULONG refcount; - HRESULT hr; - DWORD res; - BOOL ret; - BSTR url; - - notify = create_seek_notify(); - hr = create_media_engine(¬ify->IMFMediaEngineNotify_iface, NULL, DXGI_FORMAT_B8G8R8X8_UNORM, - &IID_IMFMediaEngineEx, (void **)&media_engine); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface); - - stream = load_resource(L"i420-64x64.avi", L"video/avi"); - url = SysAllocString(L"i420-64x64.avi"); - hr = IMFMediaEngineEx_SetSourceFromByteStream(media_engine, stream, url); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaEngineEx_Play(media_engine); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - res = WaitForSingleObject(notify->playing_event, 5000); - ok(!res, "Unexpected res %#lx.\n", res); - - duration = IMFMediaEngineEx_GetDuration(media_engine); - ok(duration > 0, "Got invalid duration.\n"); - start = 0; - end = duration; - - /* Test playing state */ - hr = IMFMediaEngineEx_SetCurrentTime(media_engine, end); - ok(hr == S_OK || broken(hr == MF_INVALID_STATE_ERR) /* Win8 */, "Unexpected hr %#lx.\n", hr); - if (hr == S_OK) - test_seek_result(media_engine, notify, end); - - /* Test seeking with a negative position */ - hr = IMFMediaEngineEx_SetCurrentTime(media_engine, -1); - ok(hr == S_OK || broken(hr == MF_INVALID_STATE_ERR) /* Win8 */, "Unexpected hr %#lx.\n", hr); - if (hr == S_OK) - test_seek_result(media_engine, notify, 0); - - /* Test seeking beyond duration */ - hr = IMFMediaEngineEx_SetCurrentTime(media_engine, end + 1); - ok(hr == S_OK || broken(hr == MF_INVALID_STATE_ERR) /* Win8 */, "Unexpected hr %#lx.\n", hr); - if (hr == S_OK) - test_seek_result(media_engine, notify, end); - - hr = IMFMediaEngineEx_SetCurrentTimeEx(media_engine, start, MF_MEDIA_ENGINE_SEEK_MODE_NORMAL); - ok(hr == S_OK || broken(hr == MF_INVALID_STATE_ERR) /* Win8 */, "Unexpected hr %#lx.\n", hr); - if (hr == S_OK) - test_seek_result(media_engine, notify, start); - - hr = IMFMediaEngineEx_SetCurrentTimeEx(media_engine, end, MF_MEDIA_ENGINE_SEEK_MODE_APPROXIMATE); - ok(hr == S_OK || broken(hr == MF_INVALID_STATE_ERR) /* Win8 */, "Unexpected hr %#lx.\n", hr); - if (hr == S_OK) - test_seek_result(media_engine, notify, end); - - /* Test paused state */ - hr = IMFMediaEngineEx_Pause(media_engine); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaEngineEx_SetCurrentTime(media_engine, start); - ok(hr == S_OK || broken(hr == MF_INVALID_STATE_ERR) /* Win8 */, "Unexpected hr %#lx.\n", hr); - if (hr == S_OK) - { - ok(notify->seeking_event_received, "Seeking event not received.\n"); - notify->seeking_event_received = FALSE; - ok(notify->time_update_event_received, "Time update event not received.\n"); - notify->time_update_event_received = FALSE; - res = WaitForSingleObject(notify->seeking_event, timeout); - ok(!res, "Unexpected res %#lx.\n", res); - res = WaitForSingleObject(notify->seeked_event, timeout); - ok(res == WAIT_TIMEOUT || res == 0, /* No timeout sometimes on Win10+ */ - "Unexpected res %#lx.\n", res); - res = WaitForSingleObject(notify->time_update_event, timeout); - ok(!res, "Unexpected res %#lx.\n", res); - time = IMFMediaEngineEx_GetCurrentTime(media_engine); - ok(compare_double(time, start, allowed_error), "Unexpected time %lf.\n", time); - } - - Sleep(end * 1000); - - ret = IMFMediaEngineEx_IsPaused(media_engine); - ok(ret, "Unexpected ret %d.\n", ret); - time = IMFMediaEngineEx_GetCurrentTime(media_engine); - ok(compare_double(time, start, allowed_error) - || broken(time >= end) /* Windows 11 21H2 AMD GPU TestBot */, "Unexpected time %lf.\n", time); - - hr = IMFMediaEngineEx_Play(media_engine); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - res = WaitForSingleObject(notify->seeked_event, timeout); - ok(res == WAIT_TIMEOUT, "Unexpected res %#lx.\n", res); - - /* Media engine is shut down */ - hr = IMFMediaEngineEx_Shutdown(media_engine); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaEngineEx_SetCurrentTime(media_engine, start); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - hr = IMFMediaEngineEx_SetCurrentTimeEx(media_engine, start, MF_MEDIA_ENGINE_SEEK_MODE_NORMAL); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - refcount = IMFMediaEngineEx_Release(media_engine); - todo_wine - ok(!refcount, "Got unexpected refcount %lu.\n", refcount); - - /* Unseekable bytestreams */ - notify = create_seek_notify(); - hr = create_media_engine(¬ify->IMFMediaEngineNotify_iface, NULL, DXGI_FORMAT_B8G8R8X8_UNORM, - &IID_IMFMediaEngineEx, (void **)&media_engine); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface); - unseekable_stream = create_unseekable_stream(stream); - hr = IMFMediaEngineEx_SetSourceFromByteStream(media_engine, unseekable_stream, url); - todo_wine - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (FAILED(hr)) - goto done; - - hr = IMFMediaEngineEx_Play(media_engine); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - notify->expected_error = MF_E_INVALIDREQUEST; - res = WaitForSingleObject(notify->playing_event, 5000); - ok(res == S_OK, "Unexpected res %#lx.\n", res); - - hr = IMFMediaEngineEx_SetCurrentTime(media_engine, end); - ok(hr == S_OK || broken(hr == MF_INVALID_STATE_ERR) /* Win8 */, "Unexpected hr %#lx.\n", hr); - if (hr == S_OK) - { - ok(!notify->seeking_event_received, "Seeking event received.\n"); - res = WaitForSingleObject(notify->seeking_event, timeout); - ok(res == WAIT_TIMEOUT, "Unexpected res %#lx.\n", res); - res = WaitForSingleObject(notify->seeked_event, timeout); - ok(res == WAIT_TIMEOUT, "Unexpected res %#lx.\n", res); - res = WaitForSingleObject(notify->time_update_event, timeout); - ok(!res, "Unexpected res %#lx.\n", res); - } - -done: - hr = IMFMediaEngineEx_Shutdown(media_engine); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - refcount = IMFMediaEngineEx_Release(media_engine); - ok(!refcount || broken(refcount == 1) /* Win8.1 */, "Got unexpected refcount %lu.\n", refcount); - IMFByteStream_Release(unseekable_stream); - SysFreeString(url); - IMFByteStream_Release(stream); -} - START_TEST(mfmediaengine) { HRESULT hr; @@ -2739,7 +2539,6 @@ START_TEST(mfmediaengine) test_GetDuration(); test_GetSeekable(); test_media_extension(); - test_SetCurrentTime(); IMFMediaEngineClassFactory_Release(factory); From 6e244eaeb5a7a6518f8056fd61c520079f052b2a Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Fri, 29 Mar 2024 17:07:09 +0800 Subject: [PATCH 269/413] Revert "mfmediaengine: Implement IMFMediaEngineEx::SetCurrentTimeEx()." This reverts commit cab32f60ad8cbed6613656f7b89fa97b49f7046a. --- dlls/mfmediaengine/main.c | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index f6ea0cf3e59..c6f82ec055b 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -3027,24 +3027,9 @@ static HRESULT WINAPI media_engine_SetRealTimeMode(IMFMediaEngineEx *iface, BOOL static HRESULT WINAPI media_engine_SetCurrentTimeEx(IMFMediaEngineEx *iface, double seektime, MF_MEDIA_ENGINE_SEEK_MODE mode) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - HRESULT hr; - - TRACE("%p, %f, %#x.\n", iface, seektime, mode); - - if (mode) - FIXME("mode %#x is ignored.\n", mode); - - EnterCriticalSection(&engine->cs); - - if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) - hr = MF_E_SHUTDOWN; - else - hr = media_engine_set_current_time(engine, seektime); + FIXME("%p, %f, %#x stub.\n", iface, seektime, mode); - LeaveCriticalSection(&engine->cs); - - return hr; + return E_NOTIMPL; } static HRESULT WINAPI media_engine_EnableTimeUpdateTimer(IMFMediaEngineEx *iface, BOOL enable) From c1de0824b5f079b1b06b9cb5d39892047b810245 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Fri, 29 Mar 2024 17:07:10 +0800 Subject: [PATCH 270/413] Revert "mfmediaengine: Implement IMFMediaEngineEx::SetCurrentTime()." This reverts commit 283cde7ab8a1d3551cfae38f333c3de1ab779103. --- dlls/mfmediaengine/main.c | 59 +++++---------------------------------- 1 file changed, 7 insertions(+), 52 deletions(-) diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index c6f82ec055b..3e05626f238 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -94,7 +94,6 @@ enum media_engine_flags FLAGS_ENGINE_NEW_FRAME = 0x8000, FLAGS_ENGINE_SOURCE_PENDING = 0x10000, FLAGS_ENGINE_PLAY_PENDING = 0x20000, - FLAGS_ENGINE_SEEKING = 0x40000, }; struct vec3 @@ -165,7 +164,6 @@ struct media_engine { IMFMediaSource *source; IMFPresentationDescriptor *pd; - PROPVARIANT start_position; } presentation; struct effects video_effects; struct effects audio_effects; @@ -980,14 +978,7 @@ static HRESULT WINAPI media_engine_session_events_Invoke(IMFAsyncCallback *iface break; } case MESessionStarted: - EnterCriticalSection(&engine->cs); - if (engine->flags & FLAGS_ENGINE_SEEKING) - { - media_engine_set_flag(engine, FLAGS_ENGINE_SEEKING | FLAGS_ENGINE_IS_ENDED, FALSE); - IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_SEEKED, 0, 0); - IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_TIMEUPDATE, 0, 0); - } - LeaveCriticalSection(&engine->cs); + IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_PLAYING, 0, 0); break; case MESessionEnded: @@ -1348,9 +1339,10 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi static void media_engine_start_playback(struct media_engine *engine) { - IMFMediaSession_Start(engine->session, &GUID_NULL, &engine->presentation.start_position); - /* Reset the playback position to the current position */ - engine->presentation.start_position.vt = VT_EMPTY; + PROPVARIANT var; + + var.vt = VT_EMPTY; + IMFMediaSession_Start(engine->session, &GUID_NULL, &var); } static HRESULT WINAPI media_engine_load_handler_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) @@ -1778,10 +1770,6 @@ static double WINAPI media_engine_GetCurrentTime(IMFMediaEngineEx *iface) { ret = engine->duration; } - else if (engine->flags & FLAGS_ENGINE_PAUSED && engine->presentation.start_position.vt == VT_I8) - { - ret = (double)engine->presentation.start_position.hVal.QuadPart / 10000000; - } else if (SUCCEEDED(IMFPresentationClock_GetTime(engine->clock, &clocktime))) { ret = mftime_to_seconds(clocktime); @@ -1791,50 +1779,17 @@ static double WINAPI media_engine_GetCurrentTime(IMFMediaEngineEx *iface) return ret; } -static HRESULT media_engine_set_current_time(struct media_engine *engine, double seektime) -{ - PROPVARIANT position; - DWORD caps; - HRESULT hr; - - hr = IMFMediaSession_GetSessionCapabilities(engine->session, &caps); - if (FAILED(hr) || !(caps & MFSESSIONCAP_SEEK)) - return hr; - - position.vt = VT_I8; - position.hVal.QuadPart = min(max(0, seektime), engine->duration) * 10000000; - - if (IMFMediaEngineEx_IsPaused(&engine->IMFMediaEngineEx_iface)) - { - engine->presentation.start_position = position; - IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_SEEKING, 0, 0); - IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_SEEKED, 0, 0); - IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_TIMEUPDATE, 0, 0); - return S_OK; - } - - if (SUCCEEDED(hr = IMFMediaSession_Start(engine->session, &GUID_NULL, &position))) - { - media_engine_set_flag(engine, FLAGS_ENGINE_SEEKING, TRUE); - IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_SEEKING, 0, 0); - } - - return hr; -} - static HRESULT WINAPI media_engine_SetCurrentTime(IMFMediaEngineEx *iface, double time) { struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - HRESULT hr; + HRESULT hr = E_NOTIMPL; - TRACE("%p, %f.\n", iface, time); + FIXME("(%p, %f): stub.\n", iface, time); EnterCriticalSection(&engine->cs); if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) hr = MF_E_SHUTDOWN; - else - hr = media_engine_set_current_time(engine, time); LeaveCriticalSection(&engine->cs); From c099bc32d3772e4bd8da6c72c9107a396dd90020 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Fri, 29 Mar 2024 17:07:11 +0800 Subject: [PATCH 271/413] Revert "mf/tests: Test IMFMediaSession::Start()." This reverts commit dfce0a5d5df21bc0e7cc60c16e820c1a7537589f. --- dlls/mf/tests/mf.c | 960 +-------------------------------------------- 1 file changed, 8 insertions(+), 952 deletions(-) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index e6ae0a922d1..35d22839032 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -2075,7 +2075,6 @@ static IMFMediaSource *create_media_source(const WCHAR *name, const WCHAR *mime) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, stream, NULL, MF_RESOLUTION_MEDIASOURCE, NULL, &obj_type, (IUnknown **)&source); - todo_wine_if(hr == MF_E_UNEXPECTED) /* Gitlab CI Debian runner */ ok(hr == S_OK || broken(hr == MF_E_UNSUPPORTED_BYTESTREAM_TYPE), "Unexpected hr %#lx.\n", hr); IMFSourceResolver_Release(resolver); IMFByteStream_Release(stream); @@ -2087,626 +2086,6 @@ static IMFMediaSource *create_media_source(const WCHAR *name, const WCHAR *mime) return source; } -enum object_state -{ - SOURCE_START, - SOURCE_PAUSE, - SOURCE_STOP, - SOURCE_SHUTDOWN, - SINK_ON_CLOCK_START, - SINK_ON_CLOCK_PAUSE, - SINK_ON_CLOCK_STOP, - SINK_ON_CLOCK_RESTART, - SINK_ON_CLOCK_SETRATE, -}; - -#define MAX_OBJECT_STATE 1024 - -struct object_state_record -{ - enum object_state states[MAX_OBJECT_STATE]; - unsigned int state_count; -}; -static struct object_state_record actual_object_state_record; - -#define add_object_state(a, b) _add_object_state(__LINE__, a, b) -static void _add_object_state(int line, struct object_state_record *record, enum object_state state) -{ - ok_(__FILE__, line)(record->state_count < MAX_OBJECT_STATE, "exceeded state_count maximum %d.\n", MAX_OBJECT_STATE); - if (record->state_count < MAX_OBJECT_STATE) - record->states[record->state_count++] = state; -} - -#define compare_object_states(a, b) _compare_object_states(__LINE__, a, b) -static void _compare_object_states(int line, const struct object_state_record *r1, - const struct object_state_record *r2) -{ - ok_(__FILE__, line)(r1->state_count == r2->state_count, "State count not equal.\n"); - if (r1->state_count == r2->state_count) - ok_(__FILE__, line)(!memcmp(r1->states, r2->states, sizeof(enum object_state) * r1->state_count), "Got different states.\n"); -} - -enum source_state -{ - SOURCE_STOPPED, - SOURCE_RUNNING, - SOURCE_PAUSED, -}; - -struct test_media_stream -{ - IMFMediaStream IMFMediaStream_iface; - IMFMediaEventQueue *event_queue; - IMFStreamDescriptor *sd; - IMFMediaSource *source; - LONGLONG sample_duration; - LONGLONG sample_time; - BOOL is_new; - LONG refcount; -}; - -static struct test_media_stream *impl_from_IMFMediaStream(IMFMediaStream *iface) -{ - return CONTAINING_RECORD(iface, struct test_media_stream, IMFMediaStream_iface); -} - -static HRESULT WINAPI test_media_stream_QueryInterface(IMFMediaStream *iface, REFIID riid, void **out) -{ - if (IsEqualIID(riid, &IID_IMFMediaStream) - || IsEqualIID(riid, &IID_IMFMediaEventGenerator) - || IsEqualIID(riid, &IID_IUnknown)) - { - *out = iface; - } - else - { - *out = NULL; - return E_NOINTERFACE; - } - - IMFMediaStream_AddRef(iface); - return S_OK; -} - -static ULONG WINAPI test_media_stream_AddRef(IMFMediaStream *iface) -{ - struct test_media_stream *stream = impl_from_IMFMediaStream(iface); - return InterlockedIncrement(&stream->refcount); -} - -static ULONG WINAPI test_media_stream_Release(IMFMediaStream *iface) -{ - struct test_media_stream *stream = impl_from_IMFMediaStream(iface); - ULONG refcount = InterlockedDecrement(&stream->refcount); - - if (!refcount) - { - IMFMediaEventQueue_Release(stream->event_queue); - free(stream); - } - - return refcount; -} - -static HRESULT WINAPI test_media_stream_GetEvent(IMFMediaStream *iface, DWORD flags, IMFMediaEvent **event) -{ - struct test_media_stream *stream = impl_from_IMFMediaStream(iface); - return IMFMediaEventQueue_GetEvent(stream->event_queue, flags, event); -} - -static HRESULT WINAPI test_media_stream_BeginGetEvent(IMFMediaStream *iface, IMFAsyncCallback *callback, IUnknown *state) -{ - struct test_media_stream *stream = impl_from_IMFMediaStream(iface); - return IMFMediaEventQueue_BeginGetEvent(stream->event_queue, callback, state); -} - -static HRESULT WINAPI test_media_stream_EndGetEvent(IMFMediaStream *iface, IMFAsyncResult *result, IMFMediaEvent **event) -{ - struct test_media_stream *stream = impl_from_IMFMediaStream(iface); - return IMFMediaEventQueue_EndGetEvent(stream->event_queue, result, event); -} - -static HRESULT WINAPI test_media_stream_QueueEvent(IMFMediaStream *iface, MediaEventType event_type, REFGUID ext_type, - HRESULT hr, const PROPVARIANT *value) -{ - struct test_media_stream *stream = impl_from_IMFMediaStream(iface); - return IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, event_type, ext_type, hr, value); -} - -static HRESULT WINAPI test_media_stream_GetMediaSource(IMFMediaStream *iface, IMFMediaSource **source) -{ - struct test_media_stream *stream = impl_from_IMFMediaStream(iface); - - *source = stream->source; - IMFMediaSource_AddRef(*source); - - return S_OK; -} - -static HRESULT WINAPI test_media_stream_GetStreamDescriptor(IMFMediaStream *iface, IMFStreamDescriptor **sd) -{ - struct test_media_stream *stream = impl_from_IMFMediaStream(iface); - - *sd = stream->sd; - IMFStreamDescriptor_AddRef(*sd); - - return S_OK; -} - -static HRESULT WINAPI test_media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) -{ - struct test_media_stream *stream = impl_from_IMFMediaStream(iface); - IMFMediaBuffer *buffer; - IMFSample *sample; - HRESULT hr; - - hr = MFCreateSample(&sample); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (stream->sample_duration) - { - hr = IMFSample_SetSampleDuration(sample, stream->sample_duration); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFSample_SetSampleTime(sample, stream->sample_time); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - stream->sample_time += stream->sample_duration; - } - else - { - hr = IMFSample_SetSampleTime(sample, 123); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFSample_SetSampleDuration(sample, 1); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - } - - if (token) - IMFSample_SetUnknown(sample, &MFSampleExtension_Token, token); - - /* Reader expects buffers, empty samples are considered an error. */ - hr = MFCreateMemoryBuffer(8, &buffer); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFSample_AddBuffer(sample, buffer); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - IMFMediaBuffer_Release(buffer); - - hr = IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, &GUID_NULL, S_OK, - (IUnknown *)sample); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - IMFSample_Release(sample); - - return S_OK; -} - -static const IMFMediaStreamVtbl test_media_stream_vtbl = -{ - test_media_stream_QueryInterface, - test_media_stream_AddRef, - test_media_stream_Release, - test_media_stream_GetEvent, - test_media_stream_BeginGetEvent, - test_media_stream_EndGetEvent, - test_media_stream_QueueEvent, - test_media_stream_GetMediaSource, - test_media_stream_GetStreamDescriptor, - test_media_stream_RequestSample, -}; - -#define TEST_SOURCE_NUM_STREAMS 3 - -struct test_seek_source -{ - IMFMediaSource IMFMediaSource_iface; - IMFMediaEventQueue *event_queue; - IMFPresentationDescriptor *pd; - struct test_media_stream *streams[TEST_SOURCE_NUM_STREAMS]; - enum source_state state; - unsigned stream_count; - CRITICAL_SECTION cs; - BOOL seekable; - LONG refcount; -}; - -static struct test_seek_source *impl_test_seek_source_from_IMFMediaSource(IMFMediaSource *iface) -{ - return CONTAINING_RECORD(iface, struct test_seek_source, IMFMediaSource_iface); -} - -static HRESULT WINAPI test_seek_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out) -{ - if (IsEqualIID(riid, &IID_IMFMediaSource) - || IsEqualIID(riid, &IID_IMFMediaEventGenerator) - || IsEqualIID(riid, &IID_IUnknown)) - { - *out = iface; - } - else - { - *out = NULL; - return E_NOINTERFACE; - } - - IMFMediaSource_AddRef(iface); - return S_OK; -} - -static ULONG WINAPI test_seek_source_AddRef(IMFMediaSource *iface) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - return InterlockedIncrement(&source->refcount); -} - -static ULONG WINAPI test_seek_source_Release(IMFMediaSource *iface) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - ULONG refcount = InterlockedDecrement(&source->refcount); - - if (!refcount) - { - IMFMediaEventQueue_Release(source->event_queue); - free(source); - } - - return refcount; -} - -static HRESULT WINAPI test_seek_source_GetEvent(IMFMediaSource *iface, DWORD flags, IMFMediaEvent **event) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - return IMFMediaEventQueue_GetEvent(source->event_queue, flags, event); -} - -static HRESULT WINAPI test_seek_source_BeginGetEvent(IMFMediaSource *iface, IMFAsyncCallback *callback, IUnknown *state) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - return IMFMediaEventQueue_BeginGetEvent(source->event_queue, callback, state); -} - -static HRESULT WINAPI test_seek_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncResult *result, IMFMediaEvent **event) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - return IMFMediaEventQueue_EndGetEvent(source->event_queue, result, event); -} - -static HRESULT WINAPI test_seek_source_QueueEvent(IMFMediaSource *iface, MediaEventType event_type, REFGUID ext_type, - HRESULT hr, const PROPVARIANT *value) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, ext_type, hr, value); -} - -static HRESULT WINAPI test_seek_source_GetCharacteristics(IMFMediaSource *iface, DWORD *flags) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - - if (source->seekable) - *flags = MFMEDIASOURCE_CAN_PAUSE | MFMEDIASOURCE_CAN_SEEK; - else - *flags = MFMEDIASOURCE_CAN_PAUSE; - return S_OK; -} - -static HRESULT WINAPI test_seek_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **pd) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - IMFStreamDescriptor *sds[ARRAY_SIZE(source->streams)]; - IMFMediaType *media_type; - HRESULT hr = S_OK; - int i; - - EnterCriticalSection(&source->cs); - - if (source->pd) - { - *pd = source->pd; - IMFPresentationDescriptor_AddRef(*pd); - } - else - { - for (i = 0; i < source->stream_count; ++i) - { - hr = MFCreateMediaType(&media_type); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_RGB32); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_SIZE, (UINT64)640 << 32 | 480); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = MFCreateStreamDescriptor(i, 1, &media_type, &sds[i]); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - IMFMediaType_Release(media_type); - } - - hr = MFCreatePresentationDescriptor(source->stream_count, sds, &source->pd); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFPresentationDescriptor_SetUINT64(source->pd, &MF_PD_DURATION, 10 * 10000000); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFPresentationDescriptor_SelectStream(source->pd, 0); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - for (i = 0; i < source->stream_count; ++i) - IMFStreamDescriptor_Release(sds[i]); - - *pd = source->pd; - IMFPresentationDescriptor_AddRef(*pd); - } - - LeaveCriticalSection(&source->cs); - - return hr; -} - -static BOOL is_stream_selected(IMFPresentationDescriptor *pd, DWORD index) -{ - IMFStreamDescriptor *sd; - BOOL selected = FALSE; - - if (SUCCEEDED(IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, index, &selected, &sd))) - IMFStreamDescriptor_Release(sd); - - return selected; -} - -static HRESULT WINAPI test_seek_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *pd, const GUID *time_format, - const PROPVARIANT *start_position) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - MediaEventType event_type; - PROPVARIANT var; - HRESULT hr; - int i; - - add_object_state(&actual_object_state_record, SOURCE_START); - - ok(time_format && IsEqualGUID(time_format, &GUID_NULL), "Unexpected time format %s.\n", - wine_dbgstr_guid(time_format)); - ok(start_position && (start_position->vt == VT_I8 || start_position->vt == VT_EMPTY), - "Unexpected position type.\n"); - - /* This is what makes IMFMediaSession::Start() seeking fail, not the lacking of MFMEDIASOURCE_CAN_SEEK. - * Without this, IMFMediaSession::Start() seeking succeeds even with the missing MFMEDIASOURCE_CAN_SEEK. - * If this is check is not here, the first IMFMediaSession::Start() call to a non-zero position - * succeeds somehow on Windows 10, then all following seeks fails and no MESessionStarted events */ - if (!source->seekable && start_position && start_position->vt == VT_I8 && start_position->hVal.QuadPart) - return E_FAIL; - - EnterCriticalSection(&source->cs); - - event_type = source->state != SOURCE_STOPPED ? MESourceSeeked : MESourceStarted; - hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, NULL); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - for (i = 0; i < source->stream_count; ++i) - { - if (!is_stream_selected(pd, i)) - continue; - - var.vt = VT_UNKNOWN; - var.punkVal = (IUnknown *)&source->streams[i]->IMFMediaStream_iface; - event_type = source->streams[i]->is_new ? MENewStream : MEUpdatedStream; - source->streams[i]->is_new = FALSE; - hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, &var); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - event_type = source->state != SOURCE_STOPPED ? MEStreamSeeked : MEStreamStarted; - hr = IMFMediaEventQueue_QueueEventParamVar(source->streams[i]->event_queue, event_type, &GUID_NULL, - S_OK, NULL); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - } - - source->state = SOURCE_RUNNING; - - LeaveCriticalSection(&source->cs); - - return S_OK; -} - -static HRESULT WINAPI test_seek_source_Stop(IMFMediaSource *iface) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - MediaEventType event_type; - HRESULT hr; - int i; - - add_object_state(&actual_object_state_record, SOURCE_STOP); - - EnterCriticalSection(&source->cs); - - event_type = MESourceStopped; - hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, NULL); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - for (i = 0; i < source->stream_count; ++i) - { - if (!is_stream_selected(source->pd, i)) - continue; - - event_type = MEStreamStopped; - hr = IMFMediaEventQueue_QueueEventParamVar(source->streams[i]->event_queue, event_type, &GUID_NULL, - S_OK, NULL); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - } - - source->state = SOURCE_STOPPED; - - LeaveCriticalSection(&source->cs); - - return S_OK; -} - -static HRESULT WINAPI test_seek_source_Pause(IMFMediaSource *iface) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - MediaEventType event_type; - HRESULT hr; - int i; - - add_object_state(&actual_object_state_record, SOURCE_PAUSE); - - EnterCriticalSection(&source->cs); - - event_type = MESourcePaused; - hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, NULL); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - for (i = 0; i < source->stream_count; ++i) - { - if (!is_stream_selected(source->pd, i)) - continue; - - event_type = MEStreamPaused; - hr = IMFMediaEventQueue_QueueEventParamVar(source->streams[i]->event_queue, event_type, &GUID_NULL, - S_OK, NULL); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - } - - source->state = SOURCE_PAUSED; - LeaveCriticalSection(&source->cs); - - return S_OK; -} - -static HRESULT WINAPI test_seek_source_Shutdown(IMFMediaSource *iface) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - HRESULT hr; - - add_object_state(&actual_object_state_record, SOURCE_SHUTDOWN); - - hr = IMFMediaEventQueue_Shutdown(source->event_queue); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - return S_OK; -} - -static const IMFMediaSourceVtbl test_seek_source_vtbl = -{ - test_seek_source_QueryInterface, - test_seek_source_AddRef, - test_seek_source_Release, - test_seek_source_GetEvent, - test_seek_source_BeginGetEvent, - test_seek_source_EndGetEvent, - test_seek_source_QueueEvent, - test_seek_source_GetCharacteristics, - test_seek_source_CreatePresentationDescriptor, - test_seek_source_Start, - test_seek_source_Stop, - test_seek_source_Pause, - test_seek_source_Shutdown, -}; - -static HRESULT WINAPI test_seek_clock_sink_QueryInterface(IMFClockStateSink *iface, REFIID riid, void **obj) -{ - if (IsEqualIID(riid, &IID_IMFClockStateSink) || - IsEqualIID(riid, &IID_IUnknown)) - { - *obj = iface; - IMFClockStateSink_AddRef(iface); - return S_OK; - } - - *obj = NULL; - return E_NOINTERFACE; -} - -static ULONG WINAPI test_seek_clock_sink_AddRef(IMFClockStateSink *iface) -{ - return 2; -} - -static ULONG WINAPI test_seek_clock_sink_Release(IMFClockStateSink *iface) -{ - return 1; -} - -static HRESULT WINAPI test_seek_clock_sink_OnClockStart(IMFClockStateSink *iface, MFTIME system_time, LONGLONG offset) -{ - add_object_state(&actual_object_state_record, SINK_ON_CLOCK_START); - return S_OK; -} - -static HRESULT WINAPI test_seek_clock_sink_OnClockStop(IMFClockStateSink *iface, MFTIME system_time) -{ - add_object_state(&actual_object_state_record, SINK_ON_CLOCK_STOP); - return S_OK; -} - -static HRESULT WINAPI test_seek_clock_sink_OnClockPause(IMFClockStateSink *iface, MFTIME system_time) -{ - add_object_state(&actual_object_state_record, SINK_ON_CLOCK_PAUSE); - return S_OK; -} - -static HRESULT WINAPI test_seek_clock_sink_OnClockRestart(IMFClockStateSink *iface, MFTIME system_time) -{ - add_object_state(&actual_object_state_record, SINK_ON_CLOCK_RESTART); - return S_OK; -} - -static HRESULT WINAPI test_seek_clock_sink_OnClockSetRate(IMFClockStateSink *iface, MFTIME system_time, float rate) -{ - add_object_state(&actual_object_state_record, SINK_ON_CLOCK_SETRATE); - return S_OK; -} - -static const IMFClockStateSinkVtbl test_seek_clock_sink_vtbl = -{ - test_seek_clock_sink_QueryInterface, - test_seek_clock_sink_AddRef, - test_seek_clock_sink_Release, - test_seek_clock_sink_OnClockStart, - test_seek_clock_sink_OnClockStop, - test_seek_clock_sink_OnClockPause, - test_seek_clock_sink_OnClockRestart, - test_seek_clock_sink_OnClockSetRate, -}; - -static struct test_media_stream *create_test_stream(DWORD stream_index, IMFMediaSource *source) -{ - struct test_media_stream *stream; - IMFPresentationDescriptor *pd; - BOOL selected; - HRESULT hr; - - stream = calloc(1, sizeof(*stream)); - stream->IMFMediaStream_iface.lpVtbl = &test_media_stream_vtbl; - stream->refcount = 1; - hr = MFCreateEventQueue(&stream->event_queue); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - stream->source = source; - IMFMediaSource_AddRef(stream->source); - stream->is_new = TRUE; - - IMFMediaSource_CreatePresentationDescriptor(source, &pd); - IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, stream_index, &selected, &stream->sd); - IMFPresentationDescriptor_Release(pd); - - return stream; -} - -static IMFMediaSource *create_test_seek_source(BOOL seekable) -{ - struct test_seek_source *source; - int i; - - source = calloc(1, sizeof(*source)); - source->IMFMediaSource_iface.lpVtbl = &test_seek_source_vtbl; - source->refcount = 1; - source->stream_count = 1; - source->seekable = seekable; - MFCreateEventQueue(&source->event_queue); - InitializeCriticalSection(&source->cs); - for (i = 0; i < source->stream_count; ++i) - source->streams[i] = create_test_stream(i, &source->IMFMediaSource_iface); - - return &source->IMFMediaSource_iface; -} - static void test_media_session_events(void) { static const media_type_desc audio_float_44100 = @@ -3508,27 +2887,27 @@ static ULONG WINAPI test_grabber_callback_Release(IMFSampleGrabberSinkCallback * static HRESULT WINAPI test_grabber_callback_OnClockStart(IMFSampleGrabberSinkCallback *iface, MFTIME time, LONGLONG offset) { - return S_OK; + return E_NOTIMPL; } static HRESULT WINAPI test_grabber_callback_OnClockStop(IMFSampleGrabberSinkCallback *iface, MFTIME time) { - return S_OK; + return E_NOTIMPL; } static HRESULT WINAPI test_grabber_callback_OnClockPause(IMFSampleGrabberSinkCallback *iface, MFTIME time) { - return S_OK; + return E_NOTIMPL; } static HRESULT WINAPI test_grabber_callback_OnClockRestart(IMFSampleGrabberSinkCallback *iface, MFTIME time) { - return S_OK; + return E_NOTIMPL; } static HRESULT WINAPI test_grabber_callback_OnClockSetRate(IMFSampleGrabberSinkCallback *iface, MFTIME time, float rate) { - return S_OK; + return E_NOTIMPL; } static HRESULT WINAPI test_grabber_callback_OnSetPresentationClock(IMFSampleGrabberSinkCallback *iface, @@ -5616,8 +4995,8 @@ static void test_sample_grabber_is_mediatype_supported(void) IMFSampleGrabberSinkCallback_Release(grabber_callback); } -/* create a test topology with the specified source and sink, and return duration if required */ -static IMFTopology *create_test_topology(IMFMediaSource *source, IMFActivate *sink_activate, UINT64 *duration) +/* create a test topology with the specified source and sink */ +static IMFTopology *create_test_topology(IMFMediaSource *source, IMFActivate *sink_activate) { IMFTopologyNode *src_node, *sink_node; IMFPresentationDescriptor *pd; @@ -5643,11 +5022,6 @@ static IMFTopology *create_test_topology(IMFMediaSource *source, IMFActivate *si hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, 0, &selected, &sd); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(selected, "got selected %u.\n", !!selected); - if (duration) - { - hr = IMFPresentationDescriptor_GetUINT64(pd, &MF_PD_DURATION, duration); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - } init_source_node(source, -1, src_node, pd, sd); hr = IMFTopologyNode_SetObject(sink_node, (IUnknown *)sink_activate); ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); @@ -5709,7 +5083,7 @@ static void test_sample_grabber_orientation(GUID subtype) ok(hr == S_OK, "Failed to create grabber sink, hr %#lx.\n", hr); IMFMediaType_Release(output_type); - topology = create_test_topology(source, sink_activate, NULL); + topology = create_test_topology(source, sink_activate); hr = IMFMediaSession_SetTopology(session, 0, topology); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); IMFTopology_Release(topology); @@ -7908,323 +7282,6 @@ static void test_MFCreateSequencerSegmentOffset(void) PropVariantClear(&propvar); } -static void test_media_session_Start(void) -{ - static const struct object_state_record expected_object_state_records[] = - { - {{SOURCE_START, SINK_ON_CLOCK_START}, 2}, - {{SOURCE_STOP, SOURCE_START, SINK_ON_CLOCK_START}, 3}, - {{SOURCE_STOP, SOURCE_START, SINK_ON_CLOCK_START}, 3}, - }; - media_type_desc video_rgb32_desc = - { - ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), - ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), - }; - static const MFTIME allowed_error = 5000000; - IMFClockStateSink test_seek_clock_sink = {&test_seek_clock_sink_vtbl}; - struct test_grabber_callback *grabber_callback; - IMFPresentationClock *presentation_clock; - enum source_state initial_state; - IMFActivate *sink_activate; - IMFAsyncCallback *callback; - IMFMediaType *output_type; - IMFMediaSession *session; - IMFMediaSource *source; - IMFTopology *topology; - MFTIME time, old_time; - PROPVARIANT propvar; - IMFClock *clock; - UINT64 duration; - DWORD caps; - HRESULT hr; - - hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); - ok(hr == S_OK, "Failed to start up, hr %#lx.\n", hr); - - if (!(source = create_media_source(L"test.mp4", L"video/mp4"))) - { - todo_wine /* Gitlab CI Debian runner */ - win_skip("MP4 media source is not supported, skipping tests.\n"); - MFShutdown(); - return; - } - - grabber_callback = impl_from_IMFSampleGrabberSinkCallback(create_test_grabber_callback()); - hr = MFCreateMediaType(&output_type); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - init_media_type(output_type, video_rgb32_desc, -1); - hr = MFCreateSampleGrabberSinkActivate(output_type, &grabber_callback->IMFSampleGrabberSinkCallback_iface, &sink_activate); - ok(hr == S_OK, "Failed to create grabber sink, hr %#lx.\n", hr); - IMFMediaType_Release(output_type); - - hr = MFCreateMediaSession(NULL, &session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - topology = create_test_topology(source, sink_activate, &duration); - hr = IMFMediaSession_SetTopology(session, 0, topology); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - IMFTopology_Release(topology); - - hr = IMFMediaSession_GetClock(session, &clock); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFClock_QueryInterface(clock, &IID_IMFPresentationClock, (void **)&presentation_clock); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - IMFClock_Release(clock); - - propvar.vt = VT_EMPTY; - hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - callback = create_test_callback(TRUE); - hr = wait_media_event(session, callback, MESessionStarted, 5000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - /* Seek to 1s */ - propvar.vt = VT_I8; - propvar.hVal.QuadPart = 10000000; - hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFPresentationClock_GetTime(presentation_clock, &time); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(llabs(time - 10000000) <= allowed_error, "Unexpected time %I64d.\n", time); - - /* Seek to beyond duration */ - propvar.vt = VT_I8; - propvar.hVal.QuadPart = duration + 10000000; - hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); - ok(hr == MF_E_INVALID_POSITION, "Unexpected hr %#lx.\n", hr); - - /* Seek to negative position */ - propvar.vt = VT_I8; - propvar.hVal.QuadPart = -10000000; - hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFPresentationClock_GetTime(presentation_clock, &time); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(llabs(time - (-10000000)) <= allowed_error, "Unexpected time %I64d.\n", time); - - /* Seek backwards to 0s */ - propvar.vt = VT_I8; - propvar.hVal.QuadPart = 0; - hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFPresentationClock_GetTime(presentation_clock, &time); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(llabs(time) <= allowed_error, "Unexpected time %I64d.\n", time); - - /* Seek to 1s while in paused state */ - hr = IMFMediaSession_Pause(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionPaused, 1000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - propvar.vt = VT_I8; - propvar.hVal.QuadPart = 10000000; - hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFPresentationClock_GetTime(presentation_clock, &time); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(llabs(time - 10000000) <= allowed_error, "Unexpected time %I64d.\n", time); - old_time = time; - - /* Expected the presentation clock is running */ - Sleep(100); - hr = IMFPresentationClock_GetTime(presentation_clock, &time); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(time > old_time, "Unexpected time %I64d.\n", time); - - hr = IMFMediaSession_Stop(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFMediaSession_Close(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - /* Media session is shut down */ - hr = IMFMediaSource_Shutdown(source); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFMediaSession_Shutdown(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - propvar.vt = VT_I8; - propvar.hVal.QuadPart = 10000000; - hr = IMFMediaSession_Start(session, &GUID_NULL, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - - propvar.vt = VT_I8; - propvar.hVal.QuadPart = 10000000; - hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - propvar.vt = VT_EMPTY; - hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - IMFPresentationClock_Release(presentation_clock); - IMFMediaSource_Release(source); - IMFAsyncCallback_Release(callback); - /* sometimes briefly leaking */ - IMFMediaSession_Release(session); - IMFActivate_ShutdownObject(sink_activate); - IMFActivate_Release(sink_activate); - IMFSampleGrabberSinkCallback_Release(&grabber_callback->IMFSampleGrabberSinkCallback_iface); - - /* Unseekable media source */ - source = create_test_seek_source(FALSE); - hr = IMFMediaSource_GetCharacteristics(source, &caps); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok((caps & MFMEDIASOURCE_CAN_SEEK) == 0, "Got unexpected caps %#lx.\n", caps); - grabber_callback = impl_from_IMFSampleGrabberSinkCallback(create_test_grabber_callback()); - hr = MFCreateMediaType(&output_type); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - init_media_type(output_type, video_rgb32_desc, -1); - hr = MFCreateSampleGrabberSinkActivate(output_type, &grabber_callback->IMFSampleGrabberSinkCallback_iface, &sink_activate); - ok(hr == S_OK, "Failed to create grabber sink, hr %#lx.\n", hr); - IMFMediaType_Release(output_type); - - hr = MFCreateMediaSession(NULL, &session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - topology = create_test_topology(source, sink_activate, &duration); - hr = IMFMediaSession_SetTopology(session, 0, topology); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - IMFTopology_Release(topology); - - hr = IMFMediaSession_GetClock(session, &clock); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFClock_QueryInterface(clock, &IID_IMFPresentationClock, (void **)&presentation_clock); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - IMFClock_Release(clock); - - propvar.vt = VT_EMPTY; - hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - callback = create_test_callback(TRUE); - hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSession_GetSessionCapabilities(session, &caps); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok((caps & MFSESSIONCAP_SEEK) == 0, "Got unexpected caps %#lx\n", caps); - - /* Seek to 1s */ - propvar.vt = VT_I8; - propvar.hVal.QuadPart = 10000000; - hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); - ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); - hr = IMFPresentationClock_GetTime(presentation_clock, &time); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(llabs(time) <= allowed_error, "Unexpected time %I64d.\n", time); - - hr = IMFMediaSession_Stop(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFMediaSession_Close(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFMediaSession_Shutdown(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFMediaSource_Shutdown(source); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - IMFPresentationClock_Release(presentation_clock); - IMFAsyncCallback_Release(callback); - IMFMediaSession_Release(session); - IMFMediaSource_Release(source); - IMFActivate_ShutdownObject(sink_activate); - IMFActivate_Release(sink_activate); - IMFSampleGrabberSinkCallback_Release(&grabber_callback->IMFSampleGrabberSinkCallback_iface); - - /* Test object state transitions */ - for (initial_state = SOURCE_STOPPED; initial_state <= SOURCE_PAUSED; initial_state++) - { - winetest_push_context("Test %d", initial_state); - - source = create_test_seek_source(TRUE); - callback = create_test_callback(TRUE); - - grabber_callback = impl_from_IMFSampleGrabberSinkCallback(create_test_grabber_callback()); - hr = MFCreateMediaType(&output_type); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - init_media_type(output_type, video_rgb32_desc, -1); - hr = MFCreateSampleGrabberSinkActivate(output_type, &grabber_callback->IMFSampleGrabberSinkCallback_iface, &sink_activate); - ok(hr == S_OK, "Failed to create grabber sink, hr %#lx.\n", hr); - IMFMediaType_Release(output_type); - - hr = MFCreateMediaSession(NULL, &session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - topology = create_test_topology(source, sink_activate, &duration); - hr = IMFMediaSession_SetTopology(session, 0, topology); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - IMFTopology_Release(topology); - - hr = IMFMediaSession_GetClock(session, &clock); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFClock_QueryInterface(clock, &IID_IMFPresentationClock, (void **)&presentation_clock); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFPresentationClock_AddClockStateSink(presentation_clock, &test_seek_clock_sink); - ok(hr == S_OK, "Failed to add a sink, hr %#lx.\n", hr); - IMFClock_Release(clock); - - if (initial_state == SOURCE_RUNNING || initial_state == SOURCE_PAUSED) - { - propvar.vt = VT_EMPTY; - hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionStarted, 5000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - } - if (initial_state == SOURCE_PAUSED) - { - hr = IMFMediaSession_Pause(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionPaused, 5000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - } - - /* Seek to 1s */ - memset(&actual_object_state_record, 0, sizeof(actual_object_state_record)); - - propvar.vt = VT_I8; - propvar.hVal.QuadPart = 10000000; - hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionStarted, 5000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine_if(initial_state == SOURCE_PAUSED) - compare_object_states(&actual_object_state_record, &expected_object_state_records[initial_state]); - - hr = IMFMediaSession_Stop(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFMediaSession_Close(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFMediaSession_Shutdown(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFMediaSource_Shutdown(source); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - IMFPresentationClock_RemoveClockStateSink(presentation_clock, &test_seek_clock_sink); - IMFPresentationClock_Release(presentation_clock); - IMFAsyncCallback_Release(callback); - IMFMediaSession_Release(session); - IMFMediaSource_Release(source); - IMFActivate_ShutdownObject(sink_activate); - IMFActivate_Release(sink_activate); - IMFSampleGrabberSinkCallback_Release(&grabber_callback->IMFSampleGrabberSinkCallback_iface); - winetest_pop_context(); - } - - hr = MFShutdown(); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); -} - START_TEST(mf) { init_functions(); @@ -8261,5 +7318,4 @@ START_TEST(mf) test_MFRequireProtectedEnvironment(); test_mpeg4_media_sink(); test_MFCreateSequencerSegmentOffset(); - test_media_session_Start(); } From f8dcd06c6dd51324eff9b1f28eb0d0bd9f43554e Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Fri, 29 Mar 2024 17:07:11 +0800 Subject: [PATCH 272/413] Revert "mf: Support seeking while a session is started." This reverts commit ba94fc6d74cb1ba10b5d9cc57cb6edd7a58753d6. --- dlls/mf/session.c | 63 ++--------------------------------------------- 1 file changed, 2 insertions(+), 61 deletions(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 6590f001a00..d38e7cc0f6b 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -93,7 +93,6 @@ enum session_state SESSION_STATE_STARTING_SOURCES, SESSION_STATE_PREROLLING_SINKS, SESSION_STATE_STARTING_SINKS, - SESSION_STATE_RESTARTING_SOURCES, SESSION_STATE_STARTED, SESSION_STATE_PAUSING_SINKS, SESSION_STATE_PAUSING_SOURCES, @@ -930,7 +929,6 @@ static void session_start(struct media_session *session, const GUID *time_format { struct media_source *source; struct topo_node *topo_node; - MFTIME duration; HRESULT hr; UINT i; @@ -983,36 +981,8 @@ static void session_start(struct media_session *session, const GUID *time_format session->state = SESSION_STATE_STARTING_SOURCES; break; case SESSION_STATE_STARTED: - /* Check for invalid positions */ - LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) - { - hr = IMFPresentationDescriptor_GetUINT64(source->pd, &MF_PD_DURATION, (UINT64 *)&duration); - if (SUCCEEDED(hr) && IsEqualGUID(time_format, &GUID_NULL) - && start_position->vt == VT_I8 && start_position->hVal.QuadPart > duration) - { - WARN("Start position %s out of range, hr %#lx.\n", wine_dbgstr_longlong(start_position->hVal.QuadPart), hr); - session_command_complete_with_event(session, MESessionStarted, MF_E_INVALID_POSITION, NULL); - return; - } - } - - /* Stop sources */ - LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) - { - if (FAILED(hr = IMFMediaSource_Stop(source->source))) - { - WARN("Failed to stop media source %p, hr %#lx.\n", source->source, hr); - session_command_complete_with_event(session, MESessionStarted, hr, NULL); - return; - } - } - - session->presentation.time_format = *time_format; - session->presentation.start_position.vt = VT_EMPTY; - PropVariantCopy(&session->presentation.start_position, start_position); - - /* SESSION_STATE_STARTED -> SESSION_STATE_RESTARTING_SOURCES -> SESSION_STATE_STARTED */ - session->state = SESSION_STATE_RESTARTING_SOURCES; + FIXME("Seeking is not implemented.\n"); + session_command_complete(session); break; default: session_command_complete_with_event(session, MESessionStarted, MF_E_INVALIDREQUEST, NULL); @@ -2931,7 +2901,6 @@ static BOOL session_set_node_object_state(struct media_session *session, IUnknow static void session_set_source_object_state(struct media_session *session, IUnknown *object, MediaEventType event_type) { - struct media_source *source; IMFStreamSink *stream_sink; struct media_source *src; struct media_sink *sink; @@ -2983,15 +2952,6 @@ static void session_set_source_object_state(struct media_session *session, IUnkn session_set_presentation_clock(session); - /* If sinks are already started, start session immediately. This can happen when doing a - * seek from SESSION_STATE_STARTED */ - if (session_is_output_nodes_state(session, OBJ_STATE_STARTED) - && SUCCEEDED(session_start_clock(session))) - { - session_set_started(session); - return; - } - if ((session->presentation.flags & SESSION_FLAG_NEEDS_PREROLL) && session_is_output_nodes_state(session, OBJ_STATE_STOPPED)) { MFTIME preroll_time = 0; @@ -3029,25 +2989,6 @@ static void session_set_source_object_state(struct media_session *session, IUnkn else if (SUCCEEDED(session_start_clock(session))) session->state = SESSION_STATE_STARTING_SINKS; - break; - case SESSION_STATE_RESTARTING_SOURCES: - if (!session_is_source_nodes_state(session, OBJ_STATE_STOPPED)) - break; - - session_flush_nodes(session); - - /* Start sources */ - LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) - { - if (FAILED(hr = IMFMediaSource_Start(source->source, source->pd, - &session->presentation.time_format, &session->presentation.start_position))) - { - WARN("Failed to start media source %p, hr %#lx.\n", source->source, hr); - session_command_complete_with_event(session, MESessionStarted, hr, NULL); - return; - } - } - session->state = SESSION_STATE_STARTING_SOURCES; break; case SESSION_STATE_PAUSING_SOURCES: if (!session_is_source_nodes_state(session, OBJ_STATE_PAUSED)) From d48d4657d0bd6a97bf8ead84a199e5727a85a60e Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Fri, 29 Mar 2024 17:07:12 +0800 Subject: [PATCH 273/413] Revert "mf/tests: Add a create_test_topology() helper." This reverts commit 69e83049c9cf5e239a2fca60cac86a16368b30b7. --- dlls/mf/tests/mf.c | 80 +++++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 43 deletions(-) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 35d22839032..0a34329bd75 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -4995,48 +4995,6 @@ static void test_sample_grabber_is_mediatype_supported(void) IMFSampleGrabberSinkCallback_Release(grabber_callback); } -/* create a test topology with the specified source and sink */ -static IMFTopology *create_test_topology(IMFMediaSource *source, IMFActivate *sink_activate) -{ - IMFTopologyNode *src_node, *sink_node; - IMFPresentationDescriptor *pd; - IMFTopology *topology = NULL; - IMFStreamDescriptor *sd; - BOOL selected; - HRESULT hr; - - hr = MFCreateTopology(&topology); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &sink_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &src_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopology_AddNode(topology, sink_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopology_AddNode(topology, src_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopologyNode_ConnectOutput(src_node, 0, sink_node, 0); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFMediaSource_CreatePresentationDescriptor(source, &pd); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, 0, &selected, &sd); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(selected, "got selected %u.\n", !!selected); - init_source_node(source, -1, src_node, pd, sd); - hr = IMFTopologyNode_SetObject(sink_node, (IUnknown *)sink_activate); - ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); - hr = IMFTopologyNode_SetUINT32(sink_node, &MF_TOPONODE_CONNECT_METHOD, MF_CONNECT_ALLOW_DECODER); - ok(hr == S_OK, "Failed to set connect method, hr %#lx.\n", hr); - hr = IMFTopology_SetUINT32(topology, &MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES, TRUE); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - IMFStreamDescriptor_Release(sd); - IMFPresentationDescriptor_Release(pd); - IMFTopologyNode_Release(src_node); - IMFTopologyNode_Release(sink_node); - return topology; -} - static void test_sample_grabber_orientation(GUID subtype) { media_type_desc video_rgb32_desc = @@ -5046,13 +5004,17 @@ static void test_sample_grabber_orientation(GUID subtype) }; struct test_grabber_callback *grabber_callback; + IMFTopologyNode *src_node, *sink_node; + IMFPresentationDescriptor *pd; IMFAsyncCallback *callback; IMFActivate *sink_activate; IMFMediaType *output_type; IMFMediaSession *session; + IMFStreamDescriptor *sd; IMFMediaSource *source; IMFTopology *topology; PROPVARIANT propvar; + BOOL selected; HRESULT hr; DWORD res; @@ -5076,6 +5038,30 @@ static void test_sample_grabber_orientation(GUID subtype) hr = MFCreateMediaSession(NULL, &session); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &sink_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &src_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateTopology(&topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopology_AddNode(topology, sink_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopology_AddNode(topology, src_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_ConnectOutput(src_node, 0, sink_node, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaSource_CreatePresentationDescriptor(source, &pd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, 0, &selected, &sd); + ok(selected, "got selected %u.\n", !!selected); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_source_node(source, -1, src_node, pd, sd); + IMFTopologyNode_Release(src_node); + IMFPresentationDescriptor_Release(pd); + IMFStreamDescriptor_Release(sd); + hr = MFCreateMediaType(&output_type); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); init_media_type(output_type, video_rgb32_desc, -1); @@ -5083,7 +5069,15 @@ static void test_sample_grabber_orientation(GUID subtype) ok(hr == S_OK, "Failed to create grabber sink, hr %#lx.\n", hr); IMFMediaType_Release(output_type); - topology = create_test_topology(source, sink_activate); + hr = IMFTopologyNode_SetObject(sink_node, (IUnknown *)sink_activate); + ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); + hr = IMFTopologyNode_SetUINT32(sink_node, &MF_TOPONODE_CONNECT_METHOD, MF_CONNECT_ALLOW_DECODER); + ok(hr == S_OK, "Failed to set connect method, hr %#lx.\n", hr); + IMFTopologyNode_Release(sink_node); + + hr = IMFTopology_SetUINT32(topology, &MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES, TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_SetTopology(session, 0, topology); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); IMFTopology_Release(topology); From a71d0baef3568f842729acd67ba8fbe95188833f Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Fri, 29 Mar 2024 17:07:13 +0800 Subject: [PATCH 274/413] Revert "mf: Add a session_flush_nodes() helper." This reverts commit a46a5ee7b214cc10184dd8921d69c30991deda6d. --- dlls/mf/session.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index d38e7cc0f6b..ef707dea4de 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -912,19 +912,6 @@ static HRESULT session_subscribe_sources(struct media_session *session) return hr; } -static void session_flush_nodes(struct media_session *session) -{ - struct topo_node *node; - - LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) - { - if (node->type == MF_TOPOLOGY_OUTPUT_NODE) - IMFStreamSink_Flush(node->object.sink_stream); - else if (node->type == MF_TOPOLOGY_TRANSFORM_NODE) - IMFTransform_ProcessMessage(node->object.transform, MFT_MESSAGE_COMMAND_FLUSH, 0); - } -} - static void session_start(struct media_session *session, const GUID *time_format, const PROPVARIANT *start_position) { struct media_source *source; @@ -2905,6 +2892,7 @@ static void session_set_source_object_state(struct media_session *session, IUnkn struct media_source *src; struct media_sink *sink; enum object_state state; + struct topo_node *node; BOOL changed = FALSE; DWORD i, count; HRESULT hr; @@ -3000,7 +2988,21 @@ static void session_set_source_object_state(struct media_session *session, IUnkn if (!session_is_source_nodes_state(session, OBJ_STATE_STOPPED)) break; - session_flush_nodes(session); + LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) + { + switch (node->type) + { + case MF_TOPOLOGY_OUTPUT_NODE: + IMFStreamSink_Flush(node->object.sink_stream); + break; + case MF_TOPOLOGY_TRANSFORM_NODE: + IMFTransform_ProcessMessage(node->object.transform, MFT_MESSAGE_COMMAND_FLUSH, 0); + break; + default: + ; + } + } + session_set_caps(session, session->caps & ~MFSESSIONCAP_PAUSE); if (session->presentation.flags & SESSION_FLAG_FINALIZE_SINKS) From ea60a19ca2b048ab68dabd43277d5c12758124d7 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Sat, 25 Feb 2023 13:15:16 -0600 Subject: [PATCH 275/413] explorer: Add an environment variable setting to start Xalia. --- programs/explorer/desktop.c | 42 +++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/programs/explorer/desktop.c b/programs/explorer/desktop.c index 3571368b8f3..fe50586184f 100644 --- a/programs/explorer/desktop.c +++ b/programs/explorer/desktop.c @@ -1041,6 +1041,46 @@ static HANDLE start_tabtip_process(void) return pi.hProcess; } +static void start_xalia_process(void) +{ + PROCESS_INFORMATION pi; + STARTUPINFOW si = { sizeof(si) }; + WCHAR use_envvar[2]; + LPWSTR path; + LPCWSTR path_suffix = L"/../xalia/xalia.exe"; + DWORD ret, buffersize; + + /* check if xalia is enabled */ + ret = GetEnvironmentVariableW(L"PROTON_USE_XALIA", use_envvar, ARRAY_SIZE(use_envvar)); + if (!ret || (ret <= ARRAY_SIZE(use_envvar) && lstrcmpW(use_envvar, L"0") == 0)) + return; + + /* locate xalia.exe */ + ret = GetEnvironmentVariableW(L"WINEDATADIR", NULL, 0); + if (ret == 0) + return; + + buffersize = ret + lstrlenW(path_suffix); + path = HeapAlloc(GetProcessHeap(), 0, buffersize * sizeof(*path)); + + GetEnvironmentVariableW(L"WINEDATADIR", path, ret); + if (!memcmp(path, L"\\??\\", 8)) path[1] = '\\'; /* change \??\ into \\?\ */ + lstrcatW(path, path_suffix); + + /* setup environment */ + SetEnvironmentVariableW(L"XALIA_WINE_SYSTEM_PROCESS", L"1"); + + if (!CreateProcessW(path, NULL, + NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &si, &pi)) + { + WINE_ERR("Couldn't start xalia.exe: error %lu\n", GetLastError()); + return; + } + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + return; +} + /* main desktop management function */ void manage_desktop( WCHAR *arg ) { @@ -1169,6 +1209,8 @@ void manage_desktop( WCHAR *arg ) /* FIXME: hack, run tabtip.exe on startup. */ tabtip = start_tabtip_process(); + start_xalia_process(); + TRACE( "desktop message loop starting on hwnd %p\n", hwnd ); while (GetMessageW( &msg, 0, 0, 0 )) DispatchMessageW( &msg ); TRACE( "desktop message loop exiting for hwnd %p\n", hwnd ); From 4102ef7454e6896a1f0c9a0d59f34b9fa608ad11 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Wed, 8 Mar 2023 15:19:51 -0600 Subject: [PATCH 276/413] winex11.drv: Mark Xalia overlay window as a gamescope overlay. --- dlls/winex11.drv/window.c | 25 +++++++++++++++++++++++++ dlls/winex11.drv/x11drv.h | 1 + dlls/winex11.drv/x11drv_main.c | 1 + 3 files changed, 27 insertions(+) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index db51152e1dc..29773e871b0 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -2077,6 +2077,29 @@ void set_hwnd_style_props( Display *display, Window window, HWND hwnd ) } +void set_gamescope_overlay_prop( Display *display, Window window, HWND hwnd ) +{ + static const WCHAR class_name[] = {'X','a','l','i','a','O','v','e','r','l','a','y','B','o','x',0}; + WCHAR class_name_buf[16]; + UNICODE_STRING class_name_str; + INT ret; + + class_name_str.Buffer = class_name_buf; + class_name_str.MaximumLength = sizeof(class_name_buf); + + ret = NtUserGetClassName( hwnd, FALSE, &class_name_str ); + + if (ret && !wcscmp( class_name_buf, class_name )) { + DWORD one = 1; + + TRACE( "setting GAMESCOPE_XALIA_OVERLAY on window %lx, hwnd %p\n", window, hwnd ); + + XChangeProperty( display, window, x11drv_atom(GAMESCOPE_XALIA_OVERLAY), XA_CARDINAL, 32, + PropModeReplace, (unsigned char *)&one, sizeof(one) / 4 ); + } +} + + /********************************************************************** * create_whole_window * @@ -2147,6 +2170,8 @@ static void create_whole_window( struct x11drv_win_data *data ) set_hwnd_style_props( data->display, data->whole_window, data->hwnd ); + set_gamescope_overlay_prop( data->display, data->whole_window, data->hwnd ); + /* set the window text */ if (!NtUserInternalGetWindowText( data->hwnd, text, ARRAY_SIZE( text ))) text[0] = 0; sync_window_text( data->display, data->whole_window, text ); diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 4032ab85e9a..d60432a68e8 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -567,6 +567,7 @@ enum x11drv_atoms XATOM_text_uri_list, XATOM_GAMESCOPE_FOCUSED_APP, XATOM_GAMESCOPE_DISPLAY_EDID_PATH, + XATOM_GAMESCOPE_XALIA_OVERLAY, NB_XATOMS }; diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index a5eb1416c99..b286e6cfde8 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -234,6 +234,7 @@ static const char * const atom_names[NB_XATOMS - FIRST_XATOM] = "text/uri-list", "GAMESCOPE_FOCUSED_APP", "GAMESCOPE_DISPLAY_EDID_PATH", + "GAMESCOPE_XALIA_OVERLAY", }; /*********************************************************************** From c8c7b4d0e46cdde63c167a3550e06356af3f6312 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Tue, 5 Mar 2024 20:55:18 +0000 Subject: [PATCH 277/413] winex11.drv: Do not shape layered windows in Gamescope. The use of xshape doesn't work and actively breaks rendering in Gamescope. --- dlls/winex11.drv/bitblt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/winex11.drv/bitblt.c b/dlls/winex11.drv/bitblt.c index e6890495a51..5d2392fe450 100644 --- a/dlls/winex11.drv/bitblt.c +++ b/dlls/winex11.drv/bitblt.c @@ -1642,6 +1642,8 @@ static void update_surface_region( struct x11drv_window_surface *surface ) if (!shape_layered_windows) return; + if (wm_is_steamcompmgr(gdi_display)) return; + if (!surface->is_argb && surface->color_key == CLR_INVALID) { XShapeCombineMask( gdi_display, surface->window, ShapeBounding, 0, 0, None, ShapeSet ); From ca3dac218909a15860ff96355a4f99ab99303e38 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Tue, 12 Mar 2024 20:39:00 +0000 Subject: [PATCH 278/413] win32u: Implement EVENT_OBJECT_STATECHANGE. --- dlls/win32u/window.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index d3ce35ebb86..400500f34dc 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -873,6 +873,7 @@ BOOL enable_window( HWND hwnd, BOOL enable ) send_message( hwnd, WM_ENABLE, FALSE, 0 ); } } + NtUserNotifyWinEvent( EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, 0 ); return ret; } From a854a85f125cb34f972f31c0e8eff4ec273a15e6 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Wed, 13 Mar 2024 19:21:23 +0000 Subject: [PATCH 279/413] win32u: Implement EVENT_OBJECT_LOCATIONCHANGE. --- dlls/win32u/window.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index 400500f34dc..967b47f7ca0 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -3545,6 +3545,10 @@ BOOL set_window_pos( WINDOWPOS *winpos, int parent_x, int parent_y ) winpos->cy = new_window_rect.bottom - new_window_rect.top; send_message( winpos->hwnd, WM_WINDOWPOSCHANGED, 0, (LPARAM)winpos ); } + + if ((winpos->flags & (SWP_NOMOVE|SWP_NOSIZE)) != (SWP_NOMOVE|SWP_NOSIZE)) + NtUserNotifyWinEvent( EVENT_OBJECT_LOCATIONCHANGE, winpos->hwnd, OBJID_WINDOW, 0 ); + ret = TRUE; done: SetThreadDpiAwarenessContext( context ); From 620b0fefbdb76a9dd7d27125896254ca3d63c42e Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Thu, 16 Feb 2023 11:52:42 -0500 Subject: [PATCH 280/413] win32u: Add support for raising EVENT_OBJECT_DESTROY events on windows. --- dlls/win32u/window.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index 967b47f7ca0..2dcc3e20d3e 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -4693,6 +4693,7 @@ static void send_destroy_message( HWND hwnd ) if (hwnd == NtUserGetClipboardOwner()) release_clipboard_owner( hwnd ); send_message( hwnd, WM_DESTROY, 0, 0); + NtUserNotifyWinEvent( EVENT_OBJECT_DESTROY, hwnd, OBJID_WINDOW, 0 ); /* * This WM_DESTROY message can trigger re-entrant calls to DestroyWindow From f7050afd65fa6b34ff4c07fddb4dc7f6084d3e8e Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Sat, 19 Aug 2023 14:35:34 -0500 Subject: [PATCH 281/413] win32u: Implement cross-process GetScrollBarInfo. --- dlls/win32u/message.c | 11 +++++++++++ dlls/win32u/scroll.c | 5 ++++- dlls/win32u/win32u_private.h | 1 + include/ntuser.h | 1 + 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index 732be40beb4..41bb36a7226 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -736,6 +736,7 @@ static BOOL unpack_message( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lpa if (!get_buffer_space( buffer, sizeof(SCROLLINFO), buffer_size )) return FALSE; break; case SBM_GETSCROLLBARINFO: + case WM_WINE_GETSCROLLBARINFO: if (!get_buffer_space( buffer, sizeof(SCROLLBARINFO), buffer_size )) return FALSE; break; case EM_GETSEL: @@ -1134,6 +1135,7 @@ static size_t pack_message( HWND hwnd, UINT message, WPARAM wparam, LPARAM lpara push_data( data, (SCROLLINFO *)lparam, sizeof(SCROLLINFO) ); return sizeof(SCROLLINFO); case SBM_GETSCROLLBARINFO: + case WM_WINE_GETSCROLLBARINFO: { const SCROLLBARINFO *info = (const SCROLLBARINFO *)lparam; size_t size = min( info->cbSize, sizeof(SCROLLBARINFO) ); @@ -1370,6 +1372,10 @@ static void pack_reply( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam, case SBM_GETSCROLLINFO: push_data( data, (SCROLLINFO *)lparam, sizeof(SCROLLINFO) ); break; + case SBM_GETSCROLLBARINFO: + case WM_WINE_GETSCROLLBARINFO: + push_data( data, (SCROLLBARINFO *)lparam, sizeof(SCROLLBARINFO) ); + break; case EM_GETRECT: case LB_GETITEMRECT: case CB_GETDROPPEDCONTROLRECT: @@ -1511,6 +1517,7 @@ static void unpack_reply( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam, memcpy( (SCROLLINFO *)lparam, buffer, min( sizeof(SCROLLINFO), size )); break; case SBM_GETSCROLLBARINFO: + case WM_WINE_GETSCROLLBARINFO: memcpy( (SCROLLBARINFO *)lparam, buffer, min( sizeof(SCROLLBARINFO), size )); break; case EM_GETRECT: @@ -1715,6 +1722,7 @@ size_t user_message_size( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam, size = sizeof(SCROLLINFO); break; case SBM_GETSCROLLBARINFO: + case WM_WINE_GETSCROLLBARINFO: size = sizeof(SCROLLBARINFO); break; case EM_GETSEL: @@ -1957,6 +1965,7 @@ static void copy_user_result( void *buffer, size_t size, LRESULT result, UINT me copy_size = sizeof(SCROLLINFO); break; case SBM_GETSCROLLBARINFO: + case WM_WINE_GETSCROLLBARINFO: copy_size = sizeof(SCROLLBARINFO); break; case EM_GETSEL: @@ -2128,6 +2137,8 @@ static LRESULT handle_internal_message( HWND hwnd, UINT msg, WPARAM wparam, LPAR case WM_WINE_UPDATEWINDOWSTATE: update_window_state( hwnd ); return 0; + case WM_WINE_GETSCROLLBARINFO: + return get_scroll_bar_info( hwnd, (LONG)wparam, (SCROLLBARINFO *)lparam ); default: if (msg >= WM_WINE_FIRST_DRIVER_MSG && msg <= WM_WINE_LAST_DRIVER_MSG) return user_driver->pWindowMessage( hwnd, msg, wparam, lparam ); diff --git a/dlls/win32u/scroll.c b/dlls/win32u/scroll.c index 19a9a1379f4..65491f25c5b 100644 --- a/dlls/win32u/scroll.c +++ b/dlls/win32u/scroll.c @@ -1048,7 +1048,7 @@ static int set_scroll_info( HWND hwnd, int bar, const SCROLLINFO *info, BOOL red return ret; /* Return current position */ } -static BOOL get_scroll_bar_info( HWND hwnd, LONG id, SCROLLBARINFO *info ) +BOOL get_scroll_bar_info( HWND hwnd, LONG id, SCROLLBARINFO *info ) { struct scroll_info *scroll; int bar, dummy; @@ -1067,6 +1067,9 @@ static BOOL get_scroll_bar_info( HWND hwnd, LONG id, SCROLLBARINFO *info ) /* handle invalid data structure */ if (info->cbSize != sizeof(*info)) return FALSE; + if (bar != SB_CTL && !is_current_thread_window( hwnd )) + return send_message( hwnd, WM_WINE_GETSCROLLBARINFO, (WPARAM)id, (LPARAM)info ); + get_scroll_bar_rect( hwnd, bar, &info->rcScrollBar, &dummy, &info->dxyLineButton, &info->xyThumbTop ); /* rcScrollBar needs to be in screen coordinates */ diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index a5d2a196932..dc65ca858cb 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -153,6 +153,7 @@ extern BOOL rawinput_device_get_usages( HANDLE handle, USHORT *usage_page, USHOR /* scroll.c */ extern void draw_nc_scrollbar( HWND hwnd, HDC hdc, BOOL draw_horizontal, BOOL draw_vertical ); +extern BOOL get_scroll_bar_info( HWND hwnd, LONG id, SCROLLBARINFO *info ); extern BOOL get_scroll_info( HWND hwnd, INT bar, SCROLLINFO *info ); extern void handle_scroll_event( HWND hwnd, INT bar, UINT msg, POINT pt ); extern LRESULT scroll_bar_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, diff --git a/include/ntuser.h b/include/ntuser.h index 738fe055ff7..fc7cd98813c 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -489,6 +489,7 @@ enum wine_internal_message WM_WINE_KEYBOARD_LL_HOOK, WM_WINE_MOUSE_LL_HOOK, WM_WINE_UPDATEWINDOWSTATE, + WM_WINE_GETSCROLLBARINFO, WM_WINE_FIRST_DRIVER_MSG = 0x80001000, /* range of messages reserved for the USER driver */ WM_WINE_CLIPCURSOR = 0x80001ff0, /* internal driver notification messages */ WM_WINE_SETCURSOR, From a3fc1f98a3c8e7a694e04722b09fbe8116c4e2c6 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Sat, 19 Aug 2023 15:06:50 -0500 Subject: [PATCH 282/413] win32u: Implement cross-process GetScrollInfo. --- dlls/win32u/message.c | 8 ++++++++ dlls/win32u/scroll.c | 8 +++++++- include/ntuser.h | 1 + 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index 41bb36a7226..cf7909452ef 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -733,6 +733,7 @@ static BOOL unpack_message( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lpa minsize = sizeof(SCROLLINFO); break; case SBM_GETSCROLLINFO: + case WM_WINE_GETSCROLLINFO: if (!get_buffer_space( buffer, sizeof(SCROLLINFO), buffer_size )) return FALSE; break; case SBM_GETSCROLLBARINFO: @@ -1132,6 +1133,7 @@ static size_t pack_message( HWND hwnd, UINT message, WPARAM wparam, LPARAM lpara push_data( data, (SCROLLINFO *)lparam, sizeof(SCROLLINFO) ); return 0; case SBM_GETSCROLLINFO: + case WM_WINE_GETSCROLLINFO: push_data( data, (SCROLLINFO *)lparam, sizeof(SCROLLINFO) ); return sizeof(SCROLLINFO); case SBM_GETSCROLLBARINFO: @@ -1370,6 +1372,7 @@ static void pack_reply( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam, break; } case SBM_GETSCROLLINFO: + case WM_WINE_GETSCROLLINFO: push_data( data, (SCROLLINFO *)lparam, sizeof(SCROLLINFO) ); break; case SBM_GETSCROLLBARINFO: @@ -1514,6 +1517,7 @@ static void unpack_reply( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam, } break; case SBM_GETSCROLLINFO: + case WM_WINE_GETSCROLLINFO: memcpy( (SCROLLINFO *)lparam, buffer, min( sizeof(SCROLLINFO), size )); break; case SBM_GETSCROLLBARINFO: @@ -1719,6 +1723,7 @@ size_t user_message_size( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam, break; case SBM_SETSCROLLINFO: case SBM_GETSCROLLINFO: + case WM_WINE_GETSCROLLINFO: size = sizeof(SCROLLINFO); break; case SBM_GETSCROLLBARINFO: @@ -1962,6 +1967,7 @@ static void copy_user_result( void *buffer, size_t size, LRESULT result, UINT me break; case SBM_SETSCROLLINFO: case SBM_GETSCROLLINFO: + case WM_WINE_GETSCROLLINFO: copy_size = sizeof(SCROLLINFO); break; case SBM_GETSCROLLBARINFO: @@ -2139,6 +2145,8 @@ static LRESULT handle_internal_message( HWND hwnd, UINT msg, WPARAM wparam, LPAR return 0; case WM_WINE_GETSCROLLBARINFO: return get_scroll_bar_info( hwnd, (LONG)wparam, (SCROLLBARINFO *)lparam ); + case WM_WINE_GETSCROLLINFO: + return get_scroll_info( hwnd, (int)wparam, (SCROLLINFO *)lparam ); default: if (msg >= WM_WINE_FIRST_DRIVER_MSG && msg <= WM_WINE_LAST_DRIVER_MSG) return user_driver->pWindowMessage( hwnd, msg, wparam, lparam ); diff --git a/dlls/win32u/scroll.c b/dlls/win32u/scroll.c index 65491f25c5b..e3ed1350b09 100644 --- a/dlls/win32u/scroll.c +++ b/dlls/win32u/scroll.c @@ -892,7 +892,13 @@ BOOL get_scroll_info( HWND hwnd, int bar, SCROLLINFO *info ) struct scroll_info *scroll; /* handle invalid data structure */ - if (!validate_scroll_info( info ) || !(scroll = get_scroll_info_ptr( hwnd, bar, FALSE ))) + if (!validate_scroll_info( info )) + return FALSE; + + if (bar != SB_CTL && !is_current_thread_window( hwnd )) + return send_message( hwnd, WM_WINE_GETSCROLLINFO, (WPARAM)bar, (LPARAM)info ); + + if (!(scroll = get_scroll_info_ptr( hwnd, bar, FALSE ))) return FALSE; /* fill in the desired scroll info structure */ diff --git a/include/ntuser.h b/include/ntuser.h index fc7cd98813c..0677f114aea 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -490,6 +490,7 @@ enum wine_internal_message WM_WINE_MOUSE_LL_HOOK, WM_WINE_UPDATEWINDOWSTATE, WM_WINE_GETSCROLLBARINFO, + WM_WINE_GETSCROLLINFO, WM_WINE_FIRST_DRIVER_MSG = 0x80001000, /* range of messages reserved for the USER driver */ WM_WINE_CLIPCURSOR = 0x80001ff0, /* internal driver notification messages */ WM_WINE_SETCURSOR, From 9bed822d7f23e8d1122c917aacab00964dfd72c0 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Fri, 15 Mar 2024 16:30:57 +0000 Subject: [PATCH 283/413] win32u: Implement winevents for scrollbars. --- dlls/win32u/scroll.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/dlls/win32u/scroll.c b/dlls/win32u/scroll.c index e3ed1350b09..40a9b1a6605 100644 --- a/dlls/win32u/scroll.c +++ b/dlls/win32u/scroll.c @@ -165,6 +165,16 @@ static BOOL show_scroll_bar( HWND hwnd, int bar, BOOL show_horz, BOOL show_vert /* frame has been changed, let the window redraw itself */ NtUserSetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED ); + + if ((set_bits & WS_HSCROLL) && !(old_style & WS_HSCROLL)) + NtUserNotifyWinEvent( EVENT_OBJECT_SHOW, hwnd, OBJID_HSCROLL, 0 ); + if ((set_bits & WS_VSCROLL) && !(old_style & WS_VSCROLL)) + NtUserNotifyWinEvent( EVENT_OBJECT_SHOW, hwnd, OBJID_VSCROLL, 0 ); + if ((clear_bits & WS_HSCROLL) && (old_style & WS_HSCROLL)) + NtUserNotifyWinEvent( EVENT_OBJECT_HIDE, hwnd, OBJID_HSCROLL, 0 ); + if ((clear_bits & WS_VSCROLL) && (old_style & WS_VSCROLL)) + NtUserNotifyWinEvent( EVENT_OBJECT_HIDE, hwnd, OBJID_VSCROLL, 0 ); + return TRUE; } return FALSE; /* no frame changes */ @@ -1049,6 +1059,19 @@ static int set_scroll_info( HWND hwnd, int bar, const SCROLLINFO *info, BOOL red refresh_scroll_bar( hwnd, bar, TRUE, TRUE ); else if (action & SA_SSI_REPAINT_ARROWS) refresh_scroll_bar( hwnd, bar, TRUE, FALSE ); + + switch (bar) + { + case SB_CTL: + NtUserNotifyWinEvent( EVENT_OBJECT_VALUECHANGE, hwnd, OBJID_CLIENT, 0 ); + break; + case SB_HORZ: + NtUserNotifyWinEvent( EVENT_OBJECT_VALUECHANGE, hwnd, OBJID_HSCROLL, 0 ); + break; + case SB_VERT: + NtUserNotifyWinEvent( EVENT_OBJECT_VALUECHANGE, hwnd, OBJID_VSCROLL, 0 ); + break; + } } return ret; /* Return current position */ From d778e9df4b7b446b9c8f9ee2eb7e9e015a5869fe Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Fri, 29 Mar 2024 11:46:14 +0800 Subject: [PATCH 284/413] gdiplus/tests: Add tests for GdipPrivateAddMemoryFont(). CW-Bug-Id: #23597 --- dlls/gdiplus/tests/font.c | 64 +++++++- dlls/gdiplus/tests/resource.rc | 8 + dlls/gdiplus/tests/wine_mac_win.ttf | Bin 0 -> 1140 bytes dlls/gdiplus/tests/wine_mac_win.ttx | 203 ++++++++++++++++++++++++ dlls/gdiplus/tests/wine_unicode_mac.ttf | Bin 0 -> 1168 bytes dlls/gdiplus/tests/wine_unicode_mac.ttx | 203 ++++++++++++++++++++++++ 6 files changed, 471 insertions(+), 7 deletions(-) create mode 100644 dlls/gdiplus/tests/wine_mac_win.ttf create mode 100644 dlls/gdiplus/tests/wine_mac_win.ttx create mode 100644 dlls/gdiplus/tests/wine_unicode_mac.ttf create mode 100644 dlls/gdiplus/tests/wine_unicode_mac.ttx diff --git a/dlls/gdiplus/tests/font.c b/dlls/gdiplus/tests/font.c index 8f0bbce0eb4..6a21ea30361 100644 --- a/dlls/gdiplus/tests/font.c +++ b/dlls/gdiplus/tests/font.c @@ -43,11 +43,21 @@ static void set_rect_empty(RectF *rc) rc->Height = 0.0; } +#define load_resource(a, b, c) _load_resource(__LINE__, a, b, c) +static void _load_resource(int line, const WCHAR *filename, BYTE **data, DWORD *size) +{ + HRSRC resource = FindResourceW(NULL, filename, (const WCHAR *)RT_RCDATA); + ok_(__FILE__, line)(!!resource, "FindResourceW failed, error %lu\n", GetLastError()); + *data = LockResource(LoadResource(GetModuleHandleW(NULL), resource)); + ok_(__FILE__, line)(!!*data, "LockResource failed, error %lu\n", GetLastError()); + *size = SizeofResource(GetModuleHandleW(NULL), resource); + ok_(__FILE__, line)(*size > 0, "SizeofResource failed, error %lu\n", GetLastError()); +} + static void create_testfontfile(const WCHAR *filename, int resource, WCHAR pathW[MAX_PATH]) { - DWORD written; + DWORD written, length; HANDLE file; - HRSRC res; void *ptr; GetTempPathW(MAX_PATH, pathW); @@ -56,11 +66,9 @@ static void create_testfontfile(const WCHAR *filename, int resource, WCHAR pathW file = CreateFileW(pathW, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); ok(file != INVALID_HANDLE_VALUE, "file creation failed, at %s, error %ld\n", wine_dbgstr_w(pathW), GetLastError()); - res = FindResourceA(GetModuleHandleA(NULL), MAKEINTRESOURCEA(resource), (LPCSTR)RT_RCDATA); - ok(res != 0, "couldn't find resource\n"); - ptr = LockResource(LoadResource(GetModuleHandleA(NULL), res)); - WriteFile(file, ptr, SizeofResource(GetModuleHandleA(NULL), res), &written, NULL); - ok(written == SizeofResource(GetModuleHandleA(NULL), res), "couldn't write resource\n"); + load_resource(MAKEINTRESOURCEW(resource), (BYTE **)&ptr, &length); + WriteFile(file, ptr, length, &written, NULL); + ok(written == length, "couldn't write resource\n"); CloseHandle(file); } @@ -1524,6 +1532,47 @@ static void test_CloneFont(void) GdipDeleteFontFamily(family); } +static void test_GdipPrivateAddMemoryFont(void) +{ + static const WORD resource_ids[] = + { + 3, /* A font that has an invalid full name on Mac platform and a valid full name on Microsoft platform */ + 4, /* A font that has an invalid full name on Unicode platform and a valid full name on Mac platform */ + }; + GpFontCollection *fonts; + GpStatus stat; + int count, i; + void *buffer; + DWORD size; + + for (i = 0; i < ARRAY_SIZE(resource_ids); i++) + { + winetest_push_context("test %d", i); + + stat = GdipNewPrivateFontCollection(&fonts); + ok(stat == Ok, "GdipNewPrivateFontCollection failed, error %d\n", stat); + + load_resource(MAKEINTRESOURCEW(resource_ids[i]), (BYTE **)&buffer, &size); + stat = GdipPrivateAddMemoryFont(fonts, buffer, size); + if (stat == Ok) + { + stat = GdipGetFontCollectionFamilyCount(fonts, &count); + ok(stat == Ok, "GdipGetFontCollectionFamilyCount failed, error %d\n", stat); + todo_wine + ok(count == 1, "Expected count 1, got %d\n", count); + } + else if (i == 1 && stat == FileNotFound) + win_skip("Fonts without Microsoft platform names are unsupported on win7.\n"); + else + ok(0, "GdipPrivateAddMemoryFont failed, error %d\n", stat); + + stat = GdipDeletePrivateFontCollection(&fonts); + ok(stat == Ok, "GdipDeletePrivateFontCollection failed, error %d\n", stat); + + winetest_pop_context(); + } +} + START_TEST(font) { struct GdiplusStartupInput gdiplusStartupInput; @@ -1558,6 +1607,7 @@ START_TEST(font) test_heightgivendpi(); test_GdipGetFontCollectionFamilyList(); test_GdipGetFontCollectionFamilyCount(); + test_GdipPrivateAddMemoryFont(); GdiplusShutdown(gdiplusToken); } diff --git a/dlls/gdiplus/tests/resource.rc b/dlls/gdiplus/tests/resource.rc index 2db8d6359ea..4af61fef65d 100644 --- a/dlls/gdiplus/tests/resource.rc +++ b/dlls/gdiplus/tests/resource.rc @@ -23,3 +23,11 @@ /* @makedep: wine_testfont0.ttf */ 2 RCDATA wine_testfont0.ttf + +/* Generated with: fonttools ttx wine_mac_win.ttx */ +/* @makedep: wine_mac_win.ttf */ +3 RCDATA wine_mac_win.ttf + +/* Generated with: fonttools ttx wine_unicode_mac.ttx */ +/* @makedep: wine_unicode_mac.ttf */ +4 RCDATA wine_unicode_mac.ttf diff --git a/dlls/gdiplus/tests/wine_mac_win.ttf b/dlls/gdiplus/tests/wine_mac_win.ttf new file mode 100644 index 0000000000000000000000000000000000000000..0a0493134deea4cf31e17182df526d9ececd96d2 GIT binary patch literal 1140 zcmds0O=}ZT6g@MOOjB#4%|Z$WnT28tQj=J^kb>1z7j6m>8d{X`F_|xzk1$Qr1O)Rp z6n8EKaak8GM3*j91aT$0bgkmbcwS~~$G_km?!D)ockZ1x?Sok6wel&7v0Jh@gw@0M?(KH^NRQb{j3-EhR1fW zN$HU5QV?5$nWnyGD1~C!#dXw)f6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Copyright (c) 2024 Zhiyi Zhang for CodeWeavers + + + wine_mac_win + + + Regular + + + wine_mac_win + + + invalid_full_name + + + Version 1.0 + + + wine_mac_win + + + Copyright (c) 2024 Zhiyi Zhang for CodeWeavers + + + wine_mac_win + + + Regular + + + wine_mac_win + + + wine_mac_win + + + Version 1.0 + + + wine_mac_win + + + + diff --git a/dlls/gdiplus/tests/wine_unicode_mac.ttf b/dlls/gdiplus/tests/wine_unicode_mac.ttf new file mode 100644 index 0000000000000000000000000000000000000000..4c0af94ade4dd5f9a89246cf5f63ae9320d659c8 GIT binary patch literal 1168 zcmds0O=}ZT6g_W}OroW=bRjN+45HYgq)9BUx+tb@+z1gGT7>a2nU4pVnJ`V-1PW&D zKTyO!poq(E#LuM*7lIo>^cT2LaAiC%GfCrL@P_;Dx#ymH-8R zQU4Yo&XK+6gtCjp+*9Tw`VGG`^vKhU4_rjx%J$3d%3b2e^y>i$^IP#9@hAGlAnXlJ z?_!(M0rOHPb|fRsc+aI2%0U-5Q6v6Mw;~z3_LW!cH$JlGHT2o&Prmp~`^@GqER;w9 zN0(lVbo_p`{8h)yN-#3S9`o|}4USViSv_gq#DvMsDBYQITc}J}qXh92h5O^ewZcOd z&!;i>8J@Ai6T&>{{%3kiMJ~4V4AcAL;n?)VBPR123xX%xHg>g&H=lYr(`=H_>N4`M z81wv1y2oQ?;F_M4SePe+h5m-=4QQdZ`CU`y|6iBzeD;z0znPlgT~0FtOE}8-+#H|B z1w6?30?y)DhRum)dY2Wv&F)oT89y>?ph!Wg;ai&V`8iy`f+%JhbHXBX&w=inDCa)|C!XL^h;$C!4D{C*rO(zLaVs?hX^>2R&=mS+nZZ`i8X^ vsG;I2Bj57k#A?KCx8=$MH`zbr+pznQa!A>sbegW;@5p3_B^5_jty299 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Copyright (c) 2024 Zhiyi Zhang for CodeWeavers + + + wine_unicode_mac + + + Regular + + + wine_unicode_mac + + + invalid_full_name + + + Version 1.0 + + + wine_unicode_mac + + + Copyright (c) 2024 Zhiyi Zhang for CodeWeavers + + + wine_unicode_mac + + + Regular + + + wine_unicode_mac + + + wine_unicode_mac + + + Version 1.0 + + + wine_unicode_mac + + + + From 3a28bd4eb625274f703afb020686065b897fa8e5 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Thu, 28 Mar 2024 17:00:39 +0800 Subject: [PATCH 285/413] gdiplus: Search microsoft platform names first in load_ttf_name_id(). Search names in fonts in the order of Microsoft, Mac and finally Unicode platform. This is also the order win32u uses to load font names. Fix Granado Espada Japan (1219160) launcher crashes at start in Japanese locale. The game ships a font with a broken name record of Mac platform and encoding ID 0 (Roman) but the name string is in code page 10001 (Japanese). This broken name record is placed before the name records for the Microsoft platform so it gets selected first. Then name value in the name record doesn't get converted correctly to Unicode because of the wrong code page. Thus the EnumFontFamiliesExW() in GdipPrivateAddMemoryFont() fails to find the font. CW-Bug-Id: #23597 --- dlls/gdiplus/font.c | 38 +++++++++++++++++++++++--------------- dlls/gdiplus/tests/font.c | 1 - 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/dlls/gdiplus/font.c b/dlls/gdiplus/font.c index da37279fbef..67d75ed97f6 100644 --- a/dlls/gdiplus/font.c +++ b/dlls/gdiplus/font.c @@ -1385,12 +1385,13 @@ static WCHAR *copy_name_table_string( const tt_name_record *name, const BYTE *da static WCHAR *load_ttf_name_id( const BYTE *mem, DWORD_PTR size, DWORD id ) { + static const WORD platform_id_table[] = {TT_PLATFORM_MICROSOFT, TT_PLATFORM_MACINTOSH, TT_PLATFORM_APPLE_UNICODE}; LANGID lang = GetSystemDefaultLangID(); const tt_header *header; const tt_name_table *name_table; - const tt_name_record *name_record; + const tt_name_record *name_record_table, *name_record; DWORD pos, ofs = 0, count; - int i, res, best_lang = 0, best_index = -1; + int i, j, res, best_lang = 0, best_index = -1; if (sizeof(tt_header) > size) return NULL; @@ -1421,26 +1422,33 @@ static WCHAR *load_ttf_name_id( const BYTE *mem, DWORD_PTR size, DWORD id ) if (pos > size) return NULL; name_table = (const tt_name_table*)&mem[ofs]; + name_record_table = (const tt_name_record *)&mem[pos]; count = GET_BE_WORD(name_table->count); if (GET_BE_WORD(name_table->string_offset) >= size - ofs) return NULL; ofs += GET_BE_WORD(name_table->string_offset); - for (i=0; i size) - return NULL; + for (j = 0; j < count; j++) + { + name_record = name_record_table + j; + if ((const BYTE *)name_record - mem > size) + return NULL; - if (GET_BE_WORD(name_record->name_id) != id) continue; - if (GET_BE_WORD(name_record->offset) >= size - ofs) return NULL; - if (GET_BE_WORD(name_record->length) > size - ofs - GET_BE_WORD(name_record->offset)) return NULL; + if (GET_BE_WORD(name_record->platform_id) != platform_id_table[i]) continue; + if (GET_BE_WORD(name_record->name_id) != id) continue; + if (GET_BE_WORD(name_record->offset) >= size - ofs) return NULL; + if (GET_BE_WORD(name_record->length) > size - ofs - GET_BE_WORD(name_record->offset)) return NULL; - res = match_name_table_language( name_record, lang ); - if (res > best_lang) - { - best_lang = res; - best_index = i; + res = match_name_table_language(name_record, lang); + if (res > best_lang) + { + best_lang = res; + best_index = j; + } } + + if (best_index != -1) + break; } if (best_lang) diff --git a/dlls/gdiplus/tests/font.c b/dlls/gdiplus/tests/font.c index 6a21ea30361..1bbb18f4c6d 100644 --- a/dlls/gdiplus/tests/font.c +++ b/dlls/gdiplus/tests/font.c @@ -1558,7 +1558,6 @@ static void test_GdipPrivateAddMemoryFont(void) { stat = GdipGetFontCollectionFamilyCount(fonts, &count); ok(stat == Ok, "GdipGetFontCollectionFamilyCount failed, error %d\n", stat); - todo_wine ok(count == 1, "Expected count 1, got %d\n", count); } else if (i == 1 && stat == FileNotFound) From c0d980564f11ba6e0f65bba994b3e1ea298da237 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Wed, 3 Apr 2024 10:35:29 +0800 Subject: [PATCH 286/413] HACK: ntdll: Enable WINE_SIMULATE_WRITECOPY for Hentai Maid Memories. CW-Bug-Id: #23637 --- dlls/ntdll/unix/loader.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index b7d1f677203..4f8a84c6080 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -2112,7 +2112,8 @@ static void hacks_init(void) || !strcmp(sgi, "2053940") /* Idol Hands 2 */ || !strcmp(sgi, "391150") /* Red Tie Runner */ || !strcmp(sgi, "2152990") /* Dinogen Online */ - || !strcmp(sgi, "2176450"); /* Mr. Hopp's Playhouse 3 */ + || !strcmp(sgi, "2176450") /* Mr. Hopp's Playhouse 3 */ + || !strcmp(sgi, "2361360"); /* Hentai Maid Memories */ if (sgi) wine_allocs_2g_limit = !strcmp(sgi, "359870"); if (wine_allocs_2g_limit) ERR("Allocation 2g limit enabled.\n"); From 1e5c02f9a6030b981db161581f0a4259d4ffa90d Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Fri, 22 Mar 2024 17:08:32 -0300 Subject: [PATCH 287/413] crypt32: Fix CryptBinaryToString not adding a separator. (cherry picked from commit 624e87a7252abd1f03598c68edc56e6815af1ef2) --- dlls/crypt32/base64.c | 16 +++++++++------- dlls/crypt32/tests/base64.c | 8 ++++++++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/dlls/crypt32/base64.c b/dlls/crypt32/base64.c index b61ed7ff8cc..298756ca541 100644 --- a/dlls/crypt32/base64.c +++ b/dlls/crypt32/base64.c @@ -111,8 +111,6 @@ static DWORD encodeBase64A(const BYTE *in_buf, int in_len, LPCSTR sep, i = 0; while (div > 0 && ptr < end) { - if (i && i % 64 == 0) - ptr += stradd(ptr, end, sep, strlen(sep)); /* first char is the first 6 bits of the first byte*/ chunk[0] = b64[ ( d[0] >> 2) & 0x3f ]; /* second char is the last 2 bits of the first byte and the first 4 @@ -127,6 +125,9 @@ static DWORD encodeBase64A(const BYTE *in_buf, int in_len, LPCSTR sep, i += 4; d += 3; div--; + + if (i && i % 64 == 0) + ptr += stradd(ptr, end, sep, strlen(sep)); } switch(pad_bytes) @@ -393,11 +394,6 @@ static LONG encodeBase64W(const BYTE *in_buf, int in_len, LPCWSTR sep, i = 0; while (div > 0) { - if (i && i % 64 == 0) - { - lstrcpyW(ptr, sep); - ptr += lstrlenW(sep); - } /* first char is the first 6 bits of the first byte*/ *ptr++ = b64[ ( d[0] >> 2) & 0x3f ]; /* second char is the last 2 bits of the first byte and the first 4 @@ -411,6 +407,12 @@ static LONG encodeBase64W(const BYTE *in_buf, int in_len, LPCWSTR sep, i += 4; d += 3; div--; + + if (i && i % 64 == 0) + { + lstrcpyW(ptr, sep); + ptr += lstrlenW(sep); + } } switch(pad_bytes) diff --git a/dlls/crypt32/tests/base64.c b/dlls/crypt32/tests/base64.c index e81a57c576d..2263b236fa8 100644 --- a/dlls/crypt32/tests/base64.c +++ b/dlls/crypt32/tests/base64.c @@ -57,6 +57,8 @@ static const BYTE toEncode4[] = static const BYTE toEncode5[] = "abcdefghijlkmnopqrstuvwxyz01234567890ABCDEFGHI"; +static const BYTE toEncode6[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + static const struct BinTests tests[] = { { toEncode1, sizeof(toEncode1), "AA==\r\n", }, { toEncode2, sizeof(toEncode2), "AQI=\r\n", }, @@ -69,6 +71,9 @@ static const struct BinTests tests[] = { "SElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODkwAA==\r\n" }, { toEncode5, sizeof(toEncode5), "YWJjZGVmZ2hpamxrbW5vcHFyc3R1dnd4eXowMTIzNDU2Nzg5MEFCQ0RFRkdISQA=\r\n" }, + { toEncode6, sizeof(toEncode6), + "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh\r\n" + "YQA=\r\n" }, }; static const struct BinTests testsNoCR[] = { @@ -83,6 +88,9 @@ static const struct BinTests testsNoCR[] = { "SElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODkwAA==\n" }, { toEncode5, sizeof(toEncode5), "YWJjZGVmZ2hpamxrbW5vcHFyc3R1dnd4eXowMTIzNDU2Nzg5MEFCQ0RFRkdISQA=\n" }, + { toEncode6, sizeof(toEncode6), + "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh\n" + "YQA=\n" }, }; static WCHAR *strdupAtoW(const char *str) From 45f59c8dcb53c64fcd0a9d2aab23e7577ce46ce7 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 5 Apr 2024 12:11:51 -0600 Subject: [PATCH 288/413] ntdll: Return STATUS_NO_YIELD_PERFORMED from NtYieldExecution() on Linux if no yield was performed. CW-Bug-Id: #23654 --- dlls/ntdll/unix/sync.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index dd08632e21c..23f17e72e08 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -43,6 +43,9 @@ #ifdef HAVE_SCHED_H # include #endif +#ifdef HAVE_SYS_RESOURCE_H +# include +#endif #include #include #include @@ -1626,7 +1629,17 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, NTSTATUS WINAPI NtYieldExecution(void) { #ifdef HAVE_SCHED_YIELD +#ifdef RUSAGE_THREAD + struct rusage u1, u2; + int ret; + + ret = getrusage( RUSAGE_THREAD, &u1 ); +#endif sched_yield(); +#ifdef RUSAGE_THREAD + if (!ret) ret = getrusage( RUSAGE_THREAD, &u2 ); + if (!ret && u1.ru_nvcsw == u2.ru_nvcsw && u1.ru_nivcsw == u2.ru_nivcsw) return STATUS_NO_YIELD_PERFORMED; +#endif return STATUS_SUCCESS; #else return STATUS_NO_YIELD_PERFORMED; From 940efde4c9120a1b8ba2edf1290dcbdaf5a7eadd Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 8 Apr 2024 20:21:09 -0600 Subject: [PATCH 289/413] winhttp: Set actual default receive response timeout to 21sec. CW-Bug-Id: #23656 --- dlls/winhttp/request.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index c75d6fd513f..ee7315170c6 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -43,6 +43,14 @@ WINE_DEFAULT_DEBUG_CHANNEL(winhttp); #define DEFAULT_KEEP_ALIVE_TIMEOUT 30000 +#define ACTUAL_DEFAULT_RECEIVE_RESPONSE_TIMEOUT 21000 + +static int request_receive_response_timeout( struct request *req ) +{ + if (req->receive_response_timeout == -1) return ACTUAL_DEFAULT_RECEIVE_RESPONSE_TIMEOUT; + return req->receive_response_timeout; +} + static const WCHAR *attribute_table[] = { L"Mime-Version", /* WINHTTP_QUERY_MIME_VERSION = 0 */ @@ -1658,7 +1666,7 @@ static DWORD open_connection( struct request *request ) return ret; } netconn_set_timeout( netconn, TRUE, request->send_timeout ); - netconn_set_timeout( netconn, FALSE, request->receive_response_timeout ); + netconn_set_timeout( netconn, FALSE, request_receive_response_timeout( request )); request->netconn = netconn; @@ -1696,7 +1704,7 @@ static DWORD open_connection( struct request *request ) TRACE("using connection %p\n", netconn); netconn_set_timeout( netconn, TRUE, request->send_timeout ); - netconn_set_timeout( netconn, FALSE, request->receive_response_timeout ); + netconn_set_timeout( netconn, FALSE, request_receive_response_timeout( request )); request->netconn = netconn; } @@ -2329,7 +2337,7 @@ static DWORD send_request( struct request *request, const WCHAR *headers, DWORD if (!chunked && content_length <= optional_len) { - netconn_set_timeout( request->netconn, FALSE, request->receive_response_timeout ); + netconn_set_timeout( request->netconn, FALSE, request_receive_response_timeout( request )); request->read_reply_status = read_reply( request ); if (request->state == REQUEST_RESPONSE_STATE_READ_RESPONSE_QUEUED) request->state = REQUEST_RESPONSE_STATE_READ_RESPONSE_QUEUED_REPLY_RECEIVED; @@ -2932,7 +2940,7 @@ static DWORD receive_response( struct request *request ) } /* fallthrough */ case REQUEST_RESPONSE_STATE_READ_RESPONSE_QUEUED_REQUEST_SENT: - netconn_set_timeout( request->netconn, FALSE, request->receive_response_timeout ); + netconn_set_timeout( request->netconn, FALSE, request_receive_response_timeout( request )); request->read_reply_status = read_reply( request ); request->state = REQUEST_RESPONSE_STATE_REPLY_RECEIVED; break; From 56b7e38adad2253e5f330de28b629199a8347562 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 8 Apr 2024 22:35:05 -0600 Subject: [PATCH 290/413] winhttp/tests: Add some tests for actual connection caching. CW-Bug-Id: #23656 --- dlls/winhttp/tests/winhttp.c | 114 +++++++++++++++++++++++++++++++---- 1 file changed, 102 insertions(+), 12 deletions(-) diff --git a/dlls/winhttp/tests/winhttp.c b/dlls/winhttp/tests/winhttp.c index 09abc4c5854..8aa03596732 100644 --- a/dlls/winhttp/tests/winhttp.c +++ b/dlls/winhttp/tests/winhttp.c @@ -2197,6 +2197,12 @@ static const char okmsg[] = "Server: winetest\r\n" "\r\n"; +static const char okmsg_length0[] = +"HTTP/1.1 200 OK\r\n" +"Server: winetest\r\n" +"Content-length: 0\r\n" +"\r\n"; + static const char notokmsg[] = "HTTP/1.1 400 Bad Request\r\n" "\r\n"; @@ -2318,6 +2324,24 @@ static void create_websocket_accept(const char *key, char *buf, unsigned int buf CryptBinaryToStringA( (BYTE *)sha1, sizeof(sha1), CRYPT_STRING_BASE64, buf, &len); } +static int server_receive_request(int c, char *buffer, size_t buffer_size) +{ + int i, r; + + memset(buffer, 0, buffer_size); + for(i = 0; i < buffer_size - 1; i++) + { + r = recv(c, &buffer[i], 1, 0); + if (r != 1) + break; + if (i < 4) continue; + if (buffer[i - 2] == '\n' && buffer[i] == '\n' && + buffer[i - 3] == '\r' && buffer[i - 1] == '\r') + break; + } + return r; +} + static DWORD CALLBACK server_thread(LPVOID param) { struct server_info *si = param; @@ -2351,18 +2375,7 @@ static DWORD CALLBACK server_thread(LPVOID param) do { if (c == -1) c = accept(s, NULL, NULL); - - memset(buffer, 0, sizeof buffer); - for(i = 0; i < sizeof buffer - 1; i++) - { - r = recv(c, &buffer[i], 1, 0); - if (r != 1) - break; - if (i < 4) continue; - if (buffer[i - 2] == '\n' && buffer[i] == '\n' && - buffer[i - 3] == '\r' && buffer[i - 1] == '\r') - break; - } + server_receive_request(c, buffer, sizeof(buffer)); if (strstr(buffer, "GET /basic")) { send(c, okmsg, sizeof okmsg - 1, 0); @@ -2558,6 +2571,23 @@ static DWORD CALLBACK server_thread(LPVOID param) } send(c, okmsg, sizeof(okmsg) - 1, 0); } + + if (strstr(buffer, "GET /cached")) + { + send(c, okmsg_length0, sizeof okmsg_length0 - 1, 0); + r = server_receive_request(c, buffer, sizeof(buffer)); + ok(r > 0, "got %d.\n", r); + ok(!!strstr(buffer, "GET /cached"), "request not found.\n"); + send(c, okmsg_length0, sizeof okmsg_length0 - 1, 0); + r = server_receive_request(c, buffer, sizeof(buffer)); + todo_wine ok(!r, "got %d, buffer[0] %d.\n", r, buffer[0]); + } + if (strstr(buffer, "GET /notcached")) + { + send(c, okmsg, sizeof okmsg - 1, 0); + r = server_receive_request(c, buffer, sizeof(buffer)); + ok(!r, "got %d, buffer[0] %d.\n", r, buffer[0] ); + } shutdown(c, 2); closesocket(c); c = -1; @@ -5822,6 +5852,65 @@ static void test_client_cert_authentication(void) WinHttpCloseHandle( ses ); } +static void test_connection_cache(int port) +{ + HINTERNET ses, con, req; + DWORD status, size; + char buffer[256]; + BOOL ret; + + ses = WinHttpOpen(L"winetest", WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0); + ok(ses != NULL, "failed to open session %lu\n", GetLastError()); + + con = WinHttpConnect(ses, L"localhost", port, 0); + ok(con != NULL, "failed to open a connection %lu\n", GetLastError()); + + req = WinHttpOpenRequest(con, L"GET", L"/cached", NULL, NULL, NULL, 0); + ok(req != NULL, "failed to open a request %lu\n", GetLastError()); + ret = WinHttpSendRequest(req, NULL, 0, NULL, 0, 0, 0); + ok(ret, "failed to send request %lu\n", GetLastError()); + ret = WinHttpReceiveResponse(req, NULL); + ok(ret, "failed to receive response %lu\n", GetLastError()); + size = sizeof(status); + ret = WinHttpQueryHeaders(req, WINHTTP_QUERY_STATUS_CODE|WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL); + ok(ret, "failed to query status code %lu\n", GetLastError()); + ok(status == HTTP_STATUS_OK, "request failed unexpectedly %lu\n", status); + ret = WinHttpReadData(req, buffer, sizeof buffer, &size); + ok(ret, "failed to read data %lu\n", GetLastError()); + ok(!size, "got size %lu.\n", size); + WinHttpCloseHandle(req); + + req = WinHttpOpenRequest(con, L"GET", L"/cached", NULL, NULL, NULL, 0); + ok(req != NULL, "failed to open a request %lu\n", GetLastError()); + ret = WinHttpSendRequest(req, L"Connection: close", ~0ul, NULL, 0, 0, 0); + ok(ret, "failed to send request %lu\n", GetLastError()); + ret = WinHttpReceiveResponse(req, NULL); + ok(ret, "failed to receive response %lu\n", GetLastError()); + size = sizeof(status); + ret = WinHttpQueryHeaders(req, WINHTTP_QUERY_STATUS_CODE|WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL); + ok(ret, "failed to query status code %lu\n", GetLastError()); + ok(status == HTTP_STATUS_OK, "request failed unexpectedly %lu\n", status); + ret = WinHttpReadData(req, buffer, sizeof buffer, &size); + ok(ret, "failed to read data %lu\n", GetLastError()); + ok(!size, "got size %lu.\n", size); + WinHttpCloseHandle(req); + + req = WinHttpOpenRequest(con, L"GET", L"/notcached", NULL, NULL, NULL, 0); + ok(req != NULL, "failed to open a request %lu\n", GetLastError()); + ret = WinHttpSendRequest(req, L"Connection: close", ~0ul, NULL, 0, 0, 0); + ok(ret, "failed to send request %lu\n", GetLastError()); + ret = WinHttpReceiveResponse(req, NULL); + ok(ret, "failed to receive response %lu\n", GetLastError()); + size = sizeof(status); + ret = WinHttpQueryHeaders(req, WINHTTP_QUERY_STATUS_CODE|WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL); + ok(ret, "failed to query status code %lu\n", GetLastError()); + ok(status == HTTP_STATUS_OK, "request failed unexpectedly %lu\n", status); + WinHttpCloseHandle(req); + + WinHttpCloseHandle(con); + WinHttpCloseHandle(ses); +} + START_TEST (winhttp) { struct server_info si; @@ -5889,6 +5978,7 @@ START_TEST (winhttp) test_passport_auth(si.port); test_websocket(si.port); test_redirect(si.port); + test_connection_cache(si.port); /* send the basic request again to shutdown the server thread */ test_basic_request(si.port, NULL, L"/quit"); From 43b602721cf1f12d18c3daffe5a0ffd8d12c6fed Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 8 Apr 2024 22:45:08 -0600 Subject: [PATCH 291/413] winhttp: Do not cache connection if suggested by request headers. CW-Bug-Id: #23656 --- dlls/winhttp/request.c | 20 +++++++++----------- dlls/winhttp/tests/winhttp.c | 2 +- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index ee7315170c6..17621af8647 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -1922,7 +1922,7 @@ static DWORD refill_buffer( struct request *request, BOOL notify ) static void finished_reading( struct request *request ) { - BOOL close = FALSE, notify; + BOOL close = FALSE, close_request_headers; WCHAR connection[20]; DWORD size = sizeof(connection); @@ -1937,18 +1937,16 @@ static void finished_reading( struct request *request ) } else if (!wcscmp( request->version, L"HTTP/1.0" )) close = TRUE; - if (close) + size = sizeof(connection); + close_request_headers = + (!query_headers( request, WINHTTP_QUERY_CONNECTION | WINHTTP_QUERY_FLAG_REQUEST_HEADERS, NULL, connection, &size, NULL ) + || !query_headers( request, WINHTTP_QUERY_PROXY_CONNECTION | WINHTTP_QUERY_FLAG_REQUEST_HEADERS, NULL, connection, &size, NULL )) + && !wcsicmp( connection, L"close" ); + if (close || close_request_headers) { - size = sizeof(connection); - notify = (!query_headers( request, WINHTTP_QUERY_CONNECTION | WINHTTP_QUERY_FLAG_REQUEST_HEADERS, - NULL, connection, &size, NULL ) - || !query_headers( request, WINHTTP_QUERY_PROXY_CONNECTION | WINHTTP_QUERY_FLAG_REQUEST_HEADERS, - NULL, connection, &size, NULL )) - && !wcsicmp( connection, L"close" ); - - if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, 0, 0 ); + if (close_request_headers) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, 0, 0 ); netconn_release( request->netconn ); - if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, 0, 0 ); + if (close_request_headers) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, 0, 0 ); } else cache_connection( request->netconn ); diff --git a/dlls/winhttp/tests/winhttp.c b/dlls/winhttp/tests/winhttp.c index 8aa03596732..2c3f1791898 100644 --- a/dlls/winhttp/tests/winhttp.c +++ b/dlls/winhttp/tests/winhttp.c @@ -2580,7 +2580,7 @@ static DWORD CALLBACK server_thread(LPVOID param) ok(!!strstr(buffer, "GET /cached"), "request not found.\n"); send(c, okmsg_length0, sizeof okmsg_length0 - 1, 0); r = server_receive_request(c, buffer, sizeof(buffer)); - todo_wine ok(!r, "got %d, buffer[0] %d.\n", r, buffer[0]); + ok(!r, "got %d, buffer[0] %d.\n", r, buffer[0]); } if (strstr(buffer, "GET /notcached")) { From 8427e389c2755fdf4254f490436ab0f80d5d1600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Mar 2024 10:55:06 +0100 Subject: [PATCH 292/413] winegstreamer/media-converter: Split videoconv caps event handler to a separate helper. CW-Bug-Id: #22319 --- .../winegstreamer/media-converter/videoconv.c | 71 ++++++++++--------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/dlls/winegstreamer/media-converter/videoconv.c b/dlls/winegstreamer/media-converter/videoconv.c index b91aa96f1bf..f92c3300604 100644 --- a/dlls/winegstreamer/media-converter/videoconv.c +++ b/dlls/winegstreamer/media-converter/videoconv.c @@ -663,50 +663,55 @@ static void video_conv_init_transcode(VideoConv *conv) pthread_mutex_unlock(&dump_fozdb.mutex); } -static gboolean video_conv_sink_event(GstPad *pad, GstObject *parent, GstEvent *event) +static gboolean video_conv_sink_event_caps(VideoConv *conv, GstEvent *event) { - VideoConv *conv = VIDEO_CONV(parent); + struct video_conv_state *state; + uint32_t transcode_tag; + GstCaps *caps; bool ret; - GST_DEBUG_OBJECT(pad, "Got event %"GST_PTR_FORMAT".", event); + gst_event_unref(event); - if (event->type == GST_EVENT_CAPS) + /* push_event, below, can also grab state and cause a deadlock, so make sure it's + * unlocked before calling */ + if (!(state = video_conv_lock_state(conv))) { - struct video_conv_state *state; - uint32_t transcode_tag; - GstCaps *caps; + GST_ERROR("VideoConv not yet in READY state?"); + return false; + } - /* push_event, below, can also grab state and cause a deadlock, so make sure it's - * unlocked before calling */ - if (!(state = video_conv_lock_state(conv))) - { - GST_ERROR("VideoConv not yet in READY state?"); - return false; - } + if (!gst_pad_activate_mode(conv->sink_pad, GST_PAD_MODE_PULL, true)) + { + GST_ERROR("Failed to activate sink pad in pull mode."); + pthread_mutex_unlock(&conv->state_mutex); + return false; + } - if (!gst_pad_activate_mode(conv->sink_pad, GST_PAD_MODE_PULL, true)) - { - GST_ERROR("Failed to activate sink pad in pull mode."); - pthread_mutex_unlock(&conv->state_mutex); - return false; - } + video_conv_init_transcode(conv); + transcode_tag = state->transcoded_tag; - video_conv_init_transcode(conv); - transcode_tag = state->transcoded_tag; + pthread_mutex_unlock(&conv->state_mutex); - pthread_mutex_unlock(&conv->state_mutex); + if (transcode_tag == VIDEO_CONV_FOZ_TAG_MKVDATA) + caps = gst_caps_from_string("video/x-matroska"); + else if (transcode_tag == VIDEO_CONV_FOZ_TAG_OGVDATA) + caps = gst_caps_from_string("application/ogg"); + else + return false; - if (transcode_tag == VIDEO_CONV_FOZ_TAG_MKVDATA) - caps = gst_caps_from_string("video/x-matroska"); - else if (transcode_tag == VIDEO_CONV_FOZ_TAG_OGVDATA) - caps = gst_caps_from_string("application/ogg"); - else - return false; + ret = push_event(conv->src_pad, gst_event_new_caps(caps)); + gst_caps_unref(caps); + return ret; +} - ret = push_event(conv->src_pad, gst_event_new_caps(caps)); - gst_caps_unref(caps); - return ret; - } +static gboolean video_conv_sink_event(GstPad *pad, GstObject *parent, GstEvent *event) +{ + VideoConv *conv = VIDEO_CONV(parent); + + GST_DEBUG_OBJECT(pad, "Got event %"GST_PTR_FORMAT".", event); + + if (event->type == GST_EVENT_CAPS) + return video_conv_sink_event_caps(conv, event); return gst_pad_event_default(pad, parent, event); } From d3aea4ba5abd3699cc1fad4296b9de6f85d319af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Mar 2024 11:04:31 +0100 Subject: [PATCH 293/413] winegstreamer/media-converter: Split videoconv upstream chunk dump to a separate helper. CW-Bug-Id: #22319 --- .../winegstreamer/media-converter/videoconv.c | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/dlls/winegstreamer/media-converter/videoconv.c b/dlls/winegstreamer/media-converter/videoconv.c index f92c3300604..9597d686abb 100644 --- a/dlls/winegstreamer/media-converter/videoconv.c +++ b/dlls/winegstreamer/media-converter/videoconv.c @@ -576,6 +576,22 @@ static bool video_conv_hash_upstream_data(VideoConv *conv, struct payload_hash * return ret; } +static int video_conv_dump_upstream_chunk(VideoConv *conv, void *buffer, size_t read_size, + GList **chunk_hashes) +{ + struct bytes_reader bytes_reader; + struct payload_hash *chunk_hash; + + bytes_reader_init(&bytes_reader, buffer, read_size); + chunk_hash = calloc(1, sizeof(*chunk_hash)); + murmur3_128(&bytes_reader, bytes_reader_read, HASH_SEED, chunk_hash); + *chunk_hashes = g_list_append(*chunk_hashes, chunk_hash); + + bytes_reader_init(&bytes_reader, buffer, read_size); + return fozdb_write_entry(dump_fozdb.fozdb, VIDEO_CONV_FOZ_TAG_VIDEODATA, chunk_hash, + &bytes_reader, bytes_reader_read, true); +} + static int video_conv_dump_upstream_data(VideoConv *conv, struct payload_hash *hash) { struct hashes_reader chunk_hashes_reader; @@ -597,17 +613,7 @@ static int video_conv_dump_upstream_data(VideoConv *conv, struct payload_hash *h pad_reader = pad_reader_create(conv->sink_pad); while ((ret = pad_reader_read(pad_reader, buffer, HASH_CHUNK_SIZE, &read_size)) == CONV_OK) { - struct bytes_reader bytes_reader; - struct payload_hash *chunk_hash; - - bytes_reader_init(&bytes_reader, buffer, read_size); - chunk_hash = calloc(1, sizeof(*chunk_hash)); - murmur3_128(&bytes_reader, bytes_reader_read, HASH_SEED, chunk_hash); - chunk_hashes = g_list_append(chunk_hashes, chunk_hash); - - bytes_reader_init(&bytes_reader, buffer, read_size); - if ((ret = fozdb_write_entry(dump_fozdb.fozdb, VIDEO_CONV_FOZ_TAG_VIDEODATA, chunk_hash, - &bytes_reader, bytes_reader_read, true)) < 0) + if ((ret = video_conv_dump_upstream_chunk(conv, buffer, read_size, &chunk_hashes)) < 0) { GST_ERROR("Error writing video data to fozdb, ret %d.", ret); goto done; From 500acada1646f3c95d2aaba6387e809df8da6357 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Mar 2024 11:52:12 +0100 Subject: [PATCH 294/413] winegstreamer/media-converter: Split videoconv stream-start event push to a separate helper. CW-Bug-Id: #22319 --- .../winegstreamer/media-converter/videoconv.c | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/dlls/winegstreamer/media-converter/videoconv.c b/dlls/winegstreamer/media-converter/videoconv.c index 9597d686abb..28b391b2d68 100644 --- a/dlls/winegstreamer/media-converter/videoconv.c +++ b/dlls/winegstreamer/media-converter/videoconv.c @@ -669,6 +669,23 @@ static void video_conv_init_transcode(VideoConv *conv) pthread_mutex_unlock(&dump_fozdb.mutex); } +static gboolean video_conv_push_stream_start(VideoConv *conv, struct payload_hash *hash) +{ + struct video_conv_state *state; + + push_event(conv->src_pad, gst_event_new_stream_start(format_hash(hash))); + + if (!(state = video_conv_lock_state(conv))) + { + GST_ERROR("VideoConv not yet in READY state?"); + return false; + } + state->need_stream_start = false; + pthread_mutex_unlock(&conv->state_mutex); + + return true; +} + static gboolean video_conv_sink_event_caps(VideoConv *conv, GstEvent *event) { struct video_conv_state *state; @@ -885,18 +902,7 @@ static gboolean video_conv_src_active_mode(GstPad *pad, GstObject *parent, GstPa pthread_mutex_unlock(&conv->state_mutex); if (need_stream_start && active && has_transcoded) - { - push_event(conv->src_pad, gst_event_new_stream_start(format_hash(&hash))); - - if (!(state = video_conv_lock_state(conv))) - { - GST_ERROR("VideoConv not yet in READY state?"); - return false; - } - state->need_stream_start = false; - pthread_mutex_unlock(&conv->state_mutex); - } - + return video_conv_push_stream_start(conv, &hash); return true; } From ecb7dd760b308c03d4a8ea6d13693370d8a61162 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Mar 2024 17:10:58 +0100 Subject: [PATCH 295/413] winegstreamer/media-converter: Split videoconv event caps push to a separate helper. CW-Bug-Id: #22319 --- .../winegstreamer/media-converter/videoconv.c | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/dlls/winegstreamer/media-converter/videoconv.c b/dlls/winegstreamer/media-converter/videoconv.c index 28b391b2d68..4a58a989fdf 100644 --- a/dlls/winegstreamer/media-converter/videoconv.c +++ b/dlls/winegstreamer/media-converter/videoconv.c @@ -686,12 +686,27 @@ static gboolean video_conv_push_stream_start(VideoConv *conv, struct payload_has return true; } +static gboolean video_conv_push_caps(VideoConv *conv, uint32_t transcode_tag) +{ + GstCaps *caps; + gboolean ret; + + if (transcode_tag == VIDEO_CONV_FOZ_TAG_MKVDATA) + caps = gst_caps_from_string("video/x-matroska"); + else if (transcode_tag == VIDEO_CONV_FOZ_TAG_OGVDATA) + caps = gst_caps_from_string("application/ogg"); + else + return false; + + ret = push_event(conv->src_pad, gst_event_new_caps(caps)); + gst_caps_unref(caps); + return ret; +} + static gboolean video_conv_sink_event_caps(VideoConv *conv, GstEvent *event) { struct video_conv_state *state; uint32_t transcode_tag; - GstCaps *caps; - bool ret; gst_event_unref(event); @@ -715,16 +730,7 @@ static gboolean video_conv_sink_event_caps(VideoConv *conv, GstEvent *event) pthread_mutex_unlock(&conv->state_mutex); - if (transcode_tag == VIDEO_CONV_FOZ_TAG_MKVDATA) - caps = gst_caps_from_string("video/x-matroska"); - else if (transcode_tag == VIDEO_CONV_FOZ_TAG_OGVDATA) - caps = gst_caps_from_string("application/ogg"); - else - return false; - - ret = push_event(conv->src_pad, gst_event_new_caps(caps)); - gst_caps_unref(caps); - return ret; + return video_conv_push_caps(conv, transcode_tag); } static gboolean video_conv_sink_event(GstPad *pad, GstObject *parent, GstEvent *event) From e61bf268f67dad3dc49ca2f77b173b25a50f6d73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Mar 2024 10:54:28 +0100 Subject: [PATCH 296/413] winegstreamer/media-converter: Open the blank file on videoconv state creation. CW-Bug-Id: #22319 --- .../winegstreamer/media-converter/videoconv.c | 73 +++++++------------ 1 file changed, 26 insertions(+), 47 deletions(-) diff --git a/dlls/winegstreamer/media-converter/videoconv.c b/dlls/winegstreamer/media-converter/videoconv.c index 4a58a989fdf..bf2c4c6b168 100644 --- a/dlls/winegstreamer/media-converter/videoconv.c +++ b/dlls/winegstreamer/media-converter/videoconv.c @@ -92,6 +92,7 @@ struct video_conv_state { struct payload_hash transcode_hash; struct fozdb *read_fozdb; + int blank_file; uint64_t upstream_duration; uint64_t our_duration; uint32_t transcoded_tag; @@ -331,22 +332,37 @@ static int video_conv_state_create(struct video_conv_state **out) { struct video_conv_state *state; struct fozdb *fozdb = NULL; - char *read_fozdb_path; - int ret; + char *read_fozdb_path, *blank_video; + uint64_t blank_file_size; + int ret, fd; if (!(read_fozdb_path = getenv("MEDIACONV_VIDEO_TRANSCODED_FILE"))) { GST_ERROR("MEDIACONV_VIDEO_TRANSCODED_FILE is not set."); return CONV_ERROR_ENV_NOT_SET; } + if (!(blank_video = getenv("MEDIACONV_BLANK_VIDEO_FILE"))) + { + GST_ERROR("Env MEDIACONV_BLANK_VIDEO_FILE not set."); + return CONV_ERROR_ENV_NOT_SET; + } + + if (!open_file(blank_video, O_RDONLY, &fd)) + return CONV_ERROR_OPEN_FAILED; + if (!get_file_size(fd, &blank_file_size)) + { + close(fd); + return CONV_ERROR_OPEN_FAILED; + } if ((ret = fozdb_create(read_fozdb_path, O_RDONLY, true /* Read-only? */, VIDEO_CONV_FOZ_NUM_TAGS, &fozdb)) < 0) GST_ERROR("Failed to create read fozdb from %s, ret %d.", read_fozdb_path, ret); state = calloc(1, sizeof(*state)); state->read_fozdb = fozdb; + state->blank_file = fd; state->upstream_duration = DURATION_NONE; - state->our_duration = DURATION_NONE; + state->our_duration = blank_file_size; state->transcoded_tag = VIDEO_CONV_FOZ_TAG_MKVDATA; state->need_stream_start = true; @@ -358,16 +374,13 @@ static void video_conv_state_release(struct video_conv_state *state) { if (state->read_fozdb) fozdb_release(state->read_fozdb); + close(state->blank_file); free(state); } /* Return true if the file is transcoded, false if not. */ bool video_conv_state_begin_transcode(struct video_conv_state *state, struct payload_hash *hash) { - const char *blank_video; - uint64_t file_size = 0; - int fd; - GST_DEBUG("state %p, hash %s.", state, format_hash(hash)); if (state->read_fozdb) @@ -396,18 +409,6 @@ bool video_conv_state_begin_transcode(struct video_conv_state *state, struct pay } GST_INFO("No transcoded video for %s. Substituting a blank video.", format_hash(hash)); - - if (!(blank_video = getenv("MEDIACONV_BLANK_VIDEO_FILE"))) - { - GST_ERROR("Env MEDIACONV_BLANK_VIDEO_FILE not set."); - return false; - } - if (open_file(blank_video, O_RDONLY, &fd)) - { - get_file_size(fd, &file_size); - close(fd); - } - state->our_duration = file_size; state->has_transcoded = false; create_placeholder_file("placeholder-video-used"); @@ -418,11 +419,9 @@ bool video_conv_state_begin_transcode(struct video_conv_state *state, struct pay int video_conv_state_fill_buffer(struct video_conv_state *state, uint64_t offset, uint8_t *buffer, size_t size, size_t *fill_size) { - const char *blank_video; - uint64_t file_size; size_t to_copy; bool read_ok; - int fd, ret; + int ret; if (state->has_transcoded) { @@ -433,38 +432,18 @@ int video_conv_state_fill_buffer(struct video_conv_state *state, uint64_t offset } else /* Fill blank video data to buffer. */ { - if (!(blank_video = getenv("MEDIACONV_BLANK_VIDEO_FILE"))) - { - GST_ERROR("Env MEDIACONV_BLANK_VIDEO_FILE not set."); - return CONV_ERROR_ENV_NOT_SET; - } - if (!open_file(blank_video, O_RDONLY, &fd)) - return CONV_ERROR_OPEN_FAILED; - if (!get_file_size(fd, &file_size)) - { - close(fd); - return CONV_ERROR; - } - /* Get copy size. */ - if (offset >= file_size) - { - close(fd); + if (offset >= state->our_duration) return CONV_OK; - } - to_copy = min(file_size - offset, size); + to_copy = min(state->our_duration - offset, size); /* Copy data. */ - if (lseek(fd, offset, SEEK_SET) < 0) + if (lseek(state->blank_file, offset, SEEK_SET) < 0) { - GST_ERROR("Failed to seek %s to %#"PRIx64". %s.", blank_video, offset, strerror(errno)); - close(fd); + GST_ERROR("Failed to seek to %#"PRIx64". %s.", offset, strerror(errno)); return CONV_ERROR; } - read_ok = complete_read(fd, buffer, to_copy); - close(fd); - - if (!read_ok) + if (!(read_ok = complete_read(state->blank_file, buffer, to_copy))) { GST_ERROR("Failed to read blank video data."); return CONV_ERROR_READ_FAILED; From 819d2a32d52af591027b17e4db63ee1b9444afbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Mar 2024 10:54:28 +0100 Subject: [PATCH 297/413] winegstreamer/media-converter: Use an enum for video conv state flags. CW-Bug-Id: #22319 --- .../winegstreamer/media-converter/videoconv.c | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/dlls/winegstreamer/media-converter/videoconv.c b/dlls/winegstreamer/media-converter/videoconv.c index bf2c4c6b168..cf3f99fe7a2 100644 --- a/dlls/winegstreamer/media-converter/videoconv.c +++ b/dlls/winegstreamer/media-converter/videoconv.c @@ -88,6 +88,12 @@ struct hashes_reader GList *current_hash; }; +enum video_conv_state_flags +{ + VIDEO_CONV_STREAM_STARTED = 1, + VIDEO_CONV_HAS_TRANSCODED = 2, +}; + struct video_conv_state { struct payload_hash transcode_hash; @@ -96,7 +102,7 @@ struct video_conv_state uint64_t upstream_duration; uint64_t our_duration; uint32_t transcoded_tag; - bool has_transcoded, need_stream_start; + uint32_t state_flags; }; typedef struct @@ -364,7 +370,6 @@ static int video_conv_state_create(struct video_conv_state **out) state->upstream_duration = DURATION_NONE; state->our_duration = blank_file_size; state->transcoded_tag = VIDEO_CONV_FOZ_TAG_MKVDATA; - state->need_stream_start = true; *out = state; return CONV_OK; @@ -393,7 +398,7 @@ bool video_conv_state_begin_transcode(struct video_conv_state *state, struct pay state->transcode_hash = *hash; state->our_duration = entry_size; state->transcoded_tag = VIDEO_CONV_FOZ_TAG_MKVDATA; - state->has_transcoded = true; + state->state_flags |= VIDEO_CONV_HAS_TRANSCODED; return true; } @@ -403,13 +408,13 @@ bool video_conv_state_begin_transcode(struct video_conv_state *state, struct pay state->transcode_hash = *hash; state->our_duration = entry_size; state->transcoded_tag = VIDEO_CONV_FOZ_TAG_OGVDATA; - state->has_transcoded = true; + state->state_flags |= VIDEO_CONV_HAS_TRANSCODED; return true; } } GST_INFO("No transcoded video for %s. Substituting a blank video.", format_hash(hash)); - state->has_transcoded = false; + state->state_flags &= ~VIDEO_CONV_HAS_TRANSCODED; create_placeholder_file("placeholder-video-used"); @@ -423,7 +428,7 @@ int video_conv_state_fill_buffer(struct video_conv_state *state, uint64_t offset bool read_ok; int ret; - if (state->has_transcoded) + if (state->state_flags & VIDEO_CONV_HAS_TRANSCODED) { if ((ret = fozdb_read_entry_data(state->read_fozdb, state->transcoded_tag, &state->transcode_hash, offset, buffer, size, fill_size, false)) < 0) @@ -626,7 +631,7 @@ static void video_conv_init_transcode(VideoConv *conv) struct payload_hash hash; int ret; - if (state->has_transcoded) + if (state->state_flags & VIDEO_CONV_HAS_TRANSCODED) return; pthread_mutex_lock(&dump_fozdb.mutex); @@ -659,7 +664,7 @@ static gboolean video_conv_push_stream_start(VideoConv *conv, struct payload_has GST_ERROR("VideoConv not yet in READY state?"); return false; } - state->need_stream_start = false; + state->state_flags |= VIDEO_CONV_STREAM_STARTED; pthread_mutex_unlock(&conv->state_mutex); return true; @@ -856,8 +861,7 @@ static gboolean video_conv_src_active_mode(GstPad *pad, GstObject *parent, GstPa VideoConv *conv = VIDEO_CONV(parent); struct video_conv_state *state; struct payload_hash hash; - bool need_stream_start; - bool has_transcoded; + uint32_t state_flags; GST_DEBUG_OBJECT(pad, "mode %s, active %d.", gst_pad_mode_get_name(mode), active); @@ -879,14 +883,13 @@ static gboolean video_conv_src_active_mode(GstPad *pad, GstObject *parent, GstPa video_conv_init_transcode(conv); hash = state->transcode_hash; - need_stream_start = state->need_stream_start; - has_transcoded = state->has_transcoded; + state_flags = state->state_flags; /* push_event, below, can also grab state and cause a deadlock, so make sure it's * unlocked before calling */ pthread_mutex_unlock(&conv->state_mutex); - if (need_stream_start && active && has_transcoded) + if (active && !(state_flags & VIDEO_CONV_STREAM_STARTED) && (state_flags & VIDEO_CONV_HAS_TRANSCODED)) return video_conv_push_stream_start(conv, &hash); return true; } From 87946fbc614005ae1816013f0b6a301718d8304d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 29 Mar 2024 15:17:07 +0100 Subject: [PATCH 298/413] winegstreamer/media-converter: Wrap around the blank video if shorter than upstream data. CW-Bug-Id: #22319 --- dlls/winegstreamer/media-converter/videoconv.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dlls/winegstreamer/media-converter/videoconv.c b/dlls/winegstreamer/media-converter/videoconv.c index cf3f99fe7a2..3e2e17018f4 100644 --- a/dlls/winegstreamer/media-converter/videoconv.c +++ b/dlls/winegstreamer/media-converter/videoconv.c @@ -437,9 +437,7 @@ int video_conv_state_fill_buffer(struct video_conv_state *state, uint64_t offset } else /* Fill blank video data to buffer. */ { - /* Get copy size. */ - if (offset >= state->our_duration) - return CONV_OK; + offset = offset % state->our_duration; to_copy = min(state->our_duration - offset, size); /* Copy data. */ From 5f86fb7c7c9f8584f22b35d078312686d29cfed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 29 Mar 2024 15:17:07 +0100 Subject: [PATCH 299/413] winegstreamer/media-converter: Use gst_util_uint64_scale_round to round upstream position. CW-Bug-Id: #22319 --- dlls/winegstreamer/media-converter/videoconv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winegstreamer/media-converter/videoconv.c b/dlls/winegstreamer/media-converter/videoconv.c index 3e2e17018f4..32a097e2583 100644 --- a/dlls/winegstreamer/media-converter/videoconv.c +++ b/dlls/winegstreamer/media-converter/videoconv.c @@ -510,7 +510,7 @@ static uint64_t video_conv_duration_ours_to_upstream(VideoConv *conv, uint64_t p struct video_conv_state *state = conv->state; if (state->upstream_duration != DURATION_NONE && state->our_duration != DURATION_NONE) - return pos * state->upstream_duration / state->our_duration; + return gst_util_uint64_scale_round(pos, state->upstream_duration, state->our_duration); else return DURATION_NONE; } From c50f302b1892a350f0a47c91ee496bc88be2ff79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 29 Mar 2024 15:17:07 +0100 Subject: [PATCH 300/413] winegstreamer/media-converter: Implement support for push mode in videoconv. CW-Bug-Id: #22319 --- .../winegstreamer/media-converter/videoconv.c | 334 +++++++++++++++++- 1 file changed, 319 insertions(+), 15 deletions(-) diff --git a/dlls/winegstreamer/media-converter/videoconv.c b/dlls/winegstreamer/media-converter/videoconv.c index 32a097e2583..99c451c4a17 100644 --- a/dlls/winegstreamer/media-converter/videoconv.c +++ b/dlls/winegstreamer/media-converter/videoconv.c @@ -59,6 +59,7 @@ #endif #include "media-converter.h" +#include #include @@ -92,6 +93,7 @@ enum video_conv_state_flags { VIDEO_CONV_STREAM_STARTED = 1, VIDEO_CONV_HAS_TRANSCODED = 2, + VIDEO_CONV_IS_DUMPING = 4, }; struct video_conv_state @@ -103,6 +105,8 @@ struct video_conv_state uint64_t our_duration; uint32_t transcoded_tag; uint32_t state_flags; + uint64_t read_offset; + GList *chunk_hashes; }; typedef struct @@ -111,6 +115,8 @@ typedef struct GstPad *sink_pad, *src_pad; pthread_mutex_t state_mutex; struct video_conv_state *state; + GstPadMode active_mode; + GstAdapter *adapter; } VideoConv; typedef struct @@ -388,6 +394,8 @@ bool video_conv_state_begin_transcode(struct video_conv_state *state, struct pay { GST_DEBUG("state %p, hash %s.", state, format_hash(hash)); + state->transcode_hash = *hash; + if (state->read_fozdb) { uint32_t entry_size; @@ -395,7 +403,6 @@ bool video_conv_state_begin_transcode(struct video_conv_state *state, struct pay if (fozdb_entry_size(state->read_fozdb, VIDEO_CONV_FOZ_TAG_MKVDATA, hash, &entry_size) == CONV_OK) { GST_DEBUG("Found an MKV video for hash %s.", format_hash(hash)); - state->transcode_hash = *hash; state->our_duration = entry_size; state->transcoded_tag = VIDEO_CONV_FOZ_TAG_MKVDATA; state->state_flags |= VIDEO_CONV_HAS_TRANSCODED; @@ -405,7 +412,6 @@ bool video_conv_state_begin_transcode(struct video_conv_state *state, struct pay if (fozdb_entry_size(state->read_fozdb, VIDEO_CONV_FOZ_TAG_OGVDATA, hash, &entry_size) == CONV_OK) { GST_DEBUG("Found an OGV video for hash %s.", format_hash(hash)); - state->transcode_hash = *hash; state->our_duration = entry_size; state->transcoded_tag = VIDEO_CONV_FOZ_TAG_OGVDATA; state->state_flags |= VIDEO_CONV_HAS_TRANSCODED; @@ -544,21 +550,63 @@ static bool video_conv_get_upstream_range(VideoConv *conv, uint64_t offset, uint return true; } +static uint64_t video_conv_duration_ours_to_downstream(VideoConv *conv, uint64_t pos) +{ + struct video_conv_state *state = conv->state; + + if (state->upstream_duration != DURATION_NONE && state->our_duration != DURATION_NONE) + return gst_util_uint64_scale_round(pos, state->our_duration, state->upstream_duration); + else + return DURATION_NONE; +} + +static bool video_conv_get_downstream_range(VideoConv *conv, uint64_t offset, uint32_t end, + uint64_t *downstream_offset, uint64_t *downstream_end) +{ + struct video_conv_state *state; + + if (!(state = video_conv_lock_state(conv))) + return false; + + if (state->upstream_duration == DURATION_NONE) + video_conv_query_upstream_duration(conv); + + *downstream_offset = video_conv_duration_ours_to_downstream(conv, offset); + *downstream_end = video_conv_duration_ours_to_downstream(conv, end); + + pthread_mutex_unlock(&conv->state_mutex); + + return true; +} + static bool video_conv_hash_upstream_data(VideoConv *conv, struct payload_hash *hash) { - struct pad_reader *reader; - bool ret; + bool ret = false; memset(hash, 0, sizeof(*hash)); - reader = pad_reader_create_with_stride(conv->sink_pad, HASH_STRIDE); - ret = murmur3_128(reader, pad_reader_read, HASH_SEED, hash); - pad_reader_release(reader); + if (conv->active_mode == GST_PAD_MODE_PUSH && gst_adapter_available(conv->adapter) > 0) + { + struct bytes_reader bytes_reader; + gsize read_size = gst_adapter_available(conv->adapter); + const void *buffer = gst_adapter_map(conv->adapter, read_size); + bytes_reader_init(&bytes_reader, buffer, read_size); + ret = murmur3_128(&bytes_reader, bytes_reader_read, HASH_SEED, hash); + gst_adapter_unmap(conv->adapter); + gst_adapter_clear(conv->adapter); + } + else if (gst_pad_activate_mode(conv->sink_pad, GST_PAD_MODE_PULL, true)) + { + struct pad_reader *reader = pad_reader_create_with_stride(conv->sink_pad, HASH_STRIDE); + ret = murmur3_128(reader, pad_reader_read, HASH_SEED, hash); + pad_reader_release(reader); + conv->active_mode = GST_PAD_MODE_PULL; + } return ret; } -static int video_conv_dump_upstream_chunk(VideoConv *conv, void *buffer, size_t read_size, +static int video_conv_dump_upstream_chunk(VideoConv *conv, const void *buffer, size_t read_size, GList **chunk_hashes) { struct bytes_reader bytes_reader; @@ -591,6 +639,12 @@ static int video_conv_dump_upstream_data(VideoConv *conv, struct payload_hash *h goto done; } + if (conv->active_mode == GST_PAD_MODE_PUSH) + { + conv->state->state_flags |= VIDEO_CONV_IS_DUMPING; + return 0; + } + buffer = calloc(1, HASH_CHUNK_SIZE); pad_reader = pad_reader_create(conv->sink_pad); while ((ret = pad_reader_read(pad_reader, buffer, HASH_CHUNK_SIZE, &read_size)) == CONV_OK) @@ -648,7 +702,24 @@ static void video_conv_init_transcode(VideoConv *conv) GST_WARNING("Failed to hash upstream data."); } - pthread_mutex_unlock(&dump_fozdb.mutex); + if (!(state->state_flags & VIDEO_CONV_IS_DUMPING)) + pthread_mutex_unlock(&dump_fozdb.mutex); +} + +static uint32_t video_conv_get_state_flags(VideoConv *conv) +{ + struct video_conv_state *state; + uint32_t state_flags; + + if (!(state = video_conv_lock_state(conv))) + { + GST_ERROR("VideoConv not yet in READY state?"); + return 0; + } + state_flags = state->state_flags; + pthread_mutex_unlock(&conv->state_mutex); + + return state_flags; } static gboolean video_conv_push_stream_start(VideoConv *conv, struct payload_hash *hash) @@ -700,19 +771,81 @@ static gboolean video_conv_sink_event_caps(VideoConv *conv, GstEvent *event) return false; } - if (!gst_pad_activate_mode(conv->sink_pad, GST_PAD_MODE_PULL, true)) + video_conv_init_transcode(conv); + transcode_tag = state->transcoded_tag; + + pthread_mutex_unlock(&conv->state_mutex); + + return video_conv_push_caps(conv, transcode_tag); +} + +static gboolean video_conv_sink_event_eos(VideoConv *conv, GstEvent *event) +{ + struct video_conv_state *state; + struct payload_hash hash; + uint32_t transcode_tag; + uint32_t state_flags; + int ret; + + gst_event_unref(event); + + if (!(state = video_conv_lock_state(conv))) + return false; + + if (state->state_flags & VIDEO_CONV_IS_DUMPING) { - GST_ERROR("Failed to activate sink pad in pull mode."); + struct hashes_reader chunk_hashes_reader; + gsize read_bytes; + + if ((read_bytes = gst_adapter_available(conv->adapter))) + { + const void *buffer = gst_adapter_map(conv->adapter, read_bytes); + + if ((ret = video_conv_dump_upstream_chunk(conv, buffer, read_bytes, &state->chunk_hashes)) < 0) + GST_ERROR("Error writing stream data to fozdb, ret %d.", ret); + + gst_adapter_unmap(conv->adapter); + gst_adapter_clear(conv->adapter); + } + + hashes_reader_init(&chunk_hashes_reader, state->chunk_hashes); + if ((ret = fozdb_write_entry(dump_fozdb.fozdb, VIDEO_CONV_FOZ_TAG_STREAM, &state->transcode_hash, + &chunk_hashes_reader, hashes_reader_read, true)) < 0) + GST_ERROR("Error writing stream data to fozdb, ret %d.", ret); + + if (state->chunk_hashes) + g_list_free_full(state->chunk_hashes, free); + + pthread_mutex_unlock(&dump_fozdb.mutex); + state->state_flags &= ~VIDEO_CONV_IS_DUMPING; pthread_mutex_unlock(&conv->state_mutex); - return false; + + return gst_pad_push_event(conv->src_pad, gst_event_new_eos()); } video_conv_init_transcode(conv); + hash = state->transcode_hash; + state_flags = state->state_flags; transcode_tag = state->transcoded_tag; pthread_mutex_unlock(&conv->state_mutex); - return video_conv_push_caps(conv, transcode_tag); + if (!(state_flags & VIDEO_CONV_STREAM_STARTED)) + { + /* rewind and start a new stream for dumping or playback */ + if (!push_event(conv->sink_pad, gst_event_new_seek(1.0, GST_FORMAT_BYTES, GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_NONE, -1))) + return false; + if (!video_conv_push_stream_start(conv, &hash)) + return false; + if (!video_conv_push_caps(conv, transcode_tag)) + return false; + + /* return false to cancel upstream pads EOS event handling and avoid setting EOS flag */ + return false; + } + + return gst_pad_push_event(conv->src_pad, gst_event_new_eos()); } static gboolean video_conv_sink_event(GstPad *pad, GstObject *parent, GstEvent *event) @@ -723,8 +856,167 @@ static gboolean video_conv_sink_event(GstPad *pad, GstObject *parent, GstEvent * if (event->type == GST_EVENT_CAPS) return video_conv_sink_event_caps(conv, event); + if (event->type == GST_EVENT_EOS) + return video_conv_sink_event_eos(conv, event); + + if (video_conv_get_state_flags(conv) & VIDEO_CONV_STREAM_STARTED) + return gst_pad_event_default(pad, parent, event); + + gst_event_unref(event); + return true; +} + +static gboolean video_conv_src_event_seek(VideoConv *conv, GstEvent *event) +{ + guint seqnum = gst_event_get_seqnum(event); + guint64 upstream_offset, upstream_size; + GstSeekType offset_type, stop_type; + struct video_conv_state *state; + gint64 offset, stop; + GstSeekFlags flags; + GstFormat format; + gdouble rate; + + gst_event_parse_seek(event, &rate, &format, &flags, &offset_type, &offset, &stop_type, &stop); + gst_event_unref(event); + if (format != GST_FORMAT_BYTES) + return false; + + GST_TRACE("conv %p, rate %f, format %s, flags %#x, offset_type %u, cur %#" G_GINT64_MODIFIER "x, " + "stop_type %u, stop %#" G_GINT64_MODIFIER "x.", conv, rate, gst_format_get_name(format), + flags, offset_type, offset, stop_type, stop); + + if (!(state = video_conv_lock_state(conv))) + return GST_FLOW_ERROR; + if (state->state_flags & VIDEO_CONV_IS_DUMPING) + { + pthread_mutex_unlock(&conv->state_mutex); + return true; + } + pthread_mutex_unlock(&conv->state_mutex); + + if (!video_conv_get_upstream_range(conv, offset, HASH_CHUNK_SIZE, &upstream_offset, &upstream_size)) + return false; + + if ((event = gst_event_new_seek(1.0, GST_FORMAT_BYTES, GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, upstream_offset, GST_SEEK_TYPE_NONE, -1))) + gst_event_set_seqnum(event, seqnum); + return push_event(conv->sink_pad, event); +} + +static gboolean video_conv_src_event(GstPad *pad, GstObject *parent, GstEvent *event) +{ + VideoConv *conv = VIDEO_CONV(parent); + + GST_DEBUG_OBJECT(pad, "Got event %"GST_PTR_FORMAT".", event); + + if (event->type == GST_EVENT_SEEK) + return video_conv_src_event_seek(conv, event); + + if (video_conv_get_state_flags(conv) & VIDEO_CONV_STREAM_STARTED) + return gst_pad_event_default(pad, parent, event); + + gst_event_unref(event); + return true; +} + +static void video_conv_dump_buffered_chunks(VideoConv *conv, gsize bytes_available) +{ + struct video_conv_state *state = conv->state; + int ret; + + while (bytes_available >= HASH_CHUNK_SIZE) + { + const void *buffer = gst_adapter_map(conv->adapter, HASH_CHUNK_SIZE); + if ((ret = video_conv_dump_upstream_chunk(conv, buffer, HASH_CHUNK_SIZE, &state->chunk_hashes)) < 0) + { + pthread_mutex_unlock(&dump_fozdb.mutex); + state->state_flags &= ~VIDEO_CONV_IS_DUMPING; + break; + } + + gst_adapter_unmap(conv->adapter); + gst_adapter_flush(conv->adapter, HASH_CHUNK_SIZE); + bytes_available -= HASH_CHUNK_SIZE; + } +} + +static GstFlowReturn video_conv_sink_chain(GstPad *pad, GstObject *parent, GstBuffer *buffer) +{ + gsize buffer_size = gst_buffer_get_size(buffer), buffer_offset = GST_BUFFER_OFFSET(buffer); + uint64_t downstream_offset, downstream_end, seek_offset = -1; + VideoConv *conv = VIDEO_CONV(parent); + struct video_conv_state *state; + GstBuffer *transcoded = NULL; + uint32_t state_flags; + int ret = 0; + + GST_DEBUG_OBJECT(pad, "Got buffer %"GST_PTR_FORMAT".", buffer); + + if (!(state = video_conv_lock_state(conv))) + return GST_FLOW_ERROR; + state_flags = state->state_flags; + + if ((state_flags & VIDEO_CONV_IS_DUMPING) || !(state_flags & VIDEO_CONV_STREAM_STARTED)) + { + gsize bytes_available = gst_adapter_available(conv->adapter) + buffer_size; + gst_adapter_push(conv->adapter, buffer); + + if (state_flags & VIDEO_CONV_IS_DUMPING) + video_conv_dump_buffered_chunks(conv, bytes_available); + else if (!(bytes_available % HASH_CHUNK_SIZE) && bytes_available >= HASH_STRIDE) + { + state->read_offset += HASH_STRIDE; + seek_offset = state->read_offset; + } + } + + pthread_mutex_unlock(&conv->state_mutex); + + if (ret < 0) + { + GST_ERROR("Failed to dump or read transcoded buffer, error %d.", ret); + if (transcoded) + gst_buffer_unref(transcoded); + return GST_FLOW_ERROR; + } + + if (seek_offset != -1 && !push_event(pad, gst_event_new_seek(1.0, GST_FORMAT_BYTES, GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, seek_offset, GST_SEEK_TYPE_NONE, -1))) + return GST_FLOW_ERROR; + if (!(state_flags & VIDEO_CONV_STREAM_STARTED)) + return GST_FLOW_OK; - return gst_pad_event_default(pad, parent, event); + if (!video_conv_get_downstream_range(conv, buffer_offset, buffer_offset + buffer_size, + &downstream_offset, &downstream_end)) + return GST_FLOW_ERROR; + + if (downstream_end == downstream_offset) + return GST_FLOW_OK; + if (!(transcoded = gst_buffer_new_and_alloc(downstream_end - downstream_offset))) + return GST_FLOW_ERROR; + else + { + GstBufferMapInfo map; + size_t fill_size = 0; + + if (gst_buffer_map(transcoded, &map, GST_MAP_READWRITE)) + { + ret = video_conv_state_fill_buffer(state, downstream_offset, map.data, map.size, &fill_size); + gst_buffer_unmap(transcoded, &map); + gst_buffer_set_size(transcoded, fill_size); + } + + if (gst_buffer_get_size(transcoded)) + { + GST_BUFFER_OFFSET(transcoded) = downstream_offset; + return gst_pad_push(conv->src_pad, transcoded); + } + + gst_buffer_unref(transcoded); + } + + return GST_FLOW_OK; } static GstFlowReturn video_conv_src_get_range(GstPad *pad, GstObject *parent, @@ -825,7 +1117,13 @@ static gboolean video_conv_src_query(GstPad *pad, GstObject *parent, GstQuery *q gst_query_unref(peer_query); gst_query_set_scheduling(query, flags, min, max, align); - gst_query_add_scheduling_mode(query, GST_PAD_MODE_PULL); + if (conv->active_mode != GST_PAD_MODE_NONE) + gst_query_add_scheduling_mode(query, conv->active_mode); + else + { + gst_query_add_scheduling_mode(query, GST_PAD_MODE_PULL); + gst_query_add_scheduling_mode(query, GST_PAD_MODE_PUSH); + } return true; @@ -870,6 +1168,7 @@ static gboolean video_conv_src_active_mode(GstPad *pad, GstObject *parent, GstPa return false; } + conv->active_mode = mode; if (mode != GST_PAD_MODE_PULL) return true; @@ -897,6 +1196,7 @@ static void video_conv_finalize(GObject *object) { VideoConv *conv = VIDEO_CONV(object); + gst_object_unref(conv->adapter); pthread_mutex_destroy(&conv->state_mutex); if (conv->state) video_conv_state_release(conv->state); @@ -928,9 +1228,11 @@ static void video_conv_init(VideoConv *conv) conv->sink_pad = gst_pad_new_from_static_template(&video_conv_sink_template, "sink"); gst_pad_set_event_function(conv->sink_pad, GST_DEBUG_FUNCPTR(video_conv_sink_event)); + gst_pad_set_chain_function(conv->sink_pad, GST_DEBUG_FUNCPTR(video_conv_sink_chain)); gst_element_add_pad(element, conv->sink_pad); conv->src_pad = gst_pad_new_from_static_template(&video_conv_src_template, "src"); + gst_pad_set_event_function(conv->src_pad, GST_DEBUG_FUNCPTR(video_conv_src_event)); gst_pad_set_getrange_function(conv->src_pad, GST_DEBUG_FUNCPTR(video_conv_src_get_range)); gst_pad_set_query_function(conv->src_pad, GST_DEBUG_FUNCPTR(video_conv_src_query)); gst_pad_set_activatemode_function(conv->src_pad, GST_DEBUG_FUNCPTR(video_conv_src_active_mode)); @@ -938,4 +1240,6 @@ static void video_conv_init(VideoConv *conv) pthread_mutex_init(&conv->state_mutex, NULL); conv->state = NULL; + conv->adapter = gst_adapter_new(); + conv->active_mode = GST_PAD_MODE_NONE; } From a7176523998b19b3496338006172a2c4b7ee7fbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Mar 2024 10:54:28 +0100 Subject: [PATCH 301/413] winegstreamer/media-converter: Wrap videoconv and a demuxer in a new protondemuxer bin. CW-Bug-Id: #22319 --- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/media-converter/lib.c | 7 + .../media-converter/protondemuxer.c | 223 ++++++++++++++++++ .../winegstreamer/media-converter/videoconv.c | 2 +- 4 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 dlls/winegstreamer/media-converter/protondemuxer.c diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index ac38a1b8964..f76a7fb35f2 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -38,4 +38,5 @@ SOURCES = \ media-converter/fossilize.c \ media-converter/lib.c \ media-converter/murmur3.c \ + media-converter/protondemuxer.c \ media-converter/videoconv.c diff --git a/dlls/winegstreamer/media-converter/lib.c b/dlls/winegstreamer/media-converter/lib.c index 61d7fe8605e..4f7884c1178 100644 --- a/dlls/winegstreamer/media-converter/lib.c +++ b/dlls/winegstreamer/media-converter/lib.c @@ -26,6 +26,7 @@ GST_ELEMENT_REGISTER_DECLARE(protonvideoconverter); GST_ELEMENT_REGISTER_DECLARE(protonaudioconverter); GST_ELEMENT_REGISTER_DECLARE(protonaudioconverterbin); +GST_ELEMENT_REGISTER_DECLARE(protondemuxer); GST_DEBUG_CATEGORY(media_converter_debug); @@ -325,5 +326,11 @@ bool media_converter_init(void) return false; } + if (!GST_ELEMENT_REGISTER(protondemuxer, NULL)) + { + GST_ERROR("Failed to register protondemuxer."); + return false; + } + return true; } diff --git a/dlls/winegstreamer/media-converter/protondemuxer.c b/dlls/winegstreamer/media-converter/protondemuxer.c new file mode 100644 index 00000000000..9ab4be5ae3c --- /dev/null +++ b/dlls/winegstreamer/media-converter/protondemuxer.c @@ -0,0 +1,223 @@ +/* + * Copyright 2024 Remi 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 "media-converter.h" + +GST_DEBUG_CATEGORY_EXTERN(media_converter_debug); +#undef GST_CAT_DEFAULT +#define GST_CAT_DEFAULT media_converter_debug + +typedef struct +{ + GstBin bin; + GstElement *video_conv, *demuxer; + GstPad *sink_pad; /* Ghost pad. */ + GstPad *inner_sink, *inner_src; +} ProtonDemuxer; + +typedef struct +{ + GstBinClass class; +} ProtonDemuxerClass; + +G_DEFINE_TYPE(ProtonDemuxer, proton_demuxer, GST_TYPE_BIN); +#define PROTON_DEMUXER_TYPE (proton_demuxer_get_type()) +#define PROTON_DEMUXER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PROTON_DEMUXER_TYPE, ProtonDemuxer)) +GST_ELEMENT_REGISTER_DEFINE(protondemuxer, "protondemuxer", GST_RANK_MARGINAL, PROTON_DEMUXER_TYPE); + +static GstStaticPadTemplate proton_demuxer_sink_template = GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, + GST_PAD_ALWAYS, GST_STATIC_CAPS("video/x-ms-asf; video/x-msvideo; video/mpeg; video/quicktime;")); +static GstStaticPadTemplate proton_demuxer_src_template = GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, + GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY); + +static gboolean proton_demuxer_sink_event(GstPad *pad, GstObject *parent, GstEvent *event) +{ + GST_DEBUG_OBJECT(pad, "Got sink event %"GST_PTR_FORMAT".", event); + return gst_pad_event_default(pad, parent, event); +} + +static void proton_demuxer_pad_added(GstElement *element, GstPad *pad, gpointer user) +{ + ProtonDemuxer *bin = PROTON_DEMUXER(user); + GstPad *ghost_src; + GstEvent *event; + + GST_DEBUG_OBJECT(element, "Got inner pad added %"GST_PTR_FORMAT".", pad); + + ghost_src = gst_ghost_pad_new(GST_PAD_NAME(pad), pad); + gst_pad_set_active(ghost_src, true); + + if ((event = gst_pad_get_sticky_event(pad, GST_EVENT_STREAM_START, 0))) + { + gst_pad_store_sticky_event(ghost_src, event); + gst_event_unref(event); + } + + gst_element_add_pad(GST_ELEMENT(&bin->bin), ghost_src); +} + +static void proton_demuxer_no_more_pads(GstElement *element, gpointer user) +{ + ProtonDemuxer *bin = PROTON_DEMUXER(user); + GST_DEBUG_OBJECT(element, "Got inner no-more-pads."); + gst_element_no_more_pads(GST_ELEMENT(&bin->bin)); +} + +static GstFlowReturn proton_demuxer_inner_sink_chain(GstPad *pad, GstObject *parent, GstBuffer *buffer) +{ + ProtonDemuxer *bin = PROTON_DEMUXER(gst_pad_get_element_private(pad)); + GST_DEBUG_OBJECT(pad, "Got inner sink buffer %"GST_PTR_FORMAT".", buffer); + return gst_pad_push(bin->inner_src, buffer); +} + +static gboolean proton_demuxer_inner_sink_event_caps(ProtonDemuxer *bin, GstEvent *event) +{ + GstCaps *src_caps, *any_caps; + GstEvent *stream_start; + GstPad *src; + + gst_event_parse_caps(event, &src_caps); + + if (!bin->demuxer) + { + if (!(any_caps = gst_caps_new_any())) + return false; + if (!(bin->demuxer = find_element(GST_ELEMENT_FACTORY_TYPE_DECODABLE, src_caps, any_caps))) + { + gst_caps_unref(any_caps); + return false; + } + gst_caps_unref(any_caps); + g_signal_connect(bin->demuxer, "pad-added", G_CALLBACK(proton_demuxer_pad_added), bin); + g_signal_connect(bin->demuxer, "no-more-pads", G_CALLBACK(proton_demuxer_no_more_pads), bin); + + if ((src = gst_element_get_static_pad(bin->demuxer, "src"))) + { + GstPad *ghost_src = gst_ghost_pad_new_no_target_from_template(GST_PAD_NAME(src), + gst_element_get_pad_template(GST_ELEMENT(&bin->bin), "src")); + gst_ghost_pad_set_target(GST_GHOST_PAD(ghost_src), src); + gst_element_add_pad(GST_ELEMENT(&bin->bin), ghost_src); + gst_object_unref(src); + + gst_element_no_more_pads(GST_ELEMENT(&bin->bin)); + } + + gst_bin_add(GST_BIN(bin), bin->demuxer); + link_src_to_element(bin->inner_src, bin->demuxer); + gst_pad_set_active(bin->inner_src, true); + + if ((stream_start = gst_pad_get_sticky_event(bin->inner_sink, GST_EVENT_STREAM_START, 0))) + push_event(bin->inner_src, stream_start); + + gst_element_sync_state_with_parent(bin->demuxer); + } + + return gst_pad_push_event(bin->inner_src, event); +} + +static gboolean proton_demuxer_inner_sink_event(GstPad *pad, GstObject *parent, GstEvent *event) +{ + ProtonDemuxer *bin = PROTON_DEMUXER(gst_pad_get_element_private(pad)); + + GST_DEBUG_OBJECT(pad, "Got inner sink event %"GST_PTR_FORMAT".", event); + + if (event->type == GST_EVENT_CAPS) + return proton_demuxer_inner_sink_event_caps(bin, event); + if (!bin->demuxer) + return gst_pad_event_default(pad, parent, event); + if (event->type == GST_EVENT_STREAM_START) + { + GstEvent *stream_start; + if ((stream_start = gst_pad_get_sticky_event(bin->inner_src, GST_EVENT_STREAM_START, 0))) + push_event(bin->inner_src, stream_start); + return gst_pad_event_default(pad, parent, event); + } + + return gst_pad_push_event(bin->inner_src, event); +} + +static gboolean proton_demuxer_inner_src_query(GstPad *pad, GstObject *parent, GstQuery *query) +{ + ProtonDemuxer *bin = PROTON_DEMUXER(gst_pad_get_element_private(pad)); + + GST_DEBUG_OBJECT(pad, "Got inner src query %"GST_PTR_FORMAT".", query); + + if (!bin->demuxer) + return gst_pad_query_default(pad, parent, query); + return gst_pad_peer_query(bin->inner_sink, query); +} + +static gboolean proton_demuxer_inner_src_event(GstPad *pad, GstObject *parent, GstEvent *event) +{ + ProtonDemuxer *bin = PROTON_DEMUXER(gst_pad_get_element_private(pad)); + GST_DEBUG_OBJECT(pad, "Got inner src event %"GST_PTR_FORMAT".", event); + return gst_pad_push_event(bin->inner_sink, event); +} + +static void proton_demuxer_class_init(ProtonDemuxerClass * klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS(klass); + + /* wg_parser autoplugging ordering relies on the element "Proton video converter" name */ + gst_element_class_set_metadata(element_class, "Proton video converter", "Codec/Demuxer", "Demuxes video for Proton", + "Andrew Eikum , Ziqing Hui "); + + gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&proton_demuxer_sink_template)); + gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&proton_demuxer_src_template)); +} + +static void proton_demuxer_init(ProtonDemuxer *bin) +{ + GstStaticPadTemplate inner_sink_template = GST_STATIC_PAD_TEMPLATE("inner-sink", + GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY); + GstStaticPadTemplate inner_src_template = GST_STATIC_PAD_TEMPLATE("inner-src", + GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY); + GstElement *element = GST_ELEMENT(bin); + GstPad *sink; + + bin->sink_pad = gst_ghost_pad_new_no_target_from_template("sink", gst_element_get_pad_template(element, "sink")); + gst_pad_set_event_function(bin->sink_pad, GST_DEBUG_FUNCPTR(proton_demuxer_sink_event)); + + bin->inner_sink = gst_pad_new_from_static_template(&inner_sink_template, "inner-sink"); + gst_pad_set_chain_function(bin->inner_sink, GST_DEBUG_FUNCPTR(proton_demuxer_inner_sink_chain)); + gst_pad_set_event_function(bin->inner_sink, GST_DEBUG_FUNCPTR(proton_demuxer_inner_sink_event)); + gst_pad_set_element_private(bin->inner_sink, bin); + + bin->inner_src = gst_pad_new_from_static_template(&inner_src_template, "inner-src"); + gst_pad_set_query_function(bin->inner_src, GST_DEBUG_FUNCPTR(proton_demuxer_inner_src_query)); + gst_pad_set_event_function(bin->inner_src, GST_DEBUG_FUNCPTR(proton_demuxer_inner_src_event)); + gst_pad_set_element_private(bin->inner_src, bin); + + bin->video_conv = create_element("protonvideoconverter", "protonmediaconverter"); + gst_bin_add(GST_BIN(bin), bin->video_conv); + link_element_to_sink(bin->video_conv, bin->inner_sink); + gst_pad_set_active(bin->inner_sink, true); + + sink = gst_element_get_static_pad(bin->video_conv, "sink"); + gst_ghost_pad_set_target(GST_GHOST_PAD(bin->sink_pad), sink); + gst_object_unref(sink); + + gst_element_add_pad(element, bin->sink_pad); + + GST_INFO("Initialized ProtonDemuxer %"GST_PTR_FORMAT": video_conv %"GST_PTR_FORMAT", demuxer %"GST_PTR_FORMAT", " + "sink_pad %"GST_PTR_FORMAT".", bin, bin->video_conv, bin->demuxer, bin->sink_pad); +} diff --git a/dlls/winegstreamer/media-converter/videoconv.c b/dlls/winegstreamer/media-converter/videoconv.c index 99c451c4a17..0f916b86f66 100644 --- a/dlls/winegstreamer/media-converter/videoconv.c +++ b/dlls/winegstreamer/media-converter/videoconv.c @@ -129,7 +129,7 @@ G_DEFINE_TYPE(VideoConv, video_conv, GST_TYPE_ELEMENT); #define VIDEO_CONV(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), VIDEO_CONV_TYPE, VideoConv)) #define parent_class (video_conv_parent_class) GST_ELEMENT_REGISTER_DEFINE(protonvideoconverter, "protonvideoconverter", - GST_RANK_MARGINAL, VIDEO_CONV_TYPE); + GST_RANK_MARGINAL + 1, VIDEO_CONV_TYPE); static GstStaticPadTemplate video_conv_sink_template = GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, From 95242380175d614cab312b65bbd1765d923b39d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Mar 2024 22:23:44 +0100 Subject: [PATCH 302/413] winegstreamer/media-converter: Automatically add an audio decoder to protondemuxer. CW-Bug-Id: #22319 --- .../media-converter/protondemuxer.c | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/dlls/winegstreamer/media-converter/protondemuxer.c b/dlls/winegstreamer/media-converter/protondemuxer.c index 9ab4be5ae3c..124d105ca46 100644 --- a/dlls/winegstreamer/media-converter/protondemuxer.c +++ b/dlls/winegstreamer/media-converter/protondemuxer.c @@ -58,13 +58,39 @@ static gboolean proton_demuxer_sink_event(GstPad *pad, GstObject *parent, GstEve static void proton_demuxer_pad_added(GstElement *element, GstPad *pad, gpointer user) { ProtonDemuxer *bin = PROTON_DEMUXER(user); - GstPad *ghost_src; + GstPad *ghost_src, *src_pad; + GstElement *decoder = NULL; GstEvent *event; + GstCaps *caps; GST_DEBUG_OBJECT(element, "Got inner pad added %"GST_PTR_FORMAT".", pad); - ghost_src = gst_ghost_pad_new(GST_PAD_NAME(pad), pad); - gst_pad_set_active(ghost_src, true); + if ((caps = gst_pad_get_current_caps(pad))) + { + const char *mime_type = gst_structure_get_name(gst_caps_get_structure(caps, 0)); + GST_DEBUG_OBJECT(element, "Got inner pad caps %"GST_PTR_FORMAT".", caps); + + if (!strcmp(mime_type, "audio/x-vorbis")) + decoder = create_element("vorbisdec", "base"); + else if (!strcmp(mime_type, "audio/x-opus")) + decoder = create_element("opusdec", "base"); + + gst_caps_unref(caps); + } + + if (!decoder) + ghost_src = gst_ghost_pad_new(GST_PAD_NAME(pad), pad); + else + { + gst_bin_add(GST_BIN(bin), decoder); + link_src_to_element(pad, decoder); + + src_pad = gst_element_get_static_pad(decoder, "src"); + ghost_src = gst_ghost_pad_new(GST_PAD_NAME(src_pad), src_pad); + gst_object_unref(src_pad); + + gst_element_sync_state_with_parent(decoder); + } if ((event = gst_pad_get_sticky_event(pad, GST_EVENT_STREAM_START, 0))) { @@ -72,6 +98,7 @@ static void proton_demuxer_pad_added(GstElement *element, GstPad *pad, gpointer gst_event_unref(event); } + gst_pad_set_active(ghost_src, true); gst_element_add_pad(GST_ELEMENT(&bin->bin), ghost_src); } From 01b816d821eb9a6ff6ce89ab071890948944278e Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Tue, 9 Apr 2024 11:56:54 +0300 Subject: [PATCH 303/413] HACK: winebuild: Include target arch in the hash used for timestamp. So that 32 and 64 bit PEs produce different values even if they have the same name. --- tools/winebuild/spec32.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/winebuild/spec32.c b/tools/winebuild/spec32.c index 6e3a0941310..26cb6878391 100644 --- a/tools/winebuild/spec32.c +++ b/tools/winebuild/spec32.c @@ -62,7 +62,7 @@ static unsigned int hash_filename( const char *name ) /* FNV-1 hash */ unsigned int ret = 2166136261u; while (*name) ret = (ret * 16777619) ^ *name++; - return ret; + return ret + target.cpu; } /* check if entry point needs a relay thunk */ From 68eb55726706ae2b61be94428086c6c5747b6f67 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 5 Apr 2024 17:09:27 -0600 Subject: [PATCH 304/413] ntdll/tests: Add tests for CONTEXT_EXCEPTION_REQUEST. CW-Bug-Id: #23654 --- dlls/ntdll/tests/exception.c | 321 ++++++++++++++++++++++++++++++++-- dlls/ntdll/tests/ntdll_test.h | 2 + dlls/ntdll/tests/wow64.c | 14 +- include/winnt.h | 5 + 4 files changed, 322 insertions(+), 20 deletions(-) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 3a15c1ac7ab..710bf19bd21 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -18,20 +18,13 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -#include +#include "ntdll_test.h" + #include -#include "ntstatus.h" -#define WIN32_NO_STATUS -#include "windef.h" -#include "winbase.h" -#include "winnt.h" -#include "winreg.h" #include "winuser.h" -#include "winternl.h" #include "ddk/wdm.h" #include "excpt.h" -#include "wine/test.h" #include "intrin.h" static void *code_mem; @@ -284,6 +277,19 @@ static void test_debugger_xstate(HANDLE thread, CONTEXT *ctx, enum debugger_stag status = pNtSetContextThread(thread, xctx); ok(!status, "NtSetContextThread failed with 0x%lx\n", status); } + +#define check_context_exception_request( a, b ) check_context_exception_request_( a, b, __LINE__ ) +static void check_context_exception_request_( DWORD flags, BOOL hardware_exception, unsigned int line ) +{ + static const DWORD exception_reporting_flags = CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING + | CONTEXT_EXCEPTION_ACTIVE | CONTEXT_SERVICE_ACTIVE; + DWORD expected_flags = CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING; + + if (!(flags & CONTEXT_EXCEPTION_REPORTING)) return; + expected_flags |= hardware_exception ? CONTEXT_EXCEPTION_ACTIVE : CONTEXT_SERVICE_ACTIVE; + ok_(__FILE__, line)( (flags & exception_reporting_flags) == expected_flags, "got %#lx, expected %#lx.\n", + flags, expected_flags ); +} #endif #ifdef __i386__ @@ -1154,13 +1160,16 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) sizeof(stage), &size_read); ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); - ctx.ContextFlags = CONTEXT_FULL | CONTEXT_EXTENDED_REGISTERS; + ctx.ContextFlags = CONTEXT_FULL | CONTEXT_EXTENDED_REGISTERS | CONTEXT_EXCEPTION_REQUEST; status = pNtGetContextThread(pi.hThread, &ctx); ok(!status, "NtGetContextThread failed with 0x%lx\n", status); + todo_wine ok(ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING + || broken( !(ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) ) /* Win7 WoW64 */, + "got %#lx.\n", ctx.ContextFlags); - trace("exception 0x%lx at %p firstchance=%ld Eip=0x%lx, Eax=0x%lx\n", + trace("exception 0x%lx at %p firstchance=%ld Eip=0x%lx, Eax=0x%lx ctx.ContextFlags %#lx\n", de.u.Exception.ExceptionRecord.ExceptionCode, - de.u.Exception.ExceptionRecord.ExceptionAddress, de.u.Exception.dwFirstChance, ctx.Eip, ctx.Eax); + de.u.Exception.ExceptionRecord.ExceptionAddress, de.u.Exception.dwFirstChance, ctx.Eip, ctx.Eax, ctx.ContextFlags); if (counter > 100) { @@ -1177,6 +1186,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) (char *)ctx.Eip < (char *)ntdll + nt->OptionalHeader.SizeOfImage, "wrong eip %p ntdll %p-%p\n", (void *)ctx.Eip, ntdll, (char *)ntdll + nt->OptionalHeader.SizeOfImage ); + check_context_exception_request( ctx.ContextFlags, TRUE ); } else { @@ -1190,6 +1200,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) ctx.Eax = 0xf00f00f1; /* let the debuggee handle the exception */ continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, !is_wow64 ); } else if (stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE) { @@ -1226,6 +1237,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) ctx.Eip, (char *)code_mem_address + 0xb); /* here we handle exception */ } + check_context_exception_request( ctx.ContextFlags, !is_wow64 ); } else if (stage == STAGE_SERVICE_CONTINUE || stage == STAGE_SERVICE_NOT_HANDLED) { @@ -1235,6 +1247,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) "expected Eip = %p, got 0x%lx\n", (char *)code_mem_address + 0x1d, ctx.Eip); if (stage == STAGE_SERVICE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, TRUE ); } else if (stage == STAGE_BREAKPOINT_CONTINUE || stage == STAGE_BREAKPOINT_NOT_HANDLED) { @@ -1244,6 +1257,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) "expected Eip = %p, got 0x%lx\n", (char *)code_mem_address + 2, ctx.Eip); if (stage == STAGE_BREAKPOINT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, TRUE ); } else if (stage == STAGE_EXCEPTION_INVHANDLE_CONTINUE || stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) { @@ -1254,14 +1268,17 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) "unexpected number of parameters %ld, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters); if (stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, !is_wow64 ); } else if (stage == STAGE_NO_EXCEPTION_INVHANDLE_NOT_HANDLED) { ok(FALSE || broken(TRUE) /* < Win10 */, "should not throw exception\n"); continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, !is_wow64 ); } else if (stage == STAGE_XSTATE || stage == STAGE_XSTATE_LEGACY_SSE) { + check_context_exception_request( ctx.ContextFlags, TRUE ); test_debugger_xstate(pi.hThread, &ctx, stage); } else if (stage == STAGE_SEGMENTS) @@ -1282,6 +1299,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) ok( !ctx.SegEs, "wrong es %04lx / %04lx\n", ctx.SegEs, ctx.SegSs ); ok( !ctx.SegGs, "wrong gs %04lx / %04lx\n", ctx.SegGs, ctx.SegSs ); } + check_context_exception_request( ctx.ContextFlags, TRUE ); } else ok(FALSE, "unexpected stage %u\n", stage); @@ -4052,9 +4070,10 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) sizeof(stage), &size_read); ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); - ctx.ContextFlags = CONTEXT_FULL | CONTEXT_SEGMENTS; + ctx.ContextFlags = CONTEXT_FULL | CONTEXT_SEGMENTS | CONTEXT_EXCEPTION_REQUEST; status = pNtGetContextThread(pi.hThread, &ctx); ok(!status, "NtGetContextThread failed with 0x%lx\n", status); + todo_wine ok(ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING, "got %#lx.\n", ctx.ContextFlags); trace("exception 0x%lx at %p firstchance=%ld Rip=%p, Rax=%p\n", de.u.Exception.ExceptionRecord.ExceptionCode, @@ -4076,6 +4095,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) (char *)ctx.Rip < (char *)ntdll + nt->OptionalHeader.SizeOfImage, "wrong rip %p ntdll %p-%p\n", (void *)ctx.Rip, ntdll, (char *)ntdll + nt->OptionalHeader.SizeOfImage ); + check_context_exception_request( ctx.ContextFlags, TRUE ); } else { @@ -4089,6 +4109,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) ctx.Rax = 0xf00f00f1; /* let the debuggee handle the exception */ continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, FALSE ); } else if (stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE) { @@ -4117,6 +4138,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) ctx.Rip, (char *)code_mem_address + 0x10); /* here we handle exception */ } + check_context_exception_request( ctx.ContextFlags, FALSE ); } else if (stage == STAGE_SERVICE_CONTINUE || stage == STAGE_SERVICE_NOT_HANDLED) { @@ -4125,6 +4147,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) ok((char *)ctx.Rip == (char *)code_mem_address + 0x30, "expected Rip = %p, got %p\n", (char *)code_mem_address + 0x30, (char *)ctx.Rip); if (stage == STAGE_SERVICE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, TRUE ); } else if (stage == STAGE_BREAKPOINT_CONTINUE || stage == STAGE_BREAKPOINT_NOT_HANDLED) { @@ -4133,6 +4156,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) ok((char *)ctx.Rip == (char *)code_mem_address + 2, "expected Rip = %p, got %p\n", (char *)code_mem_address + 2, (char *)ctx.Rip); if (stage == STAGE_BREAKPOINT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, TRUE ); } else if (stage == STAGE_EXCEPTION_INVHANDLE_CONTINUE || stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) { @@ -4143,14 +4167,17 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) "unexpected number of parameters %ld, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters); if (stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, FALSE ); } else if (stage == STAGE_NO_EXCEPTION_INVHANDLE_NOT_HANDLED) { ok(FALSE || broken(TRUE) /* < Win10 */, "should not throw exception\n"); continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, FALSE ); } else if (stage == STAGE_XSTATE || stage == STAGE_XSTATE_LEGACY_SSE) { + check_context_exception_request( ctx.ContextFlags, TRUE ); test_debugger_xstate(pi.hThread, &ctx, stage); } else if (stage == STAGE_SEGMENTS) @@ -4175,6 +4202,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) ok( ctx.SegEs == ctx.SegSs, "wrong es %04x / %04x\n", ctx.SegEs, ctx.SegSs ); todo_wine ok( ctx.SegFs != ctx.SegSs, "wrong fs %04x / %04x\n", ctx.SegFs, ctx.SegSs ); ok( ctx.SegGs == ctx.SegSs, "wrong gs %04x / %04x\n", ctx.SegGs, ctx.SegSs ); + check_context_exception_request( ctx.ContextFlags, TRUE ); } else ok(FALSE, "unexpected stage %u\n", stage); @@ -4596,9 +4624,21 @@ static void test_wow64_context(void) ret = pRtlWow64GetThreadContext( pi.hThread, &ctx ); ok(ret == STATUS_SUCCESS, "got %#lx\n", ret); ok( ctx.ContextFlags == WOW64_CONTEXT_ALL, "got context flags %#lx\n", ctx.ContextFlags ); + + ctx.ContextFlags = WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST; + ret = pRtlWow64GetThreadContext( pi.hThread, &ctx ); + ok(ret == STATUS_SUCCESS, "got %#lx\n", ret); + todo_wine ok( (ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) || broken( ctx.ContextFlags == WOW64_CONTEXT_ALL ) /*Win 7*/, + "got context flags %#lx\n", ctx.ContextFlags ); + if (context.SegCs == cs32) { trace( "in 32-bit mode %04x\n", context.SegCs ); + if (ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) + ok( ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING) + || ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING + | CONTEXT_EXCEPTION_ACTIVE), + "got %#lx.\n", ctx.ContextFlags ); ok( ctx.Eip == context.Rip, "cs32: eip %08lx / %p\n", ctx.Eip, (void *)context.Rip ); ok( ctx.Ebp == context.Rbp, "cs32: ebp %08lx / %p\n", ctx.Ebp, (void *)context.Rbp ); ok( ctx.Esp == context.Rsp, "cs32: esp %08lx / %p\n", ctx.Esp, (void *)context.Rsp ); @@ -4673,6 +4713,12 @@ static void test_wow64_context(void) else { trace( "in 64-bit mode %04x\n", context.SegCs ); + if (ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) + ok( ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST + | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE) + || ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST + | CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE), + "got %#lx.\n", ctx.ContextFlags ); ok( ctx.Eip != context.Rip, "cs64: eip %08lx / %p\n", ctx.Eip, (void *)context.Rip); ok( ctx.SegCs == cs32, "cs64: wrong cs %04lx / %04x\n", ctx.SegCs, cs32 ); ok( ctx.SegDs == context.SegDs, "cs64: wrong ds %04lx / %04x\n", ctx.SegDs, context.SegDs ); @@ -12341,6 +12387,254 @@ static void test_set_live_context(void) } #endif +struct context_exception_request_thread_param +{ + LONG volatile sync; + HANDLE event; +}; +static volatile int *p_context_exception_request_value; +struct context_exception_request_thread_param *context_exception_request_param; + +static LONG CALLBACK test_context_exception_request_handler( EXCEPTION_POINTERS *info ) +{ + PEXCEPTION_RECORD rec = info->ExceptionRecord; + CONTEXT *c = info->ContextRecord; + DWORD old_prot; + + ok( !(c->ContextFlags & (CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE + | CONTEXT_EXCEPTION_ACTIVE)), "got %#lx.\n", c->ContextFlags ); + + ok( rec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION, "got %#lx.\n", rec->ExceptionCode ); + VirtualProtect( (void *)p_context_exception_request_value, sizeof(*p_context_exception_request_value), + PAGE_READWRITE, &old_prot ); + + WriteRelease( &context_exception_request_param->sync, 5 ); + while (ReadAcquire( &context_exception_request_param->sync ) != 6) + ; + + return EXCEPTION_CONTINUE_EXECUTION; +} + +static DWORD WINAPI test_context_exception_request_thread( void *arg ) +{ +#ifndef _WIN64 + static BYTE wait_sync_x64_code[] = + { + 0x89, 0x11, /* mov %edx,(%rcx) */ + 0x83, 0xc2, 0x01, /* add $0x1,%edx */ + 0x0f, 0x1f, 0x00, /* 1: nopl (%rax) */ + 0x8b, 0x01, /* mov (%rcx),%eax */ + 0x39, 0xd0, /* cmp %edx,%eax */ + 0x75, 0xfa, /* jne 1b */ + 0xc3, /* ret */ + }; + ULONG64 args[2]; +#endif + struct context_exception_request_thread_param *p = arg; + void *vectored_handler; + + context_exception_request_param = p; + vectored_handler = pRtlAddVectoredExceptionHandler( TRUE, test_context_exception_request_handler ); + ok( !!vectored_handler, "failed.\n" ); + + WriteRelease( &p->sync, 1 ); + while (ReadAcquire( &p->sync ) != 2) + ; + + WaitForSingleObject( p->event, INFINITE ); + +#ifndef _WIN64 + memcpy( (char *)code_mem + 1024, wait_sync_x64_code, sizeof(wait_sync_x64_code) ); + args[0] = (ULONG_PTR)&p->sync; + args[1] = 3; + if (is_wow64 && !old_wow64) call_func64( (ULONG64)(ULONG_PTR)code_mem + 1024, ARRAY_SIZE(args), args, code_mem ); +#endif + + p_context_exception_request_value = VirtualAlloc( NULL, sizeof(*p_context_exception_request_value), + MEM_RESERVE | MEM_COMMIT, PAGE_READONLY ); + ok( !!p_context_exception_request_value, "got NULL.\n" ); + *p_context_exception_request_value = 1; + ok( *p_context_exception_request_value == 1, "got %d.\n", *p_context_exception_request_value ); + VirtualFree( (void *)p_context_exception_request_value, 0, MEM_RELEASE ); + pRtlRemoveVectoredExceptionHandler( vectored_handler ); + +#ifndef _WIN64 + args[1] = 7; + if (is_wow64 && !old_wow64) call_func64( (ULONG64)(ULONG_PTR)code_mem + 1024, ARRAY_SIZE(args), args, code_mem ); +#endif + + return 0; +} + +static void test_context_exception_request(void) +{ + struct context_exception_request_thread_param p; + DWORD expected_flags; + HANDLE thread; + CONTEXT c; + BOOL ret; + + if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler) + { + skip( "RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found.\n" ); + return; + } + + c.ContextFlags = CONTEXT_CONTROL; + ret = GetThreadContext( GetCurrentThread(), &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + ok( c.ContextFlags == CONTEXT_CONTROL, "got %#lx.\n", c.ContextFlags ); + + expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( GetCurrentThread(), &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags || broken( c.ContextFlags == 0x10001 ) /* Win7 WoW64 */, + "got %#lx.\n", c.ContextFlags ); + if (c.ContextFlags == 0x10001) + { + win_skip( "Old WoW64 behaviour, skipping tests.\n" ); + return; + } + + ret = DuplicateHandle( GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &thread, 0, TRUE, DUPLICATE_SAME_ACCESS ); + ok( ret, "got error %lu.\n", GetLastError() ); + c.ContextFlags = expected_flags | CONTEXT_EXCEPTION_REQUEST; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + CloseHandle( thread ); + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE + | CONTEXT_EXCEPTION_ACTIVE; + ret = GetThreadContext( GetCurrentThread(), &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + + p.event = CreateEventW( NULL, FALSE, FALSE, NULL ); + thread = CreateThread( NULL, 0, test_context_exception_request_thread, &p, CREATE_SUSPENDED, NULL ); + ok( !!thread, "got error %lu.\n", GetLastError() ); + + expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags || broken( c.ContextFlags == (CONTEXT_CONTROL + | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING)) /* Win7 64 */, "got %#lx.\n", c.ContextFlags ); + + p.sync = 0; + ResumeThread(thread); + + while (ReadAcquire( &p.sync ) != 1) + SwitchToThread(); + /* thread is in user code. */ + SuspendThread( thread ); + + c.ContextFlags = CONTEXT_CONTROL; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + ok( c.ContextFlags == CONTEXT_CONTROL, "got %#lx.\n", c.ContextFlags ); + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE; + ret = SetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + + expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE + | CONTEXT_EXCEPTION_ACTIVE; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + + ResumeThread(thread); + WriteRelease( &p.sync, 2 ); + /* Try to make sure the thread entered WaitForSingleObject(). */ + Sleep(30); + + expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + + c.ContextFlags = CONTEXT_CONTROL; + ret = SetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE + | CONTEXT_EXCEPTION_ACTIVE; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + + SetEvent( p.event ); + + if (is_wow64 && !old_wow64) + { + while (ReadAcquire( &p.sync ) != 3) + SwitchToThread(); + /* thread is in x64 code. */ + + expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags, "got %#lx, expected %#lx.\n", c.ContextFlags, expected_flags ); + + WriteRelease( &p.sync, 4 ); + } + + while (ReadAcquire( &p.sync ) != 5) + SwitchToThread(); + + expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE + | CONTEXT_EXCEPTION_ACTIVE; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + + WriteRelease( &p.sync, 6 ); + + if (is_wow64 && !old_wow64) + { + while (ReadAcquire( &p.sync ) != 7) + SwitchToThread(); + /* thread is in x64 code. */ + + expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags, "got %#lx, expected %#lx.\n", c.ContextFlags, expected_flags ); + + WriteRelease( &p.sync, 8 ); + } + + WaitForSingleObject( thread, INFINITE ); + CloseHandle( thread ); + CloseHandle( p.event ); +} + START_TEST(exception) { HMODULE hkernel32 = GetModuleHandleA("kernel32.dll"); @@ -12617,5 +12911,6 @@ START_TEST(exception) test_suspend_thread(); test_suspend_process(); test_unload_trace(); + test_context_exception_request(); VirtualFree(code_mem, 0, MEM_RELEASE); } diff --git a/dlls/ntdll/tests/ntdll_test.h b/dlls/ntdll/tests/ntdll_test.h index 67278358b29..ed824583870 100644 --- a/dlls/ntdll/tests/ntdll_test.h +++ b/dlls/ntdll/tests/ntdll_test.h @@ -30,3 +30,5 @@ #include "wine/test.h" #include "wine/heap.h" + +extern NTSTATUS call_func64( ULONG64 func64, int nb_args, ULONG64 *args, void *code_mem ); diff --git a/dlls/ntdll/tests/wow64.c b/dlls/ntdll/tests/wow64.c index 01cd0cad035..1d48918165e 100644 --- a/dlls/ntdll/tests/wow64.c +++ b/dlls/ntdll/tests/wow64.c @@ -1440,7 +1440,7 @@ static const BYTE call_func64_code[] = 0xcb, /* lret */ }; -static NTSTATUS call_func64( ULONG64 func64, int nb_args, ULONG64 *args ) +NTSTATUS call_func64( ULONG64 func64, int nb_args, ULONG64 *args, void *code_mem ) { NTSTATUS (WINAPI *func)( ULONG64 func64, int nb_args, ULONG64 *args ) = code_mem; @@ -1940,7 +1940,7 @@ static void test_iosb(void) iosb64.Information = 0xdeadbeef; args[0] = (LONG_PTR)server; - status = call_func64( func, ARRAY_SIZE(args), args ); + status = call_func64( func, ARRAY_SIZE(args), args, code_mem ); ok( status == STATUS_PENDING, "NtFsControlFile returned %lx\n", status ); ok( iosb32.Status == 0x55555555, "status changed to %lx\n", iosb32.Status ); ok( iosb64.Pointer == PtrToUlong(&iosb32), "status changed to %lx\n", iosb64.Status ); @@ -1965,7 +1965,7 @@ static void test_iosb(void) args[8] = (ULONG_PTR)&id; args[9] = sizeof(id); - status = call_func64( func, ARRAY_SIZE(args), args ); + status = call_func64( func, ARRAY_SIZE(args), args, code_mem ); ok( status == STATUS_PENDING || status == STATUS_SUCCESS, "NtFsControlFile returned %lx\n", status ); todo_wine { @@ -1995,7 +1995,7 @@ static void test_iosb(void) id = 0xdeadbeef; args[0] = (LONG_PTR)server; - status = call_func64( func, ARRAY_SIZE(args), args ); + status = call_func64( func, ARRAY_SIZE(args), args, code_mem ); ok( status == STATUS_SUCCESS, "NtFsControlFile returned %lx\n", status ); ok( iosb32.Status == 0x55555555, "status changed to %lx\n", iosb32.Status ); ok( iosb32.Information == 0x55555555, "info changed to %Ix\n", iosb32.Information ); @@ -2018,7 +2018,7 @@ static NTSTATUS invoke_syscall( const char *name, ULONG args32[] ) else win_skip( "syscall thunk %s not recognized\n", name ); - return call_func64( func, ARRAY_SIZE(args64), args64 ); + return call_func64( func, ARRAY_SIZE(args64), args64, code_mem ); } static void test_syscalls(void) @@ -2124,14 +2124,14 @@ static void test_cpu_area(void) ULONG64 context, context_ex; ULONG64 args[] = { (ULONG_PTR)&machine, (ULONG_PTR)&context, (ULONG_PTR)&context_ex }; - status = call_func64( ptr, ARRAY_SIZE(args), args ); + status = call_func64( ptr, ARRAY_SIZE(args), args, code_mem ); ok( !status, "RtlWow64GetCpuAreaInfo failed %lx\n", status ); ok( machine == IMAGE_FILE_MACHINE_I386, "wrong machine %x\n", machine ); ok( context == teb64->TlsSlots[WOW64_TLS_CPURESERVED] + 4, "wrong context %s / %s\n", wine_dbgstr_longlong(context), wine_dbgstr_longlong(teb64->TlsSlots[WOW64_TLS_CPURESERVED]) ); ok( !context_ex, "got context_ex %s\n", wine_dbgstr_longlong(context_ex) ); args[0] = args[1] = args[2] = 0; - status = call_func64( ptr, ARRAY_SIZE(args), args ); + status = call_func64( ptr, ARRAY_SIZE(args), args, code_mem ); ok( !status, "RtlWow64GetCpuAreaInfo failed %lx\n", status ); } else win_skip( "RtlWow64GetCpuAreaInfo not supported\n" ); diff --git a/include/winnt.h b/include/winnt.h index 0f7c9430bfd..7f2a5f7b18b 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -1523,6 +1523,11 @@ typedef struct _CONTEXT_EX #endif } CONTEXT_EX, *PCONTEXT_EX; +#define CONTEXT_EXCEPTION_ACTIVE 0x08000000 +#define CONTEXT_SERVICE_ACTIVE 0x10000000 +#define CONTEXT_EXCEPTION_REQUEST 0x40000000 +#define CONTEXT_EXCEPTION_REPORTING 0x80000000 + #define CONTEXT_ARM 0x0200000 #define CONTEXT_ARM_CONTROL (CONTEXT_ARM | 0x00000001) #define CONTEXT_ARM_INTEGER (CONTEXT_ARM | 0x00000002) From 2c005f94fd055139ea556cb366fde8b77b4eb168 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 9 Apr 2024 17:26:10 -0600 Subject: [PATCH 305/413] ntdll: Set exception reporting flags in NtGetContextThread(). CW-Bug-Id: #23654 --- dlls/ntdll/tests/exception.c | 8 ++++---- dlls/ntdll/unix/signal_arm.c | 1 + dlls/ntdll/unix/signal_arm64.c | 3 +++ dlls/ntdll/unix/signal_i386.c | 1 + dlls/ntdll/unix/signal_x86_64.c | 2 ++ dlls/ntdll/unix/unix_private.h | 11 +++++++++++ 6 files changed, 22 insertions(+), 4 deletions(-) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 710bf19bd21..d11850fd841 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -12490,8 +12490,8 @@ static void test_context_exception_request(void) c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; ret = GetThreadContext( GetCurrentThread(), &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags || broken( c.ContextFlags == 0x10001 ) /* Win7 WoW64 */, - "got %#lx.\n", c.ContextFlags ); + ok( c.ContextFlags == expected_flags || broken( c.ContextFlags == 0x10001 ) /* Win7 WoW64 */, + "got %#lx.\n", c.ContextFlags ); if (c.ContextFlags == 0x10001) { win_skip( "Old WoW64 behaviour, skipping tests.\n" ); @@ -12505,14 +12505,14 @@ static void test_context_exception_request(void) c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; ret = GetThreadContext( thread, &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); CloseHandle( thread ); c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE | CONTEXT_EXCEPTION_ACTIVE; ret = GetThreadContext( GetCurrentThread(), &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); p.event = CreateEventW( NULL, FALSE, FALSE, NULL ); thread = CreateThread( NULL, 0, test_context_exception_request_thread, &p, CREATE_SUSPENDED, NULL ); diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c index afa3e71c5e2..56a897fcdd0 100644 --- a/dlls/ntdll/unix/signal_arm.c +++ b/dlls/ntdll/unix/signal_arm.c @@ -1033,6 +1033,7 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) memcpy( context->D, frame->d, sizeof(frame->d) ); context->ContextFlags |= CONTEXT_FLOATING_POINT; } + set_context_exception_reporting_flags( &context->ContextFlags, CONTEXT_SERVICE_ACTIVE ); return STATUS_SUCCESS; } diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c index 389f16f89d9..6785d56d7f2 100644 --- a/dlls/ntdll/unix/signal_arm64.c +++ b/dlls/ntdll/unix/signal_arm64.c @@ -710,6 +710,7 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) context->ContextFlags |= CONTEXT_FLOATING_POINT; } if (needed_flags & CONTEXT_DEBUG_REGISTERS) FIXME( "debug registers not supported\n" ); + set_context_exception_reporting_flags( &context->ContextFlags, CONTEXT_SERVICE_ACTIVE ); return STATUS_SUCCESS; } @@ -917,6 +918,7 @@ NTSTATUS get_thread_wow64_context( HANDLE handle, void *ctx, ULONG size ) context->Dr7 = wow_frame->Dr7; } /* FIXME: CONTEXT_I386_XSTATE */ + set_context_exception_reporting_flags( &context->ContextFlags, CONTEXT_SERVICE_ACTIVE ); break; } @@ -956,6 +958,7 @@ NTSTATUS get_thread_wow64_context( HANDLE handle, void *ctx, ULONG size ) memcpy( context->D, wow_frame->D, sizeof(wow_frame->D) ); context->ContextFlags |= CONTEXT_FLOATING_POINT; } + set_context_exception_reporting_flags( &context->ContextFlags, CONTEXT_SERVICE_ACTIVE ); break; } diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index b87fc077602..876a683617f 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -1168,6 +1168,7 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) x86_thread_data()->dr7 = context->Dr7; } } + set_context_exception_reporting_flags( &context->ContextFlags, CONTEXT_SERVICE_ACTIVE ); } if (context->ContextFlags & (CONTEXT_INTEGER & ~CONTEXT_i386)) diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 453be6ca1b3..847ede602fc 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1213,6 +1213,7 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) amd64_thread_data()->dr7 = context->Dr7; } } + set_context_exception_reporting_flags( &context->ContextFlags, CONTEXT_SERVICE_ACTIVE ); return STATUS_SUCCESS; } @@ -1415,6 +1416,7 @@ NTSTATUS get_thread_wow64_context( HANDLE handle, void *ctx, ULONG size ) frame->restore_flags |= CONTEXT_XSTATE; } } + set_context_exception_reporting_flags( &context->ContextFlags, CONTEXT_SERVICE_ACTIVE ); return STATUS_SUCCESS; } diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index 967422d9288..97546ca1797 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -228,6 +228,17 @@ extern int server_pipe( int fd[2] ); extern void fpux_to_fpu( I386_FLOATING_SAVE_AREA *fpu, const XSAVE_FORMAT *fpux ); extern void fpu_to_fpux( XSAVE_FORMAT *fpux, const I386_FLOATING_SAVE_AREA *fpu ); +static inline void set_context_exception_reporting_flags( DWORD *context_flags, DWORD reporting_flag ) +{ + if (!(*context_flags & CONTEXT_EXCEPTION_REQUEST)) + { + *context_flags &= ~(CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE | CONTEXT_EXCEPTION_ACTIVE); + return; + } + *context_flags = (*context_flags & ~(CONTEXT_SERVICE_ACTIVE | CONTEXT_EXCEPTION_ACTIVE)) + | CONTEXT_EXCEPTION_REPORTING | reporting_flag; +} + extern BOOL xstate_compaction_enabled; extern UINT64 xstate_supported_features_mask; extern UINT64 xstate_features_size; From 84a9a4bfb4908c55a20ed45c0a4bf4f2dc337f24 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 9 Apr 2024 17:35:56 -0600 Subject: [PATCH 306/413] ntdll: Store exception reporting flags in server context. CW-Bug-Id: #23654 --- dlls/ntdll/tests/exception.c | 18 ++++++------- dlls/ntdll/unix/server.c | 4 +++ dlls/ntdll/unix/thread.c | 50 +++++++++++++++++++++++++++++++++--- server/protocol.def | 13 ++++++++++ server/thread.c | 5 ++-- server/trace.c | 13 ++++++++++ tools/make_requests | 2 +- 7 files changed, 89 insertions(+), 16 deletions(-) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index d11850fd841..c7ba65795de 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -287,7 +287,7 @@ static void check_context_exception_request_( DWORD flags, BOOL hardware_excepti if (!(flags & CONTEXT_EXCEPTION_REPORTING)) return; expected_flags |= hardware_exception ? CONTEXT_EXCEPTION_ACTIVE : CONTEXT_SERVICE_ACTIVE; - ok_(__FILE__, line)( (flags & exception_reporting_flags) == expected_flags, "got %#lx, expected %#lx.\n", + todo_wine ok_(__FILE__, line)( (flags & exception_reporting_flags) == expected_flags, "got %#lx, expected %#lx.\n", flags, expected_flags ); } #endif @@ -1163,7 +1163,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) ctx.ContextFlags = CONTEXT_FULL | CONTEXT_EXTENDED_REGISTERS | CONTEXT_EXCEPTION_REQUEST; status = pNtGetContextThread(pi.hThread, &ctx); ok(!status, "NtGetContextThread failed with 0x%lx\n", status); - todo_wine ok(ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING + ok(ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING || broken( !(ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) ) /* Win7 WoW64 */, "got %#lx.\n", ctx.ContextFlags); @@ -4073,7 +4073,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) ctx.ContextFlags = CONTEXT_FULL | CONTEXT_SEGMENTS | CONTEXT_EXCEPTION_REQUEST; status = pNtGetContextThread(pi.hThread, &ctx); ok(!status, "NtGetContextThread failed with 0x%lx\n", status); - todo_wine ok(ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING, "got %#lx.\n", ctx.ContextFlags); + ok(ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING, "got %#lx.\n", ctx.ContextFlags); trace("exception 0x%lx at %p firstchance=%ld Rip=%p, Rax=%p\n", de.u.Exception.ExceptionRecord.ExceptionCode, @@ -4628,7 +4628,7 @@ static void test_wow64_context(void) ctx.ContextFlags = WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST; ret = pRtlWow64GetThreadContext( pi.hThread, &ctx ); ok(ret == STATUS_SUCCESS, "got %#lx\n", ret); - todo_wine ok( (ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) || broken( ctx.ContextFlags == WOW64_CONTEXT_ALL ) /*Win 7*/, + ok( (ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) || broken( ctx.ContextFlags == WOW64_CONTEXT_ALL ) /*Win 7*/, "got context flags %#lx\n", ctx.ContextFlags ); if (context.SegCs == cs32) @@ -4714,7 +4714,7 @@ static void test_wow64_context(void) { trace( "in 64-bit mode %04x\n", context.SegCs ); if (ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) - ok( ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST + todo_wine ok( ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE) || ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE), @@ -12548,13 +12548,13 @@ static void test_context_exception_request(void) c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; ret = GetThreadContext( thread, &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE | CONTEXT_EXCEPTION_ACTIVE; ret = GetThreadContext( thread, &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); ResumeThread(thread); WriteRelease( &p.sync, 2 ); @@ -12604,13 +12604,13 @@ static void test_context_exception_request(void) c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; ret = GetThreadContext( thread, &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE | CONTEXT_EXCEPTION_ACTIVE; ret = GetThreadContext( thread, &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); WriteRelease( &p.sync, 6 ); diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c index 6148cfddce2..992e5cfdc3d 100644 --- a/dlls/ntdll/unix/server.c +++ b/dlls/ntdll/unix/server.c @@ -762,7 +762,11 @@ unsigned int server_select( const select_op_t *select_op, data_size_t size, UINT if (ret == STATUS_USER_APC) *user_apc = reply_data.call.user; if (reply_size > sizeof(reply_data.call)) + { memcpy( context, reply_data.context, reply_size - sizeof(reply_data.call) ); + context[0].flags &= ~SERVER_CTX_EXEC_SPACE; + context[1].flags &= ~SERVER_CTX_EXEC_SPACE; + } return ret; } diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 812302bf346..65fa0aa9894 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -184,7 +184,7 @@ BOOL validate_context_xstate( CONTEXT *context ) */ static unsigned int get_server_context_flags( const void *context, USHORT machine ) { - unsigned int flags, ret = 0; + unsigned int flags = 0, ret = 0; switch (machine) { @@ -222,6 +222,7 @@ static unsigned int get_server_context_flags( const void *context, USHORT machin if (flags & CONTEXT_ARM64_DEBUG_REGISTERS) ret |= SERVER_CTX_DEBUG_REGISTERS; break; } + if (flags & CONTEXT_EXCEPTION_REQUEST) ret |= SERVER_CTX_EXEC_SPACE; return ret; } @@ -236,11 +237,11 @@ static unsigned int get_native_context_flags( USHORT native_machine, USHORT wow_ switch (MAKELONG( native_machine, wow_machine )) { case MAKELONG( IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_I386 ): - return SERVER_CTX_DEBUG_REGISTERS | SERVER_CTX_FLOATING_POINT | SERVER_CTX_YMM_REGISTERS; + return SERVER_CTX_DEBUG_REGISTERS | SERVER_CTX_FLOATING_POINT | SERVER_CTX_YMM_REGISTERS | SERVER_CTX_EXEC_SPACE; case MAKELONG( IMAGE_FILE_MACHINE_ARM64, IMAGE_FILE_MACHINE_ARMNT ): - return SERVER_CTX_DEBUG_REGISTERS | SERVER_CTX_FLOATING_POINT; + return SERVER_CTX_DEBUG_REGISTERS | SERVER_CTX_FLOATING_POINT | SERVER_CTX_EXEC_SPACE; default: - return 0; + return SERVER_CTX_EXEC_SPACE; } } @@ -260,6 +261,21 @@ static void xstate_to_server( context_t *to, const CONTEXT_EX *xctx ) } +/*********************************************************************** + * exception_request_flags_to_server + * + * Copy exception reporting flags to the server format. + */ +static void exception_request_flags_to_server( context_t *to, DWORD context_flags ) +{ + if (!(context_flags & CONTEXT_EXCEPTION_REPORTING)) return; + to->flags |= SERVER_CTX_EXEC_SPACE; + if (context_flags & CONTEXT_SERVICE_ACTIVE) to->exec_space.space.space = EXEC_SPACE_SYSCALL; + else if (context_flags & CONTEXT_EXCEPTION_ACTIVE) to->exec_space.space.space = EXEC_SPACE_EXCEPTION; + else to->exec_space.space.space = EXEC_SPACE_USERMODE; +} + + /*********************************************************************** * context_to_server * @@ -337,6 +353,7 @@ static NTSTATUS context_to_server( context_t *to, USHORT to_machine, const void } if (flags & CONTEXT_I386_XSTATE) xstate_to_server( to, (const CONTEXT_EX *)(from + 1) ); + exception_request_flags_to_server( to, flags ); return STATUS_SUCCESS; } @@ -395,6 +412,7 @@ static NTSTATUS context_to_server( context_t *to, USHORT to_machine, const void } if (flags & CONTEXT_I386_XSTATE) xstate_to_server( to, (const CONTEXT_EX *)(from + 1) ); + exception_request_flags_to_server( to, flags ); return STATUS_SUCCESS; } @@ -456,6 +474,7 @@ static NTSTATUS context_to_server( context_t *to, USHORT to_machine, const void } if (flags & CONTEXT_AMD64_XSTATE) xstate_to_server( to, (const CONTEXT_EX *)(from + 1) ); + exception_request_flags_to_server( to, flags ); return STATUS_SUCCESS; } @@ -521,6 +540,7 @@ static NTSTATUS context_to_server( context_t *to, USHORT to_machine, const void } if (flags & CONTEXT_AMD64_XSTATE) xstate_to_server( to, (const CONTEXT_EX *)(from + 1) ); + exception_request_flags_to_server( to, flags ); return STATUS_SUCCESS; } @@ -568,6 +588,7 @@ static NTSTATUS context_to_server( context_t *to, USHORT to_machine, const void for (i = 0; i < ARM_MAX_WATCHPOINTS; i++) to->debug.arm_regs.wvr[i] = from->Wvr[i]; for (i = 0; i < ARM_MAX_WATCHPOINTS; i++) to->debug.arm_regs.wcr[i] = from->Wcr[i]; } + exception_request_flags_to_server( to, flags ); return STATUS_SUCCESS; } @@ -609,6 +630,7 @@ static NTSTATUS context_to_server( context_t *to, USHORT to_machine, const void for (i = 0; i < ARM64_MAX_WATCHPOINTS; i++) to->debug.arm64_regs.wcr[i] = from->Wcr[i]; for (i = 0; i < ARM64_MAX_WATCHPOINTS; i++) to->debug.arm64_regs.wvr[i] = from->Wvr[i]; } + exception_request_flags_to_server( to, flags ); return STATUS_SUCCESS; } @@ -650,6 +672,20 @@ static void xstate_from_server( CONTEXT_EX *xctx, const context_t *from ) } +/*********************************************************************** + * exception_request_flags_from_server + * + * Copy exception reporting flags from the server format. + */ +static void exception_request_flags_from_server( DWORD *context_flags, const context_t *from ) +{ + if (!(*context_flags & CONTEXT_EXCEPTION_REQUEST) || !(from->flags & SERVER_CTX_EXEC_SPACE)) return; + *context_flags = (*context_flags & ~(CONTEXT_SERVICE_ACTIVE | CONTEXT_EXCEPTION_ACTIVE)) | CONTEXT_EXCEPTION_REPORTING; + if (from->exec_space.space.space == EXEC_SPACE_SYSCALL) *context_flags |= CONTEXT_SERVICE_ACTIVE; + else if (from->exec_space.space.space == EXEC_SPACE_EXCEPTION) *context_flags |= CONTEXT_EXCEPTION_ACTIVE; +} + + /*********************************************************************** * context_from_server * @@ -724,6 +760,7 @@ static NTSTATUS context_from_server( void *dst, const context_t *from, USHORT ma } if ((from->flags & SERVER_CTX_YMM_REGISTERS) && (to_flags & CONTEXT_I386_XSTATE)) xstate_from_server( (CONTEXT_EX *)(to + 1), from ); + exception_request_flags_from_server( &to->ContextFlags, from ); return STATUS_SUCCESS; } @@ -785,6 +822,7 @@ static NTSTATUS context_from_server( void *dst, const context_t *from, USHORT ma } if ((from->flags & SERVER_CTX_YMM_REGISTERS) && (to_flags & CONTEXT_I386_XSTATE)) xstate_from_server( (CONTEXT_EX *)(to + 1), from ); + exception_request_flags_from_server( &to->ContextFlags, from ); return STATUS_SUCCESS; } @@ -847,6 +885,7 @@ static NTSTATUS context_from_server( void *dst, const context_t *from, USHORT ma } if ((from->flags & SERVER_CTX_YMM_REGISTERS) && (to_flags & CONTEXT_AMD64_XSTATE)) xstate_from_server( (CONTEXT_EX *)(to + 1), from ); + exception_request_flags_from_server( &to->ContextFlags, from ); return STATUS_SUCCESS; } @@ -916,6 +955,7 @@ static NTSTATUS context_from_server( void *dst, const context_t *from, USHORT ma } if ((from->flags & SERVER_CTX_YMM_REGISTERS) && (to_flags & CONTEXT_AMD64_XSTATE)) xstate_from_server( (CONTEXT_EX *)(to + 1), from ); + exception_request_flags_from_server( &to->ContextFlags, from ); return STATUS_SUCCESS; } @@ -963,6 +1003,7 @@ static NTSTATUS context_from_server( void *dst, const context_t *from, USHORT ma for (i = 0; i < ARM_MAX_WATCHPOINTS; i++) to->Wvr[i] = from->debug.arm_regs.wvr[i]; for (i = 0; i < ARM_MAX_WATCHPOINTS; i++) to->Wcr[i] = from->debug.arm_regs.wcr[i]; } + exception_request_flags_from_server( &to->ContextFlags, from ); return STATUS_SUCCESS; } @@ -1004,6 +1045,7 @@ static NTSTATUS context_from_server( void *dst, const context_t *from, USHORT ma for (i = 0; i < ARM64_MAX_WATCHPOINTS; i++) to->Wcr[i] = from->debug.arm64_regs.wcr[i]; for (i = 0; i < ARM64_MAX_WATCHPOINTS; i++) to->Wvr[i] = from->debug.arm64_regs.wvr[i]; } + exception_request_flags_from_server( &to->ContextFlags, from ); return STATUS_SUCCESS; } diff --git a/server/protocol.def b/server/protocol.def index d6926784fe5..50bc1035359 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -125,6 +125,14 @@ typedef union } unload_dll; } debug_event_t; + +enum context_exec_space +{ + EXEC_SPACE_USERMODE, + EXEC_SPACE_SYSCALL, + EXEC_SPACE_EXCEPTION, +}; + /* context data */ typedef struct { @@ -171,6 +179,10 @@ typedef struct unsigned char i386_regs[512]; } ext; /* selected by SERVER_CTX_EXTENDED_REGISTERS */ union + { + struct { enum context_exec_space space; int __pad; } space; + } exec_space; /* selected by SERVER_CTX_EXEC_SPACE */ + union { struct { struct { unsigned __int64 low, high; } ymm_high[16]; } regs; } ymm; /* selected by SERVER_CTX_YMM_REGISTERS */ @@ -183,6 +195,7 @@ typedef struct #define SERVER_CTX_DEBUG_REGISTERS 0x10 #define SERVER_CTX_EXTENDED_REGISTERS 0x20 #define SERVER_CTX_YMM_REGISTERS 0x40 +#define SERVER_CTX_EXEC_SPACE 0x80 /* structure used in sending an fd from client to server */ struct send_fd diff --git a/server/thread.c b/server/thread.c index e54e83c178a..0620686399d 100644 --- a/server/thread.c +++ b/server/thread.c @@ -1564,6 +1564,7 @@ static void copy_context( context_t *to, const context_t *from, unsigned int fla if (flags & SERVER_CTX_DEBUG_REGISTERS) to->debug = from->debug; if (flags & SERVER_CTX_EXTENDED_REGISTERS) to->ext = from->ext; if (flags & SERVER_CTX_YMM_REGISTERS) to->ymm = from->ymm; + if (flags & SERVER_CTX_EXEC_SPACE) to->exec_space = from->exec_space; } /* gets the current impersonation token */ @@ -2224,14 +2225,14 @@ DECL_HANDLER(set_thread_context) /* If context is in a pending state, we don't know if we will use WoW or native * context, so store both and discard irrevelant one in select request. */ const int is_pending = thread->context->status == STATUS_PENDING; - unsigned int native_flags = contexts[CTX_NATIVE].flags; + unsigned int native_flags = contexts[CTX_NATIVE].flags & ~SERVER_CTX_EXEC_SPACE; if (ctx_count == 2 && (is_pending || thread->context->regs[CTX_WOW].machine)) { context_t *ctx = &thread->context->regs[CTX_WOW]; /* some regs are always set from the native context */ - flags = contexts[CTX_WOW].flags & ~req->native_flags; + flags = contexts[CTX_WOW].flags & ~(req->native_flags | SERVER_CTX_EXEC_SPACE); if (is_pending) ctx->machine = contexts[CTX_WOW].machine; else native_flags &= req->native_flags; diff --git a/server/trace.c b/server/trace.c index 313a4541a1a..a44836e6543 100644 --- a/server/trace.c +++ b/server/trace.c @@ -840,6 +840,19 @@ static void dump_varargs_context( const char *prefix, data_size_t size ) fprintf( stderr, "%s{machine=%04x", prefix, ctx.machine ); break; } + if (ctx.flags & SERVER_CTX_EXEC_SPACE) + { + const char *space; + + switch (ctx.exec_space.space.space) + { + case EXEC_SPACE_USERMODE: space = "user"; break; + case EXEC_SPACE_SYSCALL: space = "syscall"; break; + case EXEC_SPACE_EXCEPTION: space = "exception"; break; + default: space = "invalid"; break; + } + fprintf( stderr, ",exec_space=%s", space ); + } fputc( '}', stderr ); remove_data( size ); } diff --git a/tools/make_requests b/tools/make_requests index 77b5ab331f4..b450e22c2b8 100755 --- a/tools/make_requests +++ b/tools/make_requests @@ -54,7 +54,7 @@ my %formats = "hw_input_t" => [ 40, 8, "&dump_hw_input" ], # varargs-only structures "apc_call_t" => [ 64, 8 ], - "context_t" => [ 1720, 8 ], + "context_t" => [ 1728, 8 ], "cursor_pos_t" => [ 24, 8 ], "debug_event_t" => [ 160, 8 ], "message_data_t" => [ 56, 8 ], From 253b2ba09f60b904b5f0ebefaa0a780911c640aa Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 9 Apr 2024 18:00:11 -0600 Subject: [PATCH 307/413] ntdll: Store exception reporting flags on suspend. CW-Bug-Id: #23654 --- dlls/ntdll/tests/exception.c | 10 +++++----- dlls/ntdll/unix/signal_arm.c | 9 +++++++-- dlls/ntdll/unix/signal_arm64.c | 9 +++++++-- dlls/ntdll/unix/signal_i386.c | 4 +++- dlls/ntdll/unix/signal_x86_64.c | 6 ++++-- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index c7ba65795de..5b0d0662fbb 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -4714,7 +4714,7 @@ static void test_wow64_context(void) { trace( "in 64-bit mode %04x\n", context.SegCs ); if (ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) - todo_wine ok( ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST + ok( ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE) || ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE), @@ -12523,8 +12523,8 @@ static void test_context_exception_request(void) c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; ret = GetThreadContext( thread, &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags || broken( c.ContextFlags == (CONTEXT_CONTROL - | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING)) /* Win7 64 */, "got %#lx.\n", c.ContextFlags ); + ok( c.ContextFlags == expected_flags || broken( c.ContextFlags == (CONTEXT_CONTROL + | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING)) /* Win7 64 */, "got %#lx.\n", c.ContextFlags ); p.sync = 0; ResumeThread(thread); @@ -12566,7 +12566,7 @@ static void test_context_exception_request(void) c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; ret = GetThreadContext( thread, &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); c.ContextFlags = CONTEXT_CONTROL; ret = SetThreadContext( thread, &c ); @@ -12576,7 +12576,7 @@ static void test_context_exception_request(void) | CONTEXT_EXCEPTION_ACTIVE; ret = GetThreadContext( thread, &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); SetEvent( p.event ); diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c index 56a897fcdd0..0484e57eb8c 100644 --- a/dlls/ntdll/unix/signal_arm.c +++ b/dlls/ntdll/unix/signal_arm.c @@ -1525,7 +1525,7 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) if (is_inside_syscall( sigcontext )) { - context.ContextFlags = CONTEXT_FULL; + context.ContextFlags = CONTEXT_FULL | CONTEXT_EXCEPTION_REQUEST; NtGetContextThread( GetCurrentThread(), &context ); wait_suspend( &context ); NtSetContextThread( GetCurrentThread(), &context ); @@ -1533,6 +1533,7 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) else { save_context( &context, sigcontext ); + context.ContextFlags |= CONTEXT_EXCEPTION_REPORTING; wait_suspend( &context ); restore_context( &context, sigcontext ); } @@ -1639,7 +1640,11 @@ void call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, TEB if (context.Pc & 1) context.Cpsr |= 0x20; /* thumb mode */ if ((ctx = get_cpu_area( IMAGE_FILE_MACHINE_ARMNT ))) *ctx = context; - if (suspend) wait_suspend( &context ); + if (suspend) + { + context.ContextFlags |= CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE; + wait_suspend( &context ); + } ctx = (CONTEXT *)((ULONG_PTR)context.Sp & ~15) - 1; *ctx = context; diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c index 6785d56d7f2..5afc2cc2b91 100644 --- a/dlls/ntdll/unix/signal_arm64.c +++ b/dlls/ntdll/unix/signal_arm64.c @@ -1494,7 +1494,7 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) if (is_inside_syscall( sigcontext )) { - context.ContextFlags = CONTEXT_FULL; + context.ContextFlags = CONTEXT_FULL | CONTEXT_EXCEPTION_REQUEST; NtGetContextThread( GetCurrentThread(), &context ); wait_suspend( &context ); NtSetContextThread( GetCurrentThread(), &context ); @@ -1502,6 +1502,7 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) else { save_context( &context, sigcontext ); + context.ContextFlags |= CONTEXT_EXCEPTION_REPORTING; wait_suspend( &context ); restore_context( &context, sigcontext ); } @@ -1687,7 +1688,11 @@ void call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, TEB if (arm_context->Pc & 1) arm_context->Cpsr |= 0x20; /* thumb mode */ } - if (suspend) wait_suspend( &context ); + if (suspend) + { + context.ContextFlags |= CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE; + wait_suspend( &context ); + } ctx = (CONTEXT *)((ULONG_PTR)context.Sp & ~15) - 1; *ctx = context; diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index 876a683617f..61df990ce61 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -2159,7 +2159,7 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) ERR_(seh)( "kernel stack overflow.\n" ); return; } - context->c.ContextFlags = CONTEXT_FULL; + context->c.ContextFlags = CONTEXT_FULL | CONTEXT_EXCEPTION_REQUEST; NtGetContextThread( GetCurrentThread(), &context->c ); if (xstate_extended_features()) { @@ -2182,6 +2182,7 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) struct xcontext context; save_context( &context, ucontext ); + context.c.ContextFlags |= CONTEXT_EXCEPTION_REPORTING; wait_suspend( &context.c ); restore_context( &context, ucontext ); } @@ -2551,6 +2552,7 @@ void call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, TEB if (suspend) { + context.ContextFlags |= CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE; wait_suspend( &context ); if (context.ContextFlags & CONTEXT_DEBUG_REGISTERS & ~CONTEXT_i386) { diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 847ede602fc..c4790a00b47 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -2516,7 +2516,7 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) ERR_(seh)( "kernel stack overflow.\n" ); return; } - context->c.ContextFlags = CONTEXT_FULL | CONTEXT_SEGMENTS; + context->c.ContextFlags = CONTEXT_FULL | CONTEXT_SEGMENTS | CONTEXT_EXCEPTION_REQUEST; NtGetContextThread( GetCurrentThread(), &context->c ); if (xstate_extended_features()) { @@ -2539,6 +2539,8 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) struct xcontext context; save_context( &context, ucontext ); + context.c.ContextFlags |= CONTEXT_EXCEPTION_REPORTING; + if (is_wow64() && context.c.SegCs == cs64_sel) context.c.ContextFlags |= CONTEXT_EXCEPTION_ACTIVE; wait_suspend( &context.c ); restore_context( &context, ucontext ); } @@ -2885,7 +2887,7 @@ void call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, TEB # error Please define setting %gs for your architecture #endif - context.ContextFlags = CONTEXT_ALL; + context.ContextFlags = CONTEXT_ALL | CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE; context.Rcx = (ULONG_PTR)entry; context.Rdx = (ULONG_PTR)arg; context.Rsp = (ULONG_PTR)teb->Tib.StackBase - 0x28; From 228480d5de0b918e270ce4538575157a7e7a7f73 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 8 Apr 2024 10:47:02 -0600 Subject: [PATCH 308/413] ntdll: Store exception reporting flags for debug events. CW-Bug-Id: #23654 --- dlls/ntdll/tests/exception.c | 6 +++--- dlls/ntdll/unix/signal_arm.c | 2 +- dlls/ntdll/unix/signal_arm64.c | 2 +- dlls/ntdll/unix/signal_i386.c | 2 +- dlls/ntdll/unix/signal_x86_64.c | 2 +- dlls/ntdll/unix/thread.c | 6 ++++-- dlls/ntdll/unix/unix_private.h | 2 +- 7 files changed, 12 insertions(+), 10 deletions(-) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 5b0d0662fbb..1d1c9697713 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -287,7 +287,7 @@ static void check_context_exception_request_( DWORD flags, BOOL hardware_excepti if (!(flags & CONTEXT_EXCEPTION_REPORTING)) return; expected_flags |= hardware_exception ? CONTEXT_EXCEPTION_ACTIVE : CONTEXT_SERVICE_ACTIVE; - todo_wine ok_(__FILE__, line)( (flags & exception_reporting_flags) == expected_flags, "got %#lx, expected %#lx.\n", + ok_(__FILE__, line)( (flags & exception_reporting_flags) == expected_flags, "got %#lx, expected %#lx.\n", flags, expected_flags ); } #endif @@ -12591,7 +12591,7 @@ static void test_context_exception_request(void) c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; ret = GetThreadContext( thread, &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags, "got %#lx, expected %#lx.\n", c.ContextFlags, expected_flags ); + ok( c.ContextFlags == expected_flags, "got %#lx, expected %#lx.\n", c.ContextFlags, expected_flags ); WriteRelease( &p.sync, 4 ); } @@ -12625,7 +12625,7 @@ static void test_context_exception_request(void) c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; ret = GetThreadContext( thread, &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags, "got %#lx, expected %#lx.\n", c.ContextFlags, expected_flags ); + ok( c.ContextFlags == expected_flags, "got %#lx, expected %#lx.\n", c.ContextFlags, expected_flags ); WriteRelease( &p.sync, 8 ); } diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c index 0484e57eb8c..dbad562c6d3 100644 --- a/dlls/ntdll/unix/signal_arm.c +++ b/dlls/ntdll/unix/signal_arm.c @@ -1071,7 +1071,7 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec ) rec->ExceptionAddress = (void *)PC_sig(sigcontext); save_context( &context, sigcontext ); - status = send_debug_event( rec, &context, TRUE ); + status = send_debug_event( rec, &context, TRUE, TRUE ); if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED) { restore_context( &context, sigcontext ); diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c index 5afc2cc2b91..a4bae5563a8 100644 --- a/dlls/ntdll/unix/signal_arm64.c +++ b/dlls/ntdll/unix/signal_arm64.c @@ -982,7 +982,7 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec ) rec->ExceptionAddress = (void *)PC_sig(sigcontext); save_context( &context, sigcontext ); - status = send_debug_event( rec, &context, TRUE ); + status = send_debug_event( rec, &context, TRUE, TRUE ); if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED) { restore_context( &context, sigcontext ); diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index 61df990ce61..faf09c05371 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -1465,7 +1465,7 @@ static void setup_raise_exception( ucontext_t *sigcontext, void *stack_ptr, struct exc_stack_layout *stack; size_t stack_size; unsigned int xstate_size; - NTSTATUS status = send_debug_event( rec, context, TRUE ); + NTSTATUS status = send_debug_event( rec, context, TRUE, TRUE ); if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED) { diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index c4790a00b47..be9af2c23d1 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1451,7 +1451,7 @@ static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec context->EFlags &= ~0x100; /* clear single-step flag */ } - status = send_debug_event( rec, context, TRUE ); + status = send_debug_event( rec, context, TRUE, TRUE ); if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED) { restore_context( xcontext, sigcontext ); diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 65fa0aa9894..c3ab1317295 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -1506,7 +1506,7 @@ void wait_suspend( CONTEXT *context ) * * Send an EXCEPTION_DEBUG_EVENT event to the debugger. */ -NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_chance ) +NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_chance, BOOL exception ) { unsigned int ret; DWORD i; @@ -1543,6 +1543,8 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c select_op.wait.handles[0] = handle; contexts_to_server( server_contexts, context ); + server_contexts[0].flags |= SERVER_CTX_EXEC_SPACE; + server_contexts[0].exec_space.space.space = exception ? EXEC_SPACE_EXCEPTION : EXEC_SPACE_SYSCALL; server_select( &select_op, offsetof( select_op_t, wait.handles[1] ), SELECT_INTERRUPTIBLE, TIMEOUT_INFINITE, server_contexts, NULL ); @@ -1565,7 +1567,7 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c */ NTSTATUS WINAPI NtRaiseException( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_chance ) { - NTSTATUS status = send_debug_event( rec, context, first_chance ); + NTSTATUS status = send_debug_event( rec, context, first_chance, !(is_win64 || is_wow64() || is_old_wow64()) ); if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED) return NtContinue( context, FALSE ); diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index 97546ca1797..2b62d5c7754 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -257,7 +257,7 @@ extern void DECLSPEC_NORETURN abort_thread( int status ); extern void DECLSPEC_NORETURN abort_process( int status ); extern void DECLSPEC_NORETURN exit_process( int status ); extern void wait_suspend( CONTEXT *context ); -extern NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_chance ); +extern NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_chance, BOOL exception ); extern BOOL validate_context_xstate( CONTEXT *context ); extern NTSTATUS set_thread_context( HANDLE handle, const void *context, BOOL *self, USHORT machine ); extern NTSTATUS get_thread_context( HANDLE handle, void *context, BOOL *self, USHORT machine ); From f490b506f170a8cab91f5858ce1dad19c1a42847 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 9 Apr 2024 20:36:26 -0600 Subject: [PATCH 309/413] Revert "ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write watch support." This reverts commit 60b7ce66f2d4c96240dd8baf8e0e9110d4ebd14b. CW-Bug-Id: #23654 --- dlls/ntdll/unix/virtual.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index b4d0cec3a21..6c1dc3a8b88 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -4998,22 +4998,6 @@ static NTSTATUS allocate_virtual_memory( void **ret, SIZE_T *size_ptr, ULONG typ SIZE_T size = *size_ptr; NTSTATUS status = STATUS_SUCCESS; - if (type & MEM_WRITE_WATCH) - { - static int disable = -1; - - if (disable == -1) - { - const char *env_var; - - if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) - FIXME("Disabling write watch support.\n"); - } - - if (disable) - return STATUS_NOT_SUPPORTED; - } - /* Round parameters to a page boundary */ if (is_beyond_limit( 0, size, working_set_limit )) return STATUS_WORKING_SET_LIMIT_RANGE; From b7322742e6449a557e9716784d42772ede8fb07a Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Fri, 19 Jan 2024 13:42:14 +0100 Subject: [PATCH 310/413] winevulkan: Return result through NtCallbackReturn for the debug callbacks. (cherry picked from commit 643538a836f33b4996730cf05dc2af77667d6150) --- dlls/winevulkan/loader.c | 14 +++++++++----- dlls/winevulkan/vulkan.c | 13 ++++++------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c index dbb44c556f6..e35b4f42427 100644 --- a/dlls/winevulkan/loader.c +++ b/dlls/winevulkan/loader.c @@ -625,15 +625,19 @@ void WINAPI vkFreeCommandBuffers(VkDevice device, VkCommandPool cmd_pool, uint32 } } -static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) +static NTSTATUS WINAPI call_vulkan_debug_report_callback( void *args, ULONG size ) { - return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, - params->code, params->layer_prefix, params->message, params->user_data); + struct wine_vk_debug_report_params *params = args; + VkBool32 ret = params->user_callback(params->flags, params->object_type, params->object_handle, params->location, + params->code, params->layer_prefix, params->message, params->user_data); + return NtCallbackReturn( &ret, sizeof(ret), STATUS_SUCCESS ); } -static BOOL WINAPI call_vulkan_debug_utils_callback( struct wine_vk_debug_utils_params *params, ULONG size ) +static NTSTATUS WINAPI call_vulkan_debug_utils_callback( void *args, ULONG size ) { - return params->user_callback(params->severity, params->message_types, ¶ms->data, params->user_data); + struct wine_vk_debug_utils_params *params = args; + VkBool32 ret = params->user_callback(params->severity, params->message_types, ¶ms->data, params->user_data); + return NtCallbackReturn( &ret, sizeof(ret), STATUS_SUCCESS ); } BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, void *reserved) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 885af7ae970..e4e437327f3 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -169,7 +169,6 @@ static VkBool32 debug_utils_callback_conversion(VkDebugUtilsMessageSeverityFlagB struct wine_debug_utils_messenger *object; void *ret_ptr; ULONG ret_len; - VkBool32 result; unsigned int i; TRACE("%i, %u, %p, %p\n", severity, message_types, callback_data, user_data); @@ -217,12 +216,11 @@ static VkBool32 debug_utils_callback_conversion(VkDebugUtilsMessageSeverityFlagB params.data.pObjects = object_name_infos; /* applications should always return VK_FALSE */ - result = KeUserModeCallback( NtUserCallVulkanDebugUtilsCallback, ¶ms, sizeof(params), - &ret_ptr, &ret_len ); + KeUserModeCallback( NtUserCallVulkanDebugUtilsCallback, ¶ms, sizeof(params), &ret_ptr, &ret_len ); free(object_name_infos); - - return result; + if (ret_len == sizeof(VkBool32)) return *(VkBool32 *)ret_ptr; + return VK_FALSE; } static VkBool32 debug_report_callback_conversion(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT object_type, @@ -258,8 +256,9 @@ static VkBool32 debug_report_callback_conversion(VkDebugReportFlagsEXT flags, Vk if (!params.object_handle) params.object_type = VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT; - return KeUserModeCallback( NtUserCallVulkanDebugReportCallback, ¶ms, sizeof(params), - &ret_ptr, &ret_len ); + KeUserModeCallback( NtUserCallVulkanDebugReportCallback, ¶ms, sizeof(params), &ret_ptr, &ret_len ); + if (ret_len == sizeof(VkBool32)) return *(VkBool32 *)ret_ptr; + return VK_FALSE; } static void wine_vk_physical_device_free(struct wine_phys_dev *phys_dev) From 16527b7ba424c1cf9c91f30808f2c1adba09f6a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 25 Nov 2023 01:44:49 +0100 Subject: [PATCH 311/413] winevulkan: Keep the create_info HWND on the surface wrappers. (cherry picked from commit 982c2ede7a8e1bda5f10d5a662448948b04efbb8) --- dlls/winevulkan/vulkan.c | 16 ++++++---------- dlls/winevulkan/vulkan_private.h | 3 ++- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index e4e437327f3..818f8e8655c 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -2566,24 +2566,20 @@ VkResult wine_vkCreateSwapchainKHR(VkDevice device_handle, const VkSwapchainCrea return res; } -VkResult wine_vkCreateWin32SurfaceKHR(VkInstance handle, const VkWin32SurfaceCreateInfoKHR *createInfo, +VkResult wine_vkCreateWin32SurfaceKHR(VkInstance handle, const VkWin32SurfaceCreateInfoKHR *create_info, const VkAllocationCallbacks *allocator, VkSurfaceKHR *surface) { struct wine_instance *instance = wine_instance_from_handle(handle); struct wine_surface *object; VkResult res; - if (allocator) - FIXME("Support for allocation callbacks not implemented yet\n"); - - object = calloc(1, sizeof(*object)); - - if (!object) - return VK_ERROR_OUT_OF_HOST_MEMORY; + if (allocator) FIXME("Support for allocation callbacks not implemented yet\n"); - res = instance->funcs.p_vkCreateWin32SurfaceKHR(instance->host_instance, createInfo, NULL, - &object->driver_surface); + if (!(object = calloc(1, sizeof(*object)))) return VK_ERROR_OUT_OF_HOST_MEMORY; + object->hwnd = create_info->hwnd; + res = instance->funcs.p_vkCreateWin32SurfaceKHR(instance->host_instance, create_info, + NULL /* allocator */, &object->driver_surface); if (res != VK_SUCCESS) { free(object); diff --git a/dlls/winevulkan/vulkan_private.h b/dlls/winevulkan/vulkan_private.h index ffae94adbe4..800036d2c2d 100644 --- a/dlls/winevulkan/vulkan_private.h +++ b/dlls/winevulkan/vulkan_private.h @@ -324,7 +324,8 @@ static inline VkDebugReportCallbackEXT wine_debug_report_callback_to_handle( struct wine_surface { VkSurfaceKHR host_surface; - VkSurfaceKHR driver_surface; /* wine driver surface */ + VkSurfaceKHR driver_surface; + HWND hwnd; struct wine_vk_mapping mapping; }; From fccd426a0958ef864de33918d6585f97b14037ea Mon Sep 17 00:00:00 2001 From: Jacek Caban Date: Wed, 21 Feb 2024 14:28:49 +0100 Subject: [PATCH 312/413] winevulkan: Rename wine_device_memory mapping to vm_map. To avoid conflict with handle mapping macros. (cherry picked from commit 84e462070658464576e73c8a836ccab8fb296734) --- dlls/winevulkan/vulkan.c | 12 ++++++------ dlls/winevulkan/vulkan_private.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 818f8e8655c..073ae5faf51 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -3291,7 +3291,7 @@ VkResult wine_vkAllocateMemory(VkDevice handle, const VkMemoryAllocateInfo *allo return result; } - memory->mapping = mapping; + memory->vm_map = mapping; *ret = wine_device_memory_to_handle(memory); return VK_SUCCESS; } @@ -3308,10 +3308,10 @@ void wine_vkFreeMemory(VkDevice handle, VkDeviceMemory memory_handle, const VkAl destroy_keyed_mutex(device, memory); device->funcs.p_vkFreeMemory(device->host_device, memory->host_memory, NULL); - if (memory->mapping) + if (memory->vm_map) { SIZE_T alloc_size = 0; - NtFreeVirtualMemory(GetCurrentProcess(), &memory->mapping, &alloc_size, MEM_RELEASE); + NtFreeVirtualMemory(GetCurrentProcess(), &memory->vm_map, &alloc_size, MEM_RELEASE); } if (memory->handle != INVALID_HANDLE_VALUE) @@ -3343,9 +3343,9 @@ VkResult wine_vkMapMemory2KHR(VkDevice handle, const VkMemoryMapInfoKHR *map_inf VkResult result; info.memory = memory->host_memory; - if (memory->mapping) + if (memory->vm_map) { - *data = (char *)memory->mapping + info.offset; + *data = (char *)memory->vm_map + info.offset; TRACE("returning %p\n", *data); return VK_SUCCESS; } @@ -3391,7 +3391,7 @@ VkResult wine_vkUnmapMemory2KHR(VkDevice handle, const VkMemoryUnmapInfoKHR *unm struct wine_device_memory *memory = wine_device_memory_from_handle(unmap_info->memory); VkMemoryUnmapInfoKHR info; - if (memory->mapping) + if (memory->vm_map) return VK_SUCCESS; if (!device->funcs.p_vkUnmapMemory2KHR) diff --git a/dlls/winevulkan/vulkan_private.h b/dlls/winevulkan/vulkan_private.h index 800036d2c2d..fe579ee7d9a 100644 --- a/dlls/winevulkan/vulkan_private.h +++ b/dlls/winevulkan/vulkan_private.h @@ -269,7 +269,7 @@ struct wine_device_memory BOOL inherit; DWORD access; HANDLE handle; - void *mapping; + void *vm_map; struct keyed_mutex_shm *keyed_mutex_shm; VkSemaphore keyed_mutex_sem; uint64_t keyed_mutex_instance_id; From c211e2a0422153e6a6159701cb6ca5300e680b23 Mon Sep 17 00:00:00 2001 From: Jacek Caban Date: Wed, 21 Feb 2024 14:48:13 +0100 Subject: [PATCH 313/413] winevulkan: Use handle map for memory objects. (cherry picked from commit 64914849ac42cebd6d6f6b88ae8b16d22e493184) --- dlls/winevulkan/vulkan.c | 2 ++ dlls/winevulkan/vulkan_private.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 073ae5faf51..16f1b30c7c6 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -3291,6 +3291,7 @@ VkResult wine_vkAllocateMemory(VkDevice handle, const VkMemoryAllocateInfo *allo return result; } + WINE_VK_ADD_NON_DISPATCHABLE_MAPPING(device->phys_dev->instance, memory, memory->host_memory, memory); memory->vm_map = mapping; *ret = wine_device_memory_to_handle(memory); return VK_SUCCESS; @@ -3306,6 +3307,7 @@ void wine_vkFreeMemory(VkDevice handle, VkDeviceMemory memory_handle, const VkAl memory = wine_device_memory_from_handle(memory_handle); destroy_keyed_mutex(device, memory); + WINE_VK_REMOVE_HANDLE_MAPPING(device->phys_dev->instance, memory); device->funcs.p_vkFreeMemory(device->host_device, memory->host_memory, NULL); if (memory->vm_map) diff --git a/dlls/winevulkan/vulkan_private.h b/dlls/winevulkan/vulkan_private.h index fe579ee7d9a..ecd148ea48a 100644 --- a/dlls/winevulkan/vulkan_private.h +++ b/dlls/winevulkan/vulkan_private.h @@ -273,6 +273,8 @@ struct wine_device_memory struct keyed_mutex_shm *keyed_mutex_shm; VkSemaphore keyed_mutex_sem; uint64_t keyed_mutex_instance_id; + + struct wine_vk_mapping mapping; }; static inline VkDeviceMemory wine_device_memory_to_handle(struct wine_device_memory *device_memory) From 7236bc072e987aa2575124a91e8d3a4de8918c1e Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Wed, 10 Apr 2024 01:19:25 +0300 Subject: [PATCH 314/413] winevulkan: Remove handle mapping after host destory is called. For VK_EXT_debug_utils callbacks on destroy / free. --- dlls/winevulkan/vulkan.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 16f1b30c7c6..aeb4c425df8 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -684,8 +684,8 @@ static void wine_vk_device_free(struct wine_device *device) if (device->host_device && device->funcs.p_vkDestroyDevice) { - WINE_VK_REMOVE_HANDLE_MAPPING(device->phys_dev->instance, device); device->funcs.p_vkDestroyDevice(device->host_device, NULL /* pAllocator */); + WINE_VK_REMOVE_HANDLE_MAPPING(device->phys_dev->instance, device); } free(device); @@ -1419,9 +1419,8 @@ void wine_vkDestroyCommandPool(VkDevice device_handle, VkCommandPool handle, if (allocator) FIXME("Support for allocation callbacks not implemented yet\n"); - WINE_VK_REMOVE_HANDLE_MAPPING(device->phys_dev->instance, pool); - device->funcs.p_vkDestroyCommandPool(device->host_device, pool->host_command_pool, NULL); + WINE_VK_REMOVE_HANDLE_MAPPING(device->phys_dev->instance, pool); free(pool); } @@ -3307,8 +3306,8 @@ void wine_vkFreeMemory(VkDevice handle, VkDeviceMemory memory_handle, const VkAl memory = wine_device_memory_from_handle(memory_handle); destroy_keyed_mutex(device, memory); - WINE_VK_REMOVE_HANDLE_MAPPING(device->phys_dev->instance, memory); device->funcs.p_vkFreeMemory(device->host_device, memory->host_memory, NULL); + WINE_VK_REMOVE_HANDLE_MAPPING(device->phys_dev->instance, memory); if (memory->vm_map) { @@ -3671,9 +3670,9 @@ void wine_vkDestroySwapchainKHR(VkDevice device_handle, VkSwapchainKHR handle, c free(swapchain->fs_hack_images); } - WINE_VK_REMOVE_HANDLE_MAPPING(device->phys_dev->instance, swapchain); device->funcs.p_vkDestroySwapchainKHR(device->host_device, swapchain->host_swapchain, NULL); + WINE_VK_REMOVE_HANDLE_MAPPING(device->phys_dev->instance, swapchain); free(swapchain); } @@ -4813,8 +4812,8 @@ void wine_vkDestroySemaphore(VkDevice device_handle, VkSemaphore semaphore_handl if (semaphore->d3d12_fence_shm) NtUnmapViewOfSection(GetCurrentProcess(), semaphore->d3d12_fence_shm); - WINE_VK_REMOVE_HANDLE_MAPPING(device->phys_dev->instance, semaphore); device->funcs.p_vkDestroySemaphore(device->host_device, semaphore->semaphore, NULL); + WINE_VK_REMOVE_HANDLE_MAPPING(device->phys_dev->instance, semaphore); if (semaphore->fence_timeline_semaphore) device->funcs.p_vkDestroySemaphore(device->host_device, semaphore->fence_timeline_semaphore, NULL); From 6456442108144e19215c7e6fc99630ca0586018b Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Tue, 26 Mar 2024 11:01:56 +0800 Subject: [PATCH 315/413] fshack: winex11.drv: Introduce fs_hack_is_window_rect_fullscreen(). Introduce fs_hack_is_window_rect_fullscreen() and it replaces fs_hack_matches_current_mode() for determining whether fshack should be enabled for a window. The difference is that the new function considers a window with a rectangle larger than the monitor rectangle also fullscreen. This is actually closer to the behavior of NtUserIsWindowRectFullScreen(). Ideally, we should enable fshack for all windows and scale them all when a ChangeDisplaySetting() call is made to change resolutions. But that would be a bigger change and has higher overhead so let's not do that unless necessary. Another reason for this change is to avoid a black screen issue on Nvidia drivers. For example, on Arch Linux with Nvidia driver 550. Call of Juarez (21980) changes the resolution to 1024x768 then changes the game window to (-3, -22, 1030, 793). Because the new window rectangle doesn't match the fake monitor resolution, fshack is disabled for the window and its client window gets resized to the unscaled one. However, the fshack monitor still has the fake 1024x768 so Vulkan swapchains need to draw to a surface with the physical resolution. Because the unscaled client window is smaller than the physical resolution, vkAcquireNextImageKHR() always returns VK_ERROR_OUT_OF_DATE_KHR on Nvidia drivers. Of course, because Nvidia drivers are proprietary so I can't confirm this is the exact cause. CW-Bug-Id: #23466 --- dlls/winex11.drv/fs.c | 14 +++++++------- dlls/winex11.drv/window.c | 12 +++++------- dlls/winex11.drv/x11drv.h | 2 +- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/dlls/winex11.drv/fs.c b/dlls/winex11.drv/fs.c index bda79419c69..73824021060 100644 --- a/dlls/winex11.drv/fs.c +++ b/dlls/winex11.drv/fs.c @@ -720,21 +720,21 @@ RECT fs_hack_real_mode( HMONITOR monitor ) return rect; } -/* Return whether width and height are the same as the current mode used by a monitor */ -BOOL fs_hack_matches_current_mode( HMONITOR monitor, INT width, INT height ) +/* Return whether a window rectangle is fullscreen on a fshack monitor */ +BOOL fs_hack_is_window_rect_fullscreen( HMONITOR monitor, const RECT *rect ) { MONITORINFO info = {.cbSize = sizeof(MONITORINFO)}; - BOOL matched; + BOOL fullscreen; TRACE( "monitor %p\n", monitor ); if (!NtUserGetMonitorInfo( monitor, &info )) return FALSE; - matched = (width == info.rcMonitor.right - info.rcMonitor.left) && - (height == info.rcMonitor.bottom - info.rcMonitor.top); - TRACE( "matched: %s\n", matched ? "TRUE" : "FALSE" ); + fullscreen = rect->left <= info.rcMonitor.left && rect->right >= info.rcMonitor.right && + rect->top <= info.rcMonitor.top && rect->bottom >= info.rcMonitor.bottom; + TRACE( "fullscreen: %s\n", fullscreen ? "TRUE" : "FALSE" ); - return matched; + return fullscreen; } /* Transform a point in user virtual screen coordinates to real virtual screen coordinates */ diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 29773e871b0..3f64774d2cb 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -3135,9 +3135,9 @@ BOOL X11DRV_WindowPosChanging( HWND hwnd, HWND insert_after, UINT swp_flags, if (!data && !(data = X11DRV_create_win_data( hwnd, window_rect, client_rect ))) return TRUE; + monitor = fs_hack_monitor_from_rect( window_rect ); - if (fs_hack_enabled( monitor ) && fs_hack_matches_current_mode( monitor, window_rect->right - window_rect->left, - window_rect->bottom - window_rect->top )) + if (fs_hack_enabled( monitor ) && fs_hack_is_window_rect_fullscreen( monitor, window_rect )) window_update_fshack( data, window_rect, client_rect, monitor, TRUE ); else window_update_fshack( data, window_rect, client_rect, monitor, FALSE ); @@ -3500,7 +3500,7 @@ UINT X11DRV_ShowWindow( HWND hwnd, INT cmd, RECT *rect, UINT swp ) monitor = fs_hack_monitor_from_rect( rect ); if (data->fs_hack || (fs_hack_enabled( monitor ) && - fs_hack_matches_current_mode( monitor, rect->right - rect->left, rect->bottom - rect->top ))) + fs_hack_is_window_rect_fullscreen( monitor, rect ))) { MONITORINFO info = {.cbSize = sizeof(MONITORINFO)}; NtUserGetMonitorInfo( monitor, &info ); @@ -3757,8 +3757,7 @@ static void handle_window_desktop_resize( struct x11drv_win_data *data, UINT old HMONITOR monitor = fs_hack_monitor_from_hwnd( data->hwnd ); if (fs_hack_mapping_required( monitor ) && - fs_hack_matches_current_mode( monitor, data->whole_rect.right - data->whole_rect.left, - data->whole_rect.bottom - data->whole_rect.top )) + fs_hack_is_window_rect_fullscreen( monitor, &data->whole_rect )) { window_update_fshack( data, NULL, NULL, monitor, TRUE ); return; @@ -3783,8 +3782,7 @@ static void handle_window_desktop_resize( struct x11drv_win_data *data, UINT old } if (!fs_hack_mapping_required( monitor ) || - !fs_hack_matches_current_mode( monitor, data->whole_rect.right - data->whole_rect.left, - data->whole_rect.bottom - data->whole_rect.top )) + !fs_hack_is_window_rect_fullscreen( monitor, &data->whole_rect )) window_update_fshack( data, NULL, NULL, monitor, FALSE ); } diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index d60432a68e8..da610e060a6 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -710,7 +710,7 @@ extern BOOL fs_hack_mapping_required( HMONITOR monitor ); extern BOOL fs_hack_is_integer(void); extern HMONITOR fs_hack_monitor_from_hwnd( HWND hwnd ); extern HMONITOR fs_hack_monitor_from_rect( const RECT *rect ); -extern BOOL fs_hack_matches_current_mode( HMONITOR monitor, INT width, INT height ); +extern BOOL fs_hack_is_window_rect_fullscreen( HMONITOR monitor, const RECT *rect ); extern RECT fs_hack_current_mode( HMONITOR monitor ); extern RECT fs_hack_real_mode( HMONITOR monitor ); extern void fs_hack_point_user_to_real( POINT *pos ); From 9dafc27e560da791339320ecf6a3f8dc557dd707 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 10 Apr 2024 20:25:06 -0600 Subject: [PATCH 316/413] win32u: Store desktop flags in shared memory and use that in is_virtual_desktop(). CW-Bug-Id: #23665 --- dlls/win32u/winstation.c | 15 ++++++++++----- server/protocol.def | 1 + server/winstation.c | 36 +++++++++++++++++++++++++++++++++++- 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/dlls/win32u/winstation.c b/dlls/win32u/winstation.c index 2887e104c4a..fbede26d8a8 100644 --- a/dlls/win32u/winstation.c +++ b/dlls/win32u/winstation.c @@ -42,12 +42,17 @@ WINE_DECLARE_DEBUG_CHANNEL(win); BOOL is_virtual_desktop(void) { - HANDLE desktop = NtUserGetThreadDesktop( GetCurrentThreadId() ); - USEROBJECTFLAGS flags = {0}; - DWORD len; + const desktop_shm_t *desktop = get_desktop_shared_memory(); + unsigned int flags; + + if (!desktop) return FALSE; + SHARED_READ_BEGIN( desktop, desktop_shm_t ) + { + flags = desktop->flags; + } + SHARED_READ_END - if (!NtUserGetObjectInformation( desktop, UOI_FLAGS, &flags, sizeof(flags), &len )) return FALSE; - return !!(flags.dwFlags & DF_WINE_CREATE_DESKTOP); + return !!(flags & DF_WINE_CREATE_DESKTOP); } /*********************************************************************** diff --git a/server/protocol.def b/server/protocol.def index 50bc1035359..d8d79e7c6ad 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -927,6 +927,7 @@ struct desktop_shared_memory unsigned char keystate[256]; /* asynchronous key state */ thread_id_t foreground_tid; /* tid of the foreground thread */ __int64 update_serial; + unsigned int flags; }; typedef volatile struct desktop_shared_memory desktop_shm_t; diff --git a/server/winstation.c b/server/winstation.c index ac8ccdc81af..875e87ed6b2 100644 --- a/server/winstation.c +++ b/server/winstation.c @@ -136,6 +136,27 @@ static const struct object_ops desktop_ops = desktop_destroy /* destroy */ }; +#if defined(__i386__) || defined(__x86_64__) +#define __SHARED_INCREMENT_SEQ( x ) ++(x) +#else +#define __SHARED_INCREMENT_SEQ( x ) __atomic_add_fetch( &(x), 1, __ATOMIC_RELEASE ) +#endif + +#define SHARED_WRITE_BEGIN( object, type ) \ + do { \ + const type *__shared = (object)->shared; \ + type *shared = (type *)__shared; \ + unsigned int __seq = __SHARED_INCREMENT_SEQ( shared->seq ); \ + assert( (__seq & 1) != 0 ); \ + do + +#define SHARED_WRITE_END \ + while(0); \ + __seq = __SHARED_INCREMENT_SEQ( shared->seq ) - __seq; \ + assert( __seq == 1 ); \ + } while(0); + + /* create a winstation object */ static struct winstation *create_winstation( struct object *root, const struct unicode_str *name, unsigned int attr, unsigned int flags ) @@ -277,6 +298,11 @@ static struct desktop *create_desktop( const struct unicode_str *name, unsigned desktop->flags |= (flags & DF_WINE_CREATE_DESKTOP); clear_error(); } + SHARED_WRITE_BEGIN( desktop, desktop_shm_t ) + { + shared->flags = desktop->flags; + } + SHARED_WRITE_END } return desktop; } @@ -708,7 +734,15 @@ DECL_HANDLER(set_user_object_info) struct desktop *desktop = (struct desktop *)obj; reply->is_desktop = 1; reply->old_obj_flags = desktop->flags; - if (req->flags & SET_USER_OBJECT_SET_FLAGS) desktop->flags = req->obj_flags; + if (req->flags & SET_USER_OBJECT_SET_FLAGS) + { + desktop->flags = req->obj_flags; + SHARED_WRITE_BEGIN( desktop, desktop_shm_t ) + { + shared->flags = desktop->flags; + } + SHARED_WRITE_END + } if (req->flags & SET_USER_OBJECT_SET_CLOSE_TIMEOUT) desktop->close_timeout_val = req->close_timeout; } else if (obj->ops == &winstation_ops) From 9157418367535a3cfbe2f827eac8ac846349f30a Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 10 Apr 2024 20:25:45 -0600 Subject: [PATCH 317/413] user32: HACK: Avoid triggering displays update in GetDisplayConfigBufferSizes(). To be dropped on next rebase. CW-Bug-Id: #23665 --- dlls/user32/sysparams.c | 7 +++++++ dlls/user32/user32.spec | 2 +- dlls/win32u/sysparams.c | 9 ++++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/dlls/user32/sysparams.c b/dlls/user32/sysparams.c index b941f126a58..e91737cbcc2 100644 --- a/dlls/user32/sysparams.c +++ b/dlls/user32/sysparams.c @@ -999,3 +999,10 @@ LONG WINAPI SetDisplayConfig(UINT32 path_info_count, DISPLAYCONFIG_PATH_INFO *pa return ERROR_SUCCESS; } + +LONG WINAPI GetDisplayConfigBufferSizes( UINT32 flags, UINT32 *num_path_info, + UINT32 *num_mode_info ) +{ + flags |= 0x40000000; /* HACK: avoid triggering display updates in NtUserGetDisplayConfigBufferSizes(). */ + return NtUserGetDisplayConfigBufferSizes(flags, num_path_info, num_mode_info); +} diff --git a/dlls/user32/user32.spec b/dlls/user32/user32.spec index e8d94f86af6..ffe5af2daa0 100644 --- a/dlls/user32/user32.spec +++ b/dlls/user32/user32.spec @@ -292,7 +292,7 @@ @ stdcall GetDesktopWindow() @ stdcall GetDialogBaseUnits() @ stdcall GetDisplayAutoRotationPreferences(ptr) -@ stdcall GetDisplayConfigBufferSizes(long ptr ptr) NtUserGetDisplayConfigBufferSizes +@ stdcall GetDisplayConfigBufferSizes(long ptr ptr) @ stdcall GetDlgCtrlID(long) @ stdcall GetDlgItem(long long) @ stdcall GetDlgItemInt(long long ptr long) diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index d505bde85f3..ac8ddb42af3 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2427,6 +2427,7 @@ LONG WINAPI NtUserGetDisplayConfigBufferSizes( UINT32 flags, UINT32 *num_path_in volatile struct global_shared_memory *global_shared; struct monitor *monitor; UINT32 count = 0; + BOOL skip_update = FALSE; TRACE( "(0x%x %p %p)\n", flags, num_path_info, num_mode_info ); @@ -2435,6 +2436,12 @@ LONG WINAPI NtUserGetDisplayConfigBufferSizes( UINT32 flags, UINT32 *num_path_in *num_path_info = 0; + if (flags & 0x40000000) + { + flags &= ~0x40000000; + skip_update = TRUE; + } + switch (flags) { case QDC_ALL_PATHS: @@ -2450,7 +2457,7 @@ LONG WINAPI NtUserGetDisplayConfigBufferSizes( UINT32 flags, UINT32 *num_path_in FIXME( "only returning active paths\n" ); /* NtUserGetDisplayConfigBufferSizes() is called by display drivers to trigger display settings update. */ - if ((global_shared = get_global_shared_memory())) + if (!skip_update && (global_shared = get_global_shared_memory())) InterlockedIncrement( (LONG *)&global_shared->display_settings_serial ); if (lock_display_devices()) From dce7a2f1b0cc844ab58f418eb7da2198301fa200 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Thu, 11 Apr 2024 20:02:03 +0300 Subject: [PATCH 318/413] winevulkan: Make client's device memory wrapper available to callback converter. With VK_EXT_device_address_binding_report we can get debug_util callbacks used to track memory bindings. Since it's the host's implementation that starts the callback we have to be sure that we have a way of converting it to the client side's variant before it's even added to the handle map - i.e. we don't know the host handle at that time yet. ntuser_thread_info which is thread-local is used. --- dlls/winevulkan/vulkan.c | 27 +++++++++++++++++++++++++++ include/ntuser.h | 1 + 2 files changed, 28 insertions(+) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index aeb4c425df8..c40309ee468 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -49,6 +49,28 @@ WINE_DEFAULT_DEBUG_CHANNEL(vulkan); +static void set_memory_being_allocated(struct wine_instance *instance, struct wine_device_memory *memory) +{ + struct ntuser_thread_info *thread_info; + if (!instance->enable_wrapper_list) return; + thread_info = NtUserGetThreadInfo(); + thread_info->vulkan_data = (uintptr_t) memory; +} + +static uint64_t get_memory_being_allocated(uint64_t host_handle) +{ + struct ntuser_thread_info *thread_info; + struct wine_device_memory *memory; + + thread_info = NtUserGetThreadInfo(); + if (!thread_info->vulkan_data) return 0; + + memory = (struct wine_device_memory*) (uintptr_t) thread_info->vulkan_data; + memory->host_memory = host_handle; + + return (uintptr_t) memory; +} + static int debug_level; static BOOL is_wow64(void) @@ -200,6 +222,8 @@ static VkBool32 debug_utils_callback_conversion(VkDebugUtilsMessageSeverityFlagB if (wine_vk_is_type_wrapped(callback_data->pObjects[i].objectType)) { object_name_infos[i].objectHandle = wine_vk_get_wrapper(object->instance, callback_data->pObjects[i].objectHandle); + if (!object_name_infos[i].objectHandle && object_name_infos[i].objectType == VK_OBJECT_TYPE_DEVICE_MEMORY) + object_name_infos[i].objectHandle = get_memory_being_allocated(callback_data->pObjects[i].objectHandle); if (!object_name_infos[i].objectHandle) { WARN("handle conversion failed 0x%s\n", wine_dbgstr_longlong(callback_data->pObjects[i].objectHandle)); @@ -3251,7 +3275,10 @@ VkResult wine_vkAllocateMemory(VkDevice handle, const VkMemoryAllocateInfo *allo } } + set_memory_being_allocated(device->phys_dev->instance, memory); result = device->funcs.p_vkAllocateMemory(device->host_device, &info, NULL, &memory->host_memory); + set_memory_being_allocated(device->phys_dev->instance, NULL); + if (result == VK_SUCCESS && memory->handle == INVALID_HANDLE_VALUE && export_info && export_info->handleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT) { get_fd_info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR; diff --git a/include/ntuser.h b/include/ntuser.h index 0677f114aea..bf65f214bc2 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -87,6 +87,7 @@ struct ntuser_thread_info UINT default_imc; /* default input context */ UINT64 client_imm; /* client IMM thread info */ UINT64 wmchar_data; /* client data for WM_CHAR mappings */ + UINT64 vulkan_data; /* used by winevulkan for tls */ }; static inline struct ntuser_thread_info *NtUserGetThreadInfo(void) From b94d5f5615d3fe1b5aed8bd04f7ae712bc9e8b52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 12 Apr 2024 11:16:14 +0200 Subject: [PATCH 319/413] HACK: mfplat: Enable new media source by default for more games. CW-Bug-Id: #23618 --- dlls/mfplat/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index facb39ee3d6..3d1fe615a5a 100644 --- a/dlls/mfplat/main.c +++ b/dlls/mfplat/main.c @@ -6304,6 +6304,7 @@ static HRESULT resolver_create_gstreamer_handler(IMFByteStreamHandler **handler) if (!strcmp(sgi, "399810") /* Call of Cthulhu */) env = "1"; if (!strcmp(sgi, "606880") /* Greedfall */) env = "1"; if (!strcmp(sgi, "692850") /* Bloodstained */) env = "1"; + if (!strcmp(sgi, "934700") /* Dead Island 2 */) env = "1"; } if (env && atoi(env)) return CoCreateInstance(&CLSID_GStreamerByteStreamHandler2, NULL, CLSCTX_INPROC_SERVER, &IID_IMFByteStreamHandler, (void **)handler); From 4d5ffeaf74a3a008c14155a28c6d4a643a00346f Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 15 Apr 2024 12:25:00 +0300 Subject: [PATCH 320/413] win32u: Update Nvidia's DriverVersion to match 552.12. --- dlls/win32u/sysparams.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index ac8ddb42af3..2e6e0915091 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -1406,7 +1406,7 @@ static void add_gpu( const struct gdi_gpu *gpu, void *param ) break; /* Nvidia */ case 0x10de: - sprintf( buffer, "31.0.15.3625" ); + sprintf( buffer, "31.0.15.5212" ); break; /* Default value for any other vendor. */ default: From 813d89089058c86daa9d08dee7d956d22c721f29 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Thu, 4 Apr 2024 14:01:52 +0200 Subject: [PATCH 321/413] msvcrt/tests: Add tests for check buffering state of standard streams. Signed-off-by: Eric Pouech (cherry picked from commit 417d25eb72bff16cfbe1867349aa572e83aff161) Link: https://github.com/ValveSoftware/wine/issues/224 --- dlls/msvcrt/tests/file.c | 59 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/dlls/msvcrt/tests/file.c b/dlls/msvcrt/tests/file.c index 75d30648230..70689deea30 100644 --- a/dlls/msvcrt/tests/file.c +++ b/dlls/msvcrt/tests/file.c @@ -3057,6 +3057,64 @@ static void test_ioinfo_flags(void) free(tempf); } +static void test_std_stream_buffering(void) +{ + int dup_fd, ret, pos; + FILE *file; + char ch; + + dup_fd = _dup(STDOUT_FILENO); + ok(dup_fd != -1, "_dup failed\n"); + + file = freopen("std_stream_test.tmp", "w", stdout); + ok(file != NULL, "freopen failed\n"); + + ret = fprintf(stdout, "test"); + pos = _telli64(STDOUT_FILENO); + + fflush(stdout); + _dup2(dup_fd, STDOUT_FILENO); + close(dup_fd); + setvbuf(stdout, NULL, _IONBF, 0); + + ok(ret == 4, "fprintf(stdout) returned %d\n", ret); + ok(!pos, "expected stdout to be buffered\n"); + + dup_fd = _dup(STDERR_FILENO); + ok(dup_fd != -1, "_dup failed\n"); + + file = freopen("std_stream_test.tmp", "w", stderr); + ok(file != NULL, "freopen failed\n"); + + ret = fprintf(stderr, "test"); + ok(ret == 4, "fprintf(stderr) returned %d\n", ret); + pos = _telli64(STDERR_FILENO); + ok(!pos, "expected stderr to be buffered\n"); + + fflush(stderr); + _dup2(dup_fd, STDERR_FILENO); + close(dup_fd); + + dup_fd = _dup(STDIN_FILENO); + ok(dup_fd != -1, "_dup failed\n"); + + file = freopen("std_stream_test.tmp", "r", stdin); + ok(file != NULL, "freopen failed\n"); + + ch = 0; + ret = fscanf(stdin, "%c", &ch); + ok(ret == 1, "fscanf returned %d\n", ret); + ok(ch == 't', "ch = 0x%x\n", (unsigned char)ch); + pos = _telli64(STDIN_FILENO); + ok(pos == 4, "pos = %d\n", pos); + + fflush(stdin); + _dup2(dup_fd, STDIN_FILENO); + close(dup_fd); + + ok(DeleteFileA("std_stream_test.tmp"), "DeleteFile failed\n"); +} + START_TEST(file) { int arg_c; @@ -3133,6 +3191,7 @@ START_TEST(file) test_fopen_hints(); test_open_hints(); test_ioinfo_flags(); + test_std_stream_buffering(); /* Wait for the (_P_NOWAIT) spawned processes to finish to make sure the report * file contains lines in the correct order From ffc895da50a50f565ec88f063de4f56672b5574a Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Thu, 4 Apr 2024 14:02:30 +0200 Subject: [PATCH 322/413] ucrtbase/tests: Add tests for checking buffering state of standard streams. Signed-off-by: Eric Pouech (cherry picked from commit d9b3ad05a7d2d0618bcfb37b0993242a3e399c84) Link: https://github.com/ValveSoftware/wine/issues/224 --- dlls/ucrtbase/tests/Makefile.in | 1 + dlls/ucrtbase/tests/file.c | 88 +++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 dlls/ucrtbase/tests/file.c diff --git a/dlls/ucrtbase/tests/Makefile.in b/dlls/ucrtbase/tests/Makefile.in index cd01a3aacb9..ea9e589113c 100644 --- a/dlls/ucrtbase/tests/Makefile.in +++ b/dlls/ucrtbase/tests/Makefile.in @@ -5,6 +5,7 @@ EXTRADEFS = -fno-builtin SOURCES = \ cpp.c \ environ.c \ + file.c \ misc.c \ printf.c \ scanf.c \ diff --git a/dlls/ucrtbase/tests/file.c b/dlls/ucrtbase/tests/file.c new file mode 100644 index 00000000000..4671e641dbe --- /dev/null +++ b/dlls/ucrtbase/tests/file.c @@ -0,0 +1,88 @@ +/* + * Unit test suite for file functions + * + * Copyright 2024 Eric Pouech 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 + */ + +#include "wine/test.h" + +static void test_std_stream_buffering(void) +{ + int dup_fd, ret, pos; + FILE *file; + char ch; + + dup_fd = _dup(STDOUT_FILENO); + ok(dup_fd != -1, "_dup failed\n"); + + file = freopen("std_stream_test.tmp", "w", stdout); + ok(file != NULL, "freopen failed\n"); + + ret = fprintf(stdout, "test"); + pos = _telli64(STDOUT_FILENO); + + fflush(stdout); + _dup2(dup_fd, STDOUT_FILENO); + close(dup_fd); + setvbuf(stdout, NULL, _IONBF, 0); + + ok(ret == 4, "fprintf(stdout) returned %d\n", ret); + ok(!pos, "expected stdout to be buffered\n"); + + dup_fd = _dup(STDERR_FILENO); + ok(dup_fd != -1, "_dup failed\n"); + + file = freopen("std_stream_test.tmp", "w", stderr); + ok(file != NULL, "freopen failed\n"); + + ret = fprintf(stderr, "test"); + ok(ret == 4, "fprintf(stderr) returned %d\n", ret); + pos = _telli64(STDERR_FILENO); + if (broken(!GetProcAddress(GetModuleHandleA("ucrtbase"), "__CxxFrameHandler4") && !pos)) + trace("stderr is buffered\n"); + else + todo_wine + ok(pos == 4, "expected stderr to be unbuffered (%d)\n", pos); + + fflush(stderr); + _dup2(dup_fd, STDERR_FILENO); + close(dup_fd); + + dup_fd = _dup(STDIN_FILENO); + ok(dup_fd != -1, "_dup failed\n"); + + file = freopen("std_stream_test.tmp", "r", stdin); + ok(file != NULL, "freopen failed\n"); + + ch = 0; + ret = fscanf(stdin, "%c", &ch); + ok(ret == 1, "fscanf returned %d\n", ret); + ok(ch == 't', "ch = 0x%x\n", (unsigned char)ch); + pos = _telli64(STDIN_FILENO); + ok(pos == 4, "pos = %d\n", pos); + + fflush(stdin); + _dup2(dup_fd, STDIN_FILENO); + close(dup_fd); + + ok(DeleteFileA("std_stream_test.tmp"), "DeleteFile failed\n"); +} + +START_TEST(file) +{ + test_std_stream_buffering(); +} From b88b982ada53b5aca0568c238f6d4908f592e2c7 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Wed, 3 Apr 2024 17:26:55 +0200 Subject: [PATCH 323/413] ucrtbase: Let stderr be always be unbuffered. Signed-off-by: Eric Pouech (cherry picked from commit 8825f4dfa5e72834577aef58810b967c53965f93) Link: https://github.com/ValveSoftware/wine/issues/224 --- dlls/msvcrt/file.c | 6 ++++++ dlls/ucrtbase/tests/file.c | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/dlls/msvcrt/file.c b/dlls/msvcrt/file.c index 3de12d1087f..4fa56384cd5 100644 --- a/dlls/msvcrt/file.c +++ b/dlls/msvcrt/file.c @@ -845,9 +845,15 @@ int CDECL _isatty(int fd) /* INTERNAL: Allocate stdio file buffer */ static BOOL msvcrt_alloc_buffer(FILE* file) { +#if _MSVCR_VER >= 140 + if((file->_file==STDOUT_FILENO && _isatty(file->_file)) + || file->_file == STDERR_FILENO) + return FALSE; +#else if((file->_file==STDOUT_FILENO || file->_file==STDERR_FILENO) && _isatty(file->_file)) return FALSE; +#endif file->_base = calloc(1, MSVCRT_INTERNAL_BUFSIZ); if(file->_base) { diff --git a/dlls/ucrtbase/tests/file.c b/dlls/ucrtbase/tests/file.c index 4671e641dbe..76233be3fa7 100644 --- a/dlls/ucrtbase/tests/file.c +++ b/dlls/ucrtbase/tests/file.c @@ -55,7 +55,6 @@ static void test_std_stream_buffering(void) if (broken(!GetProcAddress(GetModuleHandleA("ucrtbase"), "__CxxFrameHandler4") && !pos)) trace("stderr is buffered\n"); else - todo_wine ok(pos == 4, "expected stderr to be unbuffered (%d)\n", pos); fflush(stderr); From b519d8d67876c6058aa958b1da2d0ceff40bfa71 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Wed, 17 Apr 2024 16:20:01 +0800 Subject: [PATCH 324/413] light.msstyles: Use #fefefe instead of #ffffff for scrollbar parts. Imperium: Greek Wars (1183470) launcher sets a hardcoded #ffffff as the color key for transparency. It works on Windows 10 because the scrollbar parts only have very few #ffffff pixels so the scrollbar can be drawn as opaque. I don't want to change the light theme style for this. So let's work around this by using #fefefe instead of #ffffff. This have little difference visually. Spotted by Santino Mazza. --- dlls/light.msstyles/blue_scrollbar_arrows.bmp | Bin 17818 -> 17818 bytes dlls/light.msstyles/blue_scrollbar_arrows.svg | 12 ++++++------ .../blue_scrollbar_thumb_horizontal.bmp | Bin 5238 -> 5238 bytes .../blue_scrollbar_thumb_horizontal.svg | 4 ++-- .../blue_scrollbar_thumb_vertical.bmp | Bin 2998 -> 2998 bytes .../blue_scrollbar_thumb_vertical.svg | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/dlls/light.msstyles/blue_scrollbar_arrows.bmp b/dlls/light.msstyles/blue_scrollbar_arrows.bmp index b877407fbf4de57414a1aef4ada4907fc0e9089f..b39c6cb91c424c59875ea67a4cee9f71d998f91e 100644 GIT binary patch literal 17818 zcmeHOO>Wvi6n>FoZAxD5+(k-xW zU{|6$^+f zK<1D4Ve7~jKdocn4L-yivAu@(Dq@b$IX-_D&!4=wOOY}6G&J0@QO3>Qu{O#Ux4ga3 z;nXH}tJ~q!aLbz$Vvd+2=0HXbw;Yehold9OY+kt**uZi!nFxM*y`EBPHO{QPu<3TY zuormyI4pc&346gAS;ulWVNV~9fi`iFCEnWYcE8{E2~*er0Vb}2AGo167ksW@154P8 z9u1<}K~&w2PB$GZY|an$!%(8yepK0VSSN>r<@upR6^|YgA2h1l;@gAeMxWD7j)572 zwDDF!yq$fHDsOzFuDS&ri2n22xM9PJ!qsiqobBn+y&w-sZkdo;4YXty~!4EN)?V9p_Q0#FlGUiY(F!ebf_6r--=SG9LKIc=ASX3-b z9pfN5p9>${--{-=Ud;d?Z@>w>bd+h2K8KiV@*AmX)X@=Te10D zk*`mYxh3@+<2q}ef|yG=-*TBlp`N2=T1(wXJ;!%>_%09M<;nOi59@QBqiLo97oCq5 HSx(2S$7ra3A%0wq*0F$5po1rCfx%2 z26iP%1QIsG^o@57c=z4;8%dUL`2LE|3w%Coh-}Eq zWdHNU|H;PkON*qd*`s&gzW=iP%d_$9AG`U(AZ~q*>ZPdm+OgJ4@%8ciaVT-?NZnp& z6=~<9B6V|)4g@`TIiyFNO`}Fxa6ZlsYy;TShhw0VILI1r^^(L_hw9f)uP{Yi-(DaO zWd3j$w+?;tr*(vUgAa3#xxI$>D&`!YbA0}!&!4=oOObQ#acHn*ql}xqV{MczYb4Ys^KX3jC^m~#N523wBD<4&j3Y&I`l3v6IHnM?#fyyu5#3Y)V7eLoak`=l#d4(sLNV0m^Ry5iBp(g%&|w)pm7xzXojlVf1U zAZxr;5O1d+b>)?B)K#~T2crM{Hg4FkqHuK^Hm7@fbSJ1o5?kiQG1yOjb>&QaPDQ9Z zF6)-D8&av*s#{*6Zn@(iW?*jh9aK1pEp>uJh+)?clNT9VlFzMQyzw4FK1V*6V14qe zJh|qR^EoiT1)uw#mCsQV&yQn}&*eAPnfM&$Jz0FtZ+=zyJd~{1($qRLX$6a#Y6!k2 zMk4M-3eH+5I(dWqL^5YofbNP)m`CQOk9OAcP - + - + @@ -21,24 +21,24 @@ - + - + - + - + diff --git a/dlls/light.msstyles/blue_scrollbar_thumb_horizontal.bmp b/dlls/light.msstyles/blue_scrollbar_thumb_horizontal.bmp index 50a98885b3f1efacd5f89fc2d3de6158f38bb133..34ef504c929ef71d7d45970fa438f3eb391727aa 100644 GIT binary patch literal 5238 zcmeHJyH3L}6b%x800Y0kQdr6dbYWsaV&|m-GZGR~_vm8cD$fI^rlL%UJtb|+LAn7MBq$t>MwHT`m}GGvg9KMp65>=K!L4RzmO=1{GR!PTYqK#aLMj+5 zuw`jK_xt_vc&sD`GUgGaA;`<+FAIF`oN=?pZ?|PTknhh~FyYQA>sgGb{P%(U+{P8v zgy^$)#_x7upGB@)j^hEBbBexdZBb;hcok!1*2?&qL?ZRN$Lz&clS3Z$ERld;@4S1W zoWCFpd|mTrt(%MJnm;!zxnLDzrEC6NDl0XH&*fU Hf7Sdy260vi literal 5238 zcmeHJJ5Iwu5M3m000mc|0UC3FG*mQ5^!yZ{MnXag7vKQkL{5%?jOhU>DG7Dblh7a{ z1-Xcw_3)fQD6(T^*DffstMw+Hyyw|{o~KM{_i|NC#tqgAuK0Ez*OFMnD#Yi?*?@@&FhVCU!a={Ufka7Ha6KMEAyYWm445k8^{GL13Zv?f`R1=I>< znY*5w&1SdT)uIE;K7tKF&*xtn^r(JJl?}RDSDgSiPH51l`Vl=>X{__V5Af67Z$>R5 z*I78C>& - - + + diff --git a/dlls/light.msstyles/blue_scrollbar_thumb_vertical.bmp b/dlls/light.msstyles/blue_scrollbar_thumb_vertical.bmp index 95fdd8d150cc0f6d62b5797749abf9c4c12d7b18..e9d05452c94ec25370751b0988eed2e787d6bf44 100644 GIT binary patch literal 2998 zcmeH|zfQw25XKLQjfss{KthEjuyPiV69@1?gA1#;Y69cE?NX%IGa`C<~WY;`zqo7%MbDj zGkD9BK^M$a2O!8}}va6r<$5ItQmg?k6gOA8JN9+V7HUBn}KzfU;N zBAH~agD;-VvT_xbvSyVvYsGrH H&Nb@?F22>` literal 2998 zcmeHIu};G<5IrO|CN_Sd5-Kc#y$cfyUjPNQFf&yOKfnh-S|$C2Dg=oUkdl!yCp#${ zk%}@jsdKzERjE)>oK98LlkGV6dp_&l$yYmm+MFb4+!_kbubSAzI>dbanlrv9~$DlD-~|&)7POVB||^98nSs{HIX+IhN0s)rfK%_ z3>YARCwyGj6++Alw{Q*&kiZkzGKB0DAb}^`XyK{!blGA|cUT`oVHtPJ+=CQ=P1I+V198l^(hLGh&JmQ-@O7Bz1 zT9xu~ipEW?66>e@+K~!3B!QAfo%rj6^)uN#Kmr6l-l}!YTIt%ju377vwPZbA<(l;a DxHR4e diff --git a/dlls/light.msstyles/blue_scrollbar_thumb_vertical.svg b/dlls/light.msstyles/blue_scrollbar_thumb_vertical.svg index d42f84082a2..61f0bde183c 100644 --- a/dlls/light.msstyles/blue_scrollbar_thumb_vertical.svg +++ b/dlls/light.msstyles/blue_scrollbar_thumb_vertical.svg @@ -8,8 +8,8 @@ - - + + From 49a6a94a3ed6616939f3beba073395c9b2eb51dd Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Thu, 18 Apr 2024 16:22:57 +0300 Subject: [PATCH 325/413] mscoree: Update Wine Mono to 9.1.0. --- dlls/appwiz.cpl/addons.c | 4 ++-- dlls/mscoree/mscoree_private.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/appwiz.cpl/addons.c b/dlls/appwiz.cpl/addons.c index 12d32a46216..5c44dcab15f 100644 --- a/dlls/appwiz.cpl/addons.c +++ b/dlls/appwiz.cpl/addons.c @@ -56,10 +56,10 @@ WINE_DEFAULT_DEBUG_CHANNEL(appwizcpl); #define GECKO_SHA "???" #endif -#define MONO_VERSION "9.0.0" +#define MONO_VERSION "9.1.0" #if defined(__i386__) || defined(__x86_64__) #define MONO_ARCH "x86" -#define MONO_SHA "79f6c43100675566c112f4199b9ea7b944d338164b34bd91fa11b0b0a414e1c4" +#define MONO_SHA "8a0a1e6837b494df49927e5d759b1c6908e89b8a2f8e3ad025e1c2881882476e" #else #define MONO_ARCH "" #define MONO_SHA "???" diff --git a/dlls/mscoree/mscoree_private.h b/dlls/mscoree/mscoree_private.h index f27ff95be2d..9f73522c6c4 100644 --- a/dlls/mscoree/mscoree_private.h +++ b/dlls/mscoree/mscoree_private.h @@ -45,7 +45,7 @@ extern HRESULT assembly_get_runtime_version(ASSEMBLY *assembly, LPSTR *version); extern HRESULT assembly_get_vtable_fixups(ASSEMBLY *assembly, VTableFixup **fixups, DWORD *count); extern HRESULT assembly_get_native_entrypoint(ASSEMBLY *assembly, NativeEntryPointFunc *func); -#define WINE_MONO_VERSION "9.0.0" +#define WINE_MONO_VERSION "9.1.0" /* Mono embedding */ typedef struct _MonoDomain MonoDomain; From 433fec0a2782ff1105ab85ca04b2d95aef1e7c74 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 3 Apr 2024 20:07:38 -0600 Subject: [PATCH 326/413] winegstreamer: Destroy wg_transform in video_decoder/transform_SetInputType(). CW-Bug-Id: #23621 --- dlls/mf/tests/h264data.bin | Bin 3465 -> 8659 bytes dlls/mf/tests/resource.rc | 2 +- dlls/mf/tests/transform.c | 51 ++++++++++++++++++++++++++++- dlls/winegstreamer/video_decoder.c | 5 +++ 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/dlls/mf/tests/h264data.bin b/dlls/mf/tests/h264data.bin index a97ed63f2056e7f7b668bb96251c0a50690f1893..f552f277928e7b01e79a42cd29b1662c36a6a9f2 100644 GIT binary patch delta 436 zcmeB_zU(|9lH1V0Lc!QD)zC7{a$<@uqw&T`Wjvc5SWmD62`P5vRh}yxb7uO8_(=T! zcWr5a+5-j#21d>RAi+5AWR_z+1MkfVMr^4aO$==NCmS-`*2g$y8~sYZrXHlbw9)v; zv)93s8|U}E=`?#^Ju9Q1owNPOiyLRetyX!J{O|~9bILf%+7kG1o~)M$_s;AJ#=v!w z{quD6W#{opy4GH{%3J(qsXW8uf}Dv9Um8r?nQnUI!*g@db?yr*r!Ka;aOd)*gYTY* ziR?>yAS};#DOO<;&~207Fw2WK%QLX+A~`g%1nkhsyVw*NGbSr?igC8N3hGGwXJFTv z{F|+e@#f@PEP9MOlNYkvO|IkQp8Sfvf$_%VYz`?f?+2?iP{kS!oyqsvxhH$GNekaZ zGTR;;2sb7RaykH2?P3F~;+ogyDvI9%nHyk341gi&IC~EylsWW)f!wpf_u%Artg6Vi J%>NAuY5)qAo7DgS delta 72 zcmccY+$lXFlH0)2R3RxbHPytxU}B0cqtV7mWjt)g|NmWE8Zh}DyDMY)8Y-w bIuidG*mW2f7#KMNfCS_GZCQ@>47@i1bY&KK diff --git a/dlls/mf/tests/resource.rc b/dlls/mf/tests/resource.rc index d9595c68980..ab1fb7ecbb0 100644 --- a/dlls/mf/tests/resource.rc +++ b/dlls/mf/tests/resource.rc @@ -55,7 +55,7 @@ mp3encdata.bin RCDATA mp3encdata.bin mp3decdata.bin RCDATA mp3decdata.bin /* Generated with: - * gst-launch-1.0 videotestsrc num-buffers=120 pattern=smpte100 ! \ + * gst-launch-1.0 videotestsrc num-buffers=360 pattern=smpte100 ! \ * video/x-raw,format=I420,width=84,height=82,framerate=30000/1001 ! \ * videoflip method=clockwise ! videoconvert ! \ * x264enc ! filesink location=dlls/mf/tests/h264data.bin diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index 468239b0fd4..f11f4368cfb 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -4368,7 +4368,7 @@ static void test_h264_decoder(void) ok(hr == S_OK, "ProcessMessage returned %#lx\n", hr); } ok(i == 2, "got %lu iterations\n", i); - ok(h264_encoded_data_len == 2425, "got h264_encoded_data_len %lu\n", h264_encoded_data_len); + ok(h264_encoded_data_len == 7619, "got h264_encoded_data_len %lu\n", h264_encoded_data_len); ok(hr == MF_E_TRANSFORM_STREAM_CHANGE, "ProcessOutput returned %#lx\n", hr); ok(output_status == MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE, "got output[0].dwStatus %#lx\n", output_status); hr = IMFSample_GetTotalLength(output_sample, &length); @@ -8358,6 +8358,7 @@ static void test_h264_with_dxgi_manager(void) .attributes = output_sample_attributes, .sample_time = 333667, .sample_duration = 333667, .buffer_count = 1, .buffers = &output_buffer_desc_nv12, + .todo_time = TRUE, /* _SetInputType() / _SetOutputType() type resets the output sample timestamp on Windows. */ }; IMFDXGIDeviceManager *manager = NULL; @@ -8512,6 +8513,54 @@ static void test_h264_with_dxgi_manager(void) ok(info.dwFlags == (MFT_OUTPUT_STREAM_WHOLE_SAMPLES | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE | MFT_OUTPUT_STREAM_PROVIDES_SAMPLES), "got %#lx.\n", info.dwFlags); + hr = get_next_h264_output_sample(transform, &input_sample, NULL, output, &data, &data_len); + ok(hr == S_OK, "got %#lx\n", hr); + ok(output[0].dwStatus == 0, "got %#lx.\n", status); + sample = output[0].pSample; + IMFSample_Release(sample); + + /* Set input and output type trying to change output frame size (results in MF_E_TRANSFORM_STREAM_CHANGE with + * frame size reset. */ + hr = MFCreateMediaType(&type); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IMFMediaType_SetGUID(type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IMFMediaType_SetGUID(type, &MF_MT_SUBTYPE, &MFVideoFormat_H264); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IMFMediaType_SetUINT64(type, &MF_MT_FRAME_SIZE, 1088 | (1920ull << 32)); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IMFTransform_SetInputType(transform, 0, type, 0); + ok(hr == S_OK, "got %#lx\n", hr); + IMFMediaType_Release(type); + hr = IMFTransform_GetOutputAvailableType(transform, 0, 0, &type); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IMFMediaType_SetGUID(type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IMFMediaType_SetGUID(type, &MF_MT_SUBTYPE, &MFVideoFormat_NV12); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IMFMediaType_SetUINT64(type, &MF_MT_FRAME_SIZE, ((UINT64)1920) << 32 | 1088); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IMFTransform_SetOutputType(transform, 0, type, 0); + ok(hr == S_OK, "got %#lx\n", hr); + IMFMediaType_Release(type); + + hr = get_next_h264_output_sample(transform, &input_sample, NULL, output, &data, &data_len); + ok(hr == MF_E_TRANSFORM_STREAM_CHANGE, "got %#lx\n", hr); + ok(!output[0].pSample, "got %p.\n", output[0].pSample); + + /* Need to set output type with matching size now, or that hangs on Windows. */ + hr = IMFTransform_GetOutputAvailableType(transform, 0, 0, &type); + ok(hr == S_OK, "got %#lx\n", hr); + IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size); + ok(hr == S_OK, "got %#lx\n", hr); + width = frame_size >> 32; + height = frame_size & 0xffffffff; + ok(width == aligned_width, "got %u.\n", width); + ok(height == aligned_height, "got %u.\n", height); + hr = IMFTransform_SetOutputType(transform, 0, type, 0); + ok(hr == S_OK, "got %#lx\n", hr); + IMFMediaType_Release(type); + hr = get_next_h264_output_sample(transform, &input_sample, NULL, output, &data, &data_len); ok(hr == S_OK, "got %#lx\n", hr); ok(output[0].dwStatus == 0, "got %#lx.\n", status); diff --git a/dlls/winegstreamer/video_decoder.c b/dlls/winegstreamer/video_decoder.c index 44e5c4821f2..4b80fb22262 100644 --- a/dlls/winegstreamer/video_decoder.c +++ b/dlls/winegstreamer/video_decoder.c @@ -448,6 +448,11 @@ static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFM MFCalculateImageSize(decoder->output_types[0], frame_size >> 32, frame_size, (UINT32 *)&decoder->output_info.cbSize); } + if (decoder->wg_transform) + { + wg_transform_destroy(decoder->wg_transform); + decoder->wg_transform = 0; + } return S_OK; } From a099e7b8fb5b54016bf26b5f3d08efa413882246 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 18 Apr 2024 11:29:06 -0600 Subject: [PATCH 327/413] Revert "winegstreamer: HACK: Disable MF_SA_D3D11_AWARE for The Finals." This reverts commit 557bda3ae56c8a849e3b48ec30681f5c47a70ee6. CW-Bug-Id: #23621 --- dlls/winegstreamer/video_decoder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winegstreamer/video_decoder.c b/dlls/winegstreamer/video_decoder.c index 4b80fb22262..ad48e91df35 100644 --- a/dlls/winegstreamer/video_decoder.c +++ b/dlls/winegstreamer/video_decoder.c @@ -835,7 +835,7 @@ static HRESULT video_decoder_create_with_types(const GUID *const *input_types, U { const char *sgi; - if ((sgi = getenv("SteamGameId")) && ((!strcmp(sgi, "2073850")) || (!strcmp(sgi, "2009100")) || (!strcmp(sgi, "2555360")))) + if ((sgi = getenv("SteamGameId")) && ((!strcmp(sgi, "2009100")) || (!strcmp(sgi, "2555360")))) IMFAttributes_SetUINT32(decoder->attributes, &MF_SA_D3D11_AWARE, FALSE); } From 79510d4d80f3bf09bb8f64af25413ae6a144ff8a Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 25 Apr 2024 14:38:39 -0600 Subject: [PATCH 328/413] winex11.drv: Support _SHIFT_ARB attributes in X11DRV_wglGetPixelFormatAttribivARB(). CW-Bug-Id: #23713 --- dlls/opengl32/tests/opengl.c | 8 +++++++- dlls/winex11.drv/opengl.c | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/dlls/opengl32/tests/opengl.c b/dlls/opengl32/tests/opengl.c index 74aff6b4bbd..c2b4a644afa 100644 --- a/dlls/opengl32/tests/opengl.c +++ b/dlls/opengl32/tests/opengl.c @@ -725,7 +725,8 @@ static void test_makecurrent(HDC winhdc) static void test_colorbits(HDC hdc) { const int iAttribList[] = { WGL_COLOR_BITS_ARB, WGL_RED_BITS_ARB, WGL_GREEN_BITS_ARB, - WGL_BLUE_BITS_ARB, WGL_ALPHA_BITS_ARB }; + WGL_BLUE_BITS_ARB, WGL_ALPHA_BITS_ARB, WGL_BLUE_SHIFT_ARB, WGL_GREEN_SHIFT_ARB, + WGL_RED_SHIFT_ARB, WGL_ALPHA_SHIFT_ARB, }; int iAttribRet[ARRAY_SIZE(iAttribList)]; const int iAttribs[] = { WGL_ALPHA_BITS_ARB, 1, 0 }; unsigned int nFormats; @@ -753,6 +754,11 @@ static void test_colorbits(HDC hdc) skip("wglGetPixelFormatAttribivARB failed\n"); return; } + ok(!iAttribRet[5], "got %d.\n", iAttribRet[5]); + ok(iAttribRet[6] == iAttribRet[3], "got %d.\n", iAttribRet[6]); + ok(iAttribRet[7] == iAttribRet[6] + iAttribRet[2], "got %d.\n", iAttribRet[7]); + ok(iAttribRet[8] == iAttribRet[7] + iAttribRet[1], "got %d.\n", iAttribRet[8]); + iAttribRet[1] += iAttribRet[2]+iAttribRet[3]+iAttribRet[4]; ok(iAttribRet[0] == iAttribRet[1], "WGL_COLOR_BITS_ARB (%d) does not equal R+G+B+A (%d)!\n", iAttribRet[0], iAttribRet[1]); diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index 23eb26420fa..2de1a26f537 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -4238,6 +4238,7 @@ static BOOL X11DRV_wglGetPixelFormatAttribivARB( HDC hdc, int iPixelFormat, int int hTest; int tmp; int curGLXAttr = 0; + PIXELFORMATDESCRIPTOR pfd; TRACE("(%p, %d, %d, %d, %p, %p)\n", hdc, iPixelFormat, iLayerPlane, nAttributes, piAttributes, piValues); @@ -4254,6 +4255,12 @@ static BOOL X11DRV_wglGetPixelFormatAttribivARB( HDC hdc, int iPixelFormat, int WARN("Unable to convert iPixelFormat %d to a GLX one!\n", iPixelFormat); } + if (!describe_pixel_format(iPixelFormat, &pfd, TRUE)) + { + WARN("describe_pixel_format failed.\n"); + memset(&pfd, 0, sizeof(pfd)); + } + for (i = 0; i < nAttributes; ++i) { const int curWGLAttr = piAttributes[i]; TRACE("pAttr[%d] = %x\n", i, curWGLAttr); @@ -4355,6 +4362,23 @@ static BOOL X11DRV_wglGetPixelFormatAttribivARB( HDC hdc, int iPixelFormat, int curGLXAttr = GLX_AUX_BUFFERS; break; + case WGL_RED_SHIFT_ARB: + if (!pfd.nSize) goto pix_error; + piValues[i] = pfd.cRedShift; + continue; + case WGL_GREEN_SHIFT_ARB: + if (!pfd.nSize) goto pix_error; + piValues[i] = pfd.cGreenShift; + continue; + case WGL_BLUE_SHIFT_ARB: + if (!pfd.nSize) goto pix_error; + piValues[i] = pfd.cBlueShift; + continue; + case WGL_ALPHA_SHIFT_ARB: + if (!pfd.nSize) goto pix_error; + piValues[i] = pfd.cAlphaShift; + continue; + case WGL_SUPPORT_GDI_ARB: if (!fmt) goto pix_error; piValues[i] = (fmt->dwFlags & PFD_SUPPORT_GDI) != 0; From f819412131a12413d3e3d3aa0a96c8ac203e960b Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 24 Apr 2024 17:21:59 -0600 Subject: [PATCH 329/413] crypt32: Mind constructor tag in CRYPT_AsnDecodeOCSPSignatureInfoCertEncoded(). (cherry picked from commit 7300b40b47a226b38715952e664d1ba8a01efac4) CW-Bug-Id: #23715 --- dlls/crypt32/decode.c | 17 ++++- dlls/crypt32/tests/encode.c | 122 ++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 2 deletions(-) diff --git a/dlls/crypt32/decode.c b/dlls/crypt32/decode.c index 6d683974f44..cfdeef5380a 100644 --- a/dlls/crypt32/decode.c +++ b/dlls/crypt32/decode.c @@ -6254,13 +6254,26 @@ static BOOL CRYPT_AsnDecodeOCSPSignatureInfoCertEncoded(const BYTE *pbEncoded, DWORD *pcbDecoded) { BOOL ret; - struct AsnArrayDescriptor arrayDesc = { 0, + DWORD data_len, len_bytes; + struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCE, offsetof(OCSP_SIGNATURE_INFO, cCertEncoded), offsetof(OCSP_SIGNATURE_INFO, rgCertEncoded), FINALMEMBERSIZE(OCSP_SIGNATURE_INFO, cCertEncoded), verify_and_copy_certificate, sizeof(CRYPT_DER_BLOB), TRUE, offsetof(CRYPT_DER_BLOB, pbData) }; - ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded, + if (pbEncoded[0] != (ASN_CONTEXT | ASN_CONSTRUCTOR)) + { + WARN("Unexpected tag %#x.\n", pbEncoded[0]); + SetLastError(CRYPT_E_ASN1_BADTAG); + return FALSE; + } + + if (!(ret = CRYPT_GetLen(pbEncoded, cbEncoded, &data_len))) return FALSE; + len_bytes = GET_LEN_BYTES(pbEncoded[1]); + + ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded + 1 + len_bytes, cbEncoded - 1 - len_bytes, dwFlags, NULL, pvStructInfo, pcbStructInfo, pcbDecoded); + if (pcbDecoded) + *pcbDecoded = 1 + len_bytes + data_len; return ret; } diff --git a/dlls/crypt32/tests/encode.c b/dlls/crypt32/tests/encode.c index da3a9094d8c..b5db9cf7d0d 100644 --- a/dlls/crypt32/tests/encode.c +++ b/dlls/crypt32/tests/encode.c @@ -8728,6 +8728,122 @@ static const BYTE ocsp_signature[] = { 0xe8, 0x67, 0xcf, 0xa7 }; +static const BYTE ocsp_basic_signed_response_with_cert[] = +{ + 0x30, 0x82, 0x05, 0x32, 0x30, 0x81, 0xdf, 0xa1, 0x54, 0x30, 0x52, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x4d, 0x58, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, 0x44, + 0x46, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x02, + 0x43, 0x57, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, + 0x04, 0x74, 0x65, 0x73, 0x74, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x0b, 0x74, 0x65, + 0x73, 0x74, 0x40, 0x71, 0x71, 0x2e, 0x63, 0x6f, 0x6d, 0x18, 0x0f, 0x32, + 0x30, 0x32, 0x34, 0x30, 0x34, 0x32, 0x35, 0x30, 0x30, 0x30, 0x36, 0x31, + 0x31, 0x5a, 0x30, 0x51, 0x30, 0x4f, 0x30, 0x3a, 0x30, 0x09, 0x06, 0x05, + 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 0x1b, 0xe8, 0x99, + 0x10, 0xe7, 0x3d, 0x9c, 0x6b, 0xba, 0x65, 0xb8, 0x6e, 0x6f, 0xd1, 0x63, + 0x52, 0xa5, 0x6f, 0xd9, 0x81, 0x04, 0x14, 0xc4, 0x57, 0x2a, 0x53, 0xb7, + 0x21, 0x6d, 0x03, 0x2d, 0xd0, 0xbc, 0xd4, 0x2a, 0x88, 0xd2, 0xae, 0x62, + 0xa9, 0x97, 0x2a, 0x02, 0x01, 0x02, 0x80, 0x00, 0x18, 0x0f, 0x32, 0x30, + 0x32, 0x34, 0x30, 0x34, 0x32, 0x35, 0x30, 0x30, 0x30, 0x36, 0x31, 0x31, + 0x5a, 0xa1, 0x23, 0x30, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x30, 0x01, 0x02, 0x04, 0x12, 0x04, 0x10, 0xab, 0x7a, + 0x07, 0x8a, 0xef, 0xc9, 0x7e, 0xb3, 0x51, 0x90, 0xa5, 0x72, 0x09, 0x60, + 0x84, 0x27, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xb5, 0x61, + 0x78, 0x5b, 0xc4, 0xb5, 0xe4, 0x8d, 0xff, 0xe1, 0xc5, 0x95, 0xd1, 0xad, + 0xb8, 0x55, 0xb4, 0xca, 0xcc, 0xf5, 0xe9, 0x6f, 0x63, 0x64, 0x4b, 0xf7, + 0x46, 0xf9, 0x12, 0x02, 0x36, 0xe1, 0x9f, 0xce, 0xe0, 0x5e, 0x6c, 0xf7, + 0x35, 0x19, 0x80, 0x89, 0x6b, 0x7d, 0x8f, 0xa1, 0x8c, 0xb1, 0x7d, 0xdc, + 0xf1, 0x1c, 0xf7, 0x70, 0x45, 0x77, 0xf3, 0xb4, 0x42, 0x99, 0x58, 0x68, + 0x11, 0xec, 0x41, 0x47, 0x11, 0xcc, 0xb1, 0x2f, 0xbb, 0x11, 0xe7, 0x81, + 0x7d, 0x17, 0x93, 0x30, 0xab, 0x58, 0xb1, 0xe0, 0x69, 0x34, 0x17, 0x3a, + 0xa0, 0x4a, 0xed, 0xd6, 0x9e, 0x02, 0xfd, 0xb7, 0xd1, 0x77, 0x3c, 0x59, + 0x47, 0xeb, 0xce, 0xa0, 0x64, 0x06, 0x38, 0x78, 0x96, 0x86, 0x77, 0x1e, + 0x3f, 0xa8, 0x56, 0x9d, 0xc4, 0x8f, 0x1d, 0x23, 0x23, 0x1c, 0xe6, 0x03, + 0x2c, 0xb9, 0xfd, 0xac, 0x3e, 0x30, 0x52, 0x51, 0x27, 0x35, 0x20, 0x93, + 0x94, 0xaa, 0x69, 0x83, 0xeb, 0x04, 0x32, 0x2a, 0xc0, 0x6b, 0x24, 0x30, + 0x6f, 0x0f, 0x61, 0xdb, 0xac, 0x01, 0x46, 0x71, 0x52, 0x96, 0x07, 0x48, + 0xba, 0xdf, 0x71, 0x96, 0x25, 0xa6, 0x04, 0x36, 0x49, 0xb9, 0xd9, 0x93, + 0x9a, 0x79, 0xbf, 0xad, 0x4a, 0x4f, 0x49, 0x98, 0x7d, 0xa3, 0x49, 0x1d, + 0x65, 0xb8, 0x51, 0x93, 0x60, 0x63, 0x91, 0x34, 0x5c, 0xe4, 0xad, 0x91, + 0x44, 0xc7, 0x69, 0x93, 0x82, 0x28, 0xce, 0xc1, 0xf6, 0xc2, 0xfb, 0xf5, + 0xef, 0xaf, 0xdb, 0xc2, 0x14, 0xed, 0x26, 0xad, 0xc9, 0xba, 0xee, 0xe8, + 0x40, 0xc6, 0x03, 0x21, 0x9a, 0x7a, 0x47, 0x45, 0x24, 0x5b, 0xc6, 0xf3, + 0xb4, 0x55, 0x7e, 0xa5, 0x86, 0x22, 0x60, 0x16, 0x4a, 0x67, 0x0b, 0xbd, + 0x92, 0x7a, 0x53, 0x6b, 0xa0, 0x05, 0x2e, 0x3c, 0xfa, 0x5e, 0x06, 0x4f, + 0xf1, 0x70, 0xa0, 0x82, 0x03, 0x38, 0x30, 0x82, 0x03, 0x34, 0x30, 0x82, + 0x03, 0x30, 0x30, 0x82, 0x02, 0x99, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, + 0x01, 0x03, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x61, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x4d, 0x58, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, 0x44, 0x46, 0x31, 0x0d, 0x30, + 0x0b, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x04, 0x43, 0x44, 0x4d, 0x58, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x02, 0x43, + 0x57, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x04, + 0x74, 0x65, 0x73, 0x74, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x0b, 0x74, 0x65, 0x73, + 0x74, 0x40, 0x71, 0x71, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x20, 0x17, 0x0d, + 0x32, 0x34, 0x30, 0x34, 0x32, 0x34, 0x32, 0x33, 0x35, 0x31, 0x31, 0x39, + 0x5a, 0x18, 0x0f, 0x32, 0x30, 0x35, 0x31, 0x30, 0x39, 0x30, 0x39, 0x32, + 0x33, 0x35, 0x31, 0x31, 0x39, 0x5a, 0x30, 0x52, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x4d, 0x58, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, 0x44, 0x46, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x02, 0x43, 0x57, 0x31, + 0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x04, 0x74, 0x65, + 0x73, 0x74, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x0b, 0x74, 0x65, 0x73, 0x74, 0x40, + 0x71, 0x71, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, + 0x01, 0x01, 0x00, 0xd5, 0x4d, 0x96, 0x69, 0x63, 0x59, 0x0a, 0x6e, 0x79, + 0x9e, 0x5a, 0x16, 0x0a, 0xb2, 0xc1, 0x2f, 0x9c, 0x73, 0x9f, 0x0d, 0x61, + 0xac, 0x48, 0x31, 0x04, 0x9f, 0xcb, 0x30, 0xb6, 0x47, 0xf3, 0xe3, 0x9d, + 0x9e, 0x96, 0xe6, 0xad, 0x2e, 0xe7, 0x40, 0x9e, 0x54, 0xe1, 0x85, 0x94, + 0x2f, 0xf5, 0xc4, 0x46, 0x21, 0x37, 0x57, 0xbe, 0x4f, 0x47, 0xda, 0x91, + 0x0b, 0xd6, 0x51, 0xe0, 0x13, 0x1c, 0x4b, 0x3f, 0xe8, 0xf2, 0xad, 0x8b, + 0xdb, 0xc1, 0x3d, 0xb5, 0x6c, 0x4d, 0xf7, 0x52, 0x5e, 0x67, 0x90, 0xd7, + 0xc8, 0xe9, 0xf5, 0x0a, 0xc1, 0x26, 0xbc, 0x00, 0x21, 0xca, 0xc1, 0xd5, + 0x37, 0xc2, 0xea, 0xd7, 0x82, 0x18, 0x94, 0x3e, 0xd7, 0x1a, 0x9e, 0xa6, + 0x77, 0x76, 0x37, 0xe8, 0x90, 0xeb, 0x4d, 0x09, 0x18, 0x6f, 0xda, 0xca, + 0x73, 0xbc, 0x15, 0x1f, 0xac, 0x14, 0x1f, 0xe5, 0x28, 0x3e, 0x04, 0x11, + 0x91, 0x71, 0x34, 0x50, 0xc4, 0xfc, 0x32, 0xb3, 0x2d, 0x0a, 0x9d, 0xa7, + 0x15, 0xca, 0x7a, 0xf8, 0x57, 0xc0, 0xe6, 0x92, 0x5e, 0x55, 0xa7, 0x45, + 0x58, 0x2b, 0xbf, 0x82, 0x23, 0x8d, 0xe4, 0xb7, 0x4a, 0xd4, 0x15, 0x44, + 0x80, 0x88, 0x16, 0x10, 0xcd, 0x42, 0x98, 0x46, 0xd1, 0x55, 0xa4, 0xa2, + 0xd8, 0xd8, 0x65, 0x33, 0x4d, 0x21, 0x6d, 0x1d, 0x11, 0x66, 0xd8, 0xa9, + 0xf9, 0x12, 0x42, 0x38, 0x2d, 0x36, 0x43, 0xa5, 0xe2, 0x5e, 0xff, 0x7c, + 0xae, 0xaa, 0xc8, 0x85, 0x42, 0xf3, 0xa0, 0x90, 0xd2, 0x04, 0xc9, 0xe4, + 0xa0, 0x0d, 0x97, 0xbb, 0x66, 0x8d, 0x81, 0xaa, 0x86, 0xa9, 0x49, 0x4c, + 0x14, 0x67, 0x02, 0xf6, 0x32, 0xde, 0x19, 0xf9, 0x14, 0xd0, 0xdb, 0x89, + 0xbf, 0x65, 0xc9, 0x87, 0x1d, 0xcc, 0xd3, 0x5f, 0x6a, 0xd4, 0x9d, 0x54, + 0x9d, 0x34, 0x08, 0xef, 0x7a, 0x77, 0x4f, 0x02, 0x03, 0x01, 0x00, 0x01, + 0xa3, 0x81, 0x80, 0x30, 0x7e, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x25, 0x30, 0x23, 0x30, 0x21, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x15, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, + 0x30, 0x2e, 0x31, 0x3a, 0x38, 0x30, 0x38, 0x30, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x58, 0x25, 0x0b, 0x7b, 0x6c, 0xe7, + 0x50, 0xdf, 0x45, 0x4b, 0x35, 0x37, 0xd1, 0x84, 0x25, 0x66, 0xbb, 0xda, + 0x7b, 0xc5, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, + 0x16, 0x80, 0x14, 0xc4, 0x57, 0x2a, 0x53, 0xb7, 0x21, 0x6d, 0x03, 0x2d, + 0xd0, 0xbc, 0xd4, 0x2a, 0x88, 0xd2, 0xae, 0x62, 0xa9, 0x97, 0x2a, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0xca, 0xbf, 0xb3, 0xcb, 0xf8, 0x5d, + 0x57, 0x25, 0xc4, 0xcd, 0xd3, 0xa2, 0xae, 0xcb, 0xc8, 0xe0, 0xd0, 0x16, + 0xa5, 0x54, 0x80, 0xf9, 0x6c, 0xa9, 0x4a, 0x8d, 0xa0, 0xea, 0x21, 0x6b, + 0xec, 0xfe, 0xa1, 0xdd, 0x48, 0x4c, 0xc0, 0x37, 0x5c, 0x36, 0x9f, 0x6d, + 0x3d, 0x89, 0x31, 0xc2, 0x74, 0xfd, 0xdf, 0x60, 0xa4, 0x05, 0xcb, 0x47, + 0xd2, 0x13, 0xa4, 0x23, 0x9f, 0xfb, 0x3c, 0x3f, 0x7a, 0x1f, 0x75, 0xfc, + 0x32, 0x8f, 0xbc, 0xb1, 0x3d, 0x7b, 0xef, 0x49, 0xd1, 0x47, 0x4a, 0x6d, + 0x6d, 0x8f, 0xd4, 0xb3, 0x71, 0x3d, 0x24, 0x48, 0x05, 0x1b, 0x29, 0xa7, + 0xe0, 0xbd, 0xad, 0x01, 0xff, 0x92, 0x2a, 0x24, 0x1e, 0x94, 0x6e, 0x59, + 0x7b, 0xd4, 0x98, 0xf0, 0x60, 0xe5, 0x69, 0xa2, 0x45, 0xaf, 0xd6, 0x7f, + 0x5b, 0x69, 0x84, 0x97, 0x23, 0xc7, 0xda, 0x3b, 0x37, 0xcd, 0x15, 0x4e, + 0x6b, 0xf0, +}; + static void test_decodeOCSPBasicSignedResponseInfo(DWORD dwEncoding) { OCSP_BASIC_SIGNED_RESPONSE_INFO *info; @@ -8760,6 +8876,12 @@ static void test_decodeOCSPBasicSignedResponseInfo(DWORD dwEncoding) ok(!info->SignatureInfo.cCertEncoded, "got %lu\n", info->SignatureInfo.cCertEncoded); ok(!info->SignatureInfo.rgCertEncoded, "got %p\n", info->SignatureInfo.rgCertEncoded); LocalFree(info); + + size = 0; + ret = CryptDecodeObjectEx(dwEncoding, OCSP_BASIC_SIGNED_RESPONSE, ocsp_basic_signed_response_with_cert, + sizeof(ocsp_basic_signed_response_with_cert), CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size); + ok(ret, "got %08lx\n", GetLastError()); + LocalFree(info); } static void test_decodeOCSPBasicResponseInfo(DWORD dwEncoding) From faca0652766b3d478f1fdf5eedd29724567d4545 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 24 Apr 2024 19:43:37 -0600 Subject: [PATCH 330/413] cryptnet: Do not use InternetCombineUrlW() in build_request_url(). (cherry picked from commit 798f158a60dbf2f3e9ce004a728febbad6b7be91) CW-Bug-Id: #23715 --- dlls/cryptnet/cryptnet_main.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dlls/cryptnet/cryptnet_main.c b/dlls/cryptnet/cryptnet_main.c index 7768e9e5bd3..1068dd26868 100644 --- a/dlls/cryptnet/cryptnet_main.c +++ b/dlls/cryptnet/cryptnet_main.c @@ -1869,15 +1869,16 @@ static WCHAR *build_request_url(const WCHAR *base_url, const BYTE *data, DWORD d DWORD len = 0; if (!(path = build_request_path(data, data_size))) return NULL; - - InternetCombineUrlW(base_url, path, NULL, &len, 0); + len = (wcslen(base_url) + wcslen(path) + 1) * sizeof(WCHAR); if (!(ret = malloc(len * sizeof(WCHAR)))) { free(path); return NULL; } - InternetCombineUrlW(base_url, path, ret, &len, 0); + wcscpy(ret, base_url); + wcscat(ret, path); free(path); + TRACE("-> %s.\n", debugstr_w(ret)); return ret; } From fb3ec0a9c25a23df4dd1353971f3c4dd2800280f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 26 Apr 2024 20:29:16 -0600 Subject: [PATCH 331/413] msvcrt: Implement _mbsncpy_s[_l](). CW-Bug-Id: #23731 --- dlls/msvcr100/msvcr100.spec | 4 +- dlls/msvcr110/msvcr110.spec | 4 +- dlls/msvcr120/msvcr120.spec | 4 +- dlls/msvcr80/msvcr80.spec | 4 +- dlls/msvcr80/tests/msvcr80.c | 133 ++++++++++++++++++++++++++++++++++- dlls/msvcr90/msvcr90.spec | 4 +- dlls/msvcrt/mbcs.c | 64 +++++++++++++++++ dlls/ucrtbase/tests/string.c | 85 ++++++++++++++++++++++ dlls/ucrtbase/ucrtbase.spec | 8 +-- include/msvcrt/mbstring.h | 2 + 10 files changed, 297 insertions(+), 15 deletions(-) diff --git a/dlls/msvcr100/msvcr100.spec b/dlls/msvcr100/msvcr100.spec index 79a201f4c17..bede043830a 100644 --- a/dlls/msvcr100/msvcr100.spec +++ b/dlls/msvcr100/msvcr100.spec @@ -1157,8 +1157,8 @@ @ stub _mbsncoll_l @ cdecl _mbsncpy(ptr str long) @ cdecl _mbsncpy_l(ptr str long ptr) -@ stub _mbsncpy_s -@ stub _mbsncpy_s_l +@ cdecl _mbsncpy_s(ptr long str long) +@ cdecl _mbsncpy_s_l(ptr long str long ptr) @ cdecl _mbsnextc(str) @ cdecl _mbsnextc_l(str ptr) @ cdecl _mbsnicmp(str str long) diff --git a/dlls/msvcr110/msvcr110.spec b/dlls/msvcr110/msvcr110.spec index 6a6d38db187..a9f6520a197 100644 --- a/dlls/msvcr110/msvcr110.spec +++ b/dlls/msvcr110/msvcr110.spec @@ -1514,8 +1514,8 @@ @ stub _mbsncoll_l @ cdecl _mbsncpy(ptr str long) @ cdecl _mbsncpy_l(ptr str long ptr) -@ stub _mbsncpy_s -@ stub _mbsncpy_s_l +@ cdecl _mbsncpy_s(ptr long str long) +@ cdecl _mbsncpy_s_l(ptr long str long ptr) @ cdecl _mbsnextc(str) @ cdecl _mbsnextc_l(str ptr) @ cdecl _mbsnicmp(str str long) diff --git a/dlls/msvcr120/msvcr120.spec b/dlls/msvcr120/msvcr120.spec index 13d7e401133..6578340c2e0 100644 --- a/dlls/msvcr120/msvcr120.spec +++ b/dlls/msvcr120/msvcr120.spec @@ -1525,8 +1525,8 @@ @ stub _mbsncoll_l @ cdecl _mbsncpy(ptr str long) @ cdecl _mbsncpy_l(ptr str long ptr) -@ stub _mbsncpy_s -@ stub _mbsncpy_s_l +@ cdecl _mbsncpy_s(ptr long str long) +@ cdecl _mbsncpy_s_l(ptr long str long ptr) @ cdecl _mbsnextc(str) @ cdecl _mbsnextc_l(str ptr) @ cdecl _mbsnicmp(str str long) diff --git a/dlls/msvcr80/msvcr80.spec b/dlls/msvcr80/msvcr80.spec index 5b253b88234..182bfa039f7 100644 --- a/dlls/msvcr80/msvcr80.spec +++ b/dlls/msvcr80/msvcr80.spec @@ -829,8 +829,8 @@ @ stub _mbsncoll_l @ cdecl _mbsncpy(ptr str long) @ cdecl _mbsncpy_l(ptr str long ptr) -@ stub _mbsncpy_s -@ stub _mbsncpy_s_l +@ cdecl _mbsncpy_s(ptr long str long) +@ cdecl _mbsncpy_s_l(ptr long str long ptr) @ cdecl _mbsnextc(str) @ cdecl _mbsnextc_l(str ptr) @ cdecl _mbsnicmp(str str long) diff --git a/dlls/msvcr80/tests/msvcr80.c b/dlls/msvcr80/tests/msvcr80.c index fa04b024031..82379e1246f 100644 --- a/dlls/msvcr80/tests/msvcr80.c +++ b/dlls/msvcr80/tests/msvcr80.c @@ -39,6 +39,44 @@ #define MSVCRT_FD_BLOCK_SIZE 32 +#define DEFINE_EXPECT(func) \ + static BOOL expect_ ## func = FALSE, called_ ## func = FALSE + +#define SET_EXPECT(func) \ + expect_ ## func = TRUE + +#define CHECK_EXPECT2(func) \ + do { \ + ok(expect_ ##func, "unexpected call " #func "\n"); \ + called_ ## func = TRUE; \ + }while(0) + +#define CHECK_EXPECT(func) \ + do { \ + CHECK_EXPECT2(func); \ + expect_ ## func = FALSE; \ + }while(0) + +#define CHECK_CALLED(func) \ + do { \ + ok(called_ ## func, "expected " #func "\n"); \ + expect_ ## func = called_ ## func = FALSE; \ + }while(0) + +DEFINE_EXPECT(invalid_parameter_handler); + +static void __cdecl test_invalid_parameter_handler(const wchar_t *expression, + const wchar_t *function, const wchar_t *file, + unsigned line, uintptr_t arg) +{ + CHECK_EXPECT(invalid_parameter_handler); + ok(expression == NULL, "expression is not NULL\n"); + ok(function == NULL, "function is not NULL\n"); + ok(file == NULL, "file is not NULL\n"); + ok(line == 0, "line = %u\n", line); + ok(arg == 0, "arg = %Ix\n", arg); +} + typedef struct { HANDLE handle; @@ -57,6 +95,7 @@ typedef struct static ioinfo **__pioinfo; +static _invalid_parameter_handler (__cdecl *p__set_invalid_parameter_handler)(_invalid_parameter_handler); static int (WINAPIV *p__open)(const char *, int, ...); static int (__cdecl *p__close)(int); static intptr_t (__cdecl *p__get_osfhandle)(int); @@ -64,6 +103,8 @@ static int (__cdecl *p_strcmp)(const char *, const char *); static int (__cdecl *p_strncmp)(const char *, const char *, size_t); static int (__cdecl *p_dupenv_s)(char **, size_t *, const char *); static int (__cdecl *p_wdupenv_s)(wchar_t **, size_t *, const wchar_t *); +static errno_t (__cdecl *p__mbsncpy_s)(unsigned char*,size_t,const unsigned char*,size_t); + #define SETNOFAIL(x,y) x = (void*)GetProcAddress(hcrt,y) #define SET(x,y) do { SETNOFAIL(x,y); ok(x != NULL, "Export '%s' not found\n", y); } while(0) @@ -78,6 +119,8 @@ static BOOL init(void) return FALSE; } + SET(p__set_invalid_parameter_handler, "_set_invalid_parameter_handler"); + SET(__pioinfo, "__pioinfo"); SET(p__open,"_open"); SET(p__close,"_close"); @@ -87,7 +130,7 @@ static BOOL init(void) SET(p_strncmp, "strncmp"); SET(p_dupenv_s, "_dupenv_s"); SET(p_wdupenv_s, "_wdupenv_s"); - + SET(p__mbsncpy_s, "_mbsncpy_s"); return TRUE; } @@ -216,13 +259,101 @@ static void test_wdupenv_s(void) ok( !tmp, "_wdupenv_s returned pointer is %p\n", tmp ); } +static char *buf_to_string(const unsigned char *bin, int len, int nr) +{ + static char buf[2][1024]; + char *w = buf[nr]; + int i; + + for (i = 0; i < len; i++) + { + sprintf(w, "%02x ", (unsigned char)bin[i]); + w += strlen(w); + } + return buf[nr]; +} + +#define expect_eq(expr, value, type, format) { type ret = (expr); ok((value) == ret, #expr " expected " format " got " format "\n", value, ret); } +#define expect_bin(buf, value, len) { ok(memcmp((buf), value, len) == 0, "Binary buffer mismatch - expected %s, got %s\n", buf_to_string((unsigned char *)value, len, 1), buf_to_string((buf), len, 0)); } + +static void test__mbsncpy_s(void) +{ + unsigned char *mbstring = (unsigned char *)"\xb0\xb1\xb2\xb3Q\xb4\xb5\x0"; /* correct string */ + unsigned char buf[16]; + errno_t err; + + errno = 0xdeadbeef; + memset(buf, 0xcc, sizeof(buf)); + err = p__mbsncpy_s(buf, 6, mbstring, 1); + ok(errno == 0xdeadbeef, "Unexpected errno = %d\n", errno); + ok(!err, "got %d.\n", err); + expect_bin(buf, "\xb0\0\xcc", 3); + + memset(buf, 0xcc, sizeof(buf)); + err = p__mbsncpy_s(buf, 6, mbstring, 2); + ok(!err, "got %d.\n", err); + expect_bin(buf, "\xb0\xb1\0\xcc", 4); + + memset(buf, 0xcc, sizeof(buf)); + err = p__mbsncpy_s(buf, 2, mbstring, _TRUNCATE); + ok(err == STRUNCATE, "got %d.\n", err); + expect_bin(buf, "\xb0\0\xcc", 3); + + memset(buf, 0xcc, sizeof(buf)); + err = p__mbsncpy_s(buf, 2, mbstring, 1); + ok(!err, "got %d.\n", err); + expect_bin(buf, "\xb0\0\xcc", 3); + + memset(buf, 0xcc, sizeof(buf)); + SET_EXPECT(invalid_parameter_handler); + err = p__mbsncpy_s(buf, 2, mbstring, 3); + CHECK_CALLED(invalid_parameter_handler); + ok(err == ERANGE, "got %d.\n", err); + expect_bin(buf, "\x0\xb1\xcc", 3); + + memset(buf, 0xcc, sizeof(buf)); + SET_EXPECT(invalid_parameter_handler); + err = p__mbsncpy_s(buf, 1, mbstring, 3); + CHECK_CALLED(invalid_parameter_handler); + ok(err == ERANGE, "got %d.\n", err); + expect_bin(buf, "\x0\xcc", 2); + + memset(buf, 0xcc, sizeof(buf)); + SET_EXPECT(invalid_parameter_handler); + err = p__mbsncpy_s(buf, 0, mbstring, 3); + CHECK_CALLED(invalid_parameter_handler); + ok(err == EINVAL, "got %d.\n", err); + expect_bin(buf, "\xcc", 1); + + memset(buf, 0xcc, sizeof(buf)); + SET_EXPECT(invalid_parameter_handler); + err = p__mbsncpy_s(buf, 0, mbstring, 0); + CHECK_CALLED(invalid_parameter_handler); + ok(err == EINVAL, "got %d.\n", err); + expect_bin(buf, "\xcc", 1); + + memset(buf, 0xcc, sizeof(buf)); + err = p__mbsncpy_s(buf, -1, mbstring, 0); + ok(!err, "got %d.\n", err); + expect_bin(buf, "\x0\xcc", 2); + + memset(buf, 0xcc, sizeof(buf)); + err = p__mbsncpy_s(buf, -1, mbstring, 256); + ok(!err, "got %d.\n", err); + expect_bin(buf, "\xb0\xb1\xb2\xb3Q\xb4\xb5\x0\xcc", 9); +} + START_TEST(msvcr80) { if(!init()) return; + ok(p__set_invalid_parameter_handler(test_invalid_parameter_handler) == NULL, + "Invalid parameter handler was already set\n"); + test_ioinfo_flags(); test_strcmp(); test_dupenv_s(); test_wdupenv_s(); + test__mbsncpy_s(); } diff --git a/dlls/msvcr90/msvcr90.spec b/dlls/msvcr90/msvcr90.spec index df989581d5e..906024529e5 100644 --- a/dlls/msvcr90/msvcr90.spec +++ b/dlls/msvcr90/msvcr90.spec @@ -807,8 +807,8 @@ @ stub _mbsncoll_l @ cdecl _mbsncpy(ptr str long) @ cdecl _mbsncpy_l(ptr str long ptr) -@ stub _mbsncpy_s -@ stub _mbsncpy_s_l +@ cdecl _mbsncpy_s(ptr long str long) +@ cdecl _mbsncpy_s_l(ptr long str long ptr) @ cdecl _mbsnextc(str) @ cdecl _mbsnextc_l(str ptr) @ cdecl _mbsnicmp(str str long) diff --git a/dlls/msvcrt/mbcs.c b/dlls/msvcrt/mbcs.c index ce8a3115eeb..3ddbae09078 100644 --- a/dlls/msvcrt/mbcs.c +++ b/dlls/msvcrt/mbcs.c @@ -900,6 +900,70 @@ unsigned char* CDECL _mbsncpy_l(unsigned char* dst, const unsigned char* src, si return ret; } +#if _MSVCR_VER>=80 +errno_t CDECL _mbsncpy_s_l(unsigned char* dst, size_t maxsize, const unsigned char* src, size_t n, _locale_t locale) +{ + BOOL truncate = (n == _TRUNCATE); + unsigned char *start = dst; + pthreadmbcinfo mbcinfo; + + if(!MSVCRT_CHECK_PMT(dst != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(maxsize != 0)) return EINVAL; + if (!MSVCRT_CHECK_PMT(src != NULL)) + { + *start = 0; + return EINVAL; + } + + if (!n) + { + *start = 0; + return 0; + } + + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + while (*src && n && maxsize) + { + if (mbcinfo->ismbcodepage && _ismbblead_l(*src, locale)) + { + --maxsize; + if (!*(src + 1)) + { + *dst++ = 0; + break; + } + *dst++ = *src++; + if (!maxsize) break; + } + --maxsize; + --n; + *dst++ = *src++; + } + + if (!maxsize && truncate) + { + *(dst - 1) = 0; + return STRUNCATE; + } + if (!maxsize) + { + *start = 0; + if (!MSVCRT_CHECK_PMT(FALSE)) return ERANGE; + } + *dst = 0; + return 0; +} + +errno_t CDECL _mbsncpy_s(unsigned char* dst, size_t maxsize, const unsigned char* src, size_t n) +{ + return _mbsncpy_s_l(dst, maxsize, src, n, NULL); +} +#endif + /********************************************************************* * _mbsncpy(MSVCRT.@) * REMARKS diff --git a/dlls/ucrtbase/tests/string.c b/dlls/ucrtbase/tests/string.c index 6dcd15fb5b9..5789e7ba619 100644 --- a/dlls/ucrtbase/tests/string.c +++ b/dlls/ucrtbase/tests/string.c @@ -651,6 +651,90 @@ static void test_strcmp(void) ok( ret == 0, "wrong ret %d\n", ret ); } +static char *buf_to_string(const unsigned char *bin, int len, int nr) +{ + static char buf[2][1024]; + char *w = buf[nr]; + int i; + + for (i = 0; i < len; i++) + { + sprintf(w, "%02x ", (unsigned char)bin[i]); + w += strlen(w); + } + return buf[nr]; +} + +#define expect_eq(expr, value, type, format) { type ret = (expr); ok((value) == ret, #expr " expected " format " got " format "\n", value, ret); } +#define expect_bin(buf, value, len) { ok(memcmp((buf), value, len) == 0, "Binary buffer mismatch - expected %s, got %s\n", buf_to_string((unsigned char *)value, len, 1), buf_to_string((buf), len, 0)); } + +static void test__mbsncpy_s(void) +{ + unsigned char *mbstring = (unsigned char *)"\xb0\xb1\xb2\xb3Q\xb4\xb5\x0"; /* correct string */ + unsigned char buf[16]; + errno_t err; + + errno = 0xdeadbeef; + memset(buf, 0xcc, sizeof(buf)); + err = _mbsncpy_s(buf, 6, mbstring, 1); + ok(errno == 0xdeadbeef, "Unexpected errno = %d\n", errno); + ok(!err, "got %d.\n", err); + expect_bin(buf, "\xb0\0\xcc", 3); + + memset(buf, 0xcc, sizeof(buf)); + err = _mbsncpy_s(buf, 6, mbstring, 2); + ok(!err, "got %d.\n", err); + expect_bin(buf, "\xb0\xb1\0\xcc", 4); + + memset(buf, 0xcc, sizeof(buf)); + err = _mbsncpy_s(buf, 2, mbstring, _TRUNCATE); + ok(err == STRUNCATE, "got %d.\n", err); + expect_bin(buf, "\xb0\0\xcc", 3); + + memset(buf, 0xcc, sizeof(buf)); + err = _mbsncpy_s(buf, 2, mbstring, 1); + ok(!err, "got %d.\n", err); + expect_bin(buf, "\xb0\0\xcc", 3); + + memset(buf, 0xcc, sizeof(buf)); + SET_EXPECT(invalid_parameter_handler); + err = _mbsncpy_s(buf, 2, mbstring, 3); + CHECK_CALLED(invalid_parameter_handler); + ok(err == ERANGE, "got %d.\n", err); + expect_bin(buf, "\x0\xb1\xcc", 3); + + memset(buf, 0xcc, sizeof(buf)); + SET_EXPECT(invalid_parameter_handler); + err = _mbsncpy_s(buf, 1, mbstring, 3); + CHECK_CALLED(invalid_parameter_handler); + ok(err == ERANGE, "got %d.\n", err); + expect_bin(buf, "\x0\xcc", 2); + + memset(buf, 0xcc, sizeof(buf)); + SET_EXPECT(invalid_parameter_handler); + err = _mbsncpy_s(buf, 0, mbstring, 3); + CHECK_CALLED(invalid_parameter_handler); + ok(err == EINVAL, "got %d.\n", err); + expect_bin(buf, "\xcc", 1); + + memset(buf, 0xcc, sizeof(buf)); + SET_EXPECT(invalid_parameter_handler); + err = _mbsncpy_s(buf, 0, mbstring, 0); + CHECK_CALLED(invalid_parameter_handler); + ok(err == EINVAL, "got %d.\n", err); + expect_bin(buf, "\xcc", 1); + + memset(buf, 0xcc, sizeof(buf)); + err = _mbsncpy_s(buf, -1, mbstring, 0); + ok(!err, "got %d.\n", err); + expect_bin(buf, "\x0\xcc", 2); + + memset(buf, 0xcc, sizeof(buf)); + err = _mbsncpy_s(buf, -1, mbstring, 256); + ok(!err, "got %d.\n", err); + expect_bin(buf, "\xb0\xb1\xb2\xb3Q\xb4\xb5\x0\xcc", 9); +} + START_TEST(string) { ok(_set_invalid_parameter_handler(test_invalid_parameter_handler) == NULL, @@ -669,4 +753,5 @@ START_TEST(string) test_SpecialCasing(); test__mbbtype_l(); test_strcmp(); + test__mbsncpy_s(); } diff --git a/dlls/ucrtbase/ucrtbase.spec b/dlls/ucrtbase/ucrtbase.spec index 60ebe06ea82..a9dc4583beb 100644 --- a/dlls/ucrtbase/ucrtbase.spec +++ b/dlls/ucrtbase/ucrtbase.spec @@ -673,8 +673,8 @@ @ stub _mbsncoll_l @ cdecl _mbsncpy(ptr str long) @ cdecl _mbsncpy_l(ptr str long ptr) -@ stub _mbsncpy_s -@ stub _mbsncpy_s_l +@ cdecl _mbsncpy_s(ptr long str long) +@ cdecl _mbsncpy_s_l(ptr long str long ptr) @ cdecl _mbsnextc(str) @ cdecl _mbsnextc_l(str ptr) @ cdecl _mbsnicmp(str str long) @@ -1242,8 +1242,8 @@ @ stub _o__mbsncoll_l @ cdecl _o__mbsncpy(ptr str long) _mbsncpy @ cdecl _o__mbsncpy_l(ptr str long ptr) _mbsncpy_l -@ stub _o__mbsncpy_s -@ stub _o__mbsncpy_s_l +@ cdecl _o__mbsncpy_s(ptr long str long) _mbsncpy_s +@ cdecl _o__mbsncpy_s_l(ptr long str long ptr) _mbsncpy_s_l @ cdecl _o__mbsnextc(str) _mbsnextc @ cdecl _o__mbsnextc_l(str ptr) _mbsnextc_l @ cdecl _o__mbsnicmp(str str long) _mbsnicmp diff --git a/include/msvcrt/mbstring.h b/include/msvcrt/mbstring.h index 28a0e41f10d..f51d8ed6bc3 100644 --- a/include/msvcrt/mbstring.h +++ b/include/msvcrt/mbstring.h @@ -93,6 +93,8 @@ _ACRTIMP size_t __cdecl _mbsnccnt(const unsigned char*,size_t); _ACRTIMP int __cdecl _mbsncmp(const unsigned char*,const unsigned char*,size_t); _ACRTIMP int __cdecl _mbsncoll(const unsigned char*,const unsigned char*,size_t); _ACRTIMP unsigned char* __cdecl _mbsncpy(unsigned char*,const unsigned char*,size_t); +_ACRTIMP errno_t __cdecl _mbsncpy_s(unsigned char*,size_t,const unsigned char*,size_t); +_ACRTIMP errno_t __cdecl _mbsncpy_s_l(unsigned char*,size_t,const unsigned char*,size_t,_locale_t); _ACRTIMP unsigned int __cdecl _mbsnextc(const unsigned char*); _ACRTIMP unsigned int __cdecl _mbsnextc_l(const unsigned char*,_locale_t); _ACRTIMP int __cdecl _mbsnicmp(const unsigned char*,const unsigned char*,size_t); From 3862f6892e393d7cb9380d3c9f14e7253195d3e8 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Wed, 28 Feb 2024 16:04:41 +0100 Subject: [PATCH 332/413] wmvcore: Start and stop read & delivery threads on OP_START/OP_STOP. Otherwise, it keeps pushing samples after calling OnStatus(WMT_STOPPED), which doesn't seem right. (Leading to potential crash). CW-Bug-Id: #22313 --- dlls/wmvcore/async_reader.c | 73 ++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 22 deletions(-) diff --git a/dlls/wmvcore/async_reader.c b/dlls/wmvcore/async_reader.c index 76e749be690..62f695baf20 100644 --- a/dlls/wmvcore/async_reader.c +++ b/dlls/wmvcore/async_reader.c @@ -82,6 +82,8 @@ struct stream HANDLE deliver_thread; struct list deliver_samples; CONDITION_VARIABLE deliver_cv; + + bool running; }; struct async_reader @@ -346,7 +348,7 @@ static DWORD WINAPI stream_deliver_thread(void *arg) EnterCriticalSection(&reader->callback_cs); - while (reader->running) + while (reader->running && stream->running) { if (list_empty(&stream->deliver_samples)) { @@ -380,7 +382,7 @@ static DWORD WINAPI stream_read_thread(void *arg) EnterCriticalSection(&reader->callback_cs); - while (reader->running) + while (reader->running && stream->running) { if (!stream->read_requested) { @@ -450,6 +452,8 @@ static void stream_flush_samples(struct stream *stream) static void stream_close(struct stream *stream) { + if (!stream->running) return; + stream->running = false; if (stream->read_thread) { WakeConditionVariable(&stream->read_cv); @@ -484,13 +488,44 @@ static HRESULT stream_open(struct stream *stream, struct async_reader *reader, W if (!(stream->deliver_thread = CreateThread(NULL, 0, stream_deliver_thread, stream, 0, NULL))) { + LeaveCriticalSection(&reader->callback_cs); stream_close(stream); + EnterCriticalSection(&reader->callback_cs); return E_OUTOFMEMORY; } + stream->running = true; return S_OK; } +static void async_reader_close_all_streams(struct async_reader *reader) +{ + int i; + + if (!reader->streams) return; + for (i = 0; i < reader->stream_count; i++) + { + struct stream *stream = reader->streams + i; + stream_close(stream); + } +} + +static HRESULT async_reader_open_all_streams(struct async_reader *reader) +{ + HRESULT hr; + int i; + + for (i = 0; i < reader->stream_count; ++i) + { + struct stream *stream = reader->streams + i; + + if (FAILED(hr = stream_open(stream, reader, i + 1))) + return hr; + stream_request_read(stream); + } + return S_OK; +} + static HRESULT async_reader_get_next_sample(struct async_reader *reader, struct stream **out_stream, struct sample **out_sample) { @@ -601,7 +636,6 @@ static DWORD WINAPI async_reader_callback_thread(void *arg) struct async_reader *reader = arg; struct list *entry; HRESULT hr = S_OK; - DWORD i; IWMReaderCallback_OnStatus(reader->callback, WMT_OPENED, S_OK, WMT_TYPE_DWORD, (BYTE *)&zero, reader->context); @@ -629,11 +663,15 @@ static DWORD WINAPI async_reader_callback_thread(void *arg) { reader->clock_start = get_current_time(reader); - for (i = 0; i < reader->stream_count; ++i) + if (FAILED(hr = async_reader_open_all_streams(reader))) { - struct stream *stream = reader->streams + i; - stream_flush_samples(stream); - stream_request_read(stream); + WARN("Unable to open all required streams, aborting\n"); + LeaveCriticalSection(&reader->callback_cs); + async_reader_close_all_streams(reader); + IWMReaderCallback_OnStatus(reader->callback, WMT_CLOSED, hr, + WMT_TYPE_DWORD, (BYTE *)&zero, reader->context); + EnterCriticalSection(&reader->callback_cs); + reader->running = false; } } @@ -646,8 +684,12 @@ static DWORD WINAPI async_reader_callback_thread(void *arg) case ASYNC_OP_STOP: if (SUCCEEDED(hr)) + { reader->clock_start = 0; - + LeaveCriticalSection(&reader->callback_cs); + async_reader_close_all_streams(reader); + EnterCriticalSection(&reader->callback_cs); + } LeaveCriticalSection(&reader->callback_cs); IWMReaderCallback_OnStatus(reader->callback, WMT_STOPPED, hr, WMT_TYPE_DWORD, (BYTE *)&zero, reader->context); @@ -684,7 +726,6 @@ static DWORD WINAPI async_reader_callback_thread(void *arg) static void async_reader_close(struct async_reader *reader) { struct async_op *op, *next; - int i; if (reader->callback_thread) { @@ -699,11 +740,7 @@ static void async_reader_close(struct async_reader *reader) free(op); } - for (i = 0; reader->streams && i < reader->stream_count; ++i) - { - struct stream *stream = reader->streams + i; - stream_close(stream); - } + async_reader_close_all_streams(reader); free(reader->streams); reader->streams = NULL; reader->stream_count = 0; @@ -725,7 +762,6 @@ static void async_reader_close(struct async_reader *reader) static HRESULT async_reader_open(struct async_reader *reader, IWMReaderCallback *callback, void *context) { HRESULT hr = E_OUTOFMEMORY; - DWORD i; IWMReaderCallback_AddRef((reader->callback = callback)); reader->context = context; @@ -751,13 +787,6 @@ static HRESULT async_reader_open(struct async_reader *reader, IWMReaderCallback reader->running = true; - for (i = 0; i < reader->stream_count; ++i) - { - struct stream *stream = reader->streams + i; - if (FAILED(hr = stream_open(stream, reader, i + 1))) - goto error; - } - if (!(reader->callback_thread = CreateThread(NULL, 0, async_reader_callback_thread, reader, 0, NULL))) goto error; From deb0ae1efb67d246c6791d5b54a78cc2b15e6a14 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 30 Apr 2024 12:42:59 -0600 Subject: [PATCH 333/413] fixup! ntdll: Support gpuvis tracing. CW-Bug-Id: #19529 --- dlls/ntdll/unix/debug.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/dlls/ntdll/unix/debug.c b/dlls/ntdll/unix/debug.c index 73c2084d025..cda455a035d 100644 --- a/dlls/ntdll/unix/debug.c +++ b/dlls/ntdll/unix/debug.c @@ -290,12 +290,7 @@ unsigned int WINAPI __wine_dbg_ftrace( char *str, unsigned int str_size, unsigne const char *fn; int fd; - if (!(fn = getenv( "WINE_FTRACE_FILE" ))) - { - MESSAGE( "wine: WINE_FTRACE_FILE is not set.\n" ); - ftrace_fd = -2; - return 0; - } + if (!(fn = getenv( "WINE_FTRACE_FILE" ))) fn = "/sys/kernel/tracing/trace_marker"; if ((fd = open( fn, O_WRONLY )) == -1) { MESSAGE( "wine: error opening ftrace file: %s.\n", strerror(errno) ); From aa08566add95a7523e72797e64e26027bd707d22 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 30 Apr 2024 14:14:23 -0600 Subject: [PATCH 334/413] kernelbase: HACK: Force angle d3d9 for Antenna. CW-Bug-Id: #23739 --- dlls/kernelbase/process.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index 4267a8c57e6..4e3d8292e1e 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -594,6 +594,7 @@ static const WCHAR *hack_append_command_line( const WCHAR *cmd ) } options[] = { + {L"antenna\\antenna.exe", L" --use-angle=d3d9"}, {L"Bloody Walls\\game.exe", L" --disable_direct_composition=1"}, {L"Insanitys Blade\\nw.exe", L" --use-gl=swiftshader"}, {L"Warhammer2.exe", L" --in-process-gpu"}, From e1d214820f19f24dfb654e950deea8edcfe5f748 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 30 Apr 2024 14:17:26 -0600 Subject: [PATCH 335/413] kernelbase: HACK: Force angle d3d9 for A Raven Monologue. CW-Bug-Id: #23740 --- dlls/kernelbase/process.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index 4e3d8292e1e..98dbbe657c4 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -594,6 +594,7 @@ static const WCHAR *hack_append_command_line( const WCHAR *cmd ) } options[] = { + {L"A Raven Monologue.exe", L" --use-angle=d3d9"}, {L"antenna\\antenna.exe", L" --use-angle=d3d9"}, {L"Bloody Walls\\game.exe", L" --disable_direct_composition=1"}, {L"Insanitys Blade\\nw.exe", L" --use-gl=swiftshader"}, From 9e10c1f6ae156dde202759edab332fa0ef8e9066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 29 Apr 2024 18:35:51 +0200 Subject: [PATCH 336/413] fixup! user32: Add or remove rawinput devices individually on WM_DEVICECHANGE. CW-Bug-Id: #23732 --- dlls/win32u/rawinput.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/dlls/win32u/rawinput.c b/dlls/win32u/rawinput.c index 1e071e30a94..19eb9f26fbd 100644 --- a/dlls/win32u/rawinput.c +++ b/dlls/win32u/rawinput.c @@ -440,12 +440,13 @@ static void rawinput_update_device_list(void) enumerate_devices( RIM_TYPEHID, guid_devinterface_hidW ); } -static struct device *find_device_from_handle( HANDLE handle ) +static struct device *find_device_from_handle( HANDLE handle, BOOL refresh ) { struct device *device; LIST_FOR_EACH_ENTRY( device, &devices, struct device, entry ) if (device->handle == handle) return device; + if (!refresh) return NULL; rawinput_update_device_list(); @@ -461,7 +462,7 @@ BOOL rawinput_device_get_usages( HANDLE handle, USAGE *usage_page, USAGE *usage pthread_mutex_lock( &rawinput_mutex ); - if (!(device = find_device_from_handle( handle )) || device->info.dwType != RIM_TYPEHID) + if (!(device = find_device_from_handle( handle, TRUE )) || device->info.dwType != RIM_TYPEHID) *usage_page = *usage = 0; else { @@ -581,7 +582,7 @@ UINT WINAPI NtUserGetRawInputDeviceInfo( HANDLE handle, UINT command, void *data pthread_mutex_lock( &rawinput_mutex ); - if (!(device = find_device_from_handle( handle ))) + if (!(device = find_device_from_handle( handle, TRUE ))) { pthread_mutex_unlock( &rawinput_mutex ); RtlSetLastWin32Error( ERROR_INVALID_HANDLE ); @@ -856,23 +857,18 @@ BOOL process_rawinput_message( MSG *msg, UINT hw_id, const struct hardware_msg_d if (msg->message == WM_INPUT_DEVICE_CHANGE) { + BOOL refresh = msg_data->rawinput.hid.param == GIDC_ARRIVAL; + struct device *device; + pthread_mutex_lock( &rawinput_mutex ); - if (msg_data->rawinput.type != RIM_TYPEHID || msg_data->rawinput.hid.param != GIDC_REMOVAL) - rawinput_update_device_list(); - else + if ((device = find_device_from_handle( UlongToHandle( msg_data->rawinput.hid.device ), refresh ))) { - struct device *device; - - LIST_FOR_EACH_ENTRY( device, &devices, struct device, entry ) + if (msg_data->rawinput.hid.param == GIDC_REMOVAL) { - if (device->handle == UlongToHandle(msg_data->rawinput.hid.device)) - { - list_remove( &device->entry ); - NtClose( device->file ); - free( device->data ); - free( device ); - break; - } + list_remove( &device->entry ); + NtClose( device->file ); + free( device->data ); + free( device ); } } pthread_mutex_unlock( &rawinput_mutex ); From 7cb0074322913112b9aa6adaf7d4d4deb2b2b252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 2 May 2024 17:54:32 +0200 Subject: [PATCH 337/413] win32u: Move rawinput device cache ticks check to rawinput_update_device_list. CW-Bug-Id: #23732 --- dlls/win32u/rawinput.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/dlls/win32u/rawinput.c b/dlls/win32u/rawinput.c index 19eb9f26fbd..044bc1c4c91 100644 --- a/dlls/win32u/rawinput.c +++ b/dlls/win32u/rawinput.c @@ -421,12 +421,17 @@ static void enumerate_devices( DWORD type, const WCHAR *class ) NtClose( class_key ); } -static void rawinput_update_device_list(void) +static void rawinput_update_device_list( BOOL force ) { + unsigned int ticks = NtGetTickCount(); + static unsigned int last_check; struct device *device, *next; TRACE( "\n" ); + if (ticks - last_check <= 2000 && !force) return; + last_check = ticks; + LIST_FOR_EACH_ENTRY_SAFE( device, next, &devices, struct device, entry ) { list_remove( &device->entry ); @@ -448,7 +453,7 @@ static struct device *find_device_from_handle( HANDLE handle, BOOL refresh ) if (device->handle == handle) return device; if (!refresh) return NULL; - rawinput_update_device_list(); + rawinput_update_device_list( TRUE ); LIST_FOR_EACH_ENTRY( device, &devices, struct device, entry ) if (device->handle == handle) return device; @@ -480,8 +485,7 @@ BOOL rawinput_device_get_usages( HANDLE handle, USAGE *usage_page, USAGE *usage */ UINT WINAPI NtUserGetRawInputDeviceList( RAWINPUTDEVICELIST *device_list, UINT *device_count, UINT size ) { - unsigned int count = 0, ticks = NtGetTickCount(); - static unsigned int last_check; + unsigned int count = 0; struct device *device; TRACE( "device_list %p, device_count %p, size %u.\n", device_list, device_count, size ); @@ -500,11 +504,7 @@ UINT WINAPI NtUserGetRawInputDeviceList( RAWINPUTDEVICELIST *device_list, UINT * pthread_mutex_lock( &rawinput_mutex ); - if (ticks - last_check > 2000) - { - last_check = ticks; - rawinput_update_device_list(); - } + rawinput_update_device_list( FALSE ); LIST_FOR_EACH_ENTRY( device, &devices, struct device, entry ) { From b1a2335f82dca460a666b2ba30689d92db4fd3aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 2 May 2024 17:44:18 +0200 Subject: [PATCH 338/413] win32u: Post device arrival messages in NtUserRegisterRawInputDevices. CW-Bug-Id: #23732 --- dlls/win32u/rawinput.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/dlls/win32u/rawinput.c b/dlls/win32u/rawinput.c index 044bc1c4c91..35c547c695e 100644 --- a/dlls/win32u/rawinput.c +++ b/dlls/win32u/rawinput.c @@ -885,6 +885,30 @@ BOOL process_rawinput_message( MSG *msg, UINT hw_id, const struct hardware_msg_d return TRUE; } +static void post_device_notifications( const RAWINPUTDEVICE *filter ) +{ + ULONG usages = MAKELONG( filter->usUsagePage, filter->usUsage ); + struct device *device; + + LIST_FOR_EACH_ENTRY( device, &devices, struct device, entry ) + { + switch (device->info.dwType) + { + case RIM_TYPEMOUSE: + if (usages != MAKELONG( HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_MOUSE )) continue; + break; + case RIM_TYPEKEYBOARD: + if (usages != MAKELONG( HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_KEYBOARD )) continue; + break; + case RIM_TYPEHID: + if (usages != MAKELONG( device->info.hid.usUsagePage, device->info.hid.usUsage )) continue; + break; + } + + NtUserPostMessage( filter->hwndTarget, WM_INPUT_DEVICE_CHANGE, GIDC_ARRIVAL, (LPARAM)device->handle ); + } +} + static void register_rawinput_device( const RAWINPUTDEVICE *device ) { RAWINPUTDEVICE *pos, *end; @@ -906,6 +930,7 @@ static void register_rawinput_device( const RAWINPUTDEVICE *device ) } else { + if ((device->dwFlags & RIDEV_DEVNOTIFY) && device->hwndTarget) post_device_notifications( device ); if (pos == end || pos->usUsagePage != device->usUsagePage || pos->usUsage != device->usUsage) { memmove( pos + 1, pos, (char *)end - (char *)pos ); @@ -971,6 +996,8 @@ BOOL WINAPI NtUserRegisterRawInputDevices( const RAWINPUTDEVICE *devices, UINT d return FALSE; } + rawinput_update_device_list( TRUE ); + registered_devices = new_registered_devices; for (i = 0; i < device_count; ++i) register_rawinput_device( devices + i ); From 71dc1819f19a6fb2cedadd53e05d2c8736e2c83f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 2 May 2024 18:44:33 -0600 Subject: [PATCH 339/413] fshack: winex11.drv: Use a better fshack interpolation in xrender_blit(). CW-Bug-Id: #23755 --- dlls/winex11.drv/xrender.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/dlls/winex11.drv/xrender.c b/dlls/winex11.drv/xrender.c index da6c15b9676..87538ae3a1a 100644 --- a/dlls/winex11.drv/xrender.c +++ b/dlls/winex11.drv/xrender.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "windef.h" #include "winbase.h" @@ -183,6 +184,8 @@ MAKE_FUNCPTR(XRenderFindVisualFormat) MAKE_FUNCPTR(XRenderFreeGlyphSet) MAKE_FUNCPTR(XRenderFreePicture) MAKE_FUNCPTR(XRenderSetPictureClipRectangles) +MAKE_FUNCPTR(XRenderQueryFilters) +MAKE_FUNCPTR(XRenderSetPictureFilter) #ifdef HAVE_XRENDERCREATELINEARGRADIENT MAKE_FUNCPTR(XRenderCreateLinearGradient) #endif @@ -339,6 +342,8 @@ const struct gdi_dc_funcs *X11DRV_XRender_Init(void) LOAD_FUNCPTR(XRenderFreePicture); LOAD_FUNCPTR(XRenderSetPictureClipRectangles); LOAD_FUNCPTR(XRenderQueryExtension); + LOAD_FUNCPTR(XRenderQueryFilters); + LOAD_FUNCPTR(XRenderSetPictureFilter); #ifdef HAVE_XRENDERCREATELINEARGRADIENT LOAD_OPTIONAL_FUNCPTR(XRenderCreateLinearGradient); #endif @@ -1494,6 +1499,7 @@ static void xrender_blit( struct xrender_physdev *physdev, int op, Picture src_p Picture dst_pict, int x_src, int y_src, int width_src, int height_src, int x_dst, int y_dst, int width_dst, int height_dst, double xscale, double yscale ) { + const char *scale_filter = NULL; int x_offset, y_offset; HMONITOR monitor; @@ -1501,6 +1507,9 @@ static void xrender_blit( struct xrender_physdev *physdev, int op, Picture src_p if (fs_hack_mapping_required( monitor )) { double user_to_real_scale; + XFilters *filters; + int i; + POINT p; p.x = x_dst; p.y = y_dst; @@ -1509,10 +1518,24 @@ static void xrender_blit( struct xrender_physdev *physdev, int op, Picture src_p y_dst = p.y; user_to_real_scale = fs_hack_get_user_to_real_scale( monitor ); - width_dst *= user_to_real_scale; - height_dst *= user_to_real_scale; + width_dst = lround(width_dst * user_to_real_scale); + height_dst = lround(height_dst * user_to_real_scale); xscale /= user_to_real_scale; yscale /= user_to_real_scale; + if ((filters = pXRenderQueryFilters( gdi_display, physdev->x11dev->drawable ))) + { + for (i = 0; i < filters->nfilter; ++i) + { + if (!filters->filter[i]) continue; + if (!strcmp( filters->filter[i], "good" )) + { + scale_filter = "good"; + break; + } + if (!strcmp( filters->filter[i], "bilinear" )) scale_filter = "bilinear"; + } + XFree( filters ); + } } if (width_src < 0) @@ -1545,6 +1568,7 @@ static void xrender_blit( struct xrender_physdev *physdev, int op, Picture src_p x_offset = (xscale < 0) ? -width_dst : 0; y_offset = (yscale < 0) ? -height_dst : 0; set_xrender_transformation(src_pict, xscale, yscale, x_src, y_src); + if (scale_filter) pXRenderSetPictureFilter(gdi_display, src_pict, scale_filter, NULL, 0); } else { From dedc6993d75775924a313eac3a421f32f9d373df Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 2 May 2024 18:47:37 -0600 Subject: [PATCH 340/413] fshack: winex11.drv: Interpolate image in x11drv_surface_flush() with xrender. CW-Bug-Id: #23755 --- dlls/winex11.drv/bitblt.c | 21 ++++++--- dlls/winex11.drv/window.c | 4 +- dlls/winex11.drv/x11drv.h | 4 +- dlls/winex11.drv/xrender.c | 91 ++++++++++++++++++++++++++++++++++---- 4 files changed, 102 insertions(+), 18 deletions(-) diff --git a/dlls/winex11.drv/bitblt.c b/dlls/winex11.drv/bitblt.c index 5d2392fe450..68858c7cefa 100644 --- a/dlls/winex11.drv/bitblt.c +++ b/dlls/winex11.drv/bitblt.c @@ -1574,6 +1574,7 @@ DWORD get_pixmap_image( Pixmap pixmap, int width, int height, const XVisualInfo struct x11drv_window_surface { struct window_surface header; + HWND hwnd; Window window; GC gc; XImage *image; @@ -1962,12 +1963,17 @@ static void x11drv_surface_flush( struct window_surface *window_surface ) #ifdef HAVE_LIBXXSHM if (surface->shminfo.shmid != -1) - XShmPutImage( gdi_display, surface->window, surface->gc, surface->image, - coords.visrect.left, coords.visrect.top, - surface->header.rect.left + coords.visrect.left, - surface->header.rect.top + coords.visrect.top, - coords.visrect.right - coords.visrect.left, - coords.visrect.bottom - coords.visrect.top, False ); + { + if (!fs_hack_put_image_scaled( surface->hwnd, surface->window, surface->gc, surface->image, + surface->header.rect.left, surface->header.rect.top, + coords.width, coords.height, surface->is_argb )) + XShmPutImage( gdi_display, surface->window, surface->gc, surface->image, + coords.visrect.left, coords.visrect.top, + surface->header.rect.left + coords.visrect.left, + surface->header.rect.top + coords.visrect.top, + coords.visrect.right - coords.visrect.left, + coords.visrect.bottom - coords.visrect.top, False ); + } else #endif XPutImage( gdi_display, surface->window, surface->gc, surface->image, @@ -2024,7 +2030,7 @@ static const struct window_surface_funcs x11drv_surface_funcs = /*********************************************************************** * create_surface */ -struct window_surface *create_surface( Window window, const XVisualInfo *vis, const RECT *rect, +struct window_surface *create_surface( HWND hwnd, Window window, const XVisualInfo *vis, const RECT *rect, COLORREF color_key, BOOL use_alpha ) { const XPixmapFormatValues *format = pixmap_formats[vis->depth]; @@ -2047,6 +2053,7 @@ struct window_surface *create_surface( Window window, const XVisualInfo *vis, co surface->header.funcs = &x11drv_surface_funcs; surface->header.rect = *rect; surface->header.ref = 1; + surface->hwnd = hwnd; surface->window = window; surface->is_argb = (use_alpha && vis->depth == 32 && surface->info.bmiHeader.biCompression == BI_RGB); set_color_key( surface, color_key ); diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 3f64774d2cb..98723a302ed 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -3192,7 +3192,7 @@ BOOL X11DRV_WindowPosChanging( HWND hwnd, HWND insert_after, UINT swp_flags, if (!layered || !NtUserGetLayeredWindowAttributes( hwnd, &key, NULL, &flags ) || !(flags & LWA_COLORKEY)) key = CLR_INVALID; - *surface = create_surface( data->whole_window, &data->vis, &surface_rect, key, FALSE ); + *surface = create_surface( hwnd, data->whole_window, &data->vis, &surface_rect, key, FALSE ); done: release_win_data( data ); @@ -3648,7 +3648,7 @@ BOOL X11DRV_UpdateLayeredWindow( HWND hwnd, const UPDATELAYEREDWINDOWINFO *info, surface = data->surface; if (!surface || !EqualRect( &surface->rect, &rect )) { - data->surface = create_surface( data->whole_window, &data->vis, &rect, + data->surface = create_surface( hwnd, data->whole_window, &data->vis, &rect, color_key, data->use_alpha ); if (surface) window_surface_release( surface ); surface = data->surface; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index da610e060a6..2868325055b 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -275,7 +275,7 @@ extern Pixmap create_pixmap_from_image( HDC hdc, const XVisualInfo *vis, const B const struct gdi_image_bits *bits, UINT coloruse ); extern DWORD get_pixmap_image( Pixmap pixmap, int width, int height, const XVisualInfo *vis, BITMAPINFO *info, struct gdi_image_bits *bits ); -extern struct window_surface *create_surface( Window window, const XVisualInfo *vis, const RECT *rect, +extern struct window_surface *create_surface( HWND hwnd, Window window, const XVisualInfo *vis, const RECT *rect, COLORREF color_key, BOOL use_alpha ); extern void set_surface_color_key( struct window_surface *window_surface, COLORREF color_key ); extern HRGN expose_surface( struct window_surface *window_surface, const RECT *rect ); @@ -723,6 +723,8 @@ extern RECT fs_hack_get_real_virtual_screen(void); extern void fs_hack_init(void); extern const float *fs_hack_get_gamma_ramp( LONG *serial ); extern void fs_hack_set_gamma_ramp( const WORD *ramp ); +extern BOOL fs_hack_put_image_scaled( HWND hwnd, Window window, GC gc, XImage *image, unsigned int x_dst, unsigned int y_dst, + unsigned int width, unsigned int height, BOOL is_argb ); static inline void mirror_rect( const RECT *window_rect, RECT *rect ) { diff --git a/dlls/winex11.drv/xrender.c b/dlls/winex11.drv/xrender.c index 87538ae3a1a..9180827143d 100644 --- a/dlls/winex11.drv/xrender.c +++ b/dlls/winex11.drv/xrender.c @@ -50,6 +50,7 @@ WINE_DECLARE_DEBUG_CHANNEL(winediag); #include #include +#include #ifndef RepeatNone /* added in 0.10 */ #define RepeatNone 0 @@ -449,6 +450,33 @@ static enum wxr_format get_xrender_format_from_bitmapinfo( const BITMAPINFO *inf return WXR_INVALID_FORMAT; } +static enum wxr_format get_xrender_format_from_ximage( const XImage *image ) +{ + unsigned int i; + + switch (image->depth) + { + case 1: + return WXR_FORMAT_MONO; + case 4: + case 8: + break; + case 16: + case 24: + case 32: + for (i = 0; i < WXR_NB_FORMATS; i++) + { + if (image->depth == wxr_formats_template[i].depth && + image->red_mask == (wxr_formats_template[i].redMask << wxr_formats_template[i].red) && + image->green_mask == (wxr_formats_template[i].greenMask << wxr_formats_template[i].green) && + image->blue_mask == (wxr_formats_template[i].blueMask << wxr_formats_template[i].blue)) + return i; + } + break; + } + return WXR_INVALID_FORMAT; +} + /* Set the x/y scaling and x/y offsets in the transformation matrix of the source picture */ static void set_xrender_transformation(Picture src_pict, double xscale, double yscale, int xoffset, int yoffset) { @@ -1495,16 +1523,17 @@ static void fs_hack_draw_black_bars( HMONITOR monitor, Picture dst_pict ) } /* Helper function for (stretched) blitting using xrender */ -static void xrender_blit( struct xrender_physdev *physdev, int op, Picture src_pict, Picture mask_pict, - Picture dst_pict, int x_src, int y_src, int width_src, int height_src, int x_dst, - int y_dst, int width_dst, int height_dst, double xscale, double yscale ) +static void xrender_blit_fshack( HWND hwnd, Drawable drawable, int op, Picture src_pict, Picture mask_pict, + Picture dst_pict, int x_src, int y_src, int width_src, int height_src, int x_dst, + int y_dst, int width_dst, int height_dst, double xscale, double yscale ) { const char *scale_filter = NULL; int x_offset, y_offset; - HMONITOR monitor; + HMONITOR monitor = 0; + BOOL fs_hack; - monitor = fs_hack_monitor_from_hwnd( NtUserWindowFromDC( physdev->dev.hdc ) ); - if (fs_hack_mapping_required( monitor )) + fs_hack = hwnd && fs_hack_mapping_required( monitor = fs_hack_monitor_from_hwnd( hwnd )); + if (fs_hack) { double user_to_real_scale; XFilters *filters; @@ -1522,7 +1551,7 @@ static void xrender_blit( struct xrender_physdev *physdev, int op, Picture src_p height_dst = lround(height_dst * user_to_real_scale); xscale /= user_to_real_scale; yscale /= user_to_real_scale; - if ((filters = pXRenderQueryFilters( gdi_display, physdev->x11dev->drawable ))) + if ((filters = pXRenderQueryFilters( gdi_display, drawable ))) { for (i = 0; i < filters->nfilter; ++i) { @@ -1579,7 +1608,53 @@ static void xrender_blit( struct xrender_physdev *physdev, int op, Picture src_p pXRenderComposite( gdi_display, op, src_pict, mask_pict, dst_pict, x_offset, y_offset, 0, 0, x_dst, y_dst, width_dst, height_dst ); - if (fs_hack_mapping_required( monitor )) fs_hack_draw_black_bars( monitor, dst_pict ); + if (fs_hack) fs_hack_draw_black_bars( monitor, dst_pict ); +} + +static void xrender_blit( struct xrender_physdev *physdev, int op, Picture src_pict, Picture mask_pict, + Picture dst_pict, int x_src, int y_src, int width_src, int height_src, int x_dst, + int y_dst, int width_dst, int height_dst, double xscale, double yscale ) +{ + xrender_blit_fshack( NtUserWindowFromDC( physdev->dev.hdc ), physdev->x11dev->drawable, op, src_pict, mask_pict, + dst_pict, x_src, y_src, width_src, height_src, x_dst, y_dst, width_dst, height_dst, xscale, yscale ); +} + +BOOL fs_hack_put_image_scaled( HWND hwnd, Window window, GC gc, XImage *image, unsigned int x_dst, unsigned int y_dst, + unsigned int width, unsigned int height, BOOL is_argb ) +{ + Picture src_pict, dst_pict, mask_pict = 0; + struct x11drv_win_data *data; + XRenderPictureAttributes pa; + enum wxr_format src_format; + Pixmap pixmap; + BOOL fshack; + + if (default_format == WXR_INVALID_FORMAT) return FALSE; + if (!(data = get_win_data( hwnd ))) return FALSE; + fshack = data->fs_hack; + release_win_data( data ); + if (!fshack) return FALSE; + + if ((src_format = get_xrender_format_from_ximage( image )) == WXR_INVALID_FORMAT) + { + FIXME( "Unknown XImage format.\n"); + return FALSE; + } + + pixmap = XCreatePixmap( gdi_display, window, width, height, image->depth ); + gc = XCreateGC( gdi_display, pixmap, 0, NULL ); + XShmPutImage( gdi_display, pixmap, gc, image, 0, 0, 0, 0, width, height, False ); + XFreeGC( gdi_display, gc ); + src_pict = pXRenderCreatePicture( gdi_display, pixmap, pict_formats[src_format], 0, NULL ); + pa.subwindow_mode = IncludeInferiors; + dst_pict = pXRenderCreatePicture( gdi_display, window, pict_formats[default_format], CPSubwindowMode, &pa ); + if (!is_argb && pict_formats[default_format]->depth == 32) mask_pict = get_no_alpha_mask(); + xrender_blit_fshack( hwnd, window, PictOpSrc, src_pict, mask_pict, dst_pict, 0, 0, + width, height, x_dst, y_dst, width, height, 1.0, 1.0 ); + pXRenderFreePicture( gdi_display, src_pict ); + pXRenderFreePicture( gdi_display, dst_pict ); + XFreePixmap( gdi_display, pixmap ); + return TRUE; } /* Helper function for (stretched) mono->color blitting using xrender */ From 6c558cf0f98f4d7868c67e0e16cbe037d175abe1 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Fri, 3 May 2024 12:30:37 +0300 Subject: [PATCH 341/413] Revert "HACK: mfplat: Enable new media source by default for more games." This reverts commit b11119b3805815969955afeffbc5f1d109390095. --- dlls/mfplat/main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index 3d1fe615a5a..facb39ee3d6 100644 --- a/dlls/mfplat/main.c +++ b/dlls/mfplat/main.c @@ -6304,7 +6304,6 @@ static HRESULT resolver_create_gstreamer_handler(IMFByteStreamHandler **handler) if (!strcmp(sgi, "399810") /* Call of Cthulhu */) env = "1"; if (!strcmp(sgi, "606880") /* Greedfall */) env = "1"; if (!strcmp(sgi, "692850") /* Bloodstained */) env = "1"; - if (!strcmp(sgi, "934700") /* Dead Island 2 */) env = "1"; } if (env && atoi(env)) return CoCreateInstance(&CLSID_GStreamerByteStreamHandler2, NULL, CLSCTX_INPROC_SERVER, &IID_IMFByteStreamHandler, (void **)handler); From 93e737e8d5243ad34650ae72492656fdd3697d28 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 14 Feb 2024 10:48:03 -0600 Subject: [PATCH 342/413] Revert "explorer: Don't pop start menu on "minimize all windows" systray command." This reverts commit 1cf7a85d1288f67b3e677f3b03533d46da1d2775. --- programs/explorer/systray.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/programs/explorer/systray.c b/programs/explorer/systray.c index 84ae37d8b36..61490f7a325 100644 --- a/programs/explorer/systray.c +++ b/programs/explorer/systray.c @@ -1059,15 +1059,7 @@ static LRESULT WINAPI shell_traywnd_proc( HWND hwnd, UINT msg, WPARAM wparam, LP break; case WM_COMMAND: - if (HIWORD(wparam) == BN_CLICKED) - { - if (LOWORD(wparam) == 419) - { - FIXME( "Minimize all windows command is not supported.\n" ); - break; - } - click_taskbar_button( (HWND)lparam ); - } + if (HIWORD(wparam) == BN_CLICKED) click_taskbar_button( (HWND)lparam ); break; case WM_CONTEXTMENU: From 6a82acb20bd72649e9f5f6a5aee9a3523b02da47 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Fri, 3 May 2024 14:46:46 +0300 Subject: [PATCH 343/413] Revert "win32u: Only send mouse input in ReleaseCapture() when a window is captured." This reverts commit 17d457d46e67aa94fec823445fec67071876d873. --- dlls/user32/tests/win.c | 2 ++ dlls/win32u/input.c | 7 ++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 3bbb8cffed0..62e6b32f73b 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -13112,6 +13112,7 @@ static void test_ReleaseCapture(void) ok(ret, "ReleaseCapture failed, error %#lx.\n", GetLastError()); flush_events(TRUE); do_release_capture = FALSE; + todo_wine ok(wm_mousemove_count < 10, "Got too many WM_MOUSEMOVE.\n"); /* Test that ReleaseCapture() should send a WM_MOUSEMOVE if a window is captured */ @@ -13127,6 +13128,7 @@ static void test_ReleaseCapture(void) ret = ReleaseCapture(); ok(ret, "ReleaseCapture failed, error %#lx.\n", GetLastError()); flush_events(TRUE); + todo_wine ok(wm_mousemove_count == 0, "Got WM_MOUSEMOVE.\n"); ret = SetCursorPos(pt.x, pt.y); diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 9acdeb40388..69bc57f3ca4 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1803,13 +1803,10 @@ HWND WINAPI NtUserSetCapture( HWND hwnd ) */ BOOL release_capture(void) { - HWND previous = NULL; - BOOL ret; - - ret = set_capture_window( 0, 0, &previous ); + BOOL ret = set_capture_window( 0, 0, NULL ); /* Somebody may have missed some mouse movements */ - if (ret && previous) + if (ret) { INPUT input = { .type = INPUT_MOUSE }; input.mi.dwFlags = MOUSEEVENTF_MOVE; From 54b128a7effabaa5fe74cef8b711640af69b8a9b Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Fri, 3 May 2024 14:46:50 +0300 Subject: [PATCH 344/413] Revert "user32/tests: Add some ReleaseCapture() tests." This reverts commit a6fe061bfb1c1af6fa9f65248e1bda8b64fafd36. --- dlls/user32/tests/win.c | 108 ---------------------------------------- 1 file changed, 108 deletions(-) diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 62e6b32f73b..2fd31be3314 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -13030,113 +13030,6 @@ static void test_WM_NCCALCSIZE(void) DestroyWindow(hwnd); } -#define TRAY_MINIMIZE_ALL 419 -#define TRAY_MINIMIZE_ALL_UNDO 416 - -static void test_shell_tray(void) -{ - HWND hwnd, traywnd; - - if (!(traywnd = FindWindowA( "Shell_TrayWnd", NULL ))) - { - skip( "Shell_TrayWnd not found, skipping tests.\n" ); - return; - } - - hwnd = CreateWindowW( L"static", L"parent", WS_OVERLAPPEDWINDOW|WS_VISIBLE, - 100, 100, 200, 200, 0, 0, 0, NULL ); - ok( !!hwnd, "failed, error %lu.\n", GetLastError() ); - flush_events( TRUE ); - - ok( !IsIconic( hwnd ), "window is minimized.\n" ); - - SendMessageA( traywnd, WM_COMMAND, TRAY_MINIMIZE_ALL, 0xdeadbeef ); - flush_events( TRUE ); - todo_wine ok( IsIconic( hwnd ), "window is not minimized.\n" ); - - SendMessageA( traywnd, WM_COMMAND, TRAY_MINIMIZE_ALL_UNDO, 0xdeadbeef ); - flush_events( TRUE ); - ok( !IsIconic( hwnd ), "window is minimized.\n" ); - - DestroyWindow(hwnd); -} - -static int wm_mousemove_count; -static BOOL do_release_capture; - -static LRESULT WINAPI test_ReleaseCapture_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) -{ - if (msg == WM_MOUSEMOVE) - { - wm_mousemove_count++; - if (wm_mousemove_count >= 100) - return 1; - - if (do_release_capture) - ReleaseCapture(); - return 0; - } - return DefWindowProcA(hwnd, msg, wp, lp); -} - -static void test_ReleaseCapture(void) -{ - WNDCLASSA cls = {0}; - ATOM atom; - HWND hwnd; - POINT pt; - BOOL ret; - - cls.lpfnWndProc = test_ReleaseCapture_proc; - cls.hInstance = GetModuleHandleA(0); - cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW); - cls.hbrBackground = GetStockObject(BLACK_BRUSH); - cls.lpszClassName = "test_ReleaseCapture_class"; - atom = RegisterClassA(&cls); - ok(!!atom, "RegisterClassA failed, error %#lx.\n", GetLastError()); - - hwnd = CreateWindowExA(WS_EX_TOPMOST, cls.lpszClassName, "", WS_POPUP | WS_VISIBLE, 100, 100, - 100, 100, NULL, NULL, 0, NULL); - ok(!!hwnd, "CreateWindowA failed, error %#lx.\n", GetLastError()); - ret = SetForegroundWindow(hwnd); - ok(ret, "SetForegroundWindow failed, error %#lx.\n", GetLastError()); - GetCursorPos(&pt); - ret = SetCursorPos(150, 150); - ok(ret, "SetCursorPos failed, error %#lx.\n", GetLastError()); - flush_events(TRUE); - - /* Test a Wine bug that WM_MOUSEMOVE is post too many times when calling ReleaseCapture() during - * handling WM_MOUSEMOVE and the cursor is on the window */ - do_release_capture = TRUE; - ret = ReleaseCapture(); - ok(ret, "ReleaseCapture failed, error %#lx.\n", GetLastError()); - flush_events(TRUE); - do_release_capture = FALSE; - todo_wine - ok(wm_mousemove_count < 10, "Got too many WM_MOUSEMOVE.\n"); - - /* Test that ReleaseCapture() should send a WM_MOUSEMOVE if a window is captured */ - SetCapture(hwnd); - wm_mousemove_count = 0; - ret = ReleaseCapture(); - ok(ret, "ReleaseCapture failed, error %#lx.\n", GetLastError()); - flush_events(TRUE); - ok(wm_mousemove_count == 1, "Got no WM_MOUSEMOVE.\n"); - - /* Test that ReleaseCapture() shouldn't send WM_MOUSEMOVE if no window is captured */ - wm_mousemove_count = 0; - ret = ReleaseCapture(); - ok(ret, "ReleaseCapture failed, error %#lx.\n", GetLastError()); - flush_events(TRUE); - todo_wine - ok(wm_mousemove_count == 0, "Got WM_MOUSEMOVE.\n"); - - ret = SetCursorPos(pt.x, pt.y); - ok(ret, "SetCursorPos failed, error %#lx.\n", GetLastError()); - DestroyWindow(hwnd); - UnregisterClassA(cls.lpszClassName, GetModuleHandleA(0)); -} - START_TEST(win) { char **argv; @@ -13319,7 +13212,6 @@ START_TEST(win) test_cancel_mode(); test_DragDetect(); test_WM_NCCALCSIZE(); - test_ReleaseCapture(); /* add the tests above this line */ if (hhook) UnhookWindowsHookEx(hhook); From 195d861d7c932d6cdfc5984af427af9ded90f135 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 8 Jan 2024 21:05:35 -0600 Subject: [PATCH 345/413] explorer: Don't pop start menu on "minimize all windows" systray command. (cherry picked from commit d66fe6206d950eeeabfcf408ae4d199a558150f3) CW-Bug-Id: #23178 --- dlls/user32/tests/win.c | 29 ++++++++++++++++++++++++++++- programs/explorer/systray.c | 12 +++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 2fd31be3314..44ea1c04e61 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -1796,7 +1796,7 @@ static void test_shell_window(void) WaitForSingleObject(hthread, INFINITE); - DeleteObject(hthread); + CloseHandle(hthread); CloseDesktop(hdesk); } @@ -13030,6 +13030,32 @@ static void test_WM_NCCALCSIZE(void) DestroyWindow(hwnd); } +#define TRAY_MINIMIZE_ALL 419 + +static void test_shell_tray(void) +{ + HWND hwnd, traywnd; + + if (!(traywnd = FindWindowA( "Shell_TrayWnd", NULL ))) + { + skip( "Shell_TrayWnd not found, skipping tests.\n" ); + return; + } + + hwnd = CreateWindowW( L"static", L"parent", WS_OVERLAPPEDWINDOW|WS_VISIBLE, + 100, 100, 200, 200, 0, 0, 0, NULL ); + ok( !!hwnd, "failed, error %lu.\n", GetLastError() ); + flush_events( TRUE ); + + ok( !IsIconic( hwnd ), "window is minimized.\n" ); + + SendMessageA( traywnd, WM_COMMAND, TRAY_MINIMIZE_ALL, 0xdeadbeef ); + flush_events( TRUE ); + todo_wine ok( IsIconic( hwnd ), "window is not minimized.\n" ); + + DestroyWindow(hwnd); +} + START_TEST(win) { char **argv; @@ -13226,4 +13252,5 @@ START_TEST(win) test_topmost(); test_shell_window(); + test_shell_tray(); } diff --git a/programs/explorer/systray.c b/programs/explorer/systray.c index 61490f7a325..330dea710fc 100644 --- a/programs/explorer/systray.c +++ b/programs/explorer/systray.c @@ -33,6 +33,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(systray); +#define TRAY_MINIMIZE_ALL 419 + struct notify_data /* platform-independent format for NOTIFYICONDATA */ { LONG hWnd; @@ -1059,7 +1061,15 @@ static LRESULT WINAPI shell_traywnd_proc( HWND hwnd, UINT msg, WPARAM wparam, LP break; case WM_COMMAND: - if (HIWORD(wparam) == BN_CLICKED) click_taskbar_button( (HWND)lparam ); + if (HIWORD(wparam) == BN_CLICKED) + { + if (LOWORD(wparam) == TRAY_MINIMIZE_ALL) + { + FIXME( "Shell command %u is not supported.\n", LOWORD(wparam) ); + break; + } + click_taskbar_button( (HWND)lparam ); + } break; case WM_CONTEXTMENU: From f6b6cee21cb2dabf8a6286a96840c4ba8fc5e6e8 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 6 Feb 2024 10:04:09 -0600 Subject: [PATCH 346/413] explorer: Don't pop start menu on "undo minimize all windows" systray command. (cherry picked from commit 5da459f1f2471365bc98078be1a3a1f84ef1a3dd) CW-Bug-Id: #23178 --- dlls/user32/tests/win.c | 5 +++++ programs/explorer/systray.c | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 44ea1c04e61..69c8d945951 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -13031,6 +13031,7 @@ static void test_WM_NCCALCSIZE(void) } #define TRAY_MINIMIZE_ALL 419 +#define TRAY_MINIMIZE_ALL_UNDO 416 static void test_shell_tray(void) { @@ -13053,6 +13054,10 @@ static void test_shell_tray(void) flush_events( TRUE ); todo_wine ok( IsIconic( hwnd ), "window is not minimized.\n" ); + SendMessageA( traywnd, WM_COMMAND, TRAY_MINIMIZE_ALL_UNDO, 0xdeadbeef ); + flush_events( TRUE ); + ok( !IsIconic( hwnd ), "window is minimized.\n" ); + DestroyWindow(hwnd); } diff --git a/programs/explorer/systray.c b/programs/explorer/systray.c index 330dea710fc..54f1abbb30f 100644 --- a/programs/explorer/systray.c +++ b/programs/explorer/systray.c @@ -34,6 +34,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(systray); #define TRAY_MINIMIZE_ALL 419 +#define TRAY_MINIMIZE_ALL_UNDO 416 struct notify_data /* platform-independent format for NOTIFYICONDATA */ { @@ -1063,7 +1064,7 @@ static LRESULT WINAPI shell_traywnd_proc( HWND hwnd, UINT msg, WPARAM wparam, LP case WM_COMMAND: if (HIWORD(wparam) == BN_CLICKED) { - if (LOWORD(wparam) == TRAY_MINIMIZE_ALL) + if (LOWORD(wparam) == TRAY_MINIMIZE_ALL || LOWORD(wparam) == TRAY_MINIMIZE_ALL_UNDO) { FIXME( "Shell command %u is not supported.\n", LOWORD(wparam) ); break; From 1e2448afbaf7e19c075b4e5da2ba38b04acc78da Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Fri, 8 Mar 2024 15:21:54 +0800 Subject: [PATCH 347/413] user32/tests: Add some ReleaseCapture() tests. (cherry picked from commit d47b13c45aea8809b46ac64e2cdbb300b760549b) CW-Bug-Id: #23531 --- dlls/user32/tests/win.c | 77 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 69c8d945951..822c266eba6 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -13061,6 +13061,82 @@ static void test_shell_tray(void) DestroyWindow(hwnd); } +static int wm_mousemove_count; +static BOOL do_release_capture; + +static LRESULT WINAPI test_ReleaseCapture_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) +{ + if (msg == WM_MOUSEMOVE) + { + wm_mousemove_count++; + if (wm_mousemove_count >= 100) + return 1; + + if (do_release_capture) + ReleaseCapture(); + return 0; + } + return DefWindowProcA(hwnd, msg, wp, lp); +} + +static void test_ReleaseCapture(void) +{ + WNDCLASSA cls = {0}; + ATOM atom; + HWND hwnd; + POINT pt; + BOOL ret; + + cls.lpfnWndProc = test_ReleaseCapture_proc; + cls.hInstance = GetModuleHandleA(0); + cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW); + cls.hbrBackground = GetStockObject(BLACK_BRUSH); + cls.lpszClassName = "test_ReleaseCapture_class"; + atom = RegisterClassA(&cls); + ok(!!atom, "RegisterClassA failed, error %#lx.\n", GetLastError()); + + hwnd = CreateWindowExA(WS_EX_TOPMOST, cls.lpszClassName, "", WS_POPUP | WS_VISIBLE, 100, 100, + 100, 100, NULL, NULL, 0, NULL); + ok(!!hwnd, "CreateWindowA failed, error %#lx.\n", GetLastError()); + ret = SetForegroundWindow(hwnd); + ok(ret, "SetForegroundWindow failed, error %#lx.\n", GetLastError()); + GetCursorPos(&pt); + ret = SetCursorPos(150, 150); + ok(ret, "SetCursorPos failed, error %#lx.\n", GetLastError()); + flush_events(TRUE); + + /* Test a Wine bug that WM_MOUSEMOVE is post too many times when calling ReleaseCapture() during + * handling WM_MOUSEMOVE and the cursor is on the window */ + do_release_capture = TRUE; + ret = ReleaseCapture(); + ok(ret, "ReleaseCapture failed, error %#lx.\n", GetLastError()); + flush_events(TRUE); + do_release_capture = FALSE; + todo_wine + ok(wm_mousemove_count < 10, "Got too many WM_MOUSEMOVE.\n"); + + /* Test that ReleaseCapture() should send a WM_MOUSEMOVE if a window is captured */ + SetCapture(hwnd); + wm_mousemove_count = 0; + ret = ReleaseCapture(); + ok(ret, "ReleaseCapture failed, error %#lx.\n", GetLastError()); + flush_events(TRUE); + ok(wm_mousemove_count == 1, "Got no WM_MOUSEMOVE.\n"); + + /* Test that ReleaseCapture() shouldn't send WM_MOUSEMOVE if no window is captured */ + wm_mousemove_count = 0; + ret = ReleaseCapture(); + ok(ret, "ReleaseCapture failed, error %#lx.\n", GetLastError()); + flush_events(TRUE); + todo_wine + ok(wm_mousemove_count == 0, "Got WM_MOUSEMOVE.\n"); + + ret = SetCursorPos(pt.x, pt.y); + ok(ret, "SetCursorPos failed, error %#lx.\n", GetLastError()); + DestroyWindow(hwnd); + UnregisterClassA(cls.lpszClassName, GetModuleHandleA(0)); +} + START_TEST(win) { char **argv; @@ -13243,6 +13319,7 @@ START_TEST(win) test_cancel_mode(); test_DragDetect(); test_WM_NCCALCSIZE(); + test_ReleaseCapture(); /* add the tests above this line */ if (hhook) UnhookWindowsHookEx(hhook); From e8172fc9d5dbfcb88c92f7e8832ea773bccf38c4 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Fri, 8 Mar 2024 16:38:16 +0800 Subject: [PATCH 348/413] win32u: Only send mouse input in ReleaseCapture() when a window is captured. Fix a regression from "bb496ea8 - server: Always queue mouse messages delivered to another window." Fix ETHER VAPOR Remaster (214570) launches to black screen when the cursor is in the game window. The game calls ReleaseCapture() when handling WM_MOUSEMOVE. After bb496ea8, WM_MOUSEMOVE is always queued because the message window is NULL. So ReleaseCapture() ends up queuing another WM_MOUSEMOVE. So the game ends up handling infinite WM_MOUSEMOVE messages at startup and is not able to do anything. (cherry picked from commit 818d9a12100bfa6e574e88cd1567985b5884d002) CW-Bug-Id: #23531 --- dlls/user32/tests/win.c | 2 -- dlls/win32u/input.c | 7 +++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 822c266eba6..8f5d1c0b973 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -13112,7 +13112,6 @@ static void test_ReleaseCapture(void) ok(ret, "ReleaseCapture failed, error %#lx.\n", GetLastError()); flush_events(TRUE); do_release_capture = FALSE; - todo_wine ok(wm_mousemove_count < 10, "Got too many WM_MOUSEMOVE.\n"); /* Test that ReleaseCapture() should send a WM_MOUSEMOVE if a window is captured */ @@ -13128,7 +13127,6 @@ static void test_ReleaseCapture(void) ret = ReleaseCapture(); ok(ret, "ReleaseCapture failed, error %#lx.\n", GetLastError()); flush_events(TRUE); - todo_wine ok(wm_mousemove_count == 0, "Got WM_MOUSEMOVE.\n"); ret = SetCursorPos(pt.x, pt.y); diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 69bc57f3ca4..9acdeb40388 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1803,10 +1803,13 @@ HWND WINAPI NtUserSetCapture( HWND hwnd ) */ BOOL release_capture(void) { - BOOL ret = set_capture_window( 0, 0, NULL ); + HWND previous = NULL; + BOOL ret; + + ret = set_capture_window( 0, 0, &previous ); /* Somebody may have missed some mouse movements */ - if (ret) + if (ret && previous) { INPUT input = { .type = INPUT_MOUSE }; input.mi.dwFlags = MOUSEEVENTF_MOVE; From 14952dfe6cb7b5a5bd0b1fc2724ea0d88fdf1b06 Mon Sep 17 00:00:00 2001 From: Andrew Conrad Date: Fri, 3 May 2024 19:34:08 -0700 Subject: [PATCH 349/413] winebus.sys: Enable hidraw for VKB Gunfighter SCG. Link: https://github.com/ValveSoftware/wine/pull/236 --- dlls/winebus.sys/unixlib.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/winebus.sys/unixlib.c b/dlls/winebus.sys/unixlib.c index 223e96f3821..09d3dd64c0b 100644 --- a/dlls/winebus.sys/unixlib.c +++ b/dlls/winebus.sys/unixlib.c @@ -115,6 +115,8 @@ static BOOL is_vkb_controller(WORD vid, WORD pid, INT buttons) if (buttons == 128) return TRUE; /* if customized, less than 128 buttons may be shown, decide by PID */ + if (pid == 0x0126) return TRUE; /* VKB-Sim Space Gunfighter */ + if (pid == 0x0127) return TRUE; /* VKB-Sim Space Gunfighter L */ if (pid == 0x0200) return TRUE; /* VKBsim Gladiator EVO Right Grip */ if (pid == 0x0201) return TRUE; /* VKBsim Gladiator EVO Left Grip */ return FALSE; From 424917c2c4d3a7f2490bd47826bed89fcbde42bb Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Sat, 4 May 2024 12:47:09 -0600 Subject: [PATCH 350/413] win32u: Update last message time in NtUserGetRawInputBuffer(). --- dlls/win32u/rawinput.c | 3 +++ server/protocol.def | 1 + server/queue.c | 1 + 3 files changed, 5 insertions(+) diff --git a/dlls/win32u/rawinput.c b/dlls/win32u/rawinput.c index 35c547c695e..e730e08ad97 100644 --- a/dlls/win32u/rawinput.c +++ b/dlls/win32u/rawinput.c @@ -677,6 +677,7 @@ UINT WINAPI NtUserGetRawInputDeviceInfo( HANDLE handle, UINT command, void *data */ UINT WINAPI NtUserGetRawInputBuffer( RAWINPUT *data, UINT *data_size, UINT header_size ) { + struct user_thread_info *thread_info; static int cached_clear_qs_rawinput = -1; unsigned int count = 0, remaining, rawinput_size, next_size, overhead; struct rawinput_thread_data *thread_data; @@ -728,6 +729,7 @@ UINT WINAPI NtUserGetRawInputBuffer( RAWINPUT *data, UINT *data_size, UINT heade cached_clear_qs_rawinput = (sgi = getenv( "SteamGameId" )) && !strcmp( sgi, "1172470" ); } + thread_info = get_user_thread_info(); /* first RAWINPUT block in the buffer is used for WM_INPUT message data */ msg_data = (struct hardware_msg_data *)NEXTRAWINPUTBLOCK(rawinput); SERVER_START_REQ( get_rawinput_buffer ) @@ -739,6 +741,7 @@ UINT WINAPI NtUserGetRawInputBuffer( RAWINPUT *data, UINT *data_size, UINT heade if (wine_server_call( req )) return ~0u; next_size = reply->next_size; count = reply->count; + if (count) thread_info->client_info.message_time = reply->last_message_time; } SERVER_END_REQ; diff --git a/server/protocol.def b/server/protocol.def index d8d79e7c6ad..d611701e0ab 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3894,6 +3894,7 @@ struct handle_info @REPLY data_size_t next_size; /* minimum size to get next message data */ unsigned int count; + unsigned int last_message_time; VARARG(data,bytes); @END diff --git a/server/queue.c b/server/queue.c index 6206857d46c..0ed30b6256f 100644 --- a/server/queue.c +++ b/server/queue.c @@ -3970,6 +3970,7 @@ DECL_HANDLER(get_rawinput_buffer) } memcpy( buf + pos, data, data->size ); + reply->last_message_time = msg->time; list_remove( &msg->entry ); free_message( msg ); From f2a54d16aa2b5efd6f4344a872b844578cded89a Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 6 May 2024 19:39:15 -0600 Subject: [PATCH 351/413] kernelbase: HACK: Force angle d3d9 for Alpha League. CW-Bug-Id: #23761 --- dlls/kernelbase/process.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index 98dbbe657c4..0898e30f603 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -616,6 +616,7 @@ static const WCHAR *hack_append_command_line( const WCHAR *cmd ) {L"Everquest F2P\\LaunchPad.exe", L" --use-gl=swiftshader"}, {L"Red Tie Runner.exe", L" --use-angle=gl"}, {L"UnrealCEFSubProcess.exe", L" --use-gl=swiftshader", "2316580"}, + {L"UnrealCEFSubProcess.exe", L" --use-angle=d3d9", "2684500"}, {L"\\EACefSubProcess.exe", L" --use-angle=vulkan"}, }; unsigned int i; From d9dadb144bdb0a96caf27bf521f6cc13d5e30173 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 6 May 2024 19:43:24 -0600 Subject: [PATCH 352/413] ntdll: HACK: Enable WINE_SIMULATE_WRITECOPY for Lovey-Dovey Lockdown. CW-Bug-Id: #23763 --- dlls/ntdll/unix/loader.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 4f8a84c6080..f8695a70deb 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -2113,6 +2113,7 @@ static void hacks_init(void) || !strcmp(sgi, "391150") /* Red Tie Runner */ || !strcmp(sgi, "2152990") /* Dinogen Online */ || !strcmp(sgi, "2176450") /* Mr. Hopp's Playhouse 3 */ + || !strcmp(sgi, "2329630") /* Lovey-Dovey Lockdown */ || !strcmp(sgi, "2361360"); /* Hentai Maid Memories */ if (sgi) wine_allocs_2g_limit = !strcmp(sgi, "359870"); From 4683fb12f5d079da7f99d4b17ce0a4ce72f90049 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 7 May 2024 15:25:50 -0600 Subject: [PATCH 353/413] bcrypt: Support RSA/PKCS1 signatures with unspecified hash algorithm. CW-Bug-Id: #23773 --- dlls/bcrypt/gnutls.c | 17 +++++++++++------ dlls/bcrypt/tests/bcrypt.c | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c index 115219a64f6..0a921279458 100644 --- a/dlls/bcrypt/gnutls.c +++ b/dlls/bcrypt/gnutls.c @@ -2263,9 +2263,9 @@ static NTSTATUS pubkey_set_rsa_pss_params( gnutls_pubkey_t key, gnutls_digest_al static NTSTATUS key_asymmetric_verify( void *args ) { #ifdef GNUTLS_VERIFY_ALLOW_BROKEN - static const unsigned int verify_flags = GNUTLS_VERIFY_ALLOW_BROKEN; + unsigned int verify_flags = GNUTLS_VERIFY_ALLOW_BROKEN; #else - static const unsigned int verify_flags = 0; + unsigned int verify_flags = 0; #endif const struct key_asymmetric_verify_params *params = args; struct key *key = params->key; @@ -2306,8 +2306,12 @@ static NTSTATUS key_asymmetric_verify( void *args ) BCRYPT_PKCS1_PADDING_INFO *info = params->padding; if (!info) return STATUS_INVALID_PARAMETER; - if (!info->pszAlgId) return STATUS_INVALID_SIGNATURE; - if ((hash_alg = get_digest_from_id(info->pszAlgId)) == GNUTLS_DIG_UNKNOWN) + if (!info->pszAlgId) + { + hash_alg = GNUTLS_DIG_UNKNOWN; + verify_flags |= GNUTLS_VERIFY_USE_TLS1_RSA; + } + else if ((hash_alg = get_digest_from_id(info->pszAlgId)) == GNUTLS_DIG_UNKNOWN) { FIXME( "hash algorithm %s not supported\n", debugstr_w(info->pszAlgId) ); return STATUS_NOT_SUPPORTED; @@ -2498,12 +2502,13 @@ static NTSTATUS key_asymmetric_sign( void *args ) { BCRYPT_PKCS1_PADDING_INFO *pad = params->padding; - if (!pad || !pad->pszAlgId) + if (!pad) { WARN( "padding info not found\n" ); return STATUS_INVALID_PARAMETER; } - if ((hash_alg = get_digest_from_id( pad->pszAlgId )) == GNUTLS_DIG_UNKNOWN) + if (!pad->pszAlgId) hash_alg = GNUTLS_DIG_UNKNOWN; + else if ((hash_alg = get_digest_from_id( pad->pszAlgId )) == GNUTLS_DIG_UNKNOWN) { FIXME( "hash algorithm %s not recognized\n", debugstr_w(pad->pszAlgId) ); return STATUS_NOT_SUPPORTED; diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index 511ee7634b6..06dc0182d78 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -2696,6 +2696,21 @@ static void test_RSA(void) ret = BCryptVerifySignature(key, &pad, hash, sizeof(hash), sig, len, BCRYPT_PAD_PKCS1); ok(!ret, "BCryptVerifySignature failed: %#lx\n", ret); + pad.pszAlgId = NULL; + memset(sig, 0, sizeof(sig)); + len = 0; + ret = BCryptSignHash(key, &pad, hash, sizeof(hash), sig, sizeof(sig), &len, BCRYPT_PAD_PKCS1); + ok(!ret, "got %#lx\n", ret); + ok(len == 256, "got %lu\n", len); + + pad.pszAlgId = BCRYPT_SHA1_ALGORITHM; + ret = BCryptVerifySignature(key, &pad, hash, sizeof(hash), sig, len, BCRYPT_PAD_PKCS1); + ok(ret == STATUS_INVALID_SIGNATURE, "BCryptVerifySignature failed: %#lx, len %ld\n", ret, len); + + pad.pszAlgId = NULL; + ret = BCryptVerifySignature(key, &pad, hash, sizeof(hash), sig, len, BCRYPT_PAD_PKCS1); + ok(!ret, "BCryptVerifySignature failed: %#lx, len %ld\n", ret, len); + pad_pss.pszAlgId = BCRYPT_SHA384_ALGORITHM; pad_pss.cbSalt = 48; memset(sig_pss, 0, sizeof(sig_pss)); @@ -2843,6 +2858,10 @@ static void test_RSA_SIGN(void) ret = BCryptExportKey(key, NULL, BCRYPT_RSAPRIVATE_BLOB, buf2, sizeof(buf2), &size, 0); ok(ret == STATUS_INVALID_PARAMETER, "got %#lx\n", ret); + pad.pszAlgId = NULL; + ret = BCryptVerifySignature(key, &pad, rsaHash, sizeof(rsaHash), rsaSignature, sizeof(rsaSignature), BCRYPT_PAD_PKCS1); + ok(ret == STATUS_INVALID_SIGNATURE, "BCryptVerifySignature failed: %#lx\n", ret); + pad.pszAlgId = BCRYPT_SHA1_ALGORITHM; ret = BCryptVerifySignature(key, &pad, rsaHash, sizeof(rsaHash), rsaSignature, sizeof(rsaSignature), BCRYPT_PAD_PKCS1); ok(!ret, "BCryptVerifySignature failed: %#lx\n", ret); From 1f07f6d16366bfbc3b44c38d73aa428d5425ec83 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 8 May 2024 12:57:36 -0600 Subject: [PATCH 354/413] fixup! ntdll: Use UFFD for write watches support if available. Open UFFD fd with UFFD_USER_MODE_ONLY. CW-Bug-Id: #23524 --- dlls/ntdll/unix/uffd_tmp_defs.h | 2 ++ dlls/ntdll/unix/virtual.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/dlls/ntdll/unix/uffd_tmp_defs.h b/dlls/ntdll/unix/uffd_tmp_defs.h index f79c34c6145..d9f7a286c58 100644 --- a/dlls/ntdll/unix/uffd_tmp_defs.h +++ b/dlls/ntdll/unix/uffd_tmp_defs.h @@ -1,6 +1,8 @@ #ifndef __UFFD_TMP_DEFS__ #define __UFFD_TMP_DEFS__ +#define UFFD_USER_MODE_ONLY 1 + #ifdef __x86_64__ #define __NR_userfaultfd 323 #else diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 6c1dc3a8b88..62391642d82 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -400,7 +400,7 @@ static void kernel_writewatch_init(void) { struct uffdio_api uffdio_api; - uffd_fd = syscall( __NR_userfaultfd, O_CLOEXEC | O_NONBLOCK ); + uffd_fd = syscall( __NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY ); if (uffd_fd == -1) { kernel_writewatch_softdirty_init(); From af1fe952cf9e70546610099634eaae46e309b58f Mon Sep 17 00:00:00 2001 From: Anna Lasky Date: Wed, 8 May 2024 20:26:51 -0500 Subject: [PATCH 355/413] HACK: mfplat: Enable the new media source for Twisted Sails CW-Bug-ID: #20833 --- dlls/mfplat/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index facb39ee3d6..f05c221d389 100644 --- a/dlls/mfplat/main.c +++ b/dlls/mfplat/main.c @@ -6304,6 +6304,7 @@ static HRESULT resolver_create_gstreamer_handler(IMFByteStreamHandler **handler) if (!strcmp(sgi, "399810") /* Call of Cthulhu */) env = "1"; if (!strcmp(sgi, "606880") /* Greedfall */) env = "1"; if (!strcmp(sgi, "692850") /* Bloodstained */) env = "1"; + if (!strmcp(sgi, "782630") /* Twisted Sails */) env = "1"; } if (env && atoi(env)) return CoCreateInstance(&CLSID_GStreamerByteStreamHandler2, NULL, CLSCTX_INPROC_SERVER, &IID_IMFByteStreamHandler, (void **)handler); From 1c0c586c4c80cf20dd0430606fb28f4f222f2e3d Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 8 May 2024 19:01:42 -0600 Subject: [PATCH 356/413] fixup! win32u: Use font AA flags when querying glyph outline with GGO_METRICS. CW-Bug-Id: #23776 --- dlls/win32u/freetype.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dlls/win32u/freetype.c b/dlls/win32u/freetype.c index 880b1f8603b..09dc3f14184 100644 --- a/dlls/win32u/freetype.c +++ b/dlls/win32u/freetype.c @@ -3479,8 +3479,7 @@ static UINT freetype_get_glyph_outline( struct gdi_font *font, UINT glyph, UINT matrices = get_transform_matrices( font, tategaki, lpmat, transform_matrices ); - if (aa_flags && (format & ~GGO_GLYPH_INDEX) == GGO_METRICS) - effective_format = aa_flags | (format & GGO_GLYPH_INDEX); + if (aa_flags) effective_format = aa_flags | (format & (GGO_GLYPH_INDEX | GGO_UNHINTED)); vertical_metrics = (tategaki && FT_HAS_VERTICAL(ft_face)); /* there is a freetype bug where vertical metrics are only properly scaled and correct in 2.4.0 or greater */ From c31f496c7e0ddd4fd8a41acbfe796b8e6063158f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 8 May 2024 20:01:37 -0600 Subject: [PATCH 357/413] fixup! HACK: mfplat: Enable the new media source for Twisted Sails --- dlls/mfplat/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index f05c221d389..29ebc46860d 100644 --- a/dlls/mfplat/main.c +++ b/dlls/mfplat/main.c @@ -6304,7 +6304,7 @@ static HRESULT resolver_create_gstreamer_handler(IMFByteStreamHandler **handler) if (!strcmp(sgi, "399810") /* Call of Cthulhu */) env = "1"; if (!strcmp(sgi, "606880") /* Greedfall */) env = "1"; if (!strcmp(sgi, "692850") /* Bloodstained */) env = "1"; - if (!strmcp(sgi, "782630") /* Twisted Sails */) env = "1"; + if (!strcmp(sgi, "782630") /* Twisted Sails */) env = "1"; } if (env && atoi(env)) return CoCreateInstance(&CLSID_GStreamerByteStreamHandler2, NULL, CLSCTX_INPROC_SERVER, &IID_IMFByteStreamHandler, (void **)handler); From 293ab541148186c00f546f8e651aae16b03f1690 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 8 May 2024 21:12:37 -0600 Subject: [PATCH 358/413] fixup! win32u: Use font AA flags when querying glyph outline with GGO_METRICS. CW-Bug-Id: #23776 --- dlls/win32u/freetype.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dlls/win32u/freetype.c b/dlls/win32u/freetype.c index 09dc3f14184..8a56833e6ad 100644 --- a/dlls/win32u/freetype.c +++ b/dlls/win32u/freetype.c @@ -3454,6 +3454,12 @@ static FT_Int get_load_flags( UINT format, BOOL vertical_metrics, BOOL force_no_ return load_flags; } +static BOOL is_curve_format(UINT format) +{ + format &= ~(GGO_GLYPH_INDEX | GGO_UNHINTED); + return format == GGO_BEZIER || format == GGO_NATIVE; +} + /************************************************************* * freetype_get_glyph_outline */ @@ -3479,7 +3485,7 @@ static UINT freetype_get_glyph_outline( struct gdi_font *font, UINT glyph, UINT matrices = get_transform_matrices( font, tategaki, lpmat, transform_matrices ); - if (aa_flags) effective_format = aa_flags | (format & (GGO_GLYPH_INDEX | GGO_UNHINTED)); + if (aa_flags && !is_curve_format( format )) effective_format = aa_flags | (format & GGO_GLYPH_INDEX); vertical_metrics = (tategaki && FT_HAS_VERTICAL(ft_face)); /* there is a freetype bug where vertical metrics are only properly scaled and correct in 2.4.0 or greater */ From ee54561f790d19a19f179a4ca300c8a01e607118 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Fri, 10 May 2024 12:58:30 +0300 Subject: [PATCH 359/413] winevulkan: Update vk.xml to 1.3.285. --- dlls/winevulkan/make_vulkan | 2 +- dlls/winevulkan/vk.xml | 513 ++++++++++++++++++++++-------------- 2 files changed, 317 insertions(+), 198 deletions(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 467eee61c2a..4194df3a3dd 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -64,7 +64,7 @@ from enum import Enum LOGGER = logging.Logger("vulkan") LOGGER.addHandler(logging.StreamHandler()) -VK_XML_VERSION = "1.3.279" +VK_XML_VERSION = "1.3.285" WINE_VK_VERSION = (1, 3) # Filenames to create. diff --git a/dlls/winevulkan/vk.xml b/dlls/winevulkan/vk.xml index ee9221e9eb3..aa8c8ee2869 100644 --- a/dlls/winevulkan/vk.xml +++ b/dlls/winevulkan/vk.xml @@ -67,7 +67,7 @@ branch of the member gitlab server. - + @@ -175,7 +175,7 @@ branch of the member gitlab server. #define VKSC_API_VERSION_1_0 VK_MAKE_API_VERSION(VKSC_API_VARIANT, 1, 0, 0)// Patch version should always be set to 0 // Version of this file -#define VK_HEADER_VERSION 279 +#define VK_HEADER_VERSION 285 // Complete version of this file #define VK_HEADER_VERSION_COMPLETE VK_MAKE_API_VERSION(0, 1, 3, VK_HEADER_VERSION) // Version of this file @@ -237,31 +237,31 @@ typedef void CAMetalLayer; #endif #ifdef __OBJC__ @protocol MTLDevice; -typedef id<MTLDevice> MTLDevice_id; +typedef __unsafe_unretained id<MTLDevice> MTLDevice_id; #else typedef void* MTLDevice_id; #endif #ifdef __OBJC__ @protocol MTLCommandQueue; -typedef id<MTLCommandQueue> MTLCommandQueue_id; +typedef __unsafe_unretained id<MTLCommandQueue> MTLCommandQueue_id; #else typedef void* MTLCommandQueue_id; #endif #ifdef __OBJC__ @protocol MTLBuffer; -typedef id<MTLBuffer> MTLBuffer_id; +typedef __unsafe_unretained id<MTLBuffer> MTLBuffer_id; #else typedef void* MTLBuffer_id; #endif #ifdef __OBJC__ @protocol MTLTexture; -typedef id<MTLTexture> MTLTexture_id; +typedef __unsafe_unretained id<MTLTexture> MTLTexture_id; #else typedef void* MTLTexture_id; #endif #ifdef __OBJC__ @protocol MTLSharedEvent; -typedef id<MTLSharedEvent> MTLSharedEvent_id; +typedef __unsafe_unretained id<MTLSharedEvent> MTLSharedEvent_id; #else typedef void* MTLSharedEvent_id; #endif @@ -6258,6 +6258,16 @@ typedef void* MTLSharedEvent_id; void* pNext VkBool32 attachmentFeedbackLoopDynamicState + + VkStructureType sType + void* pNext + VkBool32 legacyVertexAttributes + + + VkStructureType sType + void* pNext + VkBool32 nativeUnalignedPerformance + VkStructureType sType void* pNext @@ -7557,6 +7567,11 @@ typedef void* MTLSharedEvent_id; VkBool32 rayTracingMotionBlur VkBool32 rayTracingMotionBlurPipelineTraceRaysIndirect + + VkStructureType sType + void* pNext + VkBool32 rayTracingValidation + VkStructureType sType @@ -9108,6 +9123,21 @@ typedef void* MTLSharedEvent_id; void* pNext VkBool32 shaderRawAccessChains + + VkStructureType sType + void* pNext + VkBool32 imageAlignmentControl + + + VkStructureType sType + void* pNext + uint32_t supportedImageAlignmentMask + + + VkStructureType sType + const void* pNext + uint32_t maximumRequestedAlignment + @@ -13935,7 +13965,7 @@ typedef void* MTLSharedEvent_id; VkDevice device const VkImageViewHandleInfoNVX* pInfo - + VkResult vkGetImageViewAddressNVX VkDevice device VkImageView imageView @@ -15337,7 +15367,7 @@ typedef void* MTLSharedEvent_id; VkSwapchainKHR swapchain const VkLatencySleepModeInfoNV* pSleepModeInfo - + VkResult vkLatencySleepNV VkDevice device VkSwapchainKHR swapchain @@ -17037,7 +17067,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17116,7 +17146,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17205,7 +17235,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17241,7 +17271,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17270,7 +17300,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17497,7 +17527,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17521,7 +17551,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17600,7 +17630,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17622,7 +17652,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17851,7 +17881,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17892,7 +17922,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17925,7 +17955,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17939,7 +17969,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17977,7 +18007,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18041,7 +18071,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18059,7 +18089,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18078,7 +18108,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18088,7 +18118,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18239,7 +18269,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18296,7 +18326,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18335,7 +18365,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18357,7 +18387,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18372,7 +18402,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18396,7 +18426,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18416,7 +18446,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18561,7 +18591,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18624,14 +18654,14 @@ typedef void* MTLSharedEvent_id; - + - + @@ -18677,7 +18707,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18698,12 +18728,12 @@ typedef void* MTLSharedEvent_id; - + - + @@ -18736,7 +18766,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18794,7 +18824,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18964,7 +18994,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19048,7 +19078,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19147,7 +19177,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19243,7 +19273,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19266,7 +19296,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19296,7 +19326,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19327,7 +19357,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19337,7 +19367,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19365,7 +19395,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19458,14 +19488,14 @@ typedef void* MTLSharedEvent_id; - + - + @@ -19481,7 +19511,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19558,7 +19588,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19566,7 +19596,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19588,7 +19618,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19635,7 +19665,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19670,7 +19700,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19744,7 +19774,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19768,7 +19798,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19787,7 +19817,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19811,7 +19841,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19819,7 +19849,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19837,7 +19867,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19845,7 +19875,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19853,7 +19883,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19867,7 +19897,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19879,7 +19909,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19912,7 +19942,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19959,7 +19989,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19967,7 +19997,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19975,7 +20005,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20022,7 +20052,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20042,7 +20072,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20059,7 +20089,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20101,7 +20131,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20125,7 +20155,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20145,7 +20175,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20167,7 +20197,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20210,7 +20240,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20218,7 +20248,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20236,7 +20266,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20244,7 +20274,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20273,7 +20303,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20339,7 +20369,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20369,7 +20399,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20392,7 +20422,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20400,7 +20430,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20408,7 +20438,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20421,7 +20451,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20513,7 +20543,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20604,7 +20634,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20629,7 +20659,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20681,7 +20711,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20719,7 +20749,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20747,7 +20777,7 @@ typedef void* MTLSharedEvent_id; - + This extension requires buffer_device_address functionality. @@ -20795,7 +20825,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20811,7 +20841,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20842,7 +20872,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20856,7 +20886,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20965,7 +20995,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20983,7 +21013,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20993,7 +21023,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21019,7 +21049,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21039,7 +21069,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21112,12 +21142,12 @@ typedef void* MTLSharedEvent_id; - + - + @@ -21231,7 +21261,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21388,7 +21418,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21466,7 +21496,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21484,7 +21514,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21492,7 +21522,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21516,7 +21546,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21626,7 +21656,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21648,7 +21678,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21690,7 +21720,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21712,7 +21742,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21740,7 +21770,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21758,7 +21788,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21778,7 +21808,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21845,7 +21875,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21855,7 +21885,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21869,7 +21899,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21879,7 +21909,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21905,7 +21935,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21933,7 +21963,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21961,7 +21991,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22013,7 +22043,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22031,7 +22061,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22043,7 +22073,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22057,7 +22087,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22138,7 +22168,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22240,11 +22270,11 @@ typedef void* MTLSharedEvent_id; - + - + @@ -22258,7 +22288,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22281,7 +22311,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22291,7 +22321,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22305,7 +22335,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22331,7 +22361,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22451,7 +22481,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22579,7 +22609,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22597,7 +22627,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22611,7 +22641,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22619,7 +22649,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22634,7 +22664,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22650,7 +22680,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22663,7 +22693,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22677,7 +22707,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22719,14 +22749,14 @@ typedef void* MTLSharedEvent_id; - + - + @@ -22792,7 +22822,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22808,7 +22838,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22877,7 +22907,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22900,7 +22930,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22914,7 +22944,7 @@ typedef void* MTLSharedEvent_id; - + @@ -23032,7 +23062,7 @@ typedef void* MTLSharedEvent_id; - + @@ -23076,7 +23106,7 @@ typedef void* MTLSharedEvent_id; - + @@ -23093,7 +23123,7 @@ typedef void* MTLSharedEvent_id; - + @@ -23109,7 +23139,7 @@ typedef void* MTLSharedEvent_id; - + @@ -23120,7 +23150,8 @@ typedef void* MTLSharedEvent_id; NV internal use only - + + @@ -23154,22 +23185,20 @@ typedef void* MTLSharedEvent_id; - + - + - - - - - + + + - + @@ -23196,7 +23225,7 @@ typedef void* MTLSharedEvent_id; - + @@ -23206,7 +23235,7 @@ typedef void* MTLSharedEvent_id; - + @@ -23276,10 +23305,10 @@ typedef void* MTLSharedEvent_id; - + - + @@ -23407,7 +23436,7 @@ typedef void* MTLSharedEvent_id; - + @@ -23540,7 +23569,7 @@ typedef void* MTLSharedEvent_id; - + @@ -23551,11 +23580,11 @@ typedef void* MTLSharedEvent_id; - + - + @@ -23577,7 +23606,7 @@ typedef void* MTLSharedEvent_id; - + @@ -23672,10 +23701,14 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + + @@ -23688,7 +23721,7 @@ typedef void* MTLSharedEvent_id; - + @@ -23706,7 +23739,7 @@ typedef void* MTLSharedEvent_id; - + @@ -23775,7 +23808,7 @@ typedef void* MTLSharedEvent_id; - + @@ -23944,7 +23977,7 @@ typedef void* MTLSharedEvent_id; - + @@ -24007,7 +24040,7 @@ typedef void* MTLSharedEvent_id; - + @@ -24111,7 +24144,7 @@ typedef void* MTLSharedEvent_id; - + @@ -24187,6 +24220,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -24271,7 +24307,6 @@ typedef void* MTLSharedEvent_id; - @@ -24306,10 +24341,12 @@ typedef void* MTLSharedEvent_id; - + - - + + + + @@ -24343,6 +24380,88 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -25720,9 +25839,9 @@ typedef void* MTLSharedEvent_id; - - - + + + @@ -26475,6 +26594,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -26502,9 +26624,6 @@ typedef void* MTLSharedEvent_id; - - - From 1c6fc73636d16a7b6d7e2d5acfab3bc296f89274 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 10 May 2024 13:09:21 -0600 Subject: [PATCH 360/413] win32u: Use FT_LOAD_PEDANTIC on first load try in freetype_get_glyph_outline(). CW-Bug-Id: #23769 --- dlls/win32u/freetype.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/win32u/freetype.c b/dlls/win32u/freetype.c index 8a56833e6ad..752e8e9e57d 100644 --- a/dlls/win32u/freetype.c +++ b/dlls/win32u/freetype.c @@ -3493,12 +3493,12 @@ static UINT freetype_get_glyph_outline( struct gdi_font *font, UINT glyph, UINT vertical_metrics = FALSE; load_flags = get_load_flags(effective_format, vertical_metrics, !!matrices); - err = pFT_Load_Glyph(ft_face, glyph, load_flags); + err = pFT_Load_Glyph(ft_face, glyph, load_flags & FT_LOAD_NO_HINTING ? load_flags : load_flags | FT_LOAD_PEDANTIC); if (err && format != effective_format) { WARN("Failed to load glyph %#x, retrying with GGO_METRICS. Error %#x.\n", glyph, err); load_flags = get_load_flags(effective_format, vertical_metrics, !!matrices); - err = pFT_Load_Glyph(ft_face, glyph, load_flags); + err = pFT_Load_Glyph(ft_face, glyph, load_flags & FT_LOAD_NO_HINTING ? load_flags : load_flags | FT_LOAD_PEDANTIC); } if (err && !(load_flags & FT_LOAD_NO_HINTING)) { From cd0172ffb83cb7e92d8884117725ae4ef1bedfd8 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 10 May 2024 16:08:58 -0600 Subject: [PATCH 361/413] win32u: HACK: Ignore font GASP flags for Alien Swarm Reactive Drop. CW-Bug-Id: #23776 --- dlls/win32u/freetype.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dlls/win32u/freetype.c b/dlls/win32u/freetype.c index 752e8e9e57d..4259c009a6d 100644 --- a/dlls/win32u/freetype.c +++ b/dlls/win32u/freetype.c @@ -555,8 +555,11 @@ static BOOL is_hinting_enabled(void) if (enabled == -1) { + const char *sgi; + + if ((sgi = getenv("SteamGameId")) && !strcmp(sgi, "563560")) enabled = FALSE; /* Use the >= 2.2.0 function if available */ - if (pFT_Get_TrueType_Engine_Type) + else if (pFT_Get_TrueType_Engine_Type) { FT_TrueTypeEngineType type = pFT_Get_TrueType_Engine_Type(library); enabled = (type == FT_TRUETYPE_ENGINE_TYPE_PATENTED); From 608842f50cde690d949495d5762a15a092ececbb Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Mon, 13 May 2024 16:04:38 -0500 Subject: [PATCH 362/413] win32u: Implement EVENT_SYSTEM_FOREGROUND. CW-Bug-ID: #23779. --- dlls/win32u/input.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 9acdeb40388..7c4e0363281 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1918,6 +1918,8 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) if (hwnd) { + NtUserNotifyWinEvent( EVENT_SYSTEM_FOREGROUND, hwnd, 0, 0 ); + /* send palette messages */ if (send_message( hwnd, WM_QUERYNEWPALETTE, 0, 0 )) send_message_timeout( HWND_BROADCAST, WM_PALETTEISCHANGING, (WPARAM)hwnd, 0, From 2c1f6238be3c6ff0bc5e560ab3b1154379aceb4a Mon Sep 17 00:00:00 2001 From: John Brooks Date: Wed, 27 Dec 2023 22:06:11 -0500 Subject: [PATCH 363/413] fshack: Use texture name hack for Descent 3 Descent 3's OpenGL renderer will use a texture conflicting with the FS hack texture, and only a small rectangle in the bottom left corner will be rendered. Signed-off-by: John Brooks https://github.com/ValveSoftware/wine/pull/211 CW-Bug-Id: #23791 --- dlls/winex11.drv/opengl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index 2de1a26f537..3baf52cc9f8 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -2385,7 +2385,7 @@ static void gen_texture( struct wgl_context *ctx, GLuint *tex, enum fshack_textu { const char *sgi = getenv( "SteamGameId" ); - texture_name_hack = sgi && (!strcmp( sgi, "6020" ) || !strcmp( sgi, "2200" ) || !strcmp( sgi, "2350" )); + texture_name_hack = sgi && (!strcmp( sgi, "6020" ) || !strcmp( sgi, "2200" ) || !strcmp( sgi, "2350" ) || !strcmp( sgi, "273590" )); } if (!texture_name_hack || opengl_funcs.gl.p_glIsTexture( texture_names[type] )) From e36c29c03f7ca808a51a8b530b94b9b66a736027 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 13 May 2024 20:15:30 -0600 Subject: [PATCH 364/413] opengl32: Prefer formats with depth if unspecified in wglChoosePixelFormat(). CW-Bug-Id: #23791 --- dlls/opengl32/tests/opengl.c | 1 + dlls/opengl32/wgl.c | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/dlls/opengl32/tests/opengl.c b/dlls/opengl32/tests/opengl.c index c2b4a644afa..d12568aaf44 100644 --- a/dlls/opengl32/tests/opengl.c +++ b/dlls/opengl32/tests/opengl.c @@ -301,6 +301,7 @@ static void test_choosepixelformat(void) ok( ret_fmt.cAlphaShift == 24, "Got %u.\n", ret_fmt.cAlphaShift ); else ok( !ret_fmt.cAlphaShift, "Got %u.\n", ret_fmt.cAlphaShift ); + ok( ret_fmt.cDepthBits, "Got %u.\n", ret_fmt.cDepthBits ); pfd.dwFlags |= PFD_DOUBLEBUFFER_DONTCARE; ok( test_pfd(&pfd, NULL), "PFD_DOUBLEBUFFER_DONTCARE failed\n" ); diff --git a/dlls/opengl32/wgl.c b/dlls/opengl32/wgl.c index 2a308152735..01f743023d9 100644 --- a/dlls/opengl32/wgl.c +++ b/dlls/opengl32/wgl.c @@ -278,7 +278,14 @@ INT WINAPI wglChoosePixelFormat(HDC hdc, const PIXELFORMATDESCRIPTOR* ppfd) continue; } } - if (ppfd->dwFlags & PFD_DEPTH_DONTCARE && format.cDepthBits < best.cDepthBits) + if (ppfd->dwFlags & PFD_DEPTH_DONTCARE) + { + if (format.cDepthBits < best.cDepthBits) + goto found; + continue; + } + + if (!ppfd->cDepthBits && format.cDepthBits > best.cDepthBits) goto found; continue; From 7d890963609063de82b1254b103fb5bdee842195 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 15 May 2024 12:00:04 -0600 Subject: [PATCH 365/413] nsiproxy.sys: Return success and zero count from ipv6_forward_enumerate_all() if IPV6 is unsupported. CW-Bug-Id: #23805 --- dlls/nsiproxy.sys/ip.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dlls/nsiproxy.sys/ip.c b/dlls/nsiproxy.sys/ip.c index 09f033ac634..43c9ad9e9fe 100644 --- a/dlls/nsiproxy.sys/ip.c +++ b/dlls/nsiproxy.sys/ip.c @@ -1578,7 +1578,11 @@ static NTSTATUS ipv6_forward_enumerate_all( void *key_data, UINT key_size, void UINT rtf_flags; FILE *fp; - if (!(fp = fopen( "/proc/net/ipv6_route", "r" ))) return STATUS_NOT_SUPPORTED; + if (!(fp = fopen( "/proc/net/ipv6_route", "r" ))) + { + *count = 0; + return STATUS_SUCCESS; + } while ((ptr = fgets( buf, sizeof(buf), fp ))) { From e071717ef498ebd8d975429c706014a87083c222 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 15 May 2024 17:29:38 -0600 Subject: [PATCH 366/413] win32u: Disable undecorated windows hack for Halo Infinite. CW-Bug-Id: #23802 --- dlls/win32u/defwnd.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dlls/win32u/defwnd.c b/dlls/win32u/defwnd.c index d68fd2b7f23..2f40ee7295d 100644 --- a/dlls/win32u/defwnd.c +++ b/dlls/win32u/defwnd.c @@ -1871,8 +1871,10 @@ static void handle_nc_calc_size( HWND hwnd, WPARAM wparam, RECT *win_rect ) /* Disable gamescope undecorated windows hack for following games. They don't expect client * rect equals to window rect when in windowed mode. */ const char *sgi = getenv( "SteamGameId" ); - if (!((style & WS_POPUP) && (ex_style & WS_EX_TOOLWINDOW)) && /* Bug 20038: game splash screens */ - !(sgi && !strcmp( sgi, "2563800" ))) /* Bug 23342: The Last Game */ + if (!((style & WS_POPUP) && (ex_style & WS_EX_TOOLWINDOW)) /* Bug 20038: game splash screens */ + && !(sgi && !strcmp( sgi, "2563800" )) /* Bug 23342: The Last Game */ + && !(sgi && !strcmp( sgi, "1240440" )) /* Bug 23802: Halo Infinite */ + ) return; } From 8e746a3a2595cb2681c88ddf8bd36fb5f1f0179f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 15 May 2024 17:22:13 -0600 Subject: [PATCH 367/413] winex11.drv: HACK: Reduce SM_CYCAPTION on low resolutions. CW-Bug-Id: #23802 --- dlls/winex11.drv/x11drv_main.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index b286e6cfde8..5f6f0773d92 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -850,6 +850,19 @@ static NTSTATUS x11drv_init( void *arg ) init_user_driver(); X11DRV_DisplayDevices_Init(FALSE); + + if (native_screen_rect.bottom && native_screen_rect.bottom <= 800) + { + NONCLIENTMETRICSW ncm; + + ncm.cbSize = sizeof(ncm); + if (NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 ) && ncm.iCaptionHeight > 18) + { + TRACE( "Reducing iCaptionHeight from %d to 17.\n", ncm.iCaptionHeight ); + ncm.iCaptionHeight = 17; + NtUserSystemParametersInfo( SPI_SETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0); + } + } return STATUS_SUCCESS; } From 7e70051b01d3698595e99b8342a031d1dbd93de8 Mon Sep 17 00:00:00 2001 From: Anna Lasky Date: Thu, 16 May 2024 10:25:22 -0500 Subject: [PATCH 368/413] HACK: mfplat: Enable the new media source for Planet of the Apes: Last Frontier CW-Bug-ID: #20833 --- dlls/mfplat/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index 29ebc46860d..695b86548f3 100644 --- a/dlls/mfplat/main.c +++ b/dlls/mfplat/main.c @@ -6305,6 +6305,7 @@ static HRESULT resolver_create_gstreamer_handler(IMFByteStreamHandler **handler) if (!strcmp(sgi, "606880") /* Greedfall */) env = "1"; if (!strcmp(sgi, "692850") /* Bloodstained */) env = "1"; if (!strcmp(sgi, "782630") /* Twisted Sails */) env = "1"; + if (!strcmp(sgi, "789910") /* Planet of the Apes: Last Frontier */) env = "1"; } if (env && atoi(env)) return CoCreateInstance(&CLSID_GStreamerByteStreamHandler2, NULL, CLSCTX_INPROC_SERVER, &IID_IMFByteStreamHandler, (void **)handler); From 6c8a54667849dbe43f7846a971ee87c070c23511 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 16 May 2024 14:36:10 -0600 Subject: [PATCH 369/413] ntdll: Fill object pointer in NtQueryInformation( SystemHandleInformation / SystemExtendedHandleInformation ). CW-Bug-Id: #23813 --- dlls/ntdll/unix/system.c | 4 ++-- server/handle.c | 2 ++ server/protocol.def | 2 ++ tools/make_requests | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index 0e77e7d7b14..6c1ca2ce6ce 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -3188,7 +3188,7 @@ NTSTATUS WINAPI NtQuerySystemInformation( SYSTEM_INFORMATION_CLASS class, shi->Handle[i].AccessMask = handle_info[i].access; shi->Handle[i].HandleFlags = handle_info[i].attributes; shi->Handle[i].ObjectType = handle_info[i].type; - /* FIXME: Fill out ObjectPointer */ + shi->Handle[i].ObjectPointer = (void *)(ULONG_PTR)handle_info[i].object; } } else if (ret == STATUS_BUFFER_TOO_SMALL) @@ -3409,7 +3409,7 @@ NTSTATUS WINAPI NtQuerySystemInformation( SYSTEM_INFORMATION_CLASS class, shi->Handles[i].GrantedAccess = handle_info[i].access; shi->Handles[i].HandleAttributes = handle_info[i].attributes; shi->Handles[i].ObjectTypeIndex = handle_info[i].type; - /* FIXME: Fill out Object */ + shi->Handles[i].Object = (void *)(ULONG_PTR)handle_info[i].object; } } else if (ret == STATUS_BUFFER_TOO_SMALL) diff --git a/server/handle.c b/server/handle.c index 48b5d8101bb..86147586034 100644 --- a/server/handle.c +++ b/server/handle.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "ntstatus.h" #define WIN32_NO_STATUS @@ -855,6 +856,7 @@ static int enum_handles( struct process *process, void *user ) handle->access = entry->access & ~RESERVED_ALL; handle->type = entry->ptr->ops->type->index; handle->attributes = 0; + handle->object = (uint64_t)(uintptr_t)entry->ptr; if (entry->access & RESERVED_INHERIT) handle->attributes |= OBJ_INHERIT; if (entry->access & RESERVED_CLOSE_PROTECT) handle->attributes |= OBJ_PROTECT_CLOSE; info->count--; diff --git a/server/protocol.def b/server/protocol.def index d611701e0ab..e47c986a9c4 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3464,6 +3464,8 @@ struct handle_info unsigned int access; unsigned int attributes; unsigned int type; + unsigned int __pad; + unsigned __int64 object; }; /* Return a list of all opened handles */ diff --git a/tools/make_requests b/tools/make_requests index b450e22c2b8..44193f0b766 100755 --- a/tools/make_requests +++ b/tools/make_requests @@ -64,7 +64,7 @@ my %formats = "startup_info_t" => [ 96, 4 ], "user_apc_t" => [ 40, 8 ], "struct filesystem_event" => [ 12, 4 ], - "struct handle_info" => [ 20, 4 ], + "struct handle_info" => [ 32, 4 ], "struct luid_attr" => [ 12, 4 ], "struct object_attributes" => [ 16, 4 ], "struct object_type_info" => [ 44, 4 ], From 5940c7eaaa4c98bfb3a417c778f062b60fc3892c Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Fri, 29 Mar 2024 18:09:05 +1100 Subject: [PATCH 370/413] winegstreamer: Respond to the URI query. The default behaviour for gstreamer is to use the URI to create the stream-id. Without this, gstreamer creates a random stream-id per pad which can cause inconsistent ordering on the decodebin src pads. This in turn can cause issues, for example, with audio stream selection. (cherry picked from commit 752126d60ef8249beee57b6d5f9f7ccde84d0d58) CW-Bug-Id: #22403 --- dlls/winegstreamer/wg_parser.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 2d00f4247e0..8e2248f748c 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1452,6 +1452,15 @@ static gboolean src_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) return TRUE; } + case GST_QUERY_URI: + if (parser->uri) + { + GST_LOG_OBJECT(pad, "Responding with %" GST_PTR_FORMAT, query); + gst_query_set_uri(query, parser->uri); + return TRUE; + } + return FALSE; + default: GST_WARNING("Unhandled query type %s.", GST_QUERY_TYPE_NAME(query)); return FALSE; From 1ed3b382b75e6465b9a006234e0b7c05633063c1 Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Wed, 1 May 2024 13:32:39 +1000 Subject: [PATCH 371/413] winegstreamer: Fix wow64 support for wg_parser_connect. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=56595 (cherry picked from commit 87eaf899551077817106bce62f5a8b980a47635f) CW-Bug-Id: #22403 --- dlls/winegstreamer/wg_parser.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 8e2248f748c..4ef51d45677 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -2283,6 +2283,24 @@ C_ASSERT(ARRAYSIZE(__wine_unix_call_funcs) == unix_wg_funcs_count); typedef ULONG PTR32; +static NTSTATUS wow64_wg_parser_connect(void *args) +{ + struct + { + wg_parser_t parser; + PTR32 uri; + UINT64 file_size; + } *params32 = args; + struct wg_parser_connect_params params = + { + .parser = params32->parser, + .uri = ULongToPtr(params32->uri), + .file_size = params32->file_size, + }; + + return wg_parser_connect(¶ms); +} + static NTSTATUS wow64_wg_parser_push_data(void *args) { struct { @@ -2649,7 +2667,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = X(wg_parser_create), X(wg_parser_destroy), - X(wg_parser_connect), + X64(wg_parser_connect), X(wg_parser_disconnect), X(wg_parser_get_next_read_offset), From d31f7a4edf47500cc308741a585871add9c5011d Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Wed, 1 May 2024 13:34:06 +1000 Subject: [PATCH 372/413] winegstreamer: Log query after setting the URI. (cherry picked from commit 130b97a2b0737ff07e51d678d5ee23482de44bf3) CW-Bug-Id: #22403 --- dlls/winegstreamer/wg_parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 4ef51d45677..6a7fd6d1c71 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1455,8 +1455,8 @@ static gboolean src_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) case GST_QUERY_URI: if (parser->uri) { - GST_LOG_OBJECT(pad, "Responding with %" GST_PTR_FORMAT, query); gst_query_set_uri(query, parser->uri); + GST_LOG_OBJECT(pad, "Responding with %" GST_PTR_FORMAT, query); return TRUE; } return FALSE; From 8bb48603bdc433971d16ce2e42119612752db408 Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Fri, 10 May 2024 15:49:19 +1000 Subject: [PATCH 373/413] winegstreamer: Pass filename to wg_parser when available. (cherry picked from commit a48d34300229176c3687b0a29bc386208059f5a2) CW-Bug-Id: #22403 --- dlls/winegstreamer/wm_reader.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/dlls/winegstreamer/wm_reader.c b/dlls/winegstreamer/wm_reader.c index a172f1cfbb3..3afd021bcac 100644 --- a/dlls/winegstreamer/wm_reader.c +++ b/dlls/winegstreamer/wm_reader.c @@ -54,6 +54,7 @@ struct wm_reader QWORD start_time; QWORD file_size; + WCHAR *filename; IStream *source_stream; HANDLE file; HANDLE read_thread; @@ -1467,7 +1468,7 @@ static HRESULT init_stream(struct wm_reader *reader) goto out_destroy_parser; } - if (FAILED(hr = wg_parser_connect(reader->wg_parser, reader->file_size, NULL))) + if (FAILED(hr = wg_parser_connect(reader->wg_parser, reader->file_size, reader->filename))) { ERR("Failed to connect parser, hr %#lx.\n", hr); goto out_shutdown_thread; @@ -1584,7 +1585,7 @@ static HRESULT reinit_stream(struct wm_reader *reader, bool read_compressed) goto out_destroy_parser; } - if (FAILED(hr = wg_parser_connect(reader->wg_parser, reader->file_size, NULL))) + if (FAILED(hr = wg_parser_connect(reader->wg_parser, reader->file_size, reader->filename))) { ERR("Failed to connect parser, hr %#lx.\n", hr); goto out_shutdown_thread; @@ -1908,6 +1909,8 @@ static HRESULT WINAPI reader_Close(IWMSyncReader2 *iface) if (reader->file) CloseHandle(reader->file); reader->file = NULL; + free(reader->filename); + reader->filename = NULL; LeaveCriticalSection(&reader->cs); return S_OK; @@ -2238,11 +2241,17 @@ static HRESULT WINAPI reader_Open(IWMSyncReader2 *iface, const WCHAR *filename) return E_UNEXPECTED; } + reader->filename = wcsdup(filename); reader->file = file; reader->file_size = size.QuadPart; if (FAILED(hr = init_stream(reader))) + { + CloseHandle(reader->file); reader->file = NULL; + free(reader->filename); + reader->filename = NULL; + } LeaveCriticalSection(&reader->cs); return hr; From 1e8285ac09d0946c97fa94c1fc9eac16ded4184b Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Fri, 10 May 2024 15:57:19 +1000 Subject: [PATCH 374/413] winegstreamer: Don't send stream_start in PULL mode. This fixes a deadlock that can occur when the stream-start is dropped by the demuxer when it is in PULL mode. CW-Bug-Id: #22403 --- dlls/winegstreamer/media-converter/videoconv.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/media-converter/videoconv.c b/dlls/winegstreamer/media-converter/videoconv.c index 0f916b86f66..184989647bf 100644 --- a/dlls/winegstreamer/media-converter/videoconv.c +++ b/dlls/winegstreamer/media-converter/videoconv.c @@ -726,7 +726,9 @@ static gboolean video_conv_push_stream_start(VideoConv *conv, struct payload_has { struct video_conv_state *state; - push_event(conv->src_pad, gst_event_new_stream_start(format_hash(hash))); + /* we don't send a stream-start in pull mode, as it can cause a deadlock */ + if (conv->active_mode == GST_PAD_MODE_PUSH) + push_event(conv->src_pad, gst_event_new_stream_start(format_hash(hash))); if (!(state = video_conv_lock_state(conv))) { @@ -1147,6 +1149,19 @@ static gboolean video_conv_src_query(GstPad *pad, GstObject *parent, GstQuery *q gst_query_set_duration(query, GST_FORMAT_BYTES, duration); return true; + case GST_QUERY_URI: + if (!gst_pad_query_default(pad, parent, query)) + { + if (!(state = video_conv_lock_state(conv))) + return false; + /* if we don't already have a uri, we will use the hash. This is to ensure + * downstream will use a consistent stream-id */ + gst_query_set_uri(query, format_hash(&state->transcode_hash)); + pthread_mutex_unlock(&conv->state_mutex); + GST_LOG_OBJECT(pad, "Responding with %" GST_PTR_FORMAT, query); + } + return true; + default: return gst_pad_query_default(pad, parent, query); } From 1745426d8eed672708e260f93ff5ab95315db8ca Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Thu, 11 Apr 2024 15:24:34 +1000 Subject: [PATCH 375/413] mfmediaengine: Implement SVR. CW-Bug-Id: #22403 --- dlls/mfmediaengine/Makefile.in | 3 +- dlls/mfmediaengine/main.c | 274 +++-- dlls/mfmediaengine/mediaengine_private.h | 29 + dlls/mfmediaengine/tests/mfmediaengine.c | 8 +- dlls/mfmediaengine/video_frame_sink.c | 1201 ++++++++++++++++++++++ 5 files changed, 1354 insertions(+), 161 deletions(-) create mode 100644 dlls/mfmediaengine/mediaengine_private.h create mode 100644 dlls/mfmediaengine/video_frame_sink.c diff --git a/dlls/mfmediaengine/Makefile.in b/dlls/mfmediaengine/Makefile.in index 6e83cf19b9c..b8941a38f5c 100644 --- a/dlls/mfmediaengine/Makefile.in +++ b/dlls/mfmediaengine/Makefile.in @@ -6,4 +6,5 @@ EXTRADLLFLAGS = -Wb,--prefer-native SOURCES = \ main.c \ mediaengine.idl \ - mediaengine_classes.idl + mediaengine_classes.idl \ + video_frame_sink.c diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index 3e05626f238..9e41d9dad84 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -32,6 +32,8 @@ #include "mmdeviceapi.h" #include "audiosessiontypes.h" +#include "mediaengine_private.h" + #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(mfplat); @@ -137,8 +139,8 @@ struct media_engine IMFMediaEngineEx IMFMediaEngineEx_iface; IMFGetService IMFGetService_iface; IMFAsyncCallback session_events; + IMFAsyncCallback sink_events; IMFAsyncCallback load_handler; - IMFSampleGrabberSinkCallback grabber_callback; LONG refcount; IMFMediaEngineNotify *callback; IMFAttributes *attributes; @@ -164,6 +166,8 @@ struct media_engine { IMFMediaSource *source; IMFPresentationDescriptor *pd; + PROPVARIANT start_position; + struct video_frame_sink *frame_sink; } presentation; struct effects video_effects; struct effects audio_effects; @@ -782,14 +786,14 @@ static struct media_engine *impl_from_session_events_IMFAsyncCallback(IMFAsyncCa return CONTAINING_RECORD(iface, struct media_engine, session_events); } -static struct media_engine *impl_from_load_handler_IMFAsyncCallback(IMFAsyncCallback *iface) +static struct media_engine *impl_from_sink_events_IMFAsyncCallback(IMFAsyncCallback *iface) { - return CONTAINING_RECORD(iface, struct media_engine, load_handler); + return CONTAINING_RECORD(iface, struct media_engine, sink_events); } -static struct media_engine *impl_from_IMFSampleGrabberSinkCallback(IMFSampleGrabberSinkCallback *iface) +static struct media_engine *impl_from_load_handler_IMFAsyncCallback(IMFAsyncCallback *iface) { - return CONTAINING_RECORD(iface, struct media_engine, grabber_callback); + return CONTAINING_RECORD(iface, struct media_engine, load_handler); } static unsigned int get_gcd(unsigned int a, unsigned int b) @@ -991,6 +995,10 @@ static HRESULT WINAPI media_engine_session_events_Invoke(IMFAsyncCallback *iface IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_ENDED, 0, 0); break; + + case MEEndOfPresentation: + video_frame_sink_notify_end_of_presentation(engine->presentation.frame_sink); + break; } failed: @@ -1013,6 +1021,48 @@ static const IMFAsyncCallbackVtbl media_engine_session_events_vtbl = media_engine_session_events_Invoke, }; +static ULONG WINAPI media_engine_sink_events_AddRef(IMFAsyncCallback *iface) +{ + struct media_engine *engine = impl_from_sink_events_IMFAsyncCallback(iface); + return IMFMediaEngineEx_AddRef(&engine->IMFMediaEngineEx_iface); +} + +static ULONG WINAPI media_engine_sink_events_Release(IMFAsyncCallback *iface) +{ + struct media_engine *engine = impl_from_sink_events_IMFAsyncCallback(iface); + return IMFMediaEngineEx_Release(&engine->IMFMediaEngineEx_iface); +} + +static HRESULT WINAPI media_engine_sink_events_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct media_engine *engine = impl_from_sink_events_IMFAsyncCallback(iface); + MF_MEDIA_ENGINE_EVENT event = IMFAsyncResult_GetStatus(result); + + EnterCriticalSection(&engine->cs); + + switch (event) + { + case MF_MEDIA_ENGINE_EVENT_FIRSTFRAMEREADY: + IMFMediaEngineNotify_EventNotify(engine->callback, event, 0, 0); + break; + default: + ; + } + + LeaveCriticalSection(&engine->cs); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl media_engine_sink_events_vtbl = +{ + media_engine_callback_QueryInterface, + media_engine_sink_events_AddRef, + media_engine_sink_events_Release, + media_engine_callback_GetParameters, + media_engine_sink_events_Invoke, +}; + static ULONG WINAPI media_engine_load_handler_AddRef(IMFAsyncCallback *iface) { struct media_engine *engine = impl_from_load_handler_IMFAsyncCallback(iface); @@ -1110,9 +1160,8 @@ static HRESULT media_engine_create_audio_renderer(struct media_engine *engine, I static HRESULT media_engine_create_video_renderer(struct media_engine *engine, IMFTopologyNode **node) { - DXGI_FORMAT output_format; IMFMediaType *media_type; - IMFActivate *activate; + UINT32 output_format; GUID subtype; HRESULT hr; @@ -1137,33 +1186,47 @@ static HRESULT media_engine_create_video_renderer(struct media_engine *engine, I IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &subtype); - hr = MFCreateSampleGrabberSinkActivate(media_type, &engine->grabber_callback, &activate); + hr = create_video_frame_sink(media_type, &engine->sink_events, &engine->presentation.frame_sink); IMFMediaType_Release(media_type); if (FAILED(hr)) return hr; if (SUCCEEDED(hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, node))) { - IMFTopologyNode_SetObject(*node, (IUnknown *)activate); + IMFStreamSink *sink; + video_frame_sink_query_iface(engine->presentation.frame_sink, &IID_IMFStreamSink, (void **)&sink); + + IMFTopologyNode_SetObject(*node, (IUnknown *)sink); IMFTopologyNode_SetUINT32(*node, &MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE); - } - IMFActivate_Release(activate); + IMFStreamSink_Release(sink); + } engine->video_frame.output_format = output_format; return hr; } +/* must be called with engine->cs held */ static void media_engine_clear_presentation(struct media_engine *engine) { if (engine->presentation.source) { + /* critical section can not be held during shutdown, as shut down requires all pending + * callbacks to complete, and some callbacks require this cs */ + LeaveCriticalSection(&engine->cs); IMFMediaSource_Shutdown(engine->presentation.source); + EnterCriticalSection(&engine->cs); IMFMediaSource_Release(engine->presentation.source); } if (engine->presentation.pd) IMFPresentationDescriptor_Release(engine->presentation.pd); + if (engine->presentation.frame_sink) + { + video_frame_sink_release(engine->presentation.frame_sink); + engine->presentation.frame_sink = NULL; + } + memset(&engine->presentation, 0, sizeof(engine->presentation)); } @@ -1262,7 +1325,7 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi if (SUCCEEDED(hr = MFCreateTopology(&topology))) { IMFTopologyNode *sar_node = NULL, *audio_src = NULL; - IMFTopologyNode *grabber_node = NULL, *video_src = NULL; + IMFTopologyNode *svr_node = NULL, *video_src = NULL; if (engine->flags & MF_MEDIA_ENGINE_REAL_TIME_MODE) IMFTopology_SetUINT32(topology, &MF_LOW_LATENCY, TRUE); @@ -1296,24 +1359,24 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi if (FAILED(hr = media_engine_create_source_node(source, pd, sd_video, &video_src))) WARN("Failed to create video source node, hr %#lx.\n", hr); - if (FAILED(hr = media_engine_create_video_renderer(engine, &grabber_node))) - WARN("Failed to create video grabber node, hr %#lx.\n", hr); + if (FAILED(hr = media_engine_create_video_renderer(engine, &svr_node))) + WARN("Failed to create simple video render node, hr %#lx.\n", hr); - if (grabber_node && video_src) + if (svr_node && video_src) { IMFTopology_AddNode(topology, video_src); - IMFTopology_AddNode(topology, grabber_node); + IMFTopology_AddNode(topology, svr_node); if (FAILED(hr = media_engine_create_effects(engine->video_effects.effects, engine->video_effects.count, - video_src, grabber_node, topology))) + video_src, svr_node, topology))) WARN("Failed to create video effect nodes, hr %#lx.\n", hr); } if (SUCCEEDED(hr)) IMFTopologyNode_GetTopoNodeID(video_src, &engine->video_frame.node_id); - if (grabber_node) - IMFTopologyNode_Release(grabber_node); + if (svr_node) + IMFTopologyNode_Release(svr_node); if (video_src) IMFTopologyNode_Release(video_src); } @@ -1457,6 +1520,7 @@ static ULONG WINAPI media_engine_AddRef(IMFMediaEngineEx *iface) static void free_media_engine(struct media_engine *engine) { + EnterCriticalSection(&engine->cs); if (engine->callback) IMFMediaEngineNotify_Release(engine->callback); if (engine->clock) @@ -1479,6 +1543,7 @@ static void free_media_engine(struct media_engine *engine) IMFDXGIDeviceManager_Release(engine->device_manager); } SysFreeString(engine->current_source); + LeaveCriticalSection(&engine->cs); DeleteCriticalSection(&engine->cs); free(engine->video_frame.buffer); free(engine); @@ -2242,28 +2307,26 @@ static HRESULT WINAPI media_engine_GetVideoAspectRatio(IMFMediaEngineEx *iface, static HRESULT WINAPI media_engine_Shutdown(IMFMediaEngineEx *iface) { struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - IMFMediaSession *session = NULL; HRESULT hr = S_OK; TRACE("%p.\n", iface); EnterCriticalSection(&engine->cs); if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) + { + LeaveCriticalSection(&engine->cs); hr = MF_E_SHUTDOWN; + } else { media_engine_set_flag(engine, FLAGS_ENGINE_SHUT_DOWN, TRUE); media_engine_clear_presentation(engine); - IMFMediaSession_AddRef(engine->session); - session = engine->session; + /* critical section can not be held during shutdown, as shut down requires all pending + * callbacks to complete, and some callbacks require this cs */ + LeaveCriticalSection(&engine->cs); + IMFMediaSession_Shutdown(engine->session); } - LeaveCriticalSection(&engine->cs); - if (SUCCEEDED(hr)) - { - IMFMediaSession_Shutdown(session); - IMFMediaSession_Release(session); - } return hr; } @@ -2313,8 +2376,10 @@ static void media_engine_adjust_destination_for_ratio(const struct media_engine static void media_engine_update_d3d11_frame_surface(ID3D11DeviceContext *context, struct media_engine *engine) { D3D11_TEXTURE2D_DESC surface_desc; + IMFMediaBuffer *media_buffer; + IMFSample *sample; - if (!(engine->flags & FLAGS_ENGINE_NEW_FRAME)) + if (!video_frame_sink_get_sample(engine->presentation.frame_sink, &sample)) return; ID3D11Texture2D_GetDesc(engine->video_frame.d3d11.source, &surface_desc); @@ -2330,13 +2395,24 @@ static void media_engine_update_d3d11_frame_surface(ID3D11DeviceContext *context surface_desc.Width = 0; } - if (engine->video_frame.buffer_size == surface_desc.Width * surface_desc.Height) + if (SUCCEEDED(IMFSample_ConvertToContiguousBuffer(sample, &media_buffer))) { - ID3D11DeviceContext_UpdateSubresource(context, (ID3D11Resource *)engine->video_frame.d3d11.source, - 0, NULL, engine->video_frame.buffer, surface_desc.Width, 0); + BYTE *buffer; + DWORD buffer_size; + if (SUCCEEDED(IMFMediaBuffer_Lock(media_buffer, &buffer, NULL, &buffer_size))) + { + if (buffer_size == surface_desc.Width * surface_desc.Height) + { + ID3D11DeviceContext_UpdateSubresource(context, (ID3D11Resource *)engine->video_frame.d3d11.source, + 0, NULL, buffer, surface_desc.Width, 0); + } + + IMFMediaBuffer_Unlock(media_buffer); + } + IMFMediaBuffer_Release(media_buffer); } - media_engine_set_flag(engine, FLAGS_ENGINE_NEW_FRAME, FALSE); + IMFSample_Release(sample); } static HRESULT media_engine_transfer_to_d3d11_texture(struct media_engine *engine, ID3D11Texture2D *texture, @@ -2532,8 +2608,9 @@ static HRESULT WINAPI media_engine_OnVideoStreamTick(IMFMediaEngineEx *iface, LO hr = E_POINTER; else { - *pts = engine->video_frame.pts; - hr = *pts == MINLONGLONG ? S_FALSE : S_OK; + MFTIME clocktime; + IMFPresentationClock_GetTime(engine->clock, &clocktime); + hr = video_frame_sink_get_pts(engine->presentation.frame_sink, clocktime, pts); } LeaveCriticalSection(&engine->cs); @@ -3114,127 +3191,6 @@ static const IMFGetServiceVtbl media_engine_get_service_vtbl = media_engine_gs_GetService, }; -static HRESULT WINAPI media_engine_grabber_callback_QueryInterface(IMFSampleGrabberSinkCallback *iface, - REFIID riid, void **obj) -{ - if (IsEqualIID(riid, &IID_IMFSampleGrabberSinkCallback) || - IsEqualIID(riid, &IID_IUnknown)) - { - *obj = iface; - IMFSampleGrabberSinkCallback_AddRef(iface); - return S_OK; - } - - *obj = NULL; - return E_NOINTERFACE; -} - -static ULONG WINAPI media_engine_grabber_callback_AddRef(IMFSampleGrabberSinkCallback *iface) -{ - struct media_engine *engine = impl_from_IMFSampleGrabberSinkCallback(iface); - return IMFMediaEngineEx_AddRef(&engine->IMFMediaEngineEx_iface); -} - -static ULONG WINAPI media_engine_grabber_callback_Release(IMFSampleGrabberSinkCallback *iface) -{ - struct media_engine *engine = impl_from_IMFSampleGrabberSinkCallback(iface); - return IMFMediaEngineEx_Release(&engine->IMFMediaEngineEx_iface); -} - -static HRESULT WINAPI media_engine_grabber_callback_OnClockStart(IMFSampleGrabberSinkCallback *iface, - MFTIME systime, LONGLONG start_offset) -{ - return S_OK; -} - -static HRESULT WINAPI media_engine_grabber_callback_OnClockStop(IMFSampleGrabberSinkCallback *iface, - MFTIME systime) -{ - struct media_engine *engine = impl_from_IMFSampleGrabberSinkCallback(iface); - - EnterCriticalSection(&engine->cs); - media_engine_set_flag(engine, FLAGS_ENGINE_FIRST_FRAME, FALSE); - engine->video_frame.pts = MINLONGLONG; - LeaveCriticalSection(&engine->cs); - - return S_OK; -} - -static HRESULT WINAPI media_engine_grabber_callback_OnClockPause(IMFSampleGrabberSinkCallback *iface, - MFTIME systime) -{ - return S_OK; -} - -static HRESULT WINAPI media_engine_grabber_callback_OnClockRestart(IMFSampleGrabberSinkCallback *iface, - MFTIME systime) -{ - return S_OK; -} - -static HRESULT WINAPI media_engine_grabber_callback_OnClockSetRate(IMFSampleGrabberSinkCallback *iface, - MFTIME systime, float rate) -{ - return S_OK; -} - -static HRESULT WINAPI media_engine_grabber_callback_OnSetPresentationClock(IMFSampleGrabberSinkCallback *iface, - IMFPresentationClock *clock) -{ - return S_OK; -} - -static HRESULT WINAPI media_engine_grabber_callback_OnProcessSample(IMFSampleGrabberSinkCallback *iface, - REFGUID major_type, DWORD sample_flags, LONGLONG sample_time, LONGLONG sample_duration, - const BYTE *buffer, DWORD buffer_size) -{ - struct media_engine *engine = impl_from_IMFSampleGrabberSinkCallback(iface); - - EnterCriticalSection(&engine->cs); - - if (!(engine->flags & FLAGS_ENGINE_FIRST_FRAME)) - { - IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_FIRSTFRAMEREADY, 0, 0); - media_engine_set_flag(engine, FLAGS_ENGINE_FIRST_FRAME, TRUE); - } - engine->video_frame.pts = sample_time; - if (engine->video_frame.buffer_size < buffer_size) - { - free(engine->video_frame.buffer); - if ((engine->video_frame.buffer = malloc(buffer_size))) - engine->video_frame.buffer_size = buffer_size; - } - if (engine->video_frame.buffer) - { - memcpy(engine->video_frame.buffer, buffer, buffer_size); - engine->flags |= FLAGS_ENGINE_NEW_FRAME; - } - - LeaveCriticalSection(&engine->cs); - - return S_OK; -} - -static HRESULT WINAPI media_engine_grabber_callback_OnShutdown(IMFSampleGrabberSinkCallback *iface) -{ - return S_OK; -} - -static const IMFSampleGrabberSinkCallbackVtbl media_engine_grabber_callback_vtbl = -{ - media_engine_grabber_callback_QueryInterface, - media_engine_grabber_callback_AddRef, - media_engine_grabber_callback_Release, - media_engine_grabber_callback_OnClockStart, - media_engine_grabber_callback_OnClockStop, - media_engine_grabber_callback_OnClockPause, - media_engine_grabber_callback_OnClockRestart, - media_engine_grabber_callback_OnClockSetRate, - media_engine_grabber_callback_OnSetPresentationClock, - media_engine_grabber_callback_OnProcessSample, - media_engine_grabber_callback_OnShutdown, -}; - static HRESULT WINAPI media_engine_factory_QueryInterface(IMFMediaEngineClassFactory *iface, REFIID riid, void **obj) { if (IsEqualIID(riid, &IID_IMFMediaEngineClassFactory) || @@ -3262,16 +3218,16 @@ static ULONG WINAPI media_engine_factory_Release(IMFMediaEngineClassFactory *ifa static HRESULT init_media_engine(DWORD flags, IMFAttributes *attributes, struct media_engine *engine) { - DXGI_FORMAT output_format; - UINT64 playback_hwnd; + UINT32 output_format; + UINT64 playback_hwnd = 0; IMFClock *clock; HRESULT hr; engine->IMFMediaEngineEx_iface.lpVtbl = &media_engine_vtbl; engine->IMFGetService_iface.lpVtbl = &media_engine_get_service_vtbl; engine->session_events.lpVtbl = &media_engine_session_events_vtbl; + engine->sink_events.lpVtbl = &media_engine_sink_events_vtbl; engine->load_handler.lpVtbl = &media_engine_load_handler_vtbl; - engine->grabber_callback.lpVtbl = &media_engine_grabber_callback_vtbl; engine->refcount = 1; engine->flags = (flags & MF_MEDIA_ENGINE_CREATEFLAGS_MASK) | FLAGS_ENGINE_PAUSED; engine->default_playback_rate = 1.0; diff --git a/dlls/mfmediaengine/mediaengine_private.h b/dlls/mfmediaengine/mediaengine_private.h new file mode 100644 index 00000000000..9920a5ef19b --- /dev/null +++ b/dlls/mfmediaengine/mediaengine_private.h @@ -0,0 +1,29 @@ +/* + * Copyright 2019 Nikolay Sivov 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 + */ + +#include "mfmediaengine.h" + +struct video_frame_sink; + +HRESULT create_video_frame_sink(IMFMediaType *media_type, IMFAsyncCallback *events_callback, + struct video_frame_sink **sink); +HRESULT video_frame_sink_query_iface(struct video_frame_sink *object, REFIID riid, void **obj); +ULONG video_frame_sink_release(struct video_frame_sink *sink); +int video_frame_sink_get_sample(struct video_frame_sink *sink, IMFSample **sample); +HRESULT video_frame_sink_get_pts(struct video_frame_sink *sink, MFTIME clocktime, LONGLONG *pts); +void video_frame_sink_notify_end_of_presentation(struct video_frame_sink *sink); diff --git a/dlls/mfmediaengine/tests/mfmediaengine.c b/dlls/mfmediaengine/tests/mfmediaengine.c index f9489f3dcb5..47130d8e436 100644 --- a/dlls/mfmediaengine/tests/mfmediaengine.c +++ b/dlls/mfmediaengine/tests/mfmediaengine.c @@ -1272,6 +1272,7 @@ static void test_TransferVideoFrame(void) HRESULT hr; DWORD res; BSTR url; + LONGLONG pts; stream = load_resource(L"i420-64x64.avi", L"video/avi"); @@ -1333,6 +1334,7 @@ static void test_TransferVideoFrame(void) ok(!res, "Unexpected res %#lx.\n", res); SetRect(&dst_rect, 0, 0, desc.Width, desc.Height); + IMFMediaEngineEx_OnVideoStreamTick(notify->media_engine, &pts); hr = IMFMediaEngineEx_TransferVideoFrame(notify->media_engine, (IUnknown *)texture, NULL, &dst_rect, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -2334,8 +2336,12 @@ static void test_GetSeekable(void) ok(time_range == NULL || broken(time_range == (IMFMediaTimeRange *)0xdeadbeef) /* <= Win10 1507 */, "Got unexpected pointer.\n"); + /* IMFMediaEngineEx_Shutdown can release in parallel. A small sleep allows this test to pass more + * often than not. But given its a matter of timing, this test is marked flaky + */ + Sleep(10); refcount = IMFMediaEngineEx_Release(media_engine); - todo_wine + flaky_wine ok(!refcount, "Got unexpected refcount %lu.\n", refcount); /* Unseekable bytestreams */ diff --git a/dlls/mfmediaengine/video_frame_sink.c b/dlls/mfmediaengine/video_frame_sink.c new file mode 100644 index 00000000000..706192fa8a7 --- /dev/null +++ b/dlls/mfmediaengine/video_frame_sink.c @@ -0,0 +1,1201 @@ +/* + * Copyright 2019 Nikolay Sivov 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 + */ + +#define COBJMACROS + +#include +#include + +#include "mfapi.h" +#include "mfidl.h" +#include "mferror.h" + +#include "mediaengine_private.h" + +#include "wine/debug.h" +#include "wine/list.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +enum sink_state +{ + SINK_STATE_STOPPED = 0, + SINK_STATE_PAUSED, + SINK_STATE_RUNNING, +}; + +static inline const char *debugstr_time(LONGLONG time) +{ + ULONGLONG abstime = time >= 0 ? time : -time; + unsigned int i = 0, j = 0; + char buffer[23], rev[23]; + + while (abstime || i <= 8) + { + buffer[i++] = '0' + (abstime % 10); + abstime /= 10; + if (i == 7) buffer[i++] = '.'; + } + if (time < 0) buffer[i++] = '-'; + + while (i--) rev[j++] = buffer[i]; + while (rev[j-1] == '0' && rev[j-2] != '.') --j; + rev[j] = 0; + + return wine_dbg_sprintf("%s", rev); +} + +enum video_frame_sink_flags +{ + FLAGS_FIRST_FRAME = 0x1, +}; + +struct video_frame_sink +{ + IMFMediaSink IMFMediaSink_iface; + IMFClockStateSink IMFClockStateSink_iface; + IMFMediaEventGenerator IMFMediaEventGenerator_iface; + IMFStreamSink IMFStreamSink_iface; + IMFMediaTypeHandler IMFMediaTypeHandler_iface; + LONG refcount; + IMFMediaType *media_type; + IMFMediaType *current_media_type; + BOOL is_shut_down; + IMFMediaEventQueue *event_queue; + IMFMediaEventQueue *stream_event_queue; + IMFPresentationClock *clock; + IMFAsyncCallback *callback; + float rate; + enum sink_state state; + unsigned int flags; + IMFSample *sample[2]; + IMFSample *presentation_sample; + int sample_write_index; + int sample_read_index; + BOOL sample_request_pending; + BOOL sample_presented; + BOOL eos; + CRITICAL_SECTION cs; +}; + +static void video_frame_sink_set_flag(struct video_frame_sink *sink, unsigned int mask, BOOL value) +{ + if (value) + sink->flags |= mask; + else + sink->flags &= ~mask; +} + +static struct video_frame_sink *impl_from_IMFMediaSink(IMFMediaSink *iface) +{ + return CONTAINING_RECORD(iface, struct video_frame_sink, IMFMediaSink_iface); +} + +static struct video_frame_sink *impl_from_IMFClockStateSink(IMFClockStateSink *iface) +{ + return CONTAINING_RECORD(iface, struct video_frame_sink, IMFClockStateSink_iface); +} + +static struct video_frame_sink *impl_from_IMFMediaEventGenerator(IMFMediaEventGenerator *iface) +{ + return CONTAINING_RECORD(iface, struct video_frame_sink, IMFMediaEventGenerator_iface); +} + +static struct video_frame_sink *impl_from_IMFStreamSink(IMFStreamSink *iface) +{ + return CONTAINING_RECORD(iface, struct video_frame_sink, IMFStreamSink_iface); +} + +static struct video_frame_sink *impl_from_IMFMediaTypeHandler(IMFMediaTypeHandler *iface) +{ + return CONTAINING_RECORD(iface, struct video_frame_sink, IMFMediaTypeHandler_iface); +} + +static void video_frame_sink_samples_release(struct video_frame_sink *sink) +{ + for (int i = 0; i < ARRAYSIZE(sink->sample); i++) + { + if (sink->sample[i]) + { + IMFSample_Release(sink->sample[i]); + sink->sample[i] = NULL; + } + } + if (sink->presentation_sample) + { + IMFSample_Release(sink->presentation_sample); + sink->presentation_sample = NULL; + } + sink->sample_read_index = 0; + sink->sample_write_index = 0; + sink->sample_presented = FALSE; +} + +static HRESULT WINAPI video_frame_sink_stream_QueryInterface(IMFStreamSink *iface, REFIID riid, void **obj) +{ + struct video_frame_sink *sink = impl_from_IMFStreamSink(iface); + + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFStreamSink) || + IsEqualIID(riid, &IID_IMFMediaEventGenerator) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = &sink->IMFStreamSink_iface; + } + else if (IsEqualIID(riid, &IID_IMFMediaTypeHandler)) + { + *obj = &sink->IMFMediaTypeHandler_iface; + } + else + { + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*obj); + + return S_OK; +} + +static ULONG WINAPI video_frame_sink_stream_AddRef(IMFStreamSink *iface) +{ + struct video_frame_sink *sink = impl_from_IMFStreamSink(iface); + return IMFMediaSink_AddRef(&sink->IMFMediaSink_iface); +} + +static ULONG WINAPI video_frame_sink_stream_Release(IMFStreamSink *iface) +{ + struct video_frame_sink *sink = impl_from_IMFStreamSink(iface); + return IMFMediaSink_Release(&sink->IMFMediaSink_iface); +} + +static HRESULT WINAPI video_frame_sink_stream_GetEvent(IMFStreamSink *iface, DWORD flags, IMFMediaEvent **event) +{ + struct video_frame_sink *sink = impl_from_IMFStreamSink(iface); + + TRACE("%p, %#lx, %p.\n", iface, flags, event); + + if (sink->is_shut_down) + return MF_E_STREAMSINK_REMOVED; + + return IMFMediaEventQueue_GetEvent(sink->stream_event_queue, flags, event); +} + +static HRESULT WINAPI video_frame_sink_stream_BeginGetEvent(IMFStreamSink *iface, IMFAsyncCallback *callback, + IUnknown *state) +{ + struct video_frame_sink *sink = impl_from_IMFStreamSink(iface); + + TRACE("%p, %p, %p.\n", iface, callback, state); + + if (sink->is_shut_down) + return MF_E_STREAMSINK_REMOVED; + + return IMFMediaEventQueue_BeginGetEvent(sink->stream_event_queue, callback, state); +} + +static HRESULT WINAPI video_frame_sink_stream_EndGetEvent(IMFStreamSink *iface, IMFAsyncResult *result, + IMFMediaEvent **event) +{ + struct video_frame_sink *sink = impl_from_IMFStreamSink(iface); + + TRACE("%p, %p, %p.\n", iface, result, event); + + if (sink->is_shut_down) + return MF_E_STREAMSINK_REMOVED; + + return IMFMediaEventQueue_EndGetEvent(sink->stream_event_queue, result, event); +} + +static HRESULT WINAPI video_frame_sink_stream_QueueEvent(IMFStreamSink *iface, MediaEventType event_type, + REFGUID ext_type, HRESULT hr, const PROPVARIANT *value) +{ + struct video_frame_sink *sink = impl_from_IMFStreamSink(iface); + + TRACE("%p, %lu, %s, %#lx, %p.\n", iface, event_type, debugstr_guid(ext_type), hr, value); + + if (sink->is_shut_down) + return MF_E_STREAMSINK_REMOVED; + + return IMFMediaEventQueue_QueueEventParamVar(sink->stream_event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI video_frame_sink_stream_GetMediaSink(IMFStreamSink *iface, IMFMediaSink **ret) +{ + struct video_frame_sink *sink = impl_from_IMFStreamSink(iface); + + TRACE("%p, %p.\n", iface, ret); + + if (sink->is_shut_down) + return MF_E_STREAMSINK_REMOVED; + + *ret = &sink->IMFMediaSink_iface; + IMFMediaSink_AddRef(*ret); + + return S_OK; +} + +static HRESULT WINAPI video_frame_sink_stream_GetIdentifier(IMFStreamSink *iface, DWORD *identifier) +{ + struct video_frame_sink *sink = impl_from_IMFStreamSink(iface); + + TRACE("%p, %p.\n", iface, identifier); + + if (sink->is_shut_down) + return MF_E_STREAMSINK_REMOVED; + + *identifier = 0; + + return S_OK; +} + +static HRESULT WINAPI video_frame_sink_stream_GetMediaTypeHandler(IMFStreamSink *iface, IMFMediaTypeHandler **handler) +{ + struct video_frame_sink *sink = impl_from_IMFStreamSink(iface); + + TRACE("%p, %p.\n", iface, handler); + + if (!handler) + return E_POINTER; + + if (sink->is_shut_down) + return MF_E_STREAMSINK_REMOVED; + + *handler = &sink->IMFMediaTypeHandler_iface; + IMFMediaTypeHandler_AddRef(*handler); + + return S_OK; +} + +/* must be called with critical section held */ +static void video_frame_sink_stream_request_sample(struct video_frame_sink *sink) +{ + if (sink->sample_request_pending || sink->eos) + return; + + IMFStreamSink_QueueEvent(&sink->IMFStreamSink_iface, MEStreamSinkRequestSample, &GUID_NULL, S_OK, NULL); + sink->sample_request_pending = TRUE; +} + +static void video_frame_sink_notify(struct video_frame_sink *sink, unsigned int event) +{ + IMFAsyncResult *result; + + if (FAILED(MFCreateAsyncResult(NULL, sink->callback, NULL, &result))) + return; + + IMFAsyncResult_SetStatus(result, event); + MFInvokeCallback(result); + IMFAsyncResult_Release(result); +} + +static void sample_index_increment(int *index) +{ + int prev = *index; + *index = (prev + 1) % 2; +} + +static HRESULT WINAPI video_frame_sink_stream_ProcessSample(IMFStreamSink *iface, IMFSample *sample) +{ + struct video_frame_sink *sink = impl_from_IMFStreamSink(iface); + LONGLONG sampletime; + HRESULT hr = S_OK; + + TRACE("%p, %p.\n", iface, sample); + + if (!sample) + return S_OK; + + EnterCriticalSection(&sink->cs); + + sink->sample_request_pending = FALSE; + + if (sink->is_shut_down) + { + hr = MF_E_STREAMSINK_REMOVED; + } + else if (sink->state == SINK_STATE_RUNNING || sink->state == SINK_STATE_PAUSED) + { + int sample_write_index = sink->sample_write_index; + hr = IMFSample_GetSampleTime(sample, &sampletime); + + if (sink->sample[sample_write_index]) + { + IMFSample_Release(sink->sample[sample_write_index]); + sink->sample[sample_write_index] = NULL; + } + + if (SUCCEEDED(hr)) + { + if (!(sink->flags & FLAGS_FIRST_FRAME)) + { + video_frame_sink_stream_request_sample(sink); + video_frame_sink_notify(sink, MF_MEDIA_ENGINE_EVENT_FIRSTFRAMEREADY); + video_frame_sink_set_flag(sink, FLAGS_FIRST_FRAME, TRUE); + } + + IMFSample_AddRef(sink->sample[sample_write_index] = sample); + sample_index_increment(&sink->sample_write_index); + } + } + + LeaveCriticalSection(&sink->cs); + + return hr; +} + +static HRESULT WINAPI video_frame_sink_stream_PlaceMarker(IMFStreamSink *iface, MFSTREAMSINK_MARKER_TYPE marker_type, + const PROPVARIANT *marker_value, const PROPVARIANT *context_value) +{ + struct video_frame_sink *sink = impl_from_IMFStreamSink(iface); + HRESULT hr = S_OK; + + TRACE("%p, %d, %p, %p.\n", iface, marker_type, marker_value, context_value); + + EnterCriticalSection(&sink->cs); + + if (sink->is_shut_down) + { + hr = MF_E_STREAMSINK_REMOVED; + } + else if (sink->state == SINK_STATE_RUNNING) + { + video_frame_sink_samples_release(sink); + hr = IMFMediaEventQueue_QueueEventParamVar(sink->stream_event_queue, MEStreamSinkMarker, + &GUID_NULL, S_OK, context_value); + } + + LeaveCriticalSection(&sink->cs); + + return hr; +} + +static HRESULT WINAPI video_frame_sink_stream_Flush(IMFStreamSink *iface) +{ + struct video_frame_sink *sink = impl_from_IMFStreamSink(iface); + HRESULT hr = S_OK; + + TRACE("%p.\n", iface); + + EnterCriticalSection(&sink->cs); + + if (sink->is_shut_down) + hr = MF_E_STREAMSINK_REMOVED; + else + video_frame_sink_samples_release(sink); + + LeaveCriticalSection(&sink->cs); + + return hr; +} + +static const IMFStreamSinkVtbl video_frame_sink_stream_vtbl = +{ + video_frame_sink_stream_QueryInterface, + video_frame_sink_stream_AddRef, + video_frame_sink_stream_Release, + video_frame_sink_stream_GetEvent, + video_frame_sink_stream_BeginGetEvent, + video_frame_sink_stream_EndGetEvent, + video_frame_sink_stream_QueueEvent, + video_frame_sink_stream_GetMediaSink, + video_frame_sink_stream_GetIdentifier, + video_frame_sink_stream_GetMediaTypeHandler, + video_frame_sink_stream_ProcessSample, + video_frame_sink_stream_PlaceMarker, + video_frame_sink_stream_Flush, +}; + +static HRESULT WINAPI video_frame_sink_stream_type_handler_QueryInterface(IMFMediaTypeHandler *iface, REFIID riid, + void **obj) +{ + struct video_frame_sink *sink = impl_from_IMFMediaTypeHandler(iface); + return IMFStreamSink_QueryInterface(&sink->IMFStreamSink_iface, riid, obj); +} + +static ULONG WINAPI video_frame_sink_stream_type_handler_AddRef(IMFMediaTypeHandler *iface) +{ + struct video_frame_sink *sink = impl_from_IMFMediaTypeHandler(iface); + return IMFStreamSink_AddRef(&sink->IMFStreamSink_iface); +} + +static ULONG WINAPI video_frame_sink_stream_type_handler_Release(IMFMediaTypeHandler *iface) +{ + struct video_frame_sink *sink = impl_from_IMFMediaTypeHandler(iface); + return IMFStreamSink_Release(&sink->IMFStreamSink_iface); +} + +static HRESULT video_frame_sink_stream_is_media_type_supported(struct video_frame_sink *sink, IMFMediaType *in_type) +{ + const DWORD supported_flags = MF_MEDIATYPE_EQUAL_MAJOR_TYPES | MF_MEDIATYPE_EQUAL_FORMAT_TYPES | + MF_MEDIATYPE_EQUAL_FORMAT_DATA; + DWORD flags; + + if (sink->is_shut_down) + return MF_E_STREAMSINK_REMOVED; + + if (!in_type) + return E_POINTER; + + if (IMFMediaType_IsEqual(sink->media_type, in_type, &flags) == S_OK) + return S_OK; + + return (flags & supported_flags) == supported_flags ? S_OK : MF_E_INVALIDMEDIATYPE; +} + +static HRESULT WINAPI video_frame_sink_stream_type_handler_IsMediaTypeSupported(IMFMediaTypeHandler *iface, + IMFMediaType *in_type, IMFMediaType **out_type) +{ + struct video_frame_sink *sink = impl_from_IMFMediaTypeHandler(iface); + + TRACE("%p, %p, %p.\n", iface, in_type, out_type); + + return video_frame_sink_stream_is_media_type_supported(sink, in_type); +} + +static HRESULT WINAPI video_frame_sink_stream_type_handler_GetMediaTypeCount(IMFMediaTypeHandler *iface, DWORD *count) +{ + TRACE("%p, %p.\n", iface, count); + + if (!count) + return E_POINTER; + + *count = 0; + + return S_OK; +} + +static HRESULT WINAPI video_frame_sink_stream_type_handler_GetMediaTypeByIndex(IMFMediaTypeHandler *iface, DWORD index, + IMFMediaType **media_type) +{ + TRACE("%p, %lu, %p.\n", iface, index, media_type); + + if (!media_type) + return E_POINTER; + + return MF_E_NO_MORE_TYPES; +} + +static HRESULT WINAPI video_frame_sink_stream_type_handler_SetCurrentMediaType(IMFMediaTypeHandler *iface, + IMFMediaType *media_type) +{ + struct video_frame_sink *sink = impl_from_IMFMediaTypeHandler(iface); + HRESULT hr; + + TRACE("%p, %p.\n", iface, media_type); + + if (FAILED(hr = video_frame_sink_stream_is_media_type_supported(sink, media_type))) + return hr; + + IMFMediaType_Release(sink->current_media_type); + sink->current_media_type = media_type; + IMFMediaType_AddRef(sink->current_media_type); + + return S_OK; +} + +static HRESULT WINAPI video_frame_sink_stream_type_handler_GetCurrentMediaType(IMFMediaTypeHandler *iface, + IMFMediaType **media_type) +{ + struct video_frame_sink *sink = impl_from_IMFMediaTypeHandler(iface); + HRESULT hr = S_OK; + + TRACE("%p, %p.\n", iface, media_type); + + if (!media_type) + return E_POINTER; + + EnterCriticalSection(&sink->cs); + + if (sink->is_shut_down) + { + hr = MF_E_STREAMSINK_REMOVED; + } + else + { + *media_type = sink->current_media_type; + IMFMediaType_AddRef(*media_type); + } + + LeaveCriticalSection(&sink->cs); + + return hr; +} + +static HRESULT WINAPI video_frame_sink_stream_type_handler_GetMajorType(IMFMediaTypeHandler *iface, GUID *type) +{ + struct video_frame_sink *sink = impl_from_IMFMediaTypeHandler(iface); + HRESULT hr; + + TRACE("%p, %p.\n", iface, type); + + if (!type) + return E_POINTER; + + EnterCriticalSection(&sink->cs); + + if (sink->is_shut_down) + hr = MF_E_STREAMSINK_REMOVED; + else + hr = IMFMediaType_GetMajorType(sink->current_media_type, type); + + LeaveCriticalSection(&sink->cs); + + return hr; +} + +static const IMFMediaTypeHandlerVtbl video_frame_sink_stream_type_handler_vtbl = +{ + video_frame_sink_stream_type_handler_QueryInterface, + video_frame_sink_stream_type_handler_AddRef, + video_frame_sink_stream_type_handler_Release, + video_frame_sink_stream_type_handler_IsMediaTypeSupported, + video_frame_sink_stream_type_handler_GetMediaTypeCount, + video_frame_sink_stream_type_handler_GetMediaTypeByIndex, + video_frame_sink_stream_type_handler_SetCurrentMediaType, + video_frame_sink_stream_type_handler_GetCurrentMediaType, + video_frame_sink_stream_type_handler_GetMajorType, +}; + +static HRESULT WINAPI video_frame_sink_QueryInterface(IMFMediaSink *iface, REFIID riid, void **obj) +{ + struct video_frame_sink *sink = impl_from_IMFMediaSink(iface); + + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFMediaSink) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = &sink->IMFMediaSink_iface; + } + else if (IsEqualIID(riid, &IID_IMFClockStateSink)) + { + *obj = &sink->IMFClockStateSink_iface; + } + else if (IsEqualIID(riid, &IID_IMFMediaEventGenerator)) + { + *obj = &sink->IMFMediaEventGenerator_iface; + } + else + { + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*obj); + + return S_OK; +} + +static ULONG WINAPI video_frame_sink_AddRef(IMFMediaSink *iface) +{ + struct video_frame_sink *sink = impl_from_IMFMediaSink(iface); + ULONG refcount = InterlockedIncrement(&sink->refcount); + + TRACE("%p, refcount %lu.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI video_frame_sink_Release(IMFMediaSink *iface) +{ + struct video_frame_sink *sink = impl_from_IMFMediaSink(iface); + ULONG refcount = InterlockedDecrement(&sink->refcount); + + TRACE("%p, refcount %lu.\n", iface, refcount); + + if (!refcount) + { + if (sink->current_media_type) + IMFMediaType_Release(sink->current_media_type); + IMFMediaType_Release(sink->media_type); + if (sink->event_queue) + IMFMediaEventQueue_Release(sink->event_queue); + if (sink->clock) + IMFPresentationClock_Release(sink->clock); + if (sink->callback) + IMFAsyncCallback_Release(sink->callback); + if (sink->stream_event_queue) + { + IMFMediaEventQueue_Shutdown(sink->stream_event_queue); + IMFMediaEventQueue_Release(sink->stream_event_queue); + } + video_frame_sink_samples_release(sink); + DeleteCriticalSection(&sink->cs); + free(sink); + } + + return refcount; +} + +static HRESULT WINAPI video_frame_sink_GetCharacteristics(IMFMediaSink *iface, DWORD *flags) +{ + struct video_frame_sink *sink = impl_from_IMFMediaSink(iface); + + TRACE("%p, %p.\n", iface, flags); + + if (sink->is_shut_down) + return MF_E_SHUTDOWN; + + *flags = MEDIASINK_FIXED_STREAMS | MEDIASINK_RATELESS | MEDIASINK_CAN_PREROLL; + + return S_OK; +} + +static HRESULT WINAPI video_frame_sink_AddStreamSink(IMFMediaSink *iface, DWORD stream_sink_id, + IMFMediaType *media_type, IMFStreamSink **stream_sink) +{ + struct video_frame_sink *sink = impl_from_IMFMediaSink(iface); + + TRACE("%p, %#lx, %p, %p.\n", iface, stream_sink_id, media_type, stream_sink); + + return sink->is_shut_down ? MF_E_SHUTDOWN : MF_E_STREAMSINKS_FIXED; +} + +static HRESULT WINAPI video_frame_sink_RemoveStreamSink(IMFMediaSink *iface, DWORD stream_sink_id) +{ + struct video_frame_sink *sink = impl_from_IMFMediaSink(iface); + + TRACE("%p, %#lx.\n", iface, stream_sink_id); + + return sink->is_shut_down ? MF_E_SHUTDOWN : MF_E_STREAMSINKS_FIXED; +} + +static HRESULT WINAPI video_frame_sink_GetStreamSinkCount(IMFMediaSink *iface, DWORD *count) +{ + struct video_frame_sink *sink = impl_from_IMFMediaSink(iface); + + TRACE("%p, %p.\n", iface, count); + + if (sink->is_shut_down) + return MF_E_SHUTDOWN; + + *count = 1; + + return S_OK; +} + +static HRESULT WINAPI video_frame_sink_GetStreamSinkByIndex(IMFMediaSink *iface, DWORD index, + IMFStreamSink **stream) +{ + struct video_frame_sink *sink = impl_from_IMFMediaSink(iface); + HRESULT hr = S_OK; + + TRACE("%p, %lu, %p.\n", iface, index, stream); + + EnterCriticalSection(&sink->cs); + + if (sink->is_shut_down) + hr = MF_E_SHUTDOWN; + else if (index > 0) + hr = MF_E_INVALIDINDEX; + else + { + *stream = &sink->IMFStreamSink_iface; + IMFStreamSink_AddRef(*stream); + } + + LeaveCriticalSection(&sink->cs); + + return hr; +} + +static HRESULT WINAPI video_frame_sink_GetStreamSinkById(IMFMediaSink *iface, DWORD stream_sink_id, + IMFStreamSink **stream) +{ + struct video_frame_sink *sink = impl_from_IMFMediaSink(iface); + HRESULT hr = S_OK; + + TRACE("%p, %#lx, %p.\n", iface, stream_sink_id, stream); + + EnterCriticalSection(&sink->cs); + + if (sink->is_shut_down) + hr = MF_E_SHUTDOWN; + else if (stream_sink_id > 0) + hr = MF_E_INVALIDSTREAMNUMBER; + else + { + *stream = &sink->IMFStreamSink_iface; + IMFStreamSink_AddRef(*stream); + } + + LeaveCriticalSection(&sink->cs); + + return hr; +} + +static void video_frame_sink_set_presentation_clock(struct video_frame_sink *sink, IMFPresentationClock *clock) +{ + if (sink->clock) + { + IMFPresentationClock_RemoveClockStateSink(sink->clock, &sink->IMFClockStateSink_iface); + IMFPresentationClock_Release(sink->clock); + } + sink->clock = clock; + if (sink->clock) + { + IMFPresentationClock_AddRef(sink->clock); + IMFPresentationClock_AddClockStateSink(sink->clock, &sink->IMFClockStateSink_iface); + } +} + +static HRESULT WINAPI video_frame_sink_SetPresentationClock(IMFMediaSink *iface, IMFPresentationClock *clock) +{ + struct video_frame_sink *sink = impl_from_IMFMediaSink(iface); + HRESULT hr = S_OK; + + TRACE("%p, %p.\n", iface, clock); + + EnterCriticalSection(&sink->cs); + + if (sink->is_shut_down) + { + hr = MF_E_SHUTDOWN; + } + else + { + video_frame_sink_set_presentation_clock(sink, clock); + } + + LeaveCriticalSection(&sink->cs); + + return hr; +} + +static HRESULT WINAPI video_frame_sink_GetPresentationClock(IMFMediaSink *iface, IMFPresentationClock **clock) +{ + struct video_frame_sink *sink = impl_from_IMFMediaSink(iface); + HRESULT hr = S_OK; + + TRACE("%p, %p.\n", iface, clock); + + if (!clock) + return E_POINTER; + + EnterCriticalSection(&sink->cs); + + if (sink->clock) + { + *clock = sink->clock; + IMFPresentationClock_AddRef(*clock); + } + else + hr = MF_E_NO_CLOCK; + + LeaveCriticalSection(&sink->cs); + + return hr; +} + +static HRESULT WINAPI video_frame_sink_Shutdown(IMFMediaSink *iface) +{ + struct video_frame_sink *sink = impl_from_IMFMediaSink(iface); + HRESULT hr; + + TRACE("%p.\n", iface); + + EnterCriticalSection(&sink->cs); + + if (sink->is_shut_down) + hr = MF_E_SHUTDOWN; + else + { + sink->is_shut_down = TRUE; + video_frame_sink_set_presentation_clock(sink, NULL); + IMFMediaType_Release(sink->current_media_type); + sink->current_media_type = NULL; + IMFAsyncCallback_Release(sink->callback); + sink->callback = NULL; + IMFMediaEventQueue_Shutdown(sink->stream_event_queue); + IMFMediaEventQueue_Shutdown(sink->event_queue); + } + + LeaveCriticalSection(&sink->cs); + + return hr; +} + +static const IMFMediaSinkVtbl video_frame_sink_vtbl = +{ + video_frame_sink_QueryInterface, + video_frame_sink_AddRef, + video_frame_sink_Release, + video_frame_sink_GetCharacteristics, + video_frame_sink_AddStreamSink, + video_frame_sink_RemoveStreamSink, + video_frame_sink_GetStreamSinkCount, + video_frame_sink_GetStreamSinkByIndex, + video_frame_sink_GetStreamSinkById, + video_frame_sink_SetPresentationClock, + video_frame_sink_GetPresentationClock, + video_frame_sink_Shutdown, +}; + +static HRESULT WINAPI video_frame_sink_clock_sink_QueryInterface(IMFClockStateSink *iface, REFIID riid, void **obj) +{ + struct video_frame_sink *sink = impl_from_IMFClockStateSink(iface); + return IMFMediaSink_QueryInterface(&sink->IMFMediaSink_iface, riid, obj); +} + +static ULONG WINAPI video_frame_sink_clock_sink_AddRef(IMFClockStateSink *iface) +{ + struct video_frame_sink *sink = impl_from_IMFClockStateSink(iface); + return IMFMediaSink_AddRef(&sink->IMFMediaSink_iface); +} + +static ULONG WINAPI video_frame_sink_clock_sink_Release(IMFClockStateSink *iface) +{ + struct video_frame_sink *sink = impl_from_IMFClockStateSink(iface); + return IMFMediaSink_Release(&sink->IMFMediaSink_iface); +} + +static HRESULT video_frame_sink_set_state(struct video_frame_sink *sink, enum sink_state state, + MFTIME systime, LONGLONG offset) +{ + static const DWORD events[] = + { + MEStreamSinkStopped, /* SINK_STATE_STOPPED */ + MEStreamSinkPaused, /* SINK_STATE_PAUSED */ + MEStreamSinkStarted, /* SINK_STATE_RUNNING */ + }; + HRESULT hr = S_OK; + + EnterCriticalSection(&sink->cs); + + if (!sink->is_shut_down) + { + if (state == SINK_STATE_PAUSED && sink->state == SINK_STATE_STOPPED) + { + hr = MF_E_INVALID_STATE_TRANSITION; + } + else + { + if (state == SINK_STATE_STOPPED) + { + video_frame_sink_samples_release(sink); + video_frame_sink_set_flag(sink, FLAGS_FIRST_FRAME, FALSE); + } + + if (state == SINK_STATE_RUNNING && sink->state != SINK_STATE_RUNNING) + { + video_frame_sink_samples_release(sink); + video_frame_sink_stream_request_sample(sink); + } + + if (state != sink->state || state != SINK_STATE_PAUSED) + { + if (sink->rate == 0.0f && state == SINK_STATE_RUNNING) + IMFStreamSink_QueueEvent(&sink->IMFStreamSink_iface, MEStreamSinkScrubSampleComplete, + &GUID_NULL, S_OK, NULL); + + IMFStreamSink_QueueEvent(&sink->IMFStreamSink_iface, events[state], &GUID_NULL, S_OK, NULL); + } + sink->state = state; + } + } + + LeaveCriticalSection(&sink->cs); + + return hr; +} + +static HRESULT WINAPI video_frame_sink_clock_sink_OnClockStart(IMFClockStateSink *iface, MFTIME systime, LONGLONG offset) +{ + struct video_frame_sink *sink = impl_from_IMFClockStateSink(iface); + + TRACE("%p, %s, %s.\n", iface, debugstr_time(systime), debugstr_time(offset)); + + return video_frame_sink_set_state(sink, SINK_STATE_RUNNING, systime, offset); +} + +static HRESULT WINAPI video_frame_sink_clock_sink_OnClockStop(IMFClockStateSink *iface, MFTIME systime) +{ + struct video_frame_sink *sink = impl_from_IMFClockStateSink(iface); + + TRACE("%p, %s.\n", iface, debugstr_time(systime)); + + return video_frame_sink_set_state(sink, SINK_STATE_STOPPED, systime, 0); +} + +static HRESULT WINAPI video_frame_sink_clock_sink_OnClockPause(IMFClockStateSink *iface, MFTIME systime) +{ + struct video_frame_sink *sink = impl_from_IMFClockStateSink(iface); + + TRACE("%p, %s.\n", iface, debugstr_time(systime)); + + return video_frame_sink_set_state(sink, SINK_STATE_PAUSED, systime, 0); +} + +static HRESULT WINAPI video_frame_sink_clock_sink_OnClockRestart(IMFClockStateSink *iface, MFTIME systime) +{ + struct video_frame_sink *sink = impl_from_IMFClockStateSink(iface); + + TRACE("%p, %s.\n", iface, debugstr_time(systime)); + + return video_frame_sink_set_state(sink, SINK_STATE_RUNNING, systime, PRESENTATION_CURRENT_POSITION); +} + +static HRESULT WINAPI video_frame_sink_clock_sink_OnClockSetRate(IMFClockStateSink *iface, MFTIME systime, float rate) +{ + struct video_frame_sink *sink = impl_from_IMFClockStateSink(iface); + HRESULT hr = S_OK; + + TRACE("%p, %s, %f.\n", iface, debugstr_time(systime), rate); + + EnterCriticalSection(&sink->cs); + + if (sink->is_shut_down) + hr = MF_E_SHUTDOWN; + else + { + IMFStreamSink_QueueEvent(&sink->IMFStreamSink_iface, MEStreamSinkRateChanged, &GUID_NULL, S_OK, NULL); + sink->rate = rate; + } + + LeaveCriticalSection(&sink->cs); + + return hr; +} + +static HRESULT WINAPI video_frame_sink_events_QueryInterface(IMFMediaEventGenerator *iface, REFIID riid, void **obj) +{ + struct video_frame_sink *sink = impl_from_IMFMediaEventGenerator(iface); + return IMFMediaSink_QueryInterface(&sink->IMFMediaSink_iface, riid, obj); +} + +static ULONG WINAPI video_frame_sink_events_AddRef(IMFMediaEventGenerator *iface) +{ + struct video_frame_sink *sink = impl_from_IMFMediaEventGenerator(iface); + return IMFMediaSink_AddRef(&sink->IMFMediaSink_iface); +} + +static ULONG WINAPI video_frame_sink_events_Release(IMFMediaEventGenerator *iface) +{ + struct video_frame_sink *sink = impl_from_IMFMediaEventGenerator(iface); + return IMFMediaSink_Release(&sink->IMFMediaSink_iface); +} + +static HRESULT WINAPI video_frame_sink_events_GetEvent(IMFMediaEventGenerator *iface, DWORD flags, IMFMediaEvent **event) +{ + struct video_frame_sink *sink = impl_from_IMFMediaEventGenerator(iface); + + TRACE("%p, %#lx, %p.\n", iface, flags, event); + + return IMFMediaEventQueue_GetEvent(sink->event_queue, flags, event); +} + +static HRESULT WINAPI video_frame_sink_events_BeginGetEvent(IMFMediaEventGenerator *iface, IMFAsyncCallback *callback, + IUnknown *state) +{ + struct video_frame_sink *sink = impl_from_IMFMediaEventGenerator(iface); + + TRACE("%p, %p, %p.\n", iface, callback, state); + + return IMFMediaEventQueue_BeginGetEvent(sink->event_queue, callback, state); +} + +static HRESULT WINAPI video_frame_sink_events_EndGetEvent(IMFMediaEventGenerator *iface, IMFAsyncResult *result, + IMFMediaEvent **event) +{ + struct video_frame_sink *sink = impl_from_IMFMediaEventGenerator(iface); + + TRACE("%p, %p, %p.\n", iface, result, event); + + return IMFMediaEventQueue_EndGetEvent(sink->event_queue, result, event); +} + +static HRESULT WINAPI video_frame_sink_events_QueueEvent(IMFMediaEventGenerator *iface, MediaEventType event_type, + REFGUID ext_type, HRESULT hr, const PROPVARIANT *value) +{ + struct video_frame_sink *sink = impl_from_IMFMediaEventGenerator(iface); + + TRACE("%p, %lu, %s, %#lx, %p.\n", iface, event_type, debugstr_guid(ext_type), hr, value); + + return IMFMediaEventQueue_QueueEventParamVar(sink->event_queue, event_type, ext_type, hr, value); +} + +static const IMFMediaEventGeneratorVtbl video_frame_sink_events_vtbl = +{ + video_frame_sink_events_QueryInterface, + video_frame_sink_events_AddRef, + video_frame_sink_events_Release, + video_frame_sink_events_GetEvent, + video_frame_sink_events_BeginGetEvent, + video_frame_sink_events_EndGetEvent, + video_frame_sink_events_QueueEvent, +}; + +static const IMFClockStateSinkVtbl video_frame_sink_clock_sink_vtbl = +{ + video_frame_sink_clock_sink_QueryInterface, + video_frame_sink_clock_sink_AddRef, + video_frame_sink_clock_sink_Release, + video_frame_sink_clock_sink_OnClockStart, + video_frame_sink_clock_sink_OnClockStop, + video_frame_sink_clock_sink_OnClockPause, + video_frame_sink_clock_sink_OnClockRestart, + video_frame_sink_clock_sink_OnClockSetRate, +}; + +HRESULT create_video_frame_sink(IMFMediaType *media_type, IMFAsyncCallback *events_callback, struct video_frame_sink **sink) +{ + struct video_frame_sink *object; + HRESULT hr; + + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + + object->IMFMediaSink_iface.lpVtbl = &video_frame_sink_vtbl; + object->IMFClockStateSink_iface.lpVtbl = &video_frame_sink_clock_sink_vtbl; + object->IMFMediaEventGenerator_iface.lpVtbl = &video_frame_sink_events_vtbl; + object->IMFStreamSink_iface.lpVtbl = &video_frame_sink_stream_vtbl; + object->IMFMediaTypeHandler_iface.lpVtbl = &video_frame_sink_stream_type_handler_vtbl; + object->refcount = 1; + object->rate = 1.0f; + object->media_type = media_type; + IMFAsyncCallback_AddRef(object->callback = events_callback); + IMFMediaType_AddRef(object->media_type); + object->current_media_type = media_type; + IMFMediaType_AddRef(object->current_media_type); + InitializeCriticalSection(&object->cs); + + if (FAILED(hr = MFCreateEventQueue(&object->stream_event_queue))) + goto failed; + + if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) + goto failed; + + *sink = object; + + return S_OK; + +failed: + + IMFMediaSink_Release(&object->IMFMediaSink_iface); + + return hr; +} + +HRESULT video_frame_sink_query_iface(struct video_frame_sink *sink, REFIID riid, void **obj) +{ + return IMFStreamSink_QueryInterface(&sink->IMFStreamSink_iface, riid, obj); +} + +int video_frame_sink_get_sample(struct video_frame_sink *sink, IMFSample **ret) +{ + *ret = NULL; + + if (sink) + { + EnterCriticalSection(&sink->cs); + + if (sink->presentation_sample) + { + IMFSample_AddRef(*ret = sink->presentation_sample); + sink->sample_presented = TRUE; + } + + LeaveCriticalSection(&sink->cs); + } + + return !!*ret; +} + +static HRESULT sample_get_pts(IMFSample *sample, MFTIME clocktime, LONGLONG *pts) +{ + HRESULT hr = S_FALSE; + if (sample) + { + if (SUCCEEDED(hr = IMFSample_GetSampleTime(sample, pts))) + { + if (clocktime < *pts) + *pts = MINLONGLONG; + hr = *pts == MINLONGLONG ? S_FALSE : S_OK; + } + else + WARN("Failed to get sample time, hr %#lx.\n", hr); + } + return hr; +} + +HRESULT video_frame_sink_get_pts(struct video_frame_sink *sink, MFTIME clocktime, LONGLONG *pts) +{ + HRESULT hr = S_FALSE; + + *pts = MINLONGLONG; + if (sink) + { + int sample_read_index; + BOOL transfer_sample = FALSE; + EnterCriticalSection(&sink->cs); + sample_read_index = sink->sample_read_index; + hr = sample_get_pts(sink->sample[sample_read_index], clocktime, pts); + + if (hr == S_OK) + { + LONGLONG pts2; + transfer_sample = TRUE; + /* if the second sample we have is also OK, we'll drop the first and use the second */ + sample_index_increment(&sample_read_index); + if (sink->sample[sample_read_index] && sample_get_pts(sink->sample[sample_read_index], clocktime, &pts2) == S_OK) + { + *pts = pts2; + IMFSample_Release(sink->sample[sink->sample_read_index]); + sink->sample[sink->sample_read_index] = NULL; + sink->sample_read_index = sample_read_index; + } + } + else if (sink->presentation_sample && !sink->sample_presented) + { + hr = sample_get_pts(sink->presentation_sample, clocktime, pts); + } + + if (transfer_sample || !sink->sample[sink->sample_write_index]) + video_frame_sink_stream_request_sample(sink); + + if (transfer_sample) + { + if (sink->presentation_sample) + IMFSample_Release(sink->presentation_sample); + /* transfer ownership from sample array to presentation sample */ + sink->presentation_sample = sink->sample[sink->sample_read_index]; + sink->sample[sink->sample_read_index] = NULL; + sink->sample_presented = FALSE; + sample_index_increment(&sink->sample_read_index); + } + + LeaveCriticalSection(&sink->cs); + } + + return hr; +} + +void video_frame_sink_notify_end_of_presentation(struct video_frame_sink *sink) +{ + sink->eos = TRUE; +} + +ULONG video_frame_sink_release(struct video_frame_sink *sink) +{ + return video_frame_sink_Release(&sink->IMFMediaSink_iface); +} + From 3a83106f0a814855ccc91bb68a13df99230edcf8 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 17 May 2024 17:48:00 -0600 Subject: [PATCH 376/413] wine.inf: Add InstallationType field to CurrentVersion. CW-Bug-Id: #23825 --- loader/wine.inf.in | 1 + 1 file changed, 1 insertion(+) diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 1e5b16a53a6..3f6af1f27a2 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -2628,6 +2628,7 @@ HKLM,%CurrentVersionNT%,"DigitalProductId",2,00,00,00,00,00,00,00,00,00,00,00,\ 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00 HKLM,%CurrentVersionNT%,"DisplayVersion",2,"21H1" HKLM,%CurrentVersionNT%,"EditionId",2,"Professional" +HKLM,%CurrentVersionNT%,"InstallationType",2,"Client" HKLM,%CurrentVersionNT%,"ProductName",2,"Windows 10 Pro" HKLM,%CurrentVersionNT%,"ReleaseId",,"2009" HKLM,%Control%\ProductOptions,"ProductType",2,"WinNT" From 01e8eebf8ffab329549eb1c2f87867c5f1eb2aa5 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 20 May 2024 12:06:49 +0300 Subject: [PATCH 377/413] win32u: Update Nvidia's DriverVersion to match 552.44. This helps with an outdated driver warning in Ghost of Tsushima. --- dlls/win32u/sysparams.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 2e6e0915091..2b4a397af9b 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -1406,7 +1406,7 @@ static void add_gpu( const struct gdi_gpu *gpu, void *param ) break; /* Nvidia */ case 0x10de: - sprintf( buffer, "31.0.15.5212" ); + sprintf( buffer, "31.0.15.5244" ); break; /* Default value for any other vendor. */ default: From 218bb205f185275e939b19e723a2f267e3b4f06e Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Mon, 20 May 2024 11:26:24 -0500 Subject: [PATCH 378/413] fixup! winex11.drv: Do not shape layered windows in Gamescope. --- dlls/winex11.drv/bitblt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winex11.drv/bitblt.c b/dlls/winex11.drv/bitblt.c index 68858c7cefa..469900dce40 100644 --- a/dlls/winex11.drv/bitblt.c +++ b/dlls/winex11.drv/bitblt.c @@ -1643,7 +1643,7 @@ static void update_surface_region( struct x11drv_window_surface *surface ) if (!shape_layered_windows) return; - if (wm_is_steamcompmgr(gdi_display)) return; + if (wm_is_steamcompmgr(gdi_display) && surface->color_key == CLR_INVALID) return; if (!surface->is_argb && surface->color_key == CLR_INVALID) { From b93ba801a6a70f6f22779667299a66f83c816a6d Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 21 May 2024 16:53:42 -0600 Subject: [PATCH 379/413] wine.inf: Add Explorer\Advanced registry key. CW-Bug-Id: #23844 --- dlls/opengl32/wgl.c | 1 + loader/wine.inf.in | 2 ++ 2 files changed, 3 insertions(+) diff --git a/dlls/opengl32/wgl.c b/dlls/opengl32/wgl.c index 01f743023d9..3d458699344 100644 --- a/dlls/opengl32/wgl.c +++ b/dlls/opengl32/wgl.c @@ -1294,6 +1294,7 @@ GLboolean WINAPI glUnmapNamedBufferEXT( GLuint buffer ) static BOOL WINAPI call_opengl_debug_message_callback( struct wine_gl_debug_message_params *params, ULONG size ) { + ERR(".\n"); params->user_callback( params->source, params->type, params->id, params->severity, params->length, params->message, params->user_data ); return TRUE; diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 3f6af1f27a2..1f4019cc417 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -375,6 +375,7 @@ HKLM,System\CurrentControlSet\Control\Class\{745a17a0-74d3-11d0-b6fe-00a0c90f57d HKLM,System\CurrentControlSet\Control\Class\{745a17a0-74d3-11d0-b6fe-00a0c90f57da},"Class",,"HIDClass" [CurrentVersion] +HKCU,%CurrentVersion%\Explorer\Advanced,,16 HKCU,%CurrentVersion%\Run,,16 HKCU,%CurrentVersionNT%\Winlogon,,16 HKLM,%CurrentVersion%,"CommonFilesDir",,"%16427%" @@ -389,6 +390,7 @@ HKLM,%CurrentVersion%\Control Panel\Cursors\Schemes,,16 HKLM,%CurrentVersion%\Controls Folder\PowerCfg,"DiskSpinDownMax",,"3600" HKLM,%CurrentVersion%\Controls Folder\PowerCfg,"DiskSpinDownMin",,"3" HKLM,%CurrentVersion%\Controls Folder\PowerCfg,"LastID",,"5" +HKLM,%CurrentVersion%\Explorer\Advanced,,16 HKLM,%CurrentVersion%\Explorer\AutoplayHandlers,,16 HKLM,%CurrentVersion%\Explorer\DriveIcons,,16 HKLM,%CurrentVersion%\Explorer\KindMap,,16 From fa03486b68b3f0a6c002edcfac743036b7b088e4 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 21 May 2024 16:57:52 -0600 Subject: [PATCH 380/413] fixup! wine.inf: Add Explorer\Advanced registry key. CW-Bug-Id: #23844 --- dlls/opengl32/wgl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/dlls/opengl32/wgl.c b/dlls/opengl32/wgl.c index 3d458699344..01f743023d9 100644 --- a/dlls/opengl32/wgl.c +++ b/dlls/opengl32/wgl.c @@ -1294,7 +1294,6 @@ GLboolean WINAPI glUnmapNamedBufferEXT( GLuint buffer ) static BOOL WINAPI call_opengl_debug_message_callback( struct wine_gl_debug_message_params *params, ULONG size ) { - ERR(".\n"); params->user_callback( params->source, params->type, params->id, params->severity, params->length, params->message, params->user_data ); return TRUE; From 2b51f9bca6408b2f81af4977f7e29bac508f54f8 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 21 May 2024 19:43:54 -0600 Subject: [PATCH 381/413] pdh: Add stub for PdhGetFormattedCounterArray(). CW-Bug-Id: #23849 --- dlls/pdh/pdh.spec | 4 ++-- dlls/pdh/pdh_main.c | 20 ++++++++++++++++++++ include/pdh.h | 12 ++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/dlls/pdh/pdh.spec b/dlls/pdh/pdh.spec index 711c970029b..9cdd441947b 100644 --- a/dlls/pdh/pdh.spec +++ b/dlls/pdh/pdh.spec @@ -58,8 +58,8 @@ @ stub PdhGetDefaultPerfObjectHW @ stub PdhGetDefaultPerfObjectW @ stdcall PdhGetDllVersion(ptr) -@ stub PdhGetFormattedCounterArrayA -@ stub PdhGetFormattedCounterArrayW +@ stdcall PdhGetFormattedCounterArrayA(ptr long ptr ptr ptr) +@ stdcall PdhGetFormattedCounterArrayW(ptr long ptr ptr ptr) @ stdcall PdhGetFormattedCounterValue(ptr long ptr ptr) @ stub PdhGetLogFileSize @ stdcall PdhGetLogFileTypeA(str ptr) diff --git a/dlls/pdh/pdh_main.c b/dlls/pdh/pdh_main.c index 12bda6bedc0..0bf4608c119 100644 --- a/dlls/pdh/pdh_main.c +++ b/dlls/pdh/pdh_main.c @@ -793,6 +793,26 @@ PDH_STATUS WINAPI PdhGetRawCounterValue( PDH_HCOUNTER handle, LPDWORD type, return ERROR_SUCCESS; } +PDH_STATUS WINAPI PdhGetFormattedCounterArrayW( PDH_HCOUNTER handle, DWORD format, DWORD *size, DWORD *count, + PDH_FMT_COUNTERVALUE_ITEM_W *buffer) +{ + FIXME( "%p %lu %p %p %p stub.\n", handle, format, size, count, buffer ); + + *size = 0; + *count = 0; + return ERROR_SUCCESS; +} + +PDH_STATUS WINAPI PdhGetFormattedCounterArrayA( PDH_HCOUNTER handle, DWORD format, DWORD *size, DWORD *count, + PDH_FMT_COUNTERVALUE_ITEM_A *buffer) +{ + FIXME( "%p %lu %p %p %p stub.\n", handle, format, size, count, buffer ); + + *size = 0; + *count = 0; + return ERROR_SUCCESS; +} + /*********************************************************************** * PdhLookupPerfIndexByNameA (PDH.@) */ diff --git a/include/pdh.h b/include/pdh.h index 3d688bd8ded..5a6d89e91c9 100644 --- a/include/pdh.h +++ b/include/pdh.h @@ -189,6 +189,18 @@ typedef struct _PDH_COUNTER_INFO_W DWORD DataBuffer[1]; } PDH_COUNTER_INFO_W, *PPDH_COUNTER_INFO_W; +typedef struct _PDH_FMT_COUNTERVALUE_ITEM_A +{ + LPSTR szName; + PDH_FMT_COUNTERVALUE FmtValue; +} PDH_FMT_COUNTERVALUE_ITEM_A, *PPDH_FMT_COUNTERVALUE_ITEM_A; + +typedef struct _PDH_FMT_COUNTERVALUE_ITEM_W +{ + LPWSTR szName; + PDH_FMT_COUNTERVALUE FmtValue; +} PDH_FMT_COUNTERVALUE_ITEM_W, *PPDH_FMT_COUNTERVALUE_ITEM_W; + DECL_PDH_TYPE_AW(PDH_COUNTER_INFO) DECL_PDH_TYPE_AW(PPDH_COUNTER_INFO) From d4a7c82e655efe2e8f890a0bcfb5458996f9be4f Mon Sep 17 00:00:00 2001 From: Ziqing Hui Date: Thu, 23 May 2024 13:22:53 +0800 Subject: [PATCH 382/413] winegstreamer/media-converter: Continue creating state object even if env is not set. This make sure media-converter work with steam shader cache disabled. STEAM_COMPAT_MEDIA_PATH and STEAM_COMPAT_TRANSCODED_MEDIA_PATH won't be set with steam shader cache disabled, resuling in other media-converter envs missing. CW-Bug-Id: #23856 --- .../winegstreamer/media-converter/audioconv.c | 18 ++++++------- .../winegstreamer/media-converter/videoconv.c | 26 +++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/dlls/winegstreamer/media-converter/audioconv.c b/dlls/winegstreamer/media-converter/audioconv.c index 71612cbfddd..462cec16a8a 100644 --- a/dlls/winegstreamer/media-converter/audioconv.c +++ b/dlls/winegstreamer/media-converter/audioconv.c @@ -682,15 +682,18 @@ static int audio_conv_state_create(struct audio_conv_state **out) char *read_fozdb_path; int ret; - if (!(read_fozdb_path = getenv("MEDIACONV_AUDIO_TRANSCODED_FILE"))) + if ((read_fozdb_path = getenv("MEDIACONV_AUDIO_TRANSCODED_FILE"))) + { + if ((ret = fozdb_create(read_fozdb_path, O_RDONLY, true /* Read-only? */, + AUDIO_CONV_FOZ_NUM_TAGS, &fozdb)) < 0) + GST_ERROR("Failed to create fozdb from %s, ret %d.", read_fozdb_path, ret); + } + else { GST_ERROR("Env MEDIACONV_AUDIO_TRANSCODED_FILE is not set!"); - return CONV_ERROR_ENV_NOT_SET; + ret = CONV_ERROR_ENV_NOT_SET; } - if ((ret = fozdb_create(read_fozdb_path, O_RDONLY, true /* Read-only? */, AUDIO_CONV_FOZ_NUM_TAGS, &fozdb)) < 0) - GST_ERROR("Failed to create fozdb from %s, ret %d.", read_fozdb_path, ret); - state = calloc(1, sizeof(*state)); murmur3_128_state_init(&state->hash_state, HASH_SEED); murmur3_128_state_init(&state->loop_hash_state, HASH_SEED); @@ -698,7 +701,7 @@ static int audio_conv_state_create(struct audio_conv_state **out) state->read_fozdb = fozdb; *out = state; - return CONV_OK; + return ret; } static void audio_conv_state_release(struct audio_conv_state *state) @@ -870,10 +873,7 @@ static GstStateChangeReturn audio_conv_change_state(GstElement *element, GstStat /* Create audio conv state. */ if ((ret = audio_conv_state_create(&state)) < 0) - { GST_ERROR("Failed to create audio conv state, ret %d.", ret); - return GST_STATE_CHANGE_FAILURE; - } pthread_mutex_lock(&conv->state_mutex); assert(!conv->state); conv->state = state; diff --git a/dlls/winegstreamer/media-converter/videoconv.c b/dlls/winegstreamer/media-converter/videoconv.c index 184989647bf..70b92a84485 100644 --- a/dlls/winegstreamer/media-converter/videoconv.c +++ b/dlls/winegstreamer/media-converter/videoconv.c @@ -348,17 +348,11 @@ static int video_conv_state_create(struct video_conv_state **out) uint64_t blank_file_size; int ret, fd; - if (!(read_fozdb_path = getenv("MEDIACONV_VIDEO_TRANSCODED_FILE"))) - { - GST_ERROR("MEDIACONV_VIDEO_TRANSCODED_FILE is not set."); - return CONV_ERROR_ENV_NOT_SET; - } if (!(blank_video = getenv("MEDIACONV_BLANK_VIDEO_FILE"))) { GST_ERROR("Env MEDIACONV_BLANK_VIDEO_FILE not set."); return CONV_ERROR_ENV_NOT_SET; } - if (!open_file(blank_video, O_RDONLY, &fd)) return CONV_ERROR_OPEN_FAILED; if (!get_file_size(fd, &blank_file_size)) @@ -367,8 +361,17 @@ static int video_conv_state_create(struct video_conv_state **out) return CONV_ERROR_OPEN_FAILED; } - if ((ret = fozdb_create(read_fozdb_path, O_RDONLY, true /* Read-only? */, VIDEO_CONV_FOZ_NUM_TAGS, &fozdb)) < 0) - GST_ERROR("Failed to create read fozdb from %s, ret %d.", read_fozdb_path, ret); + if ((read_fozdb_path = getenv("MEDIACONV_VIDEO_TRANSCODED_FILE"))) + { + if ((ret = fozdb_create(read_fozdb_path, O_RDONLY, true /* Read-only? */, + VIDEO_CONV_FOZ_NUM_TAGS, &fozdb)) < 0) + GST_ERROR("Failed to create read fozdb from %s, ret %d.", read_fozdb_path, ret); + } + else + { + GST_ERROR("Env MEDIACONV_VIDEO_TRANSCODED_FILE is not set."); + ret = CONV_ERROR_ENV_NOT_SET; + } state = calloc(1, sizeof(*state)); state->read_fozdb = fozdb; @@ -378,7 +381,7 @@ static int video_conv_state_create(struct video_conv_state **out) state->transcoded_tag = VIDEO_CONV_FOZ_TAG_MKVDATA; *out = state; - return CONV_OK; + return ret; } static void video_conv_state_release(struct video_conv_state *state) @@ -474,7 +477,7 @@ static struct video_conv_state *video_conv_lock_state(VideoConv *conv) static GstStateChangeReturn video_conv_change_state(GstElement *element, GstStateChange transition) { VideoConv *conv = VIDEO_CONV(element); - struct video_conv_state *state; + struct video_conv_state *state = NULL; int ret; GST_INFO_OBJECT(element, "State transition: %s.", gst_state_change_get_name(transition)); @@ -484,10 +487,7 @@ static GstStateChangeReturn video_conv_change_state(GstElement *element, GstStat case GST_STATE_CHANGE_NULL_TO_READY: /* Do runtime setup. */ if ((ret = video_conv_state_create(&state)) < 0) - { GST_ERROR("Failed to create video conv state, ret %d.", ret); - return GST_STATE_CHANGE_FAILURE; - } pthread_mutex_lock(&conv->state_mutex); assert(!conv->state); conv->state = state; From 1e78a35949da4895fc8088b94c168c174a85c9e3 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 23 May 2024 22:45:49 -0600 Subject: [PATCH 383/413] win32u: HACK: Hide a window for Syberia. Until it is possible to properly implement exclusive fullscreen ddraw mode. CW-Bug-Id: #23858 --- dlls/win32u/window.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index 2dcc3e20d3e..0e639068694 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -475,6 +475,17 @@ HWND WINAPI NtUserSetParent( HWND hwnd, HWND parent ) set_window_pos( &winpos, new_screen_rect.left - old_screen_rect.left, new_screen_rect.top - old_screen_rect.top ); + { + WCHAR name[32]; + UNICODE_STRING us = { 0, sizeof(name), name }; + + if (NtUserGetClassName( hwnd, FALSE, &us ) && !wcscmp( us.Buffer, u"SyberiaRenderWindowClass" )) + { + ERR( "HACK: Hiding window.\n" ); + was_visible = FALSE; + } + } + if (was_visible) NtUserShowWindow( hwnd, SW_SHOW ); SetThreadDpiAwarenessContext( context ); From ae9017ae2f63607fbf425d4f40cc1349a07f4f2d Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 24 May 2024 19:08:52 -0600 Subject: [PATCH 384/413] mf: Implement audio capture device enumeration in MFEnumDeviceSources(). CW-Bug-Id: #23847 --- dlls/mf/main.c | 150 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 144 insertions(+), 6 deletions(-) diff --git a/dlls/mf/main.c b/dlls/mf/main.c index 92558a86b30..d3556dcae64 100644 --- a/dlls/mf/main.c +++ b/dlls/mf/main.c @@ -32,6 +32,10 @@ #include "wine/list.h" #include "wine/mfinternal.h" +#include "mmdeviceapi.h" +#include "initguid.h" +#include "devpkey.h" + WINE_DEFAULT_DEBUG_CHANNEL(mfplat); extern const GUID CLSID_FileSchemePlugin; @@ -744,19 +748,153 @@ HRESULT WINAPI MFShutdownObject(IUnknown *object) return S_OK; } + +static HRESULT source_create_object(IMFAttributes *attributes, void *user_context, IUnknown **obj) +{ + FIXME("%p, %p, %p stub.\n", attributes, user_context, obj); + + return E_NOTIMPL; +} + +static void source_shutdown_object(void *user_context, IUnknown *obj) +{ + TRACE("%p %p.\n", user_context, obj); +} + +static void source_free_private(void *user_context) +{ + TRACE("%p.\n", user_context); +} + +static const struct activate_funcs source_activate_funcs = +{ + source_create_object, + source_shutdown_object, + source_free_private, +}; + /*********************************************************************** * MFEnumDeviceSources (mf.@) */ -HRESULT WINAPI MFEnumDeviceSources(IMFAttributes *attributes, IMFActivate ***sources, UINT32 *count) -{ - FIXME("%p, %p, %p.\n", attributes, sources, count); +HRESULT WINAPI MFEnumDeviceSources(IMFAttributes *attributes, IMFActivate ***sources, UINT32 *ret_count) +{ + UINT32 i, count, default_index = 0; + IMMDeviceEnumerator *devenum = NULL; + IMMDeviceCollection *devices = NULL; + WCHAR *default_id = NULL; + IMMDevice *device; + GUID source_type; + HRESULT hr; - if (!attributes || !sources || !count) + TRACE("%p, %p, %p.\n", attributes, sources, ret_count); + + if (!attributes || !sources || !ret_count) return E_INVALIDARG; - *count = 0; + *ret_count = 0; - return S_OK; + if (FAILED(hr = IMFAttributes_GetGUID(attributes, &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, &source_type))) + { + TRACE("MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE not found.\n"); + return hr; + } + if (!IsEqualIID(&source_type, &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID)) + { + FIXME("Unknown source type %s.\n", debugstr_guid(&source_type)); + return S_OK; + } + + if (FAILED(hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, + (void **)&devenum))) + goto done; + if (FAILED(hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, eCapture, DEVICE_STATE_ACTIVE, &devices))) + goto done; + if (FAILED(hr = IMMDeviceCollection_GetCount(devices, &count))) + goto done; + if (SUCCEEDED(hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, eCapture, eMultimedia, &device))) + { + IMMDevice_GetId(device, &default_id); + IMMDevice_Release(device); + } + *sources = count ? CoTaskMemAlloc(count * sizeof(**sources)) : NULL; + for (i = 0; i < count; ++i) + { + IPropertyStore *ps; + PROPVARIANT pv; + WCHAR *str; + + if (FAILED(hr = create_activation_object(NULL, &source_activate_funcs, &(*sources)[i]))) + goto device_error; + IMFActivate_SetGUID((*sources)[i], &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID); + if (FAILED(hr = IMMDeviceCollection_Item(devices, i, &device))) + { + IMFActivate_Release((*sources)[i]); + goto device_error; + } + if (FAILED(IMMDevice_GetId(device, &str))) + { + IMFActivate_Release((*sources)[i]); + IMMDevice_Release(device); + goto device_error; + } + IMFActivate_SetString((*sources)[i], &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_ENDPOINT_ID, str); + if (!wcscmp(str, default_id)) + default_index = i; + CoTaskMemFree(str); + + hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps); + IMMDevice_Release(device); + if (FAILED(hr)) + { + IMFActivate_Release((*sources)[i]); + goto device_error; + } + hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv); + IPropertyStore_Release(ps); + if (FAILED(hr)) + { + IMFActivate_Release((*sources)[i]); + goto device_error; + } + if (pv.vt != VT_LPWSTR) + { + IMFActivate_Release((*sources)[i]); + PropVariantClear(&pv); + goto device_error; + } + IMFActivate_SetString((*sources)[i], &MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, pv.pwszVal); + PropVariantClear(&pv); + continue; + +device_error: + if (default_index == i) + default_index = 0; + --i; + --count; + } + *ret_count = count; + if (default_index) + { + IMFActivate *tmp = (*sources)[0]; + + (*sources)[0] = (*sources)[default_index]; + (*sources)[default_index] = tmp; + } + if (!count) + { + CoTaskMemFree(*sources); + *sources = NULL; + } + hr = S_OK; + +done: + CoTaskMemFree(default_id); + if (devices) + IMMDeviceCollection_Release(devices); + if (devenum) + IMMDeviceEnumerator_Release(devenum); + TRACE("ret %#lx, *ret_count %u.\n", hr, *ret_count); + return hr; } struct simple_type_handler From 7374ef563eac28f94e0ce9b6b16929496bb3af4e Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Mon, 27 May 2024 13:41:59 +1000 Subject: [PATCH 385/413] mfmediaengine: Request second sample if we only have one. This matches what Windows does and improves the audio and video sync if video playback is lagging. CW-Bug-Id: #22585 --- dlls/mfmediaengine/video_frame_sink.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dlls/mfmediaengine/video_frame_sink.c b/dlls/mfmediaengine/video_frame_sink.c index 706192fa8a7..025b2dd341a 100644 --- a/dlls/mfmediaengine/video_frame_sink.c +++ b/dlls/mfmediaengine/video_frame_sink.c @@ -346,13 +346,14 @@ static HRESULT WINAPI video_frame_sink_stream_ProcessSample(IMFStreamSink *iface { if (!(sink->flags & FLAGS_FIRST_FRAME)) { - video_frame_sink_stream_request_sample(sink); video_frame_sink_notify(sink, MF_MEDIA_ENGINE_EVENT_FIRSTFRAMEREADY); video_frame_sink_set_flag(sink, FLAGS_FIRST_FRAME, TRUE); } IMFSample_AddRef(sink->sample[sample_write_index] = sample); sample_index_increment(&sink->sample_write_index); + if (!sink->sample[sink->sample_write_index]) + video_frame_sink_stream_request_sample(sink); } } @@ -1169,11 +1170,9 @@ HRESULT video_frame_sink_get_pts(struct video_frame_sink *sink, MFTIME clocktime hr = sample_get_pts(sink->presentation_sample, clocktime, pts); } - if (transfer_sample || !sink->sample[sink->sample_write_index]) - video_frame_sink_stream_request_sample(sink); - if (transfer_sample) { + video_frame_sink_stream_request_sample(sink); if (sink->presentation_sample) IMFSample_Release(sink->presentation_sample); /* transfer ownership from sample array to presentation sample */ From 6390ca6f55bbc80290de0c8f6f45ad3852e09061 Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Fri, 31 May 2024 12:06:54 -0400 Subject: [PATCH 386/413] uiautomationcore: NULL initialize SAFEARRAY variable passed to IRawElementProviderFragment::GetRuntimeId(). Fixes a crash in Battle.net setup where success is returned but the variable is not set. Signed-off-by: Connor McAdams --- dlls/uiautomationcore/uia_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index df9b2b283e0..8d1e3c8c7dc 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -1542,7 +1542,7 @@ static HRESULT uia_provider_get_special_prop_val(struct uia_provider *prov, case UIA_RuntimeIdPropertyId: { IRawElementProviderFragment *elfrag; - SAFEARRAY *sa; + SAFEARRAY *sa = NULL; LONG lbound; int val; From 5cebcb8ebc2a84e4fffb8cea2b40494f3f2bd95e Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Mon, 3 Jun 2024 13:23:21 +0200 Subject: [PATCH 387/413] ntdll: Provide fallback values for some DMI table entries. This is a port of upstream e39a973778198c638c5062d4e2e436bfa24a13c0. (to avoid importing a bunch of other changes) CW-Bug-Id: https://www.codeweavers.com/support/bugs/browse?cmd=bug_edit;bug_id=22831;layout=my_bugs --- dlls/ntdll/unix/system.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index 6c1ca2ce6ce..a349d3b01eb 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -1914,6 +1914,14 @@ static void get_system_uuid( GUID *uuid ) } } +static size_t fixup_missing_information( const GUID *uuid, char *buffer, size_t buflen ) +{ + const BYTE *p = (const BYTE *)uuid; + snprintf( buffer, 33, "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", p[0], p[1], + p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15] ); + return strlen(buffer); +} + static NTSTATUS get_firmware_info( SYSTEM_FIRMWARE_TABLE_INFORMATION *sfti, ULONG available_len, ULONG *required_len ) { @@ -1975,6 +1983,17 @@ static NTSTATUS get_firmware_info( SYSTEM_FIRMWARE_TABLE_INFORMATION *sfti, ULON chassis_args.serial = chassis_serial; chassis_args.asset_tag_len = get_smbios_string("/sys/class/dmi/id/chassis_tag", S(chassis_asset_tag)); chassis_args.asset_tag = chassis_asset_tag; + + /* Some fields may not have been read + * (either, they are not filled by the BIOS, or some of the files above are only readable by root). + * Ensure that some fields are always filled in. + */ + if (!board_args.serial_len) + board_args.serial_len = fixup_missing_information(&system_args.uuid, S(board_serial)); + if (!chassis_args.serial_len) + chassis_args.serial_len = strlen(strcpy(chassis_serial, "Chassis Serial Number")); + if (!system_args.serial_len) + system_args.serial_len = strlen(strcpy(system_serial, "System Serial Number")); #undef S return create_smbios_tables( sfti, available_len, required_len, From 071b4a5bdadffdce27a8467e5846e2798e5d35c7 Mon Sep 17 00:00:00 2001 From: Benjamin Mayes Date: Fri, 31 May 2024 22:54:12 -0400 Subject: [PATCH 388/413] windowscodecs: Add conversions from PixelFormat32bppBGRA->PixelFormat16bppBGRA5551. (cherry picked from commit dcb5c97d02d58a0478e735eb7158c9bf3239791c) --- dlls/windowscodecs/converter.c | 65 +++++++++++++++++++++++++++- dlls/windowscodecs/tests/converter.c | 21 +++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/dlls/windowscodecs/converter.c b/dlls/windowscodecs/converter.c index 2f7f5c971ca..4720a360feb 100644 --- a/dlls/windowscodecs/converter.c +++ b/dlls/windowscodecs/converter.c @@ -1492,6 +1492,69 @@ static HRESULT copypixels_to_8bppIndexed(struct FormatConverter *This, const WIC return hr; } +static HRESULT copypixels_to_16bppBGRA5551(struct FormatConverter *This, const WICRect *prc, + UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer, enum pixelformat source_format) +{ + switch (source_format) + { + case format_16bppBGRA5551: + if (prc) + return IWICBitmapSource_CopyPixels(This->source, prc, cbStride, cbBufferSize, pbBuffer); + return S_OK; + case format_32bppBGRA: + if(prc) + { + HRESULT res; + INT x, y; + BYTE *srcdata; + UINT srcstride, srcdatasize; + const BYTE *srcrow; + const DWORD *srcpixel; + BYTE *dstrow; + DWORD srcval = 0; + WORD *dstpixel; + + int a, r, g, b; + + srcstride = 4 * prc->Width; + srcdatasize = srcstride * prc->Height; + + srcdata = malloc(srcdatasize); + if (!srcdata) return E_OUTOFMEMORY; + + res = IWICBitmapSource_CopyPixels(This->source, prc, srcstride, srcdatasize, srcdata); + if(SUCCEEDED(res)) + { + srcrow = srcdata; + dstrow = pbBuffer; + for(y=0; y< prc->Height; y++) { + srcpixel = (const DWORD*)srcrow; + dstpixel = (WORD *)dstrow; + for(x=0; xWidth; x++) { + srcval=*srcpixel++; + a = (srcval & 0xff000000) >> 24; + r = (srcval & 0x00ff0000) >> 16; + g = (srcval & 0x0000ff00) >> 8; + b = (srcval & 0x000000ff); + a = (a >> 7) << 15; + r = (r >> 3) << 10; + g = (g >> 3) << 5; + b = (b >> 3); + *dstpixel++ = (a|r|g|b); + } + srcrow += srcstride; + dstrow += cbStride; + } + } + free(srcdata); + } + return S_OK; + default: + FIXME("Unimplemented conversion path! %d\n", source_format); + return WINCODEC_ERR_UNSUPPORTEDOPERATION; + } +} + static const struct pixelformatinfo supported_formats[] = { {format_1bppIndexed, &GUID_WICPixelFormat1bppIndexed, NULL, TRUE}, {format_2bppIndexed, &GUID_WICPixelFormat2bppIndexed, NULL, TRUE}, @@ -1504,7 +1567,7 @@ static const struct pixelformatinfo supported_formats[] = { {format_16bppGray, &GUID_WICPixelFormat16bppGray, NULL}, {format_16bppBGR555, &GUID_WICPixelFormat16bppBGR555, NULL}, {format_16bppBGR565, &GUID_WICPixelFormat16bppBGR565, NULL}, - {format_16bppBGRA5551, &GUID_WICPixelFormat16bppBGRA5551, NULL}, + {format_16bppBGRA5551, &GUID_WICPixelFormat16bppBGRA5551, copypixels_to_16bppBGRA5551}, {format_24bppBGR, &GUID_WICPixelFormat24bppBGR, copypixels_to_24bppBGR}, {format_24bppRGB, &GUID_WICPixelFormat24bppRGB, copypixels_to_24bppRGB}, {format_32bppGrayFloat, &GUID_WICPixelFormat32bppGrayFloat, copypixels_to_32bppGrayFloat}, diff --git a/dlls/windowscodecs/tests/converter.c b/dlls/windowscodecs/tests/converter.c index d13ee896e95..ff2a1e0a067 100644 --- a/dlls/windowscodecs/tests/converter.c +++ b/dlls/windowscodecs/tests/converter.c @@ -602,6 +602,26 @@ static const BYTE bits_24bppBGR_gray[] = { static const struct bitmap_data testdata_24bppBGR_gray = { &GUID_WICPixelFormat24bppBGR, 24, bits_24bppBGR_gray, 32, 2, 96.0, 96.0}; +#define TO_16bppBGRA5551(b,g,r,a) ( \ + ((a >> 7) << 15) | \ + ((r >> 3) << 10) | \ + ((g >> 3) << 5) | \ + ((b >> 3)) \ +) + +static const WORD bits_16bppBGRA5551[] = { + TO_16bppBGRA5551(255,0,0,255), TO_16bppBGRA5551(0,255,0,255), TO_16bppBGRA5551(0,0,255,255), TO_16bppBGRA5551(0,0,0,255), TO_16bppBGRA5551(255,0,0,255), TO_16bppBGRA5551(0,255,0,255), TO_16bppBGRA5551(0,0,255,255), TO_16bppBGRA5551(0,0,0,255), + TO_16bppBGRA5551(255,0,0,255), TO_16bppBGRA5551(0,255,0,255), TO_16bppBGRA5551(0,0,255,255), TO_16bppBGRA5551(0,0,0,255), TO_16bppBGRA5551(255,0,0,255), TO_16bppBGRA5551(0,255,0,255), TO_16bppBGRA5551(0,0,255,255), TO_16bppBGRA5551(0,0,0,255), + TO_16bppBGRA5551(255,0,0,255), TO_16bppBGRA5551(0,255,0,255), TO_16bppBGRA5551(0,0,255,255), TO_16bppBGRA5551(0,0,0,255), TO_16bppBGRA5551(255,0,0,255), TO_16bppBGRA5551(0,255,0,255), TO_16bppBGRA5551(0,0,255,255), TO_16bppBGRA5551(0,0,0,255), + TO_16bppBGRA5551(255,0,0,255), TO_16bppBGRA5551(0,255,0,255), TO_16bppBGRA5551(0,0,255,255), TO_16bppBGRA5551(0,0,0,255), TO_16bppBGRA5551(255,0,0,255), TO_16bppBGRA5551(0,255,0,255), TO_16bppBGRA5551(0,0,255,255), TO_16bppBGRA5551(0,0,0,255), + TO_16bppBGRA5551(0,255,255,255), TO_16bppBGRA5551(255,0,255,255), TO_16bppBGRA5551(255,255,0,255), TO_16bppBGRA5551(255,255,255,255), TO_16bppBGRA5551(0,255,255,255), TO_16bppBGRA5551(255,0,255,255), TO_16bppBGRA5551(255,255,0,255), TO_16bppBGRA5551(255,255,255,255), + TO_16bppBGRA5551(0,255,255,255), TO_16bppBGRA5551(255,0,255,255), TO_16bppBGRA5551(255,255,0,255), TO_16bppBGRA5551(255,255,255,255), TO_16bppBGRA5551(0,255,255,255), TO_16bppBGRA5551(255,0,255,255), TO_16bppBGRA5551(255,255,0,255), TO_16bppBGRA5551(255,255,255,255), + TO_16bppBGRA5551(0,255,255,255), TO_16bppBGRA5551(255,0,255,255), TO_16bppBGRA5551(255,255,0,255), TO_16bppBGRA5551(255,255,255,255), TO_16bppBGRA5551(0,255,255,255), TO_16bppBGRA5551(255,0,255,255), TO_16bppBGRA5551(255,255,0,255), TO_16bppBGRA5551(255,255,255,255), + TO_16bppBGRA5551(0,255,255,255), TO_16bppBGRA5551(255,0,255,255), TO_16bppBGRA5551(255,255,0,255), TO_16bppBGRA5551(255,255,255,255), TO_16bppBGRA5551(0,255,255,255), TO_16bppBGRA5551(255,0,255,255), TO_16bppBGRA5551(255,255,0,255), TO_16bppBGRA5551(255,255,255,255)}; + +static const struct bitmap_data testdata_16bppBGRA5551 = { + &GUID_WICPixelFormat16bppBGRA5551, 16, (BYTE*)bits_16bppBGRA5551, 32, 2, 96.0, 96.0}; + static void test_conversion(const struct bitmap_data *src, const struct bitmap_data *dst, const char *name, BOOL todo) { BitmapTestSrc *src_obj; @@ -2054,6 +2074,7 @@ START_TEST(converter) test_conversion(&testdata_32bppBGR, &testdata_8bppGray, "32bppBGR -> 8bppGray", FALSE); test_conversion(&testdata_32bppGrayFloat, &testdata_24bppBGR_gray, "32bppGrayFloat -> 24bppBGR gray", FALSE); test_conversion(&testdata_32bppGrayFloat, &testdata_8bppGray, "32bppGrayFloat -> 8bppGray", FALSE); + test_conversion(&testdata_32bppBGRA, &testdata_16bppBGRA5551, "32bppBGRA -> 16bppBGRA5551", FALSE); test_invalid_conversion(); test_default_converter(); From ed0ffc2242994b8d65e887fe6590c87f6af6eed5 Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Mon, 3 Jun 2024 11:36:14 -0300 Subject: [PATCH 389/413] HACK: winex11.drv: Workaround fullscreen issue with Sleeping Dogs. Cw-Bug-Id: #20283 --- dlls/winex11.drv/event.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 48903cd5623..b0edbf88da9 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -1219,9 +1219,9 @@ static BOOL X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev ) { const char *steamgameid = getenv( "SteamGameId" ); - if (steamgameid && !strcmp( steamgameid, "590380" )) + if (steamgameid && (!strcmp( steamgameid, "590380" ) || !strcmp( steamgameid, "307690" ))) { - /* Into The Breach is extremely picky about the size of its window. */ + /* Into The Breach and Sleeping Dogs are extremely picky about the size of its window. */ if (NtUserIsWindowRectFullScreen( &data->whole_rect ) && NtUserIsWindowRectFullScreen( &rect )) { TRACE( "window is fullscreen and new size is also fullscreen, so preserving window size\n" ); From 824ccfa43f05bf7eeb39bccaec91b464bc2614e7 Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Mon, 3 Jun 2024 11:43:39 -0300 Subject: [PATCH 390/413] HACK: winex11.drv: Add option to enable fullscreen workaround for preserving window size. Cw-Bug-Id: #20283 --- dlls/winex11.drv/event.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index b0edbf88da9..e379b8ce02c 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -1218,8 +1218,10 @@ static BOOL X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev ) { const char *steamgameid = getenv( "SteamGameId" ); + const char *preserve_window_size = getenv( "FULLSCREEN_PRESERVE_WINDOW_SIZE" ); - if (steamgameid && (!strcmp( steamgameid, "590380" ) || !strcmp( steamgameid, "307690" ))) + if ((preserve_window_size && *preserve_window_size == '1') || + (steamgameid && (!strcmp( steamgameid, "590380" ) || !strcmp( steamgameid, "307690" )))) { /* Into The Breach and Sleeping Dogs are extremely picky about the size of its window. */ if (NtUserIsWindowRectFullScreen( &data->whole_rect ) && NtUserIsWindowRectFullScreen( &rect )) From 411f89315e5402f088db2368b22ba0b2deeddfe3 Mon Sep 17 00:00:00 2001 From: Kai Krakow Date: Wed, 5 Jun 2024 23:08:01 +0200 Subject: [PATCH 391/413] winebus.sys: Prepare support for a secondary Virpil USB VID --- dlls/winebus.sys/unixlib.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/dlls/winebus.sys/unixlib.c b/dlls/winebus.sys/unixlib.c index 09d3dd64c0b..ffbf53b574c 100644 --- a/dlls/winebus.sys/unixlib.c +++ b/dlls/winebus.sys/unixlib.c @@ -124,13 +124,16 @@ static BOOL is_vkb_controller(WORD vid, WORD pid, INT buttons) static BOOL is_virpil_controller(WORD vid, WORD pid, INT buttons) { - if (vid != 0x3344) return FALSE; - - /* comes with 31 buttons in the default configuration, or 128 max */ - if ((buttons == 31) || (buttons == 128)) return TRUE; + switch (vid) + { + case 0x3344: + /* comes with 31 buttons in the default configuration, or 128 max */ + if ((buttons == 31) || (buttons == 128)) return TRUE; - /* if customized, arbitrary amount of buttons may be shown, decide by PID */ - if (pid == 0x412f) return TRUE; /* Virpil Constellation ALPHA-R */ + /* if customized, arbitrary amount of buttons may be shown, decide by PID */ + if (pid == 0x412f) return TRUE; /* Virpil Constellation ALPHA-R */ + break; + } return FALSE; } From 670a3a79e87873b4b26666d5e8fe3240d9267955 Mon Sep 17 00:00:00 2001 From: Kai Krakow Date: Wed, 5 Jun 2024 23:10:00 +0200 Subject: [PATCH 392/413] winebuy.sys: Add support for Virpil Throttle MT-50 CM2 Link: https://github.com/ValveSoftware/Proton/issues/6839#issuecomment-2148791581 --- dlls/winebus.sys/unixlib.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dlls/winebus.sys/unixlib.c b/dlls/winebus.sys/unixlib.c index ffbf53b574c..1f8ffe1f448 100644 --- a/dlls/winebus.sys/unixlib.c +++ b/dlls/winebus.sys/unixlib.c @@ -126,6 +126,9 @@ static BOOL is_virpil_controller(WORD vid, WORD pid, INT buttons) { switch (vid) { + case 0x03eb: + if (pid == 0x2055) return TRUE; /* ATMEL/VIRPIL/200325 VPC Throttle MT-50 CM2 */ + break; case 0x3344: /* comes with 31 buttons in the default configuration, or 128 max */ if ((buttons == 31) || (buttons == 128)) return TRUE; From f952c5218c6e0ac0e9a9e820e1ca6ab91c0dbc48 Mon Sep 17 00:00:00 2001 From: Kai Krakow Date: Wed, 5 Jun 2024 23:11:08 +0200 Subject: [PATCH 393/413] winebuy.sys: Add support for Virpil Constellation Alpha-L Link: https://github.com/ValveSoftware/Proton/issues/6839#issuecomment-2144038021 --- dlls/winebus.sys/unixlib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/winebus.sys/unixlib.c b/dlls/winebus.sys/unixlib.c index 1f8ffe1f448..1be4f37ac9e 100644 --- a/dlls/winebus.sys/unixlib.c +++ b/dlls/winebus.sys/unixlib.c @@ -135,6 +135,7 @@ static BOOL is_virpil_controller(WORD vid, WORD pid, INT buttons) /* if customized, arbitrary amount of buttons may be shown, decide by PID */ if (pid == 0x412f) return TRUE; /* Virpil Constellation ALPHA-R */ + if (pid == 0x812c) return TRUE; /* Virpil Constellation ALPHA-L */ break; } return FALSE; From 8a34bfc5c21b68eeb9e69ff58e5bb0f304a33348 Mon Sep 17 00:00:00 2001 From: Kai Krakow Date: Wed, 5 Jun 2024 23:14:43 +0200 Subject: [PATCH 394/413] winebuy.sys: Add heuristic for typical Virpil user configurations Some games support a maximum of 32, 50 or 64 buttons specifically, thus we're seeing HOTAS configurations out there which are adjusted to show exactly that amount of buttons. Let's add it as a heuristic for Virpil game controllers. Link: https://github.com/ValveSoftware/Proton/issues/6839#issuecomment-2149579749 --- dlls/winebus.sys/unixlib.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dlls/winebus.sys/unixlib.c b/dlls/winebus.sys/unixlib.c index 1be4f37ac9e..c3407e39b3f 100644 --- a/dlls/winebus.sys/unixlib.c +++ b/dlls/winebus.sys/unixlib.c @@ -127,12 +127,18 @@ static BOOL is_virpil_controller(WORD vid, WORD pid, INT buttons) switch (vid) { case 0x03eb: + /* users may have configured button limits, usually 32/50/64 */ + if ((buttons == 32) || (buttons == 50) || (buttons == 64)) return TRUE; + if (pid == 0x2055) return TRUE; /* ATMEL/VIRPIL/200325 VPC Throttle MT-50 CM2 */ break; case 0x3344: /* comes with 31 buttons in the default configuration, or 128 max */ if ((buttons == 31) || (buttons == 128)) return TRUE; + /* users may have configured button limits, usually 32/50/64 */ + if ((buttons == 32) || (buttons == 50) || (buttons == 64)) return TRUE; + /* if customized, arbitrary amount of buttons may be shown, decide by PID */ if (pid == 0x412f) return TRUE; /* Virpil Constellation ALPHA-R */ if (pid == 0x812c) return TRUE; /* Virpil Constellation ALPHA-L */ From 360e01a288f2858c5329e604244cde5a316cb332 Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Tue, 28 May 2024 12:24:24 +1000 Subject: [PATCH 395/413] kernel32/tests: Test error code when FindFirstFileA uses file as directory. (cherry picked from commit 34446f8cb66036dd3cd0083a558eafd351317150) CW-Bug-Id: #23860 --- dlls/kernel32/tests/file.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index 17cfe24d50a..f47128ee0a0 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -2665,6 +2665,7 @@ static void test_FindFirstFileA(void) char buffer[5] = "C:\\"; char buffer2[100]; char nonexistent[MAX_PATH]; + BOOL found = FALSE; /* try FindFirstFileA on "C:\" */ buffer[0] = get_windows_drive(); @@ -2696,10 +2697,32 @@ static void test_FindFirstFileA(void) ok( FindNextFileA( handle, &data ), "FindNextFile failed\n" ); ok( !strcmp( data.cFileName, ".." ), "FindNextFile should return '..' as second entry\n" ); while (FindNextFileA( handle, &data )) + { ok ( strcmp( data.cFileName, "." ) && strcmp( data.cFileName, ".." ), "FindNextFile shouldn't return '%s'\n", data.cFileName ); + if (!found && (data.dwFileAttributes == FILE_ATTRIBUTE_NORMAL || + data.dwFileAttributes == FILE_ATTRIBUTE_ARCHIVE)) + { + GetWindowsDirectoryA( buffer2, sizeof(buffer2) ); + strcat(buffer2, "\\"); + strcat(buffer2, data.cFileName); + strcat(buffer2, "\\*"); + found = TRUE; + } + } ok ( FindClose(handle) == TRUE, "Failed to close handle %s\n", buffer2 ); + ok ( found, "Windows dir should not be empty\n" ); + if (found) + { + SetLastError( 0xdeadbeef ); + handle = FindFirstFileA(buffer2, &data); + err = GetLastError(); + ok ( handle == INVALID_HANDLE_VALUE, "FindFirstFile on %s should fail\n", buffer2 ); + todo_wine + ok ( err == ERROR_DIRECTORY, "Bad Error number %x\n", err ); + } + /* try FindFirstFileA on "C:\foo\" */ SetLastError( 0xdeadbeaf ); if (!GetTempFileNameA( buffer, "foo", 0, nonexistent )) From 92d0dfd02ffa5bf74c94f88daa9611207775ea87 Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Wed, 29 May 2024 11:46:11 +1000 Subject: [PATCH 396/413] ntdll/tests: Test error code when NtOpenFile uses file as directory. (cherry picked from commit a0c2f40b4e26128bd3ba99613eeba4b2e41d6e05) CW-Bug-Id: #23860 --- dlls/ntdll/tests/file.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index c8e598b9288..7f8dfc68c3e 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -457,6 +457,16 @@ static void open_file_test(void) CloseHandle( handle ); pRtlFreeUnicodeString( &nameW ); + wcscat( path, L"\\" ); + pRtlDosPathNameToNtPathName_U( path, &nameW, NULL, NULL ); + status = NtOpenFile( &handle, FILE_LIST_DIRECTORY | SYNCHRONIZE, &attr, &io, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT ); + todo_wine + ok( status == STATUS_NOT_A_DIRECTORY, "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); + CloseHandle( handle ); + pRtlFreeUnicodeString( &nameW ); + wcscat( path, L"\\cmd.exe" ); pRtlDosPathNameToNtPathName_U( path, &nameW, NULL, NULL ); status = pNtOpenFile( &handle, GENERIC_READ, &attr, &io, From 2e73791ae44c9848b3c40d1e89157bf73329c18c Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Wed, 29 May 2024 11:52:18 +1000 Subject: [PATCH 397/413] server: Don't always return STATUS_OBJECT_NAME_INVALID on ENOTDIR. Only return STATUS_OBJECT_NAME_INVALID if we're not looking for a FILE_DIRECTORY_FILE. (cherry picked from commit d88863805dc4b79d965577a31b2845c0c81a09d9) CW-Bug-Id: #23860 --- dlls/kernel32/tests/file.c | 1 - dlls/ntdll/tests/file.c | 1 - server/fd.c | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index f47128ee0a0..cd7442b70fb 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -2719,7 +2719,6 @@ static void test_FindFirstFileA(void) handle = FindFirstFileA(buffer2, &data); err = GetLastError(); ok ( handle == INVALID_HANDLE_VALUE, "FindFirstFile on %s should fail\n", buffer2 ); - todo_wine ok ( err == ERROR_DIRECTORY, "Bad Error number %x\n", err ); } diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 7f8dfc68c3e..3a585cc155f 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -462,7 +462,6 @@ static void open_file_test(void) status = NtOpenFile( &handle, FILE_LIST_DIRECTORY | SYNCHRONIZE, &attr, &io, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT ); - todo_wine ok( status == STATUS_NOT_A_DIRECTORY, "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); CloseHandle( handle ); pRtlFreeUnicodeString( &nameW ); diff --git a/server/fd.c b/server/fd.c index 36e4697e33b..04d5f9bbe10 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2000,7 +2000,7 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam if (fd->unix_fd == -1) { /* check for trailing slash on file path */ - if ((errno == ENOENT || errno == ENOTDIR) && name[strlen(name) - 1] == '/') + if ((errno == ENOENT || (errno == ENOTDIR && !(options & FILE_DIRECTORY_FILE))) && name[strlen(name) - 1] == '/') set_error( STATUS_OBJECT_NAME_INVALID ); else file_set_error(); From 9fbe6c5d9498b202862e8b52fa7c189a38e1e9f3 Mon Sep 17 00:00:00 2001 From: James Hollingworth Date: Fri, 31 May 2024 12:21:54 +0100 Subject: [PATCH 398/413] autogen.sh: Run tools/make_specfiles. If you don't call tools/make_specfiles then build will fail saying it's missing ntsyscalls.h. Link: https://github.com/ValveSoftware/wine/pull/239 --- autogen.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/autogen.sh b/autogen.sh index afd4a144aad..a173b6b4fce 100755 --- a/autogen.sh +++ b/autogen.sh @@ -2,6 +2,7 @@ set -e tools/make_requests dlls/winevulkan/make_vulkan -x vk.xml +tools/make_specfiles autoreconf -ifv rm -rf autom4te.cache From dc83797aafe30f8bc4c1457f0c9403fbf81f9f5c Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Mon, 10 Jun 2024 18:25:51 +0200 Subject: [PATCH 399/413] amend! ntdll: Provide fallback values for some DMI table entries. ntdll: Provide fallback values for some DMI table entries. This is a port of upstream e39a973778198c638c5062d4e2e436bfa24a13c0. (to avoid importing a bunch of other changes) CW-Bug-Id: 22831 From 9341c6789e6484c172fc66a7e9bfaef3b25f18a3 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Fri, 24 May 2024 18:20:18 +0000 Subject: [PATCH 400/413] win32u: Send EVENT_SYSTEM_FOREGROUND in another case. CW-Bug-Id: #23779 EVENT_SYSTEM_FOREGROUND is sent by set_active_window, but in some cases the active window is set before the foreground window, meaning the foreground window hasn't changed yet at the time of the event. This is most likely wrong, as it leads to duplicate EVENT_SYSTEM_FOREGROUND events, but solving it correctly is a thorny problem that will require tests. --- dlls/win32u/input.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 7c4e0363281..25e5aee16d5 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -2108,7 +2108,10 @@ BOOL set_foreground_window( HWND hwnd, BOOL mouse ) /* already active, set_active_window will do no nothing */ if (!send_msg_new && hwnd == get_active_window()) + { send_message( hwnd, WM_NCACTIVATE, TRUE, (LPARAM)hwnd ); + NtUserNotifyWinEvent( EVENT_SYSTEM_FOREGROUND, hwnd, 0, 0 ); + } if (send_msg_new) /* new window belongs to other thread */ NtUserPostMessage( hwnd, WM_WINE_SETACTIVEWINDOW, (WPARAM)hwnd, 0 ); From a662e302f089ab69b68dc7e12f2f050272b9839a Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Mon, 3 Jun 2024 20:07:59 +0000 Subject: [PATCH 401/413] win32u: Send EVENT_OBJECT_FOCUS in more cases. (cherry picked from commit 069a0b1e26823121e8beac3756a3c9c60422601a) --- dlls/win32u/input.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 25e5aee16d5..e835b88ff4e 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1865,8 +1865,7 @@ static HWND set_focus_window( HWND hwnd, BOOL from_active, BOOL force ) send_message( ime_hwnd, WM_IME_INTERNAL, IME_INTERNAL_ACTIVATE, HandleToUlong(hwnd) ); - if (previous) - NtUserNotifyWinEvent( EVENT_OBJECT_FOCUS, hwnd, OBJID_CLIENT, 0 ); + NtUserNotifyWinEvent( EVENT_OBJECT_FOCUS, hwnd, OBJID_CLIENT, 0 ); send_message( hwnd, WM_SETFOCUS, (WPARAM)previous, 0 ); } From ee6a15ec6790fc2e69adc0828db5d6863833e81d Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Tue, 11 Jun 2024 18:50:46 +0000 Subject: [PATCH 402/413] Revert "win32u: Implement EVENT_OBJECT_LOCATIONCHANGE." This reverts commit a854a85f125cb34f972f31c0e8eff4ec273a15e6. --- dlls/win32u/window.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index 0e639068694..71a99f6b376 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -3556,10 +3556,6 @@ BOOL set_window_pos( WINDOWPOS *winpos, int parent_x, int parent_y ) winpos->cy = new_window_rect.bottom - new_window_rect.top; send_message( winpos->hwnd, WM_WINDOWPOSCHANGED, 0, (LPARAM)winpos ); } - - if ((winpos->flags & (SWP_NOMOVE|SWP_NOSIZE)) != (SWP_NOMOVE|SWP_NOSIZE)) - NtUserNotifyWinEvent( EVENT_OBJECT_LOCATIONCHANGE, winpos->hwnd, OBJID_WINDOW, 0 ); - ret = TRUE; done: SetThreadDpiAwarenessContext( context ); From a9b70124452207d4f9f658929d635a9b98e77075 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Sat, 8 Jun 2024 19:58:16 +0000 Subject: [PATCH 403/413] win32u: Implement EVENT_OBJECT_LOCATIONCHANGE. (cherry picked from commit 479e0b27c8fe3eb658cef0246267d63596e557ac) --- dlls/win32u/window.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index 71a99f6b376..3f76161daa1 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -3556,6 +3556,10 @@ BOOL set_window_pos( WINDOWPOS *winpos, int parent_x, int parent_y ) winpos->cy = new_window_rect.bottom - new_window_rect.top; send_message( winpos->hwnd, WM_WINDOWPOSCHANGED, 0, (LPARAM)winpos ); } + + if ((winpos->flags & (SWP_NOSIZE|SWP_NOMOVE|SWP_FRAMECHANGED)) != (SWP_NOSIZE|SWP_NOMOVE)) + NtUserNotifyWinEvent( EVENT_OBJECT_LOCATIONCHANGE, winpos->hwnd, OBJID_WINDOW, 0 ); + ret = TRUE; done: SetThreadDpiAwarenessContext( context ); From 5ea3074ceda7c01a3af82c9c9a9617f8a9b6e520 Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Wed, 12 Jun 2024 12:22:22 +1000 Subject: [PATCH 404/413] kernelbase: HACK: Add WINE_SUPPRESS_XDG_ENV option. CW-Bug-Id: #23899 --- dlls/kernelbase/process.c | 66 +++++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 16 deletions(-) diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index 0898e30f603..08f39020189 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -1743,6 +1743,29 @@ LPSTR WINAPI DECLSPEC_HOTPATCH GetEnvironmentStringsA(void) return ret; } +static void remove_env_variables( const char **skip, SIZE_T array_size, WCHAR *env, SIZE_T len ) +{ + unsigned int i, j; + SIZE_T l; + + while (*env) + { + for (i = 0; i < array_size; ++i) + { + j = 0; + while (skip[i][j] && skip[i][j] == env[j]) + ++j; + if (!skip[i][j]) break; + } + l = lstrlenW( env ); + len -= (l + 1) * sizeof(WCHAR); + if (i == array_size) + env += l + 1; + else + memmove( env, env + l + 1, len ); + } +} + static void hack_shrink_environment( WCHAR *env, SIZE_T len ) { static int enabled = -1; @@ -1758,8 +1781,6 @@ static void hack_shrink_environment( WCHAR *env, SIZE_T len ) "BASH_FUNC_", "XDG_DATA_DIRS=", }; - SIZE_T l; - unsigned int i, j; if (enabled == -1) { @@ -1777,24 +1798,36 @@ static void hack_shrink_environment( WCHAR *env, SIZE_T len ) ERR( "HACK: shrinking environment size.\n" ); } - if (!enabled) return; + if (enabled) + remove_env_variables(skip, ARRAY_SIZE(skip), env, len); +} - while (*env) +static void hack_suppress_xdg_environment( WCHAR *env, SIZE_T len ) +{ + static int enabled = -1; + static const char *skip[] = { - for (i = 0; i < ARRAY_SIZE(skip); ++i) - { - j = 0; - while (skip[i][j] && skip[i][j] == env[j]) - ++j; - if (!skip[i][j]) break; - } - l = lstrlenW( env ); - len -= (l + 1) * sizeof(WCHAR); - if (i == ARRAY_SIZE(skip)) - env += l + 1; + "XDG_SESSION_TYPE=", + }; + + if (enabled == -1) + { + WCHAR str[40]; + + *str = 0; + if (GetEnvironmentVariableW( L"WINE_SUPPRESS_XDG_ENV", str, sizeof(str)) ) + enabled = *str != '0'; + else if (GetEnvironmentVariableW( L"SteamGameId", str, sizeof(str)) ) + enabled = !wcscmp( str, L"809230" ); else - memmove( env, env + l + 1, len ); + enabled = 0; + + if (enabled) + ERR( "HACK: suppressing XDG environment.\n" ); } + + if (enabled) + remove_env_variables(skip, ARRAY_SIZE(skip), env, len); } /*********************************************************************** @@ -1811,6 +1844,7 @@ LPWSTR WINAPI DECLSPEC_HOTPATCH GetEnvironmentStringsW(void) { memcpy( ret, NtCurrentTeb()->Peb->ProcessParameters->Environment, len ); hack_shrink_environment( ret, len ); + hack_suppress_xdg_environment( ret, len ); } RtlReleasePebLock(); return ret; From 5cfa0ea033bfe215c29f9e8224e8f79b3b7b48af Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 23 May 2024 19:43:34 -0600 Subject: [PATCH 405/413] winex11.drv: Always call resize_vk_surfaces() from sync_client_position(). CW-Bug-Id: #23855 --- dlls/winex11.drv/window.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 98723a302ed..ca99a845781 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1771,8 +1771,6 @@ static void sync_client_position( struct x11drv_win_data *data, int mask = 0; XWindowChanges changes; - if (!data->client_window) return; - changes.x = data->client_rect.left - data->whole_rect.left; changes.y = data->client_rect.top - data->whole_rect.top; changes.width = min( max( 1, data->client_rect.right - data->client_rect.left ), 65535 ); @@ -1800,9 +1798,12 @@ static void sync_client_position( struct x11drv_win_data *data, if (mask) { - TRACE( "setting client win %lx pos %d,%d,%dx%d changes=%x\n", - data->client_window, changes.x, changes.y, changes.width, changes.height, mask ); - XConfigureWindow( gdi_display, data->client_window, mask, &changes ); + if (data->client_window) + { + TRACE( "setting client win %lx pos %d,%d,%dx%d changes=%x\n", + data->client_window, changes.x, changes.y, changes.width, changes.height, mask ); + XConfigureWindow( gdi_display, data->client_window, mask, &changes ); + } resize_vk_surfaces( data->hwnd, data->client_window, mask, &changes ); } } From e22775b1a9061523925a0cb89786849915b9f08f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 23 May 2024 19:45:12 -0600 Subject: [PATCH 406/413] winex11.drv: Detach offscreen Vulkan surfaces. CW-Bug-Id: #23855 --- dlls/winex11.drv/vulkan.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c index 33761764c74..275bda4b743 100644 --- a/dlls/winex11.drv/vulkan.c +++ b/dlls/winex11.drv/vulkan.c @@ -291,6 +291,8 @@ static BOOL wine_vk_surface_set_offscreen( struct wine_vk_surface *surface, BOOL #ifdef SONAME_LIBXCOMPOSITE if (usexcomposite) { + struct x11drv_win_data *data; + if (vulkan_disable_child_window_rendering_hack) { FIXME("Vulkan child window rendering is supported, but it's disabled.\n"); @@ -300,12 +302,22 @@ static BOOL wine_vk_surface_set_offscreen( struct wine_vk_surface *surface, BOOL if (!surface->offscreen && offscreen) { FIXME( "Redirecting vulkan surface %lx offscreen, expect degraded performance.\n", surface->window ); + if ((data = get_win_data( surface->hwnd ))) + { + detach_client_window( data, surface->window, TRUE ); + release_win_data( data ); + } pXCompositeRedirectWindow( gdi_display, surface->window, CompositeRedirectManual ); } else if (surface->offscreen && !offscreen) { FIXME( "Putting vulkan surface %lx back onscreen, expect standard performance.\n", surface->window ); pXCompositeUnredirectWindow( gdi_display, surface->window, CompositeRedirectManual ); + if ((data = get_win_data( surface->hwnd ))) + { + attach_client_window( data, surface->window ); + release_win_data( data ); + } } surface->offscreen = offscreen; return TRUE; @@ -1048,7 +1060,7 @@ static VkResult X11DRV_vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR * struct x11drv_win_data *data; if (!XFindContext( gdi_display, (XID)swapchain, swapchain_context, (char **)&surface ) && - !surface->gdi_blit_source && (data = get_win_data( surface->hwnd ))) + !surface->gdi_blit_source && !surface->offscreen && (data = get_win_data( surface->hwnd ))) { attach_client_window( data, surface->window ); release_win_data( data ); From c4e0abaf30d67de91a129370e67307378f26c40d Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 23 May 2024 19:42:07 -0600 Subject: [PATCH 407/413] winex11.drv: Invalidate Vulkan surfaces when detaching. CW-Bug-Id: #23855 --- dlls/winex11.drv/vulkan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c index 275bda4b743..5808a01d186 100644 --- a/dlls/winex11.drv/vulkan.c +++ b/dlls/winex11.drv/vulkan.c @@ -308,6 +308,7 @@ static BOOL wine_vk_surface_set_offscreen( struct wine_vk_surface *surface, BOOL release_win_data( data ); } pXCompositeRedirectWindow( gdi_display, surface->window, CompositeRedirectManual ); + invalidate_vk_surfaces(surface->hwnd); } else if (surface->offscreen && !offscreen) { @@ -976,7 +977,7 @@ static VkResult X11DRV_vkAcquireNextImageKHR( VkDevice device, VkSwapchainKHR sw result = pvkAcquireNextImageKHR( device, swapchain, timeout, semaphore, fence, image_index ); - if ((result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) && surface->offscreen) + if ((result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) && surface->offscreen && !surface->invalidated) { DWORD dc_flags = DCX_USESTYLE; if (!surface->gdi_blit_source || surface->other_process) dc_flags |= DCX_CACHE; From d42ab2d90d56479a6d593e313f6024ea5ad8e9ff Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 12 Jun 2024 12:29:20 -0600 Subject: [PATCH 408/413] amd_ags_x64: Check for NULL context in agsDriverExtensionsDX11_Init(). CW-Bug-Id: #23904 --- dlls/amd_ags_x64/amd_ags_x64_main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dlls/amd_ags_x64/amd_ags_x64_main.c b/dlls/amd_ags_x64/amd_ags_x64_main.c index 75f74e86b20..318200aba7a 100644 --- a/dlls/amd_ags_x64/amd_ags_x64_main.c +++ b/dlls/amd_ags_x64/amd_ags_x64_main.c @@ -1403,6 +1403,12 @@ AGSReturnCode WINAPI agsDriverExtensionsDX11_Init( AGSContext *context, ID3D11De { FIXME("context %p, device %p, uavSlot %u, extensionsSupported %p stub.\n", context, device, uavSlot, extensionsSupported); + if (!context) + { + ERR("NULL context.\n"); + return AGS_INVALID_ARGS; + } + *extensionsSupported = 0; if (device) { From 15a3b75a32d047f0955aaef4a72a58995cf75b9f Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Thu, 13 Jun 2024 06:32:15 +1000 Subject: [PATCH 409/413] Revert "kernelbase: HACK: Add WINE_SUPPRESS_XDG_ENV option." This reverts commit 5ea3074ceda7c01a3af82c9c9a9617f8a9b6e520. --- dlls/kernelbase/process.c | 66 ++++++++++----------------------------- 1 file changed, 16 insertions(+), 50 deletions(-) diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index 08f39020189..0898e30f603 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -1743,29 +1743,6 @@ LPSTR WINAPI DECLSPEC_HOTPATCH GetEnvironmentStringsA(void) return ret; } -static void remove_env_variables( const char **skip, SIZE_T array_size, WCHAR *env, SIZE_T len ) -{ - unsigned int i, j; - SIZE_T l; - - while (*env) - { - for (i = 0; i < array_size; ++i) - { - j = 0; - while (skip[i][j] && skip[i][j] == env[j]) - ++j; - if (!skip[i][j]) break; - } - l = lstrlenW( env ); - len -= (l + 1) * sizeof(WCHAR); - if (i == array_size) - env += l + 1; - else - memmove( env, env + l + 1, len ); - } -} - static void hack_shrink_environment( WCHAR *env, SIZE_T len ) { static int enabled = -1; @@ -1781,6 +1758,8 @@ static void hack_shrink_environment( WCHAR *env, SIZE_T len ) "BASH_FUNC_", "XDG_DATA_DIRS=", }; + SIZE_T l; + unsigned int i, j; if (enabled == -1) { @@ -1798,36 +1777,24 @@ static void hack_shrink_environment( WCHAR *env, SIZE_T len ) ERR( "HACK: shrinking environment size.\n" ); } - if (enabled) - remove_env_variables(skip, ARRAY_SIZE(skip), env, len); -} - -static void hack_suppress_xdg_environment( WCHAR *env, SIZE_T len ) -{ - static int enabled = -1; - static const char *skip[] = - { - "XDG_SESSION_TYPE=", - }; + if (!enabled) return; - if (enabled == -1) + while (*env) { - WCHAR str[40]; - - *str = 0; - if (GetEnvironmentVariableW( L"WINE_SUPPRESS_XDG_ENV", str, sizeof(str)) ) - enabled = *str != '0'; - else if (GetEnvironmentVariableW( L"SteamGameId", str, sizeof(str)) ) - enabled = !wcscmp( str, L"809230" ); + for (i = 0; i < ARRAY_SIZE(skip); ++i) + { + j = 0; + while (skip[i][j] && skip[i][j] == env[j]) + ++j; + if (!skip[i][j]) break; + } + l = lstrlenW( env ); + len -= (l + 1) * sizeof(WCHAR); + if (i == ARRAY_SIZE(skip)) + env += l + 1; else - enabled = 0; - - if (enabled) - ERR( "HACK: suppressing XDG environment.\n" ); + memmove( env, env + l + 1, len ); } - - if (enabled) - remove_env_variables(skip, ARRAY_SIZE(skip), env, len); } /*********************************************************************** @@ -1844,7 +1811,6 @@ LPWSTR WINAPI DECLSPEC_HOTPATCH GetEnvironmentStringsW(void) { memcpy( ret, NtCurrentTeb()->Peb->ProcessParameters->Environment, len ); hack_shrink_environment( ret, len ); - hack_suppress_xdg_environment( ret, len ); } RtlReleasePebLock(); return ret; From a4bf20aba62864b05089202393f4fb1ec0720b43 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 12 Jun 2024 19:12:17 -0600 Subject: [PATCH 410/413] fshack: winex11.drv: Enable GL fshack blitting to GL_FRONT for Arcanum (500810). CW-Bug-Id: #23916 --- dlls/winex11.drv/opengl.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index 3baf52cc9f8..6748912a3fb 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -3073,6 +3073,20 @@ static void fs_hack_handle_ds_test( int mode, struct gl_drawable *gl, struct wgl fs_hack_handle_enable_switch( mode, GL_STENCIL_TEST, &state->stencil_test, FALSE ); } +static BOOL fs_hack_direct_front_blit(void) +{ + static int cached = -1; + + if (cached == -1) + { + const char *sgi = getenv( "SteamGameId" ); + + cached = sgi && !strcmp( sgi, "500810" ); + } + + return cached; +} + static void fs_hack_blit_framebuffer( struct gl_drawable *gl, GLenum draw_buffer ) { static const struct @@ -3189,11 +3203,11 @@ static void fs_hack_blit_framebuffer( struct gl_drawable *gl, GLenum draw_buffer pglBlitFramebuffer( 0, 0, src.cx, src.cy, 0, 0, src.cx, src.cy, GL_COLOR_BUFFER_BIT, GL_NEAREST ); pglBindFramebuffer( GL_READ_FRAMEBUFFER, ctx->fs_hack_resolve_fbo ); } + pglBindFramebuffer( GL_DRAW_FRAMEBUFFER, 0 ); - // HACK - // pglDrawBuffer( draw_buffer ); - pglDrawBuffer( GL_BACK ); + if (draw_buffer == GL_FRONT && fs_hack_direct_front_blit()) pglDrawBuffer( GL_FRONT ); + else pglDrawBuffer( GL_BACK ); opengl_funcs.gl.p_glClear( GL_COLOR_BUFFER_BIT ); @@ -3217,8 +3231,11 @@ static void fs_hack_blit_framebuffer( struct gl_drawable *gl, GLenum draw_buffer GL_COLOR_BUFFER_BIT, ctx->fs_hack_integer ? GL_NEAREST : GL_LINEAR ); } - // HACK - if (draw_buffer == GL_FRONT) pglXSwapBuffers( gdi_display, gl->drawable ); + if (draw_buffer == GL_FRONT) + { + if (fs_hack_direct_front_blit()) pglFlush(); + else pglXSwapBuffers( gdi_display, gl->drawable ); + } if (gamma_ramp) { From 8f56b53c2ac43f256b58b26f76f2924a1ed38905 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 14 Jun 2024 14:28:08 -0600 Subject: [PATCH 411/413] winex11.drv: fshack: Ensure 60Hz display mode otherwise matching current mode exists. CW-Bug-Id: #23891 --- dlls/winex11.drv/fs.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/dlls/winex11.drv/fs.c b/dlls/winex11.drv/fs.c index 73824021060..8a7582757c7 100644 --- a/dlls/winex11.drv/fs.c +++ b/dlls/winex11.drv/fs.c @@ -287,6 +287,7 @@ static void monitor_get_modes( struct fs_monitor *monitor, DEVMODEW **modes, UIN DEVMODEW *real_modes, *real_mode, mode_host = {0}; BOOL additional_modes = FALSE, center_modes = FALSE, landscape; const char *env; + DEVMODEW mode; *mode_count = 0; *modes = NULL; @@ -305,7 +306,7 @@ static void monitor_get_modes( struct fs_monitor *monitor, DEVMODEW **modes, UIN else if ((env = getenv( "SteamAppId" ))) center_modes = !strcmp( env, "359870" ); - max_count = ARRAY_SIZE(fs_monitor_sizes) * DEPTH_COUNT + real_mode_count; + max_count = ARRAY_SIZE(fs_monitor_sizes) * DEPTH_COUNT + real_mode_count + 1; if (center_modes) max_count += ARRAY_SIZE(fs_monitor_sizes) + real_mode_count; if (!(*modes = calloc( max_count, sizeof(DEVMODEW) ))) @@ -321,6 +322,9 @@ static void monitor_get_modes( struct fs_monitor *monitor, DEVMODEW **modes, UIN /* Add the current mode early, in case we have to limit */ modes_append( *modes, mode_count, &resolutions, &mode_host ); + mode = mode_host; + mode.dmDisplayFrequency = 60; + modes_append( *modes, mode_count, &resolutions, &mode ); if ((env = getenv( "WINE_ADDITIONAL_DISPLAY_MODES" ))) additional_modes = (env[0] != '0'); @@ -330,10 +334,10 @@ static void monitor_get_modes( struct fs_monitor *monitor, DEVMODEW **modes, UIN /* Linux reports far fewer resolutions than Windows. Add modes that some games may expect. */ for (i = 0; i < ARRAY_SIZE(fs_monitor_sizes); ++i) { - DEVMODEW mode = mode_host; - if (!additional_modes && fs_monitor_sizes[i].additional) continue; + mode = mode_host; + if (landscape) { mode.dmPelsWidth = fs_monitor_sizes[i].size.cx; @@ -366,7 +370,7 @@ static void monitor_get_modes( struct fs_monitor *monitor, DEVMODEW **modes, UIN for (i = 0, real_mode = real_modes; i < real_mode_count; ++i) { - DEVMODEW mode = *real_mode; + mode = *real_mode; /* Don't report modes that are larger than the current mode */ if (mode.dmPelsWidth <= mode_host.dmPelsWidth && mode.dmPelsHeight <= mode_host.dmPelsHeight) From 1e9b83546ca9eb6d12b5c9b13d23cabbf53e8e03 Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Thu, 13 Jun 2024 07:37:01 +1000 Subject: [PATCH 412/413] ntdll: Treat XDG_SESSION_TYPE as special env variable. This can be used to incorrectly determine the availability of X graphics. (cherry picked from commit 05c0a50454f278fef0f142e11d5491890a979893) CW-Bug-Id: #23899 --- dlls/ntdll/unix/env.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/ntdll/unix/env.c b/dlls/ntdll/unix/env.c index c2c46dd0a12..d068c1a7df9 100644 --- a/dlls/ntdll/unix/env.c +++ b/dlls/ntdll/unix/env.c @@ -344,7 +344,8 @@ static BOOL is_special_env_var( const char *var ) STARTS_WITH( var, "TMP=" ) || STARTS_WITH( var, "QT_" ) || STARTS_WITH( var, "SDL_AUDIODRIVER=" ) || - STARTS_WITH( var, "VK_" )); + STARTS_WITH( var, "VK_" ) || + STARTS_WITH( var, "XDG_SESSION_TYPE=" )); } /* check if an environment variable changes dynamically in every new process */ From 56da5371d7c9c0f9e744b07d0612342ca7e99131 Mon Sep 17 00:00:00 2001 From: Amin Ahmadi Kasani Date: Sun, 16 Jun 2024 11:54:16 +0330 Subject: [PATCH 413/413] Update fa.po --- po/fa.po | 96 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/po/fa.po b/po/fa.po index eb94afa5ce3..dda7753718c 100644 --- a/po/fa.po +++ b/po/fa.po @@ -16,39 +16,39 @@ msgstr "" #: dlls/aclui/aclui.rc:29 dlls/inetcpl.cpl/inetcpl.rc:90 #: programs/winefile/winefile.rc:114 msgid "Security" -msgstr "" +msgstr "امنیت" #: dlls/aclui/aclui.rc:32 #, fuzzy msgid "&Group or user names:" -msgstr "&پرونده" +msgstr "&گروه یا نام کاربری" #: dlls/aclui/aclui.rc:38 msgid "Allow" -msgstr "" +msgstr "اجازه دادن" #: dlls/aclui/aclui.rc:39 msgid "Deny" -msgstr "" +msgstr "رد کردن" #: dlls/aclui/aclui.rc:47 #, fuzzy msgid "Permissions for %1" -msgstr "انتخاب &همه\tCtrl+A" +msgstr "دسترسی برای %1" #: dlls/appwiz.cpl/appwiz.rc:59 msgid "Install/Uninstall" -msgstr "" +msgstr "نصب/حذف" #: dlls/appwiz.cpl/appwiz.rc:62 msgid "" "To install a new program from a floppy disk, CD-ROM drive, or your hard " "drive, click Install." -msgstr "" +msgstr "برای نصب نرم افزار از درایو خارجی به داخلی، نصب را انتخاب کنید" #: dlls/appwiz.cpl/appwiz.rc:63 msgid "&Install..." -msgstr "" +msgstr "نصب..." #: dlls/appwiz.cpl/appwiz.rc:66 msgid "" @@ -72,7 +72,7 @@ msgstr "" #: programs/winecfg/winecfg.rc:205 programs/winecfg/winecfg.rc:242 #: programs/wordpad/wordpad.rc:256 msgid "&Remove" -msgstr "" +msgstr "حذف" #: dlls/appwiz.cpl/appwiz.rc:76 #, fuzzy @@ -118,15 +118,15 @@ msgstr "قبول" #: dlls/appwiz.cpl/appwiz.rc:80 msgid "The following information can be used to get technical support for %s:" -msgstr "" +msgstr "این اطلاعات ممکن است برای پشتیبانی فنی استفاده شوند %s:" #: dlls/appwiz.cpl/appwiz.rc:81 msgid "Publisher:" -msgstr "" +msgstr "ناشر:" #: dlls/appwiz.cpl/appwiz.rc:82 programs/winefile/winefile.rc:165 msgid "Version:" -msgstr "" +msgstr "نسخه:" #: dlls/appwiz.cpl/appwiz.rc:83 #, fuzzy @@ -136,28 +136,28 @@ msgstr "&محتویات" #: dlls/appwiz.cpl/appwiz.rc:84 #, fuzzy msgid "Support Information:" -msgstr "اطلاعات" +msgstr "اطلاعات پشتیبانی:" #: dlls/appwiz.cpl/appwiz.rc:85 msgid "Support Telephone:" -msgstr "" +msgstr "پشتیبانی تلفنی:" #: dlls/appwiz.cpl/appwiz.rc:86 msgid "Readme:" -msgstr "" +msgstr "من را بخوان:" #: dlls/appwiz.cpl/appwiz.rc:87 msgid "Product Updates:" -msgstr "" +msgstr "بروزرسانی محصول:" #: dlls/appwiz.cpl/appwiz.rc:88 #, fuzzy msgid "Comments:" -msgstr "&محتویات" +msgstr "نظرات:" #: dlls/appwiz.cpl/appwiz.rc:101 msgid "Wine Gecko Installer" -msgstr "" +msgstr "نصاب واین گکو" #: dlls/appwiz.cpl/appwiz.rc:104 msgid "" @@ -172,7 +172,7 @@ msgstr "" #: dlls/appwiz.cpl/appwiz.rc:110 dlls/appwiz.cpl/appwiz.rc:125 msgid "&Install" -msgstr "" +msgstr "نصب&" #: dlls/appwiz.cpl/appwiz.rc:111 dlls/appwiz.cpl/appwiz.rc:126 #: dlls/avifil32/avifil32.rc:55 dlls/browseui/browseui.rc:45 @@ -219,7 +219,7 @@ msgstr "انصراف" #: dlls/appwiz.cpl/appwiz.rc:116 msgid "Wine Mono Installer" -msgstr "" +msgstr "نصاب واین مونو" #: dlls/appwiz.cpl/appwiz.rc:119 msgid "" @@ -234,18 +234,18 @@ msgstr "" #: dlls/appwiz.cpl/appwiz.rc:31 msgid "Add/Remove Programs" -msgstr "" +msgstr "نصب/حذف برنامه ها" #: dlls/appwiz.cpl/appwiz.rc:32 msgid "" "Allows you to install new software, or remove existing software from your " "computer." -msgstr "" +msgstr "اجازه می‌دهد نرم افزار جدیدی حذف و یا قبلی را حذف کنید." #: dlls/appwiz.cpl/appwiz.rc:33 programs/taskmgr/taskmgr.rc:262 #: programs/winecfg/winecfg.rc:32 msgid "Applications" -msgstr "" +msgstr "نرم افزار ها" #: dlls/appwiz.cpl/appwiz.rc:35 msgid "" @@ -255,25 +255,25 @@ msgstr "" #: dlls/appwiz.cpl/appwiz.rc:36 msgid "Not specified" -msgstr "" +msgstr "مشخص نشده" #: dlls/appwiz.cpl/appwiz.rc:38 dlls/oledb32/version.rc:38 #: dlls/shell32/shell32.rc:144 dlls/shell32/shell32.rc:239 #: programs/regedit/regedit.rc:150 programs/winefile/winefile.rc:106 msgid "Name" -msgstr "" +msgstr "نام" #: dlls/appwiz.cpl/appwiz.rc:39 msgid "Publisher" -msgstr "" +msgstr "ناشر" #: dlls/appwiz.cpl/appwiz.rc:40 dlls/cryptui/cryptui.rc:54 msgid "Version" -msgstr "" +msgstr "نسخه" #: dlls/appwiz.cpl/appwiz.rc:41 msgid "Installation programs" -msgstr "" +msgstr "نصب کردن برنامه ها" #: dlls/appwiz.cpl/appwiz.rc:42 msgid "Programs (*.exe)" @@ -289,15 +289,15 @@ msgstr "همه‌ی پرونده‌ها (*.*)" #: dlls/appwiz.cpl/appwiz.rc:46 msgid "&Modify/Remove" -msgstr "" +msgstr "&ویرایش/حذف" #: dlls/appwiz.cpl/appwiz.rc:52 msgid "Downloading..." -msgstr "" +msgstr "در حال دانلود..." #: dlls/appwiz.cpl/appwiz.rc:53 msgid "Installing..." -msgstr "" +msgstr "در حال نصب..." #: dlls/appwiz.cpl/appwiz.rc:54 msgid "" @@ -307,7 +307,7 @@ msgstr "" #: dlls/appwiz.cpl/appwiz.rc:47 msgid "Please wait until the current uninstall/modify process is complete." -msgstr "" +msgstr "لطفا تا پایان فرایند نصب/حذف صبر کنید." #: dlls/avifil32/avifil32.rc:42 msgid "Compress options" @@ -376,15 +376,15 @@ msgstr "" #: dlls/browseui/browseui.rc:31 msgid "seconds" -msgstr "" +msgstr "ثانیه ها" #: dlls/browseui/browseui.rc:32 msgid "minutes" -msgstr "" +msgstr "دقایق" #: dlls/browseui/browseui.rc:33 msgid "hours" -msgstr "" +msgstr "ساعت ها" #: dlls/comctl32/comctl32.rc:68 programs/winefile/winefile.rc:156 #, fuzzy @@ -393,7 +393,7 @@ msgstr "انتخاب &همه\tCtrl+A" #: dlls/comctl32/comctl32.rc:73 dlls/comdlg32/comdlg32.rc:265 msgid "&Apply" -msgstr "" +msgstr "&اعمال" #: dlls/comctl32/comctl32.rc:74 dlls/comctl32/comctl32.rc:88 #: dlls/comdlg32/comdlg32.rc:309 dlls/user32/user32.rc:99 @@ -408,15 +408,15 @@ msgstr "" #: dlls/comctl32/comctl32.rc:84 msgid "< &Back" -msgstr "" +msgstr "< بازگشت" #: dlls/comctl32/comctl32.rc:85 dlls/oledb32/version.rc:61 msgid "&Next >" -msgstr "" +msgstr "> بعدی" #: dlls/comctl32/comctl32.rc:86 msgid "Finish" -msgstr "" +msgstr "پایان" #: dlls/comctl32/comctl32.rc:97 msgid "Customize Toolbar" @@ -428,11 +428,11 @@ msgstr "" #: programs/oleview/oleview.rc:187 programs/oleview/oleview.rc:200 #: programs/oleview/oleview.rc:212 programs/taskmgr/taskmgr.rc:139 msgid "&Close" -msgstr "" +msgstr "&بستن" #: dlls/comctl32/comctl32.rc:101 msgid "R&eset" -msgstr "" +msgstr "&بازنشانی" #: dlls/comctl32/comctl32.rc:102 dlls/comdlg32/comdlg32.rc:173 #: dlls/comdlg32/comdlg32.rc:195 dlls/comdlg32/comdlg32.rc:266 @@ -491,36 +491,36 @@ msgstr "" #: dlls/comctl32/comctl32.rc:52 dlls/shell32/shell32.rc:307 #: dlls/shlwapi/shlwapi.rc:46 dlls/user32/user32.rc:90 dlls/user32/user32.rc:78 msgid "&Yes" -msgstr "" +msgstr "&بله" #: dlls/comctl32/comctl32.rc:53 dlls/shell32/shell32.rc:309 #: dlls/shlwapi/shlwapi.rc:47 dlls/user32/user32.rc:91 dlls/user32/user32.rc:79 msgid "&No" -msgstr "" +msgstr "&خیر" #: dlls/comctl32/comctl32.rc:54 dlls/user32/user32.rc:93 #: dlls/user32/user32.rc:76 msgid "&Retry" -msgstr "" +msgstr "&تلاش دوباره" #: dlls/comctl32/comctl32.rc:62 msgid "Hide details" -msgstr "" +msgstr "مخفی کردن جزئیات" #: dlls/comctl32/comctl32.rc:63 msgid "See details" -msgstr "" +msgstr "نمایش جزئیات" #: dlls/comctl32/comctl32.rc:31 dlls/cryptui/cryptui.rc:235 #: programs/regedit/regedit.rc:291 programs/taskmgr/taskmgr.rc:434 #: programs/winedbg/winedbg.rc:61 programs/winedbg/winedbg.rc:76 #: programs/wordpad/wordpad.rc:180 msgid "Close" -msgstr "" +msgstr "بستن" #: dlls/comctl32/comctl32.rc:36 msgid "Today:" -msgstr "" +msgstr "امروز:" #: dlls/comctl32/comctl32.rc:37 msgid "Go to today"