The last patch is a huge optimization to what is done in patch 2. Without that the sample is first copied from GPU to CPU each time wg_transform_read_mf() locks the buffer (allocating extra linear buffer on the way) and then copies the data back to GPU. That happens even if there is no sample available from wg_transform. With the last patch there is just one memory copy to the (write-only) locked DXGI surface buffer. And CPU to GPU texture transfer (which would be there anyway in most cases on software path as most of the apps are going to get the image to GPU anyway). In principle we could also skip the explicit staging texture and use _UpdateSubresource instead directly from h264 decoder, but this is currently not supported in wined3d for chroma formats and the overall difference between _UpdateSubresource and explicit mapped staging texture is not that great probably.
-- v2: winegstreamer: Pass temporary sample to wg_transform_read_mf() in h264 decoder. winegstreamer: Provide samples if DXGI device manager is set in h264 decoder. winegstreamer: Process MFT_MESSAGE_SET_D3D_MANAGER in h264 decoder. mf/tests: Test h264 decoder with dxgi device manager.
From: Paul Gofman pgofman@codeweavers.com
--- dlls/mf/tests/Makefile.in | 2 +- dlls/mf/tests/mf.c | 4 + dlls/mf/tests/mf_test.h | 2 + dlls/mf/tests/transform.c | 292 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 299 insertions(+), 1 deletion(-)
diff --git a/dlls/mf/tests/Makefile.in b/dlls/mf/tests/Makefile.in index adb9800ca07..c684d033207 100644 --- a/dlls/mf/tests/Makefile.in +++ b/dlls/mf/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = mf.dll -IMPORTS = mf mfplat dmoguids mfuuid strmiids uuid wmcodecdspuuid ole32 user32 propsys msdmo +IMPORTS = mf mfplat dmoguids mfuuid strmiids uuid wmcodecdspuuid ole32 user32 propsys msdmo d3d11
C_SRCS = \ mf.c \ diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 399f983983f..59689652677 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -45,6 +45,7 @@ extern GUID DMOVideoFormat_RGB32;
HRESULT (WINAPI *pMFCreateSampleCopierMFT)(IMFTransform **copier); HRESULT (WINAPI *pMFGetTopoNodeCurrentType)(IMFTopologyNode *node, DWORD stream, BOOL output, IMFMediaType **type); +HRESULT (WINAPI *pMFCreateDXGIDeviceManager)(UINT *token, IMFDXGIDeviceManager **manager); BOOL has_video_processor;
static BOOL is_vista(void) @@ -6467,6 +6468,9 @@ void init_functions(void) #define X(f) p##f = (void*)GetProcAddress(mod, #f) X(MFCreateSampleCopierMFT); X(MFGetTopoNodeCurrentType); + + mod = GetModuleHandleA("mfplat.dll"); + X(MFCreateDXGIDeviceManager); #undef X
hr = CoInitialize(NULL); diff --git a/dlls/mf/tests/mf_test.h b/dlls/mf/tests/mf_test.h index 7973e007a68..77af51abd55 100644 --- a/dlls/mf/tests/mf_test.h +++ b/dlls/mf/tests/mf_test.h @@ -32,6 +32,8 @@
extern HRESULT (WINAPI *pMFCreateSampleCopierMFT)(IMFTransform **copier); extern HRESULT (WINAPI *pMFGetTopoNodeCurrentType)(IMFTopologyNode *node, DWORD stream, BOOL output, IMFMediaType **type); +extern HRESULT (WINAPI *pMFCreateDXGIDeviceManager)(UINT *token, IMFDXGIDeviceManager **manager); + extern BOOL has_video_processor; void init_functions(void);
diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index 602a03aabd0..7aa8113d703 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -42,6 +42,8 @@
#include "initguid.h"
+#include "d3d11_4.h" + DEFINE_GUID(DMOVideoFormat_RGB24,D3DFMT_R8G8B8,0x524f,0x11ce,0x9f,0x53,0x00,0x20,0xaf,0x0b,0xa7,0x70); DEFINE_GUID(DMOVideoFormat_RGB32,D3DFMT_X8R8G8B8,0x524f,0x11ce,0x9f,0x53,0x00,0x20,0xaf,0x0b,0xa7,0x70); DEFINE_GUID(DMOVideoFormat_RGB555,D3DFMT_X1R5G5B5,0x524f,0x11ce,0x9f,0x53,0x00,0x20,0xaf,0x0b,0xa7,0x70); @@ -6713,6 +6715,295 @@ failed: CoUninitialize(); }
+static HRESULT get_next_h264_output_sample(IMFTransform *transform, IMFSample **input_sample, + IMFSample *output_sample, MFT_OUTPUT_DATA_BUFFER *output, const BYTE **data, ULONG *data_len) +{ + DWORD status; + HRESULT hr; + + while (1) + { + status = 0; + memset(output, 0, sizeof(*output)); + output[0].pSample = output_sample; + hr = IMFTransform_ProcessOutput(transform, 0, 1, output, &status); + if (hr != S_OK) + ok(output[0].pSample == output_sample, "got %p.\n", output[0].pSample); + if (hr != MF_E_TRANSFORM_NEED_MORE_INPUT) + return hr; + + ok(status == 0, "got output[0].dwStatus %#lx\n", status); + hr = IMFTransform_ProcessInput(transform, 0, *input_sample, 0); + ok(hr == S_OK, "got %#lx\n", hr); + IMFSample_Release(*input_sample); + *input_sample = next_h264_sample(data, data_len); + } +} + +static void test_h264_with_dxgi_manager(void) +{ + static const unsigned int set_width = 82, set_height = 84, aligned_width = 96, aligned_height = 96; + const struct attribute_desc output_sample_attributes[] = + { + ATTR_UINT32(MFSampleExtension_CleanPoint, 1), + {0}, + }; + const struct buffer_desc output_buffer_desc_nv12 = + { + .length = aligned_width * aligned_height * 3 / 2, + .compare = compare_nv12, .dump = dump_nv12, .rect = {.top=0, .left=0, .right = set_width, .bottom = set_height}, + }; + const struct sample_desc output_sample_desc_nv12 = + { + .attributes = output_sample_attributes, + .sample_time = 333667, .sample_duration = 333667, + .buffer_count = 1, .buffers = &output_buffer_desc_nv12, + }; + + IMFDXGIDeviceManager *manager = NULL; + IMFTrackedSample *tracked_sample; + IMFSample *input_sample, *sample; + MFT_OUTPUT_DATA_BUFFER output[1]; + IMFTransform *transform = NULL; + ID3D11Multithread *multithread; + IMFCollection *output_samples; + MFT_OUTPUT_STREAM_INFO info; + IMFDXGIBuffer *dxgi_buffer; + unsigned int width, height; + D3D11_TEXTURE2D_DESC desc; + IMFMediaBuffer *buffer; + IMFAttributes *attribs; + ID3D11Texture2D *tex2d; + IMF2DBuffer2 *buffer2d; + ID3D11Device *d3d11; + IMFMediaType *type; + DWORD status, val; + UINT64 frame_size; + MFVideoArea area; + const BYTE *data; + ULONG data_len; + UINT32 value; + HRESULT hr; + UINT token; + GUID guid; + DWORD ret; + + if (!pMFCreateDXGIDeviceManager) + { + win_skip("MFCreateDXGIDeviceManager() is not avaliable, skipping tests.\n"); + return; + } + + hr = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, D3D11_CREATE_DEVICE_VIDEO_SUPPORT, NULL, 0, + D3D11_SDK_VERSION, &d3d11, NULL, NULL); + if (FAILED(hr)) + { + skip("D3D11 device creation failed, skipping tests.\n"); + return; + } + + hr = MFStartup(MF_VERSION, 0); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = CoInitialize(NULL); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = ID3D11Device_QueryInterface(d3d11, &IID_ID3D11Multithread, (void **)&multithread); + ok(hr == S_OK, "got %#lx\n", hr); + ID3D11Multithread_SetMultithreadProtected(multithread, TRUE); + ID3D11Multithread_Release(multithread); + + hr = pMFCreateDXGIDeviceManager(&token, &manager); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IMFDXGIDeviceManager_ResetDevice(manager, (IUnknown *)d3d11, token); + ok(hr == S_OK, "got %#lx\n", hr); + ID3D11Device_Release(d3d11); + + if (FAILED(hr = CoCreateInstance(&CLSID_MSH264DecoderMFT, NULL, CLSCTX_INPROC_SERVER, + &IID_IMFTransform, (void **)&transform))) + goto failed; + + hr = IMFTransform_GetAttributes(transform, &attribs); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IMFAttributes_GetUINT32(attribs, &MF_SA_D3D11_AWARE, &value); + ok(hr == S_OK, "got %#lx\n", hr); + ok(value == 1, "got %u.\n", value); + IMFAttributes_Release(attribs); + + hr = IMFTransform_ProcessMessage(transform, MFT_MESSAGE_SET_D3D_MANAGER, (ULONG_PTR)transform); + todo_wine ok(hr == E_NOINTERFACE, "got %#lx\n", hr); + + hr = IMFTransform_ProcessMessage(transform, MFT_MESSAGE_SET_D3D_MANAGER, (ULONG_PTR)manager); + ok(hr == S_OK || broken(hr == E_NOINTERFACE), "got %#lx\n", hr); + if (hr == E_NOINTERFACE) + { + win_skip("No hardware video decoding support.\n"); + goto failed; + } + + 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 = IMFTransform_SetOutputType(transform, 0, type, 0); + ok(hr == S_OK, "got %#lx\n", hr); + IMFMediaType_Release(type); + + status = 0; + memset(output, 0, sizeof(output)); + hr = IMFTransform_ProcessOutput(transform, 0, 1, output, &status); + todo_wine ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "got %#lx\n", hr); + if (hr != MF_E_TRANSFORM_NEED_MORE_INPUT) goto failed; + + hr = IMFTransform_GetAttributes(transform, &attribs); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IMFAttributes_GetUINT32(attribs, &MF_SA_D3D11_AWARE, &value); + ok(hr == S_OK, "got %#lx\n", hr); + ok(value == 1, "got %u.\n", value); + IMFAttributes_Release(attribs); + + load_resource(L"h264data.bin", &data, &data_len); + + input_sample = next_h264_sample(&data, &data_len); + hr = get_next_h264_output_sample(transform, &input_sample, (void *)0xdeadbeef, output, &data, &data_len); + ok(hr == MF_E_TRANSFORM_STREAM_CHANGE, "got %#lx\n", hr); + ok(output[0].pSample == (void *)0xdeadbeef, "got %p.\n", output[0].pSample); + + 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); + memset(&area, 0xcc, sizeof(area)); + hr = IMFMediaType_GetBlob(type, &MF_MT_MINIMUM_DISPLAY_APERTURE, (BYTE *)&area, sizeof(area), NULL); + ok(hr == S_OK, "got %#lx\n", hr); + ok(!area.OffsetX.value && !area.OffsetX.fract, "got %d.%d.\n", area.OffsetX.value, area.OffsetX.fract); + ok(!area.OffsetY.value && !area.OffsetY.fract, "got %d.%d.\n", area.OffsetY.value, area.OffsetY.fract); + ok(area.Area.cx == set_width, "got %ld.\n", area.Area.cx); + ok(area.Area.cy == set_height, "got %ld.\n", area.Area.cy); + + hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &guid); + ok(hr == S_OK, "Failed to get subtype, hr %#lx.\n", hr); + ok(IsEqualIID(&guid, &MEDIASUBTYPE_NV12), "got guid %s.\n", debugstr_guid(&guid)); + + hr = IMFTransform_SetOutputType(transform, 0, type, 0); + ok(hr == S_OK, "got %#lx\n", hr); + IMFMediaType_Release(type); + + hr = IMFTransform_GetOutputStreamInfo(transform, 0, &info); + ok(hr == S_OK, "got %#lx\n", hr); + 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); + + status = 0; + memset(output, 0, sizeof(output)); + output[0].pSample = (void *)0xdeadbeef; + + hr = get_next_h264_output_sample(transform, &input_sample, (void *)0xdeadbeef, 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; + + hr = IMFSample_QueryInterface(sample, &IID_IMFTrackedSample, (void **)&tracked_sample); + ok(hr == S_OK, "got %#lx\n", hr); + IMFTrackedSample_Release(tracked_sample); + + hr = IMFSample_GetBufferCount(sample, &val); + ok(hr == S_OK, "got %#lx\n", hr); + ok(val == 1, "got %lu.\n", val); + hr = IMFSample_GetBufferByIndex(sample, 0, &buffer); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IMFMediaBuffer_QueryInterface(buffer, &IID_IMFDXGIBuffer, (void **)&dxgi_buffer); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IMFMediaBuffer_QueryInterface(buffer, &IID_IMF2DBuffer2, (void **)&buffer2d); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IMFDXGIBuffer_GetResource(dxgi_buffer, &IID_ID3D11Texture2D, (void **)&tex2d); + ok(hr == S_OK, "got %#lx\n", hr); + memset(&desc, 0xcc, sizeof(desc)); + ID3D11Texture2D_GetDesc(tex2d, &desc); + ok(desc.Format == DXGI_FORMAT_NV12, "got %u.\n", desc.Format); + ok(!desc.Usage, "got %u.\n", desc.Usage); + todo_wine ok(desc.BindFlags == D3D11_BIND_DECODER, "got %#x.\n", desc.BindFlags); + ok(!desc.CPUAccessFlags, "got %#x.\n", desc.CPUAccessFlags); + ok(!desc.MiscFlags, "got %#x.\n", desc.MiscFlags); + ok(desc.MipLevels == 1, "git %u.\n", desc.MipLevels); + ok(desc.Width == aligned_width, "got %u.\n", desc.Width); + ok(desc.Height == aligned_height, "got %u.\n", desc.Height); + + ID3D11Texture2D_Release(tex2d); + IMFDXGIBuffer_Release(dxgi_buffer); + IMF2DBuffer2_Release(buffer2d); + IMFMediaBuffer_Release(buffer); + IMFSample_Release(sample); + + status = 0; + hr = get_next_h264_output_sample(transform, &input_sample, (void *)0xdeadbeef, output, &data, &data_len); + ok(hr == S_OK, "got %#lx\n", hr); + ok(sample != output[0].pSample, "got %p.\n", output[0].pSample); + sample = output[0].pSample; + + hr = MFCreateCollection(&output_samples); + ok(hr == S_OK, "MFCreateCollection returned %#lx\n", hr); + + hr = IMFCollection_AddElement(output_samples, (IUnknown *)sample); + ok(hr == S_OK, "AddElement returned %#lx\n", hr); + IMFSample_Release(sample); + + ret = check_mf_sample_collection(output_samples, &output_sample_desc_nv12, L"nv12frame.bmp"); + ok(ret == 0, "got %lu%% diff\n", ret); + IMFCollection_Release(output_samples); + + memset(&info, 0xcc, sizeof(info)); + hr = IMFTransform_GetOutputStreamInfo(transform, 0, &info); + ok(hr == S_OK, "got %#lx\n", hr); + 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); + ok(info.cbSize == aligned_width * aligned_height * 2, "got %lu.\n", info.cbSize); + ok(!info.cbAlignment, "got %lu.\n", info.cbAlignment); + + hr = IMFTransform_ProcessMessage(transform, MFT_MESSAGE_SET_D3D_MANAGER, 0); + ok(hr == S_OK, "got %#lx\n", hr); + + memset(&info, 0xcc, sizeof(info)); + hr = IMFTransform_GetOutputStreamInfo(transform, 0, &info); + ok(hr == S_OK, "got %#lx\n", hr); + ok(info.dwFlags == (MFT_OUTPUT_STREAM_WHOLE_SAMPLES | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER + | MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE), "got %#lx.\n", info.dwFlags); + if (0) + { + /* hangs on Windows. */ + get_next_h264_output_sample(transform, &input_sample, NULL, output, &data, &data_len); + } + + IMFSample_Release(input_sample); + +failed: + if (manager) + IMFDXGIDeviceManager_Release(manager); + if (transform) + IMFTransform_Release(transform); + CoUninitialize(); +} + START_TEST(transform) { init_functions(); @@ -6731,4 +7022,5 @@ START_TEST(transform) test_color_convert(); test_video_processor(); test_mp3_decoder(); + test_h264_with_dxgi_manager(); }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/mf/tests/transform.c | 2 +- dlls/winegstreamer/h264_decoder.c | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index 7aa8113d703..3ab596317f9 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -6832,7 +6832,7 @@ static void test_h264_with_dxgi_manager(void) IMFAttributes_Release(attribs);
hr = IMFTransform_ProcessMessage(transform, MFT_MESSAGE_SET_D3D_MANAGER, (ULONG_PTR)transform); - todo_wine ok(hr == E_NOINTERFACE, "got %#lx\n", hr); + ok(hr == E_NOINTERFACE, "got %#lx\n", hr);
hr = IMFTransform_ProcessMessage(transform, MFT_MESSAGE_SET_D3D_MANAGER, (ULONG_PTR)manager); ok(hr == S_OK || broken(hr == E_NOINTERFACE), "got %#lx\n", hr); diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index 7d9c38837d3..3278440fc02 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -61,6 +61,8 @@ struct h264_decoder struct wg_format wg_format; struct wg_transform *wg_transform; struct wg_sample_queue *wg_sample_queue; + + IMFVideoSampleAllocatorEx *allocator; };
static struct h264_decoder *impl_from_IMFTransform(IMFTransform *iface) @@ -241,6 +243,7 @@ static ULONG WINAPI transform_Release(IMFTransform *iface)
if (!refcount) { + IMFVideoSampleAllocatorEx_Release(decoder->allocator); if (decoder->wg_transform) wg_transform_destroy(decoder->wg_transform); if (decoder->input_type) @@ -251,7 +254,6 @@ static ULONG WINAPI transform_Release(IMFTransform *iface) IMFAttributes_Release(decoder->output_attributes); if (decoder->attributes) IMFAttributes_Release(decoder->attributes); - wg_sample_queue_destroy(decoder->wg_sample_queue); free(decoder); } @@ -580,7 +582,14 @@ 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 %Ix stub!\n", iface, message, param); + struct h264_decoder *decoder = impl_from_IMFTransform(iface); + + TRACE("iface %p, message %#x, param %Ix.\n", iface, message, param); + + if (message == MFT_MESSAGE_SET_D3D_MANAGER) + return IMFVideoSampleAllocatorEx_SetDirectXManager(decoder->allocator, (IUnknown *)param); + + FIXME("Ignoring message %#x.\n", message); return S_OK; }
@@ -732,6 +741,8 @@ HRESULT h264_decoder_create(REFIID riid, void **ret) goto failed; if (FAILED(hr = wg_sample_queue_create(&decoder->wg_sample_queue))) goto failed; + if (FAILED(hr = MFCreateVideoSampleAllocatorEx(&IID_IMFVideoSampleAllocatorEx, (void **)&decoder->allocator))) + goto failed;
*ret = &decoder->IMFTransform_iface; TRACE("Created decoder %p\n", *ret);
From: Paul Gofman pgofman@codeweavers.com
--- dlls/mf/tests/transform.c | 3 +- dlls/winegstreamer/h264_decoder.c | 63 +++++++++++++++++++++++++++++-- 2 files changed, 61 insertions(+), 5 deletions(-)
diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index 3ab596317f9..04fcdf1f98c 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -6867,8 +6867,7 @@ static void test_h264_with_dxgi_manager(void) status = 0; memset(output, 0, sizeof(output)); hr = IMFTransform_ProcessOutput(transform, 0, 1, output, &status); - todo_wine ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "got %#lx\n", hr); - if (hr != MF_E_TRANSFORM_NEED_MORE_INPUT) goto failed; + ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "got %#lx\n", hr);
hr = IMFTransform_GetAttributes(transform, &attribs); ok(hr == S_OK, "got %#lx\n", hr); diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index 3278440fc02..239f28b74e7 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -63,6 +63,7 @@ struct h264_decoder struct wg_sample_queue *wg_sample_queue;
IMFVideoSampleAllocatorEx *allocator; + BOOL allocator_initialized; };
static struct h264_decoder *impl_from_IMFTransform(IMFTransform *iface) @@ -204,6 +205,26 @@ static HRESULT fill_output_media_type(struct h264_decoder *decoder, IMFMediaType return S_OK; }
+static HRESULT init_allocator(struct h264_decoder *decoder) +{ + HRESULT hr; + + if (decoder->allocator_initialized) + return S_OK; + + if (FAILED(hr = IMFVideoSampleAllocatorEx_InitializeSampleAllocatorEx(decoder->allocator, 10, 10, + decoder->attributes, decoder->output_type))) + return hr; + decoder->allocator_initialized = TRUE; + return S_OK; +} + +static void uninit_allocator(struct h264_decoder *decoder) +{ + IMFVideoSampleAllocatorEx_UninitializeSampleAllocator(decoder->allocator); + decoder->allocator_initialized = FALSE; +} + static HRESULT WINAPI transform_QueryInterface(IMFTransform *iface, REFIID iid, void **out) { struct h264_decoder *decoder = impl_from_IMFTransform(iface); @@ -583,11 +604,22 @@ 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); + HRESULT hr;
TRACE("iface %p, message %#x, param %Ix.\n", iface, message, param);
if (message == MFT_MESSAGE_SET_D3D_MANAGER) - return IMFVideoSampleAllocatorEx_SetDirectXManager(decoder->allocator, (IUnknown *)param); + { + if (FAILED(hr = IMFVideoSampleAllocatorEx_SetDirectXManager(decoder->allocator, (IUnknown *)param))) + return hr; + + uninit_allocator(decoder); + if (param) + decoder->output_info.dwFlags |= MFT_OUTPUT_STREAM_PROVIDES_SAMPLES; + else + decoder->output_info.dwFlags &= ~MFT_OUTPUT_STREAM_PROVIDES_SAMPLES; + return S_OK; + }
FIXME("Ignoring message %#x.\n", message); return S_OK; @@ -611,6 +643,7 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, struct h264_decoder *decoder = impl_from_IMFTransform(iface); struct wg_format wg_format; UINT32 sample_size; + IMFSample *sample; UINT64 frame_rate; GUID subtype; HRESULT hr; @@ -624,7 +657,7 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, return MF_E_TRANSFORM_TYPE_NOT_SET;
*status = samples->dwStatus = 0; - if (!samples->pSample) + if (!(sample = samples->pSample) && !(decoder->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES)) return E_INVALIDARG;
if (FAILED(hr = IMFMediaType_GetGUID(decoder->output_type, &MF_MT_SUBTYPE, &subtype))) @@ -633,7 +666,21 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, decoder->wg_format.u.video.height, &sample_size))) return hr;
- if (SUCCEEDED(hr = wg_transform_read_mf(decoder->wg_transform, samples->pSample, + if (decoder->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) + { + if (FAILED(hr = init_allocator(decoder))) + { + ERR("Failed to initialize allocator, hr %#lx.\n", hr); + return hr; + } + if (FAILED(hr = IMFVideoSampleAllocatorEx_AllocateSample(decoder->allocator, &sample))) + { + ERR("Failed to allocate sample, hr %#lx.\n", hr); + return hr; + } + } + + if (SUCCEEDED(hr = wg_transform_read_mf(decoder->wg_transform, sample, sample_size, &wg_format, &samples->dwStatus))) wg_sample_queue_flush(decoder->wg_sample_queue, false);
@@ -652,6 +699,16 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags,
samples[0].dwStatus |= MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE; *status |= MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE; + + uninit_allocator(decoder); + } + + if (decoder->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) + { + if (hr == S_OK) + samples->pSample = sample; + else + IMFSample_Release(sample); }
return hr;
From: Paul Gofman pgofman@codeweavers.com
--- dlls/winegstreamer/Makefile.in | 2 +- dlls/winegstreamer/h264_decoder.c | 55 ++++++++++++++++++++++++------- 2 files changed, 45 insertions(+), 12 deletions(-)
diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 82b1c148d6b..ae6420bad84 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -2,7 +2,7 @@ MODULE = winegstreamer.dll UNIXLIB = winegstreamer.so IMPORTLIB = winegstreamer IMPORTS = strmbase ole32 oleaut32 msdmo -DELAYIMPORTS = mfplat +DELAYIMPORTS = mfplat mf UNIX_CFLAGS = $(GSTREAMER_CFLAGS) UNIX_LIBS = $(GSTREAMER_LIBS) $(PTHREAD_LIBS)
diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index 239f28b74e7..b307e051193 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -64,6 +64,8 @@ struct h264_decoder
IMFVideoSampleAllocatorEx *allocator; BOOL allocator_initialized; + IMFTransform *copier; + IMFMediaBuffer *temp_buffer; };
static struct h264_decoder *impl_from_IMFTransform(IMFTransform *iface) @@ -212,6 +214,9 @@ static HRESULT init_allocator(struct h264_decoder *decoder) if (decoder->allocator_initialized) return S_OK;
+ IMFTransform_SetInputType(decoder->copier, 0, decoder->output_type, 0); + IMFTransform_SetOutputType(decoder->copier, 0, decoder->output_type, 0); + if (FAILED(hr = IMFVideoSampleAllocatorEx_InitializeSampleAllocatorEx(decoder->allocator, 10, 10, decoder->attributes, decoder->output_type))) return hr; @@ -264,7 +269,10 @@ static ULONG WINAPI transform_Release(IMFTransform *iface)
if (!refcount) { + IMFTransform_Release(decoder->copier); IMFVideoSampleAllocatorEx_Release(decoder->allocator); + if (decoder->temp_buffer) + IMFMediaBuffer_Release(decoder->temp_buffer); if (decoder->wg_transform) wg_transform_destroy(decoder->wg_transform); if (decoder->input_type) @@ -637,6 +645,25 @@ 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) +{ + MFT_OUTPUT_DATA_BUFFER output[1]; + DWORD status; + HRESULT hr; + + if (FAILED(hr = init_allocator(decoder))) + { + ERR("Failed to initialize allocator, hr %#lx.\n", hr); + return hr; + } + if (FAILED(hr = IMFVideoSampleAllocatorEx_AllocateSample(decoder->allocator, out))) + return hr; + + IMFTransform_ProcessInput(decoder->copier, 0, src_sample, 0); + output[0].pSample = *out; + return IMFTransform_ProcessOutput(decoder->copier, 0, 1, output, &status); +} + static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) { @@ -646,6 +673,7 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, IMFSample *sample; UINT64 frame_rate; GUID subtype; + DWORD size; HRESULT hr;
TRACE("iface %p, flags %#lx, count %lu, samples %p, status %p.\n", iface, flags, count, samples, status); @@ -668,16 +696,20 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags,
if (decoder->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) { - if (FAILED(hr = init_allocator(decoder))) + if (decoder->temp_buffer) { - ERR("Failed to initialize allocator, hr %#lx.\n", hr); - return hr; + if (FAILED(IMFMediaBuffer_GetMaxLength(decoder->temp_buffer, &size)) || size < sample_size) + { + IMFMediaBuffer_Release(decoder->temp_buffer); + decoder->temp_buffer = NULL; + } } - if (FAILED(hr = IMFVideoSampleAllocatorEx_AllocateSample(decoder->allocator, &sample))) - { - ERR("Failed to allocate sample, hr %#lx.\n", hr); + if (!decoder->temp_buffer && FAILED(hr = MFCreateMemoryBuffer(sample_size, &decoder->temp_buffer))) + return hr; + if (FAILED(hr = MFCreateSample(&sample))) + return hr; + if (FAILED(hr = IMFSample_AddBuffer(sample, decoder->temp_buffer))) return hr; - } }
if (SUCCEEDED(hr = wg_transform_read_mf(decoder->wg_transform, sample, @@ -705,10 +737,9 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags,
if (decoder->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) { - if (hr == S_OK) - samples->pSample = sample; - else - IMFSample_Release(sample); + if (hr == S_OK && FAILED(hr = output_sample(decoder, &samples->pSample, sample))) + ERR("Failed to output sample, hr %#lx.\n", hr); + IMFSample_Release(sample); }
return hr; @@ -800,6 +831,8 @@ HRESULT h264_decoder_create(REFIID riid, void **ret) goto failed; if (FAILED(hr = MFCreateVideoSampleAllocatorEx(&IID_IMFVideoSampleAllocatorEx, (void **)&decoder->allocator))) goto failed; + if (FAILED(hr = MFCreateSampleCopierMFT(&decoder->copier))) + goto failed;
*ret = &decoder->IMFTransform_iface; TRACE("Created decoder %p\n", *ret);
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=131538
Your paranoid android.
=== w11pro64_amd (64 bit report) ===
mf: 203c:transform: unhandled exception c0000005 at 00007FFEF7D3E0B4
v2: - put test first; - store just allocator without dxgi manager; - use sample copier to copy patches. That does only part of optimization which avoids extra copies back and forth when no sample is returned. When a sample is returned there is still an extra GPU->CPU copy and temporary linear buffer. I believe these parts can be optimized in mfplat/sample.c:sample_CopyToBuffer(); - removed "mfplat: Fix returned buffer length in dxgi_surface_buffer_lock()." patch: that is not directly related and I hope not mixing it in might simplify review.
Rémi Bernon (@rbernon) commented about dlls/winegstreamer/h264_decoder.c:
goto failed; if (FAILED(hr = wg_sample_queue_create(&decoder->wg_sample_queue))) goto failed;
if (FAILED(hr = MFCreateVideoSampleAllocatorEx(&IID_IMFVideoSampleAllocatorEx, (void **)&decoder->allocator)))
goto failed;
*ret = &decoder->IMFTransform_iface; TRACE("Created decoder %p\n", *ret); return S_OK;
failed:
Now you need to destroy the sample queue here.
Rémi Bernon (@rbernon) commented about dlls/winegstreamer/h264_decoder.c:
if (decoder->allocator_initialized) return S_OK;
- IMFTransform_SetInputType(decoder->copier, 0, decoder->output_type, 0);
- IMFTransform_SetOutputType(decoder->copier, 0, decoder->output_type, 0);
I know it's unlikely going to fail but please don't swallow errors.
Rémi Bernon (@rbernon) commented about dlls/winegstreamer/h264_decoder.c:
+static HRESULT output_sample(struct h264_decoder *decoder, IMFSample **out, IMFSample *src_sample) +{
- MFT_OUTPUT_DATA_BUFFER output[1];
- DWORD status;
- HRESULT hr;
- if (FAILED(hr = init_allocator(decoder)))
- {
ERR("Failed to initialize allocator, hr %#lx.\n", hr);
return hr;
- }
- if (FAILED(hr = IMFVideoSampleAllocatorEx_AllocateSample(decoder->allocator, out)))
return hr;
- IMFTransform_ProcessInput(decoder->copier, 0, src_sample, 0);
Same thing here.
Rémi Bernon (@rbernon) commented about dlls/winegstreamer/h264_decoder.c:
goto failed; if (FAILED(hr = MFCreateVideoSampleAllocatorEx(&IID_IMFVideoSampleAllocatorEx, (void **)&decoder->allocator))) goto failed;
if (FAILED(hr = MFCreateSampleCopierMFT(&decoder->copier)))
goto failed;
*ret = &decoder->IMFTransform_iface; TRACE("Created decoder %p\n", *ret); return S_OK;
failed:
Now you need to release the allocator on failure.
(Something went wrong when submitting the batch of comments, I can see only a single e-mail with the last two comments I added the second time, that were missing)