From: Bernhard Kölbl besentv@gmail.com
To be able to create MfMediaEngine instances in Direct3D mode. --- dlls/mfmediaengine/tests/mfmediaengine.c | 43 +++++++++++++++--------- 1 file changed, 28 insertions(+), 15 deletions(-)
diff --git a/dlls/mfmediaengine/tests/mfmediaengine.c b/dlls/mfmediaengine/tests/mfmediaengine.c index 008ee58849c..e19c248b8df 100644 --- a/dlls/mfmediaengine/tests/mfmediaengine.c +++ b/dlls/mfmediaengine/tests/mfmediaengine.c @@ -138,24 +138,37 @@ static struct media_engine_notify *create_callback(void) return object; }
-static IMFMediaEngine *create_media_engine(IMFMediaEngineNotify *callback) +static IMFMediaEngine *create_media_engine(IMFMediaEngineNotify *callback, IUnknown *d3d11_dev, UINT32 output_format) { - IMFDXGIDeviceManager *manager; - IMFMediaEngine *media_engine; - IMFAttributes *attributes; + IMFDXGIDeviceManager *manager = NULL; + IMFMediaEngine *media_engine = NULL; + IMFAttributes *attributes = NULL; UINT token; HRESULT hr;
hr = pMFCreateDXGIDeviceManager(&token, &manager); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- hr = MFCreateAttributes(&attributes, 3); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (d3d11_dev) + { + hr = pMFCreateDXGIDeviceManager(&token, &manager); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFDXGIDeviceManager_ResetDevice(manager, d3d11_dev, token); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFAttributes_SetUnknown(attributes, &MF_MEDIA_ENGINE_DXGI_MANAGER, (IUnknown *)manager); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + }
hr = IMFAttributes_SetUnknown(attributes, &MF_MEDIA_ENGINE_CALLBACK, (IUnknown *)callback); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFAttributes_SetUINT32(attributes, &MF_MEDIA_ENGINE_VIDEO_OUTPUT_FORMAT, DXGI_FORMAT_UNKNOWN); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + if (output_format) + { + hr = IMFAttributes_SetUINT32(attributes, &MF_MEDIA_ENGINE_VIDEO_OUTPUT_FORMAT, output_format); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + }
hr = IMFMediaEngineClassFactory_CreateInstance(factory, 0, attributes, &media_engine); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -168,7 +181,7 @@ static IMFMediaEngine *create_media_engine(IMFMediaEngineNotify *callback)
static IMFMediaEngineEx *create_media_engine_ex(IMFMediaEngineNotify *callback) { - IMFMediaEngine *engine = create_media_engine(callback); + IMFMediaEngine *engine = create_media_engine(callback, NULL, DXGI_FORMAT_UNKNOWN); IMFMediaEngineEx *engine_ex = NULL;
if (engine) @@ -331,7 +344,7 @@ static void test_Shutdown(void)
notify = create_callback();
- media_engine = create_media_engine(¬ify->IMFMediaEngineNotify_iface); + media_engine = create_media_engine(¬ify->IMFMediaEngineNotify_iface, NULL, DXGI_FORMAT_UNKNOWN);
hr = IMFMediaEngine_Shutdown(media_engine); ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr); @@ -518,7 +531,7 @@ static void test_Play(void)
notify = create_callback();
- media_engine = create_media_engine(¬ify->IMFMediaEngineNotify_iface); + media_engine = create_media_engine(¬ify->IMFMediaEngineNotify_iface, NULL, DXGI_FORMAT_UNKNOWN);
hr = IMFMediaEngine_GetBuffered(media_engine, &range); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -567,7 +580,7 @@ static void test_Play(void) IMFMediaEngine_Release(media_engine);
/* Play -> Pause */ - media_engine = create_media_engine(¬ify->IMFMediaEngineNotify_iface); + media_engine = create_media_engine(¬ify->IMFMediaEngineNotify_iface, NULL, DXGI_FORMAT_UNKNOWN);
hr = IMFMediaEngine_Play(media_engine); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -594,7 +607,7 @@ static void test_playback_rate(void)
notify = create_callback();
- media_engine = create_media_engine(¬ify->IMFMediaEngineNotify_iface); + media_engine = create_media_engine(¬ify->IMFMediaEngineNotify_iface, NULL, DXGI_FORMAT_UNKNOWN);
rate = IMFMediaEngine_GetDefaultPlaybackRate(media_engine); ok(rate == 1.0, "Unexpected default rate.\n"); @@ -624,7 +637,7 @@ static void test_mute(void)
notify = create_callback();
- media_engine = create_media_engine(¬ify->IMFMediaEngineNotify_iface); + media_engine = create_media_engine(¬ify->IMFMediaEngineNotify_iface, NULL, DXGI_FORMAT_UNKNOWN);
ret = IMFMediaEngine_GetMuted(media_engine); ok(!ret, "Unexpected state.\n"); @@ -655,7 +668,7 @@ static void test_error(void)
notify = create_callback();
- media_engine = create_media_engine(¬ify->IMFMediaEngineNotify_iface); + media_engine = create_media_engine(¬ify->IMFMediaEngineNotify_iface, NULL, DXGI_FORMAT_UNKNOWN);
eo = (void *)0xdeadbeef; hr = IMFMediaEngine_GetError(media_engine, &eo);
From: Bernhard Kölbl besentv@gmail.com
--- dlls/mfmediaengine/tests/Makefile.in | 4 +- dlls/mfmediaengine/tests/mfmediaengine.c | 392 ++++++++++++++++++++++- dlls/mfmediaengine/tests/resource.rc | 24 ++ dlls/mfmediaengine/tests/test.mp4 | Bin 0 -> 59990 bytes 4 files changed, 416 insertions(+), 4 deletions(-) create mode 100644 dlls/mfmediaengine/tests/resource.rc create mode 100644 dlls/mfmediaengine/tests/test.mp4
diff --git a/dlls/mfmediaengine/tests/Makefile.in b/dlls/mfmediaengine/tests/Makefile.in index 421b75587a0..ffdcc53fd28 100644 --- a/dlls/mfmediaengine/tests/Makefile.in +++ b/dlls/mfmediaengine/tests/Makefile.in @@ -1,5 +1,7 @@ TESTDLL = mfmediaengine.dll -IMPORTS = ole32 mfplat oleaut32 mfuuid uuid +IMPORTS = ole32 mfplat oleaut32 mfuuid uuid user32
C_SRCS = \ mfmediaengine.c + +RC_SRCS = resource.rc diff --git a/dlls/mfmediaengine/tests/mfmediaengine.c b/dlls/mfmediaengine/tests/mfmediaengine.c index e19c248b8df..ca06f064eb4 100644 --- a/dlls/mfmediaengine/tests/mfmediaengine.c +++ b/dlls/mfmediaengine/tests/mfmediaengine.c @@ -16,25 +16,42 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include <math.h> #include <stdarg.h>
#define COBJMACROS
#include "windef.h" #include "winbase.h" +#include "winuser.h" +#include "wtypes.h"
#include "mfapi.h" #include "mfidl.h" #include "mfmediaengine.h" #include "mferror.h" -#include "dxgi.h" +#define D3D11_INIT_GUID #include "initguid.h" +#include "d3d11.h" +#include "dxgi.h" #include "mmdeviceapi.h" #include "audiosessiontypes.h"
#include "wine/test.h"
+ static HRESULT (WINAPI *pMFCreateDXGIDeviceManager)(UINT *token, IMFDXGIDeviceManager **manager); +static HRESULT (WINAPI *pD3D11CreateDevice)(IDXGIAdapter *adapter, D3D_DRIVER_TYPE driver_type, HMODULE swrast, UINT flags, + const D3D_FEATURE_LEVEL *feature_levels, UINT levels, UINT sdk_version, ID3D11Device **device_out, + D3D_FEATURE_LEVEL *obtained_feature_level, ID3D11DeviceContext **immediate_context); +static HRESULT (WINAPI *pD3D11CreateDeviceAndSwapChain)(IDXGIAdapter *adapter, D3D_DRIVER_TYPE driver_type, + HMODULE swrast, UINT flags, const D3D_FEATURE_LEVEL *feature_levels, UINT levels, + UINT sdk_version, const DXGI_SWAP_CHAIN_DESC *swapchain_desc, IDXGISwapChain **swapchain, + ID3D11Device **device_out, D3D_FEATURE_LEVEL *obtained_feature_level, ID3D11DeviceContext **immediate_context); +static HRESULT (WINAPI *pD3DCompile)(const void *data, SIZE_T data_size, const char *filename, + const D3D_SHADER_MACRO *defines, ID3DInclude *include, const char *entrypoint, + const char *target, UINT sflags, UINT eflags, ID3DBlob **shader, ID3DBlob **error_messages); +//pD3DCompile pD3DCompile;
static IMFMediaEngineClassFactory *factory;
@@ -68,13 +85,58 @@ static void init_functions(void)
#define X(f) p##f = (void*)GetProcAddress(mod, #f) X(MFCreateDXGIDeviceManager); + + if ((mod = LoadLibraryA("d3d11.dll"))) + { + X(D3D11CreateDevice); + X(D3D11CreateDeviceAndSwapChain); + } + + if ((mod = LoadLibraryA("D3DCompiler_47.dll"))) + { + X(D3DCompile); + } #undef X }
+static WCHAR *load_resource(const WCHAR *name) +{ + static WCHAR pathW[MAX_PATH]; + DWORD written; + HANDLE file; + HRSRC res; + void *ptr; + + GetTempPathW(ARRAY_SIZE(pathW), pathW); + lstrcatW(pathW, name); + + 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 = FindResourceW(NULL, name, (LPCWSTR)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" ); + CloseHandle(file); + + return pathW; +} + struct media_engine_notify { IMFMediaEngineNotify IMFMediaEngineNotify_iface; LONG refcount; + + HANDLE event_ended; + HANDLE event_error; + HANDLE event_first_frame; + HANDLE event_loadeddata; + HANDLE event_playing; };
static inline struct media_engine_notify *impl_from_IMFMediaEngineNotify(IMFMediaEngineNotify *iface) @@ -108,13 +170,44 @@ static ULONG WINAPI media_engine_notify_Release(IMFMediaEngineNotify *iface) ULONG refcount = InterlockedDecrement(¬ify->refcount);
if (!refcount) + { + CloseHandle(notify->event_ended); + CloseHandle(notify->event_error); + CloseHandle(notify->event_first_frame); + CloseHandle(notify->event_loadeddata); + CloseHandle(notify->event_playing); free(notify); + }
return refcount; }
static HRESULT WINAPI media_engine_notify_EventNotify(IMFMediaEngineNotify *iface, DWORD event, DWORD_PTR param1, DWORD param2) { + struct media_engine_notify *impl = impl_from_IMFMediaEngineNotify(iface); + + switch (event) + { + case MF_MEDIA_ENGINE_EVENT_ERROR: + trace("MF_MEDIA_ENGINE_EVENT_ERROR: err %p, hr %lx.\n", (void *)param1, param2); + SetEvent(impl->event_error); + break; + case MF_MEDIA_ENGINE_EVENT_LOADEDDATA: + SetEvent(impl->event_loadeddata); + break; + case MF_MEDIA_ENGINE_EVENT_PLAYING: + SetEvent(impl->event_playing); + break; + case MF_MEDIA_ENGINE_EVENT_ENDED: + SetEvent(impl->event_ended); + break; + case MF_MEDIA_ENGINE_EVENT_FIRSTFRAMEREADY: + SetEvent(impl->event_first_frame); + break; + default: + break; + } + return S_OK; }
@@ -132,6 +225,11 @@ static struct media_engine_notify *create_callback(void)
object = calloc(1, sizeof(*object));
+ object->event_ended = CreateEventW(NULL, FALSE, FALSE, NULL); + object->event_error = CreateEventW(NULL, FALSE, FALSE, NULL); + object->event_first_frame = CreateEventW(NULL, FALSE, FALSE, NULL); + object->event_loadeddata = CreateEventW(NULL, FALSE, FALSE, NULL); + object->event_playing = CreateEventW(NULL, FALSE, FALSE, NULL); object->IMFMediaEngineNotify_iface.lpVtbl = &media_engine_notify_vtbl; object->refcount = 1;
@@ -146,7 +244,7 @@ static IMFMediaEngine *create_media_engine(IMFMediaEngineNotify *callback, IUnkn UINT token; HRESULT hr;
- hr = pMFCreateDXGIDeviceManager(&token, &manager); + hr = MFCreateAttributes(&attributes, 1); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
if (d3d11_dev) @@ -174,7 +272,6 @@ static IMFMediaEngine *create_media_engine(IMFMediaEngineNotify *callback, IUnkn ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
IMFAttributes_Release(attributes); - IMFDXGIDeviceManager_Release(manager);
return media_engine; } @@ -985,6 +1082,294 @@ static void test_audio_configuration(void) IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface); }
+LRESULT CALLBACK WindowCallback(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + return DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + + +static void test_playback_on_d3d_device(void) +{ + static const char ps_input[] = "Texture2D<float4> tex : register(t0);\n" + "SamplerState smp : register(s0);\n" + "\n" + //"float4 main(in float4 p : SV_POSITION, in float2 texcoord : TEX_COORD) : SV_TARGET {\n" + "float4 main(in float2 texcoord : TEX_COORD) : SV_TARGET {\n" + "texcoord.y = 1 - texcoord.y;\n" /* We get the image horizontally flipped so we transform it back. */ + "return tex.Sample(smp, texcoord);\n" + "}"; + static const char vs_input[] = "float4 main(out float2 texcoord : TEX_COORD, in uint vid : SV_VERTEXID) : SV_POSITION {\n" + "texcoord = float2(float(vid & 1) * 2.0f, float(vid & 2));\n" + "return float4(-1.0f + 2.0f * texcoord, 0.0f, 1.0f);\n" + "}"; + static const enum D3D_FEATURE_LEVEL levels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0 }; + struct media_engine_notify *notify = NULL; + IMFMediaEngine *engine = NULL; + ID3D11Texture2D *video_texture = NULL, *swapchain_texture = NULL; + ID3D11ShaderResourceView *d3d11_srv = NULL; + ID3D11RenderTargetView *d3d11_rtv = NULL; + ID3D11RasterizerState *d3d11_rs = NULL; + ID3D11VertexShader *d3d11_vs = NULL; + ID3D11PixelShader *d3d11_ps = NULL; + ID3D11Device *d3d11_dev = NULL; + IDXGISwapChain *swapchain = NULL; + ID3DBlob *error_blob = NULL; + ID3DBlob *ps_blob = NULL; + ID3DBlob *vs_blob = NULL; + + D3D11_RASTERIZER_DESC rasterizer_desc = { 0 }; + MFVideoNormalizedRect video_norm_rect = { 0 }; + DXGI_SWAP_CHAIN_DESC swapchain_desc = { 0 }; + D3D11_TEXTURE2D_DESC texture_desc = { 0 }; + D3D11_VIEWPORT viewport = { 0 }; + MFARGB border_color = { 0 }; + WNDCLASSW windowclass = { 0 }; + MSG message = { 0 }; + RECT rect = { 0 }; + + const UINT VIDEO_HEIGHT = 240, VIDEO_WIDTH = 320; + double current_time = 0, duration = 0; + DWORD size_x = 0, size_y = 0; + LONGLONG presentation_time = 0; + HINSTANCE hinstance = NULL; + WCHAR *filename = NULL; + HWND window = NULL; + HRESULT hr = 0; + LONG ref = -1; + + windowclass.lpfnWndProc = WindowCallback; + windowclass.hInstance = hinstance; + windowclass.lpszClassName = L"Playback Window Class"; + RegisterClassW(&windowclass); + + window = CreateWindowExW(0, L"Playback Window Class", L"test_playback_on_d3d_device", WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, 320, 240, + NULL, NULL, hinstance, NULL); + ok(!!window, "window creation failed, last error: %#lx\n", GetLastError()); + + ShowWindow(window, SW_SHOW); + + /* D3D11 init */ + swapchain_desc.BufferDesc.RefreshRate.Numerator = 0, + swapchain_desc.BufferDesc.RefreshRate.Denominator = 1, + swapchain_desc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM, + swapchain_desc.SampleDesc.Count = 1, + swapchain_desc.SampleDesc.Quality = 0, + swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT, + swapchain_desc.BufferCount = 1, + swapchain_desc.OutputWindow = window; + swapchain_desc.Windowed = TRUE, + swapchain_desc.Flags = 0, + swapchain_desc.SwapEffect = 0, + hr = pD3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, D3D11_CREATE_DEVICE_BGRA_SUPPORT, levels, ARRAY_SIZE(levels), + D3D11_SDK_VERSION, &swapchain_desc, &swapchain, &d3d11_dev, NULL, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = pD3DCompile((void *)ps_input, strlen(ps_input), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &ps_blob, &error_blob); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (error_blob) + { + trace("Compiling pixel shader failed, reason: %s.\n", debugstr_a(ID3D10Blob_GetBufferPointer(error_blob))); + ID3D10Blob_Release(error_blob); + } + + hr = pD3DCompile((void *)vs_input, strlen(vs_input), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &vs_blob, &error_blob); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (error_blob) + { + trace("Compiling vertex shader failed, reason: %s.\n", debugstr_a(ID3D10Blob_GetBufferPointer(error_blob))); + ID3D10Blob_Release(error_blob); + } + + hr = ID3D11Device_CreatePixelShader(d3d11_dev, ID3D10Blob_GetBufferPointer(ps_blob), ID3D10Blob_GetBufferSize(ps_blob), NULL, &d3d11_ps); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ID3D10Blob_Release(ps_blob); + + hr = ID3D11Device_CreateVertexShader(d3d11_dev, ID3D10Blob_GetBufferPointer(vs_blob), ID3D10Blob_GetBufferSize(vs_blob), NULL, &d3d11_vs); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ID3D10Blob_Release(vs_blob); + + hr = IDXGISwapChain_GetBuffer(swapchain, 0, &IID_ID3D11Texture2D, (void **)&swapchain_texture); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ID3D11Device_CreateRenderTargetView(d3d11_dev, (ID3D11Resource *)swapchain_texture, NULL, &d3d11_rtv); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + texture_desc.Width = VIDEO_WIDTH; + texture_desc.Height = VIDEO_HEIGHT; + texture_desc.MipLevels = 1; + texture_desc.ArraySize = 1; + texture_desc.Format = DXGI_FORMAT_B8G8R8X8_UNORM; + texture_desc.SampleDesc.Count = 1; + texture_desc.SampleDesc.Quality = 0; + texture_desc.Usage = D3D11_USAGE_DEFAULT; + texture_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; + texture_desc.CPUAccessFlags = 0; + texture_desc.MiscFlags = 0; + hr = ID3D11Device_CreateTexture2D(d3d11_dev, &texture_desc, NULL, &video_texture); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ID3D11Device_CreateShaderResourceView(d3d11_dev, (ID3D11Resource *)video_texture, NULL, &d3d11_srv); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + rasterizer_desc.CullMode = D3D11_CULL_NONE; + rasterizer_desc.FillMode = D3D11_FILL_SOLID; + hr = ID3D11Device_CreateRasterizerState(d3d11_dev, &rasterizer_desc, &d3d11_rs); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Media engine init */ + notify = create_callback(); + ok(!!notify, "notify was %p.\n", notify); + + engine = create_media_engine(¬ify->IMFMediaEngineNotify_iface, (IUnknown *)d3d11_dev, DXGI_FORMAT_R32G32B32_FLOAT); + ok(!!engine, "engine was %p.\n", engine); + + filename = load_resource(L"test.mp4"); + ok(!!filename, "filename was %s.\n", debugstr_w(filename)); + + /* Actual tests */ + hr = IMFMediaEngine_SetSource(engine, filename); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Skip tests if media engine init fails. Currently always happening in Wine! */ + if (!WaitForSingleObject(notify->event_error, 100)) + goto done; + + hr = IMFMediaEngine_Play(engine); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaEngine_SetLoop(engine, FALSE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaEngine_GetNativeVideoSize(engine, &size_x, &size_y); + ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); + + current_time = IMFMediaEngine_GetCurrentTime(engine); + ok(current_time == 0.0, "IMFMediaEngine_GetCurrentTime returned %lf.\n", current_time); + + duration = IMFMediaEngine_GetDuration(engine); + ok(isnan(duration), "IMFMediaEngine_GetDuration returned %lf.\n", duration); + + /* Wait for test.mp4 to be loaded. */ + ok(!WaitForSingleObject(notify->event_loadeddata, 5000), "wait for event_loadeddata failed.\n"); + + hr = IMFMediaEngine_Play(engine); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaEngine_SetVolume(engine, 1.0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaEngine_GetNativeVideoSize(engine, &size_x, &size_y); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(size_x == 320 ,"size_x was %lu.\n", size_x); + ok(size_y == 240 ,"size_y was %lu.\n", size_y); + + duration = IMFMediaEngine_GetDuration(engine); + ok(duration == 4.0, "IMFMediaEngine_GetDuration returned %lf.\n", duration); + + ok(!WaitForSingleObject(notify->event_first_frame, 5000), "Wait for event_first_frame failed.\n"); + + hr = IMFMediaEngine_Play(engine); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + if (WaitForSingleObject(notify->event_playing, 5000) > 0) + { + win_skip("Wait for event_playing failed. Skipping this test.\n"); + goto done; + } + + rect.right = VIDEO_WIDTH; + rect.bottom = VIDEO_HEIGHT; + video_norm_rect.right = 1.0f; + video_norm_rect.bottom = 1.0f; + + viewport.Height = VIDEO_HEIGHT; + viewport.Width = VIDEO_WIDTH; + + while (WaitForSingleObject(notify->event_ended, 0) > WAIT_OBJECT_0) + { + ID3D11DeviceContext *d3d11_dev_ctx = NULL; + BOOL enable_print = FALSE; + D3D11_MAPPED_SUBRESOURCE mapped_subresource = { 0 }; + + if (PeekMessageW(&message, window, 0, 0, PM_REMOVE) > 0) + { + TranslateMessage(&message); + DispatchMessageW(&message); + } + + current_time = IMFMediaEngine_GetCurrentTime(engine); + /* Workaround for a current bug in Wine, where video playback isn't properly finishing. Break in this case. */ + if (current_time > 4.3) + { + todo_wine ok(0, "Forcefully breaking out of video loop, because video playback is partially broken.\n"); + break; + } + ok(current_time < 4.3 && current_time > 0.0, "IMFMediaEngine_GetCurrentTime returned %lf.\n", current_time); + + hr = IMFMediaEngine_OnVideoStreamTick(engine, &presentation_time); + ok(hr == S_OK || hr == S_FALSE, "Unexpected hr %#lx.\n", hr); /* Returns S_FALSE when no frame is ready. */ + + if (hr == S_OK) + { + hr = IMFMediaEngine_TransferVideoFrame(engine, (IUnknown *)video_texture, &video_norm_rect, &rect, &border_color); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + + ok(!enable_print, "ID3D11Device_GetImmediateContext\n"); + ID3D11Device_GetImmediateContext(d3d11_dev, &d3d11_dev_ctx); + + /* Map and unmap to wait for the Draw calls in MF to have finished. */ + hr = ID3D11DeviceContext_Map(d3d11_dev_ctx, (ID3D11Resource *)video_texture, 0, D3D11_MAP_READ, 0, &mapped_subresource); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ID3D11DeviceContext_Unmap(d3d11_dev_ctx, (ID3D11Resource *)video_texture, 0); + + ID3D11DeviceContext_ClearState(d3d11_dev_ctx); + ID3D11DeviceContext_RSSetState(d3d11_dev_ctx, d3d11_rs); + ID3D11DeviceContext_OMSetRenderTargets(d3d11_dev_ctx, 1, &d3d11_rtv, NULL); + ID3D11DeviceContext_VSSetShader(d3d11_dev_ctx, d3d11_vs, NULL, 0); + ID3D11DeviceContext_PSSetShader(d3d11_dev_ctx, d3d11_ps, NULL, 0); + ID3D11DeviceContext_PSSetShaderResources(d3d11_dev_ctx, 0, 1, &d3d11_srv); + ID3D11DeviceContext_IASetPrimitiveTopology(d3d11_dev_ctx, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + ID3D11DeviceContext_RSSetViewports(d3d11_dev_ctx, 1, &viewport); + ID3D11DeviceContext_Draw(d3d11_dev_ctx, 3, 0); + + ID3D11DeviceContext_Release(d3d11_dev_ctx); + + hr = IDXGISwapChain_Present(swapchain, 1, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + Sleep(16); /* Simulate a framerate of ~60FPS */ + } + + hr = IMFMediaEngine_OnVideoStreamTick(engine, &presentation_time); + todo_wine ok(hr == S_FALSE, "Unexpected hr %#lx.\n", hr); + +done: + /* Release MF */ + IMFMediaEngine_Release(engine); + IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface); + /* Release D3D */ + ref = ID3D11RasterizerState_Release(d3d11_rs); + ok(!ref, "Unexpected ref %lu.\n", ref); + ref = ID3D11ShaderResourceView_Release(d3d11_srv); + ok(!ref, "Unexpected ref %lu.\n", ref); + ref = ID3D11Texture2D_Release(video_texture); + ok(!ref, "Unexpected ref %lu.\n", ref); + hr = ID3D11RenderTargetView_Release(d3d11_rtv); + ok(!ref, "Unexpected ref %lu.\n", ref); + ref = ID3D11Texture2D_Release(swapchain_texture); + ok(!ref, "Unexpected ref %lu.\n", ref); + ref = ID3D11VertexShader_Release(d3d11_vs); + ok(!ref, "Unexpected ref %lu.\n", ref); + ref = ID3D11PixelShader_Release(d3d11_ps); + ok(!ref, "Unexpected ref %lu.\n", ref); + ref = IDXGISwapChain_Release(swapchain); + ok(!ref, "Unexpected ref %lu.\n", ref); + ID3D11Device_Release(d3d11_dev); + + DestroyWindow(window); +} + START_TEST(mfmediaengine) { HRESULT hr; @@ -1015,6 +1400,7 @@ START_TEST(mfmediaengine) test_time_range(); test_SetSourceFromByteStream(); test_audio_configuration(); + test_playback_on_d3d_device();
IMFMediaEngineClassFactory_Release(factory);
diff --git a/dlls/mfmediaengine/tests/resource.rc b/dlls/mfmediaengine/tests/resource.rc new file mode 100644 index 00000000000..db9db42a4f4 --- /dev/null +++ b/dlls/mfmediaengine/tests/resource.rc @@ -0,0 +1,24 @@ +/* + * Resources for mfplat test suite. + * + * Copyright 2022 Bernhard Kölbl + * + * 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 "windef.h" + +/* @makedep: test.mp4 */ +test.mp4 RCDATA test.mp4 diff --git a/dlls/mfmediaengine/tests/test.mp4 b/dlls/mfmediaengine/tests/test.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..d645faf172450bfd0e2809db74553167b532b6c1 GIT binary patch literal 59990 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
literal 0 HcmV?d00001
From: Bernhard Kölbl besentv@gmail.com
Some games try to use DXGI formats for MfMediaEngine, which are not converable to DX9 (e.g. DXGI_FORMAT_R32G32B32_FLOAT). This works on Windows, so in this case use DXGI_FORMAT_B8G8R8A8_UNORM as a fallback. --- dlls/mfmediaengine/main.c | 6 +++--- dlls/mfmediaengine/tests/mfmediaengine.c | 9 --------- 2 files changed, 3 insertions(+), 12 deletions(-)
diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index a191448b69f..5b887c14293 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -1046,10 +1046,10 @@ static HRESULT media_engine_create_video_renderer(struct media_engine *engine, I }
memcpy(&subtype, &MFVideoFormat_Base, sizeof(subtype)); - if (!(subtype.Data1 = MFMapDXGIFormatToDX9Format(output_format))) + while (!(subtype.Data1 = MFMapDXGIFormatToDX9Format(output_format))) { - WARN("Unrecognized output format %#x.\n", output_format); - return E_FAIL; + WARN("Unrecognized output format %#x, using DXGI_FORMAT_B8G8R8A8_UNORM as fallback.\n", output_format); + output_format = DXGI_FORMAT_B8G8R8A8_UNORM; }
if (FAILED(hr = MFCreateMediaType(&media_type))) diff --git a/dlls/mfmediaengine/tests/mfmediaengine.c b/dlls/mfmediaengine/tests/mfmediaengine.c index ca06f064eb4..4f3bc2920cd 100644 --- a/dlls/mfmediaengine/tests/mfmediaengine.c +++ b/dlls/mfmediaengine/tests/mfmediaengine.c @@ -133,7 +133,6 @@ struct media_engine_notify LONG refcount;
HANDLE event_ended; - HANDLE event_error; HANDLE event_first_frame; HANDLE event_loadeddata; HANDLE event_playing; @@ -172,7 +171,6 @@ static ULONG WINAPI media_engine_notify_Release(IMFMediaEngineNotify *iface) if (!refcount) { CloseHandle(notify->event_ended); - CloseHandle(notify->event_error); CloseHandle(notify->event_first_frame); CloseHandle(notify->event_loadeddata); CloseHandle(notify->event_playing); @@ -190,7 +188,6 @@ static HRESULT WINAPI media_engine_notify_EventNotify(IMFMediaEngineNotify *ifac { case MF_MEDIA_ENGINE_EVENT_ERROR: trace("MF_MEDIA_ENGINE_EVENT_ERROR: err %p, hr %lx.\n", (void *)param1, param2); - SetEvent(impl->event_error); break; case MF_MEDIA_ENGINE_EVENT_LOADEDDATA: SetEvent(impl->event_loadeddata); @@ -224,9 +221,7 @@ static struct media_engine_notify *create_callback(void) struct media_engine_notify *object;
object = calloc(1, sizeof(*object)); - object->event_ended = CreateEventW(NULL, FALSE, FALSE, NULL); - object->event_error = CreateEventW(NULL, FALSE, FALSE, NULL); object->event_first_frame = CreateEventW(NULL, FALSE, FALSE, NULL); object->event_loadeddata = CreateEventW(NULL, FALSE, FALSE, NULL); object->event_playing = CreateEventW(NULL, FALSE, FALSE, NULL); @@ -1230,10 +1225,6 @@ static void test_playback_on_d3d_device(void) hr = IMFMediaEngine_SetSource(engine, filename); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- /* Skip tests if media engine init fails. Currently always happening in Wine! */ - if (!WaitForSingleObject(notify->event_error, 100)) - goto done; - hr = IMFMediaEngine_Play(engine); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
From: Bernhard Kölbl besentv@gmail.com
Otherwise video playback in MfMediaEngine won't have any sound and will get stuck at the end of playback. --- dlls/mf/sar.c | 4 ++++ dlls/mfmediaengine/tests/mfmediaengine.c | 8 +------- 2 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/dlls/mf/sar.c b/dlls/mf/sar.c index 84824f954dd..01e0b7af944 100644 --- a/dlls/mf/sar.c +++ b/dlls/mf/sar.c @@ -1688,6 +1688,7 @@ static HRESULT WINAPI audio_renderer_stream_type_handler_SetCurrentMediaType(IMF audio_renderer_preroll(renderer); } } + else WARN("Failed to create audio client, hr %#lx.\n", hr); } else hr = MF_E_INVALIDMEDIATYPE; @@ -1947,6 +1948,9 @@ static HRESULT sar_create_object(IMFAttributes *attributes, void *user_context, if (FAILED(hr = audio_renderer_collect_supported_types(renderer))) goto failed;
+ if (FAILED(hr = audio_renderer_create_audio_client(renderer))) + goto failed; + *obj = (IUnknown *)&renderer->IMFMediaSink_iface;
return S_OK; diff --git a/dlls/mfmediaengine/tests/mfmediaengine.c b/dlls/mfmediaengine/tests/mfmediaengine.c index 4f3bc2920cd..34e62c76cd5 100644 --- a/dlls/mfmediaengine/tests/mfmediaengine.c +++ b/dlls/mfmediaengine/tests/mfmediaengine.c @@ -1289,12 +1289,6 @@ static void test_playback_on_d3d_device(void) }
current_time = IMFMediaEngine_GetCurrentTime(engine); - /* Workaround for a current bug in Wine, where video playback isn't properly finishing. Break in this case. */ - if (current_time > 4.3) - { - todo_wine ok(0, "Forcefully breaking out of video loop, because video playback is partially broken.\n"); - break; - } ok(current_time < 4.3 && current_time > 0.0, "IMFMediaEngine_GetCurrentTime returned %lf.\n", current_time);
hr = IMFMediaEngine_OnVideoStreamTick(engine, &presentation_time); @@ -1333,7 +1327,7 @@ static void test_playback_on_d3d_device(void) }
hr = IMFMediaEngine_OnVideoStreamTick(engine, &presentation_time); - todo_wine ok(hr == S_FALSE, "Unexpected hr %#lx.\n", hr); + ok(hr == S_FALSE, "Unexpected hr %#lx.\n", hr);
done: /* Release MF */
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=124932
Your paranoid android.
=== w8 (32 bit report) ===
mfmediaengine: mfmediaengine.c:267: Test failed: Unexpected hr 0xc00d36e6. 0a84:mfmediaengine: unhandled exception c0000005 at 004052EA
=== w8adm (32 bit report) ===
mfmediaengine: mfmediaengine.c:267: Test failed: Unexpected hr 0xc00d36e6. 0994:mfmediaengine: unhandled exception c0000005 at 004052EA
=== w864 (32 bit report) ===
mfmediaengine: mfmediaengine.c:267: Test failed: Unexpected hr 0xc00d36e6. 056c:mfmediaengine: unhandled exception c0000005 at 004052EA
=== w1064v1507 (32 bit report) ===
mfmediaengine: mfmediaengine.c:267: Test failed: Unexpected hr 0xc00d36e6. 0d38:mfmediaengine: unhandled exception c0000005 at 004052EA
=== w1064v1809 (32 bit report) ===
mfmediaengine: mfmediaengine.c:1292: Test failed: IMFMediaEngine_GetCurrentTime returned 0.000000.
=== w1064 (32 bit report) ===
mfmediaengine: mfmediaengine.c:1292: Test failed: IMFMediaEngine_GetCurrentTime returned 0.000000.
=== w1064_tsign (32 bit report) ===
mfmediaengine: mfmediaengine.c:1292: Test failed: IMFMediaEngine_GetCurrentTime returned 0.000000.
=== w10pro64 (32 bit report) ===
mfmediaengine: mfmediaengine.c:1292: Test failed: IMFMediaEngine_GetCurrentTime returned 0.000000.
=== w864 (64 bit report) ===
mfmediaengine: mfmediaengine.c:267: Test failed: Unexpected hr 0xc00d36e6. 0b20:mfmediaengine: unhandled exception c0000005 at 0000000000404ACC
=== w1064v1507 (64 bit report) ===
mfmediaengine: mfmediaengine.c:267: Test failed: Unexpected hr 0xc00d36e6. 09bc:mfmediaengine: unhandled exception c0000005 at 0000000000404ACC
=== w1064v1809 (64 bit report) ===
mfmediaengine: mfmediaengine.c:1292: Test failed: IMFMediaEngine_GetCurrentTime returned 0.000000.
=== w1064 (64 bit report) ===
mfmediaengine: mfmediaengine.c:1292: Test failed: IMFMediaEngine_GetCurrentTime returned 0.000000.
=== w1064_adm (64 bit report) ===
mfmediaengine: mfmediaengine.c:1292: Test failed: IMFMediaEngine_GetCurrentTime returned 0.000000.
=== w1064_tsign (64 bit report) ===
mfmediaengine: mfmediaengine.c:1292: Test failed: IMFMediaEngine_GetCurrentTime returned 0.000000.
=== w10pro64 (64 bit report) ===
mfmediaengine: mfmediaengine.c:1292: Test failed: IMFMediaEngine_GetCurrentTime returned 0.000000.
=== w10pro64_en_AE_u8 (64 bit report) ===
mfmediaengine: mfmediaengine.c:1292: Test failed: IMFMediaEngine_GetCurrentTime returned 0.000000.
=== w10pro64_ar (64 bit report) ===
mfmediaengine: mfmediaengine.c:1292: Test failed: IMFMediaEngine_GetCurrentTime returned 0.000000.
=== w10pro64_ja (64 bit report) ===
mfmediaengine: mfmediaengine.c:1292: Test failed: IMFMediaEngine_GetCurrentTime returned 0.000000.
=== w10pro64_zh_CN (64 bit report) ===
mfmediaengine: mfmediaengine.c:1292: Test failed: IMFMediaEngine_GetCurrentTime returned 0.000000.
=== debian11 (32 bit report) ===
mf: mf.c:4938: Test failed: Unexpected hr 0. mf.c:4941: Test failed: Unexpected hr 0. mf.c:4944: Test failed: Unexpected hr 0. mf.c:4947: Test failed: Unexpected hr 0. mf.c:5091: Test failed: Unexpected hr 0x1. mf.c:5222: Test failed: Release returned 1 mf.c:5246: Test failed: Release returned 1
=== debian11 (32 bit zh:CN report) ===
mf: mf.c:4938: Test failed: Unexpected hr 0. mf.c:4941: Test failed: Unexpected hr 0. mf.c:4944: Test failed: Unexpected hr 0. mf.c:4947: Test failed: Unexpected hr 0. mf.c:5091: Test failed: Unexpected hr 0x1. mf.c:5222: Test failed: Release returned 1 mf.c:5246: Test failed: Release returned 1
=== debian11 (build log) ===
Use of uninitialized value $Flaky in addition (+) at /home/testbot/lib/WineTestBot/LogUtils.pm line 720, <$LogFile> line 24693. Use of uninitialized value $Flaky in addition (+) at /home/testbot/lib/WineTestBot/LogUtils.pm line 720, <$LogFile> line 24693. Use of uninitialized value $Flaky in addition (+) at /home/testbot/lib/WineTestBot/LogUtils.pm line 720, <$LogFile> line 24693.
=== debian11 (64 bit WoW report) ===
mf: mf.c:4938: Test failed: Unexpected hr 0. mf.c:4941: Test failed: Unexpected hr 0. mf.c:4944: Test failed: Unexpected hr 0. mf.c:4947: Test failed: Unexpected hr 0. mf.c:5091: Test failed: Unexpected hr 0x1. mf.c:5222: Test failed: Release returned 1 mf.c:5246: Test failed: Release returned 1
Stefan Dösinger (@stefan) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
- IMFAttributes *attributes;
IMFDXGIDeviceManager *manager = NULL;
IMFMediaEngine *media_engine = NULL;
IMFAttributes *attributes = NULL; UINT token; HRESULT hr;
hr = pMFCreateDXGIDeviceManager(&token, &manager); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- hr = MFCreateAttributes(&attributes, 3);
- ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- if (d3d11_dev)
- {
hr = pMFCreateDXGIDeviceManager(&token, &manager);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
Is gitlab showing this in a weird way or do you create two IMFDXGIDeviceManager instances here? Above the if condition you unconditionally create one, and here again
Stefan Dösinger (@stefan) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
hr = IMFAttributes_SetUnknown(attributes, &MF_MEDIA_ENGINE_DXGI_MANAGER, (IUnknown *)manager);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
}
hr = IMFAttributes_SetUnknown(attributes, &MF_MEDIA_ENGINE_CALLBACK, (IUnknown *)callback); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- hr = IMFAttributes_SetUINT32(attributes, &MF_MEDIA_ENGINE_VIDEO_OUTPUT_FORMAT, DXGI_FORMAT_UNKNOWN);
- ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- if (output_format)
- {
hr = IMFAttributes_SetUINT32(attributes, &MF_MEDIA_ENGINE_VIDEO_OUTPUT_FORMAT, output_format);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- }
Is the if check necessary here? Before this patch MF_MEDIA_ENGINE_VIDEO_OUTPUT_FORMAT=DXGI_FORMAT_UNKNOWN was called all the time. It feels like this patch should keep calling it.
Stefan Dösinger (@stefan) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
#include "wine/test.h"
static HRESULT (WINAPI *pMFCreateDXGIDeviceManager)(UINT *token, IMFDXGIDeviceManager **manager); +static HRESULT (WINAPI *pD3D11CreateDevice)(IDXGIAdapter *adapter, D3D_DRIVER_TYPE driver_type, HMODULE swrast, UINT flags,
const D3D_FEATURE_LEVEL *feature_levels, UINT levels, UINT sdk_version, ID3D11Device **device_out,
D3D_FEATURE_LEVEL *obtained_feature_level, ID3D11DeviceContext **immediate_context);
+static HRESULT (WINAPI *pD3D11CreateDeviceAndSwapChain)(IDXGIAdapter *adapter, D3D_DRIVER_TYPE driver_type,
HMODULE swrast, UINT flags, const D3D_FEATURE_LEVEL *feature_levels, UINT levels,
UINT sdk_version, const DXGI_SWAP_CHAIN_DESC *swapchain_desc, IDXGISwapChain **swapchain,
ID3D11Device **device_out, D3D_FEATURE_LEVEL *obtained_feature_level, ID3D11DeviceContext **immediate_context);
+static HRESULT (WINAPI *pD3DCompile)(const void *data, SIZE_T data_size, const char *filename,
const D3D_SHADER_MACRO *defines, ID3DInclude *include, const char *entrypoint,
const char *target, UINT sflags, UINT eflags, ID3DBlob **shader, ID3DBlob **error_messages);
+//pD3DCompile pD3DCompile;
leftover?
Stefan Dösinger (@stefan) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
- GetTempPathW(ARRAY_SIZE(pathW), pathW);
- lstrcatW(pathW, name);
- 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 = FindResourceW(NULL, name, (LPCWSTR)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" );
- CloseHandle(file);
inconsistent indentation
Stefan Dösinger (@stefan) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
X(MFCreateDXGIDeviceManager);
- if ((mod = LoadLibraryA("d3d11.dll")))
- {
X(D3D11CreateDevice);
X(D3D11CreateDeviceAndSwapChain);
- }
- if ((mod = LoadLibraryA("D3DCompiler_47.dll")))
- {
X(D3DCompile);
- }
#undef X }
+static WCHAR *load_resource(const WCHAR *name)
wouldn't extract_resource or unpack_resource be a better name for something that copies data from the executable to a file? Ignore this if there is precedent for this name.
Stefan Dösinger (@stefan) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface);
}
+LRESULT CALLBACK WindowCallback(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{
- return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+}
+static void test_playback_on_d3d_device(void) +{
- static const char ps_input[] = "Texture2D<float4> tex : register(t0);\n"
"SamplerState smp : register(s0);\n"
"\n"
//"float4 main(in float4 p : SV_POSITION, in float2 texcoord : TEX_COORD) : SV_TARGET {\n"
Did you figure out why you needed the extra parameter to make native d3dcompiler happy? It seems like this comment is a leftover from those experiments
Stefan Dösinger (@stefan) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
- notify = create_callback();
- ok(!!notify, "notify was %p.\n", notify);
- engine = create_media_engine(¬ify->IMFMediaEngineNotify_iface, (IUnknown *)d3d11_dev, DXGI_FORMAT_R32G32B32_FLOAT);
- ok(!!engine, "engine was %p.\n", engine);
- filename = load_resource(L"test.mp4");
- ok(!!filename, "filename was %s.\n", debugstr_w(filename));
- /* Actual tests */
- hr = IMFMediaEngine_SetSource(engine, filename);
- ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- /* Skip tests if media engine init fails. Currently always happening in Wine! */
- if (!WaitForSingleObject(notify->event_error, 100))
goto done;
is there no better way to check for the result of SetSource than to wait an arbitrary amount of time for a callback?
Stefan Dösinger (@stefan) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
goto done;
- hr = IMFMediaEngine_Play(engine);
- ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- hr = IMFMediaEngine_SetLoop(engine, FALSE);
- ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- hr = IMFMediaEngine_GetNativeVideoSize(engine, &size_x, &size_y);
- ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr);
- current_time = IMFMediaEngine_GetCurrentTime(engine);
- ok(current_time == 0.0, "IMFMediaEngine_GetCurrentTime returned %lf.\n", current_time);
- duration = IMFMediaEngine_GetDuration(engine);
- ok(isnan(duration), "IMFMediaEngine_GetDuration returned %lf.\n", duration);
Does this assume that the test video is not yet loaded? IOW, will those tests fail in unlucky situations where MF loads the file faster than expected?
Stefan Dösinger (@stefan) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
- hr = ID3D11Device_CreateTexture2D(d3d11_dev, &texture_desc, NULL, &video_texture);
- ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- hr = ID3D11Device_CreateShaderResourceView(d3d11_dev, (ID3D11Resource *)video_texture, NULL, &d3d11_srv);
- ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- rasterizer_desc.CullMode = D3D11_CULL_NONE;
- rasterizer_desc.FillMode = D3D11_FILL_SOLID;
- hr = ID3D11Device_CreateRasterizerState(d3d11_dev, &rasterizer_desc, &d3d11_rs);
- ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- /* Media engine init */
- notify = create_callback();
- ok(!!notify, "notify was %p.\n", notify);
- engine = create_media_engine(¬ify->IMFMediaEngineNotify_iface, (IUnknown *)d3d11_dev, DXGI_FORMAT_R32G32B32_FLOAT);
I know the RGB32F triggered writing this test, but I think a basic test with a known-good video format is worth having too. Consider using RGBA8 here and add a separate test for weird formats.
Is there a way to prove that patch 4 is correct, i.e., can you query the actual video format MF gives you somehow?
Stefan Dösinger (@stefan) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
hr = IMFMediaEngine_OnVideoStreamTick(engine, &presentation_time);
ok(hr == S_OK || hr == S_FALSE, "Unexpected hr %#lx.\n", hr); /* Returns S_FALSE when no frame is ready. */
if (hr == S_OK)
{
hr = IMFMediaEngine_TransferVideoFrame(engine, (IUnknown *)video_texture, &video_norm_rect, &rect, &border_color);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
}
ok(!enable_print, "ID3D11Device_GetImmediateContext\n");
ID3D11Device_GetImmediateContext(d3d11_dev, &d3d11_dev_ctx);
/* Map and unmap to wait for the Draw calls in MF to have finished. */
hr = ID3D11DeviceContext_Map(d3d11_dev_ctx, (ID3D11Resource *)video_texture, 0, D3D11_MAP_READ, 0, &mapped_subresource);
ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
ID3D11DeviceContext_Unmap(d3d11_dev_ctx, (ID3D11Resource *)video_texture, 0);
If map() synchronizes vs MF properly, why do draws not do that? Now that you are mapping the texture anyway you could check the mapped data for expected contents.
Stefan Dösinger (@stefan) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
}
ok(!enable_print, "ID3D11Device_GetImmediateContext\n");
ID3D11Device_GetImmediateContext(d3d11_dev, &d3d11_dev_ctx);
/* Map and unmap to wait for the Draw calls in MF to have finished. */
hr = ID3D11DeviceContext_Map(d3d11_dev_ctx, (ID3D11Resource *)video_texture, 0, D3D11_MAP_READ, 0, &mapped_subresource);
ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
ID3D11DeviceContext_Unmap(d3d11_dev_ctx, (ID3D11Resource *)video_texture, 0);
ID3D11DeviceContext_ClearState(d3d11_dev_ctx);
ID3D11DeviceContext_RSSetState(d3d11_dev_ctx, d3d11_rs);
ID3D11DeviceContext_OMSetRenderTargets(d3d11_dev_ctx, 1, &d3d11_rtv, NULL);
ID3D11DeviceContext_VSSetShader(d3d11_dev_ctx, d3d11_vs, NULL, 0);
ID3D11DeviceContext_PSSetShader(d3d11_dev_ctx, d3d11_ps, NULL, 0);
ID3D11DeviceContext_PSSetShaderResources(d3d11_dev_ctx, 0, 1, &d3d11_srv);
I think all of these only need to be called once, so you can pull them out of the render loop (that while(!event_ended) loop)
Nikolay Sivov (@nsivov) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
- IMFAttributes *attributes;
IMFDXGIDeviceManager *manager = NULL;
IMFMediaEngine *media_engine = NULL;
IMFAttributes *attributes = NULL; UINT token; HRESULT hr;
hr = pMFCreateDXGIDeviceManager(&token, &manager); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- hr = MFCreateAttributes(&attributes, 3);
- ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- if (d3d11_dev)
- {
hr = pMFCreateDXGIDeviceManager(&token, &manager);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
It's easier to pass in initialized device manager instance.
On Thu Oct 13 10:12:12 2022 +0000, Stefan Dösinger wrote:
Is the if check necessary here? Before this patch MF_MEDIA_ENGINE_VIDEO_OUTPUT_FORMAT=DXGI_FORMAT_UNKNOWN was called all the time. It feels like this patch should keep calling it.
Yes, this is changing behavior. It's better to call it unconditionally.
On Thu Oct 13 10:12:16 2022 +0000, Stefan Dösinger wrote:
If map() synchronizes vs MF properly, why do draws not do that? Now that you are mapping the texture anyway you could check the mapped data for expected contents.
I don't understand this part either.
Nikolay Sivov (@nsivov) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
{
TranslateMessage(&message);
DispatchMessageW(&message);
}
current_time = IMFMediaEngine_GetCurrentTime(engine);
/* Workaround for a current bug in Wine, where video playback isn't properly finishing. Break in this case. */
if (current_time > 4.3)
{
todo_wine ok(0, "Forcefully breaking out of video loop, because video playback is partially broken.\n");
break;
}
ok(current_time < 4.3 && current_time > 0.0, "IMFMediaEngine_GetCurrentTime returned %lf.\n", current_time);
hr = IMFMediaEngine_OnVideoStreamTick(engine, &presentation_time);
ok(hr == S_OK || hr == S_FALSE, "Unexpected hr %#lx.\n", hr); /* Returns S_FALSE when no frame is ready. */
This is SUCCEEDED().
Nikolay Sivov (@nsivov) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
ID3D11DeviceContext_Unmap(d3d11_dev_ctx, (ID3D11Resource *)video_texture, 0);
ID3D11DeviceContext_ClearState(d3d11_dev_ctx);
ID3D11DeviceContext_RSSetState(d3d11_dev_ctx, d3d11_rs);
ID3D11DeviceContext_OMSetRenderTargets(d3d11_dev_ctx, 1, &d3d11_rtv, NULL);
ID3D11DeviceContext_VSSetShader(d3d11_dev_ctx, d3d11_vs, NULL, 0);
ID3D11DeviceContext_PSSetShader(d3d11_dev_ctx, d3d11_ps, NULL, 0);
ID3D11DeviceContext_PSSetShaderResources(d3d11_dev_ctx, 0, 1, &d3d11_srv);
ID3D11DeviceContext_IASetPrimitiveTopology(d3d11_dev_ctx, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ID3D11DeviceContext_RSSetViewports(d3d11_dev_ctx, 1, &viewport);
ID3D11DeviceContext_Draw(d3d11_dev_ctx, 3, 0);
ID3D11DeviceContext_Release(d3d11_dev_ctx);
hr = IDXGISwapChain_Present(swapchain, 1, 0);
What's the purpose of presenting to a window? What does this test?
Nikolay Sivov (@nsivov) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
+static void test_playback_on_d3d_device(void) +{
- static const char ps_input[] = "Texture2D<float4> tex : register(t0);\n"
"SamplerState smp : register(s0);\n"
"\n"
//"float4 main(in float4 p : SV_POSITION, in float2 texcoord : TEX_COORD) : SV_TARGET {\n"
"float4 main(in float2 texcoord : TEX_COORD) : SV_TARGET {\n"
"texcoord.y = 1 - texcoord.y;\n" /* We get the image horizontally flipped so we transform it back. */
"return tex.Sample(smp, texcoord);\n"
"}";
- static const char vs_input[] = "float4 main(out float2 texcoord : TEX_COORD, in uint vid : SV_VERTEXID) : SV_POSITION {\n"
"texcoord = float2(float(vid & 1) * 2.0f, float(vid & 2));\n"
"return float4(-1.0f + 2.0f * texcoord, 0.0f, 1.0f);\n"
"}";
We usually put precompiled shaders in such cases. Is current wine capable to compile this? What about missing compiler library on Windows?
On 10/13/22 06:39, Nikolay Sivov (@nsivov) wrote:
Nikolay Sivov (@nsivov) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
+static void test_playback_on_d3d_device(void) +{
- static const char ps_input[] = "Texture2D<float4> tex : register(t0);\n"
"SamplerState smp : register(s0);\n"
"\n"
//"float4 main(in float4 p : SV_POSITION, in float2 texcoord : TEX_COORD) : SV_TARGET {\n"
"float4 main(in float2 texcoord : TEX_COORD) : SV_TARGET {\n"
"texcoord.y = 1 - texcoord.y;\n" /* We get the image horizontally flipped so we transform it back. */
"return tex.Sample(smp, texcoord);\n"
"}";
- static const char vs_input[] = "float4 main(out float2 texcoord : TEX_COORD, in uint vid : SV_VERTEXID) : SV_POSITION {\n"
"texcoord = float2(float(vid & 1) * 2.0f, float(vid & 2));\n"
"return float4(-1.0f + 2.0f * texcoord, 0.0f, 1.0f);\n"
"}";
We usually put precompiled shaders in such cases. Is current wine capable to compile this? What about missing compiler library on Windows?
I believe Wine is capable of compiling those, although I haven't checked.
(Incidentally, there's a stray comment line in one of them.)
Compiler availability is more of a concern, though. As far as I can tell from my VMs, windows 7 ships d3dcompiler_43 but not 47, but windows 10 ships d3dcompiler_47 but not 43. All of our testbot machines have 47 on them (manually installed, I guess), and all but the win11 machines have 43 as well.
There's also the option of putting vkd3d-compiler in our build system, though that gets more complicated.
Nikolay Sivov (@nsivov) commented about dlls/mfmediaengine/main.c:
} memcpy(&subtype, &MFVideoFormat_Base, sizeof(subtype));
- if (!(subtype.Data1 = MFMapDXGIFormatToDX9Format(output_format)))
- while (!(subtype.Data1 = MFMapDXGIFormatToDX9Format(output_format))) {
WARN("Unrecognized output format %#x.\n", output_format);
return E_FAIL;
WARN("Unrecognized output format %#x, using DXGI_FORMAT_B8G8R8A8_UNORM as fallback.\n", output_format);
}output_format = DXGI_FORMAT_B8G8R8A8_UNORM;
Why this format in particular, and why a "while" loop?
Nikolay Sivov (@nsivov) commented about dlls/mf/sar.c:
if (FAILED(hr = audio_renderer_collect_supported_types(renderer))) goto failed;
if (FAILED(hr = audio_renderer_create_audio_client(renderer)))
goto failed;
*obj = (IUnknown *)&renderer->IMFMediaSink_iface;
return S_OK;
This is not related to mfmediaengine at all. This function is called when media type is set, and is normally working. This deserves some explanation as to what's different in the media session that media engine creates.
Jinoh Kang (@iamahuman) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
- rect.right = VIDEO_WIDTH;
- rect.bottom = VIDEO_HEIGHT;
- video_norm_rect.right = 1.0f;
- video_norm_rect.bottom = 1.0f;
- viewport.Height = VIDEO_HEIGHT;
- viewport.Width = VIDEO_WIDTH;
- while (WaitForSingleObject(notify->event_ended, 0) > WAIT_OBJECT_0)
- {
ID3D11DeviceContext *d3d11_dev_ctx = NULL;
BOOL enable_print = FALSE;
D3D11_MAPPED_SUBRESOURCE mapped_subresource = { 0 };
if (PeekMessageW(&message, window, 0, 0, PM_REMOVE) > 0)
you could do
```suggestion:-0+0 while (PeekMessageW(&message, window, 0, 0, PM_REMOVE) > 0) ```
but just remove this altogether and replace the `Sleep(16)` below with time-bounded message loop
Jinoh Kang (@iamahuman) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
ID3D11DeviceContext_ClearState(d3d11_dev_ctx);
ID3D11DeviceContext_RSSetState(d3d11_dev_ctx, d3d11_rs);
ID3D11DeviceContext_OMSetRenderTargets(d3d11_dev_ctx, 1, &d3d11_rtv, NULL);
ID3D11DeviceContext_VSSetShader(d3d11_dev_ctx, d3d11_vs, NULL, 0);
ID3D11DeviceContext_PSSetShader(d3d11_dev_ctx, d3d11_ps, NULL, 0);
ID3D11DeviceContext_PSSetShaderResources(d3d11_dev_ctx, 0, 1, &d3d11_srv);
ID3D11DeviceContext_IASetPrimitiveTopology(d3d11_dev_ctx, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ID3D11DeviceContext_RSSetViewports(d3d11_dev_ctx, 1, &viewport);
ID3D11DeviceContext_Draw(d3d11_dev_ctx, 3, 0);
ID3D11DeviceContext_Release(d3d11_dev_ctx);
hr = IDXGISwapChain_Present(swapchain, 1, 0);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
Sleep(16); /* Simulate a framerate of ~60FPS */
```suggestion:-0+0 wait_for_events(16); /* Simulate a framerate of ~60FPS */ ```
where
```c static void wait_for_events(DWORD timeout) { DWORD start = GetTickCount(), elapsed; BOOL has_msg; MSG msg;
while (1) { if ((has_msg = PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))) { TranslateMessage(&msg); DispatchMessageW(&msg); } elapsed = GetTickCount() - start; if (elapsed >= timeout) break; if (!has_msg && MsgWaitForMultipleObjects(0, NULL, FALSE, timeout - elapsed, QS_ALLINPUT) != WAIT_OBJECT_0) break; } } ```
I have some fixes for Windows, can you check? Thanks!
On Thu Oct 13 10:12:12 2022 +0000, Stefan Dösinger wrote:
Is gitlab showing this in a weird way or do you create two IMFDXGIDeviceManager instances here? Above the if condition you unconditionally create one, and here again
This one slipped in in the first commit.
On Thu Oct 13 11:32:11 2022 +0000, Nikolay Sivov wrote:
Yes, this is changing behavior. It's better to call it unconditionally.
Yep, makes sense.
On Thu Oct 13 10:12:13 2022 +0000, Stefan Dösinger wrote:
leftover?
yep
On Thu Oct 13 10:12:14 2022 +0000, Stefan Dösinger wrote:
inconsistent indentation
prolly shouldn't copy paste code :D
On Thu Oct 13 10:12:14 2022 +0000, Stefan Dösinger wrote:
wouldn't extract_resource or unpack_resource be a better name for something that copies data from the executable to a file? Ignore this if there is precedent for this name.
sure, unpack_resource sounds good.
On 10/16/22 15:20, Bernhard Kölbl (@besentv) wrote:
On Thu Oct 13 10:12:14 2022 +0000, Stefan Dösinger wrote:
wouldn't extract_resource or unpack_resource be a better name for something that copies data from the executable to a file? Ignore this if there is precedent for this name.
sure, unpack_resource sounds good.
FWIW, there is precedent, load_resource() appears in quite a lot of tests with an identical body. I think I'm ultimately to blame for most if not all of them.
On Thu Oct 13 10:12:15 2022 +0000, Stefan Dösinger wrote:
Did you figure out why you needed the extra parameter to make native d3dcompiler happy? It seems like this comment is a leftover from those experiments
According to Philip Rebohle it's a bug/ABI inconsitency in compiler_47, not sure what to do about it... I think other versions aren't installed by default on Windows 10+?
On Thu Oct 13 10:12:15 2022 +0000, Stefan Dösinger wrote:
is there no better way to check for the result of SetSource than to wait an arbitrary amount of time for a callback?
Afaiu, MfMediaEngine is mostly event driven, that's why I wait for a possible error here.
On Thu Oct 13 10:12:16 2022 +0000, Stefan Dösinger wrote:
Does this assume that the test video is not yet loaded? IOW, will those tests fail in unlucky situations where MF loads the file faster than expected?
Could happen, but haven't encountered it yet, not sure if it needs a change.
On Thu Oct 13 10:12:16 2022 +0000, Stefan Dösinger wrote:
I know the RGB32F triggered writing this test, but I think a basic test with a known-good video format is worth having too. Consider using RGBA8 here and add a separate test for weird formats. Is there a way to prove that patch 4 is correct, i.e., can you query the actual video format MF gives you somehow?
My best guess is that MfMediaEngine actually uses D3D11VideoDevice instead of Mf backend in the first place. D3D video decoding allows for all DXGI formats but from what I can tell Mf is bound to DX9.
On Thu Oct 13 11:36:54 2022 +0000, Nikolay Sivov wrote:
I don't understand this part either.
Mhm, I did some further testing, and it's actually not doing anything useful. I'm going to remove this part.
On Thu Oct 13 11:37:35 2022 +0000, Nikolay Sivov wrote:
What's the purpose of presenting to a window? What does this test?
Well, my idea was to recreate some extremely weird behavior by some app I'm debugging that does mixing of different DXGI formats when rendering a video with media engine. The window is just to have some visible aid when you run the tests.
On Thu Oct 13 18:10:36 2022 +0000, **** wrote:
Zebediah Figura replied on the mailing list:
On 10/13/22 06:39, Nikolay Sivov (@nsivov) wrote: > Nikolay Sivov (@nsivov) commented about dlls/mfmediaengine/tests/mfmediaengine.c: >> + >> + >> +static void test_playback_on_d3d_device(void) >> +{ >> + static const char ps_input[] = "Texture2D<float4> tex : register(t0);\n" >> + "SamplerState smp : register(s0);\n" >> + "\n" >> + //"float4 main(in float4 p : SV_POSITION, in float2 texcoord : TEX_COORD) : SV_TARGET {\n" >> + "float4 main(in float2 texcoord : TEX_COORD) : SV_TARGET {\n" >> + "texcoord.y = 1 - texcoord.y;\n" /* We get the image horizontally flipped so we transform it back. */ >> + "return tex.Sample(smp, texcoord);\n" >> + "}"; >> + static const char vs_input[] = "float4 main(out float2 texcoord : TEX_COORD, in uint vid : SV_VERTEXID) : SV_POSITION {\n" >> + "texcoord = float2(float(vid & 1) * 2.0f, float(vid & 2));\n" >> + "return float4(-1.0f + 2.0f * texcoord, 0.0f, 1.0f);\n" >> + "}"; > We usually put precompiled shaders in such cases. Is current wine capable to compile this? What about missing compiler library on Windows? > I believe Wine is capable of compiling those, although I haven't checked. (Incidentally, there's a stray comment line in one of them.) Compiler availability is more of a concern, though. As far as I can tell from my VMs, windows 7 ships d3dcompiler_43 but not 47, but windows 10 ships d3dcompiler_47 but not 43. All of our testbot machines have 47 on them (manually installed, I guess), and all but the win11 machines have 43 as well. There's also the option of putting vkd3d-compiler in our build system, though that gets more complicated.
So should I better put a precompiled shader here?
On Thu Oct 13 11:40:21 2022 +0000, Nikolay Sivov wrote:
Why this format in particular, and why a "while" loop?
Because it currently works and to minimize duplicate code. Basically, the while executes at most 2 times. Anyway, I'll probably submit this patch for Proton, because it's more of a hack and the current MfMediaEngine impl doesn't seem correct, especially the DXGI to DX9 part here.
On Thu Oct 13 11:43:31 2022 +0000, Nikolay Sivov wrote:
This is not related to mfmediaengine at all. This function is called when media type is set, and is normally working. This deserves some explanation as to what's different in the media session that media engine creates.
Will be scrapped.
On Mon Oct 17 14:19:55 2022 +0000, Bernhard Kölbl wrote:
So should I better put a precompiled shader here?
I think the HLSL shader code is way nicer than precompiled shaders. I would keep it since Wine is capable of compiling them.
If either d3dcompiler_43 or d3dcompiler_47 are available on Windows I guess we can adjust the LoadLibrary code to try _47 first and fall back to _43 if necessary.