/* * Copyright 2017 Nikolay Sivov * Copyright 2022 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 */ #include #include #define COBJMACROS #include "windef.h" #include "winbase.h" #include "control.h" #include "d3d9types.h" #include "dmo.h" #include "mferror.h" #include "mfidl.h" #include "mftransform.h" #include "propvarutil.h" #include "uuids.h" #include "wmcodecdsp.h" #include "mediaerr.h" #include "amvideo.h" #include "vfw.h" #include "ks.h" #include "ksmedia.h" #include "mf_test.h" #include "wine/test.h" #include "initguid.h" #include "codecapi.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); DEFINE_GUID(DMOVideoFormat_RGB565,D3DFMT_R5G6B5,0x524f,0x11ce,0x9f,0x53,0x00,0x20,0xaf,0x0b,0xa7,0x70); DEFINE_GUID(DMOVideoFormat_RGB8,D3DFMT_P8,0x524f,0x11ce,0x9f,0x53,0x00,0x20,0xaf,0x0b,0xa7,0x70); DEFINE_GUID(MFAudioFormat_RAW_AAC1,WAVE_FORMAT_RAW_AAC1,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71); DEFINE_GUID(MFVideoFormat_ABGR32,0x00000020,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71); DEFINE_GUID(MFVideoFormat_P208,0x38303250,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71); DEFINE_GUID(MFVideoFormat_VC1S,0x53314356,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71); DEFINE_GUID(MFVideoFormat_WMV_Unknown,0x7ce12ca9,0xbfbf,0x43d9,0x9d,0x00,0x82,0xb8,0xed,0x54,0x31,0x6b); DEFINE_MEDIATYPE_GUID(MEDIASUBTYPE_IV50,MAKEFOURCC('I','V','5','0')); DEFINE_GUID(mft_output_sample_incomplete,0xffffff,0xffff,0xffff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff); struct media_buffer { IMediaBuffer IMediaBuffer_iface; LONG refcount; DWORD length; DWORD max_length; BYTE data[]; }; static inline struct media_buffer *impl_from_IMediaBuffer(IMediaBuffer *iface) { return CONTAINING_RECORD(iface, struct media_buffer, IMediaBuffer_iface); } static HRESULT WINAPI media_buffer_QueryInterface(IMediaBuffer *iface, REFIID iid, void **obj) { if (IsEqualIID(iid, &IID_IMediaBuffer) || IsEqualIID(iid, &IID_IUnknown)) { *obj = iface; return S_OK; } return E_NOINTERFACE; } static ULONG WINAPI media_buffer_AddRef(IMediaBuffer *iface) { struct media_buffer *buffer = impl_from_IMediaBuffer(iface); return InterlockedIncrement(&buffer->refcount); } static ULONG WINAPI media_buffer_Release(IMediaBuffer *iface) { struct media_buffer *buffer = impl_from_IMediaBuffer(iface); ULONG ref = InterlockedDecrement(&buffer->refcount); if (!ref) free(buffer); return ref; } static HRESULT WINAPI media_buffer_SetLength(IMediaBuffer *iface, DWORD length) { struct media_buffer *buffer = impl_from_IMediaBuffer(iface); if (length > buffer->max_length) return E_INVALIDARG; buffer->length = length; return S_OK; } static HRESULT WINAPI media_buffer_GetMaxLength(IMediaBuffer *iface, DWORD *max_length) { struct media_buffer *buffer = impl_from_IMediaBuffer(iface); if (!max_length) return E_POINTER; *max_length = buffer->max_length; return S_OK; } static HRESULT WINAPI media_buffer_GetBufferAndLength(IMediaBuffer *iface, BYTE **data, DWORD *length) { struct media_buffer *buffer = impl_from_IMediaBuffer(iface); if (!data || ! length) return E_POINTER; *data = buffer->data; *length = buffer->length; return S_OK; } static IMediaBufferVtbl media_buffer_vtbl = { media_buffer_QueryInterface, media_buffer_AddRef, media_buffer_Release, media_buffer_SetLength, media_buffer_GetMaxLength, media_buffer_GetBufferAndLength, }; HRESULT media_buffer_create(DWORD max_length, struct media_buffer **ret) { struct media_buffer *buffer; if (!(buffer = calloc(1, offsetof(struct media_buffer, data[max_length])))) return E_OUTOFMEMORY; buffer->IMediaBuffer_iface.lpVtbl = &media_buffer_vtbl; buffer->refcount = 1; buffer->length = 0; buffer->max_length = max_length; *ret = buffer; return S_OK; } static BOOL is_compressed_subtype(const GUID *subtype) { if (IsEqualGUID(subtype, &MEDIASUBTYPE_WMV1) || IsEqualGUID(subtype, &MEDIASUBTYPE_WMV2) || IsEqualGUID(subtype, &MEDIASUBTYPE_WMVA) || IsEqualGUID(subtype, &MEDIASUBTYPE_WMVP) || IsEqualGUID(subtype, &MEDIASUBTYPE_WVP2) || IsEqualGUID(subtype, &MFVideoFormat_WMV_Unknown) || IsEqualGUID(subtype, &MEDIASUBTYPE_WVC1) || IsEqualGUID(subtype, &MEDIASUBTYPE_WMV3) || IsEqualGUID(subtype, &MFVideoFormat_VC1S)) return TRUE; return FALSE; } static DWORD subtype_to_tag(const GUID *subtype) { if (IsEqualGUID(subtype, &MEDIASUBTYPE_RGB32) || IsEqualGUID(subtype, &MEDIASUBTYPE_RGB24) || IsEqualGUID(subtype, &MEDIASUBTYPE_RGB555) || IsEqualGUID(subtype, &MEDIASUBTYPE_RGB8)) return BI_RGB; else if (IsEqualGUID(subtype, &MEDIASUBTYPE_RGB565)) return BI_BITFIELDS; else if (IsEqualGUID(subtype, &MEDIASUBTYPE_PCM)) return WAVE_FORMAT_PCM; else return subtype->Data1; } static DWORD subtype_to_bpp(const GUID *subtype) { if (IsEqualGUID(subtype, &MEDIASUBTYPE_RGB8)) return 8; else if (IsEqualGUID(subtype, &MEDIASUBTYPE_NV12) || IsEqualGUID(subtype, &MEDIASUBTYPE_YV12) || IsEqualGUID(subtype, &MEDIASUBTYPE_IYUV) || IsEqualGUID(subtype, &MEDIASUBTYPE_I420) || IsEqualGUID(subtype, &MEDIASUBTYPE_NV11)) return 12; else if (IsEqualGUID(subtype, &MEDIASUBTYPE_YUY2) || IsEqualGUID(subtype, &MEDIASUBTYPE_UYVY) || IsEqualGUID(subtype, &MEDIASUBTYPE_YVYU) || IsEqualGUID(subtype, &MEDIASUBTYPE_RGB565) || IsEqualGUID(subtype, &MEDIASUBTYPE_RGB555)) return 16; else if (IsEqualGUID(subtype, &MEDIASUBTYPE_RGB24)) return 24; else if (IsEqualGUID(subtype, &MEDIASUBTYPE_RGB32)) return 32; else return 0; } static DWORD subtype_to_extra_bytes(const GUID *subtype) { if (IsEqualGUID(subtype, &MEDIASUBTYPE_MSAUDIO1)) return MSAUDIO1_WFX_EXTRA_BYTES; else if (IsEqualGUID(subtype, &MEDIASUBTYPE_WMAUDIO2)) return WMAUDIO2_WFX_EXTRA_BYTES; else if (IsEqualGUID(subtype, &MEDIASUBTYPE_WMAUDIO3)) return WMAUDIO3_WFX_EXTRA_BYTES; else if (IsEqualGUID(subtype, &MEDIASUBTYPE_RGB8)) return sizeof(RGBQUAD) * (1 << 8); else if (IsEqualGUID(subtype, &MEDIASUBTYPE_RGB565)) return sizeof(DWORD) * 3; else if (is_compressed_subtype(subtype)) return 4; else return 0; } static void load_resource(const WCHAR *filename, const BYTE **data, DWORD *length) { HRSRC resource = FindResourceW(NULL, filename, (const WCHAR *)RT_RCDATA); ok(resource != 0, "FindResourceW failed, error %lu\n", GetLastError()); *data = LockResource(LoadResource(GetModuleHandleW(NULL), resource)); *length = SizeofResource(GetModuleHandleW(NULL), resource); } #define EXPECT_REF(obj,ref) _expect_ref((IUnknown*)obj, ref, __LINE__) static void _expect_ref(IUnknown* obj, ULONG expected_refcount, int line) { ULONG refcount; IUnknown_AddRef(obj); refcount = IUnknown_Release(obj); ok_(__FILE__, line)(refcount == expected_refcount, "Unexpected refcount %ld, expected %ld.\n", refcount, expected_refcount); } #define check_interface(a, b, c) check_interface_(__LINE__, a, b, c) static void check_interface_(unsigned int line, void *iface_ptr, REFIID iid, BOOL supported) { IUnknown *iface = iface_ptr; HRESULT hr, expected_hr; IUnknown *unk; expected_hr = supported ? S_OK : E_NOINTERFACE; hr = IUnknown_QueryInterface(iface, iid, (void **)&unk); ok_(__FILE__, line)(hr == expected_hr, "Got hr %#lx, expected %#lx.\n", hr, expected_hr); if (SUCCEEDED(hr)) IUnknown_Release(unk); } #define check_member_(file, line, val, exp, fmt, member) \ ok_ (file, line)((val).member == (exp).member, "Got " #member " " fmt ", expected " fmt ".\n", (val).member, (exp).member) #define check_member(val, exp, fmt, member) check_member_(__FILE__, __LINE__, val, exp, fmt, member) 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); } } struct transform_info { const WCHAR *name; const GUID *major_type; struct { const GUID *subtype; BOOL broken; } inputs[32], input_end, outputs[32], output_end; }; static BOOL check_mft_enum(GUID category, MFT_REGISTER_TYPE_INFO *input_type, MFT_REGISTER_TYPE_INFO *output_type, const GUID *expect_class_id) { GUID *class_ids = NULL; UINT32 count = 0, i; HRESULT hr; hr = MFTEnum(category, 0, input_type, output_type, NULL, &class_ids, &count); if (FAILED(hr) || count == 0) { todo_wine win_skip("MFTEnum returned %#lx, count %u, skipping tests.\n", hr, count); return FALSE; } ok(hr == S_OK, "MFTEnum returned %#lx\n", hr); for (i = 0; i < count; ++i) if (IsEqualGUID(expect_class_id, class_ids + i)) break; ok(i < count, "Failed to find transform.\n"); CoTaskMemFree(class_ids); return i < count; } static void check_mft_get_info(const GUID *class_id, const struct transform_info *expect) { MFT_REGISTER_TYPE_INFO *input_types = NULL, *output_types = NULL; UINT32 input_count = 0, output_count = 0, i; WCHAR *name; HRESULT hr; hr = MFTGetInfo(*class_id, &name, &input_types, &input_count, &output_types, &output_count, NULL); ok(hr == S_OK, "MFTEnum returned %#lx\n", hr); ok(!wcscmp(name, expect->name), "got name %s\n", debugstr_w(name)); for (i = 0; i < input_count && expect->inputs[i].subtype; ++i) { ok(IsEqualGUID(&input_types[i].guidMajorType, expect->major_type), "got input[%u] major %s\n", i, debugstr_guid(&input_types[i].guidMajorType)); ok(IsEqualGUID(&input_types[i].guidSubtype, expect->inputs[i].subtype), "got input[%u] subtype %s\n", i, debugstr_guid(&input_types[i].guidSubtype)); } for (; expect->inputs[i].subtype; ++i) ok(broken(expect->inputs[i].broken), "missing input[%u] subtype %s\n", i, debugstr_guid(expect->inputs[i].subtype)); for (; i < input_count; ++i) ok(0, "extra input[%u] subtype %s\n", i, debugstr_guid(&input_types[i].guidSubtype)); for (i = 0; expect->outputs[i].subtype; ++i) { ok(IsEqualGUID(&output_types[i].guidMajorType, expect->major_type), "got output[%u] major %s\n", i, debugstr_guid(&output_types[i].guidMajorType)); ok(IsEqualGUID(&output_types[i].guidSubtype, expect->outputs[i].subtype), "got output[%u] subtype %s\n", i, debugstr_guid(&output_types[i].guidSubtype)); } for (; expect->outputs[i].subtype; ++i) ok(0, "missing output[%u] subtype %s\n", i, debugstr_guid(expect->outputs[i].subtype)); for (; i < output_count; ++i) ok(0, "extra output[%u] subtype %s\n", i, debugstr_guid(&output_types[i].guidSubtype)); CoTaskMemFree(output_types); CoTaskMemFree(input_types); CoTaskMemFree(name); } static void check_dmo_get_info(const GUID *class_id, const struct transform_info *expect) { DWORD input_count = 0, output_count = 0; DMO_PARTIAL_MEDIATYPE output[32] = {{{0}}}; DMO_PARTIAL_MEDIATYPE input[32] = {{{0}}}; WCHAR name[80]; HRESULT hr; int i; hr = DMOGetName(class_id, name); ok(hr == S_OK, "DMOGetName returned %#lx\n", hr); ok(!wcscmp(name, expect->name), "got name %s\n", debugstr_w(name)); hr = DMOGetTypes(class_id, ARRAY_SIZE(input), &input_count, input, ARRAY_SIZE(output), &output_count, output); ok(hr == S_OK, "DMOGetTypes returned %#lx\n", hr); for (i = 0; i < input_count && expect->inputs[i].subtype; ++i) { ok(IsEqualGUID(&input[i].type, expect->major_type), "got input[%u] major %s\n", i, debugstr_guid(&input[i].type)); ok(IsEqualGUID(&input[i].subtype, expect->inputs[i].subtype), "got input[%u] subtype %s\n", i, debugstr_guid(&input[i].subtype)); } for (; expect->inputs[i].subtype; ++i) ok(0, "missing input[%u] subtype %s\n", i, debugstr_guid(expect->inputs[i].subtype)); for (; i < input_count; ++i) ok(0, "extra input[%u] subtype %s\n", i, debugstr_guid(&input[i].subtype)); for (i = 0; expect->outputs[i].subtype; ++i) { ok(IsEqualGUID(&output[i].type, expect->major_type), "got output[%u] major %s\n", i, debugstr_guid(&output[i].type)); ok(IsEqualGUID(&output[i].subtype, expect->outputs[i].subtype), "got output[%u] subtype %s\n", i, debugstr_guid(&output[i].subtype)); } for (; expect->outputs[i].subtype; ++i) ok(0, "missing output[%u] subtype %s\n", i, debugstr_guid(expect->outputs[i].subtype)); for (; i < output_count; ++i) ok(0, "extra output[%u] subtype %s\n", i, debugstr_guid(&output[i].subtype)); } void init_media_type(IMFMediaType *mediatype, const struct attribute_desc *desc, ULONG limit) { HRESULT hr; ULONG i; hr = IMFMediaType_DeleteAllItems(mediatype); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); for (i = 0; i < limit && desc[i].key; ++i) { hr = IMFMediaType_SetItem(mediatype, desc[i].key, &desc[i].value); ok(hr == S_OK, "SetItem %s returned %#lx\n", debugstr_a(desc[i].name), hr); } } static void init_dmo_media_type_video(DMO_MEDIA_TYPE *media_type, const GUID *subtype, const LONG width, const LONG height) { UINT32 image_size = 0, extra_bytes = subtype_to_extra_bytes(subtype); VIDEOINFOHEADER *header = (VIDEOINFOHEADER *)(media_type + 1); BOOL compressed = is_compressed_subtype(subtype); MFCalculateImageSize(subtype, width, height, &image_size); memset(media_type, 0, sizeof(*media_type) + sizeof(*header) + extra_bytes); header->rcSource.top = 0; header->rcSource.left = 0; header->rcSource.right = width; header->rcSource.bottom = height; header->rcTarget.top = 0; header->rcTarget.left = 0; header->rcTarget.right = width; header->rcTarget.bottom = height; header->bmiHeader.biSize = sizeof(header->bmiHeader); header->bmiHeader.biWidth = width; header->bmiHeader.biHeight = height; header->bmiHeader.biPlanes = 1; header->bmiHeader.biBitCount = subtype_to_bpp(subtype); header->bmiHeader.biCompression = subtype_to_tag(subtype); header->bmiHeader.biSizeImage = image_size; if (IsEqualGUID(subtype, &MEDIASUBTYPE_RGB8)) { header->bmiHeader.biClrUsed = 226; header->bmiHeader.biClrImportant = header->bmiHeader.biClrUsed; } media_type->majortype = MEDIATYPE_Video; media_type->subtype = *subtype; media_type->bFixedSizeSamples = !compressed; media_type->bTemporalCompression = compressed; media_type->lSampleSize = image_size; media_type->formattype = FORMAT_VideoInfo; media_type->pUnk = NULL; media_type->cbFormat = sizeof(*header) + extra_bytes; media_type->pbFormat = (BYTE *)header; } static void init_dmo_media_type_audio(DMO_MEDIA_TYPE *media_type, const GUID *subtype, UINT channel_count, UINT rate, UINT bits_per_sample) { WAVEFORMATEX *format = (WAVEFORMATEX *)(media_type + 1); DWORD extra_bytes = subtype_to_extra_bytes(subtype); memset(media_type, 0, sizeof(*media_type) + sizeof(*format) + extra_bytes); media_type->majortype = MEDIATYPE_Audio; media_type->subtype = *subtype; media_type->bFixedSizeSamples = TRUE; media_type->bTemporalCompression = FALSE; media_type->lSampleSize = 0; media_type->formattype = FORMAT_WaveFormatEx; media_type->pUnk = NULL; media_type->cbFormat = sizeof(*format) + extra_bytes; media_type->pbFormat = (BYTE *)format; format->wFormatTag = subtype_to_tag(subtype); format->nChannels = channel_count; format->nSamplesPerSec = rate; format->wBitsPerSample = bits_per_sample; format->nBlockAlign = channel_count * bits_per_sample / 8; format->nAvgBytesPerSec = format->nBlockAlign * rate; format->cbSize = extra_bytes; } static void check_mft_optional_methods(IMFTransform *transform, DWORD output_count) { DWORD in_id, out_id, in_count, out_count, in_min, in_max, out_min, out_max; PROPVARIANT propvar = {.vt = VT_EMPTY}; IMFMediaEvent *event; HRESULT hr; in_min = in_max = out_min = out_max = 0xdeadbeef; hr = IMFTransform_GetStreamLimits(transform, &in_min, &in_max, &out_min, &out_max); ok(hr == S_OK, "GetStreamLimits returned %#lx\n", hr); ok(in_min == 1, "got input_min %lu\n", in_min); ok(in_max == 1, "got input_max %lu\n", in_max); ok(out_min == output_count, "got output_min %lu\n", out_min); ok(out_max == output_count, "got output_max %lu\n", out_max); in_count = out_count = 0xdeadbeef; hr = IMFTransform_GetStreamCount(transform, &in_count, &out_count); ok(hr == S_OK, "GetStreamCount returned %#lx\n", hr); ok(in_count == 1, "got input_count %lu\n", in_count); ok(out_count == output_count, "got output_count %lu\n", out_count); in_count = out_count = 1; in_id = out_id = 0xdeadbeef; hr = IMFTransform_GetStreamIDs(transform, in_count, &in_id, out_count, &out_id); ok(hr == E_NOTIMPL, "GetStreamIDs returned %#lx\n", hr); hr = IMFTransform_DeleteInputStream(transform, 0); ok(hr == E_NOTIMPL, "DeleteInputStream returned %#lx\n", hr); hr = IMFTransform_DeleteInputStream(transform, 1); ok(hr == E_NOTIMPL, "DeleteInputStream returned %#lx\n", hr); hr = IMFTransform_AddInputStreams(transform, 0, NULL); ok(hr == E_NOTIMPL, "AddInputStreams returned %#lx\n", hr); in_id = 0xdeadbeef; hr = IMFTransform_AddInputStreams(transform, 1, &in_id); ok(hr == E_NOTIMPL, "AddInputStreams returned %#lx\n", hr); hr = IMFTransform_SetOutputBounds(transform, 0, 0); ok(hr == E_NOTIMPL || hr == S_OK, "SetOutputBounds returned %#lx\n", hr); hr = MFCreateMediaEvent(MEEndOfStream, &GUID_NULL, S_OK, &propvar, &event); ok(hr == S_OK, "MFCreateMediaEvent returned %#lx\n", hr); hr = IMFTransform_ProcessEvent(transform, 0, NULL); ok(hr == E_NOTIMPL || hr == E_POINTER || hr == E_INVALIDARG, "ProcessEvent returned %#lx\n", hr); hr = IMFTransform_ProcessEvent(transform, 1, event); ok(hr == E_NOTIMPL, "ProcessEvent returned %#lx\n", hr); hr = IMFTransform_ProcessEvent(transform, 0, event); ok(hr == E_NOTIMPL, "ProcessEvent returned %#lx\n", hr); IMFMediaEvent_Release(event); } static void check_mft_get_attributes(IMFTransform *transform, const struct attribute_desc *expect_transform_attributes, BOOL expect_output_attributes) { IMFAttributes *attributes, *tmp_attributes; UINT32 count; HRESULT hr; ULONG ref; hr = IMFTransform_GetAttributes(transform, &attributes); todo_wine_if(expect_transform_attributes && hr == E_NOTIMPL) ok(hr == (expect_transform_attributes ? S_OK : E_NOTIMPL), "GetAttributes returned %#lx\n", hr); if (hr == S_OK) { ok(hr == S_OK, "GetAttributes returned %#lx\n", hr); check_attributes(attributes, expect_transform_attributes, -1); hr = IMFTransform_GetAttributes(transform, &tmp_attributes); ok(hr == S_OK, "GetAttributes returned %#lx\n", hr); ok(attributes == tmp_attributes, "got attributes %p\n", tmp_attributes); IMFAttributes_Release(tmp_attributes); ref = IMFAttributes_Release(attributes); ok(ref == 1, "Release returned %lu\n", ref); } hr = IMFTransform_GetOutputStreamAttributes(transform, 0, &attributes); todo_wine_if(expect_output_attributes && hr == E_NOTIMPL) ok(hr == (expect_output_attributes ? S_OK : E_NOTIMPL) || broken(hr == MF_E_UNSUPPORTED_REPRESENTATION) /* Win7 */, "GetOutputStreamAttributes returned %#lx\n", hr); if (hr == S_OK) { ok(hr == S_OK, "GetOutputStreamAttributes returned %#lx\n", hr); count = 0xdeadbeef; hr = IMFAttributes_GetCount(attributes, &count); ok(hr == S_OK, "GetCount returned %#lx\n", hr); ok(!count, "got %u attributes\n", count); hr = IMFTransform_GetOutputStreamAttributes(transform, 0, &tmp_attributes); ok(hr == S_OK, "GetAttributes returned %#lx\n", hr); ok(attributes == tmp_attributes, "got attributes %p\n", tmp_attributes); IMFAttributes_Release(tmp_attributes); ref = IMFAttributes_Release(attributes); ok(ref == 1, "Release returned %lu\n", ref); hr = IMFTransform_GetOutputStreamAttributes(transform, 0, NULL); ok(hr == E_NOTIMPL || hr == E_POINTER, "GetOutputStreamAttributes returned %#lx\n", hr); hr = IMFTransform_GetOutputStreamAttributes(transform, 1, &attributes); ok(hr == MF_E_INVALIDSTREAMNUMBER, "GetOutputStreamAttributes returned %#lx\n", hr); } hr = IMFTransform_GetInputStreamAttributes(transform, 0, &attributes); ok(hr == E_NOTIMPL || broken(hr == MF_E_UNSUPPORTED_REPRESENTATION) /* Win7 */, "GetInputStreamAttributes returned %#lx\n", hr); } #define check_mft_input_stream_info(a, b) check_mft_input_stream_info_(__LINE__, a, b) static void check_mft_input_stream_info_(int line, MFT_INPUT_STREAM_INFO *value, const MFT_INPUT_STREAM_INFO *expect) { check_member_(__FILE__, line, *value, *expect, "%I64d", hnsMaxLatency); check_member_(__FILE__, line, *value, *expect, "%#lx", dwFlags); check_member_(__FILE__, line, *value, *expect, "%#lx", cbSize); check_member_(__FILE__, line, *value, *expect, "%#lx", cbMaxLookahead); check_member_(__FILE__, line, *value, *expect, "%#lx", cbAlignment); } #define check_mft_get_input_stream_info(a, b, c) check_mft_get_input_stream_info_(__LINE__, a, b, c) static void check_mft_get_input_stream_info_(int line, IMFTransform *transform, HRESULT expect_hr, const MFT_INPUT_STREAM_INFO *expect) { MFT_INPUT_STREAM_INFO info, empty = {0}; HRESULT hr; memset(&info, 0xcd, sizeof(info)); hr = IMFTransform_GetInputStreamInfo(transform, 0, &info); ok_(__FILE__, line)(hr == expect_hr, "GetInputStreamInfo returned %#lx\n", hr); check_mft_input_stream_info_(line, &info, expect ? expect : &empty); } #define check_mft_output_stream_info(a, b) check_mft_output_stream_info_(__LINE__, a, b) static void check_mft_output_stream_info_(int line, MFT_OUTPUT_STREAM_INFO *value, const MFT_OUTPUT_STREAM_INFO *expect) { check_member_(__FILE__, line, *value, *expect, "%#lx", dwFlags); check_member_(__FILE__, line, *value, *expect, "%#lx", cbSize); check_member_(__FILE__, line, *value, *expect, "%#lx", cbAlignment); } #define check_mft_get_output_stream_info(a, b, c) check_mft_get_output_stream_info_(__LINE__, a, b, c) static void check_mft_get_output_stream_info_(int line, IMFTransform *transform, HRESULT expect_hr, const MFT_OUTPUT_STREAM_INFO *expect) { MFT_OUTPUT_STREAM_INFO info, empty = {0}; HRESULT hr; memset(&info, 0xcd, sizeof(info)); hr = IMFTransform_GetOutputStreamInfo(transform, 0, &info); ok_(__FILE__, line)(hr == expect_hr, "GetOutputStreamInfo returned %#lx\n", hr); check_mft_output_stream_info_(line, &info, expect ? expect : &empty); } #define check_mft_set_input_type_required(a, b) check_mft_set_input_type_required_(__LINE__, a, b) static void check_mft_set_input_type_required_(int line, IMFTransform *transform, const struct attribute_desc *attributes) { const struct attribute_desc *attr; IMFMediaType *media_type; HRESULT hr; ULONG ref; hr = MFCreateMediaType(&media_type); ok_(__FILE__, line)(hr == S_OK, "MFCreateMediaType returned hr %#lx.\n", hr); init_media_type(media_type, attributes, -1); for (attr = attributes; attr && attr->key; attr++) { winetest_push_context("%s", debugstr_a(attr->name)); hr = IMFMediaType_DeleteItem(media_type, attr->key); ok_(__FILE__, line)(hr == S_OK, "DeleteItem returned %#lx\n", hr); hr = IMFTransform_SetInputType(transform, 0, media_type, MFT_SET_TYPE_TEST_ONLY); ok_(__FILE__, line)(FAILED(hr) == attr->required, "SetInputType returned %#lx.\n", hr); hr = IMFMediaType_SetItem(media_type, attr->key, &attr->value); ok_(__FILE__, line)(hr == S_OK, "SetItem returned %#lx\n", hr); winetest_pop_context(); } hr = IMFTransform_SetInputType(transform, 0, media_type, MFT_SET_TYPE_TEST_ONLY); ok_(__FILE__, line)(hr == S_OK, "SetInputType returned %#lx.\n", hr); ref = IMFMediaType_Release(media_type); ok_(__FILE__, line)(!ref, "Release returned %lu\n", ref); } static void check_mft_set_input_type(IMFTransform *transform, const struct attribute_desc *attributes) { IMFMediaType *media_type; HRESULT hr; hr = MFCreateMediaType(&media_type); ok(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); hr = IMFTransform_SetInputType(transform, 0, media_type, 0); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); 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, 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_GetInputCurrentType(transform, 0, ¤t_type); todo_wine_if(todo_current) ok(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); 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); todo_wine_if(todo_compare) ok(result, "got result %u.\n", !!result); IMFMediaType_Release(media_type); IMFMediaType_Release(current_type); } #define check_mft_set_output_type_required(a, b) check_mft_set_output_type_required_(__LINE__, a, b) static void check_mft_set_output_type_required_(int line, IMFTransform *transform, const struct attribute_desc *attributes) { const struct attribute_desc *attr; IMFMediaType *media_type; HRESULT hr; ULONG ref; hr = MFCreateMediaType(&media_type); ok_(__FILE__, line)(hr == S_OK, "MFCreateMediaType returned hr %#lx.\n", hr); init_media_type(media_type, attributes, -1); for (attr = attributes; attr && attr->key; attr++) { winetest_push_context("%s", debugstr_a(attr->name)); hr = IMFMediaType_DeleteItem(media_type, attr->key); ok_(__FILE__, line)(hr == S_OK, "DeleteItem returned %#lx\n", hr); hr = IMFTransform_SetOutputType(transform, 0, media_type, MFT_SET_TYPE_TEST_ONLY); ok_(__FILE__, line)(FAILED(hr) == attr->required, "SetOutputType returned %#lx.\n", hr); hr = IMFMediaType_SetItem(media_type, attr->key, &attr->value); ok_(__FILE__, line)(hr == S_OK, "SetItem returned %#lx\n", hr); winetest_pop_context(); } hr = IMFTransform_SetOutputType(transform, 0, media_type, MFT_SET_TYPE_TEST_ONLY); ok_(__FILE__, line)(hr == S_OK, "SetOutputType returned %#lx.\n", hr); ref = IMFMediaType_Release(media_type); ok_(__FILE__, line)(!ref, "Release returned %lu\n", ref); } static void check_mft_set_output_type(IMFTransform *transform, const struct attribute_desc *attributes, HRESULT expect_hr) { IMFMediaType *media_type; HRESULT hr; hr = MFCreateMediaType(&media_type); ok(hr == S_OK, "MFCreateMediaType returned hr %#lx.\n", hr); init_media_type(media_type, attributes, -1); hr = IMFTransform_SetOutputType(transform, 0, media_type, MFT_SET_TYPE_TEST_ONLY); ok(hr == expect_hr, "SetOutputType returned %#lx.\n", hr); hr = IMFTransform_SetOutputType(transform, 0, media_type, 0); ok(hr == expect_hr, "SetOutputType returned %#lx.\n", hr); 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) { HRESULT hr, expect_hr = attributes ? S_OK : MF_E_TRANSFORM_TYPE_NOT_SET; IMFMediaType *media_type, *current_type; hr = IMFTransform_GetOutputCurrentType(transform, 0, ¤t_type); todo_wine_if(todo_current) ok(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); init_media_type(media_type, attributes, -1); check_attributes((IMFAttributes *)current_type, attributes, -1); IMFMediaType_Release(media_type); IMFMediaType_Release(current_type); } #define check_mft_process_output(a, b, c) check_mft_process_output_(__LINE__, a, b, c) static HRESULT check_mft_process_output_(int line, IMFTransform *transform, IMFSample *output_sample, DWORD *output_status) { static const DWORD expect_flags = MFT_OUTPUT_DATA_BUFFER_INCOMPLETE | MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE | MFT_OUTPUT_DATA_BUFFER_STREAM_END | MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE; MFT_OUTPUT_DATA_BUFFER output[3]; HRESULT hr, ret; DWORD status; status = 0; memset(&output, 0, sizeof(output)); output[0].pSample = output_sample; output[0].dwStreamID = 0; ret = IMFTransform_ProcessOutput(transform, 0, 1, output, &status); ok_(__FILE__, line)(output[0].dwStreamID == 0, "got dwStreamID %#lx\n", output[0].dwStreamID); ok_(__FILE__, line)(output[0].pEvents == NULL, "got pEvents %p\n", output[0].pEvents); ok_(__FILE__, line)(output[0].pSample == output_sample, "got pSample %p\n", output[0].pSample); ok_(__FILE__, line)((output[0].dwStatus & ~expect_flags) == 0 || broken((output[0].dwStatus & ~expect_flags) == 6) /* Win7 */ || broken((output[0].dwStatus & ~expect_flags) == 7) /* Win7 */, "got dwStatus %#lx\n", output[0].dwStatus); *output_status = output[0].dwStatus & expect_flags; if (!output_sample) ok_(__FILE__, line)(status == 0, "got status %#lx\n", status); else if (ret == MF_E_TRANSFORM_STREAM_CHANGE) ok_(__FILE__, line)(status == MFT_PROCESS_OUTPUT_STATUS_NEW_STREAMS, "got status %#lx\n", status); else { if (*output_status & MFT_OUTPUT_DATA_BUFFER_INCOMPLETE) { hr = IMFSample_SetUINT32(output_sample, &mft_output_sample_incomplete, 1); ok_(__FILE__, line)(hr == S_OK, "SetUINT32 returned %#lx\n", hr); } else { hr = IMFSample_DeleteItem(output_sample, &mft_output_sample_incomplete); ok_(__FILE__, line)(hr == S_OK, "DeleteItem returned %#lx\n", hr); } ok_(__FILE__, line)(status == 0, "got status %#lx\n", status); } return ret; } DWORD compare_nv12(const BYTE *data, DWORD *length, const RECT *rect, const BYTE *expect) { DWORD x, y, size, diff = 0, width = (rect->right + 0xf) & ~0xf, height = (rect->bottom + 0xf) & ~0xf; /* skip BMP header and RGB data from the dump */ size = *(DWORD *)(expect + 2); *length = *length + size; expect = expect + size; for (y = 0; y < height; y++, data += width, expect += width) { if (y < rect->top || y >= rect->bottom) continue; for (x = 0; x < width; x++) { if (x < rect->left || x >= rect->right) continue; diff += abs((int)expect[x] - (int)data[x]); } } for (y = 0; y < height; y += 2, data += width, expect += width) { if (y < rect->top || y >= rect->bottom) continue; for (x = 0; x < width; x += 2) { if (x < rect->left || x >= rect->right) continue; diff += abs((int)expect[x + 0] - (int)data[x + 0]); diff += abs((int)expect[x + 1] - (int)data[x + 1]); } } size = (rect->right - rect->left) * (rect->bottom - rect->top) * 3 / 2; return diff * 100 / 256 / size; } DWORD compare_i420(const BYTE *data, DWORD *length, const RECT *rect, const BYTE *expect) { DWORD i, x, y, size, diff = 0, width = (rect->right + 0xf) & ~0xf, height = (rect->bottom + 0xf) & ~0xf; /* skip BMP header and RGB data from the dump */ size = *(DWORD *)(expect + 2); *length = *length + size; expect = expect + size; for (y = 0; y < height; y++, data += width, expect += width) { if (y < rect->top || y >= rect->bottom) continue; for (x = 0; x < width; x++) { if (x < rect->left || x >= rect->right) continue; diff += abs((int)expect[x] - (int)data[x]); } } for (i = 0; i < 2; ++i) for (y = 0; y < height; y += 2, data += width / 2, expect += width / 2) { if (y < rect->top || y >= rect->bottom) continue; for (x = 0; x < width; x += 2) { if (x < rect->left || x >= rect->right) continue; diff += abs((int)expect[x / 2] - (int)data[x / 2]); } } size = (rect->right - rect->left) * (rect->bottom - rect->top) * 3 / 2; return diff * 100 / 256 / size; } static DWORD compare_rgb(const BYTE *data, DWORD *length, const RECT *rect, const BYTE *expect, UINT bits) { DWORD x, y, step = bits / 8, size, diff = 0, width = (rect->right + 0xf) & ~0xf, height = (rect->bottom + 0xf) & ~0xf; /* skip BMP header from the dump */ size = *(DWORD *)(expect + 2 + 2 * sizeof(DWORD)); *length = *length + size; expect = expect + size; for (y = 0; y < height; y++, data += width * step, expect += width * step) { if (y < rect->top || y >= rect->bottom) continue; for (x = 0; x < width; x++) { if (x < rect->left || x >= rect->right) continue; diff += abs((int)expect[step * x + 0] - (int)data[step * x + 0]); diff += abs((int)expect[step * x + 1] - (int)data[step * x + 1]); if (step >= 3) diff += abs((int)expect[step * x + 2] - (int)data[step * x + 2]); } } size = (rect->right - rect->left) * (rect->bottom - rect->top) * min(step, 3); return diff * 100 / 256 / size; } DWORD compare_rgb32(const BYTE *data, DWORD *length, const RECT *rect, const BYTE *expect) { return compare_rgb(data, length, rect, expect, 32); } DWORD compare_rgb24(const BYTE *data, DWORD *length, const RECT *rect, const BYTE *expect) { return compare_rgb(data, length, rect, expect, 24); } DWORD compare_rgb16(const BYTE *data, DWORD *length, const RECT *rect, const BYTE *expect) { return compare_rgb(data, length, rect, expect, 16); } DWORD compare_pcm16(const BYTE *data, DWORD *length, const RECT *rect, const BYTE *expect) { const INT16 *data_pcm = (INT16 *)data, *expect_pcm = (INT16 *)expect; DWORD i, size = *length / 2, diff = 0; for (i = 0; i < size; i++) diff += abs((int)*expect_pcm++ - (int)*data_pcm++); return diff * 100 / 65536 / size; } static DWORD compare_bytes(const BYTE *data, DWORD *length, const RECT *rect, const BYTE *expect) { DWORD i, size = *length, diff = 0; for (i = 0; i < size; i++) diff += abs((int)*expect++ - (int)*data++); return diff * 100 / 256 / size; } static void dump_rgb(const BYTE *data, DWORD length, const RECT *rect, HANDLE output, UINT bits) { DWORD width = (rect->right + 0xf) & ~0xf, height = (rect->bottom + 0xf) & ~0xf; static const char magic[2] = "BM"; struct { DWORD length; DWORD reserved; DWORD offset; BITMAPINFOHEADER biHeader; } header = { .length = length + sizeof(header) + 2, .offset = sizeof(header) + 2, .biHeader = { .biSize = sizeof(BITMAPINFOHEADER), .biWidth = width, .biHeight = height, .biPlanes = 1, .biBitCount = bits, .biCompression = BI_RGB, .biSizeImage = width * height * (bits / 8), }, }; DWORD written; BOOL ret; ret = WriteFile(output, magic, sizeof(magic), &written, NULL); ok(ret, "WriteFile failed, error %lu\n", GetLastError()); ok(written == sizeof(magic), "written %lu bytes\n", written); ret = WriteFile(output, &header, sizeof(header), &written, NULL); ok(ret, "WriteFile failed, error %lu\n", GetLastError()); ok(written == sizeof(header), "written %lu bytes\n", written); ret = WriteFile(output, data, length, &written, NULL); ok(ret, "WriteFile failed, error %lu\n", GetLastError()); ok(written == length, "written %lu bytes\n", written); } void dump_rgb32(const BYTE *data, DWORD length, const RECT *rect, HANDLE output) { return dump_rgb(data, length, rect, output, 32); } void dump_rgb24(const BYTE *data, DWORD length, const RECT *rect, HANDLE output) { return dump_rgb(data, length, rect, output, 24); } void dump_rgb16(const BYTE *data, DWORD length, const RECT *rect, HANDLE output) { return dump_rgb(data, length, rect, output, 16); } void dump_nv12(const BYTE *data, DWORD length, const RECT *rect, HANDLE output) { DWORD written, x, y, width = (rect->right + 0xf) & ~0xf, height = (rect->bottom + 0xf) & ~0xf; BYTE *rgb32_data = malloc(width * height * 4), *rgb32 = rgb32_data; BOOL ret; for (y = 0; y < height; y++) for (x = 0; x < width; x++) { *rgb32++ = data[width * y + x]; *rgb32++ = data[width * height + width * (y / 2) + (x & ~1) + 0]; *rgb32++ = data[width * height + width * (y / 2) + (x & ~1) + 1]; *rgb32++ = 0xff; } dump_rgb32(rgb32_data, width * height * 4, rect, output); free(rgb32_data); ret = WriteFile(output, data, length, &written, NULL); ok(ret, "WriteFile failed, error %lu\n", GetLastError()); ok(written == length, "written %lu bytes\n", written); } void dump_i420(const BYTE *data, DWORD length, const RECT *rect, HANDLE output) { DWORD written, x, y, width = (rect->right + 0xf) & ~0xf, height = (rect->bottom + 0xf) & ~0xf; BYTE *rgb32_data = malloc(width * height * 4), *rgb32 = rgb32_data; BOOL ret; for (y = 0; y < height; y++) for (x = 0; x < width; x++) { *rgb32++ = data[width * y + x]; *rgb32++ = data[width * height + (width / 2) * (y / 2) + x / 2]; *rgb32++ = data[width * height + (width / 2) * (y / 2) + (width / 2) * (height / 2) + x / 2]; *rgb32++ = 0xff; } dump_rgb32(rgb32_data, width * height * 4, rect, output); free(rgb32_data); ret = WriteFile(output, data, length, &written, NULL); ok(ret, "WriteFile failed, error %lu\n", GetLastError()); ok(written == length, "written %lu bytes\n", written); } typedef void (*enum_mf_media_buffers_cb)(IMFMediaBuffer *buffer, const struct buffer_desc *desc, void *context); static void enum_mf_media_buffers(IMFSample *sample, const struct sample_desc *sample_desc, enum_mf_media_buffers_cb callback, void *context) { IMFMediaBuffer *buffer; HRESULT hr; DWORD i; for (i = 0; SUCCEEDED(hr = IMFSample_GetBufferByIndex(sample, i, &buffer)); i++) { winetest_push_context("buffer %lu", i); ok(hr == S_OK, "GetBufferByIndex returned %#lx\n", hr); ok(i < sample_desc->buffer_count, "got unexpected buffer\n"); callback(buffer, sample_desc->buffers + i, context); IMFMediaBuffer_Release(buffer); winetest_pop_context(); } ok(hr == E_INVALIDARG, "GetBufferByIndex returned %#lx\n", hr); } struct enum_mf_sample_state { const struct sample_desc *next_sample; struct sample_desc sample; }; typedef void (*enum_mf_sample_cb)(IMFSample *sample, const struct sample_desc *sample_desc, void *context); static void enum_mf_samples(IMFCollection *samples, const struct sample_desc *collection_desc, enum_mf_sample_cb callback, void *context) { struct enum_mf_sample_state state = {.next_sample = collection_desc}; IMFSample *sample; HRESULT hr; DWORD i; for (i = 0; SUCCEEDED(hr = IMFCollection_GetElement(samples, i, (IUnknown **)&sample)); i++) { winetest_push_context("sample %lu", i); ok(hr == S_OK, "GetElement returned %#lx\n", hr); state.sample.sample_time += state.sample.sample_duration; if (!state.sample.repeat_count--) state.sample = *state.next_sample++; callback(sample, &state.sample, context); IMFSample_Release(sample); winetest_pop_context(); } ok(hr == E_INVALIDARG, "GetElement returned %#lx\n", hr); } static void dump_mf_media_buffer(IMFMediaBuffer *buffer, const struct buffer_desc *buffer_desc, HANDLE output) { DWORD length, written; HRESULT hr; BYTE *data; BOOL ret; hr = IMFMediaBuffer_Lock(buffer, &data, NULL, &length); ok(hr == S_OK, "Lock returned %#lx\n", hr); if (buffer_desc->dump) buffer_desc->dump(data, length, &buffer_desc->rect, output); else { if (buffer_desc->length == -1) { ret = WriteFile(output, &length, sizeof(length), &written, NULL); ok(ret, "WriteFile failed, error %lu\n", GetLastError()); ok(written == sizeof(length), "written %lu bytes\n", written); } ret = WriteFile(output, data, length, &written, NULL); ok(ret, "WriteFile failed, error %lu\n", GetLastError()); ok(written == length, "written %lu bytes\n", written); } hr = IMFMediaBuffer_Unlock(buffer); ok(hr == S_OK, "Unlock returned %#lx\n", hr); } static void dump_mf_sample(IMFSample *sample, const struct sample_desc *sample_desc, HANDLE output) { enum_mf_media_buffers(sample, sample_desc, dump_mf_media_buffer, output); } static void dump_mf_sample_collection(IMFCollection *samples, const struct sample_desc *collection_desc, const WCHAR *output_filename) { WCHAR path[MAX_PATH]; HANDLE output; GetTempPathW(ARRAY_SIZE(path), path); lstrcatW(path, output_filename); output = CreateFileW(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); ok(output != INVALID_HANDLE_VALUE, "CreateFileW failed, error %lu\n", GetLastError()); enum_mf_samples(samples, collection_desc, dump_mf_sample, output); trace("created %s\n", debugstr_w(path)); CloseHandle(output); } #define check_mf_media_buffer(a, b, c) check_mf_media_buffer_(__FILE__, __LINE__, a, b, c) static DWORD check_mf_media_buffer_(const char *file, int line, IMFMediaBuffer *buffer, const struct buffer_desc *expect, const BYTE **expect_data, DWORD *expect_data_len) { DWORD length, diff = 0, expect_length = expect->length; HRESULT hr; BYTE *data; if (expect_length == -1) { expect_length = *(DWORD *)*expect_data; *expect_data = *expect_data + sizeof(DWORD); *expect_data_len = *expect_data_len - sizeof(DWORD); } hr = IMFMediaBuffer_Lock(buffer, &data, NULL, &length); ok_(file, line)(hr == S_OK, "Lock returned %#lx\n", hr); todo_wine_if(expect->todo_length) ok_(file, line)(length == expect_length, "got length %#lx\n", length); if (*expect_data) { if (*expect_data_len < length) todo_wine_if(expect->todo_length) ok_(file, line)(0, "missing %#lx bytes\n", length - *expect_data_len); else if (!expect->compare) diff = compare_bytes(data, &length, NULL, *expect_data); else diff = expect->compare(data, &length, &expect->rect, *expect_data); } hr = IMFMediaBuffer_Unlock(buffer); ok_(file, line)(hr == S_OK, "Unlock returned %#lx\n", hr); *expect_data = *expect_data + min(length, *expect_data_len); *expect_data_len = *expect_data_len - min(length, *expect_data_len); return diff; } struct check_mf_sample_context { DWORD total_length; const BYTE *data; DWORD data_len; DWORD diff; const char *file; int line; }; static void check_mf_sample_buffer(IMFMediaBuffer *buffer, const struct buffer_desc *expect, void *context) { struct check_mf_sample_context *ctx = context; DWORD expect_length = expect->length == -1 ? *(DWORD *)ctx->data : expect->length; ctx->diff += check_mf_media_buffer_(ctx->file, ctx->line, buffer, expect, &ctx->data, &ctx->data_len); ctx->total_length += expect_length; } #define check_mf_sample(a, b, c, d) check_mf_sample_(__FILE__, __LINE__, a, b, c, d) static DWORD check_mf_sample_(const char *file, int line, IMFSample *sample, const struct sample_desc *expect, const BYTE **expect_data, DWORD *expect_data_len) { struct check_mf_sample_context ctx = {.data = *expect_data, .data_len = *expect_data_len, .file = file, .line = line}; DWORD buffer_count, total_length, sample_flags; LONGLONG timestamp; HRESULT hr; if (expect->attributes) check_attributes_(file, line, (IMFAttributes *)sample, expect->attributes, -1); buffer_count = 0xdeadbeef; hr = IMFSample_GetBufferCount(sample, &buffer_count); ok_(file, line)(hr == S_OK, "GetBufferCount returned %#lx\n", hr); ok_(file, line)(buffer_count == expect->buffer_count, "got %lu buffers\n", buffer_count); sample_flags = 0xdeadbeef; hr = IMFSample_GetSampleFlags(sample, &sample_flags); ok_(file, line)(hr == S_OK, "GetSampleFlags returned %#lx\n", hr); ok_(file, line)(sample_flags == 0, "got sample flags %#lx\n", sample_flags); timestamp = 0xdeadbeef; hr = IMFSample_GetSampleTime(sample, ×tamp); ok_(file, line)(hr == S_OK, "GetSampleTime returned %#lx\n", hr); todo_wine_if(expect->todo_time) ok_(file, line)(llabs(timestamp - expect->sample_time) <= 50, "got sample time %I64d\n", timestamp); timestamp = 0xdeadbeef; hr = IMFSample_GetSampleDuration(sample, ×tamp); ok_(file, line)(hr == S_OK, "GetSampleDuration returned %#lx\n", hr); todo_wine_if(expect->todo_duration && expect->todo_duration == timestamp) ok_(file, line)(llabs(timestamp - expect->sample_duration) <= 1, "got sample duration %I64d\n", timestamp); enum_mf_media_buffers(sample, expect, check_mf_sample_buffer, &ctx); total_length = 0xdeadbeef; hr = IMFSample_GetTotalLength(sample, &total_length); ok_(file, line)(hr == S_OK, "GetTotalLength returned %#lx\n", hr); todo_wine_if(expect->todo_length) ok_(file, line)(total_length == ctx.total_length, "got total length %#lx\n", total_length); ok_(file, line)(!*expect_data || *expect_data_len >= ctx.total_length, "missing %#lx data\n", ctx.total_length - *expect_data_len); *expect_data = ctx.data; *expect_data_len = ctx.data_len; return ctx.diff / buffer_count; } static void check_mf_sample_collection_enum(IMFSample *sample, const struct sample_desc *expect, void *context) { struct check_mf_sample_context *ctx = context; ctx->diff += check_mf_sample_(ctx->file, ctx->line, sample, expect, &ctx->data, &ctx->data_len); } DWORD check_mf_sample_collection_(const char *file, int line, IMFCollection *samples, const struct sample_desc *expect_samples, const WCHAR *expect_data_filename) { struct check_mf_sample_context ctx = {.file = file, .line = line}; DWORD count; HRESULT hr; if (expect_data_filename) load_resource(expect_data_filename, &ctx.data, &ctx.data_len); enum_mf_samples(samples, expect_samples, check_mf_sample_collection_enum, &ctx); if (expect_data_filename) dump_mf_sample_collection(samples, expect_samples, expect_data_filename); hr = IMFCollection_GetElementCount(samples, &count); ok_(file, line)(hr == S_OK, "GetElementCount returned %#lx\n", hr); return ctx.diff / count; } #define check_video_info_header(a, b) check_video_info_header_(__LINE__, a, b) static void check_video_info_header_(int line, VIDEOINFOHEADER *info, const VIDEOINFOHEADER *expected) { ok_(__FILE__, line)(info->rcSource.left == expected->rcSource.left && info->rcSource.top == expected->rcSource.top && info->rcSource.right == expected->rcSource.right && info->rcSource.bottom == expected->rcSource.bottom, "Got unexpected rcSource {%ld, %ld, %ld, %ld}, expected {%ld, %ld, %ld, %ld}.\n", info->rcSource.left, info->rcSource.top, info->rcSource.right, info->rcSource.bottom, expected->rcSource.left, expected->rcSource.top, expected->rcSource.right, expected->rcSource.bottom); ok_(__FILE__, line)(info->rcTarget.left == expected->rcTarget.left && info->rcTarget.top == expected->rcTarget.top && info->rcTarget.right == expected->rcTarget.right && info->rcTarget.bottom == expected->rcTarget.bottom, "Got unexpected rcTarget {%ld, %ld, %ld, %ld}, expected {%ld, %ld, %ld, %ld}.\n", info->rcTarget.left, info->rcTarget.top, info->rcTarget.right, info->rcTarget.bottom, expected->rcTarget.left, expected->rcTarget.top, expected->rcTarget.right, expected->rcTarget.bottom); check_member_(__FILE__, line, *info, *expected, "%lu", dwBitRate); check_member_(__FILE__, line, *info, *expected, "%lu", dwBitErrorRate); check_member_(__FILE__, line, *info, *expected, "%I64d", AvgTimePerFrame); check_member_(__FILE__, line, *info, *expected, "%lu", bmiHeader.biSize); check_member_(__FILE__, line, *info, *expected, "%ld", bmiHeader.biWidth); check_member_(__FILE__, line, *info, *expected, "%ld", bmiHeader.biHeight); check_member_(__FILE__, line, *info, *expected, "%u", bmiHeader.biPlanes); check_member_(__FILE__, line, *info, *expected, "%u", bmiHeader.biBitCount); check_member_(__FILE__, line, *info, *expected, "%lx", bmiHeader.biCompression); check_member_(__FILE__, line, *info, *expected, "%lu", bmiHeader.biSizeImage); check_member_(__FILE__, line, *info, *expected, "%ld", bmiHeader.biXPelsPerMeter); check_member_(__FILE__, line, *info, *expected, "%ld", bmiHeader.biYPelsPerMeter); todo_wine_if(expected->bmiHeader.biClrUsed != 0) check_member_(__FILE__, line, *info, *expected, "%lu", bmiHeader.biClrUsed); todo_wine_if(expected->bmiHeader.biClrImportant != 0) check_member_(__FILE__, line, *info, *expected, "%lu", bmiHeader.biClrImportant); } #define check_wave_format(a, b) check_wave_format_(__LINE__, a, b) static void check_wave_format_(int line, WAVEFORMATEX *info, const WAVEFORMATEX *expected) { check_member_(__FILE__, line, *info, *expected, "%#x", wFormatTag); check_member_(__FILE__, line, *info, *expected, "%u", nChannels); check_member_(__FILE__, line, *info, *expected, "%lu", nSamplesPerSec); check_member_(__FILE__, line, *info, *expected, "%lu", nAvgBytesPerSec); check_member_(__FILE__, line, *info, *expected, "%u", nBlockAlign); check_member_(__FILE__, line, *info, *expected, "%u", wBitsPerSample); check_member_(__FILE__, line, *info, *expected, "%u", cbSize); } #define check_dmo_media_type(a, b) check_dmo_media_type_(__LINE__, a, b) static void check_dmo_media_type_(int line, DMO_MEDIA_TYPE *media_type, const DMO_MEDIA_TYPE *expected) { ok_(__FILE__, line)(IsEqualGUID(&media_type->majortype, &expected->majortype), "Got unexpected majortype %s, expected %s.\n", debugstr_guid(&media_type->majortype), debugstr_guid(&expected->majortype)); ok_(__FILE__, line)(IsEqualGUID(&media_type->subtype, &expected->subtype), "Got unexpected subtype %s, expected %s.\n", debugstr_guid(&media_type->subtype), debugstr_guid(&expected->subtype)); if (IsEqualGUID(&expected->majortype, &MEDIATYPE_Video)) { check_member_(__FILE__, line, *media_type, *expected, "%d", bFixedSizeSamples); check_member_(__FILE__, line, *media_type, *expected, "%d", bTemporalCompression); check_member_(__FILE__, line, *media_type, *expected, "%lu", lSampleSize); } ok_(__FILE__, line)(IsEqualGUID(&media_type->formattype, &expected->formattype), "Got unexpected formattype %s.\n", debugstr_guid(&media_type->formattype)); ok_(__FILE__, line)(media_type->pUnk == NULL, "Got unexpected pUnk %p.\n", media_type->pUnk); todo_wine_if(expected->cbFormat && expected->cbFormat != sizeof(VIDEOINFOHEADER) && IsEqualGUID(&expected->majortype, &MEDIATYPE_Video)) check_member_(__FILE__, line, *media_type, *expected, "%lu", cbFormat); if (expected->pbFormat) { ok_(__FILE__, line)(!!media_type->pbFormat, "Got NULL pbFormat.\n"); if (!media_type->pbFormat) return; if (IsEqualGUID(&media_type->formattype, &FORMAT_VideoInfo) && IsEqualGUID(&expected->formattype, &FORMAT_VideoInfo)) check_video_info_header((VIDEOINFOHEADER *)media_type->pbFormat, (VIDEOINFOHEADER *)expected->pbFormat); else if (IsEqualGUID(&media_type->formattype, &FORMAT_WaveFormatEx) && IsEqualGUID(&expected->formattype, &FORMAT_WaveFormatEx)) check_wave_format((WAVEFORMATEX *)media_type->pbFormat, (WAVEFORMATEX *)expected->pbFormat); } } #define check_dmo_output_data_buffer(a, b, c, d, e) check_dmo_output_data_buffer_(__LINE__, a, b, c, d, e) static DWORD check_dmo_output_data_buffer_(int line, DMO_OUTPUT_DATA_BUFFER *output_data_buffer, const struct buffer_desc *buffer_desc, const WCHAR *expect_data_filename, REFERENCE_TIME time_stamp, REFERENCE_TIME time_length) { DWORD diff, data_length, buffer_length, expect_length; BYTE *data, *buffer; HRESULT hr; if (output_data_buffer->dwStatus & DMO_OUTPUT_DATA_BUFFERF_TIME) ok_(__FILE__, line)(output_data_buffer->rtTimestamp == time_stamp, "Unexpected time stamp %I64d, expected %I64d.\n", output_data_buffer->rtTimestamp, time_stamp); if (output_data_buffer->dwStatus & DMO_OUTPUT_DATA_BUFFERF_TIMELENGTH) todo_wine ok_(__FILE__, line)(output_data_buffer->rtTimelength == time_length, "Unexpected time length %I64d, expected %I64d.\n", output_data_buffer->rtTimelength, time_length); load_resource(expect_data_filename, (const BYTE **)&data, &data_length); expect_length = buffer_desc->length; if (expect_length == -1) { expect_length = *(DWORD *)data; data += sizeof(DWORD); data_length = data_length - sizeof(DWORD); } hr = IMediaBuffer_GetBufferAndLength(output_data_buffer->pBuffer, &buffer, &buffer_length); ok(hr == S_OK, "GetBufferAndLength returned %#lx.\n", hr); ok_(__FILE__, line)(buffer_length == expect_length, "Unexpected length %#lx, expected %#lx\n", buffer_length, expect_length); diff = 0; if (data_length < buffer_length) ok_(__FILE__, line)(0, "Missing %#lx bytes\n", buffer_length - data_length); else if (!buffer_desc->compare) diff = compare_bytes(buffer, &buffer_length, NULL, data); else diff = buffer_desc->compare(buffer, &buffer_length, &buffer_desc->rect, data); return diff; } #define check_dmo_get_output_size_info_video(a, b, c, d, e) check_dmo_get_output_size_info_video_(__LINE__, a, b, c, d, e) static void check_dmo_get_output_size_info_video_(int line, IMediaObject *dmo, const GUID *input_subtype, const GUID *output_subtype, const LONG width, const LONG height) { DWORD size, alignment, expected_size; DMO_MEDIA_TYPE *type; char buffer[2048]; HRESULT hr; type = (void *)buffer; hr = IMediaObject_SetInputType(dmo, 0, NULL, DMO_SET_TYPEF_CLEAR); ok_(__FILE__, line)(hr == S_OK, "Failed to clear input type, hr %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, NULL, DMO_SET_TYPEF_CLEAR); ok_(__FILE__, line)(hr == S_OK, "Failed to clear output type, hr %#lx.\n", hr); init_dmo_media_type_video(type, input_subtype, width, height); hr = IMediaObject_SetInputType(dmo, 0, type, 0); ok_(__FILE__, line)(hr == S_OK, "SetInputType returned %#lx.\n", hr); init_dmo_media_type_video(type, output_subtype, width, height); hr = IMediaObject_SetOutputType(dmo, 0, type, 0); todo_wine_if(IsEqualGUID(output_subtype, &MEDIASUBTYPE_NV11) || IsEqualGUID(output_subtype, &MEDIASUBTYPE_IYUV)) ok_(__FILE__, line)(hr == S_OK, "SetOutputType returned %#lx.\n", hr); if (hr != S_OK) return; size = 0xdeadbeef; alignment = 0xdeadbeef; hr = MFCalculateImageSize(output_subtype, width, height, (UINT32 *)&expected_size); ok_(__FILE__, line)(hr == S_OK, "MFCalculateImageSize returned %#lx.\n", hr); hr = IMediaObject_GetOutputSizeInfo(dmo, 0, &size, &alignment); ok_(__FILE__, line)(hr == S_OK, "GetOutputSizeInfo returned %#lx.\n", hr); ok_(__FILE__, line)(size == expected_size, "Unexpected size %lu, expected %lu.\n", size, expected_size); ok_(__FILE__, line)(alignment == 1, "Unexpected alignment %lu.\n", alignment); } static HRESULT WINAPI test_unk_QueryInterface(IUnknown *iface, REFIID riid, void **obj) { if (IsEqualIID(riid, &IID_IUnknown)) { *obj = iface; IUnknown_AddRef(iface); return S_OK; } *obj = NULL; return E_NOINTERFACE; } static ULONG WINAPI test_unk_AddRef(IUnknown *iface) { return 2; } static ULONG WINAPI test_unk_Release(IUnknown *iface) { return 1; } static const IUnknownVtbl test_unk_vtbl = { test_unk_QueryInterface, test_unk_AddRef, test_unk_Release, }; static BOOL is_supported_video_type(const GUID *guid) { return IsEqualGUID(guid, &MFVideoFormat_L8) || IsEqualGUID(guid, &MFVideoFormat_L16) || IsEqualGUID(guid, &MFVideoFormat_D16) || IsEqualGUID(guid, &MFVideoFormat_IYUV) || IsEqualGUID(guid, &MFVideoFormat_YV12) || IsEqualGUID(guid, &MFVideoFormat_NV12) || IsEqualGUID(guid, &MFVideoFormat_NV21) || IsEqualGUID(guid, &MFVideoFormat_420O) || IsEqualGUID(guid, &MFVideoFormat_P010) || IsEqualGUID(guid, &MFVideoFormat_P016) || IsEqualGUID(guid, &MFVideoFormat_UYVY) || IsEqualGUID(guid, &MFVideoFormat_YUY2) || IsEqualGUID(guid, &MFVideoFormat_P208) || IsEqualGUID(guid, &MFVideoFormat_NV11) || IsEqualGUID(guid, &MFVideoFormat_AYUV) || IsEqualGUID(guid, &MFVideoFormat_ARGB32) || IsEqualGUID(guid, &MFVideoFormat_RGB32) || IsEqualGUID(guid, &MFVideoFormat_A2R10G10B10) || IsEqualGUID(guid, &MFVideoFormat_A16B16G16R16F) || IsEqualGUID(guid, &MFVideoFormat_RGB24) || IsEqualGUID(guid, &MFVideoFormat_I420) || IsEqualGUID(guid, &MFVideoFormat_YVYU) || IsEqualGUID(guid, &MFVideoFormat_RGB555) || IsEqualGUID(guid, &MFVideoFormat_RGB565) || IsEqualGUID(guid, &MFVideoFormat_RGB8) || IsEqualGUID(guid, &MFVideoFormat_Y216) || IsEqualGUID(guid, &MFVideoFormat_v410) || IsEqualGUID(guid, &MFVideoFormat_Y41P) || IsEqualGUID(guid, &MFVideoFormat_Y41T) || IsEqualGUID(guid, &MFVideoFormat_Y42T) || IsEqualGUID(guid, &MFVideoFormat_ABGR32); } static BOOL is_sample_copier_available_type(IMFMediaType *type) { GUID major = { 0 }; UINT32 count; HRESULT hr; hr = IMFMediaType_GetMajorType(type, &major); ok(hr == S_OK, "Failed to get major type, hr %#lx.\n", hr); hr = IMFMediaType_GetCount(type, &count); ok(hr == S_OK, "Failed to get attribute count, hr %#lx.\n", hr); ok(count == 1, "Unexpected attribute count %u.\n", count); return IsEqualGUID(&major, &MFMediaType_Video) || IsEqualGUID(&major, &MFMediaType_Audio); } static void test_sample_copier(void) { static const struct attribute_desc expect_transform_attributes[] = { ATTR_UINT32(MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE, 1), {0}, }; const MFT_OUTPUT_STREAM_INFO initial_output_info = {0}, output_info = {.cbSize = 16 * 16}; const MFT_INPUT_STREAM_INFO initial_input_info = {0}, input_info = {.cbSize = 16 * 16}; IMFMediaType *mediatype, *mediatype2; IMFSample *sample, *client_sample; IMFMediaBuffer *media_buffer; MFT_INPUT_STREAM_INFO info; DWORD flags, output_status; IMFTransform *copier; HRESULT hr; LONG ref; if (!pMFCreateSampleCopierMFT) { win_skip("MFCreateSampleCopierMFT() is not available.\n"); return; } winetest_push_context("copier"); hr = pMFCreateSampleCopierMFT(&copier); ok(hr == S_OK, "Failed to create sample copier, hr %#lx.\n", hr); check_interface(copier, &IID_IMFTransform, TRUE); check_interface(copier, &IID_IMediaObject, FALSE); check_interface(copier, &IID_IPropertyStore, FALSE); check_interface(copier, &IID_IPropertyBag, FALSE); check_mft_optional_methods(copier, 1); check_mft_get_attributes(copier, expect_transform_attributes, FALSE); /* Available types. */ hr = IMFTransform_GetInputAvailableType(copier, 0, 0, &mediatype); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(is_sample_copier_available_type(mediatype), "Unexpected type.\n"); IMFMediaType_Release(mediatype); hr = IMFTransform_GetInputAvailableType(copier, 0, 1, &mediatype); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(is_sample_copier_available_type(mediatype), "Unexpected type.\n"); IMFMediaType_Release(mediatype); hr = IMFTransform_GetInputAvailableType(copier, 0, 2, &mediatype); ok(hr == MF_E_NO_MORE_TYPES, "Unexpected hr %#lx.\n", hr); hr = IMFTransform_GetInputAvailableType(copier, 1, 0, &mediatype); ok(hr == MF_E_INVALIDSTREAMNUMBER, "Unexpected hr %#lx.\n", hr); hr = IMFTransform_GetOutputAvailableType(copier, 0, 0, &mediatype); ok(hr == MF_E_NO_MORE_TYPES, "Unexpected hr %#lx.\n", hr); hr = IMFTransform_GetOutputAvailableType(copier, 1, 0, &mediatype); ok(hr == MF_E_INVALIDSTREAMNUMBER, "Unexpected hr %#lx.\n", hr); check_mft_get_input_current_type(copier, NULL); check_mft_get_output_current_type(copier, NULL); hr = MFCreateSample(&sample); ok(hr == S_OK, "Failed to create a sample, hr %#lx.\n", hr); hr = IMFTransform_ProcessInput(copier, 0, sample, 0); ok(hr == MF_E_TRANSFORM_TYPE_NOT_SET, "Unexpected hr %#lx.\n", hr); hr = MFCreateMediaType(&mediatype); ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); hr = IMFTransform_SetOutputType(copier, 0, mediatype, 0); ok(hr == MF_E_ATTRIBUTENOTFOUND, "Unexpected hr %#lx.\n", hr); hr = IMFMediaType_SetGUID(mediatype, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); hr = IMFMediaType_SetGUID(mediatype, &MF_MT_SUBTYPE, &MFVideoFormat_RGB8); ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); hr = IMFMediaType_SetUINT64(mediatype, &MF_MT_FRAME_SIZE, ((UINT64)16) << 32 | 16); ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); check_mft_get_input_stream_info(copier, S_OK, &initial_input_info); check_mft_get_output_stream_info(copier, S_OK, &initial_output_info); hr = IMFTransform_SetOutputType(copier, 0, mediatype, 0); ok(hr == S_OK, "Failed to set input type, hr %#lx.\n", hr); memset(&info, 0xcd, sizeof(info)); hr = IMFTransform_GetInputStreamInfo(copier, 0, &info); ok(hr == S_OK, "GetInputStreamInfo returned %#lx\n", hr); check_member(info, initial_input_info, "%I64d", hnsMaxLatency); check_member(info, initial_input_info, "%#lx", dwFlags); todo_wine check_member(info, initial_input_info, "%#lx", cbSize); check_member(info, initial_input_info, "%#lx", cbMaxLookahead); check_member(info, initial_input_info, "%#lx", cbAlignment); check_mft_get_output_stream_info(copier, S_OK, &output_info); hr = IMFTransform_GetOutputCurrentType(copier, 0, &mediatype2); ok(hr == S_OK, "Failed to get current type, hr %#lx.\n", hr); IMFMediaType_Release(mediatype2); check_mft_get_input_current_type(copier, NULL); hr = IMFTransform_GetInputStatus(copier, 0, &flags); ok(hr == MF_E_TRANSFORM_TYPE_NOT_SET, "Unexpected hr %#lx.\n", hr); /* Setting input type resets output type. */ hr = IMFTransform_GetOutputCurrentType(copier, 0, &mediatype2); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); IMFMediaType_Release(mediatype2); hr = IMFTransform_SetInputType(copier, 0, mediatype, 0); ok(hr == S_OK, "Failed to set input type, hr %#lx.\n", hr); hr = IMFTransform_GetOutputCurrentType(copier, 0, &mediatype2); ok(hr == MF_E_TRANSFORM_TYPE_NOT_SET, "Unexpected hr %#lx.\n", hr); hr = IMFTransform_GetInputAvailableType(copier, 0, 1, &mediatype2); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(is_sample_copier_available_type(mediatype2), "Unexpected type.\n"); IMFMediaType_Release(mediatype2); check_mft_get_input_stream_info(copier, S_OK, &input_info); check_mft_get_output_stream_info(copier, S_OK, &output_info); hr = IMFTransform_GetOutputAvailableType(copier, 0, 0, &mediatype2); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFMediaType_IsEqual(mediatype2, mediatype, &flags); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); IMFMediaType_Release(mediatype2); hr = IMFTransform_GetInputStatus(copier, 0, &flags); ok(hr == S_OK, "Failed to get input status, hr %#lx.\n", hr); ok(flags == MFT_INPUT_STATUS_ACCEPT_DATA, "Unexpected flags %#lx.\n", flags); hr = IMFTransform_GetInputCurrentType(copier, 0, &mediatype2); ok(hr == S_OK, "Failed to get current type, hr %#lx.\n", hr); IMFMediaType_Release(mediatype2); hr = IMFTransform_GetOutputStatus(copier, &flags); ok(hr == MF_E_TRANSFORM_TYPE_NOT_SET, "Unexpected hr %#lx.\n", hr); hr = IMFTransform_SetOutputType(copier, 0, mediatype, 0); ok(hr == S_OK, "Failed to set output type, hr %#lx.\n", hr); hr = IMFTransform_GetOutputStatus(copier, &flags); ok(hr == S_OK, "Failed to get output status, hr %#lx.\n", hr); ok(!flags, "Unexpected flags %#lx.\n", flags); /* Pushing samples. */ hr = MFCreateAlignedMemoryBuffer(output_info.cbSize, output_info.cbAlignment, &media_buffer); ok(hr == S_OK, "Failed to create media buffer, hr %#lx.\n", hr); hr = IMFSample_AddBuffer(sample, media_buffer); ok(hr == S_OK, "Failed to add a buffer, hr %#lx.\n", hr); IMFMediaBuffer_Release(media_buffer); EXPECT_REF(sample, 1); hr = IMFTransform_ProcessInput(copier, 0, sample, 0); ok(hr == S_OK, "Failed to process input, hr %#lx.\n", hr); EXPECT_REF(sample, 2); hr = IMFTransform_GetInputStatus(copier, 0, &flags); ok(hr == S_OK, "Failed to get input status, hr %#lx.\n", hr); ok(!flags, "Unexpected flags %#lx.\n", flags); hr = IMFTransform_GetOutputStatus(copier, &flags); ok(hr == S_OK, "Failed to get output status, hr %#lx.\n", hr); ok(flags == MFT_OUTPUT_STATUS_SAMPLE_READY, "Unexpected flags %#lx.\n", flags); hr = IMFTransform_ProcessInput(copier, 0, sample, 0); ok(hr == MF_E_NOTACCEPTING, "Unexpected hr %#lx.\n", hr); check_mft_get_input_stream_info(copier, S_OK, &input_info); check_mft_get_output_stream_info(copier, S_OK, &output_info); hr = MFCreateAlignedMemoryBuffer(output_info.cbSize, output_info.cbAlignment, &media_buffer); ok(hr == S_OK, "Failed to create media buffer, hr %#lx.\n", hr); hr = MFCreateSample(&client_sample); ok(hr == S_OK, "Failed to create a sample, hr %#lx.\n", hr); hr = IMFSample_AddBuffer(client_sample, media_buffer); ok(hr == S_OK, "Failed to add a buffer, hr %#lx.\n", hr); IMFMediaBuffer_Release(media_buffer); hr = check_mft_process_output(copier, client_sample, &output_status); ok(hr == S_OK, "Failed to get output, hr %#lx.\n", hr); ok(output_status == 0, "got output[0].dwStatus %#lx\n", output_status); EXPECT_REF(sample, 1); hr = check_mft_process_output(copier, client_sample, &output_status); ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "Failed to get output, hr %#lx.\n", hr); ok(output_status == 0, "got output[0].dwStatus %#lx\n", output_status); /* Flushing. */ hr = IMFTransform_ProcessInput(copier, 0, sample, 0); ok(hr == S_OK, "Failed to process input, hr %#lx.\n", hr); EXPECT_REF(sample, 2); hr = IMFTransform_ProcessMessage(copier, MFT_MESSAGE_COMMAND_FLUSH, 0); ok(hr == S_OK, "Failed to flush, hr %#lx.\n", hr); ref = IMFSample_Release(sample); ok(ref == 0, "Release returned %ld\n", ref); ref = IMFSample_Release(client_sample); ok(ref == 0, "Release returned %ld\n", ref); ref = IMFTransform_Release(copier); ok(ref == 0, "Release returned %ld\n", ref); ref = IMFMediaType_Release(mediatype); ok(ref == 0, "Release returned %ld\n", ref); winetest_pop_context(); } struct sample_metadata { unsigned int flags; LONGLONG duration; LONGLONG time; }; static void sample_copier_process(IMFTransform *copier, IMFMediaBuffer *input_buffer, IMFMediaBuffer *output_buffer, const struct sample_metadata *md) { static const struct sample_metadata zero_md = { 0, ~0u, ~0u }; IMFSample *input_sample, *output_sample; DWORD flags, output_status; LONGLONG time; HRESULT hr; LONG ref; hr = MFCreateSample(&input_sample); ok(hr == S_OK, "Failed to create a sample, hr %#lx.\n", hr); if (md) { hr = IMFSample_SetSampleFlags(input_sample, md->flags); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFSample_SetSampleTime(input_sample, md->time); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFSample_SetSampleDuration(input_sample, md->duration); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); } hr = MFCreateSample(&output_sample); ok(hr == S_OK, "Failed to create a sample, hr %#lx.\n", hr); hr = IMFSample_SetSampleFlags(output_sample, ~0u); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFSample_SetSampleTime(output_sample, ~0u); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFSample_SetSampleDuration(output_sample, ~0u); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFSample_AddBuffer(input_sample, input_buffer); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFSample_AddBuffer(output_sample, output_buffer); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFTransform_ProcessInput(copier, 0, input_sample, 0); ok(hr == S_OK, "Failed to process input, hr %#lx.\n", hr); hr = check_mft_process_output(copier, output_sample, &output_status); ok(hr == S_OK, "Failed to get output, hr %#lx.\n", hr); ok(output_status == 0, "got output[0].dwStatus %#lx\n", output_status); if (!md) md = &zero_md; hr = IMFSample_GetSampleFlags(output_sample, &flags); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(md->flags == flags, "Unexpected flags.\n"); hr = IMFSample_GetSampleTime(output_sample, &time); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(md->time == time, "Unexpected time.\n"); hr = IMFSample_GetSampleDuration(output_sample, &time); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(md->duration == time, "Unexpected duration.\n"); ref = IMFSample_Release(input_sample); ok(ref == 0, "Release returned %ld\n", ref); ref = IMFSample_Release(output_sample); ok(ref == 0, "Release returned %ld\n", ref); } static void test_sample_copier_output_processing(void) { IMFMediaBuffer *input_buffer, *output_buffer; MFT_OUTPUT_STREAM_INFO output_info; struct sample_metadata md; IMFMediaType *mediatype; IMFTransform *copier; DWORD max_length; HRESULT hr; BYTE *ptr; LONG ref; if (!pMFCreateSampleCopierMFT) return; hr = pMFCreateSampleCopierMFT(&copier); ok(hr == S_OK, "Failed to create sample copier, hr %#lx.\n", hr); /* Configure for 16 x 16 of D3DFMT_X8R8G8B8. */ hr = MFCreateMediaType(&mediatype); ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); hr = IMFMediaType_SetGUID(mediatype, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); hr = IMFMediaType_SetGUID(mediatype, &MF_MT_SUBTYPE, &MFVideoFormat_RGB32); ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); hr = IMFMediaType_SetUINT64(mediatype, &MF_MT_FRAME_SIZE, ((UINT64)16) << 32 | 16); ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); hr = IMFTransform_SetInputType(copier, 0, mediatype, 0); ok(hr == S_OK, "Failed to set input type, hr %#lx.\n", hr); hr = IMFTransform_SetOutputType(copier, 0, mediatype, 0); ok(hr == S_OK, "Failed to set input type, hr %#lx.\n", hr); /* Source and destination are linear buffers, destination is twice as large. */ hr = IMFTransform_GetOutputStreamInfo(copier, 0, &output_info); ok(hr == S_OK, "Failed to get output info, hr %#lx.\n", hr); hr = MFCreateAlignedMemoryBuffer(output_info.cbSize, output_info.cbAlignment, &output_buffer); ok(hr == S_OK, "Failed to create media buffer, hr %#lx.\n", hr); hr = IMFMediaBuffer_Lock(output_buffer, &ptr, &max_length, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); memset(ptr, 0xcc, max_length); hr = IMFMediaBuffer_Unlock(output_buffer); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = MFCreateAlignedMemoryBuffer(output_info.cbSize, output_info.cbAlignment, &input_buffer); ok(hr == S_OK, "Failed to create media buffer, hr %#lx.\n", hr); hr = IMFMediaBuffer_Lock(input_buffer, &ptr, &max_length, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); memset(ptr, 0xaa, max_length); hr = IMFMediaBuffer_Unlock(input_buffer); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFMediaBuffer_SetCurrentLength(input_buffer, 4); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); sample_copier_process(copier, input_buffer, output_buffer, NULL); hr = IMFMediaBuffer_Lock(output_buffer, &ptr, &max_length, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(ptr[0] == 0xaa && ptr[4] == 0xcc, "Unexpected buffer contents.\n"); hr = IMFMediaBuffer_Unlock(output_buffer); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); md.flags = 123; md.time = 10; md.duration = 2; sample_copier_process(copier, input_buffer, output_buffer, &md); ref = IMFMediaBuffer_Release(input_buffer); ok(ref == 0, "Release returned %ld\n", ref); ref = IMFMediaBuffer_Release(output_buffer); ok(ref == 0, "Release returned %ld\n", ref); ref = IMFTransform_Release(copier); ok(ref == 0, "Release returned %ld\n", ref); ref = IMFMediaType_Release(mediatype); ok(ref == 0, "Release returned %ld\n", ref); } static IMFSample *create_sample(const BYTE *data, ULONG size) { IMFMediaBuffer *media_buffer; IMFSample *sample; DWORD length; BYTE *buffer; HRESULT hr; ULONG ret; hr = MFCreateSample(&sample); ok(hr == S_OK, "MFCreateSample returned %#lx\n", hr); hr = MFCreateMemoryBuffer(size, &media_buffer); ok(hr == S_OK, "MFCreateMemoryBuffer returned %#lx\n", hr); hr = IMFMediaBuffer_Lock(media_buffer, &buffer, NULL, &length); ok(hr == S_OK, "Lock returned %#lx\n", hr); ok(length == 0, "got length %lu\n", length); if (!data) memset(buffer, 0xcd, size); else memcpy(buffer, data, size); hr = IMFMediaBuffer_Unlock(media_buffer); ok(hr == S_OK, "Unlock returned %#lx\n", hr); hr = IMFMediaBuffer_SetCurrentLength(media_buffer, data ? size : 0); ok(hr == S_OK, "SetCurrentLength returned %#lx\n", hr); hr = IMFSample_AddBuffer(sample, media_buffer); ok(hr == S_OK, "AddBuffer returned %#lx\n", hr); ret = IMFMediaBuffer_Release(media_buffer); ok(ret == 1, "Release returned %lu\n", ret); return sample; } static void test_aac_encoder(void) { const GUID *const class_id = &CLSID_AACMFTEncoder; const struct transform_info expect_mft_info = { .name = L"Microsoft AAC Audio Encoder MFT", .major_type = &MFMediaType_Audio, .inputs = { {.subtype = &MFAudioFormat_PCM}, }, .outputs = { {.subtype = &MFAudioFormat_AAC}, }, }; static const struct attribute_desc input_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 1, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100, .required = TRUE), {0}, }; const struct attribute_desc output_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_AAC, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 1, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 12000, .required = TRUE), {0}, }; static const struct attribute_desc expect_input_type_desc[] = { ATTR_GUID(MF_MT_AM_FORMAT_TYPE, FORMAT_WaveFormatEx), ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 1), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 2), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 44100 * 2), ATTR_UINT32(MF_MT_AVG_BITRATE, 44100 * 2 * 8), ATTR_UINT32(MF_MT_COMPRESSED, 0), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_UINT32(MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1), {0}, }; const struct attribute_desc expect_output_type_desc[] = { ATTR_GUID(MF_MT_AM_FORMAT_TYPE, FORMAT_WaveFormatEx), ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_AAC), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 1), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 1), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 12000), ATTR_UINT32(MF_MT_AVG_BITRATE, 96000), ATTR_UINT32(MF_MT_COMPRESSED, 1), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 0), ATTR_UINT32(MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1), ATTR_UINT32(MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, 41), ATTR_UINT32(MF_MT_AAC_PAYLOAD_TYPE, 0), ATTR_BLOB(MF_MT_USER_DATA, test_aac_codec_data, sizeof(test_aac_codec_data)), {0}, }; const MFT_OUTPUT_STREAM_INFO initial_output_info = {0}, output_info = {.cbSize = 0x600}; const MFT_INPUT_STREAM_INFO input_info = {0}; const struct buffer_desc output_buffer_desc[] = { {.length = -1 /* variable */}, }; const struct attribute_desc output_sample_attributes[] = { ATTR_UINT32(MFSampleExtension_CleanPoint, 1), {0}, }; const struct sample_desc output_sample_desc[] = { { .repeat_count = 88, .attributes = output_sample_attributes, .sample_time = 0, .sample_duration = 113823, .buffer_count = 1, .buffers = output_buffer_desc, }, }; MFT_REGISTER_TYPE_INFO output_type = {MFMediaType_Audio, MFAudioFormat_AAC}; MFT_REGISTER_TYPE_INFO input_type = {MFMediaType_Audio, MFAudioFormat_PCM}; IMFSample *input_sample, *output_sample; IMFCollection *output_samples; ULONG i, ret, audio_data_len; DWORD length, output_status; IMFTransform *transform; const BYTE *audio_data; HRESULT hr; LONG ref; hr = CoInitialize(NULL); ok(hr == S_OK, "Failed to initialize, hr %#lx.\n", hr); winetest_push_context("aacenc"); if (!check_mft_enum(MFT_CATEGORY_AUDIO_ENCODER, &input_type, &output_type, class_id)) goto failed; check_mft_get_info(class_id, &expect_mft_info); if (FAILED(hr = CoCreateInstance(class_id, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)&transform))) goto failed; check_interface(transform, &IID_IMFTransform, TRUE); check_interface(transform, &IID_IMediaObject, FALSE); check_interface(transform, &IID_IPropertyStore, FALSE); check_interface(transform, &IID_IPropertyBag, FALSE); check_mft_optional_methods(transform, 1); check_mft_get_attributes(transform, NULL, FALSE); check_mft_get_input_stream_info(transform, S_OK, &input_info); check_mft_get_output_stream_info(transform, S_OK, &initial_output_info); 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); 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); check_mft_get_input_stream_info(transform, S_OK, &input_info); check_mft_get_output_stream_info(transform, S_OK, &output_info); if (!has_video_processor) { win_skip("Skipping AAC encoder tests on Win7\n"); goto done; } load_resource(L"audiodata.bin", &audio_data, &audio_data_len); ok(audio_data_len == 179928, "got length %lu\n", audio_data_len); input_sample = create_sample(audio_data, audio_data_len); hr = IMFSample_SetSampleTime(input_sample, 0); ok(hr == S_OK, "SetSampleTime returned %#lx\n", hr); hr = IMFSample_SetSampleDuration(input_sample, 10000000); ok(hr == S_OK, "SetSampleDuration returned %#lx\n", hr); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == S_OK, "ProcessInput returned %#lx\n", hr); hr = IMFTransform_ProcessMessage(transform, MFT_MESSAGE_COMMAND_DRAIN, 0); ok(hr == S_OK, "ProcessMessage returned %#lx\n", hr); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == MF_E_NOTACCEPTING, "ProcessInput returned %#lx\n", hr); ref = IMFSample_Release(input_sample); ok(ref <= 1, "Release returned %ld\n", ref); hr = MFCreateCollection(&output_samples); ok(hr == S_OK, "MFCreateCollection returned %#lx\n", hr); output_sample = create_sample(NULL, output_info.cbSize); for (i = 0; SUCCEEDED(hr = check_mft_process_output(transform, output_sample, &output_status)); i++) { winetest_push_context("%lu", i); ok(hr == S_OK, "ProcessOutput returned %#lx\n", hr); hr = IMFCollection_AddElement(output_samples, (IUnknown *)output_sample); ok(hr == S_OK, "AddElement returned %#lx\n", hr); ref = IMFSample_Release(output_sample); ok(ref == 1, "Release returned %ld\n", ref); output_sample = create_sample(NULL, output_info.cbSize); winetest_pop_context(); } ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); ret = IMFSample_Release(output_sample); ok(ret == 0, "Release returned %lu\n", ret); ok(i == 88, "got %lu output samples\n", i); ret = check_mf_sample_collection(output_samples, output_sample_desc, L"aacencdata.bin"); ok(ret == 0, "got %lu%% diff\n", ret); IMFCollection_Release(output_samples); output_sample = create_sample(NULL, output_info.cbSize); hr = check_mft_process_output(transform, output_sample, &output_status); ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); hr = IMFSample_GetTotalLength(output_sample, &length); ok(hr == S_OK, "GetTotalLength returned %#lx\n", hr); ok(length == 0, "got length %lu\n", length); ret = IMFSample_Release(output_sample); ok(ret == 0, "Release returned %lu\n", ret); done: ret = IMFTransform_Release(transform); ok(ret == 0, "Release returned %lu\n", ret); failed: winetest_pop_context(); CoUninitialize(); } static void test_aac_decoder_subtype(const struct attribute_desc *input_type_desc) { const GUID *const class_id = &CLSID_MSAACDecMFT; const struct transform_info expect_mft_info = { .name = L"Microsoft AAC Audio Decoder MFT", .major_type = &MFMediaType_Audio, .inputs = { {.subtype = &MFAudioFormat_AAC}, {.subtype = &MFAudioFormat_RAW_AAC1}, {.subtype = &MFAudioFormat_ADTS, .broken = TRUE /* <= w8 */}, }, .outputs = { {.subtype = &MFAudioFormat_Float}, {.subtype = &MFAudioFormat_PCM}, }, }; static const struct attribute_desc expect_input_attributes[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_UINT32(MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 32), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 6), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 24), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 48000), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 1152000), {0}, }; static const media_type_desc expect_available_inputs[] = { { ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_AAC), ATTR_UINT32(MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, 0), ATTR_UINT32(MF_MT_AAC_PAYLOAD_TYPE, 0), /* MF_MT_USER_DATA with some AAC codec data */ }, { ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_RAW_AAC1), }, { ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_AAC), ATTR_UINT32(MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, 0), ATTR_UINT32(MF_MT_AAC_PAYLOAD_TYPE, 1), /* MF_MT_USER_DATA with some AAC codec data */ }, { ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_AAC), ATTR_UINT32(MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, 0), ATTR_UINT32(MF_MT_AAC_PAYLOAD_TYPE, 3), /* MF_MT_USER_DATA with some AAC codec data */ }, { ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_ADTS), }, }; static const struct attribute_desc expect_output_attributes[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_UINT32(MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 1), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), {0}, }; static const media_type_desc expect_available_outputs[] = { { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_Float), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 32), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 4), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 4 * 44100), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 2), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 2 * 44100), }, }; const struct attribute_desc expect_transform_attributes[] = { ATTR_UINT32(MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE, !has_video_processor /* 1 on W7 */, .todo = TRUE), /* more AAC decoder specific attributes from CODECAPI */ {0}, }; static const struct attribute_desc output_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 1, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100, .required = TRUE), {0}, }; const MFT_OUTPUT_STREAM_INFO output_info = { .dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES, .cbSize = 0xc000, }; const MFT_INPUT_STREAM_INFO input_info = { .dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_INPUT_STREAM_FIXED_SAMPLE_SIZE | MFT_INPUT_STREAM_HOLDS_BUFFERS, }; const struct buffer_desc output_buffer_desc[] = { {.length = 0x800}, }; const struct attribute_desc output_sample_attributes[] = { ATTR_UINT32(MFSampleExtension_CleanPoint, 1), {0}, }; const struct sample_desc output_sample_desc[] = { { .attributes = output_sample_attributes + (has_video_processor ? 0 : 1) /* MFSampleExtension_CleanPoint missing on Win7 */, .sample_time = 0, .sample_duration = 232200, .buffer_count = 1, .buffers = output_buffer_desc, }, }; MFT_REGISTER_TYPE_INFO output_type = {MFMediaType_Audio, MFAudioFormat_Float}; MFT_REGISTER_TYPE_INFO input_type = {MFMediaType_Audio, MFAudioFormat_AAC}; IMFSample *input_sample, *output_sample; ULONG i, ret, ref, aacenc_data_len; IMFCollection *output_samples; DWORD length, output_status; IMFMediaType *media_type; IMFTransform *transform; const BYTE *aacenc_data; DWORD flags; HRESULT hr; hr = CoInitialize(NULL); ok(hr == S_OK, "Failed to initialize, hr %#lx.\n", hr); winetest_push_context("aacdec"); if (!check_mft_enum(MFT_CATEGORY_AUDIO_DECODER, &input_type, &output_type, class_id)) goto failed; check_mft_get_info(class_id, &expect_mft_info); if (FAILED(hr = CoCreateInstance(class_id, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)&transform))) goto failed; check_interface(transform, &IID_IMFTransform, TRUE); check_interface(transform, &IID_IMediaObject, FALSE); check_interface(transform, &IID_IPropertyStore, FALSE); check_interface(transform, &IID_IPropertyBag, FALSE); check_mft_optional_methods(transform, 1); check_mft_get_attributes(transform, expect_transform_attributes, FALSE); check_mft_get_input_stream_info(transform, S_OK, &input_info); check_mft_get_output_stream_info(transform, S_OK, &output_info); hr = IMFTransform_GetOutputAvailableType(transform, 0, 0, &media_type); ok(hr == MF_E_TRANSFORM_TYPE_NOT_SET, "GetOutputAvailableType returned %#lx\n", hr); i = -1; while (SUCCEEDED(hr = IMFTransform_GetInputAvailableType(transform, 0, ++i, &media_type))) { winetest_push_context("in %lu", i); ok(hr == S_OK, "GetInputAvailableType returned %#lx\n", hr); check_media_type(media_type, expect_input_attributes, -1); check_media_type(media_type, expect_available_inputs[i], -1); ret = IMFMediaType_Release(media_type); ok(ret <= 1, "Release returned %lu\n", ret); winetest_pop_context(); } ok(hr == MF_E_NO_MORE_TYPES, "GetInputAvailableType returned %#lx\n", hr); ok(i == ARRAY_SIZE(expect_available_inputs) || broken(i == 2) /* w7 */ || broken(i == 4) /* w8 */, "%lu input media types\n", i); /* 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); 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, input_type_desc); /* check new output media types */ i = -1; while (SUCCEEDED(hr = IMFTransform_GetOutputAvailableType(transform, 0, ++i, &media_type))) { winetest_push_context("out %lu", i); ok(hr == S_OK, "GetOutputAvailableType returned %#lx\n", hr); check_media_type(media_type, expect_output_attributes, -1); check_media_type(media_type, expect_available_outputs[i], -1); ret = IMFMediaType_Release(media_type); ok(ret <= 1, "Release returned %lu\n", ret); winetest_pop_context(); } ok(hr == MF_E_NO_MORE_TYPES, "GetOutputAvailableType returned %#lx\n", hr); ok(i == ARRAY_SIZE(expect_available_outputs), "%lu input media types\n", i); 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, output_type_desc); check_mft_get_input_stream_info(transform, S_OK, &input_info); check_mft_get_output_stream_info(transform, S_OK, &output_info); load_resource(L"aacencdata.bin", &aacenc_data, &aacenc_data_len); ok(aacenc_data_len == 24861, "got length %lu\n", aacenc_data_len); input_sample = create_sample(aacenc_data + sizeof(DWORD), *(DWORD *)aacenc_data); flags = 0; hr = IMFTransform_GetInputStatus(transform, 0, &flags); ok(hr == S_OK, "Got %#lx\n", hr); ok(flags == MFT_INPUT_STATUS_ACCEPT_DATA, "Got flags %#lx.\n", flags); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == S_OK, "ProcessInput returned %#lx\n", hr); flags = 0xdeadbeef; hr = IMFTransform_GetInputStatus(transform, 0, &flags); ok(hr == S_OK, "Got %#lx\n", hr); ok(!flags, "Got flags %#lx.\n", flags); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == MF_E_NOTACCEPTING, "ProcessInput returned %#lx\n", hr); flags = 0xdeadbeef; hr = IMFTransform_GetInputStatus(transform, 0, &flags); ok(hr == S_OK, "Got %#lx\n", hr); ok(!flags, "Got flags %#lx.\n", flags); /* As output_info.dwFlags doesn't have MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES * IMFTransform_ProcessOutput needs a sample or returns MF_E_TRANSFORM_NEED_MORE_INPUT */ hr = check_mft_process_output(transform, NULL, &output_status); ok(hr == E_INVALIDARG, "ProcessOutput returned %#lx\n", hr); ok(output_status == 0, "got output[0].dwStatus %#lx\n", output_status); flags = 0xdeadbeef; hr = IMFTransform_GetInputStatus(transform, 0, &flags); ok(hr == S_OK, "Got %#lx\n", hr); ok(!flags, "Got flags %#lx.\n", flags); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == MF_E_NOTACCEPTING, "ProcessInput returned %#lx\n", hr); hr = IMFTransform_ProcessMessage(transform, MFT_MESSAGE_COMMAND_DRAIN, 0); ok(hr == S_OK, "ProcessMessage returned %#lx\n", hr); flags = 0xdeadbeef; hr = IMFTransform_GetInputStatus(transform, 0, &flags); ok(hr == S_OK, "Got %#lx\n", hr); ok(!flags, "Got flags %#lx.\n", flags); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == MF_E_NOTACCEPTING, "ProcessInput returned %#lx\n", hr); hr = MFCreateCollection(&output_samples); ok(hr == S_OK, "MFCreateCollection returned %#lx\n", hr); output_sample = create_sample(NULL, output_info.cbSize); for (i = 0; SUCCEEDED(hr = check_mft_process_output(transform, output_sample, &output_status)); i++) { winetest_push_context("%lu", i); ok(hr == S_OK, "ProcessOutput returned %#lx\n", hr); hr = IMFCollection_AddElement(output_samples, (IUnknown *)output_sample); ok(hr == S_OK, "AddElement returned %#lx\n", hr); ref = IMFSample_Release(output_sample); ok(ref == 1, "Release returned %ld\n", ref); output_sample = create_sample(NULL, output_info.cbSize); winetest_pop_context(); } ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); flags = 0; hr = IMFTransform_GetInputStatus(transform, 0, &flags); ok(hr == S_OK, "Got %#lx\n", hr); ok(flags == MFT_INPUT_STATUS_ACCEPT_DATA, "Got flags %#lx.\n", flags); ok(output_status == MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE, "got output[0].dwStatus %#lx\n", output_status); ret = IMFSample_Release(output_sample); ok(ret == 0, "Release returned %lu\n", ret); ok(i == 1, "got %lu output samples\n", i); ret = check_mf_sample_collection(output_samples, output_sample_desc, L"aacdecdata.bin"); todo_wine_if(ret <= 5) ok(ret == 0, "got %lu%% diff\n", ret); IMFCollection_Release(output_samples); output_sample = create_sample(NULL, output_info.cbSize); hr = check_mft_process_output(transform, output_sample, &output_status); ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); ok(output_status == MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE, "got output[0].dwStatus %#lx\n", output_status); hr = IMFSample_GetTotalLength(output_sample, &length); ok(hr == S_OK, "GetTotalLength returned %#lx\n", hr); ok(length == 0, "got length %lu\n", length); ret = IMFSample_Release(output_sample); ok(ret == 0, "Release returned %lu\n", ret); ret = IMFSample_Release(input_sample); ok(ret == 0, "Release returned %lu\n", ret); ret = IMFTransform_Release(transform); ok(ret == 0, "Release returned %lu\n", ret); failed: winetest_pop_context(); CoUninitialize(); } static void test_aac_decoder_channels(const struct attribute_desc *input_type_desc) { static const struct attribute_desc expect_output_attributes[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), {0}, }; static const media_type_desc expect_available_outputs[] = { { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_Float), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 32), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16), }, }; static const UINT32 expected_mask[7] = { 0, 0, 0, SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_CENTER, KSAUDIO_SPEAKER_QUAD, KSAUDIO_SPEAKER_QUAD | SPEAKER_FRONT_CENTER, KSAUDIO_SPEAKER_5POINT1, }; UINT32 value, num_channels, expected_chans, format_index, sample_size; unsigned int num_channels_index = ~0u; struct attribute_desc input_desc[64]; IMFTransform *transform; IMFAttributes *attrs; IMFMediaType *type; BOOL many_channels; ULONG i, ret; HRESULT hr; for (i = 0; i < ARRAY_SIZE(input_desc); i++) { input_desc[i] = input_type_desc[i]; if (!input_desc[i].key) break; if (IsEqualGUID(input_desc[i].key, &MF_MT_AUDIO_NUM_CHANNELS)) num_channels_index = i; } ok(num_channels_index != ~0u, "Could not find MF_MT_AUDIO_NUM_CHANNELS.\n"); ok(i < ARRAY_SIZE(input_desc), "Too many attributes.\n"); hr = CoInitialize(NULL); ok(hr == S_OK, "got %#lx.\n", hr); winetest_push_context("aacdec channels"); if (FAILED(hr = CoCreateInstance(&CLSID_MSAACDecMFT, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)&transform))) { win_skip("AAC decoder transform is not available.\n"); goto failed; } hr = MFCreateMediaType(&type); ok(hr == S_OK, "got %#lx.\n", hr); input_desc[num_channels_index].value.vt = VT_UI8; input_desc[num_channels_index].value.ulVal = 1; init_media_type(type, input_desc, -1); 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 = IMFAttributes_GetUINT32((IMFAttributes *)type, &MF_MT_AUDIO_NUM_CHANNELS, &value); ok(hr == S_OK, "got %#lx.\n", hr); ok(value == 2, "got %u.\n", value); IMFMediaType_Release(type); input_desc[num_channels_index].value.vt = VT_UI4; for (num_channels = 0; num_channels < 16; ++num_channels) { many_channels = num_channels > 2; winetest_push_context("chans %u", num_channels); input_desc[num_channels_index].value.ulVal = num_channels; hr = MFCreateMediaType(&type); ok(hr == S_OK, "got %#lx.\n", hr); init_media_type(type, input_desc, -1); hr = IMFTransform_SetInputType(transform, 0, type, 0); IMFMediaType_Release(type); if (num_channels <= 6) ok(hr == S_OK, "got %#lx.\n", hr); else { ok(hr == MF_E_INVALIDMEDIATYPE, "got %#lx.\n", hr); winetest_pop_context(); continue; } i = -1; while (SUCCEEDED(hr = IMFTransform_GetOutputAvailableType(transform, 0, ++i, &type))) { winetest_push_context("out %lu", i); ok(hr == S_OK, "got %#lx.\n", hr); check_media_type(type, expect_output_attributes, -1); format_index = i % 2; sample_size = format_index ? 2 : 4; check_media_type(type, expect_available_outputs[format_index], -1); attrs = (IMFAttributes *)type; hr = IMFAttributes_GetUINT32(attrs, &MF_MT_AUDIO_NUM_CHANNELS, &value); ok(hr == S_OK, "got %#lx.\n", hr); if (!num_channels || i >= ARRAY_SIZE(expect_available_outputs)) expected_chans = 2; else expected_chans = num_channels; ok(value == expected_chans, "got %u, expected %u.\n", value, expected_chans); hr = IMFAttributes_GetUINT32(attrs, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &value); ok(hr == S_OK, "got %#lx.\n", hr); ok(value == sample_size * 44100 * expected_chans, "got %u, expected %u.\n", value, sample_size * 44100 * expected_chans); hr = IMFAttributes_GetUINT32(attrs, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &value); ok(hr == S_OK, "got %#lx.\n", hr); ok(value == sample_size * expected_chans, "got %u, expected %u.\n", value, sample_size * expected_chans); hr = IMFAttributes_GetUINT32(attrs, &MF_MT_AUDIO_PREFER_WAVEFORMATEX, &value); if (many_channels && i < ARRAY_SIZE(expect_available_outputs)) { ok(hr == MF_E_ATTRIBUTENOTFOUND, "got %#lx.\n", hr); } else { ok(hr == S_OK, "got %#lx.\n", hr); ok(value == 1, "got %u.\n", value); } value = 0xdeadbeef; hr = IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_CHANNEL_MASK, &value); if (expected_chans <= 2) { ok(hr == MF_E_ATTRIBUTENOTFOUND, "got %#lx.\n", hr); } else { ok(hr == S_OK, "got %#lx.\n", hr); ok(value == expected_mask[expected_chans], "got %#x, expected %#x.\n", value, expected_mask[expected_chans]); } ret = IMFMediaType_Release(type); ok(ret <= 1, "got %lu.\n", ret); winetest_pop_context(); } ok(hr == MF_E_NO_MORE_TYPES, "got %#lx.\n", hr); if (many_channels) ok(i == ARRAY_SIZE(expect_available_outputs) * 2, "got %lu media types.\n", i); else ok(i == ARRAY_SIZE(expect_available_outputs), "got %lu media types.\n", i); winetest_pop_context(); } ret = IMFTransform_Release(transform); ok(!ret, "got %lu.\n", ret); failed: winetest_pop_context(); CoUninitialize(); } static void test_aac_decoder(void) { static const BYTE aac_raw_codec_data[] = {0x12, 0x08}; static const struct attribute_desc raw_aac_input_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_RAW_AAC1, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 1), ATTR_BLOB(MF_MT_USER_DATA, aac_raw_codec_data, sizeof(aac_raw_codec_data), .required = TRUE), {0}, }; static const struct attribute_desc aac_input_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_AAC, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100, .required = TRUE), ATTR_BLOB(MF_MT_USER_DATA, test_aac_codec_data, sizeof(test_aac_codec_data), .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 1), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 12000), ATTR_UINT32(MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, 41), ATTR_UINT32(MF_MT_AAC_PAYLOAD_TYPE, 0), {0}, }; test_aac_decoder_subtype(aac_input_type_desc); test_aac_decoder_subtype(raw_aac_input_type_desc); test_aac_decoder_channels(aac_input_type_desc); test_aac_decoder_channels(raw_aac_input_type_desc); } static const BYTE wma_codec_data[10] = {0, 0x44, 0, 0, 0x17, 0, 0, 0, 0, 0}; static const ULONG wmaenc_block_size = 1487; static const ULONG wmadec_block_size = 0x2000; static void test_wma_encoder(void) { const GUID *const class_id = &CLSID_CWMAEncMediaObject; const struct transform_info expect_mft_info = { .name = L"WMAudio Encoder MFT", .major_type = &MFMediaType_Audio, .inputs = { {.subtype = &MFAudioFormat_PCM}, {.subtype = &MFAudioFormat_Float}, }, .outputs = { {.subtype = &MFAudioFormat_WMAudioV8}, {.subtype = &MFAudioFormat_WMAudioV9}, {.subtype = &MFAudioFormat_WMAudio_Lossless}, }, }; const struct transform_info expect_dmo_info = { .name = L"WMAudio Encoder DMO", .major_type = &MEDIATYPE_Audio, .inputs = { {.subtype = &MEDIASUBTYPE_PCM}, }, .outputs = { {.subtype = &MEDIASUBTYPE_WMAUDIO2}, {.subtype = &MEDIASUBTYPE_WMAUDIO3}, {.subtype = &MEDIASUBTYPE_WMAUDIO_LOSSLESS}, }, }; static const struct attribute_desc input_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_Float, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 32, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 22050, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 2 * (32 / 8), .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 2 * (32 / 8) * 22050, .required = TRUE), {0}, }; const struct attribute_desc output_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_WMAudioV8, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 22050, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 4003, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, wmaenc_block_size, .required = TRUE), ATTR_BLOB(MF_MT_USER_DATA, wma_codec_data, sizeof(wma_codec_data), .required = TRUE), {0}, }; static const struct attribute_desc expect_input_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_Float), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 32), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 8), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 22050), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 22050 * 8), ATTR_UINT32(MF_MT_AUDIO_CHANNEL_MASK, 3), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), {0}, }; const struct attribute_desc expect_output_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_WMAudioV8), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 22050), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 4003), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, wmaenc_block_size), ATTR_BLOB(MF_MT_USER_DATA, wma_codec_data, sizeof(wma_codec_data)), ATTR_UINT32(MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1), {0}, }; const MFT_OUTPUT_STREAM_INFO output_info = { .cbSize = wmaenc_block_size, .cbAlignment = 1, }; const MFT_INPUT_STREAM_INFO input_info = { .hnsMaxLatency = 19969161, .cbSize = 8, .cbAlignment = 1, }; const struct buffer_desc output_buffer_desc[] = { {.length = wmaenc_block_size}, }; const struct attribute_desc output_sample_attributes[] = { ATTR_UINT32(mft_output_sample_incomplete, 1), ATTR_UINT32(MFSampleExtension_CleanPoint, 1), {0}, }; const struct sample_desc output_sample_desc[] = { { .attributes = output_sample_attributes, .sample_time = 0, .sample_duration = 3250794, .buffer_count = 1, .buffers = output_buffer_desc, }, { .attributes = output_sample_attributes, .sample_time = 3250794, .sample_duration = 3715193, .buffer_count = 1, .buffers = output_buffer_desc, }, { .attributes = output_sample_attributes, .sample_time = 6965986, .sample_duration = 3366893, .buffer_count = 1, .buffers = output_buffer_desc, }, }; MFT_REGISTER_TYPE_INFO output_type = {MFMediaType_Audio, MFAudioFormat_WMAudioV8}; MFT_REGISTER_TYPE_INFO input_type = {MFMediaType_Audio, MFAudioFormat_Float}; IMFSample *input_sample, *output_sample; IMFCollection *output_samples; DWORD length, output_status; IMFMediaType *media_type; IMFTransform *transform; const BYTE *audio_data; ULONG audio_data_len; ULONG i, ret; HRESULT hr; LONG ref; hr = CoInitialize(NULL); ok(hr == S_OK, "Failed to initialize, hr %#lx.\n", hr); winetest_push_context("wmaenc"); if (!check_mft_enum(MFT_CATEGORY_AUDIO_ENCODER, &input_type, &output_type, class_id)) goto failed; check_mft_get_info(class_id, &expect_mft_info); check_dmo_get_info(class_id, &expect_dmo_info); if (FAILED(hr = CoCreateInstance(class_id, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)&transform))) goto failed; check_interface(transform, &IID_IMFTransform, TRUE); check_interface(transform, &IID_IMediaObject, TRUE); check_interface(transform, &IID_IPropertyStore, TRUE); check_interface(transform, &IID_IPropertyBag, TRUE); check_mft_optional_methods(transform, 1); check_mft_get_attributes(transform, NULL, 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); check_mft_set_input_type_required(transform, input_type_desc); hr = MFCreateMediaType(&media_type); ok(hr == S_OK, "MFCreateMediaType returned %#lx\n", hr); init_media_type(media_type, input_type_desc, -1); hr = IMFTransform_SetInputType(transform, 0, media_type, 0); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); ret = IMFMediaType_Release(media_type); ok(ret == 0, "Release returned %lu\n", ret); 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); 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); check_mft_get_input_stream_info(transform, S_OK, &input_info); check_mft_get_output_stream_info(transform, S_OK, &output_info); load_resource(L"audiodata.bin", &audio_data, &audio_data_len); ok(audio_data_len == 179928, "got length %lu\n", audio_data_len); input_sample = create_sample(audio_data, audio_data_len); hr = IMFSample_SetSampleTime(input_sample, 0); ok(hr == S_OK, "SetSampleTime returned %#lx\n", hr); hr = IMFSample_SetSampleDuration(input_sample, 10000000); ok(hr == S_OK, "SetSampleDuration returned %#lx\n", hr); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == S_OK, "ProcessInput returned %#lx\n", hr); hr = IMFTransform_ProcessMessage(transform, MFT_MESSAGE_COMMAND_DRAIN, 0); ok(hr == S_OK, "ProcessMessage returned %#lx\n", hr); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == MF_E_NOTACCEPTING, "ProcessInput returned %#lx\n", hr); ref = IMFSample_Release(input_sample); ok(ref <= 1, "Release returned %ld\n", ref); hr = MFCreateCollection(&output_samples); ok(hr == S_OK, "MFCreateCollection returned %#lx\n", hr); output_sample = create_sample(NULL, output_info.cbSize); for (i = 0; SUCCEEDED(hr = check_mft_process_output(transform, output_sample, &output_status)); i++) { winetest_push_context("%lu", i); ok(hr == S_OK, "ProcessOutput returned %#lx\n", hr); ok(output_status == MFT_OUTPUT_DATA_BUFFER_INCOMPLETE, "got output[0].dwStatus %#lx\n", output_status); hr = IMFCollection_AddElement(output_samples, (IUnknown *)output_sample); ok(hr == S_OK, "AddElement returned %#lx\n", hr); ref = IMFSample_Release(output_sample); ok(ref == 1, "Release returned %ld\n", ref); output_sample = create_sample(NULL, output_info.cbSize); winetest_pop_context(); } ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); ok(output_status == 0, "got output[0].dwStatus %#lx\n", output_status); ret = IMFSample_Release(output_sample); ok(ret == 0, "Release returned %lu\n", ret); ok(i == 3, "got %lu output samples\n", i); ret = check_mf_sample_collection(output_samples, output_sample_desc, L"wmaencdata.bin"); ok(ret == 0, "got %lu%% diff\n", ret); IMFCollection_Release(output_samples); output_sample = create_sample(NULL, output_info.cbSize); hr = check_mft_process_output(transform, output_sample, &output_status); ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); ok(output_status == 0, "got output[0].dwStatus %#lx\n", output_status); hr = IMFSample_GetTotalLength(output_sample, &length); ok(hr == S_OK, "GetTotalLength returned %#lx\n", hr); ok(length == 0, "got length %lu\n", length); ret = IMFSample_Release(output_sample); ok(ret == 0, "Release returned %lu\n", ret); ret = IMFTransform_Release(transform); ok(ret == 0, "Release returned %lu\n", ret); failed: winetest_pop_context(); CoUninitialize(); } static void test_wma_decoder(void) { const GUID *const class_id = &CLSID_CWMADecMediaObject; const struct transform_info expect_mft_info = { .name = L"WMAudio Decoder MFT", .major_type = &MFMediaType_Audio, .inputs = { {.subtype = &MEDIASUBTYPE_MSAUDIO1}, {.subtype = &MFAudioFormat_WMAudioV8}, {.subtype = &MFAudioFormat_WMAudioV9}, {.subtype = &MFAudioFormat_WMAudio_Lossless}, }, .outputs = { {.subtype = &MFAudioFormat_PCM}, {.subtype = &MFAudioFormat_Float}, }, }; const struct transform_info expect_dmo_info = { .name = L"WMAudio Decoder DMO", .major_type = &MEDIATYPE_Audio, .inputs = { {.subtype = &MEDIASUBTYPE_MSAUDIO1}, {.subtype = &MEDIASUBTYPE_WMAUDIO2}, {.subtype = &MEDIASUBTYPE_WMAUDIO3}, {.subtype = &MEDIASUBTYPE_WMAUDIO_LOSSLESS}, }, .outputs = { {.subtype = &MEDIASUBTYPE_PCM}, {.subtype = &MEDIASUBTYPE_IEEE_FLOAT}, }, }; static const media_type_desc expect_available_inputs[] = { { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MEDIASUBTYPE_MSAUDIO1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_WMAudioV8), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_WMAudioV9), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_WMAudio_Lossless), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), }, }; static const media_type_desc expect_available_outputs[] = { { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_Float), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 32), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 22050), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 176400), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 8), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 22050), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 88200), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 4), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1), }, }; const struct attribute_desc input_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_WMAudioV8, .required = TRUE), ATTR_BLOB(MF_MT_USER_DATA, wma_codec_data, sizeof(wma_codec_data), .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, wmaenc_block_size, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 22050, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 4003), /* not required by SetInputType, but needed for the transform to work */ ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16), {0}, }; static const struct attribute_desc output_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 22050, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 2 * (16 / 8), .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 2 * (16 / 8) * 22050, .required = TRUE), {0}, }; const struct attribute_desc expect_input_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_WMAudioV8), ATTR_BLOB(MF_MT_USER_DATA, wma_codec_data, sizeof(wma_codec_data)), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, wmaenc_block_size), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 22050), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 4003), ATTR_UINT32(MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16), {0}, }; static const struct attribute_desc expect_output_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 22050 * 4), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 22050), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 4), ATTR_UINT32(MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), {0}, }; const MFT_INPUT_STREAM_INFO input_info = { .cbSize = wmaenc_block_size, .cbAlignment = 1, }; const MFT_OUTPUT_STREAM_INFO output_info = { .cbSize = wmadec_block_size, .cbAlignment = 1, }; const struct buffer_desc output_buffer_desc[] = { {.length = wmadec_block_size, .compare = compare_pcm16}, {.length = wmadec_block_size / 2, .compare = compare_pcm16, .todo_length = TRUE}, }; const struct attribute_desc output_sample_attributes[] = { ATTR_UINT32(mft_output_sample_incomplete, 1), ATTR_UINT32(MFSampleExtension_CleanPoint, 1), {0}, }; const struct attribute_desc output_sample_attributes_todo[] = { ATTR_UINT32(mft_output_sample_incomplete, 1, .todo = TRUE), ATTR_UINT32(MFSampleExtension_CleanPoint, 1), {0}, }; struct sample_desc output_sample_desc[] = { { .attributes = output_sample_attributes + 0, .sample_time = 0, .sample_duration = 928798, .buffer_count = 1, .buffers = output_buffer_desc + 0, .repeat_count = 1, }, { .attributes = output_sample_attributes + 0, .sample_time = 1857596, .sample_duration = 928798, .buffer_count = 1, .buffers = output_buffer_desc + 0, }, { .attributes = output_sample_attributes + 1, /* not MFT_OUTPUT_DATA_BUFFER_INCOMPLETE */ .sample_time = 2786394, .sample_duration = 464399, .todo_duration = 928798, .buffer_count = 1, .buffers = output_buffer_desc + 1, .todo_length = TRUE, }, }; MFT_REGISTER_TYPE_INFO input_type = {MFMediaType_Audio, MFAudioFormat_WMAudioV8}; MFT_REGISTER_TYPE_INFO output_type = {MFMediaType_Audio, MFAudioFormat_Float}; IUnknown *unknown, *tmp_unknown, outer = {&test_unk_vtbl}; IMFSample *input_sample, *output_sample; IMFCollection *output_samples; DWORD length, output_status; IMediaObject *media_object; IPropertyBag *property_bag; IMFMediaType *media_type; IMFTransform *transform; const BYTE *wmaenc_data; ULONG wmaenc_data_len; ULONG i, ret, ref; HRESULT hr; hr = CoInitialize(NULL); ok(hr == S_OK, "Failed to initialize, hr %#lx.\n", hr); winetest_push_context("wmadec"); if (!check_mft_enum(MFT_CATEGORY_AUDIO_DECODER, &input_type, &output_type, class_id)) goto failed; check_mft_get_info(class_id, &expect_mft_info); check_dmo_get_info(class_id, &expect_dmo_info); if (FAILED(hr = CoCreateInstance(class_id, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)&transform))) goto failed; check_interface(transform, &IID_IMFTransform, TRUE); check_interface(transform, &IID_IMediaObject, TRUE); todo_wine check_interface(transform, &IID_IPropertyStore, TRUE); check_interface(transform, &IID_IPropertyBag, TRUE); check_mft_optional_methods(transform, 1); check_mft_get_attributes(transform, NULL, 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); hr = IMFTransform_GetOutputAvailableType(transform, 0, 0, &media_type); ok(hr == MF_E_TRANSFORM_TYPE_NOT_SET, "GetOutputAvailableType returned %#lx\n", hr); i = -1; while (SUCCEEDED(hr = IMFTransform_GetInputAvailableType(transform, 0, ++i, &media_type))) { winetest_push_context("in %lu", i); ok(hr == S_OK, "GetInputAvailableType returned %#lx\n", hr); check_media_type(media_type, expect_available_inputs[i], -1); ret = IMFMediaType_Release(media_type); ok(ret == 0, "Release returned %lu\n", ret); winetest_pop_context(); } todo_wine ok(hr == MF_E_NO_MORE_TYPES, "GetInputAvailableType returned %#lx\n", hr); todo_wine ok(i == 4, "%lu input media types\n", i); /* 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_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_stream_info(transform, MF_E_TRANSFORM_TYPE_NOT_SET, NULL); check_mft_get_output_stream_info(transform, MF_E_TRANSFORM_TYPE_NOT_SET, NULL); /* check new output media types */ i = -1; while (SUCCEEDED(hr = IMFTransform_GetOutputAvailableType(transform, 0, ++i, &media_type))) { winetest_push_context("out %lu", i); ok(hr == S_OK, "GetOutputAvailableType returned %#lx\n", hr); check_media_type(media_type, expect_available_outputs[i], -1); ret = IMFMediaType_Release(media_type); ok(ret == 0, "Release returned %lu\n", ret); winetest_pop_context(); } ok(hr == MF_E_NO_MORE_TYPES, "GetOutputAvailableType returned %#lx\n", hr); ok(i == 2, "%lu output media types\n", i); 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_input_stream_info(transform, S_OK, &input_info); check_mft_get_output_stream_info(transform, S_OK, &output_info); load_resource(L"wmaencdata.bin", &wmaenc_data, &wmaenc_data_len); ok(wmaenc_data_len % wmaenc_block_size == 0, "got length %lu\n", wmaenc_data_len); input_sample = create_sample(wmaenc_data, wmaenc_block_size / 2); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == S_OK, "ProcessInput returned %#lx\n", hr); ret = IMFSample_Release(input_sample); ok(ret == 0, "Release returned %lu\n", ret); input_sample = create_sample(wmaenc_data, wmaenc_block_size + 1); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == S_OK, "ProcessInput returned %#lx\n", hr); ret = IMFSample_Release(input_sample); ok(ret == 0, "Release returned %lu\n", ret); input_sample = create_sample(wmaenc_data, wmaenc_block_size); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == S_OK, "ProcessInput returned %#lx\n", hr); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == MF_E_NOTACCEPTING, "ProcessInput returned %#lx\n", hr); ret = IMFSample_Release(input_sample); ok(ret == 1, "Release returned %lu\n", ret); /* As output_info.dwFlags doesn't have MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES * IMFTransform_ProcessOutput needs a sample or returns MF_E_TRANSFORM_NEED_MORE_INPUT */ hr = check_mft_process_output(transform, NULL, &output_status); ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); ok(output_status == MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE || broken(output_status == (MFT_OUTPUT_DATA_BUFFER_INCOMPLETE|MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE)) /* Win7 */, "got output[0].dwStatus %#lx\n", output_status); input_sample = create_sample(wmaenc_data, wmaenc_block_size); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == MF_E_NOTACCEPTING, "ProcessInput returned %#lx\n", hr); ret = IMFSample_Release(input_sample); ok(ret == 0, "Release returned %lu\n", ret); hr = check_mft_process_output(transform, NULL, &output_status); ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); ok(output_status == MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE || broken(output_status == (MFT_OUTPUT_DATA_BUFFER_INCOMPLETE|MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE)) /* Win7 */, "got output[0].dwStatus %#lx\n", output_status); hr = MFCreateCollection(&output_samples); ok(hr == S_OK, "MFCreateCollection returned %#lx\n", hr); output_sample = create_sample(NULL, output_info.cbSize); for (i = 0; SUCCEEDED(hr = check_mft_process_output(transform, output_sample, &output_status)); i++) { winetest_push_context("%lu", i); ok(!(output_status & ~MFT_OUTPUT_DATA_BUFFER_INCOMPLETE), "got output[0].dwStatus %#lx\n", output_status); ok(hr == S_OK, "ProcessOutput returned %#lx\n", hr); hr = IMFCollection_AddElement(output_samples, (IUnknown *)output_sample); ok(hr == S_OK, "AddElement returned %#lx\n", hr); ref = IMFSample_Release(output_sample); ok(ref == 1, "Release returned %ld\n", ref); output_sample = create_sample(NULL, output_info.cbSize); winetest_pop_context(); } ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); ok(output_status == 0, "got output[0].dwStatus %#lx\n", output_status); ret = IMFSample_Release(output_sample); ok(ret == 0, "Release returned %lu\n", ret); todo_wine_if(i == 3) /* wmadec output depends on ffmpeg version used */ ok(i == 4, "got %lu output samples\n", i); if (!strcmp(winetest_platform, "wine") && i == 3) output_sample_desc[1].attributes = output_sample_attributes_todo; ret = check_mf_sample_collection(output_samples, output_sample_desc, L"wmadecdata.bin"); todo_wine_if(ret > 0 && ret <= 10) /* ffmpeg sometimes offsets the decoded data */ ok(ret == 0, "got %lu%% diff\n", ret); IMFCollection_Release(output_samples); output_sample = create_sample(NULL, output_info.cbSize); hr = check_mft_process_output(transform, output_sample, &output_status); ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); ok(output_status == 0, "got output[0].dwStatus %#lx\n", output_status); hr = IMFSample_GetTotalLength(output_sample, &length); ok(hr == S_OK, "GetTotalLength returned %#lx\n", hr); ok(length == 0, "got length %lu\n", length); ret = IMFSample_Release(output_sample); ok(ret == 0, "Release returned %lu\n", ret); input_sample = create_sample(wmaenc_data, wmaenc_block_size); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == S_OK, "ProcessInput returned %#lx\n", hr); ret = IMFTransform_Release(transform); ok(ret == 0, "Release returned %lu\n", ret); ret = IMFSample_Release(input_sample); ok(ret == 0, "Release returned %lu\n", ret); hr = CoCreateInstance( &CLSID_CWMADecMediaObject, &outer, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&unknown ); ok( hr == S_OK, "CoCreateInstance returned %#lx\n", hr ); hr = IUnknown_QueryInterface( unknown, &IID_IMFTransform, (void **)&transform ); ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); hr = IUnknown_QueryInterface( unknown, &IID_IMediaObject, (void **)&media_object ); ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); hr = IUnknown_QueryInterface( unknown, &IID_IPropertyBag, (void **)&property_bag ); ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); hr = IUnknown_QueryInterface( media_object, &IID_IUnknown, (void **)&tmp_unknown ); ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); ok( unknown != &outer, "got outer IUnknown\n" ); ok( transform != (void *)unknown, "got IUnknown == IMFTransform\n" ); ok( media_object != (void *)unknown, "got IUnknown == IMediaObject\n" ); ok( property_bag != (void *)unknown, "got IUnknown == IPropertyBag\n" ); ok( tmp_unknown != unknown, "got inner IUnknown\n" ); check_interface( unknown, &IID_IPersistPropertyBag, FALSE ); check_interface( unknown, &IID_IAMFilterMiscFlags, FALSE ); check_interface( unknown, &IID_IMediaSeeking, FALSE ); check_interface( unknown, &IID_IMediaPosition, FALSE ); check_interface( unknown, &IID_IReferenceClock, FALSE ); check_interface( unknown, &IID_IBasicAudio, FALSE ); ref = IUnknown_Release( tmp_unknown ); ok( ref == 1, "Release returned %lu\n", ref ); ref = IPropertyBag_Release( property_bag ); ok( ref == 1, "Release returned %lu\n", ref ); ref = IMediaObject_Release( media_object ); ok( ref == 1, "Release returned %lu\n", ref ); ref = IMFTransform_Release( transform ); ok( ref == 1, "Release returned %lu\n", ref ); ref = IUnknown_Release( unknown ); ok( ref == 0, "Release returned %lu\n", ref ); failed: winetest_pop_context(); CoUninitialize(); } static void test_wma_decoder_dmo_input_type(void) { const DMO_MEDIA_TYPE expected_input_types[] = { {MEDIATYPE_Audio, MEDIASUBTYPE_MSAUDIO1 }, {MEDIATYPE_Audio, MEDIASUBTYPE_WMAUDIO2 }, {MEDIATYPE_Audio, MEDIASUBTYPE_WMAUDIO3 }, {MEDIATYPE_Audio, MEDIASUBTYPE_WMAUDIO_LOSSLESS}, }; DMO_MEDIA_TYPE *good_input_type, *bad_input_type, type; char buffer_good[1024], buffer_bad[1024]; DWORD count, i, ret; IMediaObject *dmo; HRESULT hr; winetest_push_context("wmadec"); hr = CoInitialize(NULL); ok(hr == S_OK, "CoInitialize failed, hr %#lx.\n", hr); if (FAILED(hr = CoCreateInstance(&CLSID_CWMADecMediaObject, NULL, CLSCTX_INPROC_SERVER, &IID_IMediaObject, (void **)&dmo))) { CoUninitialize(); winetest_pop_context(); return; } good_input_type = (void *)buffer_good; bad_input_type = (void *)buffer_bad; /* Test GetInputType. */ count = ARRAY_SIZE(expected_input_types); hr = IMediaObject_GetInputType(dmo, 1, 0, NULL); ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetInputType returned %#lx.\n", hr); hr = IMediaObject_GetInputType(dmo, 1, 0, &type); ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetInputType returned %#lx.\n", hr); hr = IMediaObject_GetInputType(dmo, 1, count, &type); ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetInputType returned %#lx.\n", hr); hr = IMediaObject_GetInputType(dmo, 0, count, &type); ok(hr == DMO_E_NO_MORE_ITEMS, "GetInputType returned %#lx.\n", hr); hr = IMediaObject_GetInputType(dmo, 0, count, NULL); ok(hr == DMO_E_NO_MORE_ITEMS, "GetInputType returned %#lx.\n", hr); hr = IMediaObject_GetInputType(dmo, 0, 0xdeadbeef, NULL); ok(hr == DMO_E_NO_MORE_ITEMS, "GetInputType returned %#lx.\n", hr); hr = IMediaObject_GetInputType(dmo, 0, count - 1, NULL); ok(hr == S_OK, "GetInputType returned %#lx.\n", hr); i = -1; while (SUCCEEDED(hr = IMediaObject_GetInputType(dmo, 0, ++i, &type))) { winetest_push_context("type %lu", i); check_dmo_media_type(&type, &expected_input_types[i]); MoFreeMediaType(&type); winetest_pop_context(); } ok(hr == DMO_E_NO_MORE_ITEMS, "GetInputType returned %#lx.\n", hr); ok(i == count, "%lu types.\n", i); /* Test SetInputType. */ init_dmo_media_type_audio(good_input_type, &MEDIASUBTYPE_WMAUDIO2, 2, 22050, 32); memset(bad_input_type, 0, sizeof(buffer_bad)); hr = IMediaObject_SetInputType(dmo, 1, NULL, 0); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 1, bad_input_type, 0); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 1, good_input_type, 0); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 1, NULL, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 1, bad_input_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 1, good_input_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 1, NULL, DMO_SET_TYPEF_CLEAR); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 1, bad_input_type, DMO_SET_TYPEF_CLEAR); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 1, good_input_type, DMO_SET_TYPEF_CLEAR); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 1, NULL, 0x4); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 1, bad_input_type, 0x4); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 1, good_input_type, 0x4); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, NULL, DMO_SET_TYPEF_CLEAR); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, NULL, DMO_SET_TYPEF_CLEAR | DMO_SET_TYPEF_TEST_ONLY); ok(hr == E_INVALIDARG, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, NULL, DMO_SET_TYPEF_CLEAR | 0x4); ok(hr == E_INVALIDARG, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, NULL, 0); ok(hr == E_POINTER, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, NULL, DMO_SET_TYPEF_TEST_ONLY); ok(hr == E_POINTER, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, NULL, 0x4); ok(hr == E_POINTER, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, bad_input_type, 0); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, bad_input_type, DMO_SET_TYPEF_CLEAR); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, bad_input_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, bad_input_type, 0x4); ok(hr == E_INVALIDARG, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, good_input_type, 0); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_CLEAR); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, good_input_type, 0x4); ok(hr == E_INVALIDARG, "SetInputType returned %#lx.\n", hr); /* Test GetInputCurrentType. */ hr = IMediaObject_SetInputType(dmo, 0, NULL, DMO_SET_TYPEF_CLEAR); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_GetInputCurrentType(dmo, 1, NULL); todo_wine ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetInputCurrentType returned %#lx.\n", hr); hr = IMediaObject_GetInputCurrentType(dmo, 0, NULL); todo_wine ok(hr == DMO_E_TYPE_NOT_SET, "GetInputCurrentType returned %#lx.\n", hr); hr = IMediaObject_GetInputCurrentType(dmo, 1, &type); todo_wine ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetInputCurrentType returned %#lx.\n", hr); hr = IMediaObject_GetInputCurrentType(dmo, 0, &type); todo_wine ok(hr == DMO_E_TYPE_NOT_SET, "GetInputCurrentType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, good_input_type, 0); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_GetInputCurrentType(dmo, 1, NULL); todo_wine ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetInputCurrentType returned %#lx.\n", hr); hr = IMediaObject_GetInputCurrentType(dmo, 0, NULL); todo_wine ok(hr == E_POINTER, "GetInputCurrentType returned %#lx.\n", hr); hr = IMediaObject_GetInputCurrentType(dmo, 1, &type); todo_wine ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetInputCurrentType returned %#lx.\n", hr); hr = IMediaObject_GetInputCurrentType(dmo, 0, &type); todo_wine ok(hr == S_OK, "GetInputCurrentType returned %#lx.\n", hr); if (hr == S_OK) { check_dmo_media_type(&type, good_input_type); MoFreeMediaType(&type); } /* Cleanup. */ ret = IMediaObject_Release(dmo); ok(ret == 0, "Release returned %lu\n", ret); CoUninitialize(); winetest_pop_context(); } static void test_wma_decoder_dmo_output_type(void) { const UINT channel_count = 2, rate = 22050, bits_per_sample = WMAUDIO_BITS_PER_SAMPLE; const GUID* input_subtype = &MEDIASUBTYPE_WMAUDIO2; const WAVEFORMATEX expected_output_info = { WAVE_FORMAT_PCM, channel_count, rate, bits_per_sample * rate * channel_count / 8, bits_per_sample * channel_count / 8, WMAUDIO_BITS_PER_SAMPLE, 0 }; const DMO_MEDIA_TYPE expected_output_type = { MEDIATYPE_Audio, MEDIASUBTYPE_PCM, TRUE, FALSE, 0, FORMAT_WaveFormatEx, NULL, sizeof(expected_output_info), (BYTE *)&expected_output_info }; char buffer_good_output[1024], buffer_bad_output[1024], buffer_input[1024]; DMO_MEDIA_TYPE *good_output_type, *bad_output_type, *input_type, type; DWORD count, i, ret, size, alignment; IMediaObject *dmo; HRESULT hr; winetest_push_context("wmadec"); hr = CoInitialize(NULL); ok(hr == S_OK, "CoInitialize failed, hr %#lx.\n", hr); if (FAILED(hr = CoCreateInstance(&CLSID_CWMADecMediaObject, NULL, CLSCTX_INPROC_SERVER, &IID_IMediaObject, (void **)&dmo))) { CoUninitialize(); winetest_pop_context(); return; } /* Initialize media types. */ input_type = (void *)buffer_input; good_output_type = (void *)buffer_good_output; bad_output_type = (void *)buffer_bad_output; init_dmo_media_type_audio(input_type, input_subtype, channel_count, rate, 16); init_dmo_media_type_audio(good_output_type, &MEDIASUBTYPE_PCM, channel_count, rate, bits_per_sample); memset(bad_output_type, 0, sizeof(buffer_bad_output)); /* Test GetOutputType. */ hr = IMediaObject_GetOutputType(dmo, 1, 0, NULL); ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetOutputType returned %#lx.\n", hr); hr = IMediaObject_GetOutputType(dmo, 0, 0, NULL); ok(hr == DMO_E_TYPE_NOT_SET, "GetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, input_type, 0); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); count = 1; hr = IMediaObject_GetOutputType(dmo, 1, 0, NULL); ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetOutputType returned %#lx.\n", hr); hr = IMediaObject_GetOutputType(dmo, 1, 0, &type); ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetOutputType returned %#lx.\n", hr); hr = IMediaObject_GetOutputType(dmo, 1, count, &type); ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetOutputType returned %#lx.\n", hr); hr = IMediaObject_GetOutputType(dmo, 0, count, &type); ok(hr == DMO_E_NO_MORE_ITEMS, "GetOutputType returned %#lx.\n", hr); hr = IMediaObject_GetOutputType(dmo, 0, count, NULL); ok(hr == DMO_E_NO_MORE_ITEMS, "GetOutputType returned %#lx.\n", hr); hr = IMediaObject_GetOutputType(dmo, 0, 0xdeadbeef, NULL); ok(hr == DMO_E_NO_MORE_ITEMS, "GetOutputType returned %#lx.\n", hr); hr = IMediaObject_GetOutputType(dmo, 0, count - 1, NULL); ok(hr == S_OK, "GetOutputType returned %#lx.\n", hr); i = -1; while (SUCCEEDED(hr = IMediaObject_GetOutputType(dmo, 0, ++i, &type))) { winetest_push_context("type %lu", i); check_dmo_media_type(&type, &expected_output_type); MoFreeMediaType(&type); winetest_pop_context(); } ok(hr == DMO_E_NO_MORE_ITEMS, "GetInputType returned %#lx.\n", hr); ok(i == count, "%lu types.\n", i); /* Test SetOutputType. */ hr = IMediaObject_SetInputType(dmo, 0, NULL, DMO_SET_TYPEF_CLEAR); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, NULL, 0); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, good_output_type, 0); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, NULL, 0); ok(hr == E_POINTER, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, NULL, DMO_SET_TYPEF_CLEAR); ok(hr == S_OK, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, bad_output_type, 0); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, bad_output_type, DMO_SET_TYPEF_CLEAR); ok(hr == S_OK, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, good_output_type, 0); ok(hr == DMO_E_TYPE_NOT_SET, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, good_output_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_TYPE_NOT_SET, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, good_output_type, DMO_SET_TYPEF_CLEAR); ok(hr == S_OK, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, good_output_type, 0x4); ok(hr == E_INVALIDARG, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, input_type, 0); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, NULL, 0); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, bad_output_type, 0); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, good_output_type, 0); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, NULL, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, bad_output_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, good_output_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, NULL, DMO_SET_TYPEF_CLEAR); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, bad_output_type, DMO_SET_TYPEF_CLEAR); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, good_output_type, DMO_SET_TYPEF_CLEAR); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, NULL, 0x4); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, bad_output_type, 0x4); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, good_output_type, 0x4); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, NULL, DMO_SET_TYPEF_CLEAR); ok(hr == S_OK, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, NULL, DMO_SET_TYPEF_CLEAR | DMO_SET_TYPEF_TEST_ONLY); ok(hr == E_INVALIDARG, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, NULL, DMO_SET_TYPEF_CLEAR | 0x4); ok(hr == E_INVALIDARG, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, NULL, 0); ok(hr == E_POINTER, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, NULL, DMO_SET_TYPEF_TEST_ONLY); ok(hr == E_POINTER, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, NULL, 0x4); ok(hr == E_POINTER, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, bad_output_type, 0); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, bad_output_type, DMO_SET_TYPEF_CLEAR); ok(hr == S_OK, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, bad_output_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, bad_output_type, 0x4); ok(hr == E_INVALIDARG, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, good_output_type, 0); ok(hr == S_OK, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, good_output_type, DMO_SET_TYPEF_CLEAR); ok(hr == S_OK, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, good_output_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == S_OK, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, good_output_type, 0x4); ok(hr == E_INVALIDARG, "SetOutputType returned %#lx.\n", hr); /* Test GetOutputCurrentType. */ hr = IMediaObject_SetOutputType(dmo, 0, NULL, DMO_SET_TYPEF_CLEAR); ok(hr == S_OK, "SetOutputType returned %#lx.\n", hr); todo_wine { hr = IMediaObject_GetOutputCurrentType(dmo, 1, NULL); ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetOutputCurrentType returned %#lx.\n", hr); hr = IMediaObject_GetOutputCurrentType(dmo, 0, NULL); ok(hr == DMO_E_TYPE_NOT_SET, "GetOutputCurrentType returned %#lx.\n", hr); hr = IMediaObject_GetOutputCurrentType(dmo, 1, &type); ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetOutputCurrentType returned %#lx.\n", hr); hr = IMediaObject_GetOutputCurrentType(dmo, 0, &type); ok(hr == DMO_E_TYPE_NOT_SET, "GetOutputCurrentType returned %#lx.\n", hr); } hr = IMediaObject_SetOutputType(dmo, 0, good_output_type, 0); ok(hr == S_OK, "SetOutputType returned %#lx.\n", hr); todo_wine { hr = IMediaObject_GetOutputCurrentType(dmo, 1, NULL); ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetOutputCurrentType returned %#lx.\n", hr); hr = IMediaObject_GetOutputCurrentType(dmo, 0, NULL); ok(hr == E_POINTER, "GetOutputCurrentType returned %#lx.\n", hr); hr = IMediaObject_GetOutputCurrentType(dmo, 1, &type); ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetOutputCurrentType returned %#lx.\n", hr); hr = IMediaObject_GetOutputCurrentType(dmo, 0, &type); ok(hr == S_OK, "GetOutputCurrentType returned %#lx.\n", hr); } if (hr == S_OK) { check_dmo_media_type(&type, good_output_type); MoFreeMediaType(&type); } /* Test GetOutputSizeInfo. */ hr = IMediaObject_GetOutputSizeInfo(dmo, 1, NULL, NULL); ok(hr == E_POINTER, "GetOutputSizeInfo returned %#lx.\n", hr); hr = IMediaObject_GetOutputSizeInfo(dmo, 0, NULL, NULL); ok(hr == E_POINTER, "GetOutputSizeInfo returned %#lx.\n", hr); hr = IMediaObject_GetOutputSizeInfo(dmo, 0, &size, NULL); ok(hr == E_POINTER, "GetOutputSizeInfo returned %#lx.\n", hr); hr = IMediaObject_GetOutputSizeInfo(dmo, 0, NULL, &alignment); ok(hr == E_POINTER, "GetOutputSizeInfo returned %#lx.\n", hr); hr = IMediaObject_GetOutputSizeInfo(dmo, 0, &size, &alignment); ok(hr == S_OK, "GetOutputSizeInfo returned %#lx.\n", hr); ok(size == 8192, "Unexpected size %lu.\n", size); ok(alignment == 1, "Unexpected alignment %lu.\n", alignment); hr = IMediaObject_GetInputCurrentType(dmo, 0, input_type); todo_wine ok(hr == S_OK, "GetInputCurrentType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, input_type, 0); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_GetOutputCurrentType(dmo, 0, &type); todo_wine ok(hr == S_OK, "GetOutputCurrentType returned %#lx.\n", hr); init_dmo_media_type_audio(input_type, input_subtype, channel_count, rate * 2, 32); hr = IMediaObject_SetInputType(dmo, 0, input_type, 0); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_GetOutputCurrentType(dmo, 0, &type); todo_wine ok(hr == DMO_E_TYPE_NOT_SET, "GetOutputCurrentType returned %#lx.\n", hr); /* Cleanup. */ ret = IMediaObject_Release(dmo); ok(ret == 0, "Release returned %lu\n", ret); CoUninitialize(); winetest_pop_context(); } #define next_h264_sample(a, b) next_h264_sample_(__LINE__, a, b) static IMFSample *next_h264_sample_(int line, const BYTE **h264_buf, ULONG *h264_len) { const BYTE *sample_data; ok_(__FILE__, line)(*h264_len > 4, "invalid h264 length\n"); ok_(__FILE__, line)(*(UINT32 *)*h264_buf == 0x01000000, "invalid h264 buffer\n"); sample_data = *h264_buf; (*h264_len) -= 4; (*h264_buf) += 4; while (*h264_len >= 4 && *(UINT32 *)*h264_buf != 0x01000000) { (*h264_len)--; (*h264_buf)++; } return create_sample(sample_data, *h264_buf - sample_data); } static void test_h264_encoder(void) { static const DWORD actual_width = 96, actual_height = 96; const GUID *const class_id = &CLSID_MSH264EncoderMFT; const struct transform_info expect_mft_info = { .name = L"H264 Encoder MFT", .major_type = &MFMediaType_Video, .inputs = { {.subtype = &MFVideoFormat_IYUV}, {.subtype = &MFVideoFormat_YV12}, {.subtype = &MFVideoFormat_NV12}, {.subtype = &MFVideoFormat_YUY2}, }, .outputs = { {.subtype = &MFVideoFormat_H264}, }, }; static const struct attribute_desc expect_transform_attributes[] = { ATTR_UINT32(MFT_ENCODER_SUPPORTS_CONFIG_EVENT, 1), {0}, }; const struct attribute_desc input_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_RATE, 30000, 1001, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height, .required = TRUE), {0}, }; const struct attribute_desc output_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_H264, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_RATE, 30000, 1001), ATTR_UINT32(MF_MT_AVG_BITRATE, 193540), ATTR_UINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive), {0}, }; const struct attribute_desc expect_input_type_desc[] = { 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_RATIO(MF_MT_FRAME_RATE, 30000, 1001), {0}, }; const struct attribute_desc expect_output_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_H264), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), 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)), {0}, }; static const MFT_OUTPUT_STREAM_INFO expect_output_info = {.cbSize = 0x8000}; MFT_REGISTER_TYPE_INFO output_type = {MFMediaType_Video, MFVideoFormat_H264}; MFT_REGISTER_TYPE_INFO input_type = {MFMediaType_Video, MFVideoFormat_NV12}; IMFTransform *transform; HRESULT hr; ULONG ret; hr = CoInitialize(NULL); ok(hr == S_OK, "Failed to initialize, hr %#lx.\n", hr); winetest_push_context("h264enc"); if (!has_video_processor) { win_skip("Skipping inconsistent h264 encoder tests on Win7.\n"); goto failed; } if (!check_mft_enum(MFT_CATEGORY_VIDEO_ENCODER, &input_type, &output_type, class_id)) goto failed; check_mft_get_info(class_id, &expect_mft_info); hr = CoCreateInstance(class_id, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)&transform); ok(hr == S_OK, "CoCreateInstance returned %#lx.\n", hr); check_interface(transform, &IID_IMFTransform, TRUE); check_interface(transform, &IID_IMediaObject, FALSE); check_interface(transform, &IID_IPropertyStore, FALSE); check_interface(transform, &IID_IPropertyBag, FALSE); check_mft_get_attributes(transform, expect_transform_attributes, FALSE); check_mft_get_input_stream_info(transform, S_OK, NULL); check_mft_get_output_stream_info(transform, S_OK, NULL); check_mft_set_output_type_required(transform, output_type_desc); check_mft_set_output_type(transform, output_type_desc, S_OK); 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); check_mft_get_output_current_type(transform, expect_output_type_desc); check_mft_get_input_stream_info(transform, S_OK, NULL); check_mft_get_output_stream_info(transform, S_OK, &expect_output_info); ret = IMFTransform_Release(transform); ok(ret == 0, "Release returned %lu\n", ret); failed: winetest_pop_context(); CoUninitialize(); } static void test_h264_decoder(void) { const GUID *const class_id = &CLSID_MSH264DecoderMFT; const struct transform_info expect_mft_info = { .name = L"Microsoft H264 Video Decoder MFT", .major_type = &MFMediaType_Video, .inputs = { {.subtype = &MFVideoFormat_H264}, {.subtype = &MFVideoFormat_H264_ES}, }, .outputs = { {.subtype = &MFVideoFormat_NV12}, {.subtype = &MFVideoFormat_YV12}, {.subtype = &MFVideoFormat_IYUV}, {.subtype = &MFVideoFormat_I420}, {.subtype = &MFVideoFormat_YUY2}, }, }; static const media_type_desc default_inputs[] = { { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_H264), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_H264_ES), }, }; static const struct attribute_desc expect_transform_attributes[] = { ATTR_UINT32(MF_LOW_LATENCY, 0), ATTR_UINT32(MF_SA_D3D_AWARE, 1, .todo = TRUE), ATTR_UINT32(MF_SA_D3D11_AWARE, 1), ATTR_UINT32(MFT_DECODER_EXPOSE_OUTPUT_TYPES_IN_NATIVE_ORDER, 0, .todo = TRUE), /* more H264 decoder specific attributes from CODECAPI */ ATTR_UINT32(AVDecVideoAcceleration_H264, 1), {0}, }; static const DWORD input_width = 120, input_height = 248; const media_type_desc default_outputs[] = { { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_RATIO(MF_MT_FRAME_RATE, 30000, 1001), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, input_width), ATTR_UINT32(MF_MT_INTERLACE_MODE, 7), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_RATIO(MF_MT_FRAME_SIZE, input_width, input_height), ATTR_UINT32(MF_MT_SAMPLE_SIZE, input_width * input_height * 3 / 2), /* ATTR_UINT32(MF_MT_VIDEO_ROTATION, 0), missing on Win7 */ }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_YV12), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_RATIO(MF_MT_FRAME_RATE, 30000, 1001), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, input_width), ATTR_UINT32(MF_MT_INTERLACE_MODE, 7), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_RATIO(MF_MT_FRAME_SIZE, input_width, input_height), ATTR_UINT32(MF_MT_SAMPLE_SIZE, input_width * input_height * 3 / 2), /* ATTR_UINT32(MF_MT_VIDEO_ROTATION, 0), missing on Win7 */ }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_IYUV), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_RATIO(MF_MT_FRAME_RATE, 30000, 1001), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, input_width), ATTR_UINT32(MF_MT_INTERLACE_MODE, 7), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_RATIO(MF_MT_FRAME_SIZE, input_width, input_height), ATTR_UINT32(MF_MT_SAMPLE_SIZE, input_width * input_height * 3 / 2), /* ATTR_UINT32(MF_MT_VIDEO_ROTATION, 0), missing on Win7 */ }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_I420), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_RATIO(MF_MT_FRAME_RATE, 30000, 1001), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, input_width), ATTR_UINT32(MF_MT_INTERLACE_MODE, 7), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_RATIO(MF_MT_FRAME_SIZE, input_width, input_height), ATTR_UINT32(MF_MT_SAMPLE_SIZE, input_width * input_height * 3 / 2), /* ATTR_UINT32(MF_MT_VIDEO_ROTATION, 0), missing on Win7 */ }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_YUY2), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_RATIO(MF_MT_FRAME_RATE, 30000, 1001), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, input_width * 2), ATTR_UINT32(MF_MT_INTERLACE_MODE, 7), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_RATIO(MF_MT_FRAME_SIZE, input_width, input_height), ATTR_UINT32(MF_MT_SAMPLE_SIZE, input_width * input_height * 2), /* ATTR_UINT32(MF_MT_VIDEO_ROTATION, 0), missing on Win7 */ }, }; const struct attribute_desc input_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_H264, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, input_width, input_height), {0}, }; const struct attribute_desc output_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, input_width, input_height, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_RATE, 60000, 1000), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 2, 1), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, 3840), ATTR_UINT32(MF_MT_SAMPLE_SIZE, 3840 * input_height * 3 / 2), ATTR_UINT32(MF_MT_VIDEO_ROTATION, 0), {0}, }; const struct attribute_desc expect_input_type_desc[] = { 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), {0}, }; const struct attribute_desc expect_output_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12), ATTR_RATIO(MF_MT_FRAME_SIZE, input_width, input_height), ATTR_RATIO(MF_MT_FRAME_RATE, 60000, 1000), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 2, 1), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, 3840), ATTR_UINT32(MF_MT_SAMPLE_SIZE, 3840 * input_height * 3 / 2), ATTR_UINT32(MF_MT_VIDEO_ROTATION, 0), ATTR_UINT32(MF_MT_AVG_BIT_ERROR_RATE, 0, .todo = TRUE), ATTR_UINT32(MF_MT_COMPRESSED, 0, .todo = TRUE), {0}, }; static const struct attribute_desc new_output_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_I420), ATTR_RATIO(MF_MT_FRAME_SIZE, 96, 96), ATTR_RATIO(MF_MT_FRAME_RATE, 1, 1), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 2), {0}, }; static const struct attribute_desc expect_new_output_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_I420), ATTR_RATIO(MF_MT_FRAME_SIZE, 96, 96), ATTR_RATIO(MF_MT_FRAME_RATE, 1, 1), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 2), ATTR_UINT32(MF_MT_COMPRESSED, 0, .todo = TRUE), ATTR_UINT32(MF_MT_AVG_BIT_ERROR_RATE, 0, .todo = TRUE), {0}, }; static const MFVideoArea actual_aperture = {.Area={82,84}}; static const DWORD actual_width = 96, actual_height = 96; const media_type_desc actual_outputs[] = { { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_RATIO(MF_MT_FRAME_RATE, 60000, 1000), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), ATTR_UINT32(MF_MT_SAMPLE_SIZE, actual_width * actual_height * 3 / 2), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, actual_width), /* ATTR_UINT32(MF_MT_VIDEO_ROTATION, 0), missing on Win7 */ ATTR_UINT32(MF_MT_INTERLACE_MODE, 7), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_BLOB(MF_MT_MINIMUM_DISPLAY_APERTURE, &actual_aperture, 16), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_YV12), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_RATIO(MF_MT_FRAME_RATE, 60000, 1000), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), ATTR_UINT32(MF_MT_SAMPLE_SIZE, actual_width * actual_height * 3 / 2), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, actual_width), /* ATTR_UINT32(MF_MT_VIDEO_ROTATION, 0), missing on Win7 */ ATTR_UINT32(MF_MT_INTERLACE_MODE, 7), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_BLOB(MF_MT_MINIMUM_DISPLAY_APERTURE, &actual_aperture, 16), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_IYUV), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_RATIO(MF_MT_FRAME_RATE, 60000, 1000), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), ATTR_UINT32(MF_MT_SAMPLE_SIZE, actual_width * actual_height * 3 / 2), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, actual_width), /* ATTR_UINT32(MF_MT_VIDEO_ROTATION, 0), missing on Win7 */ ATTR_UINT32(MF_MT_INTERLACE_MODE, 7), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_BLOB(MF_MT_MINIMUM_DISPLAY_APERTURE, &actual_aperture, 16), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_I420), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_RATIO(MF_MT_FRAME_RATE, 60000, 1000), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), ATTR_UINT32(MF_MT_SAMPLE_SIZE, actual_width * actual_height * 3 / 2), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, actual_width), /* ATTR_UINT32(MF_MT_VIDEO_ROTATION, 0), missing on Win7 */ ATTR_UINT32(MF_MT_INTERLACE_MODE, 7), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_BLOB(MF_MT_MINIMUM_DISPLAY_APERTURE, &actual_aperture, 16), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_YUY2), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_RATIO(MF_MT_FRAME_RATE, 60000, 1000), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), ATTR_UINT32(MF_MT_SAMPLE_SIZE, actual_width * actual_height * 2), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, actual_width * 2), /* ATTR_UINT32(MF_MT_VIDEO_ROTATION, 0), missing on Win7 */ ATTR_UINT32(MF_MT_INTERLACE_MODE, 7), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_BLOB(MF_MT_MINIMUM_DISPLAY_APERTURE, &actual_aperture, 16), }, }; const MFT_OUTPUT_STREAM_INFO initial_output_info = { .dwFlags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE, .cbSize = 1920 * 1088 * 2, }; const MFT_OUTPUT_STREAM_INFO output_info = { .dwFlags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE, .cbSize = input_width * input_height * 2, }; const MFT_OUTPUT_STREAM_INFO actual_output_info = { .dwFlags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE, .cbSize = actual_width * actual_height * 2, }; const MFT_INPUT_STREAM_INFO input_info = { .dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_INPUT_STREAM_FIXED_SAMPLE_SIZE, .cbSize = 0x1000, }; const struct attribute_desc output_sample_attributes[] = { ATTR_UINT32(MFSampleExtension_CleanPoint, 1), {0}, }; const struct buffer_desc output_buffer_desc_nv12 = { .length = actual_width * actual_height * 3 / 2, .compare = compare_nv12, .dump = dump_nv12, .rect = {.right = 82, .bottom = 84}, }; const struct sample_desc output_sample_desc_nv12 = { .attributes = output_sample_attributes, .sample_time = 0, .sample_duration = 333667, .buffer_count = 1, .buffers = &output_buffer_desc_nv12, }; const struct buffer_desc output_buffer_desc_i420 = { .length = actual_width * actual_height * 3 / 2, .compare = compare_i420, .dump = dump_i420, .rect = {.right = 82, .bottom = 84}, }; const struct sample_desc expect_output_sample_i420 = { .attributes = output_sample_attributes, .sample_time = 333667, .sample_duration = 333667, .buffer_count = 1, .buffers = &output_buffer_desc_i420, }; MFT_REGISTER_TYPE_INFO input_type = {MFMediaType_Video, MFVideoFormat_H264}; MFT_REGISTER_TYPE_INFO output_type = {MFMediaType_Video, MFVideoFormat_NV12}; IMFSample *input_sample, *output_sample; const BYTE *h264_encoded_data; IMFCollection *output_samples; ULONG h264_encoded_data_len; DWORD length, output_status; IMFAttributes *attributes; IMFMediaType *media_type; IMFTransform *transform; ULONG i, ret, ref; DWORD flags; HRESULT hr; hr = CoInitialize(NULL); ok(hr == S_OK, "Failed to initialize, hr %#lx.\n", hr); winetest_push_context("h264dec"); if (!check_mft_enum(MFT_CATEGORY_VIDEO_DECODER, &input_type, &output_type, class_id)) goto failed; check_mft_get_info(class_id, &expect_mft_info); if (FAILED(hr = CoCreateInstance(class_id, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)&transform))) goto failed; check_interface(transform, &IID_IMFTransform, TRUE); check_interface(transform, &IID_IMediaObject, FALSE); check_interface(transform, &IID_IPropertyStore, FALSE); check_interface(transform, &IID_IPropertyBag, FALSE); check_mft_optional_methods(transform, 1); check_mft_get_attributes(transform, expect_transform_attributes, TRUE); check_mft_get_input_stream_info(transform, S_OK, &input_info); check_mft_get_output_stream_info(transform, S_OK, &initial_output_info); hr = IMFTransform_GetAttributes(transform, &attributes); ok(hr == S_OK, "GetAttributes returned %#lx\n", hr); hr = IMFAttributes_SetUINT32(attributes, &MF_LOW_LATENCY, 1); ok(hr == S_OK, "SetUINT32 returned %#lx\n", hr); IMFAttributes_Release(attributes); /* no output type is available before an input type is set */ hr = IMFTransform_GetOutputAvailableType(transform, 0, 0, &media_type); ok(hr == MF_E_TRANSFORM_TYPE_NOT_SET, "GetOutputAvailableType returned %#lx\n", hr); flags = 0xdeadbeef; hr = IMFTransform_GetInputStatus(transform, 0, &flags); ok(hr == MF_E_TRANSFORM_TYPE_NOT_SET, "Got %#lx\n", hr); ok(flags == 0xdeadbeef, "Got flags %#lx.\n", flags); /* 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); /* check available input types */ i = -1; while (SUCCEEDED(hr = IMFTransform_GetInputAvailableType(transform, 0, ++i, &media_type))) { winetest_push_context("in %lu", i); ok(hr == S_OK, "GetInputAvailableType returned %#lx\n", hr); check_media_type(media_type, default_inputs[i], -1); ret = IMFMediaType_Release(media_type); ok(ret == 0, "Release returned %lu\n", ret); winetest_pop_context(); } ok(hr == MF_E_NO_MORE_TYPES, "GetInputAvailableType returned %#lx\n", hr); ok(i == 2 || broken(i == 1) /* Win7 */, "%lu input media types\n", i); 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_stream_info(transform, S_OK, &input_info); check_mft_get_output_stream_info(transform, S_OK, &output_info); /* output types can now be enumerated (though they are actually the same for all input types) */ i = -1; while (SUCCEEDED(hr = IMFTransform_GetOutputAvailableType(transform, 0, ++i, &media_type))) { winetest_push_context("out %lu", i); ok(hr == S_OK, "GetOutputAvailableType returned %#lx\n", hr); check_media_type(media_type, default_outputs[i], -1); ret = IMFMediaType_Release(media_type); ok(ret == 0, "Release returned %lu\n", ret); winetest_pop_context(); } ok(hr == MF_E_NO_MORE_TYPES, "GetOutputAvailableType returned %#lx\n", hr); ok(i == 5, "%lu output media types\n", i); 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 that the output media type we've selected don't change the enumeration */ i = -1; while (SUCCEEDED(hr = IMFTransform_GetOutputAvailableType(transform, 0, ++i, &media_type))) { winetest_push_context("out %lu", i); ok(hr == S_OK, "GetOutputAvailableType returned %#lx\n", hr); check_media_type(media_type, default_outputs[i], -1); ret = IMFMediaType_Release(media_type); ok(ret == 0, "Release returned %lu\n", ret); winetest_pop_context(); } ok(hr == MF_E_NO_MORE_TYPES, "GetOutputAvailableType returned %#lx\n", hr); ok(i == 5, "%lu output media types\n", i); check_mft_get_input_stream_info(transform, S_OK, &input_info); check_mft_get_output_stream_info(transform, S_OK, &output_info); load_resource(L"h264data.bin", &h264_encoded_data, &h264_encoded_data_len); /* As output_info.dwFlags doesn't have MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES * IMFTransform_ProcessOutput needs a sample or returns an error */ hr = check_mft_process_output(transform, NULL, &output_status); ok(hr == E_INVALIDARG || hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); ok(output_status == 0, "got output[0].dwStatus %#lx\n", output_status); i = 0; input_sample = next_h264_sample(&h264_encoded_data, &h264_encoded_data_len); while (1) { output_sample = create_sample(NULL, output_info.cbSize); hr = check_mft_process_output(transform, output_sample, &output_status); if (hr != MF_E_TRANSFORM_NEED_MORE_INPUT) break; ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); ok(output_status == 0, "got output[0].dwStatus %#lx\n", output_status); hr = IMFSample_GetTotalLength(output_sample, &length); ok(hr == S_OK, "GetTotalLength returned %#lx\n", hr); ok(length == 0, "got length %lu\n", length); ret = IMFSample_Release(output_sample); ok(ret == 0, "Release returned %lu\n", ret); flags = 0; hr = IMFTransform_GetInputStatus(transform, 0, &flags); ok(hr == S_OK, "Got %#lx\n", hr); ok(flags == MFT_INPUT_STATUS_ACCEPT_DATA, "Got flags %#lx.\n", flags); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == S_OK, "ProcessInput returned %#lx\n", hr); ret = IMFSample_Release(input_sample); ok(ret <= 1, "Release returned %lu\n", ret); input_sample = next_h264_sample(&h264_encoded_data, &h264_encoded_data_len); flags = 0; hr = IMFTransform_GetInputStatus(transform, 0, &flags); ok(hr == S_OK, "Got %#lx\n", hr); ok(flags == MFT_INPUT_STATUS_ACCEPT_DATA, "Got flags %#lx.\n", flags); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == S_OK, "ProcessInput returned %#lx\n", hr); ret = IMFSample_Release(input_sample); ok(ret <= 1, "Release returned %lu\n", ret); input_sample = next_h264_sample(&h264_encoded_data, &h264_encoded_data_len); i++; hr = IMFTransform_ProcessMessage(transform, MFT_MESSAGE_COMMAND_DRAIN, 0); ok(hr == S_OK, "ProcessMessage returned %#lx\n", hr); hr = IMFTransform_ProcessMessage(transform, MFT_MESSAGE_COMMAND_DRAIN, 0); 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(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); ok(hr == S_OK, "GetTotalLength returned %#lx\n", hr); ok(length == 0, "got length %lu\n", length); ret = IMFSample_Release(output_sample); ok(ret == 0, "Release returned %lu\n", ret); check_mft_get_input_stream_info(transform, S_OK, &input_info); check_mft_get_output_stream_info(transform, S_OK, &actual_output_info); i = -1; while (SUCCEEDED(hr = IMFTransform_GetOutputAvailableType(transform, 0, ++i, &media_type))) { winetest_push_context("out %lu", i); ok(hr == S_OK, "GetOutputAvailableType returned %#lx\n", hr); check_media_type(media_type, actual_outputs[i], -1); ret = IMFMediaType_Release(media_type); ok(ret == 0, "Release returned %lu\n", ret); winetest_pop_context(); } ok(hr == MF_E_NO_MORE_TYPES, "GetOutputAvailableType returned %#lx\n", hr); 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); hr = MFCreateCollection(&output_samples); ok(hr == S_OK, "MFCreateCollection returned %#lx\n", hr); output_sample = create_sample(NULL, output_info.cbSize); hr = check_mft_process_output(transform, output_sample, &output_status); ok(hr == S_OK, "ProcessOutput returned %#lx\n", hr); ok(output_status == 0, "got output[0].dwStatus %#lx\n", output_status); hr = IMFCollection_AddElement(output_samples, (IUnknown *)output_sample); ok(hr == S_OK, "AddElement returned %#lx\n", hr); ref = IMFSample_Release(output_sample); ok(ref == 1, "Release returned %ld\n", ref); 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); /* we can change it, but only with the correct frame size */ hr = MFCreateMediaType(&media_type); ok(hr == S_OK, "MFCreateMediaType returned %#lx\n", hr); init_media_type(media_type, output_type_desc, -1); hr = IMFTransform_SetOutputType(transform, 0, media_type, 0); ok(hr == MF_E_INVALIDMEDIATYPE, "SetOutputType returned %#lx.\n", hr); init_media_type(media_type, new_output_type_desc, -1); hr = IMFTransform_SetOutputType(transform, 0, media_type, 0); ok(hr == S_OK, "SetOutputType returned %#lx.\n", hr); 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); output_sample = create_sample(NULL, actual_width * actual_height * 2); hr = check_mft_process_output(transform, output_sample, &output_status); todo_wine ok(hr == MF_E_TRANSFORM_STREAM_CHANGE, "ProcessOutput returned %#lx\n", hr); todo_wine ok(output_status == MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE, "got output[0].dwStatus %#lx\n", output_status); while (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { ok(output_status == 0, "got output[0].dwStatus %#lx\n", output_status); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == S_OK, "ProcessInput returned %#lx\n", hr); ret = IMFSample_Release(input_sample); ok(ret <= 1, "Release returned %lu\n", ret); input_sample = next_h264_sample(&h264_encoded_data, &h264_encoded_data_len); hr = check_mft_process_output(transform, output_sample, &output_status); } 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); ok(hr == S_OK, "GetTotalLength returned %#lx\n", hr); ok(length == 0, "got length %lu\n", length); ret = IMFSample_Release(output_sample); ok(ret == 0, "Release returned %lu\n", ret); hr = MFCreateCollection(&output_samples); ok(hr == S_OK, "MFCreateCollection returned %#lx\n", hr); output_sample = create_sample(NULL, actual_width * actual_height * 2); hr = check_mft_process_output(transform, output_sample, &output_status); ok(hr == S_OK, "ProcessOutput returned %#lx\n", hr); ok(output_status == 0, "got output[0].dwStatus %#lx\n", output_status); hr = IMFCollection_AddElement(output_samples, (IUnknown *)output_sample); ok(hr == S_OK, "AddElement returned %#lx\n", hr); ref = IMFSample_Release(output_sample); ok(ref == 1, "Release returned %ld\n", ref); ret = check_mf_sample_collection(output_samples, &expect_output_sample_i420, L"i420frame.bmp"); todo_wine /* wg_transform_set_output_format() should convert already processed samples instead of dropping */ ok(ret == 0, "got %lu%% diff\n", ret); IMFCollection_Release(output_samples); output_sample = create_sample(NULL, actual_width * actual_height * 2); hr = check_mft_process_output(transform, output_sample, &output_status); todo_wine_if(hr == S_OK) /* when VA-API plugin is used */ ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); ok(output_status == 0, "got output[0].dwStatus %#lx\n", output_status); ret = IMFSample_Release(output_sample); ok(ret == 0, "Release returned %lu\n", ret); do { flags = 0; hr = IMFTransform_GetInputStatus(transform, 0, &flags); ok(hr == S_OK, "Got %#lx\n", hr); ok(flags == MFT_INPUT_STATUS_ACCEPT_DATA, "Got flags %#lx.\n", flags); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == S_OK || hr == MF_E_NOTACCEPTING, "Got %#lx\n", hr); input_sample = next_h264_sample(&h264_encoded_data, &h264_encoded_data_len); } while (hr == S_OK); ok(hr == MF_E_NOTACCEPTING, "Got %#lx\n", hr); flags = 0; hr = IMFTransform_GetInputStatus(transform, 0, &flags); ok(hr == S_OK, "Got %#lx\n", hr); ok(flags == MFT_INPUT_STATUS_ACCEPT_DATA, "Got flags %#lx.\n", flags); ret = IMFTransform_Release(transform); ok(ret == 0, "Release returned %lu\n", ret); ret = IMFSample_Release(input_sample); ok(ret == 0, "Release returned %lu\n", ret); failed: winetest_pop_context(); CoUninitialize(); } static void test_h264_decoder_concat_streams(void) { const struct buffer_desc output_buffer_desc[] = { {.length = 0x3600}, {.length = 0x4980}, }; const struct attribute_desc output_sample_attributes[] = { ATTR_UINT32(MFSampleExtension_CleanPoint, 1), {0}, }; const struct sample_desc output_sample_desc[] = { { .attributes = output_sample_attributes + 0, .sample_time = 0, .sample_duration = 400000, .buffer_count = 1, .buffers = output_buffer_desc + 0, .repeat_count = 29, }, { .attributes = output_sample_attributes + 0, .sample_time = 12000000, .sample_duration = 400000, .buffer_count = 1, .buffers = output_buffer_desc + 1, .repeat_count = 29, }, { .attributes = output_sample_attributes + 0, .sample_time = 0, .sample_duration = 400000, .buffer_count = 1, .buffers = output_buffer_desc + 0, .repeat_count = 29, .todo_time = TRUE, }, { .attributes = output_sample_attributes + 0, .sample_time = 12000000, .sample_duration = 400000, .buffer_count = 1, .buffers = output_buffer_desc + 1, .repeat_count = 6, .todo_time = TRUE, }, {0}, }; const WCHAR *filenames[] = { L"h264data-1.bin", L"h264data-2.bin", L"h264data-1.bin", L"h264data-2.bin", NULL, }; DWORD output_status, input_count, output_count; const WCHAR **filename = filenames; const BYTE *h264_encoded_data; IMFCollection *output_samples; ULONG h264_encoded_data_len; IMFAttributes *attributes; IMFMediaType *media_type; IMFTransform *transform; IMFSample *input_sample; HRESULT hr; LONG ret; hr = CoInitialize(NULL); ok(hr == S_OK, "Failed to initialize, hr %#lx.\n", hr); if (FAILED(hr = CoCreateInstance(&CLSID_MSH264DecoderMFT, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)&transform))) goto failed; ok(hr == S_OK, "hr %#lx\n", hr); hr = IMFTransform_GetAttributes(transform, &attributes); ok(hr == S_OK, "GetAttributes returned %#lx\n", hr); hr = IMFAttributes_SetUINT32(attributes, &MF_LOW_LATENCY, 1); ok(hr == S_OK, "SetUINT32 returned %#lx\n", hr); IMFAttributes_Release(attributes); hr = IMFTransform_GetInputAvailableType(transform, 0, 0, &media_type); ok(hr == S_OK, "got %#lx\n", hr); hr = IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_RATE, (UINT64)25000 << 32 | 1000); ok(hr == S_OK, "got %#lx\n", hr); hr = IMFTransform_SetInputType(transform, 0, media_type, 0); ok(hr == S_OK, "got %#lx\n", hr); IMFMediaType_Release(media_type); hr = IMFTransform_GetOutputAvailableType(transform, 0, 0, &media_type); ok(hr == S_OK, "got %#lx\n", hr); hr = IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_RATE, (UINT64)50000 << 32 | 1000); ok(hr == S_OK, "got %#lx\n", hr); hr = IMFTransform_SetOutputType(transform, 0, media_type, 0); ok(hr == S_OK, "got %#lx\n", hr); IMFMediaType_Release(media_type); load_resource(*filename, &h264_encoded_data, &h264_encoded_data_len); hr = MFCreateCollection(&output_samples); ok(hr == S_OK, "MFCreateCollection returned %#lx\n", hr); input_count = 0; input_sample = next_h264_sample(&h264_encoded_data, &h264_encoded_data_len); while (input_sample) { MFT_OUTPUT_STREAM_INFO info = {0}; hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == S_OK || hr == MF_E_NOTACCEPTING, "ProcessInput returned %#lx\n", hr); if (hr == S_OK && input_count < 2) { MFT_OUTPUT_DATA_BUFFER data = {.pSample = create_sample(NULL, 1)}; hr = IMFTransform_ProcessOutput(transform, 0, 1, &data, &output_status); ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); IMFSample_Release(data.pSample); hr = S_OK; } if (hr == S_OK) { IMFSample_Release(input_sample); hr = IMFCollection_GetElementCount(output_samples, &output_count); ok(hr == S_OK, "GetElementCount returned %#lx\n", hr); if (output_count == 96) { hr = IMFTransform_ProcessMessage(transform, MFT_MESSAGE_COMMAND_FLUSH, 0); ok(hr == S_OK, "ProcessMessage returned %#lx\n", hr); } if (h264_encoded_data_len <= 4 && *++filename) { load_resource(*filename, &h264_encoded_data, &h264_encoded_data_len); if (!wcscmp(*filename, L"h264data-1.bin")) { hr = IMFTransform_ProcessMessage(transform, MFT_MESSAGE_COMMAND_DRAIN, 0); ok(hr == S_OK, "ProcessMessage returned %#lx\n", hr); } } if (h264_encoded_data_len > 4) { input_count++; input_sample = next_h264_sample(&h264_encoded_data, &h264_encoded_data_len); } else { hr = IMFTransform_ProcessMessage(transform, MFT_MESSAGE_COMMAND_DRAIN, 0); ok(hr == S_OK, "ProcessMessage returned %#lx\n", hr); input_sample = NULL; } } hr = IMFTransform_GetOutputStreamInfo(transform, 0, &info); ok(hr == S_OK, "GetOutputStreamInfo returned %#lx\n", hr); while (hr != MF_E_TRANSFORM_NEED_MORE_INPUT) { MFT_OUTPUT_DATA_BUFFER data = {.pSample = create_sample(NULL, info.cbSize)}; hr = IMFTransform_ProcessOutput(transform, 0, 1, &data, &output_status); todo_wine_if(hr == 0xd0000001) ok(hr == S_OK || hr == MF_E_TRANSFORM_NEED_MORE_INPUT || hr == MF_E_TRANSFORM_STREAM_CHANGE, "ProcessOutput returned %#lx\n", hr); if (hr == S_OK) { hr = IMFCollection_AddElement(output_samples, (IUnknown *)data.pSample); ok(hr == S_OK, "AddElement returned %#lx\n", hr); } if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { hr = IMFTransform_GetOutputAvailableType(transform, 0, 0, &media_type); ok(hr == S_OK, "got %#lx\n", hr); hr = IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_RATE, (UINT64)50000 << 32 | 1000); ok(hr == S_OK, "got %#lx\n", hr); hr = IMFTransform_SetOutputType(transform, 0, media_type, 0); ok(hr == S_OK, "got %#lx\n", hr); IMFMediaType_Release(media_type); hr = IMFTransform_GetOutputStreamInfo(transform, 0, &info); ok(hr == S_OK, "GetOutputStreamInfo returned %#lx\n", hr); } IMFSample_Release(data.pSample); } } hr = IMFCollection_GetElementCount(output_samples, &output_count); ok(hr == S_OK, "GetElementCount returned %#lx\n", hr); ok(output_count == 96 || broken(output_count == 120) /* Win7 sometimes */, "GetElementCount returned %lu\n", output_count); while (broken(output_count > 96)) /* Win7 sometimes */ { IMFSample *sample; hr = IMFCollection_RemoveElement(output_samples, --output_count, (IUnknown **)&sample); ok(hr == S_OK, "GetElementCount returned %#lx\n", hr); IMFSample_Release(sample); } ret = check_mf_sample_collection(output_samples, output_sample_desc, NULL); ok(ret == 0, "got %lu%% diff\n", ret); ret = IMFTransform_Release(transform); ok(ret == 0, "Release returned %lu\n", ret); failed: CoUninitialize(); } static void test_audio_convert(void) { const GUID *const class_id = &CLSID_CResamplerMediaObject; const struct transform_info expect_mft_info = { .name = L"Resampler MFT", .major_type = &MFMediaType_Audio, .inputs = { {.subtype = &MFAudioFormat_PCM}, {.subtype = &MFAudioFormat_Float}, }, .outputs = { {.subtype = &MFAudioFormat_PCM}, {.subtype = &MFAudioFormat_Float}, }, }; const struct transform_info expect_dmo_info = { .name = L"Resampler DMO", .major_type = &MEDIATYPE_Audio, .inputs = { {.subtype = &MEDIASUBTYPE_PCM}, {.subtype = &MEDIASUBTYPE_IEEE_FLOAT}, }, .outputs = { {.subtype = &MEDIASUBTYPE_PCM}, {.subtype = &MEDIASUBTYPE_IEEE_FLOAT}, }, }; static const media_type_desc expect_available_inputs[] = { { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_Float), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), }, }; static const media_type_desc expect_available_outputs[] = { { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_Float), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_Float), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 32), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 48000), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 384000), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 8), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_UINT32(MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 48000), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 192000), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 4), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_UINT32(MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1), }, }; static const struct attribute_desc input_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_Float, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 32, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 22050, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 2 * (32 / 8), .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 2 * (32 / 8) * 22050, .required = TRUE), {0}, }; const struct attribute_desc output_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 2 * (16 / 8), .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 2 * (16 / 8) * 44100, .required = TRUE), {0}, }; static const struct attribute_desc expect_input_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_Float), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 32), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), 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), {0}, }; const struct attribute_desc expect_output_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, 16), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 4), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 44100 * 4), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1, .todo = TRUE), ATTR_UINT32(MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1, .todo = TRUE), {0}, }; const MFT_OUTPUT_STREAM_INFO output_info = { .cbSize = 4, .cbAlignment = 1, }; const MFT_INPUT_STREAM_INFO input_info = { .cbSize = 8, .cbAlignment = 1, }; static const ULONG audioconv_block_size = 0x4000; const struct buffer_desc output_buffer_desc[] = { {.length = audioconv_block_size, .compare = compare_pcm16}, {.length = 0x3dd8, .compare = compare_pcm16, .todo_length = TRUE}, {.length = 0xfc, .compare = compare_pcm16}, }; const struct attribute_desc output_sample_attributes[] = { ATTR_UINT32(mft_output_sample_incomplete, 1), ATTR_UINT32(MFSampleExtension_CleanPoint, has_video_processor /* 0 on Win7 */, .todo = TRUE), {0}, }; const struct sample_desc output_sample_desc[] = { { .attributes = output_sample_attributes + 0, .sample_time = 0, .sample_duration = 928798, .buffer_count = 1, .buffers = output_buffer_desc + 0, .repeat_count = 9, }, { .attributes = output_sample_attributes + 1, /* not MFT_OUTPUT_DATA_BUFFER_INCOMPLETE */ .sample_time = 9287980, .sample_duration = 897506, .todo_duration = 897280, .buffer_count = 1, .buffers = output_buffer_desc + 1, .todo_length = TRUE, }, { .attributes = output_sample_attributes + 0, .sample_time = 10185486, .sample_duration = 14286, .buffer_count = 1, .buffers = output_buffer_desc + 2, }, }; MFT_REGISTER_TYPE_INFO output_type = {MFMediaType_Audio, MFAudioFormat_PCM}; MFT_REGISTER_TYPE_INFO input_type = {MFMediaType_Audio, MFAudioFormat_Float}; IMFSample *input_sample, *output_sample; IMFCollection *output_samples; DWORD length, output_status; IMFMediaType *media_type; IMFTransform *transform; const BYTE *audio_data; ULONG audio_data_len; ULONG i, ret, ref; HRESULT hr; hr = CoInitialize(NULL); ok(hr == S_OK, "Failed to initialize, hr %#lx.\n", hr); winetest_push_context("resampler"); if (!check_mft_enum(MFT_CATEGORY_AUDIO_EFFECT, &input_type, &output_type, class_id)) goto failed; check_mft_get_info(class_id, &expect_mft_info); check_dmo_get_info(class_id, &expect_dmo_info); if (FAILED(hr = CoCreateInstance(class_id, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)&transform))) goto failed; check_interface(transform, &IID_IMFTransform, TRUE); check_interface(transform, &IID_IMediaObject, TRUE); check_interface(transform, &IID_IPropertyStore, TRUE); check_interface(transform, &IID_IPropertyBag, TRUE); /* check_interface(transform, &IID_IWMResamplerProps, TRUE); */ check_mft_optional_methods(transform, 1); check_mft_get_attributes(transform, NULL, 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); i = -1; while (SUCCEEDED(hr = IMFTransform_GetOutputAvailableType(transform, 0, ++i, &media_type))) { winetest_push_context("out %lu", i); ok(hr == S_OK, "GetOutputAvailableType returned %#lx\n", hr); check_media_type(media_type, expect_available_outputs[i], -1); ret = IMFMediaType_Release(media_type); ok(ret == 0, "Release returned %lu\n", ret); winetest_pop_context(); } ok(hr == MF_E_NO_MORE_TYPES, "GetOutputAvailableType returned %#lx\n", hr); ok(i == 4, "%lu output media types\n", i); i = -1; while (SUCCEEDED(hr = IMFTransform_GetInputAvailableType(transform, 0, ++i, &media_type))) { winetest_push_context("in %lu", i); ok(hr == S_OK, "GetInputAvailableType returned %#lx\n", hr); check_media_type(media_type, expect_available_inputs[i], -1); ret = IMFMediaType_Release(media_type); ok(ret == 0, "Release returned %lu\n", ret); winetest_pop_context(); } ok(hr == MF_E_NO_MORE_TYPES, "GetInputAvailableType returned %#lx\n", hr); ok(i == 2, "%lu input media types\n", i); /* 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); 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_stream_info(transform, MF_E_TRANSFORM_TYPE_NOT_SET, NULL); check_mft_get_output_stream_info(transform, MF_E_TRANSFORM_TYPE_NOT_SET, NULL); /* check new output media types */ i = -1; while (SUCCEEDED(hr = IMFTransform_GetOutputAvailableType(transform, 0, ++i, &media_type))) { winetest_push_context("out %lu", i); ok(hr == S_OK, "GetOutputAvailableType returned %#lx\n", hr); check_media_type(media_type, expect_available_outputs[i], -1); ret = IMFMediaType_Release(media_type); ok(ret == 0, "Release returned %lu\n", ret); winetest_pop_context(); } ok(hr == MF_E_NO_MORE_TYPES, "GetOutputAvailableType returned %#lx\n", hr); ok(i == 4, "%lu output media types\n", i); 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_input_stream_info(transform, S_OK, &input_info); check_mft_get_output_stream_info(transform, S_OK, &output_info); load_resource(L"audiodata.bin", &audio_data, &audio_data_len); ok(audio_data_len == 179928, "got length %lu\n", audio_data_len); input_sample = create_sample(audio_data, audio_data_len); hr = IMFSample_SetSampleTime(input_sample, 0); ok(hr == S_OK, "SetSampleTime returned %#lx\n", hr); hr = IMFSample_SetSampleDuration(input_sample, 10000000); ok(hr == S_OK, "SetSampleDuration returned %#lx\n", hr); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == S_OK, "ProcessInput returned %#lx\n", hr); hr = IMFTransform_ProcessMessage(transform, MFT_MESSAGE_COMMAND_DRAIN, 0); ok(hr == S_OK, "ProcessMessage returned %#lx\n", hr); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == MF_E_NOTACCEPTING, "ProcessInput returned %#lx\n", hr); ret = IMFSample_Release(input_sample); ok(ret <= 1, "Release returned %ld\n", ret); hr = MFCreateCollection(&output_samples); ok(hr == S_OK, "MFCreateCollection returned %#lx\n", hr); output_sample = create_sample(NULL, audioconv_block_size); for (i = 0; SUCCEEDED(hr = check_mft_process_output(transform, output_sample, &output_status)); i++) { winetest_push_context("%lu", i); ok(hr == S_OK, "ProcessOutput returned %#lx\n", hr); ok(!(output_status & ~MFT_OUTPUT_DATA_BUFFER_INCOMPLETE), "got output[0].dwStatus %#lx\n", output_status); hr = IMFCollection_AddElement(output_samples, (IUnknown *)output_sample); ok(hr == S_OK, "AddElement returned %#lx\n", hr); ref = IMFSample_Release(output_sample); ok(ref == 1, "Release returned %ld\n", ref); output_sample = create_sample(NULL, audioconv_block_size); winetest_pop_context(); } ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); ok(output_status == 0, "got output[0].dwStatus %#lx\n", output_status); ret = IMFSample_Release(output_sample); ok(ret == 0, "Release returned %lu\n", ret); todo_wine ok(i == 12 || broken(i == 11) /* Win7 */, "got %lu output samples\n", i); ret = check_mf_sample_collection(output_samples, output_sample_desc, L"audioconvdata.bin"); ok(ret == 0, "got %lu%% diff\n", ret); IMFCollection_Release(output_samples); output_sample = create_sample(NULL, audioconv_block_size); hr = check_mft_process_output(transform, output_sample, &output_status); ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); ok(output_status == 0, "got output[0].dwStatus %#lx\n", output_status); hr = IMFSample_GetTotalLength(output_sample, &length); ok(hr == S_OK, "GetTotalLength returned %#lx\n", hr); ok(length == 0, "got length %lu\n", length); ret = IMFSample_Release(output_sample); ok(ret == 0, "Release returned %lu\n", ret); ret = IMFTransform_Release(transform); ok(ret == 0, "Release returned %lu\n", ret); failed: winetest_pop_context(); CoUninitialize(); } const GUID *wmv_decoder_output_subtypes[] = { &MEDIASUBTYPE_NV12, &MEDIASUBTYPE_YV12, &MEDIASUBTYPE_IYUV, &MEDIASUBTYPE_I420, &MEDIASUBTYPE_YUY2, &MEDIASUBTYPE_UYVY, &MEDIASUBTYPE_YVYU, &MEDIASUBTYPE_NV11, &MEDIASUBTYPE_RGB32, &MEDIASUBTYPE_RGB24, &MEDIASUBTYPE_RGB565, &MEDIASUBTYPE_RGB555, &MEDIASUBTYPE_RGB8, }; static void test_wmv_encoder(void) { const GUID *const class_id = &CLSID_CWMVXEncMediaObject; const struct transform_info expect_mft_info = { .name = L"WMVideo8 Encoder MFT", .major_type = &MFMediaType_Video, .inputs = { {.subtype = &MFVideoFormat_IYUV}, {.subtype = &MFVideoFormat_I420}, {.subtype = &MFVideoFormat_YV12}, {.subtype = &MFVideoFormat_NV11}, {.subtype = &MFVideoFormat_NV12}, {.subtype = &MFVideoFormat_YUY2}, {.subtype = &MFVideoFormat_UYVY}, {.subtype = &MFVideoFormat_YVYU}, {.subtype = &MFVideoFormat_YVU9}, {.subtype = &DMOVideoFormat_RGB32}, {.subtype = &DMOVideoFormat_RGB24}, {.subtype = &DMOVideoFormat_RGB565}, {.subtype = &DMOVideoFormat_RGB555}, {.subtype = &DMOVideoFormat_RGB8}, }, .outputs = { {.subtype = &MFVideoFormat_WMV1}, {.subtype = &MFVideoFormat_WMV2}, }, }; const struct transform_info expect_dmo_info = { .name = L"WMVideo8 Encoder DMO", .major_type = &MEDIATYPE_Video, .inputs = { {.subtype = &MEDIASUBTYPE_IYUV}, {.subtype = &MEDIASUBTYPE_I420}, {.subtype = &MEDIASUBTYPE_YV12}, {.subtype = &MEDIASUBTYPE_NV11}, {.subtype = &MEDIASUBTYPE_NV12}, {.subtype = &MEDIASUBTYPE_YUY2}, {.subtype = &MEDIASUBTYPE_UYVY}, {.subtype = &MEDIASUBTYPE_YVYU}, {.subtype = &MEDIASUBTYPE_YVU9}, {.subtype = &MEDIASUBTYPE_RGB32}, {.subtype = &MEDIASUBTYPE_RGB24}, {.subtype = &MEDIASUBTYPE_RGB565}, {.subtype = &MEDIASUBTYPE_RGB555}, {.subtype = &MEDIASUBTYPE_RGB8}, }, .outputs = { {.subtype = &MEDIASUBTYPE_WMV1}, {.subtype = &MEDIASUBTYPE_WMV2}, }, }; static const media_type_desc expect_available_inputs[] = { { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_IYUV), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_TRANSFER_FUNCTION, 0), ATTR_UINT32(MF_MT_VIDEO_PRIMARIES, 0), ATTR_UINT32(MF_MT_YUV_MATRIX, 0), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_I420), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_TRANSFER_FUNCTION, 0), ATTR_UINT32(MF_MT_VIDEO_PRIMARIES, 0), ATTR_UINT32(MF_MT_YUV_MATRIX, 0), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_YV12), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_TRANSFER_FUNCTION, 0), ATTR_UINT32(MF_MT_VIDEO_PRIMARIES, 0), ATTR_UINT32(MF_MT_YUV_MATRIX, 0), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV11), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_TRANSFER_FUNCTION, 0), ATTR_UINT32(MF_MT_VIDEO_PRIMARIES, 0), ATTR_UINT32(MF_MT_YUV_MATRIX, 0), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_TRANSFER_FUNCTION, 0), ATTR_UINT32(MF_MT_VIDEO_PRIMARIES, 0), ATTR_UINT32(MF_MT_YUV_MATRIX, 0), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_YUY2), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_TRANSFER_FUNCTION, 0), ATTR_UINT32(MF_MT_VIDEO_PRIMARIES, 0), ATTR_UINT32(MF_MT_YUV_MATRIX, 0), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_UYVY), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_TRANSFER_FUNCTION, 0), ATTR_UINT32(MF_MT_VIDEO_PRIMARIES, 0), ATTR_UINT32(MF_MT_YUV_MATRIX, 0), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_YVYU), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_TRANSFER_FUNCTION, 0), ATTR_UINT32(MF_MT_VIDEO_PRIMARIES, 0), ATTR_UINT32(MF_MT_YUV_MATRIX, 0), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_TRANSFER_FUNCTION, 0), ATTR_UINT32(MF_MT_VIDEO_PRIMARIES, 0), ATTR_UINT32(MF_MT_YUV_MATRIX, 0), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB24), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_TRANSFER_FUNCTION, 0), ATTR_UINT32(MF_MT_VIDEO_PRIMARIES, 0), ATTR_UINT32(MF_MT_YUV_MATRIX, 0), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB565), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_TRANSFER_FUNCTION, 0), ATTR_UINT32(MF_MT_VIDEO_PRIMARIES, 0), ATTR_UINT32(MF_MT_YUV_MATRIX, 0), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB555), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_TRANSFER_FUNCTION, 0), ATTR_UINT32(MF_MT_VIDEO_PRIMARIES, 0), ATTR_UINT32(MF_MT_YUV_MATRIX, 0), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MEDIASUBTYPE_RGB8), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_TRANSFER_FUNCTION, 0), ATTR_UINT32(MF_MT_VIDEO_PRIMARIES, 0), ATTR_UINT32(MF_MT_YUV_MATRIX, 0), }, }; static const media_type_desc expect_available_outputs[] = { { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_WMV1), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_UINT32(MF_MT_TRANSFER_FUNCTION, 0), ATTR_UINT32(MF_MT_VIDEO_PRIMARIES, 0), ATTR_UINT32(MF_MT_YUV_MATRIX, 0), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_WMV2), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_UINT32(MF_MT_TRANSFER_FUNCTION, 0), ATTR_UINT32(MF_MT_VIDEO_PRIMARIES, 0), ATTR_UINT32(MF_MT_YUV_MATRIX, 0), }, }; static const DWORD actual_width = 96, actual_height = 96; const struct attribute_desc input_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_RATE, 30000, 1001, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), /* required for SetOutputType */ {0}, }; const struct attribute_desc output_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_WMV1, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_RATE, 30000, 1001, .required = TRUE), ATTR_UINT32(MF_MT_AVG_BITRATE, 193540, .required = TRUE), {0}, }; const struct attribute_desc expect_input_type_desc[] = { 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, 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_FIXED_SIZE_SAMPLES, 1), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), {0}, }; const struct attribute_desc expect_output_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_WMV1), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), ATTR_RATIO(MF_MT_FRAME_RATE, 30000, 1001), ATTR_UINT32(MF_MT_AVG_BITRATE, 193540), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), {0}, }; static const MFT_OUTPUT_STREAM_INFO empty_output_info = { .dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER, }; static const MFT_INPUT_STREAM_INFO empty_input_info = { .dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER, }; static const MFT_OUTPUT_STREAM_INFO expect_output_info = { .dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER, .cbSize = 0x8000, .cbAlignment = 1, }; static const MFT_INPUT_STREAM_INFO expect_input_info = { .dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER, .cbSize = 0x3600, .cbAlignment = 1, }; const struct buffer_desc output_buffer_desc[] = { {.length = -1 /* variable */}, }; const struct attribute_desc output_sample_attributes_key[] = { ATTR_UINT32(MFSampleExtension_CleanPoint, 1), {0}, }; const struct attribute_desc output_sample_attributes[] = { ATTR_UINT32(MFSampleExtension_CleanPoint, 0), {0}, }; const struct sample_desc output_sample_desc[] = { { .attributes = output_sample_attributes_key, .sample_time = 0, .sample_duration = 333333, .buffer_count = 1, .buffers = output_buffer_desc, }, { .attributes = output_sample_attributes, .sample_time = 333333, .sample_duration = 333333, .buffer_count = 1, .buffers = output_buffer_desc, .repeat_count = 4 }, }; MFT_REGISTER_TYPE_INFO output_type = {MFMediaType_Video, MFVideoFormat_WMV1}; MFT_REGISTER_TYPE_INFO input_type = {MFMediaType_Video, MFVideoFormat_NV12}; IMFSample *input_sample, *output_sample; DWORD status, length, output_status; MFT_OUTPUT_DATA_BUFFER output; IMFCollection *output_samples; const BYTE *nv12frame_data; IMFMediaType *media_type; ULONG nv12frame_data_len; IMFTransform *transform; ULONG i, ret; HRESULT hr; LONG ref; hr = CoInitialize(NULL); ok(hr == S_OK, "Failed to initialize, hr %#lx.\n", hr); winetest_push_context("wmvenc"); if (!check_mft_enum(MFT_CATEGORY_VIDEO_ENCODER, &input_type, &output_type, class_id)) goto failed; check_mft_get_info(class_id, &expect_mft_info); check_dmo_get_info(class_id, &expect_dmo_info); if (FAILED(hr = CoCreateInstance(class_id, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)&transform))) goto failed; check_interface(transform, &IID_IMFTransform, TRUE); check_interface(transform, &IID_IMediaObject, TRUE); check_interface(transform, &IID_IPropertyStore, TRUE); check_interface(transform, &IID_IPropertyBag, TRUE); check_mft_optional_methods(transform, 2); check_mft_get_attributes(transform, NULL, FALSE); check_mft_get_input_stream_info(transform, MF_E_TRANSFORM_TYPE_NOT_SET, &empty_input_info); check_mft_get_output_stream_info(transform, MF_E_TRANSFORM_TYPE_NOT_SET, &empty_output_info); i = -1; while (SUCCEEDED(hr = IMFTransform_GetInputAvailableType(transform, 0, ++i, &media_type))) { winetest_push_context("in 0 %lu", i); ok(hr == S_OK, "GetInputAvailableType returned %#lx\n", hr); ret = IMFMediaType_Release(media_type); ok(ret <= 1, "Release returned %lu\n", ret); winetest_pop_context(); } ok(hr == MF_E_NO_MORE_TYPES, "GetInputAvailableType returned %#lx\n", hr); ok(i == ARRAY_SIZE(expect_available_inputs), "%lu input media types\n", i); i = -1; while (SUCCEEDED(hr = IMFTransform_GetOutputAvailableType(transform, 0, ++i, &media_type))) { winetest_push_context("out %lu", i); ok(hr == S_OK, "GetOutputAvailableType returned %#lx\n", hr); ret = IMFMediaType_Release(media_type); ok(ret <= 1, "Release returned %lu\n", ret); winetest_pop_context(); } ok(hr == MF_E_NO_MORE_TYPES, "GetOutputAvailableType returned %#lx\n", hr); ok(i == ARRAY_SIZE(expect_available_outputs), "%lu output media types\n", i); check_mft_set_output_type(transform, output_type_desc, MF_E_TRANSFORM_TYPE_NOT_SET); check_mft_get_output_current_type(transform, NULL); 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_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_input_stream_info(transform, S_OK, &expect_input_info); check_mft_get_output_stream_info(transform, S_OK, &expect_output_info); if (!has_video_processor) { win_skip("Skipping WMV encoder tests on Win7\n"); goto done; } load_resource(L"nv12frame.bmp", &nv12frame_data, &nv12frame_data_len); /* skip BMP header and RGB data from the dump */ length = *(DWORD *)(nv12frame_data + 2); nv12frame_data_len = nv12frame_data_len - length; nv12frame_data = nv12frame_data + length; ok(nv12frame_data_len == 13824, "got length %lu\n", nv12frame_data_len); hr = MFCreateCollection(&output_samples); ok(hr == S_OK, "MFCreateCollection returned %#lx\n", hr); for (i = 0; i < 5; i++) { input_sample = create_sample(nv12frame_data, nv12frame_data_len); hr = IMFSample_SetSampleTime(input_sample, i * 333333); ok(hr == S_OK, "SetSampleTime returned %#lx\n", hr); hr = IMFSample_SetSampleDuration(input_sample, 333333); ok(hr == S_OK, "SetSampleDuration returned %#lx\n", hr); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == S_OK, "ProcessInput returned %#lx\n", hr); ref = IMFSample_Release(input_sample); ok(ref <= 1, "Release returned %ld\n", ref); output_sample = create_sample(NULL, expect_output_info.cbSize); hr = check_mft_process_output(transform, output_sample, &output_status); ok(hr == S_OK, "ProcessOutput returned %#lx\n", hr); hr = IMFCollection_AddElement(output_samples, (IUnknown *)output_sample); ok(hr == S_OK, "AddElement returned %#lx\n", hr); ref = IMFSample_Release(output_sample); ok(ref == 1, "Release returned %ld\n", ref); } ret = check_mf_sample_collection(output_samples, output_sample_desc, L"wmvencdata.bin"); ok(ret == 0, "got %lu%% diff\n", ret); IMFCollection_Release(output_samples); output_sample = create_sample(NULL, expect_output_info.cbSize); status = 0xdeadbeef; memset(&output, 0, sizeof(output)); output.pSample = output_sample; hr = IMFTransform_ProcessOutput(transform, 0, 1, &output, &status); ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); ok(output.pSample == output_sample, "got pSample %p\n", output.pSample); ok(output.dwStatus == 0, "got dwStatus %#lx\n", output.dwStatus); ok(status == 0, "got status %#lx\n", status); hr = IMFSample_GetTotalLength(output.pSample, &length); ok(hr == S_OK, "GetTotalLength returned %#lx\n", hr); ok(length == 0, "got length %lu\n", length); ret = IMFSample_Release(output_sample); ok(ret == 0, "Release returned %lu\n", ret); done: ret = IMFTransform_Release(transform); ok(ret == 0, "Release returned %lu\n", ret); failed: winetest_pop_context(); CoUninitialize(); } static void test_wmv_decoder(void) { const GUID *const class_id = &CLSID_CWMVDecMediaObject; const struct transform_info expect_mft_info = { .name = L"WMVideo Decoder MFT", .major_type = &MFMediaType_Video, .inputs = { {.subtype = &MFVideoFormat_WMV1}, {.subtype = &MFVideoFormat_WMV2}, {.subtype = &MFVideoFormat_WMV3}, {.subtype = &MEDIASUBTYPE_WMVP}, {.subtype = &MEDIASUBTYPE_WVP2}, {.subtype = &MEDIASUBTYPE_WMVR}, {.subtype = &MEDIASUBTYPE_WMVA}, {.subtype = &MFVideoFormat_WVC1}, {.subtype = &MFVideoFormat_VC1S}, }, .outputs = { {.subtype = &MFVideoFormat_YV12}, {.subtype = &MFVideoFormat_YUY2}, {.subtype = &MFVideoFormat_UYVY}, {.subtype = &MFVideoFormat_YVYU}, {.subtype = &MFVideoFormat_NV11}, {.subtype = &MFVideoFormat_NV12}, {.subtype = &DMOVideoFormat_RGB32}, {.subtype = &DMOVideoFormat_RGB24}, {.subtype = &DMOVideoFormat_RGB565}, {.subtype = &DMOVideoFormat_RGB555}, {.subtype = &DMOVideoFormat_RGB8}, }, }; const struct transform_info expect_dmo_info = { .name = L"WMVideo Decoder DMO", .major_type = &MEDIATYPE_Video, .inputs = { {.subtype = &MEDIASUBTYPE_WMV1}, {.subtype = &MEDIASUBTYPE_WMV2}, {.subtype = &MEDIASUBTYPE_WMV3}, {.subtype = &MEDIASUBTYPE_WMVA}, {.subtype = &MEDIASUBTYPE_WVC1}, {.subtype = &MEDIASUBTYPE_WMVP}, {.subtype = &MEDIASUBTYPE_WVP2}, {.subtype = &MFVideoFormat_VC1S}, }, .outputs = { {.subtype = &MEDIASUBTYPE_YV12}, {.subtype = &MEDIASUBTYPE_YUY2}, {.subtype = &MEDIASUBTYPE_UYVY}, {.subtype = &MEDIASUBTYPE_YVYU}, {.subtype = &MEDIASUBTYPE_NV11}, {.subtype = &MEDIASUBTYPE_NV12}, {.subtype = &MEDIASUBTYPE_RGB32}, {.subtype = &MEDIASUBTYPE_RGB24}, {.subtype = &MEDIASUBTYPE_RGB565}, {.subtype = &MEDIASUBTYPE_RGB555}, {.subtype = &MEDIASUBTYPE_RGB8}, }, }; static const struct attribute_desc expect_common_attributes[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), {0}, }; static const media_type_desc expect_available_inputs[] = { {ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_WMV1)}, {ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_WMV2)}, {ATTR_GUID(MF_MT_SUBTYPE, MEDIASUBTYPE_WMVA)}, {ATTR_GUID(MF_MT_SUBTYPE, MEDIASUBTYPE_WMVP)}, {ATTR_GUID(MF_MT_SUBTYPE, MEDIASUBTYPE_WVP2)}, {ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_WMV_Unknown)}, {ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_WVC1)}, {ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_WMV3)}, {ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_VC1S)}, }; static const MFVideoArea actual_aperture = {.Area={96,96}}; static const DWORD actual_width = 96, actual_height = 96; const struct attribute_desc expect_output_attributes[] = { ATTR_BLOB(MF_MT_GEOMETRIC_APERTURE, &actual_aperture, sizeof(actual_aperture)), ATTR_BLOB(MF_MT_MINIMUM_DISPLAY_APERTURE, &actual_aperture, sizeof(actual_aperture)), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_INTERLACE_MODE, 2), {0}, }; const media_type_desc expect_available_outputs[] = { { 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_UINT32(MF_MT_DEFAULT_STRIDE, actual_width), ATTR_UINT32(MF_MT_SAMPLE_SIZE, actual_width * actual_height * 3 / 2), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_YV12), 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_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_IYUV), 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_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_I420), 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_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_YUY2), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, actual_width * 2), ATTR_UINT32(MF_MT_SAMPLE_SIZE, actual_width * actual_height * 2), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_UYVY), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, actual_width * 2), ATTR_UINT32(MF_MT_SAMPLE_SIZE, actual_width * actual_height * 2), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_YVYU), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, actual_width * 2), ATTR_UINT32(MF_MT_SAMPLE_SIZE, actual_width * actual_height * 2), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV11), 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_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, actual_width * 4), ATTR_UINT32(MF_MT_SAMPLE_SIZE, actual_width * actual_height * 4), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB24), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, actual_width * 3), ATTR_UINT32(MF_MT_SAMPLE_SIZE, actual_width * actual_height * 3), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB565), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, actual_width * 2), ATTR_UINT32(MF_MT_SAMPLE_SIZE, actual_width * actual_height * 2), /* ATTR_BLOB(MF_MT_PALETTE, ... with 12 elements), */ }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB555), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, actual_width * 2), ATTR_UINT32(MF_MT_SAMPLE_SIZE, actual_width * actual_height * 2), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB8), 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), /* ATTR_BLOB(MF_MT_PALETTE, ... with 904 elements), */ }, }; const struct attribute_desc expect_attributes[] = { ATTR_UINT32(MF_LOW_LATENCY, 0), ATTR_UINT32(MF_SA_D3D11_AWARE, 1), ATTR_UINT32(MF_SA_D3D_AWARE, 1), ATTR_UINT32(MFT_DECODER_EXPOSE_OUTPUT_TYPES_IN_NATIVE_ORDER, 0), /* more attributes from CODECAPI */ {0}, }; const struct attribute_desc input_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_WMV1, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height, .required = TRUE), {0}, }; const struct attribute_desc output_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height, .required = TRUE), {0}, }; const struct attribute_desc output_type_desc_negative_stride[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height, .required = TRUE), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, -actual_width), {0}, }; const struct attribute_desc output_type_desc_rgb[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height, .required = TRUE), {0}, }; const struct attribute_desc output_type_desc_rgb_negative_stride[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height, .required = TRUE), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, -actual_width * 4), {0}, }; const struct attribute_desc output_type_desc_rgb_positive_stride[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height, .required = TRUE), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, actual_width * 4), {0}, }; const struct attribute_desc expect_input_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_WMV1), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), {0}, }; const struct attribute_desc expect_output_type_desc[] = { 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_UINT32(MF_MT_DEFAULT_STRIDE, actual_width), ATTR_UINT32(MF_MT_SAMPLE_SIZE, actual_width * actual_height * 3 / 2), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_VIDEO_NOMINAL_RANGE, 2), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), {0}, }; const struct attribute_desc expect_output_type_desc_rgb[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, actual_width * 4), ATTR_UINT32(MF_MT_SAMPLE_SIZE, actual_width * actual_height * 4), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_VIDEO_NOMINAL_RANGE, 2), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), {0}, }; const struct attribute_desc expect_output_type_desc_rgb_negative_stride[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, -actual_width * 4), ATTR_UINT32(MF_MT_SAMPLE_SIZE, actual_width * actual_height * 4), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_VIDEO_NOMINAL_RANGE, 2), ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), {0}, }; const MFT_OUTPUT_STREAM_INFO expect_output_info = { .dwFlags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_OUTPUT_STREAM_DISCARDABLE, .cbSize = 0x3600, .cbAlignment = 1, }; const MFT_OUTPUT_STREAM_INFO expect_output_info_rgb = { .dwFlags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_OUTPUT_STREAM_DISCARDABLE, .cbSize = 0x9000, .cbAlignment = 1, }; const MFT_OUTPUT_STREAM_INFO empty_output_info = { .dwFlags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_OUTPUT_STREAM_DISCARDABLE, }; const MFT_INPUT_STREAM_INFO expect_input_info = { .cbSize = 0x3600, .cbAlignment = 1, }; const MFT_INPUT_STREAM_INFO expect_input_info_rgb = { .cbSize = 0x9000, .cbAlignment = 1, }; const struct attribute_desc output_sample_attributes[] = { ATTR_UINT32(MFSampleExtension_CleanPoint, 1), {0}, }; const struct buffer_desc output_buffer_desc_nv12 = { .length = actual_width * actual_height * 3 / 2, .compare = compare_nv12, .dump = dump_nv12, .rect = {.right = 82, .bottom = 84}, }; const struct buffer_desc output_buffer_desc_rgb = { .length = actual_width * actual_height * 4, .compare = compare_rgb32, .dump = dump_rgb32, .rect = {.right = 82, .bottom = 84}, }; const struct sample_desc output_sample_desc_nv12 = { .attributes = output_sample_attributes, .sample_time = 0, .sample_duration = 333333, .buffer_count = 1, .buffers = &output_buffer_desc_nv12, }; const struct sample_desc output_sample_desc_rgb = { .attributes = output_sample_attributes, .sample_time = 0, .sample_duration = 333333, .buffer_count = 1, .buffers = &output_buffer_desc_rgb, }; const struct transform_desc { const struct attribute_desc *output_type_desc; const struct attribute_desc *expect_output_type_desc; const MFT_INPUT_STREAM_INFO *expect_input_info; const MFT_OUTPUT_STREAM_INFO *expect_output_info; const struct sample_desc *output_sample_desc; const WCHAR *result_bitmap; ULONG delta; } transform_tests[] = { { /* WMV1 -> YUV */ .output_type_desc = output_type_desc, .expect_output_type_desc = expect_output_type_desc, .expect_input_info = &expect_input_info, .expect_output_info = &expect_output_info, .output_sample_desc = &output_sample_desc_nv12, .result_bitmap = L"nv12frame.bmp", .delta = 0, }, { /* WMV1 -> YUV (negative stride) */ .output_type_desc = output_type_desc_negative_stride, .expect_output_type_desc = expect_output_type_desc, .expect_input_info = &expect_input_info, .expect_output_info = &expect_output_info, .output_sample_desc = &output_sample_desc_nv12, .result_bitmap = L"nv12frame.bmp", .delta = 0, }, { /* WMV1 -> RGB */ .output_type_desc = output_type_desc_rgb, .expect_output_type_desc = expect_output_type_desc_rgb, .expect_input_info = &expect_input_info_rgb, .expect_output_info = &expect_output_info_rgb, .output_sample_desc = &output_sample_desc_rgb, .result_bitmap = L"rgb32frame-flip.bmp", .delta = 5, }, { /* WMV1 -> RGB (negative stride) */ .output_type_desc = output_type_desc_rgb_negative_stride, .expect_output_type_desc = expect_output_type_desc_rgb_negative_stride, .expect_input_info = &expect_input_info_rgb, .expect_output_info = &expect_output_info_rgb, .output_sample_desc = &output_sample_desc_rgb, .result_bitmap = L"rgb32frame-flip.bmp", .delta = 5, }, { /* WMV1 -> RGB (positive stride */ .output_type_desc = output_type_desc_rgb_positive_stride, .expect_output_type_desc = expect_output_type_desc_rgb, .expect_input_info = &expect_input_info_rgb, .expect_output_info = &expect_output_info_rgb, .output_sample_desc = &output_sample_desc_rgb, .result_bitmap = L"rgb32frame-flip.bmp", .delta = 5, }, }; MFT_REGISTER_TYPE_INFO output_type = {MFMediaType_Video, MFVideoFormat_NV12}; MFT_REGISTER_TYPE_INFO input_type = {MFMediaType_Video, MFVideoFormat_WMV1}; IMFSample *input_sample, *output_sample; IMFCollection *output_samples; IMFMediaType *media_type; IMFTransform *transform; const BYTE *wmvenc_data; ULONG wmvenc_data_len; DWORD output_status; ULONG i, j, ret, ref; HRESULT hr; hr = CoInitialize(NULL); ok(hr == S_OK, "Failed to initialize, hr %#lx.\n", hr); winetest_push_context("wmvdec"); if (!has_video_processor) { win_skip("Skipping inconsistent WMV decoder tests on Win7\n"); goto failed; } if (!check_mft_enum(MFT_CATEGORY_VIDEO_DECODER, &input_type, &output_type, class_id)) goto failed; check_mft_get_info(class_id, &expect_mft_info); check_dmo_get_info(class_id, &expect_dmo_info); if (FAILED(hr = CoCreateInstance(class_id, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)&transform))) goto failed; check_interface(transform, &IID_IMFTransform, TRUE); check_interface(transform, &IID_IMediaObject, TRUE); check_interface(transform, &IID_IPropertyStore, TRUE); check_interface(transform, &IID_IPropertyBag, TRUE); check_mft_optional_methods(transform, 1); check_mft_get_attributes(transform, expect_attributes, TRUE); todo_wine check_mft_get_input_stream_info(transform, MF_E_TRANSFORM_TYPE_NOT_SET, NULL); todo_wine check_mft_get_output_stream_info(transform, MF_E_TRANSFORM_TYPE_NOT_SET, &empty_output_info); hr = IMFTransform_GetOutputAvailableType(transform, 0, 0, &media_type); todo_wine ok(hr == MF_E_TRANSFORM_TYPE_NOT_SET, "GetOutputAvailableType returned %#lx\n", hr); i = -1; while (SUCCEEDED(hr = IMFTransform_GetInputAvailableType(transform, 0, ++i, &media_type))) { winetest_push_context("in %lu", i); ok(hr == S_OK, "GetInputAvailableType returned %#lx\n", hr); check_media_type(media_type, expect_common_attributes, -1); check_media_type(media_type, expect_available_inputs[i], -1); ret = IMFMediaType_Release(media_type); ok(!ret, "Release returned %lu\n", ret); winetest_pop_context(); } todo_wine ok(hr == MF_E_NO_MORE_TYPES, "GetInputAvailableType returned %#lx\n", hr); todo_wine ok(i == ARRAY_SIZE(expect_available_inputs), "%lu input media types\n", i); if (hr == E_NOTIMPL) goto skip_tests; check_mft_set_output_type(transform, output_type_desc, MF_E_TRANSFORM_TYPE_NOT_SET); check_mft_get_output_current_type(transform, NULL); 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); i = -1; while (SUCCEEDED(hr = IMFTransform_GetOutputAvailableType(transform, 0, ++i, &media_type))) { winetest_push_context("out %lu", i); ok(hr == S_OK, "GetOutputAvailableType returned %#lx\n", hr); check_media_type(media_type, expect_common_attributes, -1); check_media_type(media_type, expect_output_attributes, -1); check_media_type(media_type, expect_available_outputs[i], -1); ret = IMFMediaType_Release(media_type); ok(!ret, "Release returned %lu\n", ret); winetest_pop_context(); } ok(hr == MF_E_NO_MORE_TYPES, "GetOutputAvailableType returned %#lx\n", hr); ok(i == ARRAY_SIZE(expect_available_outputs), "%lu input media types\n", i); for (j = 0; j < ARRAY_SIZE(transform_tests); j++) { winetest_push_context("transform #%lu", j); 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_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); load_resource(L"wmvencdata.bin", &wmvenc_data, &wmvenc_data_len); input_sample = create_sample(wmvenc_data + sizeof(DWORD), *(DWORD *)wmvenc_data); wmvenc_data_len -= *(DWORD *)wmvenc_data + sizeof(DWORD); wmvenc_data += *(DWORD *)wmvenc_data + sizeof(DWORD); hr = IMFSample_SetSampleTime(input_sample, 0); ok(hr == S_OK, "SetSampleTime returned %#lx\n", hr); hr = IMFSample_SetSampleDuration(input_sample, 333333); ok(hr == S_OK, "SetSampleDuration returned %#lx\n", hr); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == S_OK, "ProcessInput returned %#lx\n", hr); ret = IMFSample_Release(input_sample); ok(ret <= 1, "Release returned %ld\n", ret); hr = MFCreateCollection(&output_samples); ok(hr == S_OK, "MFCreateCollection returned %#lx\n", hr); output_sample = create_sample(NULL, transform_tests[j].expect_output_info->cbSize); for (i = 0; SUCCEEDED(hr = check_mft_process_output(transform, output_sample, &output_status)); i++) { winetest_push_context("%lu", i); ok(hr == S_OK, "ProcessOutput returned %#lx\n", hr); hr = IMFCollection_AddElement(output_samples, (IUnknown *)output_sample); ok(hr == S_OK, "AddElement returned %#lx\n", hr); ref = IMFSample_Release(output_sample); ok(ref == 1, "Release returned %ld\n", ref); output_sample = create_sample(NULL, transform_tests[j].expect_output_info->cbSize); winetest_pop_context(); } ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); ret = IMFSample_Release(output_sample); ok(ret == 0, "Release returned %lu\n", ret); ok(i == 1, "got %lu output samples\n", i); ret = check_mf_sample_collection(output_samples, transform_tests[j].output_sample_desc, transform_tests[j].result_bitmap); ok(ret <= transform_tests[j].delta, "got %lu%% diff\n", ret); IMFCollection_Release(output_samples); winetest_pop_context(); } skip_tests: ret = IMFTransform_Release(transform); ok(ret == 0, "Release returned %lu\n", ret); failed: winetest_pop_context(); CoUninitialize(); } static void test_wmv_decoder_dmo_input_type(void) { const GUID *input_subtypes[] = { &MEDIASUBTYPE_WMV1, &MEDIASUBTYPE_WMV2, &MEDIASUBTYPE_WMVA, &MEDIASUBTYPE_WMVP, &MEDIASUBTYPE_WVP2, &MFVideoFormat_WMV_Unknown, &MEDIASUBTYPE_WVC1, &MEDIASUBTYPE_WMV3, &MFVideoFormat_VC1S, }; DMO_MEDIA_TYPE *good_input_type, *bad_input_type, type; char buffer_good_input[2048], buffer_bad_input[2048]; const GUID *input_subtype = input_subtypes[0]; LONG width = 16, height = 16; VIDEOINFOHEADER *header; DWORD count, i, ret; IMediaObject *dmo; HRESULT hr; winetest_push_context("wmvdec"); if (!has_video_processor) { win_skip("Skipping WMV decoder DMO tests on Win7.\n"); winetest_pop_context(); return; } hr = CoInitialize(NULL); ok(hr == S_OK, "CoInitialize failed, hr %#lx.\n", hr); if (FAILED(hr = CoCreateInstance(&CLSID_CWMVDecMediaObject, NULL, CLSCTX_INPROC_SERVER, &IID_IMediaObject, (void **)&dmo))) { CoUninitialize(); winetest_pop_context(); return; } good_input_type = (void *)buffer_good_input; bad_input_type = (void *)buffer_bad_input; init_dmo_media_type_video(good_input_type, input_subtype, width, height); memset(bad_input_type, 0, sizeof(buffer_bad_input)); header = (void *)(good_input_type + 1); /* Test GetInputType. */ count = ARRAY_SIZE(input_subtypes); hr = IMediaObject_GetInputType(dmo, 1, 0, NULL); ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetInputType returned %#lx.\n", hr); hr = IMediaObject_GetInputType(dmo, 1, 0, &type); ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetInputType returned %#lx.\n", hr); hr = IMediaObject_GetInputType(dmo, 1, count, &type); ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetInputType returned %#lx.\n", hr); hr = IMediaObject_GetInputType(dmo, 0, count, &type); ok(hr == DMO_E_NO_MORE_ITEMS, "GetInputType returned %#lx.\n", hr); hr = IMediaObject_GetInputType(dmo, 0, count, NULL); ok(hr == DMO_E_NO_MORE_ITEMS, "GetInputType returned %#lx.\n", hr); hr = IMediaObject_GetInputType(dmo, 0, 0xdeadbeef, NULL); ok(hr == DMO_E_NO_MORE_ITEMS, "GetInputType returned %#lx.\n", hr); hr = IMediaObject_GetInputType(dmo, 0, count - 1, NULL); ok(hr == S_OK, "GetInputType returned %#lx.\n", hr); hr = IMediaObject_GetInputType(dmo, 0, count - 1, &type); ok(hr == S_OK, "GetInputType returned %#lx.\n", hr); MoFreeMediaType(&type); i = -1; while (SUCCEEDED(hr = IMediaObject_GetInputType(dmo, 0, ++i, &type))) { winetest_push_context("in %lu", i); memset(good_input_type, 0, sizeof(*good_input_type)); good_input_type->majortype = MEDIATYPE_Video; good_input_type->subtype = *input_subtypes[i]; good_input_type->bTemporalCompression = TRUE; check_dmo_media_type(&type, good_input_type); MoFreeMediaType(&type); winetest_pop_context(); } ok(hr == DMO_E_NO_MORE_ITEMS, "GetInputType returned %#lx.\n", hr); ok(i == count, "%lu types.\n", i); /* Test SetInputType. */ hr = IMediaObject_SetInputType(dmo, 1, NULL, 0); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 1, bad_input_type, 0); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 1, good_input_type, 0); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 1, NULL, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 1, bad_input_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 1, good_input_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 1, NULL, DMO_SET_TYPEF_CLEAR); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 1, bad_input_type, DMO_SET_TYPEF_CLEAR); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 1, good_input_type, DMO_SET_TYPEF_CLEAR); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 1, NULL, 0x4); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 1, bad_input_type, 0x4); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 1, good_input_type, 0x4); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, NULL, DMO_SET_TYPEF_CLEAR); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, NULL, DMO_SET_TYPEF_CLEAR | DMO_SET_TYPEF_TEST_ONLY); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, NULL, DMO_SET_TYPEF_CLEAR | 0x4); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, NULL, 0); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, NULL, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, NULL, 0x4); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, bad_input_type, 0); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, bad_input_type, DMO_SET_TYPEF_CLEAR); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, bad_input_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, bad_input_type, 0x4); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetInputType returned %#lx.\n", hr); for (i = 0; i < ARRAY_SIZE(input_subtypes); ++i) { const GUID *subtype = input_subtypes[i]; if (IsEqualGUID(subtype, &MEDIASUBTYPE_WMV2) || IsEqualGUID(subtype, &MEDIASUBTYPE_WMVA) || IsEqualGUID(subtype, &MEDIASUBTYPE_WVP2) || IsEqualGUID(subtype, &MEDIASUBTYPE_WVC1) || IsEqualGUID(subtype, &MFVideoFormat_VC1S)) { skip("Skipping SetInputType tests for video subtype %s.\n", debugstr_guid(subtype)); continue; } winetest_push_context("type %lu", i); init_dmo_media_type_video(good_input_type, subtype, width, height); hr = IMediaObject_SetInputType(dmo, 0, good_input_type, 0); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_CLEAR); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, good_input_type, 0x4); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); winetest_pop_context(); } init_dmo_media_type_video(good_input_type, input_subtype, width, height); header->dwBitRate = 0xdeadbeef; header->dwBitErrorRate = 0xdeadbeef; header->AvgTimePerFrame = 0xdeadbeef; header->bmiHeader.biPlanes = 0xdead; header->bmiHeader.biBitCount = 0xdead; header->bmiHeader.biSizeImage = 0xdeadbeef; header->bmiHeader.biXPelsPerMeter = 0xdead; header->bmiHeader.biYPelsPerMeter = 0xdead; hr = IMediaObject_SetInputType(dmo, 0, good_input_type, 0); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); init_dmo_media_type_video(good_input_type, input_subtype, width, height); good_input_type->majortype = MFMediaType_Default; hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetInputType returned %#lx.\n", hr); init_dmo_media_type_video(good_input_type, &MEDIASUBTYPE_None, width, height); hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetInputType returned %#lx.\n", hr); init_dmo_media_type_video(good_input_type, input_subtype, width, height); good_input_type->formattype = FORMAT_None; hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetInputType returned %#lx.\n", hr); init_dmo_media_type_video(good_input_type, input_subtype, width, height); good_input_type->cbFormat = 1; hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetInputType returned %#lx.\n", hr); init_dmo_media_type_video(good_input_type, input_subtype, width, height); good_input_type->pbFormat = NULL; hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetInputType returned %#lx.\n", hr); init_dmo_media_type_video(good_input_type, input_subtype, width, height); header->bmiHeader.biSize = 0; hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_TEST_ONLY); todo_wine ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetInputType returned %#lx.\n", hr); header->bmiHeader.biSize = 1; hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_TEST_ONLY); todo_wine ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetInputType returned %#lx.\n", hr); header->bmiHeader.biSize = 0xdeadbeef; hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_TEST_ONLY); todo_wine ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetInputType returned %#lx.\n", hr); init_dmo_media_type_video(good_input_type, input_subtype, width, height); header->bmiHeader.biWidth = 0; hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_TEST_ONLY); todo_wine ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetInputType returned %#lx.\n", hr); header->bmiHeader.biWidth = -1; hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_TEST_ONLY); todo_wine ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetInputType returned %#lx.\n", hr); header->bmiHeader.biWidth = 4096 + 1; hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_TEST_ONLY); todo_wine ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetInputType returned %#lx.\n", hr); header->bmiHeader.biWidth = 4096; hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); init_dmo_media_type_video(good_input_type, input_subtype, width, height); header->bmiHeader.biHeight = 0; hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_TEST_ONLY); todo_wine ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetInputType returned %#lx.\n", hr); header->bmiHeader.biHeight = 4096 + 1; hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_TEST_ONLY); todo_wine ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetInputType returned %#lx.\n", hr); header->bmiHeader.biHeight = 4096; hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); header->bmiHeader.biHeight = -4096; hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); init_dmo_media_type_video(good_input_type, input_subtype, width, height); header->bmiHeader.biCompression = 0; hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_TEST_ONLY); todo_wine ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetInputType returned %#lx.\n", hr); header->bmiHeader.biCompression = 1; hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); header->bmiHeader.biCompression = 2; hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); header->bmiHeader.biCompression = 0xdeadbeef; hr = IMediaObject_SetInputType(dmo, 0, good_input_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); /* Release. */ ret = IMediaObject_Release(dmo); ok(ret == 0, "Release returned %lu\n", ret); CoUninitialize(); winetest_pop_context(); } static void test_wmv_decoder_dmo_output_type(void) { char buffer_good_output[2048], buffer_bad_output[2048], buffer_input[2048]; DMO_MEDIA_TYPE *good_output_type, *bad_output_type, *input_type, type; const GUID* input_subtype = &MEDIASUBTYPE_WMV1; LONG width = 16, height = 16; DWORD count, i, ret; IMediaObject *dmo; HRESULT hr; winetest_push_context("wmvdec"); if (!has_video_processor) { win_skip("Skipping WMV decoder DMO tests on Win7.\n"); winetest_pop_context(); return; } hr = CoInitialize(NULL); ok(hr == S_OK, "CoInitialize failed, hr %#lx.\n", hr); if (FAILED(hr = CoCreateInstance(&CLSID_CWMVDecMediaObject, NULL, CLSCTX_INPROC_SERVER, &IID_IMediaObject, (void **)&dmo))) { CoUninitialize(); winetest_pop_context(); return; } /* Initialize media types. */ input_type = (void *)buffer_input; good_output_type = (void *)buffer_good_output; bad_output_type = (void *)buffer_bad_output; init_dmo_media_type_video(input_type, input_subtype, width, height); memset(bad_output_type, 0, sizeof(buffer_bad_output)); /* Test GetOutputType. */ hr = IMediaObject_GetOutputType(dmo, 1, 0, NULL); ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetOutputType returned %#lx.\n", hr); hr = IMediaObject_GetOutputType(dmo, 0, 0, NULL); ok(hr == S_OK, "GetOutputType returned %#lx.\n", hr); hr = IMediaObject_GetOutputType(dmo, 0, 0, &type); ok(hr == DMO_E_TYPE_NOT_SET, "GetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, input_type, 0); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); count = ARRAY_SIZE(wmv_decoder_output_subtypes); hr = IMediaObject_GetOutputType(dmo, 1, 0, NULL); ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetOutputType returned %#lx.\n", hr); hr = IMediaObject_GetOutputType(dmo, 1, 0, &type); ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetOutputType returned %#lx.\n", hr); hr = IMediaObject_GetOutputType(dmo, 1, count, &type); ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetOutputType returned %#lx.\n", hr); hr = IMediaObject_GetOutputType(dmo, 0, count, &type); ok(hr == DMO_E_NO_MORE_ITEMS, "GetOutputType returned %#lx.\n", hr); hr = IMediaObject_GetOutputType(dmo, 0, count, NULL); todo_wine ok(hr == S_OK, "GetOutputType returned %#lx.\n", hr); hr = IMediaObject_GetOutputType(dmo, 0, 0xdeadbeef, NULL); todo_wine ok(hr == S_OK, "GetOutputType returned %#lx.\n", hr); hr = IMediaObject_GetOutputType(dmo, 0, count - 1, NULL); ok(hr == S_OK, "GetOutputType returned %#lx.\n", hr); i = -1; while (SUCCEEDED(hr = IMediaObject_GetOutputType(dmo, 0, ++i, &type))) { winetest_push_context("type %lu", i); init_dmo_media_type_video(good_output_type, wmv_decoder_output_subtypes[i], width, height); check_dmo_media_type(&type, good_output_type); MoFreeMediaType(&type); winetest_pop_context(); } ok(hr == DMO_E_NO_MORE_ITEMS, "GetInputType returned %#lx.\n", hr); ok(i == count, "%lu types.\n", i); /* Test SetOutputType. */ init_dmo_media_type_video(good_output_type, &MEDIASUBTYPE_RGB24, width, height); hr = IMediaObject_SetInputType(dmo, 0, NULL, DMO_SET_TYPEF_CLEAR); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, NULL, 0); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, good_output_type, 0); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, NULL, 0); ok(hr == E_POINTER, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, NULL, 0x4); ok(hr == E_POINTER, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, NULL, DMO_SET_TYPEF_CLEAR); ok(hr == S_OK, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, NULL, DMO_SET_TYPEF_CLEAR | DMO_SET_TYPEF_TEST_ONLY); ok(hr == S_OK, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, bad_output_type, 0); ok(hr == DMO_E_TYPE_NOT_SET, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, bad_output_type, DMO_SET_TYPEF_CLEAR); ok(hr == DMO_E_TYPE_NOT_SET, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, good_output_type, 0); ok(hr == DMO_E_TYPE_NOT_SET, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, good_output_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_TYPE_NOT_SET, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, good_output_type, DMO_SET_TYPEF_CLEAR); ok(hr == DMO_E_TYPE_NOT_SET, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, good_output_type, 0x4); ok(hr == DMO_E_TYPE_NOT_SET, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, input_type, 0); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, NULL, 0); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, bad_output_type, 0); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, good_output_type, 0); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, NULL, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, bad_output_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, good_output_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, NULL, DMO_SET_TYPEF_CLEAR); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, bad_output_type, DMO_SET_TYPEF_CLEAR); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, good_output_type, DMO_SET_TYPEF_CLEAR); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, NULL, 0x4); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, bad_output_type, 0x4); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 1, good_output_type, 0x4); ok(hr == DMO_E_INVALIDSTREAMINDEX, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, NULL, DMO_SET_TYPEF_CLEAR); ok(hr == S_OK, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, NULL, DMO_SET_TYPEF_CLEAR | DMO_SET_TYPEF_TEST_ONLY); ok(hr == S_OK, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, NULL, DMO_SET_TYPEF_CLEAR | 0x4); ok(hr == S_OK, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, NULL, 0); ok(hr == E_POINTER, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, NULL, DMO_SET_TYPEF_TEST_ONLY); ok(hr == E_POINTER, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, NULL, 0x4); ok(hr == E_POINTER, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, bad_output_type, 0); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, bad_output_type, DMO_SET_TYPEF_CLEAR); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, bad_output_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, bad_output_type, 0x4); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, good_output_type, 0); ok(hr == S_OK, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, good_output_type, DMO_SET_TYPEF_CLEAR); ok(hr == S_OK, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, good_output_type, DMO_SET_TYPEF_TEST_ONLY); ok(hr == S_OK, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, good_output_type, 0x4); ok(hr == S_OK, "SetOutputType returned %#lx.\n", hr); /* Release. */ ret = IMediaObject_Release(dmo); ok(ret == 0, "Release returned %lu\n", ret); CoUninitialize(); winetest_pop_context(); } static void test_wmv_decoder_dmo_get_size_info(void) { DWORD i, ret, size, alignment; IMediaObject *dmo; HRESULT hr; winetest_push_context("wmvdec"); if (!has_video_processor) { win_skip("Skipping WMV decoder DMO tests on Win7.\n"); winetest_pop_context(); return; } hr = CoInitialize(NULL); ok(hr == S_OK, "CoInitialize failed, hr %#lx.\n", hr); hr = CoCreateInstance(&CLSID_CWMVDecMediaObject, NULL, CLSCTX_INPROC_SERVER, &IID_IMediaObject, (void **)&dmo); ok(hr == S_OK, "CoCreateInstance failed, hr %#lx.\n", hr); /* Test GetOutputSizeInfo. */ hr = IMediaObject_GetOutputSizeInfo(dmo, 1, NULL, NULL); ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetOutputSizeInfo returned %#lx.\n", hr); hr = IMediaObject_GetOutputSizeInfo(dmo, 0, NULL, NULL); todo_wine ok(hr == E_POINTER, "GetOutputSizeInfo returned %#lx.\n", hr); hr = IMediaObject_GetOutputSizeInfo(dmo, 0, &size, NULL); todo_wine ok(hr == E_POINTER, "GetOutputSizeInfo returned %#lx.\n", hr); hr = IMediaObject_GetOutputSizeInfo(dmo, 0, NULL, &alignment); todo_wine ok(hr == E_POINTER, "GetOutputSizeInfo returned %#lx.\n", hr); hr = IMediaObject_GetOutputSizeInfo(dmo, 0, &size, &alignment); ok(hr == DMO_E_TYPE_NOT_SET, "GetOutputSizeInfo returned %#lx.\n", hr); for (i = 0; i < ARRAY_SIZE(wmv_decoder_output_subtypes); ++i) { const GUID *subtype = wmv_decoder_output_subtypes[i]; if (IsEqualGUID(subtype, &MEDIASUBTYPE_RGB565) || IsEqualGUID(subtype, &MEDIASUBTYPE_RGB8)) { skip("Skipping GetOutputSizeInfo tests for output subtype %s.\n", debugstr_guid(subtype)); continue; } winetest_push_context("out %lu", i); check_dmo_get_output_size_info_video(dmo, &MEDIASUBTYPE_WMV1, subtype, 16, 16); check_dmo_get_output_size_info_video(dmo, &MEDIASUBTYPE_WMV1, subtype, 96, 96); check_dmo_get_output_size_info_video(dmo, &MEDIASUBTYPE_WMV1, subtype, 320, 240); winetest_pop_context(); } ret = IMediaObject_Release(dmo); ok(ret == 0, "Release returned %lu\n", ret); CoUninitialize(); winetest_pop_context(); } static void test_wmv_decoder_media_object(void) { const GUID *const class_id = &CLSID_CWMVDecMediaObject; const DWORD data_width = 96, data_height = 96; const struct buffer_desc output_buffer_desc_nv12 = { .length = data_width * data_height * 3 / 2, .compare = compare_nv12, .dump = dump_nv12, .rect = {.right = 82, .bottom = 84}, }; DWORD in_count, out_count, size, alignment, wmv_data_length, status, expected_status, diff; struct media_buffer *input_media_buffer = NULL, *output_media_buffer = NULL; DMO_OUTPUT_DATA_BUFFER output_data_buffer; IMediaObject *media_object; DMO_MEDIA_TYPE *type; const BYTE *wmv_data; char buffer[2048]; HRESULT hr; ULONG ret; winetest_push_context("wmvdec"); if (!has_video_processor) { win_skip("Skipping inconsistent WMV decoder media object tests on Win7.\n"); winetest_pop_context(); return; } type = (DMO_MEDIA_TYPE *)buffer; hr = CoInitialize(NULL); ok(hr == S_OK, "CoInitialize failed, hr %#lx.\n", hr); if (FAILED(hr = CoCreateInstance(class_id, NULL, CLSCTX_INPROC_SERVER, &IID_IMediaObject, (void **)&media_object))) { CoUninitialize(); winetest_pop_context(); return; } /* Test GetStreamCount. */ in_count = out_count = 0xdeadbeef; hr = IMediaObject_GetStreamCount(media_object, &in_count, &out_count); ok(hr == S_OK, "GetStreamCount returned %#lx.\n", hr); ok(in_count == 1, "Got unexpected in_count %lu.\n", in_count); ok(out_count == 1, "Got unexpected in_count %lu.\n", out_count); /* Test GetStreamCount with invalid arguments. */ in_count = out_count = 0xdeadbeef; hr = IMediaObject_GetStreamCount(media_object, NULL, &out_count); ok(hr == E_POINTER, "GetStreamCount returned %#lx.\n", hr); ok(out_count == 0xdeadbeef, "Got unexpected out_count %lu.\n", out_count); hr = IMediaObject_GetStreamCount(media_object, &in_count, NULL); ok(hr == E_POINTER, "GetStreamCount returned %#lx.\n", hr); ok(in_count == 0xdeadbeef, "Got unexpected in_count %lu.\n", in_count); /* Test ProcessInput. */ load_resource(L"wmvencdata.bin", &wmv_data, &wmv_data_length); wmv_data_length = *((DWORD *)wmv_data); wmv_data += sizeof(DWORD); hr = media_buffer_create(wmv_data_length, &input_media_buffer); ok(hr == S_OK, "Failed to create input media buffer.\n"); memcpy(input_media_buffer->data, wmv_data, wmv_data_length); input_media_buffer->length = wmv_data_length; init_dmo_media_type_video(type, &MEDIASUBTYPE_WMV1, data_width, data_height); hr = IMediaObject_SetInputType(media_object, 0, type, 0); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); init_dmo_media_type_video(type, &MEDIASUBTYPE_NV12, data_width, data_height); hr = IMediaObject_SetOutputType(media_object, 0, type, 0); ok(hr == S_OK, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_ProcessInput(media_object, 0, &input_media_buffer->IMediaBuffer_iface, 0, 0, 0); ok(hr == S_OK, "ProcessInput returned %#lx.\n", hr); /* Test ProcessOutput. */ hr = IMediaObject_GetOutputSizeInfo(media_object, 0, &size, &alignment); ok(hr == S_OK, "GetOutputSizeInfo returned %#lx.\n", hr); hr = media_buffer_create(size, &output_media_buffer); ok(hr == S_OK, "Failed to create output media buffer.\n"); output_data_buffer.pBuffer = &output_media_buffer->IMediaBuffer_iface; output_data_buffer.dwStatus = 0xdeadbeef; output_data_buffer.rtTimestamp = 0xdeadbeef; output_data_buffer.rtTimelength = 0xdeadbeef; hr = IMediaObject_ProcessOutput(media_object, 0, 1, &output_data_buffer, &status); ok(hr == S_OK, "ProcessOutput returned %#lx.\n", hr); expected_status = DMO_OUTPUT_DATA_BUFFERF_SYNCPOINT | DMO_OUTPUT_DATA_BUFFERF_TIME | DMO_OUTPUT_DATA_BUFFERF_TIMELENGTH; ok(output_data_buffer.dwStatus == expected_status, "Got unexpected dwStatus %#lx.\n", output_data_buffer.dwStatus); diff = check_dmo_output_data_buffer(&output_data_buffer, &output_buffer_desc_nv12, L"nv12frame.bmp", 0, 0); ok(diff == 0, "Got %lu%% diff.\n", diff); /* Test GetInputStatus. */ hr = IMediaObject_GetInputStatus(media_object, 0xdeadbeef, NULL); ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetInputStatus returned %#lx.\n", hr); status = 0xdeadbeef; hr = IMediaObject_GetInputStatus(media_object, 0xdeadbeef, &status); ok(hr == DMO_E_INVALIDSTREAMINDEX, "GetInputStatus returned %#lx.\n", hr); ok(status == 0xdeadbeef, "Unexpected status %#lx.\n", status); hr = IMediaObject_GetInputStatus(media_object, 0, NULL); ok(hr == E_POINTER, "GetInputStatus returned %#lx.\n", hr); hr = IMediaObject_GetInputStatus(media_object, 0, &status); ok(hr == S_OK, "GetInputStatus returned %#lx.\n", hr); ok(status == DMO_INPUT_STATUSF_ACCEPT_DATA, "Unexpected status %#lx.\n", status); /* Test Discontinuity. */ hr = IMediaObject_Discontinuity(media_object, 0xdeadbeef); ok(hr == DMO_E_INVALIDSTREAMINDEX, "Discontinuity returned %#lx.\n", hr); hr = IMediaObject_Discontinuity(media_object, 0); ok(hr == S_OK, "Discontinuity returned %#lx.\n", hr); hr = IMediaObject_Discontinuity(media_object, 0); ok(hr == S_OK, "Discontinuity returned %#lx.\n", hr); hr = IMediaObject_GetInputStatus(media_object, 0, &status); ok(hr == S_OK, "GetInputStatus returned %#lx.\n", hr); ok(status == DMO_INPUT_STATUSF_ACCEPT_DATA, "Unexpected status %#lx.\n", status); /* Test Flush. */ hr = IMediaObject_ProcessInput(media_object, 0, &input_media_buffer->IMediaBuffer_iface, 0, 0, 0); ok(hr == S_OK, "ProcessInput returned %#lx.\n", hr); hr = IMediaObject_Flush(media_object); ok(hr == S_OK, "Flush returned %#lx.\n", hr); hr = IMediaObject_Flush(media_object); ok(hr == S_OK, "Flush returned %#lx.\n", hr); output_media_buffer->length = 0; output_data_buffer.pBuffer = &output_media_buffer->IMediaBuffer_iface; output_data_buffer.dwStatus = 0xdeadbeef; output_data_buffer.rtTimestamp = 0xdeadbeef; output_data_buffer.rtTimelength = 0xdeadbeef; hr = IMediaObject_ProcessOutput(media_object, 0, 1, &output_data_buffer, &status); todo_wine ok(hr == S_FALSE, "ProcessOutput returned %#lx.\n", hr); ok(output_media_buffer->length == 0, "Unexpected length %#lx.\n", output_media_buffer->length); /* Test ProcessOutput with setting framerate. */ init_dmo_media_type_video(type, &MEDIASUBTYPE_WMV1, data_width, data_height); ((VIDEOINFOHEADER *)type->pbFormat)->AvgTimePerFrame = 100000; hr = IMediaObject_SetInputType(media_object, 0, type, 0); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); init_dmo_media_type_video(type, &MEDIASUBTYPE_NV12, data_width, data_height); ((VIDEOINFOHEADER *)type->pbFormat)->AvgTimePerFrame = 200000; hr = IMediaObject_SetOutputType(media_object, 0, type, 0); ok(hr == S_OK, "SetOutputType returned %#lx.\n", hr); hr = IMediaObject_ProcessInput(media_object, 0, &input_media_buffer->IMediaBuffer_iface, 0, 0, 300000); ok(hr == S_OK, "ProcessInput returned %#lx.\n", hr); output_media_buffer->length = 0; output_data_buffer.pBuffer = &output_media_buffer->IMediaBuffer_iface; output_data_buffer.dwStatus = 0xdeadbeef; output_data_buffer.rtTimestamp = 0xdeadbeef; output_data_buffer.rtTimelength = 0xdeadbeef; hr = IMediaObject_ProcessOutput(media_object, 0, 1, &output_data_buffer, &status); ok(hr == S_OK, "ProcessOutput returned %#lx.\n", hr); expected_status = DMO_OUTPUT_DATA_BUFFERF_SYNCPOINT | DMO_OUTPUT_DATA_BUFFERF_TIME | DMO_OUTPUT_DATA_BUFFERF_TIMELENGTH; ok(output_data_buffer.dwStatus == expected_status, "Got unexpected dwStatus %#lx.\n", output_data_buffer.dwStatus); diff = check_dmo_output_data_buffer(&output_data_buffer, &output_buffer_desc_nv12, L"nv12frame.bmp", 0, 300000); ok(diff == 0, "Got %lu%% diff.\n", diff); ret = IMediaBuffer_Release(&output_media_buffer->IMediaBuffer_iface); ok(ret == 0, "Release returned %lu\n", ret); ret = IMediaBuffer_Release(&input_media_buffer->IMediaBuffer_iface); todo_wine ok(ret == 0, "Release returned %lu\n", ret); ret = IMediaObject_Release(media_object); ok(ret == 0, "Release returned %lu\n", ret); CoUninitialize(); winetest_pop_context(); } static void test_color_convert(void) { const GUID *const class_id = &CLSID_CColorConvertDMO; const struct transform_info expect_mft_info = { .name = L"Color Converter MFT", .major_type = &MFMediaType_Video, .inputs = { {.subtype = &MFVideoFormat_YV12}, {.subtype = &MFVideoFormat_YUY2}, {.subtype = &MFVideoFormat_UYVY}, {.subtype = &MFVideoFormat_AYUV}, {.subtype = &MFVideoFormat_NV12}, {.subtype = &DMOVideoFormat_RGB32}, {.subtype = &DMOVideoFormat_RGB565}, {.subtype = &MFVideoFormat_I420}, {.subtype = &MFVideoFormat_IYUV}, {.subtype = &MFVideoFormat_YVYU}, {.subtype = &DMOVideoFormat_RGB24}, {.subtype = &DMOVideoFormat_RGB555}, {.subtype = &DMOVideoFormat_RGB8}, {.subtype = &MEDIASUBTYPE_V216}, {.subtype = &MEDIASUBTYPE_V410}, {.subtype = &MFVideoFormat_NV11}, {.subtype = &MFVideoFormat_Y41P}, {.subtype = &MFVideoFormat_Y41T}, {.subtype = &MFVideoFormat_Y42T}, {.subtype = &MFVideoFormat_YVU9}, }, .outputs = { {.subtype = &MFVideoFormat_YV12}, {.subtype = &MFVideoFormat_YUY2}, {.subtype = &MFVideoFormat_UYVY}, {.subtype = &MFVideoFormat_AYUV}, {.subtype = &MFVideoFormat_NV12}, {.subtype = &DMOVideoFormat_RGB32}, {.subtype = &DMOVideoFormat_RGB565}, {.subtype = &MFVideoFormat_I420}, {.subtype = &MFVideoFormat_IYUV}, {.subtype = &MFVideoFormat_YVYU}, {.subtype = &DMOVideoFormat_RGB24}, {.subtype = &DMOVideoFormat_RGB555}, {.subtype = &DMOVideoFormat_RGB8}, {.subtype = &MEDIASUBTYPE_V216}, {.subtype = &MEDIASUBTYPE_V410}, {.subtype = &MFVideoFormat_NV11}, }, }; const struct transform_info expect_dmo_info = { .name = L"Color Converter DMO", .major_type = &MEDIATYPE_Video, .inputs = { {.subtype = &MEDIASUBTYPE_YV12}, {.subtype = &MEDIASUBTYPE_YUY2}, {.subtype = &MEDIASUBTYPE_UYVY}, {.subtype = &MEDIASUBTYPE_AYUV}, {.subtype = &MEDIASUBTYPE_NV12}, {.subtype = &MEDIASUBTYPE_RGB32}, {.subtype = &MEDIASUBTYPE_RGB565}, {.subtype = &MEDIASUBTYPE_I420}, {.subtype = &MEDIASUBTYPE_IYUV}, {.subtype = &MEDIASUBTYPE_YVYU}, {.subtype = &MEDIASUBTYPE_RGB24}, {.subtype = &MEDIASUBTYPE_RGB555}, {.subtype = &MEDIASUBTYPE_RGB8}, {.subtype = &MEDIASUBTYPE_V216}, {.subtype = &MEDIASUBTYPE_V410}, {.subtype = &MEDIASUBTYPE_NV11}, {.subtype = &MEDIASUBTYPE_Y41P}, {.subtype = &MEDIASUBTYPE_Y41T}, {.subtype = &MEDIASUBTYPE_Y42T}, {.subtype = &MEDIASUBTYPE_YVU9}, }, .outputs = { {.subtype = &MEDIASUBTYPE_YV12}, {.subtype = &MEDIASUBTYPE_YUY2}, {.subtype = &MEDIASUBTYPE_UYVY}, {.subtype = &MEDIASUBTYPE_AYUV}, {.subtype = &MEDIASUBTYPE_NV12}, {.subtype = &MEDIASUBTYPE_RGB32}, {.subtype = &MEDIASUBTYPE_RGB565}, {.subtype = &MEDIASUBTYPE_I420}, {.subtype = &MEDIASUBTYPE_IYUV}, {.subtype = &MEDIASUBTYPE_YVYU}, {.subtype = &MEDIASUBTYPE_RGB24}, {.subtype = &MEDIASUBTYPE_RGB555}, {.subtype = &MEDIASUBTYPE_RGB8}, {.subtype = &MEDIASUBTYPE_V216}, {.subtype = &MEDIASUBTYPE_V410}, {.subtype = &MEDIASUBTYPE_NV11}, }, }; static const media_type_desc expect_available_inputs[20] = { { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_YV12), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_YUY2), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_UYVY), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_AYUV), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB565), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_I420), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_IYUV), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_YVYU), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB24), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB555), }, { ATTR_GUID(MF_MT_SUBTYPE, MEDIASUBTYPE_RGB8), }, { ATTR_GUID(MF_MT_SUBTYPE, MEDIASUBTYPE_V216), }, { ATTR_GUID(MF_MT_SUBTYPE, MEDIASUBTYPE_V410), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV11), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_Y41P), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_Y41T), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_Y42T), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_YVU9), }, }; static const media_type_desc expect_available_outputs[16] = { { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_YV12), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_YUY2), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_UYVY), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_AYUV), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB565), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_I420), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_IYUV), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_YVYU), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB24), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB555), }, { ATTR_GUID(MF_MT_SUBTYPE, MEDIASUBTYPE_RGB8), }, { ATTR_GUID(MF_MT_SUBTYPE, MEDIASUBTYPE_V216), }, { ATTR_GUID(MF_MT_SUBTYPE, MEDIASUBTYPE_V410), }, { ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV11), }, }; static const media_type_desc expect_available_common = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), }; static const MFVideoArea actual_aperture = {.Area={82,84}}; static const DWORD actual_width = 96, actual_height = 96; const struct attribute_desc input_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height, .required = TRUE), ATTR_BLOB(MF_MT_MINIMUM_DISPLAY_APERTURE, &actual_aperture, 16), {0}, }; const struct attribute_desc output_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height, .required = TRUE), {0}, }; const struct attribute_desc output_type_desc_negative_stride[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height, .required = TRUE), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, -actual_width * 4), {0}, }; const struct attribute_desc output_type_desc_positive_stride[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height, .required = TRUE), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, actual_width * 4), {0}, }; const struct attribute_desc expect_input_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12), 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), {0}, }; const struct attribute_desc expect_output_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, actual_width * 4), ATTR_UINT32(MF_MT_SAMPLE_SIZE, actual_width * actual_height * 4, .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_negative_stride[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, -actual_width * 4), ATTR_UINT32(MF_MT_SAMPLE_SIZE, actual_width * actual_height * 4, .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 MFT_OUTPUT_STREAM_INFO output_info = { .cbSize = actual_width * actual_height * 4, .cbAlignment = 1, }; const MFT_INPUT_STREAM_INFO input_info = { .cbSize = actual_width * actual_height * 3 / 2, .cbAlignment = 1, }; const struct buffer_desc output_buffer_desc = { .length = actual_width * actual_height * 4, .compare = compare_rgb32, .dump = dump_rgb32, .rect = {.right = 82, .bottom = 84}, }; const struct attribute_desc output_sample_attributes[] = { ATTR_UINT32(MFSampleExtension_CleanPoint, 0, .todo = TRUE), {0}, }; const struct sample_desc output_sample_desc = { .attributes = output_sample_attributes, .sample_time = 0, .sample_duration = 10000000, .buffer_count = 1, .buffers = &output_buffer_desc, }; const struct transform_desc { const struct attribute_desc *output_type_desc; const struct attribute_desc *expect_output_type_desc; const WCHAR *result_bitmap; ULONG delta; } color_conversion_tests[] = { { /* YUV -> RGB */ .output_type_desc = output_type_desc, .expect_output_type_desc = expect_output_type_desc, .result_bitmap = L"rgb32frame.bmp", .delta = 4, /* Windows return 0, Wine needs 4 */ }, { /* YUV -> RGB (negative stride) */ .output_type_desc = output_type_desc_negative_stride, .expect_output_type_desc = expect_output_type_desc_negative_stride, .result_bitmap = L"rgb32frame-flip.bmp", .delta = 6, }, { /* YUV -> RGB (positive stride) */ .output_type_desc = output_type_desc_positive_stride, .expect_output_type_desc = expect_output_type_desc, .result_bitmap = L"rgb32frame.bmp", .delta = 4, /* Windows return 0, Wine needs 4 */ }, }; MFT_REGISTER_TYPE_INFO output_type = {MFMediaType_Video, MFVideoFormat_NV12}; MFT_REGISTER_TYPE_INFO input_type = {MFMediaType_Video, MFVideoFormat_I420}; IMFSample *input_sample, *output_sample; IMFCollection *output_samples; DWORD length, output_status; const BYTE *nv12frame_data; ULONG nv12frame_data_len; IMFMediaType *media_type; IMFTransform *transform; ULONG i, ret, ref; HRESULT hr; hr = CoInitialize(NULL); ok(hr == S_OK, "Failed to initialize, hr %#lx.\n", hr); winetest_push_context("colorconv"); if (!check_mft_enum(MFT_CATEGORY_VIDEO_EFFECT, &input_type, &output_type, class_id)) goto failed; check_mft_get_info(class_id, &expect_mft_info); check_dmo_get_info(class_id, &expect_dmo_info); if (FAILED(hr = CoCreateInstance(class_id, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)&transform))) goto failed; check_interface(transform, &IID_IMFTransform, TRUE); check_interface(transform, &IID_IMediaObject, TRUE); check_interface(transform, &IID_IPropertyStore, TRUE); todo_wine check_interface(transform, &IID_IPropertyBag, FALSE); todo_wine check_interface(transform, &IID_IMFRealTimeClient, TRUE); /* check_interface(transform, &IID_IWMColorConvProps, TRUE); */ check_mft_optional_methods(transform, 1); check_mft_get_attributes(transform, NULL, 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); i = -1; while (SUCCEEDED(hr = IMFTransform_GetOutputAvailableType(transform, 0, ++i, &media_type))) { winetest_push_context("out %lu", i); ok(hr == S_OK, "GetOutputAvailableType returned %#lx\n", hr); check_media_type(media_type, expect_available_common, -1); check_media_type(media_type, expect_available_outputs[i], -1); ret = IMFMediaType_Release(media_type); ok(ret == 0, "Release returned %lu\n", ret); winetest_pop_context(); } ok(hr == MF_E_NO_MORE_TYPES, "GetOutputAvailableType returned %#lx\n", hr); ok(i == 16, "%lu output media types\n", i); i = -1; while (SUCCEEDED(hr = IMFTransform_GetInputAvailableType(transform, 0, ++i, &media_type))) { winetest_push_context("in %lu", i); ok(hr == S_OK, "GetInputAvailableType returned %#lx\n", hr); check_media_type(media_type, expect_available_common, -1); check_media_type(media_type, expect_available_inputs[i], -1); ret = IMFMediaType_Release(media_type); ok(ret == 0, "Release returned %lu\n", ret); winetest_pop_context(); } ok(hr == MF_E_NO_MORE_TYPES, "GetInputAvailableType returned %#lx\n", hr); ok(i == 20, "%lu input media types\n", i); 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); 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_input_stream_info(transform, S_OK, &input_info); check_mft_get_output_stream_info(transform, S_OK, &output_info); load_resource(L"nv12frame.bmp", &nv12frame_data, &nv12frame_data_len); /* skip BMP header and RGB data from the dump */ length = *(DWORD *)(nv12frame_data + 2); nv12frame_data_len = nv12frame_data_len - length; nv12frame_data = nv12frame_data + length; ok(nv12frame_data_len == 13824, "got length %lu\n", nv12frame_data_len); input_sample = create_sample(nv12frame_data, nv12frame_data_len); hr = IMFSample_SetSampleTime(input_sample, 0); ok(hr == S_OK, "SetSampleTime returned %#lx\n", hr); hr = IMFSample_SetSampleDuration(input_sample, 10000000); ok(hr == S_OK, "SetSampleDuration returned %#lx\n", hr); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == S_OK, "ProcessInput returned %#lx\n", hr); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == MF_E_NOTACCEPTING, "ProcessInput returned %#lx\n", hr); hr = IMFTransform_ProcessMessage(transform, MFT_MESSAGE_COMMAND_DRAIN, 0); ok(hr == S_OK, "ProcessMessage returned %#lx\n", hr); ret = IMFSample_Release(input_sample); ok(ret <= 1, "Release returned %ld\n", ret); hr = MFCreateCollection(&output_samples); ok(hr == S_OK, "MFCreateCollection returned %#lx\n", hr); output_sample = create_sample(NULL, output_info.cbSize); hr = check_mft_process_output(transform, output_sample, &output_status); ok(hr == S_OK, "ProcessOutput returned %#lx\n", hr); ok(output_status == 0, "got output[0].dwStatus %#lx\n", output_status); hr = IMFCollection_AddElement(output_samples, (IUnknown *)output_sample); ok(hr == S_OK, "AddElement returned %#lx\n", hr); ref = IMFSample_Release(output_sample); ok(ref == 1, "Release returned %ld\n", ref); ret = check_mf_sample_collection(output_samples, &output_sample_desc, color_conversion_tests[i].result_bitmap); ok(ret <= color_conversion_tests[i].delta, "got %lu%% diff\n", ret); IMFCollection_Release(output_samples); output_sample = create_sample(NULL, output_info.cbSize); hr = check_mft_process_output(transform, output_sample, &output_status); ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); ok(output_status == 0, "got output[0].dwStatus %#lx\n", output_status); hr = IMFSample_GetTotalLength(output_sample, &length); ok(hr == S_OK, "GetTotalLength returned %#lx\n", hr); ok(length == 0, "got length %lu\n", length); ret = IMFSample_Release(output_sample); ok(ret == 0, "Release returned %lu\n", ret); winetest_pop_context(); } ret = IMFTransform_Release(transform); ok(ret == 0, "Release returned %ld\n", ret); failed: winetest_pop_context(); CoUninitialize(); } static void test_video_processor(void) { const GUID *const class_id = &CLSID_VideoProcessorMFT; const struct transform_info expect_mft_info = { .name = L"Microsoft Video Processor MFT", .major_type = &MFMediaType_Video, .inputs = { {.subtype = &MFVideoFormat_IYUV}, {.subtype = &MFVideoFormat_YV12}, {.subtype = &MFVideoFormat_NV12}, {.subtype = &MFVideoFormat_YUY2}, {.subtype = &MFVideoFormat_ARGB32}, {.subtype = &MFVideoFormat_RGB32}, {.subtype = &MFVideoFormat_NV11}, {.subtype = &MFVideoFormat_AYUV}, {.subtype = &MFVideoFormat_UYVY}, {.subtype = &MEDIASUBTYPE_P208}, {.subtype = &MFVideoFormat_RGB24}, {.subtype = &MFVideoFormat_RGB555}, {.subtype = &MFVideoFormat_RGB565}, {.subtype = &MFVideoFormat_RGB8}, {.subtype = &MFVideoFormat_I420}, {.subtype = &MFVideoFormat_Y216}, {.subtype = &MFVideoFormat_v410}, {.subtype = &MFVideoFormat_Y41P}, {.subtype = &MFVideoFormat_Y41T}, {.subtype = &MFVideoFormat_Y42T}, {.subtype = &MFVideoFormat_YVYU}, {.subtype = &MFVideoFormat_420O}, }, .outputs = { {.subtype = &MFVideoFormat_IYUV}, {.subtype = &MFVideoFormat_YV12}, {.subtype = &MFVideoFormat_NV12}, {.subtype = &MFVideoFormat_YUY2}, {.subtype = &MFVideoFormat_ARGB32}, {.subtype = &MFVideoFormat_RGB32}, {.subtype = &MFVideoFormat_NV11}, {.subtype = &MFVideoFormat_AYUV}, {.subtype = &MFVideoFormat_UYVY}, {.subtype = &MEDIASUBTYPE_P208}, {.subtype = &MFVideoFormat_RGB24}, {.subtype = &MFVideoFormat_RGB555}, {.subtype = &MFVideoFormat_RGB565}, {.subtype = &MFVideoFormat_RGB8}, {.subtype = &MFVideoFormat_I420}, {.subtype = &MFVideoFormat_Y216}, {.subtype = &MFVideoFormat_v410}, {.subtype = &MFVideoFormat_Y41P}, {.subtype = &MFVideoFormat_Y41T}, {.subtype = &MFVideoFormat_Y42T}, {.subtype = &MFVideoFormat_YVYU}, }, }; const struct input_type_desc { GUID guid; BOOL optional; } expect_available_inputs[] = { {MFVideoFormat_L8, .optional = TRUE /* >= W10 */}, {MFVideoFormat_L16, .optional = TRUE /* >= W10 */}, {MFAudioFormat_MPEG, .optional = TRUE /* >= W10 */}, {MFVideoFormat_IYUV}, {MFVideoFormat_YV12}, {MFVideoFormat_NV12}, {MFVideoFormat_NV21, .optional = TRUE /* >= W11 */}, {MFVideoFormat_420O}, {MFVideoFormat_P010, .optional = TRUE /* >= W10 */}, {MFVideoFormat_P016, .optional = TRUE /* >= W10 */}, {MFVideoFormat_UYVY}, {MFVideoFormat_YUY2}, {MFVideoFormat_P208}, {MFVideoFormat_NV11}, {MFVideoFormat_AYUV}, {MFVideoFormat_ARGB32}, {MFVideoFormat_ABGR32, .optional = TRUE /* >= W10 */}, {MFVideoFormat_RGB32}, {MFVideoFormat_A2R10G10B10, .optional = TRUE /* >= W10 */}, {MFVideoFormat_A16B16G16R16F, .optional = TRUE /* >= W10 */}, {MFVideoFormat_RGB24}, {MFVideoFormat_I420}, {MFVideoFormat_YVYU}, {MFVideoFormat_RGB555}, {MFVideoFormat_RGB565}, {MFVideoFormat_RGB8}, {MFVideoFormat_Y216}, {MFVideoFormat_v410}, {MFVideoFormat_Y41P}, {MFVideoFormat_Y41T}, {MFVideoFormat_Y42T}, }; const GUID expect_available_outputs[] = { MFVideoFormat_A2R10G10B10, /* enumerated with MFVideoFormat_P010 input */ MFVideoFormat_P010, /* enumerated with MFVideoFormat_A2R10G10B10 input */ MFVideoFormat_YUY2, MFVideoFormat_IYUV, MFVideoFormat_I420, MFVideoFormat_NV12, MFVideoFormat_RGB24, MFVideoFormat_ARGB32, MFVideoFormat_RGB32, MFVideoFormat_YV12, MFVideoFormat_Y216, /* enumerated with some input formats */ MFVideoFormat_UYVY, /* enumerated with some input formats */ MFVideoFormat_YVYU, /* enumerated with some input formats */ MFVideoFormat_AYUV, MFVideoFormat_RGB555, MFVideoFormat_RGB565, MFVideoFormat_AYUV, /* some inputs enumerate MFVideoFormat_AYUV after RGB565 */ MFVideoFormat_NV12, /* P010 enumerates NV12 after (A)RGB32 formats */ MFVideoFormat_A16B16G16R16F, /* enumerated with MFVideoFormat_P010 input */ MFVideoFormat_NV21, /* enumerated with some input formats */ }; static const media_type_desc expect_available_common = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), }; static const struct attribute_desc expect_transform_attributes[] = { ATTR_UINT32(MFT_SUPPORT_3DVIDEO, 1, .todo = TRUE), /* ATTR_UINT32(MF_SA_D3D_AWARE, 1), only on W7 */ {0}, }; static const MFVideoArea actual_aperture = {.Area={82,84}}; static const DWORD actual_width = 96, actual_height = 96; const struct attribute_desc rgb32_with_aperture[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height, .required = TRUE), ATTR_BLOB(MF_MT_MINIMUM_DISPLAY_APERTURE, &actual_aperture, 16), {0}, }; const struct attribute_desc nv12_default_stride[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height, .required = TRUE), {0}, }; const struct attribute_desc rgb32_default_stride[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height, .required = TRUE), {0}, }; const struct attribute_desc rgb32_negative_stride[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height, .required = TRUE), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, -actual_width * 4), {0}, }; const struct attribute_desc rgb32_positive_stride[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height, .required = TRUE), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, actual_width * 4), {0}, }; const struct attribute_desc rgb555_default_stride[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB555, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height, .required = TRUE), {0}, }; const struct attribute_desc rgb555_negative_stride[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB555, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height, .required = TRUE), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, -actual_width * 2), {0}, }; const struct attribute_desc rgb555_positive_stride[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB555, .required = TRUE), ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height, .required = TRUE), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, actual_width * 2), {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}; MFT_INPUT_STREAM_INFO input_info = {0}; const struct attribute_desc output_sample_attributes[] = { ATTR_UINT32(MFSampleExtension_CleanPoint, 1, .todo = TRUE), {0}, }; const struct buffer_desc rgb32_buffer_desc = { .length = actual_width * actual_height * 4, .compare = compare_rgb32, .dump = dump_rgb32, .rect = {.top = 12, .right = 82, .bottom = 96}, }; const struct sample_desc rgb32_sample_desc = { .attributes = output_sample_attributes, .sample_time = 0, .sample_duration = 10000000, .buffer_count = 1, .buffers = &rgb32_buffer_desc, }; const struct buffer_desc rgb555_buffer_desc = { .length = actual_width * actual_height * 2, .compare = compare_rgb16, .dump = dump_rgb16, .rect = {.top = 12, .right = 82, .bottom = 96}, }; const struct sample_desc rgb555_sample_desc = { .attributes = output_sample_attributes, .sample_time = 0, .sample_duration = 10000000, .buffer_count = 1, .buffers = &rgb555_buffer_desc, }; const struct buffer_desc nv12_buffer_desc = { .length = actual_width * actual_height * 3 / 2, .compare = compare_nv12, .dump = dump_nv12, .rect = {.top = 12, .right = 82, .bottom = 96}, }; const struct sample_desc nv12_sample_desc = { .attributes = output_sample_attributes, .sample_time = 0, .sample_duration = 10000000, .buffer_count = 1, .buffers = &nv12_buffer_desc, }; const struct transform_desc { const struct attribute_desc *input_type_desc; const struct attribute_desc *output_type_desc; const struct sample_desc *output_sample_desc; const WCHAR *result_bitmap; ULONG delta; BOOL broken; } video_processor_tests[] = { { .input_type_desc = nv12_default_stride, .output_type_desc = rgb32_default_stride, .output_sample_desc = &rgb32_sample_desc, .result_bitmap = L"rgb32frame-flip.bmp", .delta = 2, /* Windows returns 0, Wine needs 2 */ }, { .input_type_desc = nv12_default_stride, .output_type_desc = rgb32_negative_stride, .output_sample_desc = &rgb32_sample_desc, .result_bitmap = L"rgb32frame-flip.bmp", .delta = 2, /* Windows returns 0, Wine needs 2 */ }, { .input_type_desc = nv12_default_stride, .output_type_desc = rgb32_positive_stride, .output_sample_desc = &rgb32_sample_desc, .result_bitmap = L"rgb32frame.bmp", .delta = 6, }, { .input_type_desc = rgb32_default_stride, .output_type_desc = nv12_default_stride, .output_sample_desc = &nv12_sample_desc, .result_bitmap = L"nv12frame-flip.bmp", .delta = 2, /* Windows returns 0, Wine needs 2 */ }, { .input_type_desc = rgb32_negative_stride, .output_type_desc = nv12_default_stride, .output_sample_desc = &nv12_sample_desc, .result_bitmap = L"nv12frame-flip.bmp", .delta = 2, /* Windows returns 0, Wine needs 2 */ }, { .input_type_desc = rgb32_positive_stride, .output_type_desc = nv12_default_stride, .output_sample_desc = &nv12_sample_desc, .result_bitmap = L"nv12frame.bmp", .delta = 2, /* Windows returns 1, Wine needs 2 */ }, { .input_type_desc = rgb32_negative_stride, .output_type_desc = rgb32_negative_stride, .output_sample_desc = &rgb32_sample_desc, .result_bitmap = L"rgb32frame.bmp", }, { .input_type_desc = rgb32_negative_stride, .output_type_desc = rgb32_positive_stride, .output_sample_desc = &rgb32_sample_desc, .result_bitmap = L"rgb32frame-flip.bmp", .delta = 3, /* Windows returns 3 */ }, { .input_type_desc = rgb32_positive_stride, .output_type_desc = rgb32_negative_stride, .output_sample_desc = &rgb32_sample_desc, .result_bitmap = L"rgb32frame-flip.bmp", .delta = 3, /* Windows returns 3 */ }, { .input_type_desc = rgb32_positive_stride, .output_type_desc = rgb32_positive_stride, .output_sample_desc = &rgb32_sample_desc, .result_bitmap = L"rgb32frame.bmp", }, { .input_type_desc = rgb32_with_aperture, .output_type_desc = rgb32_with_aperture, .output_sample_desc = &rgb32_sample_desc, .result_bitmap = L"rgb32frame.bmp", .broken = TRUE /* old Windows version incorrectly rescale */ }, { .input_type_desc = rgb32_default_stride, .output_type_desc = rgb555_default_stride, .output_sample_desc = &rgb555_sample_desc, .result_bitmap = L"rgb555frame.bmp", }, { .input_type_desc = rgb32_default_stride, .output_type_desc = rgb555_negative_stride, .output_sample_desc = &rgb555_sample_desc, .result_bitmap = L"rgb555frame.bmp", }, { .input_type_desc = rgb32_default_stride, .output_type_desc = rgb555_positive_stride, .output_sample_desc = &rgb555_sample_desc, .result_bitmap = L"rgb555frame-flip.bmp", .delta = 3, /* Windows returns 0, Wine needs 3 */ }, { .input_type_desc = rgb555_default_stride, .output_type_desc = rgb555_positive_stride, .output_sample_desc = &rgb555_sample_desc, .result_bitmap = L"rgb555frame-flip.bmp", .delta = 4, /* Windows returns 0, Wine needs 4 */ }, }; MFT_REGISTER_TYPE_INFO output_type = {MFMediaType_Video, MFVideoFormat_NV12}; MFT_REGISTER_TYPE_INFO input_type = {MFMediaType_Video, MFVideoFormat_I420}; const struct input_type_desc *expect_input = expect_available_inputs; DWORD i, j, k, flags, length, output_status; IMFSample *input_sample, *output_sample; IMFMediaType *media_type, *media_type2; IMFCollection *output_samples; IMFTransform *transform; IMFMediaBuffer *buffer; const BYTE *input_data; ULONG input_data_len; UINT32 count; HRESULT hr; ULONG ret; GUID guid; LONG ref; hr = CoInitialize(NULL); ok(hr == S_OK, "Failed to initialize, hr %#lx.\n", hr); winetest_push_context("videoproc"); if (!check_mft_enum(MFT_CATEGORY_VIDEO_PROCESSOR, &input_type, &output_type, class_id)) goto failed; check_mft_get_info(class_id, &expect_mft_info); if (FAILED(hr = CoCreateInstance(class_id, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)&transform))) goto failed; todo_wine check_interface(transform, &IID_IMFVideoProcessorControl, TRUE); todo_wine check_interface(transform, &IID_IMFRealTimeClientEx, TRUE); check_interface(transform, &IID_IMFMediaEventGenerator, FALSE); check_interface(transform, &IID_IMFShutdown, FALSE); hr = IMFTransform_GetInputStatus(transform, 0, &flags); ok(hr == MF_E_TRANSFORM_TYPE_NOT_SET, "Unexpected hr %#lx.\n", hr); hr = IMFTransform_GetOutputStatus(transform, &flags); ok(hr == MF_E_TRANSFORM_TYPE_NOT_SET, "Unexpected hr %#lx.\n", hr); hr = IMFTransform_GetOutputAvailableType(transform, 0, 0, &media_type); ok(hr == MF_E_NO_MORE_TYPES, "Unexpected hr %#lx.\n", hr); check_mft_get_input_current_type(transform, NULL); check_mft_get_output_current_type(transform, NULL); check_mft_get_input_stream_info(transform, S_OK, &initial_input_info); check_mft_get_output_stream_info(transform, S_OK, &initial_output_info); /* Configure stream types. */ for (i = 0;;++i) { if (FAILED(hr = IMFTransform_GetInputAvailableType(transform, 0, i, &media_type))) { ok(hr == MF_E_NO_MORE_TYPES, "Unexpected hr %#lx.\n", hr); break; } hr = IMFTransform_GetInputAvailableType(transform, 0, i, &media_type2); ok(hr == S_OK, "Failed to get available type, hr %#lx.\n", hr); ok(media_type != media_type2, "Unexpected instance.\n"); ref = IMFMediaType_Release(media_type2); ok(ref == 0, "Release returned %ld\n", ref); hr = IMFMediaType_GetMajorType(media_type, &guid); ok(hr == S_OK, "Failed to get major type, hr %#lx.\n", hr); ok(IsEqualGUID(&guid, &MFMediaType_Video), "Unexpected major type.\n"); hr = IMFMediaType_GetCount(media_type, &count); ok(hr == S_OK, "Failed to get attributes count, hr %#lx.\n", hr); ok(count == 2, "Unexpected count %u.\n", count); hr = IMFMediaType_GetGUID(media_type, &MF_MT_SUBTYPE, &guid); ok(hr == S_OK, "Failed to get subtype, hr %#lx.\n", hr); ok(is_supported_video_type(&guid), "Unexpected media type %s.\n", wine_dbgstr_guid(&guid)); hr = IMFTransform_SetInputType(transform, 0, media_type, MFT_SET_TYPE_TEST_ONLY); ok(FAILED(hr), "Unexpected hr %#lx.\n", hr); hr = IMFTransform_SetInputType(transform, 0, media_type, 0); ok(FAILED(hr), "Unexpected hr %#lx.\n", hr); hr = IMFTransform_GetOutputCurrentType(transform, 0, &media_type2); ok(hr == MF_E_TRANSFORM_TYPE_NOT_SET, "Unexpected hr %#lx.\n", hr); /* FIXME: figure out if those require additional attributes or simply advertised but not supported */ if (IsEqualGUID(&guid, &MFVideoFormat_L8) || IsEqualGUID(&guid, &MFVideoFormat_L16) || IsEqualGUID(&guid, &MFVideoFormat_D16) || IsEqualGUID(&guid, &MFVideoFormat_420O) || IsEqualGUID(&guid, &MFVideoFormat_A16B16G16R16F)) { ref = IMFMediaType_Release(media_type); ok(ref == 0, "Release returned %ld\n", ref); continue; } hr = IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_SIZE, ((UINT64)16 << 32) | 16); ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); hr = IMFTransform_SetInputType(transform, 0, media_type, MFT_SET_TYPE_TEST_ONLY); ok(hr == S_OK, "Failed to test input type %s, hr %#lx.\n", wine_dbgstr_guid(&guid), hr); hr = IMFTransform_SetInputType(transform, 0, media_type, 0); ok(hr == S_OK, "Failed to test input type, hr %#lx.\n", hr); hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type2); ok(hr == S_OK, "Failed to get current type, hr %#lx.\n", hr); ok(media_type != media_type2, "Unexpected instance.\n"); IMFMediaType_Release(media_type2); hr = IMFTransform_GetInputStatus(transform, 0, &flags); ok(hr == S_OK, "Failed to get input status, hr %#lx.\n", hr); ok(flags == MFT_INPUT_STATUS_ACCEPT_DATA, "Unexpected input status %#lx.\n", flags); input_info.cbSize = 0; if (IsEqualGUID(&guid, &MFVideoFormat_NV21)) input_info.cbSize = 0x180; else if (!IsEqualGUID(&guid, &MFVideoFormat_P208) && !IsEqualGUID(&guid, &MEDIASUBTYPE_Y41T) && !IsEqualGUID(&guid, &MEDIASUBTYPE_Y42T)) { hr = MFCalculateImageSize(&guid, 16, 16, (UINT32 *)&input_info.cbSize); todo_wine_if(IsEqualGUID(&guid, &MFVideoFormat_Y216) || IsEqualGUID(&guid, &MFVideoFormat_v410) || IsEqualGUID(&guid, &MFVideoFormat_Y41P)) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); } check_mft_get_input_stream_info(transform, S_OK, &input_info); check_mft_get_output_stream_info(transform, S_OK, &initial_output_info); IMFMediaType_Release(media_type); } /* IYUV -> RGB32 */ hr = MFCreateMediaType(&media_type); ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_IYUV); ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); hr = IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_SIZE, ((UINT64)16 << 32) | 16); ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); hr = IMFTransform_SetInputType(transform, 0, media_type, 0); ok(hr == S_OK, "Failed to set input type, hr %#lx.\n", hr); hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_RGB32); ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); hr = IMFTransform_SetOutputType(transform, 0, media_type, 0); ok(hr == S_OK, "Failed to set output type, hr %#lx.\n", hr); hr = MFCalculateImageSize(&MFVideoFormat_IYUV, 16, 16, (UINT32 *)&input_info.cbSize); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = MFCalculateImageSize(&MFVideoFormat_RGB32, 16, 16, (UINT32 *)&output_info.cbSize); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); check_mft_get_input_stream_info(transform, S_OK, &input_info); check_mft_get_output_stream_info(transform, S_OK, &output_info); hr = MFCreateSample(&input_sample); ok(hr == S_OK, "Failed to create a sample, hr %#lx.\n", hr); hr = MFCreateSample(&output_sample); ok(hr == S_OK, "Failed to create a sample, hr %#lx.\n", hr); hr = check_mft_process_output(transform, output_sample, &output_status); todo_wine ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "Unexpected hr %#lx.\n", hr); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); todo_wine ok(hr == S_OK, "Failed to push a sample, hr %#lx.\n", hr); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); todo_wine ok(hr == MF_E_NOTACCEPTING, "Unexpected hr %#lx.\n", hr); hr = check_mft_process_output(transform, output_sample, &output_status); todo_wine ok(hr == MF_E_NO_SAMPLE_TIMESTAMP, "Unexpected hr %#lx.\n", hr); hr = IMFSample_SetSampleTime(input_sample, 0); ok(hr == S_OK, "Failed to set sample time, hr %#lx.\n", hr); hr = check_mft_process_output(transform, output_sample, &output_status); todo_wine ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); hr = MFCreateMemoryBuffer(1024 * 1024, &buffer); ok(hr == S_OK, "Failed to create a buffer, hr %#lx.\n", hr); hr = IMFSample_AddBuffer(input_sample, buffer); ok(hr == S_OK, "Failed to add a buffer, hr %#lx.\n", hr); hr = IMFSample_AddBuffer(output_sample, buffer); ok(hr == S_OK, "Failed to add a buffer, hr %#lx.\n", hr); hr = check_mft_process_output(transform, output_sample, &output_status); todo_wine ok(hr == S_OK || broken(FAILED(hr)) /* Win8 */, "Failed to get output buffer, hr %#lx.\n", hr); if (SUCCEEDED(hr)) { hr = check_mft_process_output(transform, output_sample, &output_status); ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "Unexpected hr %#lx.\n", hr); } ref = IMFTransform_Release(transform); ok(ref == 0, "Release returned %ld\n", ref); ref = IMFMediaType_Release(media_type); ok(ref == 0, "Release returned %ld\n", ref); ref = IMFSample_Release(input_sample); ok(ref == 0, "Release returned %ld\n", ref); ref = IMFSample_Release(output_sample); ok(ref == 0, "Release returned %ld\n", ref); ref = IMFMediaBuffer_Release(buffer); ok(ref == 0, "Release returned %ld\n", ref); hr = CoCreateInstance(class_id, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)&transform); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); check_interface(transform, &IID_IMFTransform, TRUE); check_interface(transform, &IID_IMediaObject, FALSE); check_interface(transform, &IID_IPropertyStore, FALSE); check_interface(transform, &IID_IPropertyBag, FALSE); check_mft_optional_methods(transform, 1); check_mft_get_attributes(transform, expect_transform_attributes, TRUE); check_mft_get_input_stream_info(transform, S_OK, &initial_input_info); check_mft_get_output_stream_info(transform, S_OK, &initial_output_info); hr = IMFTransform_GetOutputAvailableType(transform, 0, 0, &media_type); ok(hr == MF_E_NO_MORE_TYPES, "GetOutputAvailableType returned %#lx\n", hr); i = -1; while (SUCCEEDED(hr = IMFTransform_GetInputAvailableType(transform, 0, ++i, &media_type))) { winetest_push_context("in %lu", i); ok(hr == S_OK, "GetInputAvailableType returned %#lx\n", hr); check_media_type(media_type, expect_available_common, -1); hr = IMFMediaType_GetGUID(media_type, &MF_MT_SUBTYPE, &guid); ok(hr == S_OK, "GetGUID returned %#lx\n", hr); while (!IsEqualGUID(&expect_input->guid, &guid)) { if (!expect_input->optional) break; expect_input++; } ok(IsEqualGUID(&expect_input->guid, &guid), "got subtype %s\n", debugstr_guid(&guid)); expect_input++; /* FIXME: Skip exotic input types which aren't directly accepted */ if (IsEqualGUID(&guid, &MFVideoFormat_L8) || IsEqualGUID(&guid, &MFVideoFormat_L16) || IsEqualGUID(&guid, &MFAudioFormat_MPEG) || IsEqualGUID(&guid, &MFVideoFormat_420O) || IsEqualGUID(&guid, &MFVideoFormat_A16B16G16R16F) /* w1064v1507 */) { IMFMediaType_Release(media_type); winetest_pop_context(); continue; } hr = IMFTransform_SetInputType(transform, 0, media_type, 0); ok(hr == MF_E_ATTRIBUTENOTFOUND, "SetInputType returned %#lx.\n", hr); hr = IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_SIZE, (UINT64)actual_width << 32 | actual_height); ok(hr == S_OK, "SetUINT64 returned %#lx.\n", hr); hr = IMFTransform_SetInputType(transform, 0, media_type, 0); ok(hr == S_OK, "SetInputType returned %#lx.\n", hr); hr = IMFTransform_GetOutputAvailableType(transform, 0, 0, &media_type2); ok(hr == S_OK, "GetOutputAvailableType returned %#lx.\n", hr); hr = IMFMediaType_IsEqual(media_type, media_type2, &flags); ok(hr == S_OK, "IsEqual returned %#lx.\n", hr); IMFMediaType_Release(media_type2); ret = IMFMediaType_Release(media_type); ok(ret == 1, "Release returned %lu\n", ret); j = k = 0; while (SUCCEEDED(hr = IMFTransform_GetOutputAvailableType(transform, 0, ++j, &media_type))) { winetest_push_context("out %lu", j); ok(hr == S_OK, "GetOutputAvailableType returned %#lx\n", hr); check_media_type(media_type, expect_available_common, -1); hr = IMFMediaType_GetGUID(media_type, &MF_MT_SUBTYPE, &guid); ok(hr == S_OK, "GetGUID returned %#lx\n", hr); for (; k < ARRAY_SIZE(expect_available_outputs); k++) if (IsEqualGUID(&expect_available_outputs[k], &guid)) break; ok(k < ARRAY_SIZE(expect_available_outputs), "got subtype %s\n", debugstr_guid(&guid)); ret = IMFMediaType_Release(media_type); ok(ret == 0, "Release returned %lu\n", ret); winetest_pop_context(); } ok(hr == MF_E_NO_MORE_TYPES, "GetOutputAvailableType returned %#lx\n", hr); winetest_pop_context(); } ok(hr == MF_E_NO_MORE_TYPES, "GetInputAvailableType returned %#lx\n", hr); for (i = 0; i < ARRAY_SIZE(video_processor_tests); i++) { const struct transform_desc *test = video_processor_tests + i; winetest_push_context("transform #%lu", i); check_mft_set_input_type_required(transform, test->input_type_desc); check_mft_set_input_type(transform, test->input_type_desc); check_mft_get_input_current_type(transform, test->input_type_desc); check_mft_set_output_type_required(transform, test->output_type_desc); check_mft_set_output_type(transform, test->output_type_desc, S_OK); check_mft_get_output_current_type(transform, test->output_type_desc); if (test->output_sample_desc == &nv12_sample_desc) { output_info.cbSize = actual_width * actual_height * 3 / 2; check_mft_get_output_stream_info(transform, S_OK, &output_info); } else if (test->output_sample_desc == &rgb555_sample_desc) { output_info.cbSize = actual_width * actual_height * 2; check_mft_get_output_stream_info(transform, S_OK, &output_info); } else { output_info.cbSize = actual_width * actual_height * 4; check_mft_get_output_stream_info(transform, S_OK, &output_info); } if (test->input_type_desc == nv12_default_stride) { input_info.cbSize = actual_width * actual_height * 3 / 2; check_mft_get_input_stream_info(transform, S_OK, &input_info); load_resource(L"nv12frame.bmp", &input_data, &input_data_len); /* skip BMP header and RGB data from the dump */ length = *(DWORD *)(input_data + 2); input_data_len = input_data_len - length; ok(input_data_len == 13824, "got length %lu\n", input_data_len); input_data = input_data + length; } else if (test->input_type_desc == rgb555_default_stride) { input_info.cbSize = actual_width * actual_height * 2; check_mft_get_input_stream_info(transform, S_OK, &input_info); load_resource(L"rgb555frame.bmp", &input_data, &input_data_len); /* skip BMP header and RGB data from the dump */ length = *(DWORD *)(input_data + 2 + 2 * sizeof(DWORD)); input_data_len -= length; ok(input_data_len == 18432, "got length %lu\n", input_data_len); input_data += length; } else { input_info.cbSize = actual_width * actual_height * 4; check_mft_get_input_stream_info(transform, S_OK, &input_info); load_resource(L"rgb32frame.bmp", &input_data, &input_data_len); /* skip BMP header and RGB data from the dump */ length = *(DWORD *)(input_data + 2 + 2 * sizeof(DWORD)); input_data_len -= length; ok(input_data_len == 36864, "got length %lu\n", input_data_len); input_data += length; } input_sample = create_sample(input_data, input_data_len); hr = IMFSample_SetSampleTime(input_sample, 0); ok(hr == S_OK, "SetSampleTime returned %#lx\n", hr); hr = IMFSample_SetSampleDuration(input_sample, 10000000); ok(hr == S_OK, "SetSampleDuration returned %#lx\n", hr); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == S_OK, "ProcessInput returned %#lx\n", hr); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == MF_E_NOTACCEPTING, "ProcessInput returned %#lx\n", hr); hr = IMFTransform_ProcessMessage(transform, MFT_MESSAGE_COMMAND_DRAIN, 0); ok(hr == S_OK, "ProcessMessage returned %#lx\n", hr); ret = IMFSample_Release(input_sample); ok(ret <= 1, "Release returned %ld\n", ret); hr = MFCreateCollection(&output_samples); ok(hr == S_OK, "MFCreateCollection returned %#lx\n", hr); output_sample = create_sample(NULL, output_info.cbSize); hr = check_mft_process_output(transform, output_sample, &output_status); ok(hr == S_OK || broken(hr == MF_E_SHUTDOWN) /* w8 */, "ProcessOutput returned %#lx\n", hr); if (hr != S_OK) { win_skip("ProcessOutput returned MF_E_SHUTDOWN, skipping tests.\n"); } else { ok(output_status == 0, "got output[0].dwStatus %#lx\n", output_status); hr = IMFCollection_AddElement(output_samples, (IUnknown *)output_sample); ok(hr == S_OK, "AddElement returned %#lx\n", hr); ref = IMFSample_Release(output_sample); ok(ref == 1, "Release returned %ld\n", ref); ret = check_mf_sample_collection(output_samples, test->output_sample_desc, test->result_bitmap); ok(ret <= test->delta || broken(test->broken), "got %lu%% diff\n", ret); IMFCollection_Release(output_samples); output_sample = create_sample(NULL, output_info.cbSize); hr = check_mft_process_output(transform, output_sample, &output_status); ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); ok(output_status == 0, "got output[0].dwStatus %#lx\n", output_status); hr = IMFSample_GetTotalLength(output_sample, &length); ok(hr == S_OK, "GetTotalLength returned %#lx\n", hr); ok(length == 0, "got length %lu\n", length); } ret = IMFSample_Release(output_sample); ok(ret == 0, "Release returned %lu\n", ret); winetest_pop_context(); } ret = IMFTransform_Release(transform); ok(ret == 0, "Release returned %ld\n", ret); failed: winetest_pop_context(); CoUninitialize(); } static void test_mp3_decoder(void) { const GUID *const class_id = &CLSID_CMP3DecMediaObject; const struct transform_info expect_mft_info = { .name = L"MP3 Decoder MFT", .major_type = &MFMediaType_Audio, .inputs = { {.subtype = &MFAudioFormat_MP3}, }, .outputs = { {.subtype = &MFAudioFormat_PCM}, }, }; const struct transform_info expect_dmo_info = { .name = L"MP3 Decoder DMO", .major_type = &MEDIATYPE_Audio, .inputs = { {.subtype = &MFAudioFormat_MP3}, }, .outputs = { {.subtype = &MEDIASUBTYPE_PCM}, }, }; static const ULONG mp3dec_block_size = 0x1200; static const media_type_desc expect_available_inputs[] = { { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_MP3), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), }, }; static const media_type_desc expect_available_outputs[] = { { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_Float), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 32), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 22050), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 176400), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 8), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 22050), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 88200), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 4), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 8), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 22050), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 44100), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 2), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_Float), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 32), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 1), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 22050), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 88200), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 4), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 1), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 22050), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 44100), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 2), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), }, { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 8), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 1), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 22050), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 22050), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 1), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), }, }; const struct attribute_desc input_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_MP3, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 22050), {0}, }; static const struct attribute_desc output_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio, .required = TRUE), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 22050, .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 2 * (16 / 8), .required = TRUE), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 2 * (16 / 8) * 22050, .required = TRUE), {0}, }; const struct attribute_desc expect_input_type_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_MP3), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 22050), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), ATTR_UINT32(MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1), {0}, }; static const struct attribute_desc expect_output_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, 16), ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 22050), ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 4), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 22050 * 4), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_UINT32(MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1), {0}, }; const MFT_OUTPUT_STREAM_INFO output_info = { .cbSize = mp3dec_block_size, .cbAlignment = 1, }; const MFT_INPUT_STREAM_INFO input_info = { .cbAlignment = 1, }; const struct buffer_desc output_buffer_desc[] = { {.length = 0x9c0, .compare = compare_pcm16}, {.length = mp3dec_block_size, .compare = compare_pcm16}, }; const struct attribute_desc output_sample_attributes[] = { ATTR_UINT32(mft_output_sample_incomplete, 1), ATTR_UINT32(MFSampleExtension_CleanPoint, 1), {0}, }; const struct sample_desc output_sample_desc[] = { { .attributes = output_sample_attributes + 0, .sample_time = 0, .sample_duration = 282993, .buffer_count = 1, .buffers = output_buffer_desc + 0, }, { .attributes = output_sample_attributes + 0, .sample_time = 282993, .sample_duration = 522449, .buffer_count = 1, .buffers = output_buffer_desc + 1, .repeat_count = 18, }, { .attributes = output_sample_attributes + 1, /* not MFT_OUTPUT_DATA_BUFFER_INCOMPLETE */ .sample_time = 10209524, .sample_duration = 522449, .buffer_count = 1, .buffers = output_buffer_desc + 1, }, }; MFT_REGISTER_TYPE_INFO output_type = {MFMediaType_Audio, MFAudioFormat_PCM}; MFT_REGISTER_TYPE_INFO input_type = {MFMediaType_Audio, MFAudioFormat_MP3}; IMFSample *input_sample, *output_sample; IMFCollection *output_samples; DWORD length, output_status; IMFMediaType *media_type; IMFTransform *transform; const BYTE *mp3enc_data; ULONG mp3enc_data_len; ULONG i, ret, ref; HRESULT hr; hr = CoInitialize(NULL); ok(hr == S_OK, "Failed to initialize, hr %#lx.\n", hr); winetest_push_context("mp3dec"); if (!check_mft_enum(MFT_CATEGORY_AUDIO_DECODER, &input_type, &output_type, class_id)) goto failed; check_mft_get_info(class_id, &expect_mft_info); check_dmo_get_info(class_id, &expect_dmo_info); if (FAILED(hr = CoCreateInstance(class_id, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)&transform))) goto failed; check_interface(transform, &IID_IMFTransform, TRUE); check_interface(transform, &IID_IMediaObject, TRUE); todo_wine check_interface(transform, &IID_IPropertyStore, TRUE); check_interface(transform, &IID_IPropertyBag, FALSE); check_mft_optional_methods(transform, 1); check_mft_get_attributes(transform, NULL, 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); hr = IMFTransform_GetOutputAvailableType(transform, 0, 0, &media_type); ok(hr == MF_E_TRANSFORM_TYPE_NOT_SET, "GetOutputAvailableType returned %#lx\n", hr); i = -1; while (SUCCEEDED(hr = IMFTransform_GetInputAvailableType(transform, 0, ++i, &media_type))) { winetest_push_context("in %lu", i); ok(hr == S_OK, "GetInputAvailableType returned %#lx\n", hr); check_media_type(media_type, expect_available_inputs[i], -1); ret = IMFMediaType_Release(media_type); ok(ret == 0, "Release returned %lu\n", ret); winetest_pop_context(); } todo_wine ok(hr == MF_E_NO_MORE_TYPES, "GetInputAvailableType returned %#lx\n", hr); todo_wine ok(i == ARRAY_SIZE(expect_available_inputs), "%lu input media types\n", i); /* 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); 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); 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); /* check new output media types */ i = -1; while (SUCCEEDED(hr = IMFTransform_GetOutputAvailableType(transform, 0, ++i, &media_type))) { winetest_push_context("out %lu", i); ok(hr == S_OK, "GetOutputAvailableType returned %#lx\n", hr); check_media_type(media_type, expect_available_outputs[i], -1); ret = IMFMediaType_Release(media_type); ok(ret == 0, "Release returned %lu\n", ret); winetest_pop_context(); } ok(hr == MF_E_NO_MORE_TYPES, "GetOutputAvailableType returned %#lx\n", hr); ok(i == ARRAY_SIZE(expect_available_outputs), "%lu output media types\n", i); 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); check_mft_get_input_stream_info(transform, S_OK, &input_info); check_mft_get_output_stream_info(transform, S_OK, &output_info); load_resource(L"mp3encdata.bin", &mp3enc_data, &mp3enc_data_len); ok(mp3enc_data_len == 6295, "got length %lu\n", mp3enc_data_len); input_sample = create_sample(mp3enc_data, mp3enc_data_len); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == S_OK, "ProcessInput returned %#lx\n", hr); ret = IMFSample_Release(input_sample); ok(ret == 1, "Release returned %lu\n", ret); input_sample = create_sample(mp3enc_data, mp3enc_data_len); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); ok(hr == MF_E_NOTACCEPTING, "ProcessInput returned %#lx\n", hr); ret = IMFSample_Release(input_sample); ok(ret == 0, "Release returned %lu\n", ret); hr = IMFTransform_ProcessMessage(transform, MFT_MESSAGE_COMMAND_DRAIN, 0); ok(hr == S_OK, "ProcessMessage returned %#lx\n", hr); hr = MFCreateCollection(&output_samples); ok(hr == S_OK, "MFCreateCollection returned %#lx\n", hr); /* first sample is broken */ output_sample = create_sample(NULL, output_info.cbSize); hr = check_mft_process_output(transform, output_sample, &output_status); ok(hr == S_OK, "ProcessOutput returned %#lx\n", hr); ok(output_status == MFT_OUTPUT_DATA_BUFFER_INCOMPLETE, "got output[0].dwStatus %#lx\n", output_status); hr = IMFSample_GetTotalLength(output_sample, &length); ok(hr == S_OK, "GetTotalLength returned %#lx\n", hr); ok(length == mp3dec_block_size /* Win8 */ || length == 0x9c0 /* Win10 */ || length == 0x900 /* Win7 */, "got length %lu\n", length); hr = IMFCollection_AddElement(output_samples, (IUnknown *)output_sample); ok(hr == S_OK, "AddElement returned %#lx\n", hr); ref = IMFSample_Release(output_sample); ok(ref == 1, "Release returned %ld\n", ref); output_sample = create_sample(NULL, output_info.cbSize); for (i = 0; SUCCEEDED(hr = check_mft_process_output(transform, output_sample, &output_status)); i++) { winetest_push_context("%lu", i); ok(hr == S_OK, "ProcessOutput returned %#lx\n", hr); ok(!(output_status & ~MFT_OUTPUT_DATA_BUFFER_INCOMPLETE), "got output[0].dwStatus %#lx\n", output_status); hr = IMFCollection_AddElement(output_samples, (IUnknown *)output_sample); ok(hr == S_OK, "AddElement returned %#lx\n", hr); ref = IMFSample_Release(output_sample); ok(ref == 1, "Release returned %ld\n", ref); output_sample = create_sample(NULL, output_info.cbSize); winetest_pop_context(); } ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); ok(output_status == 0, "got output[0].dwStatus %#lx\n", output_status); ret = IMFSample_Release(output_sample); ok(ret == 0, "Release returned %lu\n", ret); ok(i == 20 || broken(i == 41) /* Win7 */, "got %lu output samples\n", i); if (broken(length != 0x9c0)) win_skip("Skipping MP3 decoder output sample checks on Win7 / Win8\n"); else { ret = check_mf_sample_collection(output_samples, output_sample_desc, L"mp3decdata.bin"); ok(ret == 0, "got %lu%% diff\n", ret); } IMFCollection_Release(output_samples); output_sample = create_sample(NULL, mp3dec_block_size); hr = check_mft_process_output(transform, output_sample, &output_status); ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); ok(output_status == 0, "got output[0].dwStatus %#lx\n", output_status); hr = IMFSample_GetTotalLength(output_sample, &length); ok(hr == S_OK, "GetTotalLength returned %#lx\n", hr); ok(length == 0, "got length %lu\n", length); ret = IMFSample_Release(output_sample); ok(ret == 0, "Release returned %lu\n", ret); ret = IMFTransform_Release(transform); ok(ret == 0, "Release returned %lu\n", ret); failed: winetest_pop_context(); 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 available, 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); 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); 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); 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, 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); 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); 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; 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, NULL, 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(); } static void test_iv50_encoder(void) { static const BITMAPINFOHEADER expect_iv50 = { .biSize = sizeof(BITMAPINFOHEADER), .biWidth = 0x60, .biHeight = 0x60, .biPlanes = 1, .biBitCount = 0x18, .biCompression = mmioFOURCC('I','V','5','0'), .biSizeImage = 0x5100, }; const struct buffer_desc iv50_buffer_desc = { .length = 0x2e8, }; const struct sample_desc iv50_sample_desc = { .buffer_count = 1, .buffers = &iv50_buffer_desc, }; const BYTE *res_data; BYTE rgb_data[0x6c00], iv50_data[0x5100]; BITMAPINFO rgb_info, iv50_info; IMFCollection *collection; DWORD flags, res_len, ret; IMFSample *sample; LRESULT res; HRESULT hr; HIC hic; load_resource(L"rgb555frame.bmp", (const BYTE **)&res_data, &res_len); res_data += 2 + 3 * sizeof(DWORD); res_len -= 2 + 3 * sizeof(DWORD); rgb_info.bmiHeader = *(BITMAPINFOHEADER *)res_data; memcpy(rgb_data, res_data + sizeof(BITMAPINFOHEADER), res_len - sizeof(BITMAPINFOHEADER)); hic = ICOpen(ICTYPE_VIDEO, expect_iv50.biCompression, ICMODE_COMPRESS); if (!hic) { todo_wine win_skip("ICOpen failed to created IV50 compressor.\n"); return; } res = ICCompressQuery(hic, &rgb_info, NULL); todo_wine ok(!res, "got res %#Ix\n", res); if (res) goto done; res = ICCompressGetSize(hic, &rgb_info, NULL); ok(res == expect_iv50.biSizeImage, "got res %#Ix\n", res); res = ICCompressGetFormatSize(hic, &rgb_info); ok(res == sizeof(BITMAPINFOHEADER), "got res %#Ix\n", res); res = ICCompressGetFormat(hic, &rgb_info, &iv50_info); ok(!res, "got res %#Ix\n", res); check_member(iv50_info.bmiHeader, expect_iv50, "%#lx", biSize); check_member(iv50_info.bmiHeader, expect_iv50, "%#lx", biWidth); check_member(iv50_info.bmiHeader, expect_iv50, "%#lx", biHeight); check_member(iv50_info.bmiHeader, expect_iv50, "%#x", biPlanes); check_member(iv50_info.bmiHeader, expect_iv50, "%#x", biBitCount); check_member(iv50_info.bmiHeader, expect_iv50, "%#lx", biCompression); check_member(iv50_info.bmiHeader, expect_iv50, "%#lx", biSizeImage); check_member(iv50_info.bmiHeader, expect_iv50, "%#lx", biXPelsPerMeter); check_member(iv50_info.bmiHeader, expect_iv50, "%#lx", biYPelsPerMeter); check_member(iv50_info.bmiHeader, expect_iv50, "%#lx", biClrUsed); check_member(iv50_info.bmiHeader, expect_iv50, "%#lx", biClrImportant); res = ICCompressQuery(hic, &rgb_info, &iv50_info); ok(!res, "got res %#Ix\n", res); res = ICCompressBegin(hic, &rgb_info, &iv50_info); ok(!res, "got res %#Ix\n", res); memset(iv50_data, 0xcd, sizeof(iv50_data)); res = ICCompress(hic, ICCOMPRESS_KEYFRAME, &iv50_info.bmiHeader, iv50_data, &rgb_info.bmiHeader, rgb_data, NULL, &flags, 1, 0, 0, NULL, NULL); ok(!res, "got res %#Ix\n", res); ok(flags == 0x10, "got flags %#lx\n", flags); ok(iv50_info.bmiHeader.biSizeImage == 0x2e8, "got res %#Ix\n", res); res = ICCompressEnd(hic); ok(!res, "got res %#Ix\n", res); hr = MFCreateCollection(&collection); ok(hr == S_OK, "got hr %#lx\n", hr); sample = create_sample(iv50_data, iv50_info.bmiHeader.biSizeImage); ok(!!sample, "got sample %p\n", sample); hr = IMFSample_SetSampleTime(sample, 0); ok(hr == S_OK, "got hr %#lx\n", hr); hr = IMFSample_SetSampleDuration(sample, 0); ok(hr == S_OK, "got hr %#lx\n", hr); hr = IMFCollection_AddElement(collection, (IUnknown *)sample); ok(hr == S_OK, "got hr %#lx\n", hr); ret = check_mf_sample_collection(collection, &iv50_sample_desc, L"iv50frame.bin"); ok(ret == 0, "got %lu%% diff\n", ret); IMFCollection_Release(collection); done: res = ICClose(hic); ok(!res, "got res %#Ix\n", res); } static void test_iv50_decoder(void) { static const BITMAPINFOHEADER expect_iv50 = { .biSize = sizeof(BITMAPINFOHEADER), .biWidth = 0x60, .biHeight = 0x60, .biPlanes = 1, .biBitCount = 24, .biCompression = mmioFOURCC('I','V','5','0'), .biSizeImage = 0x2e8, }; static const BITMAPINFOHEADER expect_rgb = { .biSize = sizeof(BITMAPINFOHEADER), .biWidth = 0x60, .biHeight = 0x60, .biPlanes = 1, .biBitCount = 24, .biCompression = BI_RGB, .biSizeImage = 96 * 96 * 3, }; const struct buffer_desc rgb_buffer_desc = { .length = 96 * 96 * 3, .compare = compare_rgb24, .dump = dump_rgb24, .rect = {.right = 82, .bottom = 84}, }; const struct sample_desc rgb_sample_desc = { .buffer_count = 1, .buffers = &rgb_buffer_desc, }; const BYTE *res_data; BYTE rgb_data[0x6c00], iv50_data[0x5100]; BITMAPINFO rgb_info, iv50_info; IMFCollection *collection; DWORD res_len, ret; IMFSample *sample; LRESULT res; HRESULT hr; HIC hic; load_resource(L"iv50frame.bin", (const BYTE **)&res_data, &res_len); memcpy(iv50_data, res_data, res_len); iv50_info.bmiHeader = expect_iv50; hic = ICOpen(ICTYPE_VIDEO, expect_iv50.biCompression, ICMODE_DECOMPRESS); if (!hic) { todo_wine win_skip("ICOpen failed to created IV50 decompressor.\n"); return; } res = ICDecompressGetFormat(hic, &iv50_info, &rgb_info); ok(!res, "got res %#Ix\n", res); check_member(rgb_info.bmiHeader, expect_rgb, "%#lx", biSize); check_member(rgb_info.bmiHeader, expect_rgb, "%#lx", biWidth); check_member(rgb_info.bmiHeader, expect_rgb, "%#lx", biHeight); check_member(rgb_info.bmiHeader, expect_rgb, "%#x", biPlanes); todo_wine check_member(rgb_info.bmiHeader, expect_rgb, "%#x", biBitCount); check_member(rgb_info.bmiHeader, expect_rgb, "%#lx", biCompression); todo_wine check_member(rgb_info.bmiHeader, expect_rgb, "%#lx", biSizeImage); check_member(rgb_info.bmiHeader, expect_rgb, "%#lx", biXPelsPerMeter); check_member(rgb_info.bmiHeader, expect_rgb, "%#lx", biYPelsPerMeter); check_member(rgb_info.bmiHeader, expect_rgb, "%#lx", biClrUsed); check_member(rgb_info.bmiHeader, expect_rgb, "%#lx", biClrImportant); rgb_info.bmiHeader = expect_rgb; res = ICDecompressBegin(hic, &iv50_info, &rgb_info); ok(!res, "got res %#Ix\n", res); res = ICDecompress(hic, 0, &iv50_info.bmiHeader, iv50_data, &rgb_info.bmiHeader, rgb_data); ok(!res, "got res %#Ix\n", res); res = ICDecompressEnd(hic); todo_wine ok(!res, "got res %#Ix\n", res); res = ICClose(hic); ok(!res, "got res %#Ix\n", res); hr = MFCreateCollection(&collection); ok(hr == S_OK, "got hr %#lx\n", hr); sample = create_sample(rgb_data, rgb_info.bmiHeader.biSizeImage); ok(!!sample, "got sample %p\n", sample); hr = IMFSample_SetSampleTime(sample, 0); ok(hr == S_OK, "got hr %#lx\n", hr); hr = IMFSample_SetSampleDuration(sample, 0); ok(hr == S_OK, "got hr %#lx\n", hr); hr = IMFCollection_AddElement(collection, (IUnknown *)sample); ok(hr == S_OK, "got hr %#lx\n", hr); ret = check_mf_sample_collection(collection, &rgb_sample_desc, L"rgb24frame.bmp"); ok(ret <= 4, "got %lu%% diff\n", ret); IMFCollection_Release(collection); } START_TEST(transform) { init_functions(); test_sample_copier(); test_sample_copier_output_processing(); test_aac_encoder(); test_aac_decoder(); test_wma_encoder(); test_wma_decoder(); test_wma_decoder_dmo_input_type(); test_wma_decoder_dmo_output_type(); test_h264_encoder(); test_h264_decoder(); test_wmv_encoder(); test_wmv_decoder(); test_wmv_decoder_dmo_input_type(); test_wmv_decoder_dmo_output_type(); test_wmv_decoder_dmo_get_size_info(); test_wmv_decoder_media_object(); test_audio_convert(); test_color_convert(); test_video_processor(); test_mp3_decoder(); test_iv50_encoder(); test_iv50_decoder(); test_h264_with_dxgi_manager(); test_h264_decoder_concat_streams(); }