wine-wine/dlls/quartz/tests/filtergraph.c

4567 lines
175 KiB
C

/*
* Unit tests for Direct Show functions
*
* Copyright (C) 2004 Christian Costa
* Copyright (C) 2008 Alexander Dorofeyev
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define COBJMACROS
#define CONST_VTABLE
#include "dshow.h"
#include "wine/heap.h"
#include "wine/test.h"
static const GUID testguid = {0xabbccdde};
typedef struct TestFilterImpl
{
IBaseFilter IBaseFilter_iface;
LONG refCount;
CRITICAL_SECTION csFilter;
FILTER_STATE state;
FILTER_INFO filterInfo;
CLSID clsid;
IPin **ppPins;
UINT nPins;
} TestFilterImpl;
static BOOL compare_time(ULONGLONG x, ULONGLONG y, unsigned int max_diff)
{
ULONGLONG diff = x > y ? x - y : y - x;
return diff <= max_diff;
}
static WCHAR *create_file(const WCHAR *name, const char *data, DWORD size)
{
static WCHAR pathW[MAX_PATH];
DWORD written;
HANDLE file;
GetTempPathW(ARRAY_SIZE(pathW), pathW);
wcscat(pathW, name);
file = CreateFileW(pathW, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
ok(file != INVALID_HANDLE_VALUE, "Failed to create file %s, error %u.\n",
wine_dbgstr_w(pathW), GetLastError());
WriteFile(file, data, size, &written, NULL);
ok(written = size, "Failed to write file data, error %u.\n", GetLastError());
CloseHandle(file);
return pathW;
}
static WCHAR *load_resource(const WCHAR *name)
{
HRSRC res;
void *ptr;
res = FindResourceW(NULL, name, (const WCHAR *)RT_RCDATA);
ok(!!res, "Failed to find resource %s, error %u.\n", wine_dbgstr_w(name), GetLastError());
ptr = LockResource(LoadResource(GetModuleHandleA(NULL), res));
return create_file(name, ptr, SizeofResource(GetModuleHandleA(NULL), res));
}
static IFilterGraph2 *create_graph(void)
{
IFilterGraph2 *ret;
HRESULT hr;
hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterGraph2, (void **)&ret);
ok(hr == S_OK, "Failed to create FilterGraph: %#x\n", hr);
return ret;
}
static ULONG get_refcount(void *iface)
{
IUnknown *unknown = iface;
IUnknown_AddRef(unknown);
return IUnknown_Release(unknown);
}
#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 %#x, expected %#x.\n", hr, expected_hr);
if (SUCCEEDED(hr))
IUnknown_Release(unk);
}
static void test_interfaces(void)
{
IFilterGraph2 *graph = create_graph();
check_interface(graph, &IID_IBasicAudio, TRUE);
check_interface(graph, &IID_IBasicVideo2, TRUE);
check_interface(graph, &IID_IFilterGraph2, TRUE);
check_interface(graph, &IID_IFilterMapper, TRUE);
check_interface(graph, &IID_IFilterMapper3, TRUE);
check_interface(graph, &IID_IGraphConfig, TRUE);
check_interface(graph, &IID_IGraphVersion, TRUE);
check_interface(graph, &IID_IMediaControl, TRUE);
check_interface(graph, &IID_IMediaEvent, TRUE);
check_interface(graph, &IID_IMediaFilter, TRUE);
check_interface(graph, &IID_IMediaEventSink, TRUE);
check_interface(graph, &IID_IMediaPosition, TRUE);
check_interface(graph, &IID_IMediaSeeking, TRUE);
check_interface(graph, &IID_IObjectWithSite, TRUE);
check_interface(graph, &IID_IVideoFrameStep, TRUE);
check_interface(graph, &IID_IVideoWindow, TRUE);
check_interface(graph, &IID_IBaseFilter, FALSE);
IFilterGraph2_Release(graph);
}
static void test_basic_video(IFilterGraph2 *graph)
{
IBasicVideo* pbv;
LONG video_width, video_height, window_width;
LONG left, top, width, height;
HRESULT hr;
hr = IFilterGraph2_QueryInterface(graph, &IID_IBasicVideo, (void **)&pbv);
ok(hr==S_OK, "Cannot get IBasicVideo interface returned: %x\n", hr);
/* test get video size */
hr = IBasicVideo_GetVideoSize(pbv, NULL, NULL);
ok(hr==E_POINTER, "IBasicVideo_GetVideoSize returned: %x\n", hr);
hr = IBasicVideo_GetVideoSize(pbv, &video_width, NULL);
ok(hr==E_POINTER, "IBasicVideo_GetVideoSize returned: %x\n", hr);
hr = IBasicVideo_GetVideoSize(pbv, NULL, &video_height);
ok(hr==E_POINTER, "IBasicVideo_GetVideoSize returned: %x\n", hr);
hr = IBasicVideo_GetVideoSize(pbv, &video_width, &video_height);
ok(hr==S_OK, "Cannot get video size returned: %x\n", hr);
/* test source position */
hr = IBasicVideo_GetSourcePosition(pbv, NULL, NULL, NULL, NULL);
ok(hr == E_POINTER, "IBasicVideo_GetSourcePosition returned: %x\n", hr);
hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, NULL, NULL);
ok(hr == E_POINTER, "IBasicVideo_GetSourcePosition returned: %x\n", hr);
hr = IBasicVideo_GetSourcePosition(pbv, NULL, NULL, &width, &height);
ok(hr == E_POINTER, "IBasicVideo_GetSourcePosition returned: %x\n", hr);
hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, &width, &height);
ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
ok(left == 0, "expected 0, got %d\n", left);
ok(top == 0, "expected 0, got %d\n", top);
ok(width == video_width, "expected %d, got %d\n", video_width, width);
ok(height == video_height, "expected %d, got %d\n", video_height, height);
hr = IBasicVideo_SetSourcePosition(pbv, 0, 0, 0, 0);
ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
hr = IBasicVideo_SetSourcePosition(pbv, 0, 0, video_width*2, video_height*2);
ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
hr = IBasicVideo_put_SourceTop(pbv, -1);
ok(hr==E_INVALIDARG, "IBasicVideo_put_SourceTop returned: %x\n", hr);
hr = IBasicVideo_put_SourceTop(pbv, 0);
ok(hr==S_OK, "Cannot put source top returned: %x\n", hr);
hr = IBasicVideo_put_SourceTop(pbv, 1);
ok(hr==E_INVALIDARG, "IBasicVideo_put_SourceTop returned: %x\n", hr);
hr = IBasicVideo_SetSourcePosition(pbv, video_width, 0, video_width, video_height);
ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
hr = IBasicVideo_SetSourcePosition(pbv, 0, video_height, video_width, video_height);
ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
hr = IBasicVideo_SetSourcePosition(pbv, -1, 0, video_width, video_height);
ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
hr = IBasicVideo_SetSourcePosition(pbv, 0, -1, video_width, video_height);
ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
hr = IBasicVideo_SetSourcePosition(pbv, video_width/2, video_height/2, video_width, video_height);
ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
hr = IBasicVideo_SetSourcePosition(pbv, video_width/2, video_height/2, video_width, video_height);
ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
hr = IBasicVideo_SetSourcePosition(pbv, 0, 0, video_width, video_height+1);
ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
hr = IBasicVideo_SetSourcePosition(pbv, 0, 0, video_width+1, video_height);
ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
hr = IBasicVideo_SetSourcePosition(pbv, video_width/2, video_height/2, video_width/3+1, video_height/3+1);
ok(hr==S_OK, "Cannot set source position returned: %x\n", hr);
hr = IBasicVideo_get_SourceLeft(pbv, &left);
ok(hr==S_OK, "Cannot get source left returned: %x\n", hr);
ok(left==video_width/2, "expected %d, got %d\n", video_width/2, left);
hr = IBasicVideo_get_SourceTop(pbv, &top);
ok(hr==S_OK, "Cannot get source top returned: %x\n", hr);
ok(top==video_height/2, "expected %d, got %d\n", video_height/2, top);
hr = IBasicVideo_get_SourceWidth(pbv, &width);
ok(hr==S_OK, "Cannot get source width returned: %x\n", hr);
ok(width==video_width/3+1, "expected %d, got %d\n", video_width/3+1, width);
hr = IBasicVideo_get_SourceHeight(pbv, &height);
ok(hr==S_OK, "Cannot get source height returned: %x\n", hr);
ok(height==video_height/3+1, "expected %d, got %d\n", video_height/3+1, height);
hr = IBasicVideo_put_SourceLeft(pbv, video_width/3);
ok(hr==S_OK, "Cannot put source left returned: %x\n", hr);
hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, &width, &height);
ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
ok(left == video_width/3, "expected %d, got %d\n", video_width/3, left);
ok(width == video_width/3+1, "expected %d, got %d\n", video_width/3+1, width);
hr = IBasicVideo_put_SourceTop(pbv, video_height/3);
ok(hr==S_OK, "Cannot put source top returned: %x\n", hr);
hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, &width, &height);
ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
ok(top == video_height/3, "expected %d, got %d\n", video_height/3, top);
ok(height == video_height/3+1, "expected %d, got %d\n", video_height/3+1, height);
hr = IBasicVideo_put_SourceWidth(pbv, video_width/4+1);
ok(hr==S_OK, "Cannot put source width returned: %x\n", hr);
hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, &width, &height);
ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
ok(left == video_width/3, "expected %d, got %d\n", video_width/3, left);
ok(width == video_width/4+1, "expected %d, got %d\n", video_width/4+1, width);
hr = IBasicVideo_put_SourceHeight(pbv, video_height/4+1);
ok(hr==S_OK, "Cannot put source height returned: %x\n", hr);
hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, &width, &height);
ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
ok(top == video_height/3, "expected %d, got %d\n", video_height/3, top);
ok(height == video_height/4+1, "expected %d, got %d\n", video_height/4+1, height);
/* test destination rectangle */
window_width = max(video_width, GetSystemMetrics(SM_CXMIN) - 2 * GetSystemMetrics(SM_CXFRAME));
hr = IBasicVideo_GetDestinationPosition(pbv, NULL, NULL, NULL, NULL);
ok(hr == E_POINTER, "IBasicVideo_GetDestinationPosition returned: %x\n", hr);
hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, NULL, NULL);
ok(hr == E_POINTER, "IBasicVideo_GetDestinationPosition returned: %x\n", hr);
hr = IBasicVideo_GetDestinationPosition(pbv, NULL, NULL, &width, &height);
ok(hr == E_POINTER, "IBasicVideo_GetDestinationPosition returned: %x\n", hr);
hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, &width, &height);
ok(hr == S_OK, "Cannot get destination position returned: %x\n", hr);
ok(left == 0, "expected 0, got %d\n", left);
ok(top == 0, "expected 0, got %d\n", top);
ok(width == window_width, "expected %d, got %d\n", window_width, width);
ok(height == video_height, "expected %d, got %d\n", video_height, height);
hr = IBasicVideo_SetDestinationPosition(pbv, 0, 0, 0, 0);
ok(hr==E_INVALIDARG, "IBasicVideo_SetDestinationPosition returned: %x\n", hr);
hr = IBasicVideo_SetDestinationPosition(pbv, 0, 0, video_width*2, video_height*2);
ok(hr==S_OK, "Cannot put destination position returned: %x\n", hr);
hr = IBasicVideo_put_DestinationLeft(pbv, -1);
ok(hr==S_OK, "Cannot put destination left returned: %x\n", hr);
hr = IBasicVideo_put_DestinationLeft(pbv, 0);
ok(hr==S_OK, "Cannot put destination left returned: %x\n", hr);
hr = IBasicVideo_put_DestinationLeft(pbv, 1);
ok(hr==S_OK, "Cannot put destination left returned: %x\n", hr);
hr = IBasicVideo_SetDestinationPosition(pbv, video_width, 0, video_width, video_height);
ok(hr==S_OK, "Cannot set destinaiton position returned: %x\n", hr);
hr = IBasicVideo_SetDestinationPosition(pbv, 0, video_height, video_width, video_height);
ok(hr==S_OK, "Cannot set destinaiton position returned: %x\n", hr);
hr = IBasicVideo_SetDestinationPosition(pbv, -1, 0, video_width, video_height);
ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
hr = IBasicVideo_SetDestinationPosition(pbv, 0, -1, video_width, video_height);
ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
hr = IBasicVideo_SetDestinationPosition(pbv, video_width/2, video_height/2, video_width, video_height);
ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
hr = IBasicVideo_SetDestinationPosition(pbv, video_width/2, video_height/2, video_width, video_height);
ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
hr = IBasicVideo_SetDestinationPosition(pbv, 0, 0, video_width, video_height+1);
ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
hr = IBasicVideo_SetDestinationPosition(pbv, 0, 0, video_width+1, video_height);
ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
hr = IBasicVideo_SetDestinationPosition(pbv, video_width/2, video_height/2, video_width/3+1, video_height/3+1);
ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
hr = IBasicVideo_get_DestinationLeft(pbv, &left);
ok(hr==S_OK, "Cannot get destination left returned: %x\n", hr);
ok(left==video_width/2, "expected %d, got %d\n", video_width/2, left);
hr = IBasicVideo_get_DestinationTop(pbv, &top);
ok(hr==S_OK, "Cannot get destination top returned: %x\n", hr);
ok(top==video_height/2, "expected %d, got %d\n", video_height/2, top);
hr = IBasicVideo_get_DestinationWidth(pbv, &width);
ok(hr==S_OK, "Cannot get destination width returned: %x\n", hr);
ok(width==video_width/3+1, "expected %d, got %d\n", video_width/3+1, width);
hr = IBasicVideo_get_DestinationHeight(pbv, &height);
ok(hr==S_OK, "Cannot get destination height returned: %x\n", hr);
ok(height==video_height/3+1, "expected %d, got %d\n", video_height/3+1, height);
hr = IBasicVideo_put_DestinationLeft(pbv, video_width/3);
ok(hr==S_OK, "Cannot put destination left returned: %x\n", hr);
hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, &width, &height);
ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
ok(left == video_width/3, "expected %d, got %d\n", video_width/3, left);
ok(width == video_width/3+1, "expected %d, got %d\n", video_width/3+1, width);
hr = IBasicVideo_put_DestinationTop(pbv, video_height/3);
ok(hr==S_OK, "Cannot put destination top returned: %x\n", hr);
hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, &width, &height);
ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
ok(top == video_height/3, "expected %d, got %d\n", video_height/3, top);
ok(height == video_height/3+1, "expected %d, got %d\n", video_height/3+1, height);
hr = IBasicVideo_put_DestinationWidth(pbv, video_width/4+1);
ok(hr==S_OK, "Cannot put destination width returned: %x\n", hr);
hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, &width, &height);
ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
ok(left == video_width/3, "expected %d, got %d\n", video_width/3, left);
ok(width == video_width/4+1, "expected %d, got %d\n", video_width/4+1, width);
hr = IBasicVideo_put_DestinationHeight(pbv, video_height/4+1);
ok(hr==S_OK, "Cannot put destination height returned: %x\n", hr);
hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, &width, &height);
ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
ok(top == video_height/3, "expected %d, got %d\n", video_height/3, top);
ok(height == video_height/4+1, "expected %d, got %d\n", video_height/4+1, height);
/* reset source rectangle */
hr = IBasicVideo_SetDefaultSourcePosition(pbv);
ok(hr==S_OK, "IBasicVideo_SetDefaultSourcePosition returned: %x\n", hr);
/* reset destination position */
hr = IBasicVideo_SetDestinationPosition(pbv, 0, 0, video_width, video_height);
ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
IBasicVideo_Release(pbv);
}
static void test_media_seeking(IFilterGraph2 *graph)
{
IMediaSeeking *seeking;
IMediaFilter *filter;
LONGLONG pos, stop, duration;
GUID format;
HRESULT hr;
IFilterGraph2_SetDefaultSyncSource(graph);
hr = IFilterGraph2_QueryInterface(graph, &IID_IMediaSeeking, (void **)&seeking);
ok(hr == S_OK, "QueryInterface(IMediaControl) failed: %08x\n", hr);
hr = IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
ok(hr == S_OK, "QueryInterface(IMediaFilter) failed: %08x\n", hr);
format = GUID_NULL;
hr = IMediaSeeking_GetTimeFormat(seeking, &format);
ok(hr == S_OK, "GetTimeFormat failed: %#x\n", hr);
ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "got %s\n", wine_dbgstr_guid(&format));
pos = 0xdeadbeef;
hr = IMediaSeeking_ConvertTimeFormat(seeking, &pos, NULL, 0x123456789a, NULL);
ok(hr == S_OK, "ConvertTimeFormat failed: %#x\n", hr);
ok(pos == 0x123456789a, "got %s\n", wine_dbgstr_longlong(pos));
pos = 0xdeadbeef;
hr = IMediaSeeking_ConvertTimeFormat(seeking, &pos, &TIME_FORMAT_MEDIA_TIME, 0x123456789a, NULL);
ok(hr == S_OK, "ConvertTimeFormat failed: %#x\n", hr);
ok(pos == 0x123456789a, "got %s\n", wine_dbgstr_longlong(pos));
pos = 0xdeadbeef;
hr = IMediaSeeking_ConvertTimeFormat(seeking, &pos, NULL, 0x123456789a, &TIME_FORMAT_MEDIA_TIME);
ok(hr == S_OK, "ConvertTimeFormat failed: %#x\n", hr);
ok(pos == 0x123456789a, "got %s\n", wine_dbgstr_longlong(pos));
hr = IMediaSeeking_GetCurrentPosition(seeking, &pos);
ok(hr == S_OK, "GetCurrentPosition failed: %#x\n", hr);
ok(pos == 0, "got %s\n", wine_dbgstr_longlong(pos));
hr = IMediaSeeking_GetDuration(seeking, &duration);
ok(hr == S_OK, "GetDuration failed: %#x\n", hr);
ok(duration > 0, "got %s\n", wine_dbgstr_longlong(duration));
hr = IMediaSeeking_GetStopPosition(seeking, &stop);
ok(hr == S_OK, "GetCurrentPosition failed: %08x\n", hr);
ok(stop == duration || stop == duration + 1, "expected %s, got %s\n",
wine_dbgstr_longlong(duration), wine_dbgstr_longlong(stop));
hr = IMediaSeeking_SetPositions(seeking, NULL, AM_SEEKING_ReturnTime, NULL, AM_SEEKING_NoPositioning);
ok(hr == S_OK, "SetPositions failed: %#x\n", hr);
hr = IMediaSeeking_SetPositions(seeking, NULL, AM_SEEKING_NoPositioning, NULL, AM_SEEKING_ReturnTime);
ok(hr == S_OK, "SetPositions failed: %#x\n", hr);
pos = 0;
hr = IMediaSeeking_SetPositions(seeking, &pos, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning);
ok(hr == S_OK, "SetPositions failed: %08x\n", hr);
IMediaFilter_SetSyncSource(filter, NULL);
pos = 0xdeadbeef;
hr = IMediaSeeking_GetCurrentPosition(seeking, &pos);
ok(hr == S_OK, "GetCurrentPosition failed: %08x\n", hr);
ok(pos == 0, "Position != 0 (%s)\n", wine_dbgstr_longlong(pos));
IFilterGraph2_SetDefaultSyncSource(graph);
IMediaSeeking_Release(seeking);
IMediaFilter_Release(filter);
}
static void test_state_change(IFilterGraph2 *graph)
{
IMediaControl *control;
OAFilterState state;
HRESULT hr;
hr = IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
ok(hr == S_OK, "QueryInterface(IMediaControl) failed: %x\n", hr);
hr = IMediaControl_GetState(control, 1000, &state);
ok(hr == S_OK, "GetState() failed: %x\n", hr);
ok(state == State_Stopped, "wrong state %d\n", state);
hr = IMediaControl_Run(control);
ok(SUCCEEDED(hr), "Run() failed: %x\n", hr);
hr = IMediaControl_GetState(control, INFINITE, &state);
ok(SUCCEEDED(hr), "GetState() failed: %x\n", hr);
ok(state == State_Running, "wrong state %d\n", state);
hr = IMediaControl_Stop(control);
ok(SUCCEEDED(hr), "Stop() failed: %x\n", hr);
hr = IMediaControl_GetState(control, 1000, &state);
ok(hr == S_OK, "GetState() failed: %x\n", hr);
ok(state == State_Stopped, "wrong state %d\n", state);
hr = IMediaControl_Pause(control);
ok(SUCCEEDED(hr), "Pause() failed: %x\n", hr);
hr = IMediaControl_GetState(control, 1000, &state);
ok(hr == S_OK, "GetState() failed: %x\n", hr);
ok(state == State_Paused, "wrong state %d\n", state);
hr = IMediaControl_Run(control);
ok(SUCCEEDED(hr), "Run() failed: %x\n", hr);
hr = IMediaControl_GetState(control, 1000, &state);
ok(hr == S_OK, "GetState() failed: %x\n", hr);
ok(state == State_Running, "wrong state %d\n", state);
hr = IMediaControl_Pause(control);
ok(SUCCEEDED(hr), "Pause() failed: %x\n", hr);
hr = IMediaControl_GetState(control, 1000, &state);
ok(hr == S_OK, "GetState() failed: %x\n", hr);
ok(state == State_Paused, "wrong state %d\n", state);
hr = IMediaControl_Stop(control);
ok(SUCCEEDED(hr), "Stop() failed: %x\n", hr);
hr = IMediaControl_GetState(control, 1000, &state);
ok(hr == S_OK, "GetState() failed: %x\n", hr);
ok(state == State_Stopped, "wrong state %d\n", state);
IMediaControl_Release(control);
}
static void test_media_event(IFilterGraph2 *graph)
{
IMediaEvent *media_event;
IMediaSeeking *seeking;
IMediaControl *control;
IMediaFilter *filter;
LONG_PTR lparam1, lparam2;
LONGLONG current, stop;
OAFilterState state;
int got_eos = 0;
HANDLE event;
HRESULT hr;
LONG code;
hr = IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
ok(hr == S_OK, "QueryInterface(IMediaFilter) failed: %#x\n", hr);
hr = IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
ok(hr == S_OK, "QueryInterface(IMediaControl) failed: %#x\n", hr);
hr = IFilterGraph2_QueryInterface(graph, &IID_IMediaEvent, (void **)&media_event);
ok(hr == S_OK, "QueryInterface(IMediaEvent) failed: %#x\n", hr);
hr = IFilterGraph2_QueryInterface(graph, &IID_IMediaSeeking, (void **)&seeking);
ok(hr == S_OK, "QueryInterface(IMediaEvent) failed: %#x\n", hr);
hr = IMediaControl_Stop(control);
ok(SUCCEEDED(hr), "Stop() failed: %#x\n", hr);
hr = IMediaControl_GetState(control, 1000, &state);
ok(hr == S_OK, "GetState() timed out\n");
hr = IMediaSeeking_GetDuration(seeking, &stop);
ok(hr == S_OK, "GetDuration() failed: %#x\n", hr);
current = 0;
hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning, &stop, AM_SEEKING_AbsolutePositioning);
ok(hr == S_OK, "SetPositions() failed: %#x\n", hr);
hr = IMediaFilter_SetSyncSource(filter, NULL);
ok(hr == S_OK, "SetSyncSource() failed: %#x\n", hr);
hr = IMediaEvent_GetEventHandle(media_event, (OAEVENT *)&event);
ok(hr == S_OK, "GetEventHandle() failed: %#x\n", hr);
/* flush existing events */
while ((hr = IMediaEvent_GetEvent(media_event, &code, &lparam1, &lparam2, 0)) == S_OK);
ok(WaitForSingleObject(event, 0) == WAIT_TIMEOUT, "event should not be signaled\n");
hr = IMediaControl_Run(control);
ok(SUCCEEDED(hr), "Run() failed: %#x\n", hr);
while (!got_eos)
{
if (WaitForSingleObject(event, 5000) == WAIT_TIMEOUT)
break;
while ((hr = IMediaEvent_GetEvent(media_event, &code, &lparam1, &lparam2, 0)) == S_OK)
{
if (code == EC_COMPLETE)
{
got_eos = 1;
break;
}
}
}
ok(got_eos, "didn't get EOS\n");
hr = IMediaSeeking_GetCurrentPosition(seeking, &current);
ok(hr == S_OK, "GetCurrentPosition() failed: %#x\n", hr);
todo_wine
ok(current == stop, "expected %s, got %s\n", wine_dbgstr_longlong(stop), wine_dbgstr_longlong(current));
hr = IMediaControl_Stop(control);
ok(SUCCEEDED(hr), "Run() failed: %#x\n", hr);
hr = IMediaControl_GetState(control, 1000, &state);
ok(hr == S_OK, "GetState() timed out\n");
hr = IFilterGraph2_SetDefaultSyncSource(graph);
ok(hr == S_OK, "SetDefaultSinkSource() failed: %#x\n", hr);
IMediaSeeking_Release(seeking);
IMediaEvent_Release(media_event);
IMediaControl_Release(control);
IMediaFilter_Release(filter);
}
static void rungraph(IFilterGraph2 *graph, BOOL video)
{
if (video)
test_basic_video(graph);
test_media_seeking(graph);
test_state_change(graph);
test_media_event(graph);
}
static HRESULT test_graph_builder_connect_file(WCHAR *filename, BOOL audio, BOOL video)
{
IBaseFilter *source_filter, *renderer;
IPin *pin_in, *pin_out;
IFilterGraph2 *graph;
IEnumPins *enumpins;
HRESULT hr;
if (video)
hr = CoCreateInstance(&CLSID_VideoRenderer, NULL, CLSCTX_INPROC_SERVER,
&IID_IBaseFilter, (void **)&renderer);
else
hr = CoCreateInstance(&CLSID_AudioRender, NULL, CLSCTX_INPROC_SERVER,
&IID_IBaseFilter, (void **)&renderer);
if (hr == VFW_E_NO_AUDIO_HARDWARE)
return VFW_E_CANNOT_CONNECT;
ok(hr == S_OK, "Got hr %#x.\n", hr);
graph = create_graph();
IBaseFilter_EnumPins(renderer, &enumpins);
IEnumPins_Next(enumpins, 1, &pin_in, NULL);
IEnumPins_Release(enumpins);
hr = IFilterGraph2_AddSourceFilter(graph, filename, NULL, &source_filter);
ok(hr == S_OK, "AddSourceFilter failed: %#x\n", hr);
hr = IFilterGraph2_AddFilter(graph, renderer, NULL);
ok(hr == S_OK, "AddFilter failed: %#x\n", hr);
hr = IBaseFilter_FindPin(source_filter, L"Output", &pin_out);
ok(hr == S_OK, "FindPin failed: %#x\n", hr);
hr = IFilterGraph2_Connect(graph, pin_out, pin_in);
if (SUCCEEDED(hr))
rungraph(graph, video);
IPin_Release(pin_in);
IPin_Release(pin_out);
IBaseFilter_Release(source_filter);
IBaseFilter_Release(renderer);
IFilterGraph2_Release(graph);
return hr;
}
static void test_render_run(const WCHAR *file, BOOL audio, BOOL video)
{
IFilterGraph2 *graph;
HANDLE h;
HRESULT hr;
LONG refs;
WCHAR *filename = load_resource(file);
h = CreateFileW(filename, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
if (h == INVALID_HANDLE_VALUE) {
skip("Could not read test file %s, skipping test\n", wine_dbgstr_w(file));
DeleteFileW(filename);
return;
}
CloseHandle(h);
trace("running %s\n", wine_dbgstr_w(file));
graph = create_graph();
hr = IFilterGraph2_RenderFile(graph, filename, NULL);
if (FAILED(hr))
{
skip("%s: codec not supported; skipping test\n", wine_dbgstr_w(file));
refs = IFilterGraph2_Release(graph);
ok(!refs, "Graph has %u references\n", refs);
hr = test_graph_builder_connect_file(filename, audio, video);
ok(hr == VFW_E_CANNOT_CONNECT, "got %#x\n", hr);
}
else
{
if (audio)
ok(hr == S_OK || hr == VFW_S_AUDIO_NOT_RENDERED, "Got hr %#x.\n", hr);
else
ok(hr == S_OK, "Got hr %#x.\n", hr);
rungraph(graph, video);
refs = IFilterGraph2_Release(graph);
ok(!refs, "Graph has %u references\n", refs);
hr = test_graph_builder_connect_file(filename, audio, video);
if (audio && video)
todo_wine ok(hr == VFW_S_PARTIAL_RENDER, "Got hr %#x.\n", hr);
else
ok(hr == S_OK, "Got hr %#x.\n", hr);
}
/* check reference leaks */
h = CreateFileW(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
ok(h != INVALID_HANDLE_VALUE, "CreateFile failed: err=%d\n", GetLastError());
CloseHandle(h);
DeleteFileW(filename);
}
static void test_enum_filters(void)
{
IBaseFilter *filter1, *filter2, *filters[2];
IFilterGraph2 *graph = create_graph();
IEnumFilters *enum1, *enum2;
ULONG count, ref;
HRESULT hr;
CoCreateInstance(&CLSID_AsyncReader, NULL, CLSCTX_INPROC_SERVER,
&IID_IBaseFilter, (void **)&filter1);
CoCreateInstance(&CLSID_AsyncReader, NULL, CLSCTX_INPROC_SERVER,
&IID_IBaseFilter, (void **)&filter2);
hr = IFilterGraph2_EnumFilters(graph, &enum1);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IEnumFilters_Next(enum1, 1, filters, NULL);
ok(hr == S_FALSE, "Got hr %#x.\n", hr);
IFilterGraph2_AddFilter(graph, filter1, NULL);
IFilterGraph2_AddFilter(graph, filter2, NULL);
hr = IEnumFilters_Next(enum1, 1, filters, NULL);
ok(hr == VFW_E_ENUM_OUT_OF_SYNC, "Got hr %#x.\n", hr);
hr = IEnumFilters_Reset(enum1);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IEnumFilters_Next(enum1, 1, filters, NULL);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(filters[0] == filter2, "Got filter %p.\n", filters[0]);
IBaseFilter_Release(filters[0]);
hr = IEnumFilters_Next(enum1, 1, filters, &count);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(count == 1, "Got count %u.\n", count);
ok(filters[0] == filter1, "Got filter %p.\n", filters[0]);
IBaseFilter_Release(filters[0]);
hr = IEnumFilters_Next(enum1, 1, filters, &count);
ok(hr == S_FALSE, "Got hr %#x.\n", hr);
ok(count == 0, "Got count %u.\n", count);
hr = IEnumFilters_Reset(enum1);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IEnumFilters_Next(enum1, 2, filters, &count);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(count == 2, "Got count %u.\n", count);
ok(filters[0] == filter2, "Got filter %p.\n", filters[0]);
ok(filters[1] == filter1, "Got filter %p.\n", filters[1]);
IBaseFilter_Release(filters[0]);
IBaseFilter_Release(filters[1]);
IFilterGraph2_RemoveFilter(graph, filter1);
IFilterGraph2_AddFilter(graph, filter1, NULL);
hr = IEnumFilters_Next(enum1, 2, filters, &count);
ok(hr == VFW_E_ENUM_OUT_OF_SYNC, "Got hr %#x.\n", hr);
hr = IEnumFilters_Reset(enum1);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IEnumFilters_Next(enum1, 2, filters, &count);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(count == 2, "Got count %u.\n", count);
ok(filters[0] == filter1, "Got filter %p.\n", filters[0]);
ok(filters[1] == filter2, "Got filter %p.\n", filters[1]);
IBaseFilter_Release(filters[0]);
IBaseFilter_Release(filters[1]);
hr = IEnumFilters_Reset(enum1);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IEnumFilters_Clone(enum1, &enum2);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IEnumFilters_Skip(enum2, 1);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IEnumFilters_Next(enum2, 2, filters, &count);
ok(hr == S_FALSE, "Got hr %#x.\n", hr);
ok(count == 1, "Got count %u.\n", count);
ok(filters[0] == filter2, "Got filter %p.\n", filters[0]);
IBaseFilter_Release(filters[0]);
hr = IEnumFilters_Skip(enum1, 3);
ok(hr == S_FALSE, "Got hr %#x.\n", hr);
IEnumFilters_Release(enum2);
IEnumFilters_Release(enum1);
ref = IFilterGraph2_Release(graph);
ok(!ref, "Got outstanding refcount %d.\n", ref);
}
static DWORD WINAPI call_RenderFile_multithread(LPVOID lParam)
{
WCHAR *filename = load_resource(L"test.avi");
IFilterGraph2 *graph = lParam;
HRESULT hr;
hr = IFilterGraph2_RenderFile(graph, filename, NULL);
ok(hr == S_OK, "Got hr %#x.\n", hr);
rungraph(graph, TRUE);
return 0;
}
static void test_render_with_multithread(void)
{
IFilterGraph2 *graph;
HANDLE thread;
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
graph = create_graph();
thread = CreateThread(NULL, 0, call_RenderFile_multithread, graph, 0, NULL);
ok(!WaitForSingleObject(thread, 10000), "Wait timed out.\n");
IFilterGraph2_Release(graph);
CloseHandle(thread);
CoUninitialize();
}
struct testpin
{
IPin IPin_iface;
LONG ref;
PIN_DIRECTION dir;
IBaseFilter *filter;
IPin *peer;
AM_MEDIA_TYPE *mt;
WCHAR name[10];
WCHAR id[10];
IEnumMediaTypes IEnumMediaTypes_iface;
const AM_MEDIA_TYPE *types;
unsigned int type_count, enum_idx;
AM_MEDIA_TYPE *request_mt, *accept_mt;
HRESULT Connect_hr;
HRESULT EnumMediaTypes_hr;
HRESULT QueryInternalConnections_hr;
};
static inline struct testpin *impl_from_IEnumMediaTypes(IEnumMediaTypes *iface)
{
return CONTAINING_RECORD(iface, struct testpin, IEnumMediaTypes_iface);
}
static HRESULT WINAPI testenummt_QueryInterface(IEnumMediaTypes *iface, REFIID iid, void **out)
{
struct testpin *pin = impl_from_IEnumMediaTypes(iface);
if (winetest_debug > 1) trace("%p->QueryInterface(%s)\n", pin, wine_dbgstr_guid(iid));
*out = NULL;
return E_NOINTERFACE;
}
static ULONG WINAPI testenummt_AddRef(IEnumMediaTypes *iface)
{
struct testpin *pin = impl_from_IEnumMediaTypes(iface);
return InterlockedIncrement(&pin->ref);
}
static ULONG WINAPI testenummt_Release(IEnumMediaTypes *iface)
{
struct testpin *pin = impl_from_IEnumMediaTypes(iface);
return InterlockedDecrement(&pin->ref);
}
static HRESULT WINAPI testenummt_Next(IEnumMediaTypes *iface, ULONG count, AM_MEDIA_TYPE **out, ULONG *fetched)
{
struct testpin *pin = impl_from_IEnumMediaTypes(iface);
unsigned int i;
for (i = 0; i < count; ++i)
{
if (pin->enum_idx + i >= pin->type_count)
break;
out[i] = CoTaskMemAlloc(sizeof(*out[i]));
*out[i] = pin->types[pin->enum_idx + i];
}
if (fetched)
*fetched = i;
pin->enum_idx += i;
return (i == count) ? S_OK : S_FALSE;
}
static HRESULT WINAPI testenummt_Skip(IEnumMediaTypes *iface, ULONG count)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static HRESULT WINAPI testenummt_Reset(IEnumMediaTypes *iface)
{
struct testpin *pin = impl_from_IEnumMediaTypes(iface);
pin->enum_idx = 0;
return S_OK;
}
static HRESULT WINAPI testenummt_Clone(IEnumMediaTypes *iface, IEnumMediaTypes **out)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static const IEnumMediaTypesVtbl testenummt_vtbl =
{
testenummt_QueryInterface,
testenummt_AddRef,
testenummt_Release,
testenummt_Next,
testenummt_Skip,
testenummt_Reset,
testenummt_Clone,
};
static inline struct testpin *impl_from_IPin(IPin *iface)
{
return CONTAINING_RECORD(iface, struct testpin, IPin_iface);
}
static HRESULT WINAPI testpin_QueryInterface(IPin *iface, REFIID iid, void **out)
{
struct testpin *pin = impl_from_IPin(iface);
if (winetest_debug > 1) trace("%p->QueryInterface(%s)\n", pin, wine_dbgstr_guid(iid));
if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IPin))
{
*out = &pin->IPin_iface;
IPin_AddRef(*out);
return S_OK;
}
*out = NULL;
return E_NOINTERFACE;
}
static ULONG WINAPI testpin_AddRef(IPin *iface)
{
struct testpin *pin = impl_from_IPin(iface);
return InterlockedIncrement(&pin->ref);
}
static ULONG WINAPI testpin_Release(IPin *iface)
{
struct testpin *pin = impl_from_IPin(iface);
return InterlockedDecrement(&pin->ref);
}
static HRESULT WINAPI testpin_Disconnect(IPin *iface)
{
struct testpin *pin = impl_from_IPin(iface);
if (winetest_debug > 1) trace("%p->Disconnect()\n", pin);
if (!pin->peer)
return S_FALSE;
IPin_Release(pin->peer);
pin->peer = NULL;
return S_OK;
}
static HRESULT WINAPI testpin_ConnectedTo(IPin *iface, IPin **peer)
{
struct testpin *pin = impl_from_IPin(iface);
if (winetest_debug > 1) trace("%p->ConnectedTo()\n", pin);
*peer = pin->peer;
if (*peer)
{
IPin_AddRef(*peer);
return S_OK;
}
return VFW_E_NOT_CONNECTED;
}
static HRESULT WINAPI testpin_ConnectionMediaType(IPin *iface, AM_MEDIA_TYPE *mt)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static HRESULT WINAPI testpin_QueryPinInfo(IPin *iface, PIN_INFO *info)
{
struct testpin *pin = impl_from_IPin(iface);
if (winetest_debug > 1) trace("%p->QueryPinInfo()\n", pin);
info->pFilter = pin->filter;
IBaseFilter_AddRef(pin->filter);
info->dir = pin->dir;
wcscpy(info->achName, pin->name);
return S_OK;
}
static HRESULT WINAPI testpin_QueryDirection(IPin *iface, PIN_DIRECTION *dir)
{
struct testpin *pin = impl_from_IPin(iface);
if (winetest_debug > 1) trace("%p->QueryDirection()\n", pin);
*dir = pin->dir;
return S_OK;
}
static HRESULT WINAPI testpin_QueryId(IPin *iface, WCHAR **id)
{
struct testpin *pin = impl_from_IPin(iface);
if (winetest_debug > 1) trace("%p->QueryId()\n", iface);
*id = CoTaskMemAlloc(11);
wcscpy(*id, pin->id);
return S_OK;
}
static HRESULT WINAPI testpin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *mt)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static HRESULT WINAPI testpin_EnumMediaTypes(IPin *iface, IEnumMediaTypes **out)
{
struct testpin *pin = impl_from_IPin(iface);
if (winetest_debug > 1) trace("%p->EnumMediaTypes()\n", pin);
if (FAILED(pin->EnumMediaTypes_hr))
return pin->EnumMediaTypes_hr;
*out = &pin->IEnumMediaTypes_iface;
IEnumMediaTypes_AddRef(*out);
pin->enum_idx = 0;
return S_OK;
}
static HRESULT WINAPI testpin_QueryInternalConnections(IPin *iface, IPin **out, ULONG *count)
{
struct testpin *pin = impl_from_IPin(iface);
if (winetest_debug > 1) trace("%p->QueryInternalConnections()\n", pin);
*count = 0;
return pin->QueryInternalConnections_hr;
}
static HRESULT WINAPI testpin_BeginFlush(IPin *iface)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static HRESULT WINAPI testpin_EndFlush(IPin * iface)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static HRESULT WINAPI testpin_NewSegment(IPin *iface, REFERENCE_TIME start, REFERENCE_TIME stop, double rate)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static HRESULT WINAPI testpin_EndOfStream(IPin *iface)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static HRESULT WINAPI no_Connect(IPin *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static HRESULT WINAPI no_ReceiveConnection(IPin *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static HRESULT WINAPI no_EnumMediaTypes(IPin *iface, IEnumMediaTypes **out)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static HRESULT WINAPI testsink_ReceiveConnection(IPin *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
{
struct testpin *pin = impl_from_IPin(iface);
if (winetest_debug > 1) trace("%p->ReceiveConnection(%p)\n", pin, peer);
if (pin->accept_mt && memcmp(pin->accept_mt, mt, sizeof(*mt)))
return VFW_E_TYPE_NOT_ACCEPTED;
pin->peer = peer;
IPin_AddRef(peer);
return S_OK;
}
static const IPinVtbl testsink_vtbl =
{
testpin_QueryInterface,
testpin_AddRef,
testpin_Release,
no_Connect,
testsink_ReceiveConnection,
testpin_Disconnect,
testpin_ConnectedTo,
testpin_ConnectionMediaType,
testpin_QueryPinInfo,
testpin_QueryDirection,
testpin_QueryId,
testpin_QueryAccept,
no_EnumMediaTypes,
testpin_QueryInternalConnections,
testpin_EndOfStream,
testpin_BeginFlush,
testpin_EndFlush,
testpin_NewSegment
};
static void testpin_init(struct testpin *pin, const IPinVtbl *vtbl, PIN_DIRECTION dir)
{
memset(pin, 0, sizeof(*pin));
pin->IPin_iface.lpVtbl = vtbl;
pin->IEnumMediaTypes_iface.lpVtbl = &testenummt_vtbl;
pin->ref = 1;
pin->dir = dir;
pin->Connect_hr = S_OK;
pin->EnumMediaTypes_hr = S_OK;
pin->QueryInternalConnections_hr = E_NOTIMPL;
}
static void testsink_init(struct testpin *pin)
{
testpin_init(pin, &testsink_vtbl, PINDIR_INPUT);
}
static HRESULT WINAPI testsource_Connect(IPin *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
{
struct testpin *pin = impl_from_IPin(iface);
HRESULT hr;
if (winetest_debug > 1) trace("%p->Connect(%p)\n", pin, peer);
if (FAILED(pin->Connect_hr))
return pin->Connect_hr;
ok(!mt, "Got media type %p.\n", mt);
if (SUCCEEDED(hr = IPin_ReceiveConnection(peer, &pin->IPin_iface, pin->request_mt)))
{
pin->peer = peer;
IPin_AddRef(peer);
return pin->Connect_hr;
}
return hr;
}
static const IPinVtbl testsource_vtbl =
{
testpin_QueryInterface,
testpin_AddRef,
testpin_Release,
testsource_Connect,
no_ReceiveConnection,
testpin_Disconnect,
testpin_ConnectedTo,
testpin_ConnectionMediaType,
testpin_QueryPinInfo,
testpin_QueryDirection,
testpin_QueryId,
testpin_QueryAccept,
testpin_EnumMediaTypes,
testpin_QueryInternalConnections,
testpin_EndOfStream,
testpin_BeginFlush,
testpin_EndFlush,
testpin_NewSegment
};
static void testsource_init(struct testpin *pin, const AM_MEDIA_TYPE *types, int type_count)
{
testpin_init(pin, &testsource_vtbl, PINDIR_OUTPUT);
pin->types = types;
pin->type_count = type_count;
}
struct testfilter
{
IBaseFilter IBaseFilter_iface;
LONG ref;
IFilterGraph *graph;
WCHAR *name;
IReferenceClock *clock;
FILTER_STATE state;
REFERENCE_TIME start_time;
IEnumPins IEnumPins_iface;
struct testpin *pins;
unsigned int pin_count, enum_idx;
HRESULT state_hr, seek_hr;
IAMFilterMiscFlags IAMFilterMiscFlags_iface;
ULONG misc_flags;
IMediaSeeking IMediaSeeking_iface;
LONG seeking_ref;
DWORD seek_caps;
BOOL support_testguid;
LONGLONG seek_duration, seek_current, seek_stop;
double seek_rate;
IReferenceClock IReferenceClock_iface;
IFileSourceFilter IFileSourceFilter_iface;
WCHAR filename[MAX_PATH];
};
static inline struct testfilter *impl_from_IEnumPins(IEnumPins *iface)
{
return CONTAINING_RECORD(iface, struct testfilter, IEnumPins_iface);
}
static HRESULT WINAPI testenumpins_QueryInterface(IEnumPins *iface, REFIID iid, void **out)
{
ok(0, "Unexpected iid %s.\n", wine_dbgstr_guid(iid));
return E_NOINTERFACE;
}
static ULONG WINAPI testenumpins_AddRef(IEnumPins * iface)
{
struct testfilter *filter = impl_from_IEnumPins(iface);
return InterlockedIncrement(&filter->ref);
}
static ULONG WINAPI testenumpins_Release(IEnumPins * iface)
{
struct testfilter *filter = impl_from_IEnumPins(iface);
return InterlockedDecrement(&filter->ref);
}
static HRESULT WINAPI testenumpins_Next(IEnumPins *iface, ULONG count, IPin **out, ULONG *fetched)
{
struct testfilter *filter = impl_from_IEnumPins(iface);
unsigned int i;
for (i = 0; i < count; ++i)
{
if (filter->enum_idx + i >= filter->pin_count)
break;
out[i] = &filter->pins[filter->enum_idx + i].IPin_iface;
IPin_AddRef(out[i]);
}
if (fetched)
*fetched = i;
filter->enum_idx += i;
return (i == count) ? S_OK : S_FALSE;
}
static HRESULT WINAPI testenumpins_Skip(IEnumPins *iface, ULONG count)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static HRESULT WINAPI testenumpins_Reset(IEnumPins *iface)
{
struct testfilter *filter = impl_from_IEnumPins(iface);
filter->enum_idx = 0;
return S_OK;
}
static HRESULT WINAPI testenumpins_Clone(IEnumPins *iface, IEnumPins **out)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static const IEnumPinsVtbl testenumpins_vtbl =
{
testenumpins_QueryInterface,
testenumpins_AddRef,
testenumpins_Release,
testenumpins_Next,
testenumpins_Skip,
testenumpins_Reset,
testenumpins_Clone,
};
static inline struct testfilter *impl_from_IBaseFilter(IBaseFilter *iface)
{
return CONTAINING_RECORD(iface, struct testfilter, IBaseFilter_iface);
}
static HRESULT WINAPI testfilter_QueryInterface(IBaseFilter *iface, REFIID iid, void **out)
{
struct testfilter *filter = impl_from_IBaseFilter(iface);
if (winetest_debug > 1) trace("%p->QueryInterface(%s)\n", filter, wine_dbgstr_guid(iid));
if (IsEqualGUID(iid, &IID_IUnknown)
|| IsEqualGUID(iid, &IID_IPersist)
|| IsEqualGUID(iid, &IID_IMediaFilter)
|| IsEqualGUID(iid, &IID_IBaseFilter))
{
*out = &filter->IBaseFilter_iface;
}
else if (IsEqualGUID(iid, &IID_IAMFilterMiscFlags) && filter->IAMFilterMiscFlags_iface.lpVtbl)
{
*out = &filter->IAMFilterMiscFlags_iface;
}
else if (IsEqualGUID(iid, &IID_IMediaSeeking) && filter->IMediaSeeking_iface.lpVtbl)
{
*out = &filter->IMediaSeeking_iface;
}
else if (IsEqualGUID(iid, &IID_IReferenceClock) && filter->IReferenceClock_iface.lpVtbl)
{
*out = &filter->IReferenceClock_iface;
}
else if (IsEqualGUID(iid, &IID_IFileSourceFilter) && filter->IFileSourceFilter_iface.lpVtbl)
{
*out = &filter->IFileSourceFilter_iface;
}
else
{
*out = NULL;
return E_NOINTERFACE;
}
IUnknown_AddRef((IUnknown *)*out);
return S_OK;
}
static ULONG WINAPI testfilter_AddRef(IBaseFilter *iface)
{
struct testfilter *filter = impl_from_IBaseFilter(iface);
return InterlockedIncrement(&filter->ref);
}
static ULONG WINAPI testfilter_Release(IBaseFilter *iface)
{
struct testfilter *filter = impl_from_IBaseFilter(iface);
return InterlockedDecrement(&filter->ref);
}
static HRESULT WINAPI testfilter_GetClassID(IBaseFilter *iface, CLSID *clsid)
{
if (winetest_debug > 1) trace("%p->GetClassID()\n", iface);
memset(clsid, 0xde, sizeof(*clsid));
return S_OK;
}
/* Downstream filters are always stopped before any filters they are connected
* to upstream. Native actually implements this by topologically sorting filters
* as they are connected. */
static void check_state_transition(struct testfilter *filter, FILTER_STATE expect)
{
FILTER_STATE state;
unsigned int i;
PIN_INFO info;
for (i = 0; i < filter->pin_count; ++i)
{
if (filter->pins[i].peer)
{
IPin_QueryPinInfo(filter->pins[i].peer, &info);
IBaseFilter_GetState(info.pFilter, 0, &state);
if (filter->pins[i].dir == PINDIR_OUTPUT)
ok(state == expect, "Expected state %d for downstream filter %p, got %d.\n",
expect, info.pFilter, state);
else
ok(state == filter->state, "Expected state %d for upstream filter %p, got %d.\n",
filter->state, info.pFilter, state);
IBaseFilter_Release(info.pFilter);
}
}
}
static HRESULT WINAPI testfilter_Stop(IBaseFilter *iface)
{
struct testfilter *filter = impl_from_IBaseFilter(iface);
if (winetest_debug > 1) trace("%p->Stop()\n", filter);
check_state_transition(filter, State_Stopped);
filter->state = State_Stopped;
return filter->state_hr;
}
static HRESULT WINAPI testfilter_Pause(IBaseFilter *iface)
{
struct testfilter *filter = impl_from_IBaseFilter(iface);
if (winetest_debug > 1) trace("%p->Pause()\n", filter);
check_state_transition(filter, State_Paused);
filter->state = State_Paused;
return filter->state_hr;
}
static HRESULT WINAPI testfilter_Run(IBaseFilter *iface, REFERENCE_TIME start)
{
struct testfilter *filter = impl_from_IBaseFilter(iface);
if (winetest_debug > 1) trace("%p->Run(%s)\n", filter, wine_dbgstr_longlong(start));
check_state_transition(filter, State_Running);
filter->state = State_Running;
filter->start_time = start;
return filter->state_hr;
}
static HRESULT WINAPI testfilter_GetState(IBaseFilter *iface, DWORD timeout, FILTER_STATE *state)
{
struct testfilter *filter = impl_from_IBaseFilter(iface);
if (winetest_debug > 1) trace("%p->GetState(%u)\n", filter, timeout);
*state = filter->state;
return filter->state_hr;
}
static HRESULT WINAPI testfilter_SetSyncSource(IBaseFilter *iface, IReferenceClock *clock)
{
struct testfilter *filter = impl_from_IBaseFilter(iface);
if (winetest_debug > 1) trace("%p->SetSyncSource(%p)\n", filter, clock);
if (filter->clock)
IReferenceClock_Release(filter->clock);
if (clock)
IReferenceClock_AddRef(clock);
filter->clock = clock;
return S_OK;
}
static HRESULT WINAPI testfilter_GetSyncSource(IBaseFilter *iface, IReferenceClock **clock)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static HRESULT WINAPI testfilter_EnumPins(IBaseFilter *iface, IEnumPins **out)
{
struct testfilter *filter = impl_from_IBaseFilter(iface);
if (winetest_debug > 1) trace("%p->EnumPins()\n", filter);
*out = &filter->IEnumPins_iface;
IEnumPins_AddRef(*out);
filter->enum_idx = 0;
return S_OK;
}
static HRESULT WINAPI testfilter_FindPin(IBaseFilter *iface, const WCHAR *id, IPin **pin)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static HRESULT WINAPI testfilter_QueryFilterInfo(IBaseFilter *iface, FILTER_INFO *info)
{
struct testfilter *filter = impl_from_IBaseFilter(iface);
if (winetest_debug > 1) trace("%p->QueryFilterInfo()\n", filter);
info->pGraph = filter->graph;
if (filter->graph)
IFilterGraph_AddRef(filter->graph);
if (filter->name)
wcscpy(info->achName, filter->name);
else
info->achName[0] = 0;
return S_OK;
}
static HRESULT WINAPI testfilter_JoinFilterGraph(IBaseFilter *iface, IFilterGraph *graph, const WCHAR *name)
{
struct testfilter *filter = impl_from_IBaseFilter(iface);
if (winetest_debug > 1) trace("%p->JoinFilterGraph(%p, %s)\n", filter, graph, wine_dbgstr_w(name));
filter->graph = graph;
heap_free(filter->name);
if (name)
{
filter->name = heap_alloc((wcslen(name) + 1) * sizeof(WCHAR));
wcscpy(filter->name, name);
}
else
filter->name = NULL;
return S_OK;
}
static HRESULT WINAPI testfilter_QueryVendorInfo(IBaseFilter * iface, WCHAR **info)
{
return E_NOTIMPL;
}
static const IBaseFilterVtbl testfilter_vtbl =
{
testfilter_QueryInterface,
testfilter_AddRef,
testfilter_Release,
testfilter_GetClassID,
testfilter_Stop,
testfilter_Pause,
testfilter_Run,
testfilter_GetState,
testfilter_SetSyncSource,
testfilter_GetSyncSource,
testfilter_EnumPins,
testfilter_FindPin,
testfilter_QueryFilterInfo,
testfilter_JoinFilterGraph,
testfilter_QueryVendorInfo
};
static struct testfilter *impl_from_IAMFilterMiscFlags(IAMFilterMiscFlags *iface)
{
return CONTAINING_RECORD(iface, struct testfilter, IAMFilterMiscFlags_iface);
}
static HRESULT WINAPI testmiscflags_QueryInterface(IAMFilterMiscFlags *iface, REFIID iid, void **out)
{
struct testfilter *filter = impl_from_IAMFilterMiscFlags(iface);
return IBaseFilter_QueryInterface(&filter->IBaseFilter_iface, iid, out);
}
static ULONG WINAPI testmiscflags_AddRef(IAMFilterMiscFlags *iface)
{
struct testfilter *filter = impl_from_IAMFilterMiscFlags(iface);
return InterlockedIncrement(&filter->ref);
}
static ULONG WINAPI testmiscflags_Release(IAMFilterMiscFlags *iface)
{
struct testfilter *filter = impl_from_IAMFilterMiscFlags(iface);
return InterlockedDecrement(&filter->ref);
}
static ULONG WINAPI testmiscflags_GetMiscFlags(IAMFilterMiscFlags *iface)
{
struct testfilter *filter = impl_from_IAMFilterMiscFlags(iface);
if (winetest_debug > 1) trace("%p->GetMiscFlags()\n", filter);
return filter->misc_flags;
}
static const IAMFilterMiscFlagsVtbl testmiscflags_vtbl =
{
testmiscflags_QueryInterface,
testmiscflags_AddRef,
testmiscflags_Release,
testmiscflags_GetMiscFlags,
};
static struct testfilter *impl_from_IMediaSeeking(IMediaSeeking *iface)
{
return CONTAINING_RECORD(iface, struct testfilter, IMediaSeeking_iface);
}
static HRESULT WINAPI testseek_QueryInterface(IMediaSeeking *iface, REFIID iid, void **out)
{
struct testfilter *filter = impl_from_IMediaSeeking(iface);
return IBaseFilter_QueryInterface(&filter->IBaseFilter_iface, iid, out);
}
static ULONG WINAPI testseek_AddRef(IMediaSeeking *iface)
{
struct testfilter *filter = impl_from_IMediaSeeking(iface);
InterlockedIncrement(&filter->seeking_ref);
return InterlockedIncrement(&filter->ref);
}
static ULONG WINAPI testseek_Release(IMediaSeeking *iface)
{
struct testfilter *filter = impl_from_IMediaSeeking(iface);
InterlockedDecrement(&filter->seeking_ref);
return InterlockedDecrement(&filter->ref);
}
static HRESULT WINAPI testseek_GetCapabilities(IMediaSeeking *iface, DWORD *caps)
{
struct testfilter *filter = impl_from_IMediaSeeking(iface);
if (winetest_debug > 1) trace("%p->GetCapabilities()\n", iface);
*caps = filter->seek_caps;
return S_OK;
}
static HRESULT WINAPI testseek_CheckCapabilities(IMediaSeeking *iface, DWORD *caps)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static HRESULT WINAPI testseek_IsFormatSupported(IMediaSeeking *iface, const GUID *format)
{
struct testfilter *filter = impl_from_IMediaSeeking(iface);
if (winetest_debug > 1) trace("%p->IsFormatSupported(%s)\n", iface, wine_dbgstr_guid(format));
if (IsEqualGUID(format, &testguid) && !filter->support_testguid)
return S_FALSE;
return S_OK;
}
static HRESULT WINAPI testseek_QueryPreferredFormat(IMediaSeeking *iface, GUID *format)
{
if (winetest_debug > 1) trace("%p->QueryPreferredFormat()\n", iface);
return E_NOTIMPL;
}
static HRESULT WINAPI testseek_GetTimeFormat(IMediaSeeking *iface, GUID *format)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static HRESULT WINAPI testseek_IsUsingTimeFormat(IMediaSeeking *iface, const GUID *format)
{
if (winetest_debug > 1) trace("%p->IsUsingTimeFormat(%s)\n", iface, wine_dbgstr_guid(format));
return S_OK;
}
static HRESULT WINAPI testseek_SetTimeFormat(IMediaSeeking *iface, const GUID *format)
{
struct testfilter *filter = impl_from_IMediaSeeking(iface);
if (winetest_debug > 1) trace("%p->SetTimeFormat(%s)\n", iface, wine_dbgstr_guid(format));
if (IsEqualGUID(format, &testguid) && !filter->support_testguid)
return E_INVALIDARG;
return S_OK;
}
static HRESULT WINAPI testseek_GetDuration(IMediaSeeking *iface, LONGLONG *duration)
{
struct testfilter *filter = impl_from_IMediaSeeking(iface);
if (winetest_debug > 1) trace("%p->GetDuration()\n", iface);
*duration = filter->seek_duration;
return S_OK;
}
static HRESULT WINAPI testseek_GetStopPosition(IMediaSeeking *iface, LONGLONG *stop)
{
struct testfilter *filter = impl_from_IMediaSeeking(iface);
if (winetest_debug > 1) trace("%p->GetStopPosition()\n", iface);
*stop = filter->seek_stop;
return filter->seek_hr;
}
static HRESULT WINAPI testseek_GetCurrentPosition(IMediaSeeking *iface, LONGLONG *current)
{
struct testfilter *filter = impl_from_IMediaSeeking(iface);
if (winetest_debug > 1) trace("%p->GetCurrentPosition()\n", iface);
ok(!filter->clock, "GetCurrentPosition() should only be called if there is no sync source.\n");
*current = 0xdeadbeef;
return S_OK;
}
static HRESULT WINAPI testseek_ConvertTimeFormat(IMediaSeeking *iface, LONGLONG *target,
const GUID *target_format, LONGLONG source, const GUID *source_format)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static HRESULT WINAPI testseek_SetPositions(IMediaSeeking *iface, LONGLONG *current,
DWORD current_flags, LONGLONG *stop, DWORD stop_flags )
{
struct testfilter *filter = impl_from_IMediaSeeking(iface);
if (winetest_debug > 1) trace("%p->SetPositions(%s, %#x, %s, %#x)\n",
iface, wine_dbgstr_longlong(*current), current_flags, wine_dbgstr_longlong(*stop), stop_flags);
ok(filter->state != State_Running, "Filter should be paused or stopped while seeking.\n");
filter->seek_current = *current;
filter->seek_stop = *stop;
*current = 12340000;
*stop = 43210000;
return filter->seek_hr;
}
static HRESULT WINAPI testseek_GetPositions(IMediaSeeking *iface, LONGLONG *current, LONGLONG *stop)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static HRESULT WINAPI testseek_GetAvailable(IMediaSeeking *iface, LONGLONG *earliest, LONGLONG *latest)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static HRESULT WINAPI testseek_SetRate(IMediaSeeking *iface, double rate)
{
struct testfilter *filter = impl_from_IMediaSeeking(iface);
if (winetest_debug > 1) trace("%p->SetRate(%.16e)\n", iface, rate);
filter->seek_rate = rate;
return S_OK;
}
static HRESULT WINAPI testseek_GetRate(IMediaSeeking *iface, double *rate)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static HRESULT WINAPI testseek_GetPreroll(IMediaSeeking *iface, LONGLONG *preroll)
{
if (winetest_debug > 1) trace("%p->GetPreroll()\n", iface);
return E_NOTIMPL;
}
static const IMediaSeekingVtbl testseek_vtbl =
{
testseek_QueryInterface,
testseek_AddRef,
testseek_Release,
testseek_GetCapabilities,
testseek_CheckCapabilities,
testseek_IsFormatSupported,
testseek_QueryPreferredFormat,
testseek_GetTimeFormat,
testseek_IsUsingTimeFormat,
testseek_SetTimeFormat,
testseek_GetDuration,
testseek_GetStopPosition,
testseek_GetCurrentPosition,
testseek_ConvertTimeFormat,
testseek_SetPositions,
testseek_GetPositions,
testseek_GetAvailable,
testseek_SetRate,
testseek_GetRate,
testseek_GetPreroll,
};
static struct testfilter *impl_from_IReferenceClock(IReferenceClock *iface)
{
return CONTAINING_RECORD(iface, struct testfilter, IReferenceClock_iface);
}
static HRESULT WINAPI testclock_QueryInterface(IReferenceClock *iface, REFIID iid, void **out)
{
struct testfilter *filter = impl_from_IReferenceClock(iface);
return IBaseFilter_QueryInterface(&filter->IBaseFilter_iface, iid, out);
}
static ULONG WINAPI testclock_AddRef(IReferenceClock *iface)
{
struct testfilter *filter = impl_from_IReferenceClock(iface);
return InterlockedIncrement(&filter->ref);
}
static ULONG WINAPI testclock_Release(IReferenceClock *iface)
{
struct testfilter *filter = impl_from_IReferenceClock(iface);
return InterlockedDecrement(&filter->ref);
}
static HRESULT WINAPI testclock_GetTime(IReferenceClock *iface, REFERENCE_TIME *time)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static HRESULT WINAPI testclock_AdviseTime(IReferenceClock *iface,
REFERENCE_TIME base, REFERENCE_TIME offset, HEVENT event, DWORD_PTR *cookie)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static HRESULT WINAPI testclock_AdvisePeriodic(IReferenceClock *iface,
REFERENCE_TIME start, REFERENCE_TIME period, HSEMAPHORE semaphore, DWORD_PTR *cookie)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static HRESULT WINAPI testclock_Unadvise(IReferenceClock *iface, DWORD_PTR cookie)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static const IReferenceClockVtbl testclock_vtbl =
{
testclock_QueryInterface,
testclock_AddRef,
testclock_Release,
testclock_GetTime,
testclock_AdviseTime,
testclock_AdvisePeriodic,
testclock_Unadvise,
};
static struct testfilter *impl_from_IFileSourceFilter(IFileSourceFilter *iface)
{
return CONTAINING_RECORD(iface, struct testfilter, IFileSourceFilter_iface);
}
static HRESULT WINAPI testfilesource_QueryInterface(IFileSourceFilter *iface, REFIID iid, void **out)
{
struct testfilter *filter = impl_from_IFileSourceFilter(iface);
return IBaseFilter_QueryInterface(&filter->IBaseFilter_iface, iid, out);
}
static ULONG WINAPI testfilesource_AddRef(IFileSourceFilter *iface)
{
struct testfilter *filter = impl_from_IFileSourceFilter(iface);
return InterlockedIncrement(&filter->ref);
}
static ULONG WINAPI testfilesource_Release(IFileSourceFilter *iface)
{
struct testfilter *filter = impl_from_IFileSourceFilter(iface);
return InterlockedDecrement(&filter->ref);
}
static HRESULT WINAPI testfilesource_Load(IFileSourceFilter *iface,
const WCHAR *filename, const AM_MEDIA_TYPE *mt)
{
struct testfilter *filter = impl_from_IFileSourceFilter(iface);
if (winetest_debug > 1) trace("%p->Load()\n", iface);
wcscpy(filter->filename, filename);
return S_OK;
}
static HRESULT WINAPI testfilesource_GetCurFile(IFileSourceFilter *iface,
WCHAR **filename, AM_MEDIA_TYPE *mt)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
}
static const IFileSourceFilterVtbl testfilesource_vtbl =
{
testfilesource_QueryInterface,
testfilesource_AddRef,
testfilesource_Release,
testfilesource_Load,
testfilesource_GetCurFile,
};
struct testfilter_cf
{
IClassFactory IClassFactory_iface;
struct testfilter *filter;
};
static void testfilter_init(struct testfilter *filter, struct testpin *pins, int pin_count)
{
unsigned int i;
memset(filter, 0, sizeof(*filter));
filter->IBaseFilter_iface.lpVtbl = &testfilter_vtbl;
filter->IEnumPins_iface.lpVtbl = &testenumpins_vtbl;
filter->ref = 1;
filter->pins = pins;
filter->pin_count = pin_count;
for (i = 0; i < pin_count; i++)
pins[i].filter = &filter->IBaseFilter_iface;
filter->state = State_Stopped;
}
static HRESULT WINAPI testfilter_cf_QueryInterface(IClassFactory *iface, REFIID iid, void **out)
{
if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IClassFactory))
{
*out = iface;
return S_OK;
}
*out = NULL;
return E_NOINTERFACE;
}
static ULONG WINAPI testfilter_cf_AddRef(IClassFactory *iface)
{
return 2;
}
static ULONG WINAPI testfilter_cf_Release(IClassFactory *iface)
{
return 1;
}
static HRESULT WINAPI testfilter_cf_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID iid, void **out)
{
struct testfilter_cf *factory = CONTAINING_RECORD(iface, struct testfilter_cf, IClassFactory_iface);
return IBaseFilter_QueryInterface(&factory->filter->IBaseFilter_iface, iid, out);
}
static HRESULT WINAPI testfilter_cf_LockServer(IClassFactory *iface, BOOL lock)
{
return E_NOTIMPL;
}
static IClassFactoryVtbl testfilter_cf_vtbl =
{
testfilter_cf_QueryInterface,
testfilter_cf_AddRef,
testfilter_cf_Release,
testfilter_cf_CreateInstance,
testfilter_cf_LockServer,
};
static void test_graph_builder_render(void)
{
static const GUID sink1_clsid = {0x12345678};
static const GUID sink2_clsid = {0x87654321};
AM_MEDIA_TYPE source_type = {{0}};
struct testpin source_pin, sink1_pin, sink2_pin, parser_pins[2];
struct testfilter source, sink1, sink2, parser;
struct testfilter_cf sink1_cf = { {&testfilter_cf_vtbl}, &sink1 };
struct testfilter_cf sink2_cf = { {&testfilter_cf_vtbl}, &sink2 };
IFilterGraph2 *graph = create_graph();
REGFILTERPINS2 regpins = {0};
REGPINTYPES regtypes = {0};
REGFILTER2 regfilter = {0};
IFilterMapper2 *mapper;
DWORD cookie1, cookie2;
HRESULT hr;
ULONG ref;
memset(&source_type.majortype, 0xcc, sizeof(GUID));
testsource_init(&source_pin, &source_type, 1);
testfilter_init(&source, &source_pin, 1);
testsink_init(&sink1_pin);
testfilter_init(&sink1, &sink1_pin, 1);
testsink_init(&sink2_pin);
testfilter_init(&sink2, &sink2_pin, 1);
testsink_init(&parser_pins[0]);
testsource_init(&parser_pins[1], &source_type, 1);
testfilter_init(&parser, parser_pins, 2);
IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &sink1.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &sink2.IBaseFilter_iface, NULL);
hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &sink2_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
IFilterGraph2_Disconnect(graph, source_pin.peer);
IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
IFilterGraph2_RemoveFilter(graph, &sink1.IBaseFilter_iface);
IFilterGraph2_AddFilter(graph, &sink1.IBaseFilter_iface, NULL);
hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &sink1_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
IFilterGraph2_Disconnect(graph, &sink1_pin.IPin_iface);
/* No preference is given to smaller chains. */
IFilterGraph2_AddFilter(graph, &parser.IBaseFilter_iface, NULL);
hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &parser_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(parser_pins[1].peer == &sink1_pin.IPin_iface, "Got peer %p.\n", parser_pins[1].peer);
IFilterGraph2_Disconnect(graph, source_pin.peer);
IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
IFilterGraph2_Disconnect(graph, parser_pins[0].peer);
IFilterGraph2_Disconnect(graph, &parser_pins[0].IPin_iface);
IFilterGraph2_RemoveFilter(graph, &sink1.IBaseFilter_iface);
IFilterGraph2_AddFilter(graph, &sink1.IBaseFilter_iface, NULL);
hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &sink1_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
IFilterGraph2_Disconnect(graph, source_pin.peer);
IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
/* A pin whose name (not ID) begins with a tilde is not rendered. */
IFilterGraph2_RemoveFilter(graph, &sink2.IBaseFilter_iface);
IFilterGraph2_RemoveFilter(graph, &parser.IBaseFilter_iface);
IFilterGraph2_AddFilter(graph, &parser.IBaseFilter_iface, NULL);
parser_pins[1].name[0] = '~';
hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &parser_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(!parser_pins[1].peer, "Got peer %p.\n", parser_pins[1].peer);
ok(!sink1_pin.peer, "Got peer %p.\n", sink1_pin.peer);
IFilterGraph2_Disconnect(graph, source_pin.peer);
IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
parser_pins[1].name[0] = 0;
parser_pins[1].id[0] = '~';
hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &parser_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(parser_pins[1].peer == &sink1_pin.IPin_iface, "Got peer %p.\n", parser_pins[1].peer);
IFilterGraph2_Disconnect(graph, source_pin.peer);
IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
ref = IFilterGraph2_Release(graph);
ok(!ref, "Got outstanding refcount %d.\n", ref);
/* Test enumeration of filters from the registry. */
graph = create_graph();
IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
CoRegisterClassObject(&sink1_clsid, (IUnknown *)&sink1_cf.IClassFactory_iface,
CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie1);
CoRegisterClassObject(&sink2_clsid, (IUnknown *)&sink2_cf.IClassFactory_iface,
CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie2);
CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER,
&IID_IFilterMapper2, (void **)&mapper);
regfilter.dwVersion = 2;
regfilter.dwMerit = MERIT_UNLIKELY;
regfilter.cPins2 = 1;
regfilter.rgPins2 = &regpins;
regpins.dwFlags = 0;
regpins.cInstances = 1;
regpins.nMediaTypes = 1;
regpins.lpMediaType = &regtypes;
regtypes.clsMajorType = &source_type.majortype;
regtypes.clsMinorType = &MEDIASUBTYPE_NULL;
hr = IFilterMapper2_RegisterFilter(mapper, &sink1_clsid, L"test", NULL, NULL, NULL, &regfilter);
if (hr == E_ACCESSDENIED)
{
skip("Not enough permission to register filters.\n");
goto out;
}
ok(hr == S_OK, "Got hr %#x.\n", hr);
regpins.dwFlags = REG_PINFLAG_B_RENDERER;
IFilterMapper2_RegisterFilter(mapper, &sink2_clsid, L"test", NULL, NULL, NULL, &regfilter);
hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &sink2_pin.IPin_iface || source_pin.peer == &sink1_pin.IPin_iface,
"Got peer %p.\n", source_pin.peer);
ref = IFilterGraph2_Release(graph);
ok(!ref, "Got outstanding refcount %d.\n", ref);
/* Preference is given to filters already in the graph. */
graph = create_graph();
IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &sink2.IBaseFilter_iface, NULL);
hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &sink2_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
ref = IFilterGraph2_Release(graph);
ok(!ref, "Got outstanding refcount %d.\n", ref);
/* No preference is given to renderer filters. */
graph = create_graph();
IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink1_clsid);
IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink2_clsid);
IFilterMapper2_RegisterFilter(mapper, &sink1_clsid, L"test", NULL, NULL, NULL, &regfilter);
regpins.dwFlags = 0;
IFilterMapper2_RegisterFilter(mapper, &sink2_clsid, L"test", NULL, NULL, NULL, &regfilter);
hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &sink2_pin.IPin_iface || source_pin.peer == &sink1_pin.IPin_iface,
"Got peer %p.\n", source_pin.peer);
ref = IFilterGraph2_Release(graph);
ok(!ref, "Got outstanding refcount %d.\n", ref);
/* Preference is given to filters with higher merit. */
graph = create_graph();
IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink1_clsid);
IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink2_clsid);
regfilter.dwMerit = MERIT_UNLIKELY;
IFilterMapper2_RegisterFilter(mapper, &sink1_clsid, L"test", NULL, NULL, NULL, &regfilter);
regfilter.dwMerit = MERIT_PREFERRED;
IFilterMapper2_RegisterFilter(mapper, &sink2_clsid, L"test", NULL, NULL, NULL, &regfilter);
hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &sink2_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
ref = IFilterGraph2_Release(graph);
ok(!ref, "Got outstanding refcount %d.\n", ref);
graph = create_graph();
IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink1_clsid);
IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink2_clsid);
regfilter.dwMerit = MERIT_PREFERRED;
IFilterMapper2_RegisterFilter(mapper, &sink1_clsid, L"test", NULL, NULL, NULL, &regfilter);
regfilter.dwMerit = MERIT_UNLIKELY;
IFilterMapper2_RegisterFilter(mapper, &sink2_clsid, L"test", NULL, NULL, NULL, &regfilter);
hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &sink1_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink1_clsid);
IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink2_clsid);
out:
CoRevokeClassObject(cookie1);
CoRevokeClassObject(cookie2);
IFilterMapper2_Release(mapper);
ref = IFilterGraph2_Release(graph);
ok(!ref, "Got outstanding refcount %d.\n", ref);
ok(source.ref == 1, "Got outstanding refcount %d.\n", source.ref);
ok(source_pin.ref == 1, "Got outstanding refcount %d.\n", source_pin.ref);
ok(sink1.ref == 1, "Got outstanding refcount %d.\n", sink1.ref);
ok(sink1_pin.ref == 1, "Got outstanding refcount %d.\n", sink1_pin.ref);
ok(sink2.ref == 1, "Got outstanding refcount %d.\n", sink2.ref);
ok(sink2_pin.ref == 1, "Got outstanding refcount %d.\n", sink2_pin.ref);
ok(parser.ref == 1, "Got outstanding refcount %d.\n", parser.ref);
ok(parser_pins[0].ref == 1, "Got outstanding refcount %d.\n", parser_pins[0].ref);
ok(parser_pins[1].ref == 1, "Got outstanding refcount %d.\n", parser_pins[1].ref);
}
static void test_graph_builder_connect(void)
{
static const GUID parser1_clsid = {0x12345678};
static const GUID parser2_clsid = {0x87654321};
AM_MEDIA_TYPE source_type = {{0}}, sink_type = {{0}}, parser3_type = {{0}};
struct testpin source_pin, sink_pin, sink2_pin, parser1_pins[3], parser2_pins[2], parser3_pins[2];
struct testfilter source, sink, sink2, parser1, parser2, parser3;
struct testfilter_cf parser1_cf = { {&testfilter_cf_vtbl}, &parser1 };
struct testfilter_cf parser2_cf = { {&testfilter_cf_vtbl}, &parser2 };
IFilterGraph2 *graph = create_graph();
REGFILTERPINS2 regpins[2] = {{0}};
REGPINTYPES regtypes = {0};
REGFILTER2 regfilter = {0};
IFilterMapper2 *mapper;
DWORD cookie1, cookie2;
HRESULT hr;
ULONG ref;
memset(&source_type.majortype, 0xcc, sizeof(GUID));
memset(&sink_type.majortype, 0x66, sizeof(GUID));
testsource_init(&source_pin, &source_type, 1);
source_pin.request_mt = &source_type;
testfilter_init(&source, &source_pin, 1);
testsink_init(&sink_pin);
testfilter_init(&sink, &sink_pin, 1);
testsink_init(&sink2_pin);
testfilter_init(&sink2, &sink2_pin, 1);
testsink_init(&parser1_pins[0]);
testsource_init(&parser1_pins[1], &sink_type, 1);
parser1_pins[1].request_mt = &sink_type;
testsource_init(&parser1_pins[2], &sink_type, 1);
parser1_pins[2].request_mt = &sink_type;
testfilter_init(&parser1, parser1_pins, 3);
parser1.pin_count = 2;
testsink_init(&parser2_pins[0]);
testsource_init(&parser2_pins[1], &sink_type, 1);
parser2_pins[1].request_mt = &sink_type;
testfilter_init(&parser2, parser2_pins, 2);
testsink_init(&parser3_pins[0]);
testsource_init(&parser3_pins[1], &sink_type, 1);
parser3_pins[1].request_mt = &parser3_type;
testfilter_init(&parser3, parser3_pins, 2);
IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
IFilterGraph2_Disconnect(graph, source_pin.peer);
IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
for (source_pin.Connect_hr = 0x00040200; source_pin.Connect_hr <= 0x000402ff;
++source_pin.Connect_hr)
{
hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
ok(hr == source_pin.Connect_hr, "Got hr %#x for Connect() hr %#x.\n",
hr, source_pin.Connect_hr);
ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
IFilterGraph2_Disconnect(graph, source_pin.peer);
IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
}
source_pin.Connect_hr = S_OK;
sink_pin.accept_mt = &sink_type;
hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
ok(hr == VFW_E_CANNOT_CONNECT, "Got hr %#x.\n", hr);
ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
for (source_pin.Connect_hr = 0x80040200; source_pin.Connect_hr <= 0x800402ff;
++source_pin.Connect_hr)
{
hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
if (source_pin.Connect_hr == VFW_E_NOT_CONNECTED
|| source_pin.Connect_hr == VFW_E_NO_AUDIO_HARDWARE)
ok(hr == source_pin.Connect_hr, "Got hr %#x for Connect() hr %#x.\n",
hr, source_pin.Connect_hr);
else
ok(hr == VFW_E_CANNOT_CONNECT, "Got hr %#x for Connect() hr %#x.\n",
hr, source_pin.Connect_hr);
ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
}
source_pin.Connect_hr = S_OK;
for (source_pin.EnumMediaTypes_hr = 0x80040200; source_pin.EnumMediaTypes_hr <= 0x800402ff;
++source_pin.EnumMediaTypes_hr)
{
hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
ok(hr == source_pin.EnumMediaTypes_hr, "Got hr %#x for EnumMediaTypes() hr %#x.\n",
hr, source_pin.EnumMediaTypes_hr);
ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
}
source_pin.EnumMediaTypes_hr = S_OK;
/* Test usage of intermediate filters. Similarly to Render(), filters are
* simply tried in enumeration order. */
IFilterGraph2_AddFilter(graph, &parser1.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &parser2.IBaseFilter_iface, NULL);
sink_pin.accept_mt = NULL;
hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
IFilterGraph2_Disconnect(graph, source_pin.peer);
IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
sink_pin.accept_mt = &sink_type;
hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &parser2_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(sink_pin.peer == &parser2_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
IFilterGraph2_Disconnect(graph, source_pin.peer);
IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
IFilterGraph2_Disconnect(graph, sink_pin.peer);
IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
for (source_pin.Connect_hr = 0x00040200; source_pin.Connect_hr <= 0x000402ff;
++source_pin.Connect_hr)
{
hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x for Connect() hr %#x.\n", hr, source_pin.Connect_hr);
ok(source_pin.peer == &parser2_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(sink_pin.peer == &parser2_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
IFilterGraph2_Disconnect(graph, source_pin.peer);
IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
IFilterGraph2_Disconnect(graph, sink_pin.peer);
IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
}
source_pin.Connect_hr = S_OK;
IFilterGraph2_RemoveFilter(graph, &parser1.IBaseFilter_iface);
IFilterGraph2_AddFilter(graph, &parser1.IBaseFilter_iface, NULL);
hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
IFilterGraph2_Disconnect(graph, source_pin.peer);
IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
IFilterGraph2_Disconnect(graph, sink_pin.peer);
IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
/* No preference is given to smaller chains. */
IFilterGraph2_AddFilter(graph, &parser3.IBaseFilter_iface, NULL);
hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &parser3_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(parser3_pins[1].peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", parser3_pins[1].peer);
ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
IFilterGraph2_Disconnect(graph, source_pin.peer);
IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
IFilterGraph2_Disconnect(graph, parser3_pins[0].peer);
IFilterGraph2_Disconnect(graph, &parser3_pins[0].IPin_iface);
IFilterGraph2_Disconnect(graph, sink_pin.peer);
IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
IFilterGraph2_RemoveFilter(graph, &parser3.IBaseFilter_iface);
IFilterGraph2_RemoveFilter(graph, &parser2.IBaseFilter_iface);
/* Extra source pins on an intermediate filter are not rendered. */
IFilterGraph2_RemoveFilter(graph, &parser1.IBaseFilter_iface);
parser1.pin_count = 3;
IFilterGraph2_AddFilter(graph, &parser1.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &sink2.IBaseFilter_iface, NULL);
hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
todo_wine
ok(hr == VFW_S_PARTIAL_RENDER, "Got hr %#x.\n", hr);
ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
ok(!parser1_pins[2].peer, "Got peer %p.\n", parser1_pins[2].peer);
IFilterGraph2_Disconnect(graph, source_pin.peer);
IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
IFilterGraph2_Disconnect(graph, sink_pin.peer);
IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
parser1.pin_count = 2;
/* QueryInternalConnections is not used to find output pins. */
parser1_pins[1].QueryInternalConnections_hr = S_OK;
hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
IFilterGraph2_Disconnect(graph, source_pin.peer);
IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
IFilterGraph2_Disconnect(graph, sink_pin.peer);
IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
parser1_pins[1].QueryInternalConnections_hr = E_NOTIMPL;
/* A pin whose name (not ID) begins with a tilde is not connected. */
parser1_pins[1].name[0] = '~';
hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
ok(hr == VFW_E_CANNOT_CONNECT, "Got hr %#x.\n", hr);
ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
parser1.pin_count = 3;
hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(sink_pin.peer == &parser1_pins[2].IPin_iface, "Got peer %p.\n", sink_pin.peer);
IFilterGraph2_Disconnect(graph, source_pin.peer);
IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
IFilterGraph2_Disconnect(graph, sink_pin.peer);
IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
parser1.pin_count = 2;
parser1_pins[1].name[0] = 0;
parser1_pins[1].id[0] = '~';
hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
IFilterGraph2_Disconnect(graph, source_pin.peer);
IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
IFilterGraph2_Disconnect(graph, sink_pin.peer);
IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
hr = IFilterGraph2_Connect(graph, &parser1_pins[1].IPin_iface, &parser1_pins[0].IPin_iface);
ok(hr == VFW_E_CANNOT_CONNECT, "Got hr %#x.\n", hr);
parser1_pins[0].QueryInternalConnections_hr = S_OK;
hr = IFilterGraph2_Connect(graph, &parser1_pins[1].IPin_iface, &parser1_pins[0].IPin_iface);
todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
parser1_pins[0].QueryInternalConnections_hr = E_NOTIMPL;
ref = IFilterGraph2_Release(graph);
ok(!ref, "Got outstanding refcount %d.\n", ref);
/* Test enumeration of filters from the registry. */
graph = create_graph();
IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
CoRegisterClassObject(&parser1_clsid, (IUnknown *)&parser1_cf.IClassFactory_iface,
CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie1);
CoRegisterClassObject(&parser2_clsid, (IUnknown *)&parser2_cf.IClassFactory_iface,
CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie2);
CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER,
&IID_IFilterMapper2, (void **)&mapper);
regfilter.dwVersion = 2;
regfilter.dwMerit = MERIT_UNLIKELY;
regfilter.cPins2 = 2;
regfilter.rgPins2 = regpins;
regpins[0].dwFlags = 0;
regpins[0].cInstances = 1;
regpins[0].nMediaTypes = 1;
regpins[0].lpMediaType = &regtypes;
regpins[1].dwFlags = REG_PINFLAG_B_OUTPUT;
regpins[1].cInstances = 1;
regpins[1].nMediaTypes = 1;
regpins[1].lpMediaType = &regtypes;
regtypes.clsMajorType = &source_type.majortype;
regtypes.clsMinorType = &MEDIASUBTYPE_NULL;
hr = IFilterMapper2_RegisterFilter(mapper, &parser1_clsid, L"test", NULL, NULL, NULL, &regfilter);
if (hr == E_ACCESSDENIED)
{
skip("Not enough permission to register filters.\n");
goto out;
}
ok(hr == S_OK, "Got hr %#x.\n", hr);
IFilterMapper2_RegisterFilter(mapper, &parser2_clsid, L"test", NULL, NULL, NULL, &regfilter);
hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &parser1_pins[0].IPin_iface
|| source_pin.peer == &parser2_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(sink_pin.peer == &parser1_pins[1].IPin_iface
|| sink_pin.peer == &parser2_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
ref = IFilterGraph2_Release(graph);
ok(!ref, "Got outstanding refcount %d.\n", ref);
/* Preference is given to filters already in the graph. */
graph = create_graph();
IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &parser1.IBaseFilter_iface, NULL);
hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
ref = IFilterGraph2_Release(graph);
ok(!ref, "Got outstanding refcount %d.\n", ref);
/* Preference is given to filters with higher merit. */
graph = create_graph();
IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser1_clsid);
IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser2_clsid);
regfilter.dwMerit = MERIT_UNLIKELY;
IFilterMapper2_RegisterFilter(mapper, &parser1_clsid, L"test", NULL, NULL, NULL, &regfilter);
regfilter.dwMerit = MERIT_PREFERRED;
IFilterMapper2_RegisterFilter(mapper, &parser2_clsid, L"test", NULL, NULL, NULL, &regfilter);
hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &parser2_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(sink_pin.peer == &parser2_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
ref = IFilterGraph2_Release(graph);
ok(!ref, "Got outstanding refcount %d.\n", ref);
graph = create_graph();
IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser1_clsid);
IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser2_clsid);
regfilter.dwMerit = MERIT_PREFERRED;
IFilterMapper2_RegisterFilter(mapper, &parser1_clsid, L"test", NULL, NULL, NULL, &regfilter);
regfilter.dwMerit = MERIT_UNLIKELY;
IFilterMapper2_RegisterFilter(mapper, &parser2_clsid, L"test", NULL, NULL, NULL, &regfilter);
hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser1_clsid);
IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser2_clsid);
out:
CoRevokeClassObject(cookie1);
CoRevokeClassObject(cookie2);
IFilterMapper2_Release(mapper);
ref = IFilterGraph2_Release(graph);
ok(!ref, "Got outstanding refcount %d.\n", ref);
ok(source.ref == 1, "Got outstanding refcount %d.\n", source.ref);
ok(source_pin.ref == 1, "Got outstanding refcount %d.\n", source_pin.ref);
ok(sink.ref == 1, "Got outstanding refcount %d.\n", sink.ref);
ok(sink_pin.ref == 1, "Got outstanding refcount %d.\n", sink_pin.ref);
ok(parser1.ref == 1, "Got outstanding refcount %d.\n", parser1.ref);
ok(parser1_pins[0].ref == 1, "Got outstanding refcount %d.\n", parser1_pins[0].ref);
ok(parser1_pins[1].ref == 1, "Got outstanding refcount %d.\n", parser1_pins[1].ref);
ok(parser1_pins[2].ref == 1, "Got outstanding refcount %d.\n", parser1_pins[2].ref);
ok(parser2.ref == 1, "Got outstanding refcount %d.\n", parser2.ref);
ok(parser2_pins[0].ref == 1, "Got outstanding refcount %d.\n", parser2_pins[0].ref);
ok(parser2_pins[1].ref == 1, "Got outstanding refcount %d.\n", parser2_pins[1].ref);
ok(parser3.ref == 1, "Got outstanding refcount %d.\n", parser3.ref);
ok(parser3_pins[0].ref == 1, "Got outstanding refcount %d.\n", parser3_pins[0].ref);
ok(parser3_pins[1].ref == 1, "Got outstanding refcount %d.\n", parser3_pins[1].ref);
}
static const GUID test_iid = {0x33333333};
static LONG outer_ref = 1;
static HRESULT WINAPI outer_QueryInterface(IUnknown *iface, REFIID iid, void **out)
{
if (IsEqualGUID(iid, &IID_IUnknown)
|| IsEqualGUID(iid, &IID_IFilterGraph2)
|| IsEqualGUID(iid, &test_iid))
{
*out = (IUnknown *)0xdeadbeef;
return S_OK;
}
ok(0, "unexpected call %s\n", wine_dbgstr_guid(iid));
return E_NOINTERFACE;
}
static ULONG WINAPI outer_AddRef(IUnknown *iface)
{
return InterlockedIncrement(&outer_ref);
}
static ULONG WINAPI outer_Release(IUnknown *iface)
{
return InterlockedDecrement(&outer_ref);
}
static const IUnknownVtbl outer_vtbl =
{
outer_QueryInterface,
outer_AddRef,
outer_Release,
};
static IUnknown test_outer = {&outer_vtbl};
static void test_aggregation(void)
{
IFilterGraph2 *graph, *graph2;
IFilterMapper2 *mapper;
IUnknown *unk, *unk2;
HRESULT hr;
ULONG ref;
graph = (IFilterGraph2 *)0xdeadbeef;
hr = CoCreateInstance(&CLSID_FilterGraph, &test_outer, CLSCTX_INPROC_SERVER,
&IID_IFilterGraph2, (void **)&graph);
ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr);
ok(!graph, "Got interface %p.\n", graph);
hr = CoCreateInstance(&CLSID_FilterGraph, &test_outer, CLSCTX_INPROC_SERVER,
&IID_IUnknown, (void **)&unk);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(outer_ref == 1, "Got unexpected refcount %d.\n", outer_ref);
ok(unk != &test_outer, "Returned IUnknown should not be outer IUnknown.\n");
ref = get_refcount(unk);
ok(ref == 1, "Got unexpected refcount %d.\n", ref);
ref = IUnknown_AddRef(unk);
ok(ref == 2, "Got unexpected refcount %d.\n", ref);
ok(outer_ref == 1, "Got unexpected refcount %d.\n", outer_ref);
ref = IUnknown_Release(unk);
ok(ref == 1, "Got unexpected refcount %d.\n", ref);
ok(outer_ref == 1, "Got unexpected refcount %d.\n", outer_ref);
hr = IUnknown_QueryInterface(unk, &IID_IUnknown, (void **)&unk2);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(unk2 == unk, "Got unexpected IUnknown %p.\n", unk2);
IUnknown_Release(unk2);
hr = IUnknown_QueryInterface(unk, &IID_IFilterGraph2, (void **)&graph);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IFilterGraph2_QueryInterface(graph, &IID_IUnknown, (void **)&unk2);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(unk2 == (IUnknown *)0xdeadbeef, "Got unexpected IUnknown %p.\n", unk2);
hr = IFilterGraph2_QueryInterface(graph, &IID_IFilterGraph2, (void **)&graph2);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(graph2 == (IFilterGraph2 *)0xdeadbeef, "Got unexpected IFilterGraph2 %p.\n", graph2);
hr = IUnknown_QueryInterface(unk, &test_iid, (void **)&unk2);
ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr);
ok(!unk2, "Got unexpected IUnknown %p.\n", unk2);
hr = IFilterGraph2_QueryInterface(graph, &test_iid, (void **)&unk2);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(unk2 == (IUnknown *)0xdeadbeef, "Got unexpected IUnknown %p.\n", unk2);
IFilterGraph2_Release(graph);
ref = IUnknown_Release(unk);
ok(!ref, "Got unexpected refcount %d.\n", ref);
ok(outer_ref == 1, "Got unexpected refcount %d.\n", outer_ref);
/* Test the aggregated filter mapper. */
graph = create_graph();
ref = get_refcount(graph);
ok(ref == 1, "Got unexpected refcount %d.\n", ref);
hr = IFilterGraph2_QueryInterface(graph, &IID_IFilterMapper2, (void **)&mapper);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ref = get_refcount(graph);
ok(ref == 2, "Got unexpected refcount %d.\n", ref);
ref = get_refcount(mapper);
ok(ref == 2, "Got unexpected refcount %d.\n", ref);
hr = IFilterMapper2_QueryInterface(mapper, &IID_IFilterGraph2, (void **)&graph2);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(graph2 == graph, "Got unexpected IFilterGraph2 %p.\n", graph2);
IFilterGraph2_Release(graph2);
IFilterMapper2_Release(mapper);
ref = IFilterGraph2_Release(graph);
ok(!ref, "Got unexpected refcount %d.\n", ref);
}
/* Test how methods from "control" interfaces (IBasicAudio, IBasicVideo,
* IVideoWindow) are delegated to filters exposing those interfaces. */
static void test_control_delegation(void)
{
IFilterGraph2 *graph = create_graph();
IBasicAudio *audio, *filter_audio;
IBaseFilter *renderer;
IVideoWindow *window;
IBasicVideo2 *video;
ITypeInfo *typeinfo;
TYPEATTR *typeattr;
ULONG count;
HRESULT hr;
LONG val;
/* IBasicAudio */
hr = IFilterGraph2_QueryInterface(graph, &IID_IBasicAudio, (void **)&audio);
ok(hr == S_OK, "got %#x\n", hr);
hr = IBasicAudio_GetTypeInfoCount(audio, &count);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(count == 1, "Got count %u.\n", count);
hr = IBasicAudio_put_Volume(audio, -10);
ok(hr == E_NOTIMPL, "got %#x\n", hr);
hr = IBasicAudio_get_Volume(audio, &val);
ok(hr == E_NOTIMPL, "got %#x\n", hr);
hr = IBasicAudio_put_Balance(audio, 10);
ok(hr == E_NOTIMPL, "got %#x\n", hr);
hr = IBasicAudio_get_Balance(audio, &val);
ok(hr == E_NOTIMPL, "got %#x\n", hr);
hr = CoCreateInstance(&CLSID_DSoundRender, NULL, CLSCTX_INPROC_SERVER, &IID_IBaseFilter, (void **)&renderer);
if (hr != VFW_E_NO_AUDIO_HARDWARE)
{
ok(hr == S_OK, "got %#x\n", hr);
hr = IFilterGraph2_AddFilter(graph, renderer, NULL);
ok(hr == S_OK, "got %#x\n", hr);
hr = IBasicAudio_put_Volume(audio, -10);
ok(hr == S_OK, "got %#x\n", hr);
hr = IBasicAudio_get_Volume(audio, &val);
ok(hr == S_OK, "got %#x\n", hr);
ok(val == -10, "got %d\n", val);
hr = IBasicAudio_put_Balance(audio, 10);
ok(hr == S_OK || hr == VFW_E_MONO_AUDIO_HW, "got %#x\n", hr);
hr = IBasicAudio_get_Balance(audio, &val);
ok(hr == S_OK || hr == VFW_E_MONO_AUDIO_HW, "got %#x\n", hr);
if (hr == S_OK)
ok(val == 10, "got balance %d\n", val);
hr = IBaseFilter_QueryInterface(renderer, &IID_IBasicAudio, (void **)&filter_audio);
ok(hr == S_OK, "got %#x\n", hr);
hr = IBasicAudio_get_Volume(filter_audio, &val);
ok(hr == S_OK, "got %#x\n", hr);
ok(val == -10, "got volume %d\n", val);
hr = IFilterGraph2_RemoveFilter(graph, renderer);
ok(hr == S_OK, "got %#x\n", hr);
IBaseFilter_Release(renderer);
IBasicAudio_Release(filter_audio);
}
hr = IBasicAudio_put_Volume(audio, -10);
ok(hr == E_NOTIMPL, "got %#x\n", hr);
hr = IBasicAudio_get_Volume(audio, &val);
ok(hr == E_NOTIMPL, "got %#x\n", hr);
hr = IBasicAudio_put_Balance(audio, 10);
ok(hr == E_NOTIMPL, "got %#x\n", hr);
hr = IBasicAudio_get_Balance(audio, &val);
ok(hr == E_NOTIMPL, "got %#x\n", hr);
IBasicAudio_Release(audio);
/* IBasicVideo and IVideoWindow */
hr = IFilterGraph2_QueryInterface(graph, &IID_IBasicVideo2, (void **)&video);
ok(hr == S_OK, "got %#x\n", hr);
hr = IFilterGraph2_QueryInterface(graph, &IID_IVideoWindow, (void **)&window);
ok(hr == S_OK, "got %#x\n", hr);
/* Unlike IBasicAudio, these return E_NOINTERFACE. */
hr = IBasicVideo2_get_BitRate(video, &val);
ok(hr == E_NOINTERFACE, "got %#x\n", hr);
hr = IBasicVideo2_GetTypeInfoCount(video, &count);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(count == 1, "Got count %u.\n", count);
hr = IBasicVideo2_GetTypeInfo(video, 0, 0, &typeinfo);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = ITypeInfo_GetTypeAttr(typeinfo, &typeattr);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(typeattr->typekind == TKIND_DISPATCH, "Got kind %u.\n", typeattr->typekind);
ok(IsEqualGUID(&typeattr->guid, &IID_IBasicVideo), "Got IID %s.\n", wine_dbgstr_guid(&typeattr->guid));
ITypeInfo_ReleaseTypeAttr(typeinfo, typeattr);
ITypeInfo_Release(typeinfo);
hr = IVideoWindow_SetWindowForeground(window, OAFALSE);
ok(hr == E_NOINTERFACE, "got %#x\n", hr);
hr = IVideoWindow_GetTypeInfoCount(window, &count);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(count == 1, "Got count %u.\n", count);
hr = CoCreateInstance(&CLSID_VideoRenderer, NULL, CLSCTX_INPROC_SERVER, &IID_IBaseFilter, (void **)&renderer);
ok(hr == S_OK, "got %#x\n", hr);
hr = IFilterGraph2_AddFilter(graph, renderer, NULL);
ok(hr == S_OK, "got %#x\n", hr);
hr = IBasicVideo2_get_BitRate(video, &val);
ok(hr == VFW_E_NOT_CONNECTED, "got %#x\n", hr);
hr = IVideoWindow_SetWindowForeground(window, OAFALSE);
ok(hr == VFW_E_NOT_CONNECTED, "got %#x\n", hr);
hr = IFilterGraph2_RemoveFilter(graph, renderer);
ok(hr == S_OK, "got %#x\n", hr);
hr = IBasicVideo2_get_BitRate(video, &val);
ok(hr == E_NOINTERFACE, "got %#x\n", hr);
hr = IVideoWindow_SetWindowForeground(window, OAFALSE);
ok(hr == E_NOINTERFACE, "got %#x\n", hr);
IBaseFilter_Release(renderer);
IBasicVideo2_Release(video);
IVideoWindow_Release(window);
IFilterGraph2_Release(graph);
}
static void test_add_remove_filter(void)
{
struct testfilter filter;
IFilterGraph2 *graph = create_graph();
IBaseFilter *ret_filter;
HRESULT hr;
testfilter_init(&filter, NULL, 0);
hr = IFilterGraph2_FindFilterByName(graph, L"testid", &ret_filter);
ok(hr == VFW_E_NOT_FOUND, "Got hr %#x.\n", hr);
ok(!ret_filter, "Got filter %p.\n", ret_filter);
hr = IFilterGraph2_AddFilter(graph, &filter.IBaseFilter_iface, L"testid");
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(filter.graph == (IFilterGraph *)graph, "Got graph %p.\n", filter.graph);
ok(!wcscmp(filter.name, L"testid"), "Got name %s.\n", wine_dbgstr_w(filter.name));
hr = IFilterGraph2_FindFilterByName(graph, L"testid", &ret_filter);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(ret_filter == &filter.IBaseFilter_iface, "Got filter %p.\n", ret_filter);
IBaseFilter_Release(ret_filter);
hr = IFilterGraph2_RemoveFilter(graph, &filter.IBaseFilter_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(!filter.graph, "Got graph %p.\n", filter.graph);
ok(!filter.name, "Got name %s.\n", wine_dbgstr_w(filter.name));
ok(!filter.clock, "Got clock %p,\n", filter.clock);
ok(filter.ref == 1, "Got outstanding refcount %d.\n", filter.ref);
hr = IFilterGraph2_FindFilterByName(graph, L"testid", &ret_filter);
ok(hr == VFW_E_NOT_FOUND, "Got hr %#x.\n", hr);
ok(!ret_filter, "Got filter %p.\n", ret_filter);
hr = IFilterGraph2_AddFilter(graph, &filter.IBaseFilter_iface, NULL);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(filter.graph == (IFilterGraph *)graph, "Got graph %p.\n", filter.graph);
ok(!wcscmp(filter.name, L"0001"), "Got name %s.\n", wine_dbgstr_w(filter.name));
hr = IFilterGraph2_FindFilterByName(graph, L"0001", &ret_filter);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(ret_filter == &filter.IBaseFilter_iface, "Got filter %p.\n", ret_filter);
IBaseFilter_Release(ret_filter);
/* test releasing the filter graph while filters are still connected */
hr = IFilterGraph2_Release(graph);
ok(!hr, "Got outstanding refcount %d.\n", hr);
ok(!filter.graph, "Got graph %p.\n", filter.graph);
ok(!filter.name, "Got name %s.\n", wine_dbgstr_w(filter.name));
ok(!filter.clock, "Got clock %p.\n", filter.clock);
ok(filter.ref == 1, "Got outstanding refcount %d.\n", filter.ref);
}
static HRESULT WINAPI test_connect_direct_Connect(IPin *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
{
struct testpin *pin = impl_from_IPin(iface);
if (winetest_debug > 1) trace("%p->Connect()\n", pin);
pin->peer = peer;
IPin_AddRef(peer);
pin->mt = (AM_MEDIA_TYPE *)mt;
return S_OK;
}
static const IPinVtbl test_connect_direct_vtbl =
{
testpin_QueryInterface,
testpin_AddRef,
testpin_Release,
test_connect_direct_Connect,
no_ReceiveConnection,
testpin_Disconnect,
testpin_ConnectedTo,
testpin_ConnectionMediaType,
testpin_QueryPinInfo,
testpin_QueryDirection,
testpin_QueryId,
testpin_QueryAccept,
no_EnumMediaTypes,
testpin_QueryInternalConnections,
testpin_EndOfStream,
testpin_BeginFlush,
testpin_EndFlush,
testpin_NewSegment
};
static void test_connect_direct_init(struct testpin *pin, PIN_DIRECTION dir)
{
testpin_init(pin, &test_connect_direct_vtbl, dir);
}
static void test_connect_direct(void)
{
struct testpin source_pin, sink_pin, parser1_pins[2], parser2_pins[2];
struct testfilter source, sink, parser1, parser2;
IFilterGraph2 *graph = create_graph();
AM_MEDIA_TYPE mt;
HRESULT hr;
test_connect_direct_init(&source_pin, PINDIR_OUTPUT);
testfilter_init(&source, &source_pin, 1);
test_connect_direct_init(&sink_pin, PINDIR_INPUT);
testfilter_init(&sink, &sink_pin, 1);
test_connect_direct_init(&parser1_pins[0], PINDIR_INPUT);
test_connect_direct_init(&parser1_pins[1], PINDIR_OUTPUT);
testfilter_init(&parser1, parser1_pins, 2);
test_connect_direct_init(&parser2_pins[0], PINDIR_INPUT);
test_connect_direct_init(&parser2_pins[1], PINDIR_OUTPUT);
testfilter_init(&parser2, parser2_pins, 2);
hr = IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
hr = IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
ok(hr == S_FALSE, "Got hr %#x.\n", hr);
ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
hr = IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
ok(hr == S_FALSE, "Got hr %#x.\n", hr);
ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
/* Swap the pins when connecting. */
hr = IFilterGraph2_ConnectDirect(graph, &sink_pin.IPin_iface, &source_pin.IPin_iface, NULL);
ok(hr == S_OK, "Got hr %#x.\n", hr);
todo_wine
ok(sink_pin.peer == &source_pin.IPin_iface, "Got peer %p.\n", sink_pin.peer);
ok(!sink_pin.mt, "Got mt %p.\n", sink_pin.mt);
todo_wine
ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
hr = IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
todo_wine
ok(hr == S_OK, "Got hr %#x.\n", hr);
todo_wine
ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
hr = IFilterGraph2_Connect(graph, &sink_pin.IPin_iface, &source_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
todo_wine
ok(sink_pin.peer == &source_pin.IPin_iface, "Got peer %p.\n", sink_pin.peer);
ok(!sink_pin.mt, "Got mt %p.\n", sink_pin.mt);
todo_wine
ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
hr = IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
todo_wine
ok(hr == S_OK, "Got hr %#x.\n", hr);
todo_wine
ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
/* Disconnect() does not disconnect the peer. */
hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
sink_pin.peer = &source_pin.IPin_iface;
IPin_AddRef(sink_pin.peer);
hr = IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
/* Test specifying the media type. */
hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, &mt);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(source_pin.mt == &mt, "Got mt %p.\n", source_pin.mt);
ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
/* Test Reconnect[Ex](). */
hr = IFilterGraph2_Reconnect(graph, &source_pin.IPin_iface);
ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#x.\n", hr);
hr = IFilterGraph2_Reconnect(graph, &sink_pin.IPin_iface);
ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#x.\n", hr);
hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, &mt);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IFilterGraph2_Reconnect(graph, &source_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, &mt);
ok(hr == S_OK, "Got hr %#x.\n", hr);
sink_pin.peer = &source_pin.IPin_iface;
IPin_AddRef(sink_pin.peer);
hr = IFilterGraph2_Reconnect(graph, &sink_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IFilterGraph2_ReconnectEx(graph, &source_pin.IPin_iface, NULL);
ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#x.\n", hr);
hr = IFilterGraph2_ReconnectEx(graph, &sink_pin.IPin_iface, NULL);
ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#x.\n", hr);
hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, &mt);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IFilterGraph2_ReconnectEx(graph, &source_pin.IPin_iface, NULL);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, &mt);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IFilterGraph2_ReconnectEx(graph, &source_pin.IPin_iface, &mt);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(source_pin.mt == &mt, "Got mt %p.\n", source_pin.mt);
ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
/* ConnectDirect() protects against cyclical connections. */
hr = IFilterGraph2_AddFilter(graph, &parser1.IBaseFilter_iface, NULL);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IFilterGraph2_AddFilter(graph, &parser2.IBaseFilter_iface, NULL);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IFilterGraph2_ConnectDirect(graph, &parser1_pins[1].IPin_iface, &parser1_pins[0].IPin_iface, NULL);
ok(hr == VFW_E_CIRCULAR_GRAPH, "Got hr %#x.\n", hr);
hr = IFilterGraph2_ConnectDirect(graph, &parser1_pins[1].IPin_iface, &parser2_pins[0].IPin_iface, NULL);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IFilterGraph2_ConnectDirect(graph, &parser2_pins[1].IPin_iface, &parser1_pins[0].IPin_iface, NULL);
todo_wine ok(hr == VFW_E_CIRCULAR_GRAPH, "Got hr %#x.\n", hr);
IFilterGraph2_Disconnect(graph, &parser1_pins[1].IPin_iface);
IFilterGraph2_Disconnect(graph, &parser2_pins[0].IPin_iface);
parser1_pins[0].QueryInternalConnections_hr = S_OK;
hr = IFilterGraph2_ConnectDirect(graph, &parser1_pins[1].IPin_iface, &parser1_pins[0].IPin_iface, NULL);
todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(!parser1_pins[0].peer, "Got peer %p.\n", parser1_pins[0].peer);
todo_wine ok(parser1_pins[1].peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", parser1_pins[1].peer);
IFilterGraph2_Disconnect(graph, &parser1_pins[0].IPin_iface);
IFilterGraph2_Disconnect(graph, &parser1_pins[1].IPin_iface);
hr = IFilterGraph2_ConnectDirect(graph, &parser1_pins[1].IPin_iface, &parser2_pins[0].IPin_iface, NULL);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IFilterGraph2_ConnectDirect(graph, &parser2_pins[1].IPin_iface, &parser1_pins[0].IPin_iface, NULL);
ok(hr == S_OK, "Got hr %#x.\n", hr);
IFilterGraph2_Disconnect(graph, &parser1_pins[1].IPin_iface);
IFilterGraph2_Disconnect(graph, &parser2_pins[0].IPin_iface);
hr = IFilterGraph2_RemoveFilter(graph, &parser1.IBaseFilter_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IFilterGraph2_RemoveFilter(graph, &parser2.IBaseFilter_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
/* Both pins are disconnected when a filter is removed. */
hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, &mt);
ok(hr == S_OK, "Got hr %#x.\n", hr);
sink_pin.peer = &source_pin.IPin_iface;
IPin_AddRef(sink_pin.peer);
hr = IFilterGraph2_RemoveFilter(graph, &source.IBaseFilter_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
/* Or when the graph is destroyed. */
hr = IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
IPin_AddRef(sink_pin.peer = &source_pin.IPin_iface);
hr = IFilterGraph2_Release(graph);
ok(!hr, "Got outstanding refcount %d.\n", hr);
ok(source.ref == 1, "Got outstanding refcount %d.\n", source.ref);
ok(sink.ref == 1, "Got outstanding refcount %d.\n", sink.ref);
ok(source_pin.ref == 1, "Got outstanding refcount %d.\n", source_pin.ref);
todo_wine
ok(sink_pin.ref == 1, "Got outstanding refcount %d.\n", sink_pin.ref);
ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
}
static void test_sync_source(void)
{
struct testfilter filter1, filter2;
IFilterGraph2 *graph = create_graph();
IReferenceClock *systemclock, *clock;
IMediaFilter *filter;
HRESULT hr;
ULONG ref;
IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
testfilter_init(&filter1, NULL, 0);
testfilter_init(&filter2, NULL, 0);
IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL);
ok(!filter1.clock, "Got clock %p.\n", filter1.clock);
ok(!filter2.clock, "Got clock %p.\n", filter2.clock);
CoCreateInstance(&CLSID_SystemClock, NULL, CLSCTX_INPROC_SERVER,
&IID_IReferenceClock, (void **)&systemclock);
hr = IMediaFilter_SetSyncSource(filter, systemclock);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(filter1.clock == systemclock, "Got clock %p.\n", filter1.clock);
ok(filter2.clock == systemclock, "Got clock %p.\n", filter2.clock);
hr = IMediaFilter_GetSyncSource(filter, &clock);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(clock == systemclock, "Got clock %p.\n", clock);
IReferenceClock_Release(clock);
hr = IMediaFilter_SetSyncSource(filter, NULL);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(!filter1.clock, "Got clock %p.\n", filter1.clock);
ok(!filter2.clock, "Got clock %p.\n", filter2.clock);
hr = IMediaFilter_GetSyncSource(filter, &clock);
todo_wine
ok(hr == S_FALSE, "Got hr %#x.\n", hr);
ok(!clock, "Got clock %p.\n", clock);
IReferenceClock_Release(systemclock);
IMediaFilter_Release(filter);
ref = IFilterGraph2_Release(graph);
ok(!ref, "Got outstanding refcount %d\n", ref);
ok(filter1.ref == 1, "Got outstanding refcount %d.\n", filter1.ref);
ok(filter2.ref == 1, "Got outstanding refcount %d.\n", filter2.ref);
}
#define check_filter_state(a, b) check_filter_state_(__LINE__, a, b)
static void check_filter_state_(unsigned int line, IFilterGraph2 *graph, FILTER_STATE expect)
{
IMediaFilter *mediafilter;
IEnumFilters *filterenum;
IMediaControl *control;
OAFilterState oastate;
IBaseFilter *filter;
FILTER_STATE state;
HRESULT hr;
IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&mediafilter);
hr = IMediaFilter_GetState(mediafilter, 1000, &state);
ok_(__FILE__, line)(hr == S_OK, "IMediaFilter_GetState() returned %#x.\n", hr);
ok_(__FILE__, line)(state == expect, "Expected state %u, got %u.\n", expect, state);
IMediaFilter_Release(mediafilter);
IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
hr = IMediaControl_GetState(control, 1000, &oastate);
ok_(__FILE__, line)(hr == S_OK, "IMediaControl_GetState() returned %#x.\n", hr);
ok_(__FILE__, line)(state == expect, "Expected state %u, got %u.\n", expect, state);
IMediaControl_Release(control);
IFilterGraph2_EnumFilters(graph, &filterenum);
while (IEnumFilters_Next(filterenum, 1, &filter, NULL) == S_OK)
{
hr = IBaseFilter_GetState(filter, 1000, &state);
ok_(__FILE__, line)(hr == S_OK, "IBaseFilter_GetState() returned %#x.\n", hr);
ok_(__FILE__, line)(state == expect, "Expected state %u, got %u.\n", expect, state);
IBaseFilter_Release(filter);
}
IEnumFilters_Release(filterenum);
}
static void test_filter_state(void)
{
struct testfilter source, sink, dummy;
struct testpin source_pin, sink_pin;
IFilterGraph2 *graph = create_graph();
REFERENCE_TIME start_time;
IReferenceClock *clock;
IMediaControl *control;
IMediaFilter *filter;
OAFilterState state;
HRESULT hr;
ULONG ref;
testsource_init(&source_pin, NULL, 0);
testsink_init(&sink_pin);
testfilter_init(&source, &source_pin, 1);
testfilter_init(&sink, &sink_pin, 1);
testfilter_init(&dummy, NULL, 0);
IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
source_pin.filter = &source.IBaseFilter_iface;
sink_pin.filter = &sink.IBaseFilter_iface;
IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &dummy.IBaseFilter_iface, NULL);
IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
check_filter_state(graph, State_Stopped);
hr = IMediaControl_Pause(control);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Paused);
/* Pausing sets the default sync source, if it's not already set. */
hr = IMediaFilter_GetSyncSource(filter, &clock);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(!!clock, "Reference clock not set.\n");
ok(source.clock == clock, "Expected %p, got %p.\n", clock, source.clock);
ok(sink.clock == clock, "Expected %p, got %p.\n", clock, sink.clock);
hr = IReferenceClock_GetTime(clock, &start_time);
ok(SUCCEEDED(hr), "Got hr %#x.\n", hr);
hr = IMediaControl_Run(control);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Running);
if (winetest_interactive) /* Timing problems make this test too liable to fail. */
ok(source.start_time >= start_time && source.start_time < start_time + 500 * 10000,
"Expected time near %s, got %s.\n",
wine_dbgstr_longlong(start_time), wine_dbgstr_longlong(source.start_time));
ok(sink.start_time == source.start_time, "Expected time %s, got %s.\n",
wine_dbgstr_longlong(source.start_time), wine_dbgstr_longlong(sink.start_time));
hr = IMediaControl_Pause(control);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Paused);
hr = IMediaControl_Stop(control);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Stopped);
hr = IMediaControl_Run(control);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Running);
hr = IMediaControl_Stop(control);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Stopped);
hr = IMediaControl_Pause(control);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Paused);
hr = IMediaControl_StopWhenReady(control);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Stopped);
hr = IMediaControl_Run(control);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Running);
hr = IMediaControl_StopWhenReady(control);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Stopped);
hr = IMediaControl_StopWhenReady(control);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Stopped);
IReferenceClock_Release(clock);
IMediaFilter_Release(filter);
IMediaControl_Release(control);
IFilterGraph2_Release(graph);
/* Test same methods using IMediaFilter. */
graph = create_graph();
IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
hr = IMediaFilter_Pause(filter);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Paused);
hr = IMediaFilter_GetSyncSource(filter, &clock);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(!!clock, "Reference clock not set.\n");
ok(source.clock == clock, "Expected %p, got %p.\n", clock, source.clock);
ok(sink.clock == clock, "Expected %p, got %p.\n", clock, sink.clock);
hr = IMediaFilter_Run(filter, 0xdeadbeef);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Running);
ok(source.start_time == 0xdeadbeef, "Got time %s.\n", wine_dbgstr_longlong(source.start_time));
ok(sink.start_time == 0xdeadbeef, "Got time %s.\n", wine_dbgstr_longlong(sink.start_time));
hr = IMediaFilter_Pause(filter);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Paused);
hr = IMediaFilter_Run(filter, 0xdeadf00d);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Running);
ok(source.start_time == 0xdeadf00d, "Got time %s.\n", wine_dbgstr_longlong(source.start_time));
ok(sink.start_time == 0xdeadf00d, "Got time %s.\n", wine_dbgstr_longlong(sink.start_time));
hr = IMediaFilter_Pause(filter);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Paused);
hr = IMediaFilter_Stop(filter);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Stopped);
hr = IReferenceClock_GetTime(clock, &start_time);
ok(SUCCEEDED(hr), "Got hr %#x.\n", hr);
hr = IMediaFilter_Run(filter, 0);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Running);
if (winetest_interactive) /* Timing problems make this test too liable to fail. */
ok(source.start_time >= start_time && source.start_time < start_time + 500 * 10000,
"Expected time near %s, got %s.\n",
wine_dbgstr_longlong(start_time), wine_dbgstr_longlong(source.start_time));
ok(sink.start_time == source.start_time, "Expected time %s, got %s.\n",
wine_dbgstr_longlong(source.start_time), wine_dbgstr_longlong(sink.start_time));
Sleep(600);
hr = IMediaFilter_Pause(filter);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Paused);
hr = IMediaFilter_Run(filter, 0);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Running);
if (winetest_interactive) /* Timing problems make this test too liable to fail. */
ok(source.start_time >= start_time && source.start_time < start_time + 500 * 10000,
"Expected time near %s, got %s.\n",
wine_dbgstr_longlong(start_time), wine_dbgstr_longlong(source.start_time));
ok(sink.start_time == source.start_time, "Expected time %s, got %s.\n",
wine_dbgstr_longlong(source.start_time), wine_dbgstr_longlong(sink.start_time));
hr = IMediaFilter_Pause(filter);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Paused);
Sleep(600);
start_time += 550 * 10000;
hr = IMediaFilter_Run(filter, 0);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Running);
if (winetest_interactive) /* Timing problems make this test too liable to fail. */
ok(source.start_time >= start_time && source.start_time < start_time + 500 * 10000,
"Expected time near %s, got %s.\n",
wine_dbgstr_longlong(start_time), wine_dbgstr_longlong(source.start_time));
ok(sink.start_time == source.start_time, "Expected time %s, got %s.\n",
wine_dbgstr_longlong(source.start_time), wine_dbgstr_longlong(sink.start_time));
hr = IMediaFilter_Stop(filter);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Stopped);
/* Test removing the sync source. */
IReferenceClock_Release(clock);
IMediaFilter_SetSyncSource(filter, NULL);
hr = IMediaControl_Run(control);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Running);
todo_wine
ok(source.start_time > 0 && source.start_time < 500 * 10000,
"Got time %s.\n", wine_dbgstr_longlong(source.start_time));
ok(sink.start_time == source.start_time, "Expected time %s, got %s.\n",
wine_dbgstr_longlong(source.start_time), wine_dbgstr_longlong(sink.start_time));
hr = IMediaControl_Stop(control);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Stopped);
sink.state_hr = S_FALSE;
hr = IMediaControl_Pause(control);
ok(hr == S_FALSE, "Got hr %#x.\n", hr);
sink.state_hr = VFW_S_STATE_INTERMEDIATE;
hr = IMediaControl_GetState(control, 0, &state);
ok(hr == VFW_S_STATE_INTERMEDIATE, "Got hr %#x.\n", hr);
ok(state == State_Paused, "Got state %u.\n", state);
sink.state_hr = VFW_S_CANT_CUE;
hr = IMediaControl_GetState(control, 0, &state);
ok(hr == VFW_S_CANT_CUE, "Got hr %#x.\n", hr);
ok(state == State_Paused, "Got state %u.\n", state);
sink.state_hr = VFW_S_STATE_INTERMEDIATE;
source.state_hr = VFW_S_CANT_CUE;
hr = IMediaControl_GetState(control, 0, &state);
ok(hr == VFW_S_CANT_CUE, "Got hr %#x.\n", hr);
ok(state == State_Paused, "Got state %u.\n", state);
sink.state_hr = VFW_S_CANT_CUE;
source.state_hr = VFW_S_STATE_INTERMEDIATE;
hr = IMediaControl_GetState(control, 0, &state);
ok(hr == VFW_S_CANT_CUE, "Got hr %#x.\n", hr);
ok(state == State_Paused, "Got state %u.\n", state);
sink.state_hr = source.state_hr = S_OK;
/* Destroying the graph while it's running stops all filters. */
hr = IMediaFilter_Run(filter, 0);
ok(hr == S_OK, "Got hr %#x.\n", hr);
check_filter_state(graph, State_Running);
todo_wine
ok(source.start_time > 0 && source.start_time < 500 * 10000,
"Got time %s.\n", wine_dbgstr_longlong(source.start_time));
ok(sink.start_time == source.start_time, "Expected time %s, got %s.\n",
wine_dbgstr_longlong(source.start_time), wine_dbgstr_longlong(sink.start_time));
IMediaFilter_Release(filter);
IMediaControl_Release(control);
ref = IFilterGraph2_Release(graph);
ok(!ref, "Got outstanding refcount %d.\n", ref);
ok(source.ref == 1, "Got outstanding refcount %d.\n", source.ref);
ok(sink.ref == 1, "Got outstanding refcount %d.\n", sink.ref);
ok(source_pin.ref == 1, "Got outstanding refcount %d.\n", source_pin.ref);
ok(sink_pin.ref == 1, "Got outstanding refcount %d.\n", sink_pin.ref);
ok(source.state == State_Stopped, "Got state %u.\n", source.state);
ok(sink.state == State_Stopped, "Got state %u.\n", sink.state);
}
/* Helper function to check whether a filter is considered a renderer, i.e.
* whether its EC_COMPLETE notification will be passed on to the application. */
static HRESULT check_ec_complete(IFilterGraph2 *graph, IBaseFilter *filter)
{
IMediaEventSink *eventsink;
LONG_PTR param1, param2;
IMediaControl *control;
IMediaEvent *eventsrc;
HRESULT hr, ret_hr;
LONG code;
IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
IFilterGraph2_QueryInterface(graph, &IID_IMediaEvent, (void **)&eventsrc);
IFilterGraph2_QueryInterface(graph, &IID_IMediaEventSink, (void **)&eventsink);
IMediaControl_Run(control);
hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 0);
ok(hr == E_ABORT, "Got hr %#x.\n", hr);
hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)filter);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ret_hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 0);
if (ret_hr == S_OK)
{
ok(code == EC_COMPLETE, "Got code %#x.\n", code);
ok(param1 == S_OK, "Got param1 %#lx.\n", param1);
ok(!param2, "Got param2 %#lx.\n", param2);
hr = IMediaEvent_FreeEventParams(eventsrc, code, param1, param2);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 0);
ok(hr == E_ABORT, "Got hr %#x.\n", hr);
}
IMediaControl_Stop(control);
IMediaControl_Release(control);
IMediaEvent_Release(eventsrc);
IMediaEventSink_Release(eventsink);
return ret_hr;
}
static void test_ec_complete(void)
{
struct testpin filter1_pin, filter2_pin, filter3_pin, source_pins[3];
struct testfilter filter1, filter2, filter3, source;
IFilterGraph2 *graph = create_graph();
IMediaEventSink *eventsink;
LONG_PTR param1, param2;
IMediaControl *control;
IMediaEvent *eventsrc;
HRESULT hr;
LONG code;
testsink_init(&filter1_pin);
testsink_init(&filter2_pin);
testsink_init(&filter3_pin);
testfilter_init(&filter1, &filter1_pin, 1);
testfilter_init(&filter2, &filter2_pin, 1);
testfilter_init(&filter3, &filter3_pin, 1);
testsource_init(&source_pins[0], NULL, 0);
testsource_init(&source_pins[1], NULL, 0);
testsource_init(&source_pins[2], NULL, 0);
testfilter_init(&source, source_pins, 3);
filter1.IAMFilterMiscFlags_iface.lpVtbl = &testmiscflags_vtbl;
filter2.IAMFilterMiscFlags_iface.lpVtbl = &testmiscflags_vtbl;
filter1.misc_flags = filter2.misc_flags = AM_FILTER_MISC_FLAGS_IS_RENDERER;
IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
IFilterGraph2_QueryInterface(graph, &IID_IMediaEvent, (void **)&eventsrc);
IFilterGraph2_QueryInterface(graph, &IID_IMediaEventSink, (void **)&eventsink);
IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &filter3.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
IFilterGraph2_ConnectDirect(graph, &source_pins[0].IPin_iface, &filter1_pin.IPin_iface, NULL);
IFilterGraph2_ConnectDirect(graph, &source_pins[1].IPin_iface, &filter2_pin.IPin_iface, NULL);
IFilterGraph2_ConnectDirect(graph, &source_pins[2].IPin_iface, &filter3_pin.IPin_iface, NULL);
/* EC_COMPLETE is only delivered to the user after all renderers deliver it. */
IMediaControl_Run(control);
while ((hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 0)) == S_OK)
{
ok(code != EC_COMPLETE, "Got unexpected EC_COMPLETE.\n");
IMediaEvent_FreeEventParams(eventsrc, code, param1, param2);
}
ok(hr == E_ABORT, "Got hr %#x.\n", hr);
hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)&filter1.IBaseFilter_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 50);
ok(hr == E_ABORT, "Got hr %#x.\n", hr);
hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)&filter2.IBaseFilter_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 0);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(code == EC_COMPLETE, "Got code %#x.\n", code);
ok(param1 == S_OK, "Got param1 %#lx.\n", param1);
ok(!param2, "Got param2 %#lx.\n", param2);
hr = IMediaEvent_FreeEventParams(eventsrc, code, param1, param2);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 50);
ok(hr == E_ABORT, "Got hr %#x.\n", hr);
hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)&filter3.IBaseFilter_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 50);
ok(hr == E_ABORT, "Got hr %#x.\n", hr);
IMediaControl_Stop(control);
/* Test CancelDefaultHandling(). */
IMediaControl_Run(control);
hr = IMediaEvent_CancelDefaultHandling(eventsrc, EC_COMPLETE);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 50);
ok(hr == E_ABORT, "Got hr %#x.\n", hr);
hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)&filter1.IBaseFilter_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 0);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(code == EC_COMPLETE, "Got code %#x.\n", code);
ok(param1 == S_OK, "Got param1 %#lx.\n", param1);
ok(param2 == (LONG_PTR)&filter1.IBaseFilter_iface, "Got param2 %#lx.\n", param2);
hr = IMediaEvent_FreeEventParams(eventsrc, code, param1, param2);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 50);
ok(hr == E_ABORT, "Got hr %#x.\n", hr);
hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)&filter3.IBaseFilter_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 0);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(code == EC_COMPLETE, "Got code %#x.\n", code);
ok(param1 == S_OK, "Got param1 %#lx.\n", param1);
ok(param2 == (LONG_PTR)&filter3.IBaseFilter_iface, "Got param2 %#lx.\n", param2);
hr = IMediaEvent_FreeEventParams(eventsrc, code, param1, param2);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 50);
ok(hr == E_ABORT, "Got hr %#x.\n", hr);
IMediaControl_Stop(control);
hr = IMediaEvent_RestoreDefaultHandling(eventsrc, EC_COMPLETE);
ok(hr == S_OK, "Got hr %#x.\n", hr);
/* A filter counts as a renderer if it (1) exposes IAMFilterMiscFlags and
* reports itself as a renderer, or (2) exposes IMediaSeeking and has no
* output pins. Despite MSDN, QueryInternalConnections() does not seem to
* be used. */
IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface);
IFilterGraph2_RemoveFilter(graph, &filter2.IBaseFilter_iface);
IFilterGraph2_RemoveFilter(graph, &filter3.IBaseFilter_iface);
filter1.misc_flags = 0;
IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
IFilterGraph2_ConnectDirect(graph, &source_pins[0].IPin_iface, &filter1_pin.IPin_iface, NULL);
hr = check_ec_complete(graph, &filter1.IBaseFilter_iface);
ok(hr == E_ABORT, "Got hr %#x.\n", hr);
IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface);
filter1_pin.dir = PINDIR_INPUT;
filter1.IAMFilterMiscFlags_iface.lpVtbl = NULL;
filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
IFilterGraph2_ConnectDirect(graph, &source_pins[0].IPin_iface, &filter1_pin.IPin_iface, NULL);
hr = check_ec_complete(graph, &filter1.IBaseFilter_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(filter1.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface);
ok(filter1.seeking_ref == 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
filter1_pin.dir = PINDIR_OUTPUT;
IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
hr = check_ec_complete(graph, &filter1.IBaseFilter_iface);
ok(hr == E_ABORT, "Got hr %#x.\n", hr);
ok(filter1.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface);
ok(filter1.seeking_ref == 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
filter1.IMediaSeeking_iface.lpVtbl = NULL;
filter1_pin.dir = PINDIR_INPUT;
filter1.pin_count = 1;
filter1_pin.QueryInternalConnections_hr = S_OK;
IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
IFilterGraph2_ConnectDirect(graph, &source_pins[0].IPin_iface, &filter1_pin.IPin_iface, NULL);
hr = check_ec_complete(graph, &filter1.IBaseFilter_iface);
ok(hr == E_ABORT, "Got hr %#x.\n", hr);
IMediaControl_Release(control);
IMediaEvent_Release(eventsrc);
IMediaEventSink_Release(eventsink);
hr = IFilterGraph2_Release(graph);
ok(!hr, "Got outstanding refcount %d.\n", hr);
ok(filter1.ref == 1, "Got outstanding refcount %d.\n", filter1.ref);
ok(filter2.ref == 1, "Got outstanding refcount %d.\n", filter2.ref);
ok(filter3.ref == 1, "Got outstanding refcount %d.\n", filter3.ref);
}
static void test_graph_seeking(void)
{
struct testfilter filter1, filter2;
LONGLONG time, current, stop, earliest, latest;
IFilterGraph2 *graph = create_graph();
IMediaControl *control;
IMediaSeeking *seeking;
IMediaFilter *filter;
unsigned int i;
double rate;
GUID format;
HRESULT hr;
DWORD caps;
ULONG ref;
static const GUID *const all_formats[] =
{
NULL,
&TIME_FORMAT_NONE,
&TIME_FORMAT_FRAME,
&TIME_FORMAT_SAMPLE,
&TIME_FORMAT_FIELD,
&TIME_FORMAT_BYTE,
&TIME_FORMAT_MEDIA_TIME,
&testguid,
};
static const GUID *const unsupported_formats[] =
{
&TIME_FORMAT_FRAME,
&TIME_FORMAT_SAMPLE,
&TIME_FORMAT_FIELD,
&TIME_FORMAT_BYTE,
&testguid,
};
testfilter_init(&filter1, NULL, 0);
testfilter_init(&filter2, NULL, 0);
filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
IFilterGraph2_QueryInterface(graph, &IID_IMediaSeeking, (void **)&seeking);
IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
hr = IMediaSeeking_GetCapabilities(seeking, &caps);
todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
todo_wine ok(!caps, "Got caps %#x.\n", caps);
caps = 0;
hr = IMediaSeeking_CheckCapabilities(seeking, &caps);
todo_wine ok(hr == E_FAIL, "Got hr %#x.\n", hr);
ok(!caps, "Got caps %#x.\n", caps);
caps = AM_SEEKING_CanSeekAbsolute;
hr = IMediaSeeking_CheckCapabilities(seeking, &caps);
todo_wine ok(hr == E_FAIL, "Got hr %#x.\n", hr);
todo_wine ok(!caps, "Got caps %#x.\n", caps);
hr = IMediaSeeking_IsFormatSupported(seeking, NULL);
todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
for (i = 0; i < ARRAY_SIZE(all_formats); ++i)
{
hr = IMediaSeeking_IsFormatSupported(seeking, all_formats[i]);
todo_wine ok(hr == E_NOTIMPL, "Got hr %#x for format %s.\n", hr, wine_dbgstr_guid(all_formats[i]));
}
hr = IMediaSeeking_QueryPreferredFormat(seeking, NULL);
ok(hr == E_POINTER, "Got hr %#x.\n", hr);
hr = IMediaSeeking_QueryPreferredFormat(seeking, &format);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
hr = IMediaSeeking_GetTimeFormat(seeking, NULL);
ok(hr == E_POINTER, "Got hr %#x.\n", hr);
hr = IMediaSeeking_GetTimeFormat(seeking, &format);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
hr = IMediaSeeking_IsUsingTimeFormat(seeking, NULL);
ok(hr == E_POINTER, "Got hr %#x.\n", hr);
hr = IMediaSeeking_IsUsingTimeFormat(seeking, &TIME_FORMAT_MEDIA_TIME);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaSeeking_IsUsingTimeFormat(seeking, &TIME_FORMAT_NONE);
ok(hr == S_FALSE, "Got hr %#x.\n", hr);
hr = IMediaSeeking_SetTimeFormat(seeking, &TIME_FORMAT_NONE);
todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaSeeking_QueryPreferredFormat(seeking, &format);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
hr = IMediaSeeking_IsUsingTimeFormat(seeking, &TIME_FORMAT_MEDIA_TIME);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaSeeking_IsUsingTimeFormat(seeking, &TIME_FORMAT_NONE);
ok(hr == S_FALSE, "Got hr %#x.\n", hr);
hr = IMediaSeeking_GetTimeFormat(seeking, &format);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
hr = IMediaSeeking_SetTimeFormat(seeking, &TIME_FORMAT_MEDIA_TIME);
ok(hr == S_OK, "Got hr %#x.\n", hr);
for (i = 0; i < ARRAY_SIZE(unsupported_formats); ++i)
{
hr = IMediaSeeking_SetTimeFormat(seeking, unsupported_formats[i]);
todo_wine ok(hr == E_NOTIMPL, "Got hr %#x for format %s.\n", hr, wine_dbgstr_guid(unsupported_formats[i]));
}
time = 0xdeadbeef;
hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, NULL, 0x123456789a, NULL);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
time = 0xdeadbeef;
hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_MEDIA_TIME, 0x123456789a, NULL);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
time = 0xdeadbeef;
hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, NULL, 0x123456789a, &TIME_FORMAT_MEDIA_TIME);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
time = 0xdeadbeef;
hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_MEDIA_TIME, 0x123456789a, &TIME_FORMAT_MEDIA_TIME);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_NONE, 0x123456789a, &TIME_FORMAT_MEDIA_TIME);
todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_NONE, 0x123456789a, NULL);
todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_MEDIA_TIME, 0x123456789a, &TIME_FORMAT_NONE);
todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, NULL, 0x123456789a, &TIME_FORMAT_NONE);
todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
time = 0xdeadbeef;
hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_NONE, 0x123456789a, &TIME_FORMAT_NONE);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
time = 0xdeadbeef;
hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &testguid, 0x123456789a, &testguid);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
hr = IMediaSeeking_GetDuration(seeking, NULL);
ok(hr == E_POINTER, "Got hr %#x.\n", hr);
hr = IMediaSeeking_GetDuration(seeking, &time);
ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
hr = IMediaSeeking_GetStopPosition(seeking, NULL);
ok(hr == E_POINTER, "Got hr %#x.\n", hr);
hr = IMediaSeeking_GetStopPosition(seeking, &time);
ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
hr = IMediaSeeking_SetPositions(seeking, &current, 0, &stop, 0);
ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
hr = IMediaSeeking_GetPositions(seeking, NULL, NULL);
todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaSeeking_GetPositions(seeking, NULL, &stop);
todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
current = 0xdeadbeef;
hr = IMediaSeeking_GetPositions(seeking, &current, NULL);
todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(!current, "Got time %s.\n", wine_dbgstr_longlong(time));
hr = IMediaSeeking_GetCurrentPosition(seeking, NULL);
ok(hr == E_POINTER, "Got hr %#x.\n", hr);
current = 0xdeadbeef;
hr = IMediaSeeking_GetCurrentPosition(seeking, &current);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(!current, "Got time %s.\n", wine_dbgstr_longlong(time));
hr = IMediaSeeking_GetAvailable(seeking, &earliest, &latest);
todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
hr = IMediaSeeking_SetRate(seeking, 1.0);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaSeeking_SetRate(seeking, 2.0);
todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
hr = IMediaSeeking_GetRate(seeking, &rate);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(rate == 1.0, "Got rate %.16e.\n", rate);
hr = IMediaSeeking_GetPreroll(seeking, &time);
todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
/* Try with filters added. Note that a filter need only expose
* IMediaSeeking—no other heuristics are used to determine if it is a
* renderer. */
IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL);
filter1.seek_caps = AM_SEEKING_CanDoSegments | AM_SEEKING_CanGetCurrentPos;
filter2.seek_caps = AM_SEEKING_CanDoSegments | AM_SEEKING_CanGetDuration;
hr = IMediaSeeking_GetCapabilities(seeking, &caps);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(caps == AM_SEEKING_CanDoSegments, "Got caps %#x.\n", caps);
ok(filter1.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
ok(filter2.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter2.seeking_ref);
caps = AM_SEEKING_CanDoSegments | AM_SEEKING_CanGetCurrentPos;
hr = IMediaSeeking_CheckCapabilities(seeking, &caps);
ok(hr == S_FALSE, "Got hr %#x.\n", hr);
ok(caps == AM_SEEKING_CanDoSegments, "Got caps %#x.\n", caps);
caps = AM_SEEKING_CanDoSegments;
hr = IMediaSeeking_CheckCapabilities(seeking, &caps);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(caps == AM_SEEKING_CanDoSegments, "Got caps %#x.\n", caps);
caps = AM_SEEKING_CanGetCurrentPos;
hr = IMediaSeeking_CheckCapabilities(seeking, &caps);
ok(hr == E_FAIL, "Got hr %#x.\n", hr);
ok(!caps, "Got caps %#x.\n", caps);
hr = IMediaSeeking_IsFormatSupported(seeking, &testguid);
ok(hr == S_FALSE, "Got hr %#x.\n", hr);
filter1.support_testguid = TRUE;
hr = IMediaSeeking_IsFormatSupported(seeking, &testguid);
todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
filter1.support_testguid = FALSE;
filter2.support_testguid = TRUE;
hr = IMediaSeeking_IsFormatSupported(seeking, &testguid);
todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
/* Filters are not consulted about preferred formats. */
hr = IMediaSeeking_QueryPreferredFormat(seeking, &format);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
hr = IMediaSeeking_GetTimeFormat(seeking, &format);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
filter2.support_testguid = FALSE;
hr = IMediaSeeking_SetTimeFormat(seeking, &testguid);
todo_wine ok(hr == E_FAIL, "Got hr %#x.\n", hr);
hr = IMediaSeeking_GetTimeFormat(seeking, &format);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
filter1.support_testguid = TRUE;
hr = IMediaSeeking_SetTimeFormat(seeking, &testguid);
todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaSeeking_GetTimeFormat(seeking, &format);
ok(hr == S_OK, "Got hr %#x.\n", hr);
todo_wine ok(IsEqualGUID(&format, &testguid), "Got format %s.\n", wine_dbgstr_guid(&format));
hr = IMediaSeeking_QueryPreferredFormat(seeking, &format);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
hr = IMediaSeeking_IsUsingTimeFormat(seeking, &TIME_FORMAT_MEDIA_TIME);
todo_wine ok(hr == S_FALSE, "Got hr %#x.\n", hr);
hr = IMediaSeeking_IsUsingTimeFormat(seeking, &testguid);
todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaSeeking_SetTimeFormat(seeking, &TIME_FORMAT_MEDIA_TIME);
ok(hr == S_OK, "Got hr %#x.\n", hr);
time = 0xdeadbeef;
hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &testguid, 0x123456789a, &testguid);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &testguid, 0x123456789a, &TIME_FORMAT_NONE);
todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
filter1.seek_duration = 0x12345;
filter2.seek_duration = 0x23456;
hr = IMediaSeeking_GetDuration(seeking, &time);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(time == 0x23456, "Got time %s.\n", wine_dbgstr_longlong(time));
filter2.seek_duration = 0x12345;
filter1.seek_duration = 0x23456;
hr = IMediaSeeking_GetDuration(seeking, &time);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(time == 0x23456, "Got time %s.\n", wine_dbgstr_longlong(time));
filter1.seek_stop = 0x54321;
filter2.seek_stop = 0x65432;
hr = IMediaSeeking_GetStopPosition(seeking, &time);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(time == 0x65432, "Got time %s.\n", wine_dbgstr_longlong(time));
filter2.seek_stop = 0x54321;
filter1.seek_stop = 0x65432;
hr = IMediaSeeking_GetStopPosition(seeking, &time);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(time == 0x65432, "Got time %s.\n", wine_dbgstr_longlong(time));
filter1.seek_hr = filter2.seek_hr = 0xbeef;
hr = IMediaSeeking_GetStopPosition(seeking, &time);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(time == 0x65432, "Got time %s.\n", wine_dbgstr_longlong(time));
filter1.seek_hr = E_NOTIMPL;
hr = IMediaSeeking_GetStopPosition(seeking, &time);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(time == 0x54321, "Got time %s.\n", wine_dbgstr_longlong(time));
filter1.seek_hr = 0xdeadbeef;
hr = IMediaSeeking_GetStopPosition(seeking, &time);
ok(hr == 0xdeadbeef, "Got hr %#x.\n", hr);
filter1.seek_hr = filter2.seek_hr = E_NOTIMPL;
hr = IMediaSeeking_GetStopPosition(seeking, &time);
ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
filter1.seek_hr = filter2.seek_hr = S_OK;
hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(!time, "Got time %s.\n", wine_dbgstr_longlong(time));
current = stop = 0xdeadbeef;
hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(!current, "Got time %s.\n", wine_dbgstr_longlong(current));
ok(stop == 0x65432, "Got time %s.\n", wine_dbgstr_longlong(stop));
current = 0x123;
stop = 0x321;
hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
&stop, AM_SEEKING_AbsolutePositioning);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(current));
ok(stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(stop));
ok(filter1.seek_current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_current));
ok(filter1.seek_stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_stop));
ok(filter2.seek_current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_current));
ok(filter2.seek_stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_stop));
filter1.seek_hr = filter2.seek_hr = 0xbeef;
hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
&stop, AM_SEEKING_AbsolutePositioning);
ok(hr == S_OK, "Got hr %#x.\n", hr);
filter1.seek_hr = E_NOTIMPL;
hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
&stop, AM_SEEKING_AbsolutePositioning);
ok(hr == S_OK, "Got hr %#x.\n", hr);
filter1.seek_hr = 0xdeadbeef;
hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
&stop, AM_SEEKING_AbsolutePositioning);
ok(hr == 0xdeadbeef, "Got hr %#x.\n", hr);
filter1.seek_hr = filter2.seek_hr = E_NOTIMPL;
hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
&stop, AM_SEEKING_AbsolutePositioning);
ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
filter1.seek_hr = filter2.seek_hr = S_OK;
hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(time == 12340000, "Got time %s.\n", wine_dbgstr_longlong(time));
current = stop = 0xdeadbeef;
hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(current == 12340000, "Got time %s.\n", wine_dbgstr_longlong(current));
ok(stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(stop));
current = 0x123;
stop = 0x321;
hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning | AM_SEEKING_ReturnTime,
&stop, AM_SEEKING_AbsolutePositioning);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(current == 12340000, "Got time %s.\n", wine_dbgstr_longlong(current));
ok(stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(stop));
ok(filter1.seek_current == 12340000, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_current));
ok(filter1.seek_stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_stop));
ok(filter2.seek_current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_current));
ok(filter2.seek_stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_stop));
current = 0x123;
stop = 0x321;
hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
&stop, AM_SEEKING_AbsolutePositioning | AM_SEEKING_ReturnTime);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(current));
ok(stop == 43210000, "Got time %s.\n", wine_dbgstr_longlong(stop));
ok(filter1.seek_current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_current));
ok(filter1.seek_stop == 43210000, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_stop));
ok(filter2.seek_current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_current));
ok(filter2.seek_stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_stop));
hr = IMediaSeeking_SetRate(seeking, 2.0);
ok(hr == S_OK, "Got hr %#x.\n", hr);
todo_wine ok(filter1.seek_rate == 2.0, "Got rate %.16e.\n", filter1.seek_rate);
todo_wine ok(filter2.seek_rate == 2.0, "Got rate %.16e.\n", filter2.seek_rate);
hr = IMediaSeeking_SetRate(seeking, 1.0);
ok(hr == S_OK, "Got hr %#x.\n", hr);
todo_wine ok(filter1.seek_rate == 1.0, "Got rate %.16e.\n", filter1.seek_rate);
todo_wine ok(filter2.seek_rate == 1.0, "Got rate %.16e.\n", filter2.seek_rate);
hr = IMediaSeeking_SetRate(seeking, -1.0);
ok(hr == S_OK, "Got hr %#x.\n", hr);
todo_wine ok(filter1.seek_rate == -1.0, "Got rate %.16e.\n", filter1.seek_rate);
todo_wine ok(filter2.seek_rate == -1.0, "Got rate %.16e.\n", filter2.seek_rate);
hr = IMediaSeeking_GetRate(seeking, &rate);
ok(hr == S_OK, "Got hr %#x.\n", hr);
todo_wine ok(rate == -1.0, "Got rate %.16e.\n", rate);
hr = IMediaSeeking_SetRate(seeking, 1.0);
ok(hr == S_OK, "Got hr %#x.\n", hr);
/* Test how retrieving the current position behaves while the graph is
* running. Apparently the graph caches the last position returned by
* SetPositions() and then adds the clock offset to the stream start. */
hr = IMediaControl_Run(control);
ok(hr == S_OK, "Got hr %#x.\n", hr);
/* Note that if the graph is running, it is paused while seeking. */
current = 0;
stop = 9000 * 10000;
hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
&stop, AM_SEEKING_AbsolutePositioning);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
ok(hr == S_OK, "Got hr %#x.\n", hr);
if (winetest_interactive) /* Timing problems make this test too liable to fail. */
ok(compare_time(time, 1234 * 10000, 40 * 10000),
"Expected about 1234ms, got %s.\n", wine_dbgstr_longlong(time));
current = stop = 0xdeadbeef;
hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
ok(hr == S_OK, "Got hr %#x.\n", hr);
if (winetest_interactive) /* Timing problems make this test too liable to fail. */
ok(compare_time(current, 1234 * 10000, 40 * 10000),
"Expected about 1234ms, got %s.\n", wine_dbgstr_longlong(current));
ok(stop == 9000 * 10000, "Got time %s.\n", wine_dbgstr_longlong(stop));
/* This remains true even if NoFlush is specified. */
current = 1000 * 10000;
stop = 8000 * 10000;
hr = IMediaSeeking_SetPositions(seeking, &current,
AM_SEEKING_AbsolutePositioning | AM_SEEKING_NoFlush,
&stop, AM_SEEKING_AbsolutePositioning | AM_SEEKING_NoFlush);
ok(hr == S_OK, "Got hr %#x.\n", hr);
Sleep(100);
hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
ok(hr == S_OK, "Got hr %#x.\n", hr);
if (winetest_interactive) /* Timing problems make this test too liable to fail. */
ok(compare_time(time, 1334 * 10000, 80 * 10000),
"Expected about 1334ms, got %s.\n", wine_dbgstr_longlong(time));
current = stop = 0xdeadbeef;
hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
ok(hr == S_OK, "Got hr %#x.\n", hr);
if (winetest_interactive) /* Timing problems make this test too liable to fail. */
ok(compare_time(current, 1334 * 10000, 80 * 10000),
"Expected about 1334ms, got %s.\n", wine_dbgstr_longlong(current));
ok(stop == 8000 * 10000, "Got time %s.\n", wine_dbgstr_longlong(stop));
hr = IMediaControl_Pause(control);
ok(hr == S_OK, "Got hr %#x.\n", hr);
Sleep(100);
hr = IMediaControl_Run(control);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
ok(hr == S_OK, "Got hr %#x.\n", hr);
if (winetest_interactive) /* Timing problems make this test too liable to fail. */
ok(compare_time(time, 1334 * 10000, 80 * 10000),
"Expected about 1334ms, got %s.\n", wine_dbgstr_longlong(time));
current = stop = 0xdeadbeef;
hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
ok(hr == S_OK, "Got hr %#x.\n", hr);
if (winetest_interactive) /* Timing problems make this test too liable to fail. */
ok(compare_time(current, 1334 * 10000, 80 * 10000),
"Expected about 1334ms, got %s.\n", wine_dbgstr_longlong(current));
ok(stop == 8000 * 10000, "Got time %s.\n", wine_dbgstr_longlong(stop));
hr = IMediaControl_Stop(control);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(time == 12340000, "Got time %s.\n", wine_dbgstr_longlong(time));
hr = IMediaFilter_SetSyncSource(filter, NULL);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaControl_Run(control);
ok(hr == S_OK, "Got hr %#x.\n", hr);
Sleep(100);
hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
ok(hr == S_OK, "Got hr %#x.\n", hr);
todo_wine ok(!time, "Got time %s.\n", wine_dbgstr_longlong(time));
current = stop = 0xdeadbeef;
hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
ok(hr == S_OK, "Got hr %#x.\n", hr);
todo_wine ok(!current, "Got time %s.\n", wine_dbgstr_longlong(current));
ok(!stop, "Got time %s.\n", wine_dbgstr_longlong(stop));
hr = IMediaControl_Stop(control);
ok(hr == S_OK, "Got hr %#x.\n", hr);
IMediaFilter_Release(filter);
IMediaControl_Release(control);
IMediaSeeking_Release(seeking);
ok(filter1.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
ok(filter2.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter2.seeking_ref);
ref = IFilterGraph2_Release(graph);
ok(!ref, "Got outstanding refcount %d.\n", hr);
ok(filter1.ref == 1, "Got outstanding refcount %d.\n", filter1.ref);
ok(filter2.ref == 1, "Got outstanding refcount %d.\n", filter2.ref);
ok(filter1.seeking_ref == 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
ok(filter2.seeking_ref == 0, "Unexpected seeking refcount %d.\n", filter2.seeking_ref);
}
static void test_default_sync_source(void)
{
struct testpin source_pin, sink1_pin, sink2_pin;
struct testfilter source, sink1, sink2;
IFilterGraph2 *graph = create_graph();
IReferenceClock *clock;
IMediaFilter *filter;
HRESULT hr;
ULONG ref;
testsink_init(&sink1_pin);
testsink_init(&sink2_pin);
testsource_init(&source_pin, NULL, 0);
testfilter_init(&source, &source_pin, 1);
testfilter_init(&sink1, &sink1_pin, 1);
testfilter_init(&sink2, &sink2_pin, 1);
IFilterGraph2_AddFilter(graph, &sink1.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &sink2.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink1_pin.IPin_iface, NULL);
IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
hr = IFilterGraph2_SetDefaultSyncSource(graph);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaFilter_GetSyncSource(filter, &clock);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(!!clock, "Reference clock not set.\n");
IReferenceClock_Release(clock);
source.IReferenceClock_iface.lpVtbl = &testclock_vtbl;
hr = IFilterGraph2_SetDefaultSyncSource(graph);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaFilter_GetSyncSource(filter, &clock);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(clock == &source.IReferenceClock_iface, "Got unexpected clock.\n");
IReferenceClock_Release(clock);
/* The documentation says that connected filters are preferred, but this
* does not in fact seem to be the case. */
sink2.IReferenceClock_iface.lpVtbl = &testclock_vtbl;
hr = IFilterGraph2_SetDefaultSyncSource(graph);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaFilter_GetSyncSource(filter, &clock);
ok(hr == S_OK, "Got hr %#x.\n", hr);
todo_wine ok(clock == &sink2.IReferenceClock_iface, "Got unexpected clock.\n");
IReferenceClock_Release(clock);
sink1.IReferenceClock_iface.lpVtbl = &testclock_vtbl;
hr = IFilterGraph2_SetDefaultSyncSource(graph);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaFilter_GetSyncSource(filter, &clock);
ok(hr == S_OK, "Got hr %#x.\n", hr);
todo_wine ok(clock == &sink1.IReferenceClock_iface, "Got unexpected clock.\n");
IReferenceClock_Release(clock);
IMediaFilter_Release(filter);
ref = IFilterGraph2_Release(graph);
ok(!ref, "Got outstanding refcount %d.\n", ref);
ok(sink1.ref == 1, "Got outstanding refcount %d.\n", sink1.ref);
ok(sink2.ref == 1, "Got outstanding refcount %d.\n", sink2.ref);
ok(source.ref == 1, "Got outstanding refcount %d.\n", source.ref);
}
static void test_add_source_filter(void)
{
static const char bogus_data[20] = {0xde, 0xad, 0xbe, 0xef};
static const char midi_data[20] = {'M','T','h','d'};
IFilterGraph2 *graph = create_graph();
IFileSourceFilter *filesource;
IBaseFilter *filter, *filter2;
FILTER_INFO filter_info;
const WCHAR *filename;
WCHAR *ret_filename;
AM_MEDIA_TYPE mt;
CLSID clsid;
HRESULT hr;
ULONG ref;
BOOL ret;
HKEY key;
/* Test a file which should be registered by extension. */
filename = create_file(L"test.mp3", midi_data, sizeof(midi_data));
hr = IFilterGraph2_AddSourceFilter(graph, filename, L"test", &filter);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IBaseFilter_GetClassID(filter, &clsid);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(IsEqualGUID(&clsid, &CLSID_AsyncReader), "Got filter %s.\n", wine_dbgstr_guid(&clsid));
hr = IBaseFilter_QueryFilterInfo(filter, &filter_info);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(!wcscmp(filter_info.achName, L"test"), "Got unexpected name %s.\n", wine_dbgstr_w(filter_info.achName));
IFilterGraph_Release(filter_info.pGraph);
hr = IBaseFilter_QueryInterface(filter, &IID_IFileSourceFilter, (void **)&filesource);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IFileSourceFilter_GetCurFile(filesource, &ret_filename, &mt);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(!wcscmp(ret_filename, filename), "Expected filename %s, got %s.\n",
wine_dbgstr_w(filename), wine_dbgstr_w(ret_filename));
ok(IsEqualGUID(&mt.majortype, &MEDIATYPE_Stream), "Got major type %s.\n", wine_dbgstr_guid(&mt.majortype));
ok(IsEqualGUID(&mt.subtype, &MEDIASUBTYPE_MPEG1Audio), "Got subtype %s.\n", wine_dbgstr_guid(&mt.subtype));
IFileSourceFilter_Release(filesource);
hr = IFilterGraph2_AddSourceFilter(graph, filename, L"test", &filter2);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(filter2 != filter, "Filters shouldn't match.\n");
hr = IFilterGraph2_RemoveFilter(graph, filter2);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ref = IBaseFilter_Release(filter2);
ok(!ref, "Got outstanding refcount %d.\n", ref);
hr = IFilterGraph2_RemoveFilter(graph, filter);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ref = IBaseFilter_Release(filter);
ok(!ref, "Got outstanding refcount %d.\n", ref);
ret = DeleteFileW(filename);
ok(ret, "Failed to delete %s, error %u.\n", wine_dbgstr_w(filename), GetLastError());
/* Test a file which should be registered by signature. */
filename = create_file(L"test.avi", midi_data, sizeof(midi_data));
hr = IFilterGraph2_AddSourceFilter(graph, filename, NULL, &filter);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IBaseFilter_GetClassID(filter, &clsid);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(IsEqualGUID(&clsid, &CLSID_AsyncReader), "Got filter %s.\n", wine_dbgstr_guid(&clsid));
hr = IBaseFilter_QueryFilterInfo(filter, &filter_info);
ok(hr == S_OK, "Got hr %#x.\n", hr);
todo_wine ok(!wcscmp(filter_info.achName, filename), "Got unexpected name %s.\n", wine_dbgstr_w(filter_info.achName));
IFilterGraph_Release(filter_info.pGraph);
hr = IBaseFilter_QueryInterface(filter, &IID_IFileSourceFilter, (void **)&filesource);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IFileSourceFilter_GetCurFile(filesource, &ret_filename, &mt);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(!wcscmp(ret_filename, filename), "Expected filename %s, got %s.\n",
wine_dbgstr_w(filename), wine_dbgstr_w(ret_filename));
ok(IsEqualGUID(&mt.majortype, &MEDIATYPE_Stream), "Got major type %s.\n", wine_dbgstr_guid(&mt.majortype));
ok(IsEqualGUID(&mt.subtype, &MEDIATYPE_Midi), "Got subtype %s.\n", wine_dbgstr_guid(&mt.subtype));
IFileSourceFilter_Release(filesource);
hr = IFilterGraph2_RemoveFilter(graph, filter);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ref = IBaseFilter_Release(filter);
ok(!ref, "Got outstanding refcount %d.\n", ref);
ret = DeleteFileW(filename);
ok(ret, "Failed to delete %s, error %u.\n", wine_dbgstr_w(filename), GetLastError());
if (!RegCreateKeyA(HKEY_CLASSES_ROOT, "Media Type\\{abbccdde-0000-0000-0000-000000000000}"
"\\{bccddeef-0000-0000-0000-000000000000}", &key))
{
static const GUID testfilter_clsid = {0x12345678};
struct testpin testfilter_pin;
struct testfilter testfilter;
struct testfilter_cf cf = {{&testfilter_cf_vtbl}, &testfilter};
DWORD cookie;
testsource_init(&testfilter_pin, NULL, 0);
testfilter_init(&testfilter, &testfilter_pin, 1);
testfilter.IFileSourceFilter_iface.lpVtbl = &testfilesource_vtbl;
CoRegisterClassObject(&testfilter_clsid, (IUnknown *)&cf.IClassFactory_iface,
CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie);
RegSetValueExA(key, "0", 0, REG_SZ, (const BYTE *)"0,4,,deadbeef", 14);
RegSetValueExA(key, "Source Filter", 0, REG_SZ, (const BYTE *)"{12345678-0000-0000-0000-000000000000}", 39);
filename = create_file(L"test.avi", bogus_data, sizeof(bogus_data));
hr = IFilterGraph2_AddSourceFilter(graph, filename, NULL, &filter);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(filter == &testfilter.IBaseFilter_iface, "Got unexpected filter %p.\n", filter);
hr = IFilterGraph2_RemoveFilter(graph, filter);
ok(hr == S_OK, "Got hr %#x.\n", hr);
IBaseFilter_Release(filter);
ref = IBaseFilter_Release(&testfilter.IBaseFilter_iface);
ok(!ref, "Got outstanding refcount %d.\n", ref);
ret = DeleteFileW(filename);
ok(ret, "Failed to delete %s, error %u.\n", wine_dbgstr_w(filename), GetLastError());
RegDeleteKeyA(HKEY_CLASSES_ROOT, "Media Type\\{abbccdde-0000-0000-0000-000000000000}"
"\\{bccddeef-0000-0000-0000-000000000000}");
RegDeleteKeyA(HKEY_CLASSES_ROOT, "Media Type\\{abbccdde-0000-0000-0000-000000000000}");
CoRevokeClassObject(cookie);
}
else
skip("Not enough permission to register media types.\n");
ref = IFilterGraph2_Release(graph);
ok(!ref, "Got outstanding refcount %d.\n", ref);
}
static HWND get_renderer_hwnd(IFilterGraph2 *graph)
{
IEnumFilters *enum_filters;
IEnumPins *enum_pins;
IBaseFilter *filter;
IOverlay *overlay;
HWND hwnd = NULL;
HRESULT hr;
IPin *pin;
hr = IFilterGraph2_EnumFilters(graph, &enum_filters);
ok(hr == S_OK, "Got hr %#x.\n", hr);
while (IEnumFilters_Next(enum_filters, 1, &filter, NULL) == S_OK)
{
hr = IBaseFilter_EnumPins(filter, &enum_pins);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IEnumPins_Next(enum_pins, 1, &pin, NULL);
ok(hr == S_OK, "Got hr %#x.\n", hr);
IEnumPins_Release(enum_pins);
if (SUCCEEDED(IPin_QueryInterface(pin, &IID_IOverlay, (void **)&overlay)))
{
hr = IOverlay_GetWindowHandle(overlay, &hwnd);
ok(hr == S_OK, "Got hr %#x.\n", hr);
IOverlay_Release(overlay);
}
IPin_Release(pin);
IBaseFilter_Release(filter);
}
IEnumFilters_Release(enum_filters);
return hwnd;
}
static BOOL expect_parent_message = TRUE;
static LRESULT CALLBACK parent_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
ok(expect_parent_message, "Got unexpected message %#x.\n", msg);
return DefWindowProcA(hwnd, msg, wparam, lparam);
}
static void test_window_threading(void)
{
static const WNDCLASSA class =
{
.lpfnWndProc = parent_proc,
.lpszClassName = "quartz_test_parent",
};
WCHAR *filename = load_resource(L"test.avi");
IFilterGraph2 *graph = create_graph();
IVideoWindow *window;
HWND hwnd, parent;
HRESULT hr;
DWORD tid;
ULONG ref;
BOOL ret;
RegisterClassA(&class);
parent = CreateWindowA("quartz_test_parent", NULL, WS_OVERLAPPEDWINDOW,
50, 50, 150, 150, NULL, NULL, NULL, NULL);
ok(!!parent, "Failed to create parent window.\n");
hr = IFilterGraph2_RenderFile(graph, filename, NULL);
if (FAILED(hr))
{
skip("Cannot render test file, hr %#x.\n", hr);
IFilterGraph2_Release(graph);
DeleteFileW(filename);
return;
}
ok(hr == S_OK, "Got hr %#x.\n", hr);
if ((hwnd = get_renderer_hwnd(graph)))
{
tid = GetWindowThreadProcessId(hwnd, NULL);
ok(tid != GetCurrentThreadId(), "Window should have been created on a separate thread.\n");
/* The thread should be processing messages, or this will hang. */
SendMessageA(hwnd, WM_NULL, 0, 0);
/* Media Player Classic deadlocks if the parent is sent any messages
* while the video window is released. In particular, we must not send
* WM_PARENTNOTIFY. This is not achieved through window styles. */
hr = IFilterGraph2_QueryInterface(graph, &IID_IVideoWindow, (void **)&window);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IVideoWindow_put_Owner(window, (OAHWND)parent);
ok(hr == S_OK, "Got hr %#x.\n", hr);
IVideoWindow_Release(window);
ok(!(GetWindowLongW(hwnd, GWL_EXSTYLE) & WS_EX_NOPARENTNOTIFY), "Window has WS_EX_NOPARENTNOTIFY.\n");
}
else
skip("Could not find renderer window.\n");
SetActiveWindow(parent);
expect_parent_message = FALSE;
ref = IFilterGraph2_Release(graph);
ok(!ref, "Got outstanding refcount %d.\n", ref);
expect_parent_message = TRUE;
hwnd = GetActiveWindow();
ok(hwnd == parent, "Parent window lost focus, active window %p.\n", hwnd);
hr = CoCreateInstance(&CLSID_FilterGraphNoThread, NULL, CLSCTX_INPROC_SERVER,
&IID_IFilterGraph2, (void **)&graph);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IFilterGraph2_RenderFile(graph, filename, NULL);
ok(hr == S_OK, "Got hr %#x.\n", hr);
if ((hwnd = get_renderer_hwnd(graph)))
{
tid = GetWindowThreadProcessId(hwnd, NULL);
ok(tid == GetCurrentThreadId(), "Window should be created on main thread.\n");
}
else
skip("Could not find renderer window.\n");
ref = IFilterGraph2_Release(graph);
ok(!ref, "Got outstanding refcount %d.\n", ref);
DestroyWindow(parent);
UnregisterClassA("quartz_test_parent", GetModuleHandleA(NULL));
ret = DeleteFileW(filename);
ok(ret, "Failed to delete file, error %u.\n", GetLastError());
}
/* Hyperdevotion Noire needs to be able to Render() from UYVY. */
static void test_autoplug_uyvy(void)
{
static const VIDEOINFOHEADER source_format =
{
.bmiHeader.biSize = sizeof(BITMAPINFOHEADER),
.bmiHeader.biWidth = 600,
.bmiHeader.biHeight = 400,
.bmiHeader.biCompression = mmioFOURCC('U','Y','V','Y'),
.bmiHeader.biBitCount = 16,
};
AM_MEDIA_TYPE source_type =
{
.majortype = MEDIATYPE_Video,
.subtype = MEDIASUBTYPE_UYVY,
.formattype = FORMAT_VideoInfo,
.cbFormat = sizeof(source_format),
.pbFormat = (BYTE *)&source_format,
};
IFilterGraph2 *graph = create_graph();
struct testpin source_pin;
struct testfilter source;
HRESULT hr;
ULONG ref;
testsource_init(&source_pin, NULL, 0);
testfilter_init(&source, &source_pin, 1);
source_pin.request_mt = &source_type;
IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, L"source");
/* Windows 2008 doesn't seem to have an UYVY decoder, and the testbot chalks
* failure to decode up to missing audio hardware, even though we're not
* trying to render audio. */
hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
todo_wine ok(hr == S_OK || hr == VFW_E_NO_AUDIO_HARDWARE, "Got hr %#x.\n", hr);
ref = IFilterGraph2_Release(graph);
ok(!ref, "Got outstanding refcount %d.\n", ref);
ok(source.ref == 1, "Got outstanding refcount %d.\n", source.ref);
ok(source_pin.ref == 1, "Got outstanding refcount %d.\n", source_pin.ref);
}
START_TEST(filtergraph)
{
CoInitializeEx(NULL, COINIT_MULTITHREADED);
test_interfaces();
test_render_run(L"test.avi", FALSE, TRUE);
test_render_run(L"test.mpg", TRUE, TRUE);
test_render_run(L"test.mp3", TRUE, FALSE);
test_render_run(L"test.wav", TRUE, FALSE);
test_enum_filters();
test_graph_builder_render();
test_graph_builder_connect();
test_aggregation();
test_control_delegation();
test_add_remove_filter();
test_connect_direct();
test_sync_source();
test_filter_state();
test_ec_complete();
test_graph_seeking();
test_default_sync_source();
test_add_source_filter();
test_window_threading();
test_autoplug_uyvy();
CoUninitialize();
test_render_with_multithread();
}