qcap: Add the SmartTee filter automatically as necessary, and test this.
This commit is contained in:
parent
419be23964
commit
982be1dea2
|
@ -249,6 +249,118 @@ fnCaptureGraphBuilder2_FindInterface(ICaptureGraphBuilder2 * iface,
|
|||
*/
|
||||
}
|
||||
|
||||
static HRESULT match_smart_tee_pin(CaptureGraphImpl *This,
|
||||
const GUID *pCategory,
|
||||
const GUID *pType,
|
||||
IUnknown *pSource,
|
||||
IPin **source_out)
|
||||
{
|
||||
static const WCHAR inputW[] = {'I','n','p','u','t',0};
|
||||
static const WCHAR captureW[] = {'C','a','p','t','u','r','e',0};
|
||||
static const WCHAR previewW[] = {'P','r','e','v','i','e','w',0};
|
||||
IPin *capture = NULL;
|
||||
IPin *preview = NULL;
|
||||
IPin *peer = NULL;
|
||||
IBaseFilter *smartTee = NULL;
|
||||
BOOL needSmartTee = FALSE;
|
||||
HRESULT hr;
|
||||
|
||||
TRACE("(%p, %s, %s, %p, %p)\n", This, debugstr_guid(pCategory), debugstr_guid(pType), pSource, source_out);
|
||||
hr = ICaptureGraphBuilder2_FindPin(&This->ICaptureGraphBuilder2_iface, pSource,
|
||||
PINDIR_OUTPUT, &PIN_CATEGORY_CAPTURE, pType, FALSE, 0, &capture);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = ICaptureGraphBuilder2_FindPin(&This->ICaptureGraphBuilder2_iface, pSource,
|
||||
PINDIR_OUTPUT, &PIN_CATEGORY_PREVIEW, pType, FALSE, 0, &preview);
|
||||
if (FAILED(hr))
|
||||
needSmartTee = TRUE;
|
||||
} else {
|
||||
hr = E_INVALIDARG;
|
||||
goto end;
|
||||
}
|
||||
if (!needSmartTee) {
|
||||
if (IsEqualIID(pCategory, &PIN_CATEGORY_CAPTURE)) {
|
||||
hr = IPin_ConnectedTo(capture, &peer);
|
||||
if (hr == VFW_E_NOT_CONNECTED) {
|
||||
*source_out = capture;
|
||||
IPin_AddRef(*source_out);
|
||||
hr = S_OK;
|
||||
} else
|
||||
hr = E_INVALIDARG;
|
||||
} else {
|
||||
hr = IPin_ConnectedTo(preview, &peer);
|
||||
if (hr == VFW_E_NOT_CONNECTED) {
|
||||
*source_out = preview;
|
||||
IPin_AddRef(*source_out);
|
||||
hr = S_OK;
|
||||
} else
|
||||
hr = E_INVALIDARG;
|
||||
}
|
||||
goto end;
|
||||
}
|
||||
hr = IPin_ConnectedTo(capture, &peer);
|
||||
if (SUCCEEDED(hr)) {
|
||||
PIN_INFO pinInfo;
|
||||
GUID classID;
|
||||
hr = IPin_QueryPinInfo(peer, &pinInfo);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = IBaseFilter_GetClassID(pinInfo.pFilter, &classID);
|
||||
if (SUCCEEDED(hr)) {
|
||||
if (IsEqualIID(&classID, &CLSID_SmartTee)) {
|
||||
smartTee = pinInfo.pFilter;
|
||||
IBaseFilter_AddRef(smartTee);
|
||||
}
|
||||
}
|
||||
IBaseFilter_Release(pinInfo.pFilter);
|
||||
}
|
||||
if (!smartTee) {
|
||||
hr = E_INVALIDARG;
|
||||
goto end;
|
||||
}
|
||||
} else if (hr == VFW_E_NOT_CONNECTED) {
|
||||
hr = CoCreateInstance(&CLSID_SmartTee, NULL, CLSCTX_INPROC_SERVER,
|
||||
&IID_IBaseFilter, (LPVOID*)&smartTee);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = IGraphBuilder_AddFilter(This->mygraph, smartTee, NULL);
|
||||
if (SUCCEEDED(hr)) {
|
||||
IPin *smartTeeInput = NULL;
|
||||
hr = IBaseFilter_FindPin(smartTee, inputW, &smartTeeInput);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = IGraphBuilder_ConnectDirect(This->mygraph, capture, smartTeeInput, NULL);
|
||||
IPin_Release(smartTeeInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (FAILED(hr)) {
|
||||
TRACE("adding SmartTee failed with hr=0x%08x\n", hr);
|
||||
hr = E_INVALIDARG;
|
||||
goto end;
|
||||
}
|
||||
} else {
|
||||
hr = E_INVALIDARG;
|
||||
goto end;
|
||||
}
|
||||
if (IsEqualIID(pCategory, &PIN_CATEGORY_CAPTURE))
|
||||
hr = IBaseFilter_FindPin(smartTee, captureW, source_out);
|
||||
else {
|
||||
hr = IBaseFilter_FindPin(smartTee, previewW, source_out);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = VFW_S_NOPREVIEWPIN;
|
||||
}
|
||||
|
||||
end:
|
||||
if (capture)
|
||||
IPin_Release(capture);
|
||||
if (preview)
|
||||
IPin_Release(preview);
|
||||
if (peer)
|
||||
IPin_Release(peer);
|
||||
if (smartTee)
|
||||
IBaseFilter_Release(smartTee);
|
||||
TRACE("for %s returning hr=0x%08x, *source_out=%p\n", IsEqualIID(pCategory, &PIN_CATEGORY_CAPTURE) ? "capture" : "preview", hr, source_out ? *source_out : 0);
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
static HRESULT WINAPI
|
||||
fnCaptureGraphBuilder2_RenderStream(ICaptureGraphBuilder2 * iface,
|
||||
const GUID *pCategory,
|
||||
|
@ -258,7 +370,8 @@ fnCaptureGraphBuilder2_RenderStream(ICaptureGraphBuilder2 * iface,
|
|||
IBaseFilter *pfRenderer)
|
||||
{
|
||||
CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder2(iface);
|
||||
IPin *source_out, *renderer_in, *capture, *preview;
|
||||
IPin *source_out = NULL, *renderer_in;
|
||||
BOOL usedSmartTeePreviewPin = FALSE;
|
||||
HRESULT hr;
|
||||
|
||||
FIXME("(%p/%p)->(%s, %s, %p, %p, %p) semi-stub!\n", This, iface,
|
||||
|
@ -276,24 +389,26 @@ fnCaptureGraphBuilder2_RenderStream(ICaptureGraphBuilder2 * iface,
|
|||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
hr = ICaptureGraphBuilder2_FindPin(iface, pSource, PINDIR_OUTPUT, pCategory, pType, TRUE, 0, &source_out);
|
||||
if (FAILED(hr))
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (pCategory && IsEqualIID(pCategory, &PIN_CATEGORY_VBI)) {
|
||||
FIXME("Tee/Sink-to-Sink filter not supported\n");
|
||||
IPin_Release(source_out);
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
hr = ICaptureGraphBuilder2_FindPin(iface, pSource, PINDIR_OUTPUT, &PIN_CATEGORY_CAPTURE, NULL, TRUE, 0, &capture);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = ICaptureGraphBuilder2_FindPin(iface, pSource, PINDIR_OUTPUT, &PIN_CATEGORY_PREVIEW, NULL, TRUE, 0, &preview);
|
||||
} else if (pCategory && (IsEqualIID(pCategory, &PIN_CATEGORY_CAPTURE) || IsEqualIID(pCategory, &PIN_CATEGORY_PREVIEW))){
|
||||
IBaseFilter *sourceFilter = NULL;
|
||||
hr = IUnknown_QueryInterface(pSource, &IID_IBaseFilter, (void**)&sourceFilter);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = match_smart_tee_pin(This, pCategory, pType, pSource, &source_out);
|
||||
if (hr == VFW_S_NOPREVIEWPIN)
|
||||
usedSmartTeePreviewPin = TRUE;
|
||||
IBaseFilter_Release(sourceFilter);
|
||||
} else {
|
||||
hr = ICaptureGraphBuilder2_FindPin(iface, pSource, PINDIR_OUTPUT, pCategory, pType, TRUE, 0, &source_out);
|
||||
}
|
||||
if (FAILED(hr))
|
||||
FIXME("Smart Tee filter not supported - not creating preview pin\n");
|
||||
else
|
||||
IPin_Release(preview);
|
||||
IPin_Release(capture);
|
||||
return E_INVALIDARG;
|
||||
} else {
|
||||
hr = ICaptureGraphBuilder2_FindPin(iface, pSource, PINDIR_OUTPUT, pCategory, pType, TRUE, 0, &source_out);
|
||||
if (FAILED(hr))
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
hr = ICaptureGraphBuilder2_FindPin(iface, (IUnknown*)pfRenderer, PINDIR_INPUT, NULL, NULL, TRUE, 0, &renderer_in);
|
||||
|
@ -331,6 +446,8 @@ fnCaptureGraphBuilder2_RenderStream(ICaptureGraphBuilder2 * iface,
|
|||
|
||||
IPin_Release(source_out);
|
||||
IPin_Release(renderer_in);
|
||||
if (SUCCEEDED(hr) && usedSmartTeePreviewPin)
|
||||
hr = VFW_S_NOPREVIEWPIN;
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
|
|
@ -140,121 +140,7 @@ static const struct {
|
|||
BOOL wine_extra;
|
||||
BOOL optional; /* fails on wine if missing */
|
||||
BOOL broken;
|
||||
} renderstream_cat_media[] = {
|
||||
{BASEFILTER_QUERYINTERFACE, SOURCE_FILTER},
|
||||
{BASEFILTER_ENUMPINS, SOURCE_FILTER},
|
||||
{ENUMPINS_NEXT, SOURCE_FILTER},
|
||||
{PIN_QUERYDIRECTION, SOURCE_FILTER},
|
||||
{PIN_CONNECTEDTO, SOURCE_FILTER},
|
||||
{PIN_QUERYPININFO, SOURCE_FILTER, TRUE},
|
||||
{KSPROPERTYSET_GET, SOURCE_FILTER},
|
||||
{PIN_ENUMMEDIATYPES, SOURCE_FILTER},
|
||||
{ENUMMEDIATYPES_RESET, SOURCE_FILTER},
|
||||
{ENUMMEDIATYPES_NEXT, SOURCE_FILTER},
|
||||
{BASEFILTER_QUERYINTERFACE, SOURCE_FILTER},
|
||||
{BASEFILTER_ENUMPINS, SOURCE_FILTER},
|
||||
{ENUMPINS_NEXT, SOURCE_FILTER},
|
||||
{PIN_QUERYDIRECTION, SOURCE_FILTER},
|
||||
{PIN_CONNECTEDTO, SOURCE_FILTER},
|
||||
{PIN_QUERYPININFO, SOURCE_FILTER, TRUE},
|
||||
{KSPROPERTYSET_GET, SOURCE_FILTER},
|
||||
{ENUMPINS_NEXT, SOURCE_FILTER},
|
||||
{BASEFILTER_QUERYINTERFACE, SOURCE_FILTER, TRUE},
|
||||
{BASEFILTER_ENUMPINS, SOURCE_FILTER, TRUE},
|
||||
{ENUMPINS_NEXT, SOURCE_FILTER, TRUE},
|
||||
{PIN_QUERYDIRECTION, SOURCE_FILTER, TRUE},
|
||||
{PIN_CONNECTEDTO, SOURCE_FILTER, TRUE},
|
||||
{PIN_QUERYPININFO, SOURCE_FILTER, TRUE},
|
||||
{KSPROPERTYSET_GET, SOURCE_FILTER, TRUE},
|
||||
{ENUMPINS_NEXT, SOURCE_FILTER, TRUE},
|
||||
{BASEFILTER_QUERYINTERFACE, SOURCE_FILTER, TRUE},
|
||||
{BASEFILTER_ENUMPINS, SOURCE_FILTER, TRUE},
|
||||
{ENUMPINS_NEXT, SOURCE_FILTER, TRUE},
|
||||
{PIN_QUERYDIRECTION, SOURCE_FILTER, TRUE},
|
||||
{PIN_CONNECTEDTO, SOURCE_FILTER, TRUE},
|
||||
{PIN_QUERYPININFO, SOURCE_FILTER, TRUE},
|
||||
{KSPROPERTYSET_GET, SOURCE_FILTER, TRUE},
|
||||
{ENUMPINS_NEXT, SOURCE_FILTER, TRUE},
|
||||
{PIN_ENUMMEDIATYPES, SOURCE_FILTER, TRUE, FALSE, TRUE},
|
||||
{ENUMMEDIATYPES_NEXT, SOURCE_FILTER, TRUE, FALSE, TRUE},
|
||||
{BASEFILTER_QUERYINTERFACE, SINK_FILTER, FALSE, TRUE},
|
||||
{BASEFILTER_ENUMPINS, SINK_FILTER},
|
||||
{ENUMPINS_NEXT, SINK_FILTER},
|
||||
{PIN_QUERYDIRECTION, SINK_FILTER},
|
||||
{PIN_CONNECTEDTO, SINK_FILTER},
|
||||
{GRAPHBUILDER_CONNECT, NOT_FILTER},
|
||||
{BASEFILTER_GETSTATE, SOURCE_FILTER, TRUE, FALSE, TRUE},
|
||||
{BASEFILTER_ENUMPINS, SOURCE_FILTER, FALSE, FALSE, FALSE, TRUE},
|
||||
{ENUMPINS_NEXT, SOURCE_FILTER, FALSE, FALSE, FALSE, TRUE},
|
||||
{PIN_QUERYDIRECTION, SOURCE_FILTER, FALSE, FALSE, FALSE, TRUE},
|
||||
{PIN_CONNECTEDTO, SOURCE_FILTER, FALSE, FALSE, FALSE, TRUE},
|
||||
{ENUMPINS_NEXT, SOURCE_FILTER, FALSE, FALSE, FALSE, TRUE},
|
||||
{END, NOT_FILTER}
|
||||
}, renderstream_intermediate[] = {
|
||||
{BASEFILTER_QUERYINTERFACE, SOURCE_FILTER},
|
||||
{BASEFILTER_ENUMPINS, SOURCE_FILTER},
|
||||
{ENUMPINS_NEXT, SOURCE_FILTER},
|
||||
{PIN_QUERYDIRECTION, SOURCE_FILTER},
|
||||
{PIN_CONNECTEDTO, SOURCE_FILTER},
|
||||
{PIN_QUERYPININFO, SOURCE_FILTER, TRUE},
|
||||
{KSPROPERTYSET_GET, SOURCE_FILTER},
|
||||
{PIN_ENUMMEDIATYPES, SOURCE_FILTER},
|
||||
{ENUMMEDIATYPES_RESET, SOURCE_FILTER},
|
||||
{ENUMMEDIATYPES_NEXT, SOURCE_FILTER},
|
||||
{BASEFILTER_QUERYINTERFACE, SOURCE_FILTER},
|
||||
{BASEFILTER_ENUMPINS, SOURCE_FILTER},
|
||||
{ENUMPINS_NEXT, SOURCE_FILTER},
|
||||
{PIN_QUERYDIRECTION, SOURCE_FILTER},
|
||||
{PIN_CONNECTEDTO, SOURCE_FILTER},
|
||||
{PIN_QUERYPININFO, SOURCE_FILTER, TRUE},
|
||||
{KSPROPERTYSET_GET, SOURCE_FILTER},
|
||||
{ENUMPINS_NEXT, SOURCE_FILTER},
|
||||
{BASEFILTER_QUERYINTERFACE, SOURCE_FILTER, TRUE},
|
||||
{BASEFILTER_ENUMPINS, SOURCE_FILTER, TRUE},
|
||||
{ENUMPINS_NEXT, SOURCE_FILTER, TRUE},
|
||||
{PIN_QUERYDIRECTION, SOURCE_FILTER, TRUE},
|
||||
{PIN_CONNECTEDTO, SOURCE_FILTER, TRUE},
|
||||
{PIN_QUERYPININFO, SOURCE_FILTER, TRUE},
|
||||
{KSPROPERTYSET_GET, SOURCE_FILTER, TRUE},
|
||||
{ENUMPINS_NEXT, SOURCE_FILTER, TRUE},
|
||||
{BASEFILTER_QUERYINTERFACE, SOURCE_FILTER, TRUE},
|
||||
{BASEFILTER_ENUMPINS, SOURCE_FILTER, TRUE},
|
||||
{ENUMPINS_NEXT, SOURCE_FILTER, TRUE},
|
||||
{PIN_QUERYDIRECTION, SOURCE_FILTER, TRUE},
|
||||
{PIN_CONNECTEDTO, SOURCE_FILTER, TRUE},
|
||||
{PIN_QUERYPININFO, SOURCE_FILTER, TRUE},
|
||||
{KSPROPERTYSET_GET, SOURCE_FILTER, TRUE},
|
||||
{ENUMPINS_NEXT, SOURCE_FILTER, TRUE},
|
||||
{PIN_ENUMMEDIATYPES, SOURCE_FILTER, TRUE, FALSE, TRUE},
|
||||
{ENUMMEDIATYPES_NEXT, SOURCE_FILTER, TRUE, FALSE, TRUE},
|
||||
{BASEFILTER_QUERYINTERFACE, SINK_FILTER, FALSE, TRUE},
|
||||
{BASEFILTER_ENUMPINS, SINK_FILTER},
|
||||
{ENUMPINS_NEXT, SINK_FILTER},
|
||||
{PIN_QUERYDIRECTION, SINK_FILTER},
|
||||
{PIN_CONNECTEDTO, SINK_FILTER},
|
||||
{BASEFILTER_QUERYINTERFACE, INTERMEDIATE_FILTER, FALSE, TRUE},
|
||||
{BASEFILTER_ENUMPINS, INTERMEDIATE_FILTER},
|
||||
{ENUMPINS_NEXT, INTERMEDIATE_FILTER},
|
||||
{PIN_QUERYDIRECTION, INTERMEDIATE_FILTER},
|
||||
{PIN_CONNECTEDTO, INTERMEDIATE_FILTER},
|
||||
{ENUMPINS_NEXT, INTERMEDIATE_FILTER},
|
||||
{PIN_QUERYDIRECTION, INTERMEDIATE_FILTER},
|
||||
{PIN_CONNECTEDTO, INTERMEDIATE_FILTER},
|
||||
{GRAPHBUILDER_CONNECT, NOT_FILTER},
|
||||
{BASEFILTER_QUERYINTERFACE, INTERMEDIATE_FILTER, FALSE, TRUE},
|
||||
{BASEFILTER_ENUMPINS, INTERMEDIATE_FILTER},
|
||||
{ENUMPINS_NEXT, INTERMEDIATE_FILTER},
|
||||
{PIN_QUERYDIRECTION, INTERMEDIATE_FILTER},
|
||||
{PIN_CONNECTEDTO, INTERMEDIATE_FILTER},
|
||||
{GRAPHBUILDER_CONNECT, NOT_FILTER},
|
||||
{BASEFILTER_GETSTATE, SOURCE_FILTER, TRUE, FALSE, TRUE},
|
||||
{BASEFILTER_ENUMPINS, SOURCE_FILTER, FALSE, FALSE, FALSE, TRUE},
|
||||
{ENUMPINS_NEXT, SOURCE_FILTER, FALSE, FALSE, FALSE, TRUE},
|
||||
{PIN_QUERYDIRECTION, SOURCE_FILTER, FALSE, FALSE, FALSE, TRUE},
|
||||
{PIN_CONNECTEDTO, SOURCE_FILTER, FALSE, FALSE, FALSE, TRUE},
|
||||
{ENUMPINS_NEXT, SOURCE_FILTER, FALSE, FALSE, FALSE, TRUE},
|
||||
{END, NOT_FILTER}
|
||||
}, *current_calls_list;
|
||||
} *current_calls_list;
|
||||
int call_no;
|
||||
|
||||
static void check_calls_list(const char *func, call_id id, filter_type type)
|
||||
|
@ -1268,49 +1154,6 @@ static void init_test_filter(test_filter *This, PIN_DIRECTION dir, filter_type t
|
|||
This->filter_type = type;
|
||||
}
|
||||
|
||||
static void test_CaptureGraphBuilder_RenderStream(void)
|
||||
{
|
||||
test_filter source_filter, sink_filter, intermediate_filter;
|
||||
ICaptureGraphBuilder2 *cgb;
|
||||
HRESULT hr;
|
||||
|
||||
init_test_filter(&source_filter, PINDIR_OUTPUT, SOURCE_FILTER);
|
||||
init_test_filter(&sink_filter, PINDIR_INPUT, SINK_FILTER);
|
||||
init_test_filter(&intermediate_filter, PINDIR_OUTPUT, INTERMEDIATE_FILTER);
|
||||
|
||||
hr = CoCreateInstance(&CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER,
|
||||
&IID_ICaptureGraphBuilder2, (void**)&cgb);
|
||||
ok(hr == S_OK || broken(hr == REGDB_E_CLASSNOTREG),
|
||||
"couldn't create CaptureGraphBuilder, hr = %08x\n", hr);
|
||||
if(hr != S_OK) {
|
||||
win_skip("CaptureGraphBuilder is not registered\n");
|
||||
return;
|
||||
}
|
||||
|
||||
hr = ICaptureGraphBuilder2_SetFiltergraph(cgb, &GraphBuilder);
|
||||
ok(hr == S_OK, "SetFiltergraph failed: %08x\n", hr);
|
||||
|
||||
trace("RenderStream with category and mediatype test\n");
|
||||
current_calls_list = renderstream_cat_media;
|
||||
call_no = 0;
|
||||
hr = ICaptureGraphBuilder2_RenderStream(cgb, &PIN_CATEGORY_EDS,
|
||||
&MEDIATYPE_Video, (IUnknown*)&source_filter.IBaseFilter_iface,
|
||||
NULL, &sink_filter.IBaseFilter_iface);
|
||||
ok(hr == S_OK, "RenderStream failed: %08x\n", hr);
|
||||
check_calls_list("test_CaptureGraphBuilder_RenderStream", END, NOT_FILTER);
|
||||
|
||||
trace("RenderStream with intermediate filter\n");
|
||||
current_calls_list = renderstream_intermediate;
|
||||
call_no = 0;
|
||||
hr = ICaptureGraphBuilder2_RenderStream(cgb, &PIN_CATEGORY_EDS,
|
||||
&MEDIATYPE_Video, (IUnknown*)&source_filter.IBaseFilter_iface,
|
||||
&intermediate_filter.IBaseFilter_iface, &sink_filter.IBaseFilter_iface);
|
||||
ok(hr == S_OK, "RenderStream failed: %08x\n", hr);
|
||||
check_calls_list("test_CaptureGraphBuilder_RenderStream", END, NOT_FILTER);
|
||||
|
||||
ICaptureGraphBuilder2_Release(cgb);
|
||||
}
|
||||
|
||||
static void test_AviMux_QueryInterface(void)
|
||||
{
|
||||
IUnknown *avimux, *unk;
|
||||
|
@ -2016,7 +1859,6 @@ START_TEST(qcap)
|
|||
|
||||
arg_c = winetest_get_mainargs(&arg_v);
|
||||
|
||||
test_CaptureGraphBuilder_RenderStream();
|
||||
test_AviMux_QueryInterface();
|
||||
test_AviMux(arg_c>2 ? arg_v[2] : NULL);
|
||||
test_AviCo();
|
||||
|
|
|
@ -602,12 +602,14 @@ typedef struct {
|
|||
IBaseFilter IBaseFilter_iface;
|
||||
LONG ref;
|
||||
IPin IPin_iface;
|
||||
IKsPropertySet IKsPropertySet_iface;
|
||||
CRITICAL_SECTION cs;
|
||||
FILTER_STATE state;
|
||||
IReferenceClock *referenceClock;
|
||||
FILTER_INFO filterInfo;
|
||||
AM_MEDIA_TYPE mediaType;
|
||||
VIDEOINFOHEADER videoInfo;
|
||||
WAVEFORMATEX audioInfo;
|
||||
IPin *connectedTo;
|
||||
IMemInputPin *memInputPin;
|
||||
IMemAllocator *allocator;
|
||||
|
@ -621,9 +623,17 @@ typedef struct {
|
|||
SourceFilter *filter;
|
||||
} SourceEnumPins;
|
||||
|
||||
typedef struct {
|
||||
IEnumMediaTypes IEnumMediaTypes_iface;
|
||||
LONG ref;
|
||||
ULONG index;
|
||||
SourceFilter *filter;
|
||||
} SourceEnumMediaTypes;
|
||||
|
||||
static const WCHAR sourcePinName[] = {'C','a','p','t','u','r','e',0};
|
||||
|
||||
static SourceEnumPins* create_SourceEnumPins(SourceFilter *filter);
|
||||
static SourceEnumMediaTypes* create_SourceEnumMediaTypes(SourceFilter *filter);
|
||||
|
||||
static inline SourceFilter* impl_from_SourceFilter_IBaseFilter(IBaseFilter *iface)
|
||||
{
|
||||
|
@ -635,11 +645,21 @@ static inline SourceFilter* impl_from_SourceFilter_IPin(IPin *iface)
|
|||
return CONTAINING_RECORD(iface, SourceFilter, IPin_iface);
|
||||
}
|
||||
|
||||
static inline SourceFilter* impl_from_SourceFilter_IKsPropertySet(IKsPropertySet *iface)
|
||||
{
|
||||
return CONTAINING_RECORD(iface, SourceFilter, IKsPropertySet_iface);
|
||||
}
|
||||
|
||||
static inline SourceEnumPins* impl_from_SourceFilter_IEnumPins(IEnumPins *iface)
|
||||
{
|
||||
return CONTAINING_RECORD(iface, SourceEnumPins, IEnumPins_iface);
|
||||
}
|
||||
|
||||
static inline SourceEnumMediaTypes* impl_from_SourceFilter_IEnumMediaTypes(IEnumMediaTypes *iface)
|
||||
{
|
||||
return CONTAINING_RECORD(iface, SourceEnumMediaTypes, IEnumMediaTypes_iface);
|
||||
}
|
||||
|
||||
static HRESULT WINAPI SourceFilter_QueryInterface(IBaseFilter *iface, REFIID riid, void **ppv)
|
||||
{
|
||||
SourceFilter *This = impl_from_SourceFilter_IBaseFilter(iface);
|
||||
|
@ -979,6 +999,122 @@ static SourceEnumPins* create_SourceEnumPins(SourceFilter *filter)
|
|||
return This;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI SourceEnumMediaTypes_QueryInterface(IEnumMediaTypes *iface, REFIID riid, void **ppv)
|
||||
{
|
||||
SourceEnumMediaTypes *This = impl_from_SourceFilter_IEnumMediaTypes(iface);
|
||||
if(IsEqualIID(riid, &IID_IUnknown)) {
|
||||
*ppv = &This->IEnumMediaTypes_iface;
|
||||
} else if(IsEqualIID(riid, &IID_IEnumMediaTypes)) {
|
||||
*ppv = &This->IEnumMediaTypes_iface;
|
||||
} else {
|
||||
trace("no interface for %s\n", wine_dbgstr_guid(riid));
|
||||
*ppv = NULL;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
IUnknown_AddRef((IUnknown*)*ppv);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static ULONG WINAPI SourceEnumMediaTypes_AddRef(IEnumMediaTypes *iface)
|
||||
{
|
||||
SourceEnumMediaTypes *This = impl_from_SourceFilter_IEnumMediaTypes(iface);
|
||||
return InterlockedIncrement(&This->ref);
|
||||
}
|
||||
|
||||
static ULONG WINAPI SourceEnumMediaTypes_Release(IEnumMediaTypes *iface)
|
||||
{
|
||||
SourceEnumMediaTypes *This = impl_from_SourceFilter_IEnumMediaTypes(iface);
|
||||
ULONG ref;
|
||||
ref = InterlockedDecrement(&This->ref);
|
||||
if (ref == 0)
|
||||
{
|
||||
IBaseFilter_Release(&This->filter->IBaseFilter_iface);
|
||||
CoTaskMemFree(This);
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI SourceEnumMediaTypes_Next(IEnumMediaTypes *iface, ULONG cMediaTypes, AM_MEDIA_TYPE **ppMediaTypes, ULONG *pcFetched)
|
||||
{
|
||||
SourceEnumMediaTypes *This = impl_from_SourceFilter_IEnumMediaTypes(iface);
|
||||
if (!ppMediaTypes)
|
||||
return E_POINTER;
|
||||
if (cMediaTypes > 1 && !pcFetched)
|
||||
return E_INVALIDARG;
|
||||
if (pcFetched)
|
||||
*pcFetched = 0;
|
||||
if (cMediaTypes == 0)
|
||||
return S_OK;
|
||||
if (This->index == 0) {
|
||||
ppMediaTypes[0] = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
|
||||
if (ppMediaTypes[0]) {
|
||||
*ppMediaTypes[0] = This->filter->mediaType;
|
||||
ppMediaTypes[0]->pbFormat = CoTaskMemAlloc(This->filter->mediaType.cbFormat);
|
||||
if (ppMediaTypes[0]->pbFormat) {
|
||||
memcpy(ppMediaTypes[0]->pbFormat, This->filter->mediaType.pbFormat, This->filter->mediaType.cbFormat);
|
||||
++This->index;
|
||||
if (pcFetched)
|
||||
*pcFetched = 1;
|
||||
return S_OK;
|
||||
}
|
||||
CoTaskMemFree(ppMediaTypes[0]);
|
||||
}
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI SourceEnumMediaTypes_Skip(IEnumMediaTypes *iface, ULONG cMediaTypes)
|
||||
{
|
||||
SourceEnumMediaTypes *This = impl_from_SourceFilter_IEnumMediaTypes(iface);
|
||||
This->index += cMediaTypes;
|
||||
if (This->index >= 1)
|
||||
return S_FALSE;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI SourceEnumMediaTypes_Reset(IEnumMediaTypes *iface)
|
||||
{
|
||||
SourceEnumMediaTypes *This = impl_from_SourceFilter_IEnumMediaTypes(iface);
|
||||
This->index = 0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI SourceEnumMediaTypes_Clone(IEnumMediaTypes *iface, IEnumMediaTypes **ppEnum)
|
||||
{
|
||||
SourceEnumMediaTypes *This = impl_from_SourceFilter_IEnumMediaTypes(iface);
|
||||
SourceEnumMediaTypes *clone = create_SourceEnumMediaTypes(This->filter);
|
||||
if (clone == NULL)
|
||||
return E_OUTOFMEMORY;
|
||||
clone->index = This->index;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static const IEnumMediaTypesVtbl SourceEnumMediaTypesVtbl = {
|
||||
SourceEnumMediaTypes_QueryInterface,
|
||||
SourceEnumMediaTypes_AddRef,
|
||||
SourceEnumMediaTypes_Release,
|
||||
SourceEnumMediaTypes_Next,
|
||||
SourceEnumMediaTypes_Skip,
|
||||
SourceEnumMediaTypes_Reset,
|
||||
SourceEnumMediaTypes_Clone
|
||||
};
|
||||
|
||||
static SourceEnumMediaTypes* create_SourceEnumMediaTypes(SourceFilter *filter)
|
||||
{
|
||||
SourceEnumMediaTypes *This;
|
||||
This = CoTaskMemAlloc(sizeof(*This));
|
||||
if (This == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
This->IEnumMediaTypes_iface.lpVtbl = &SourceEnumMediaTypesVtbl;
|
||||
This->ref = 1;
|
||||
This->index = 0;
|
||||
This->filter = filter;
|
||||
IBaseFilter_AddRef(&filter->IBaseFilter_iface);
|
||||
return This;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI SourcePin_QueryInterface(IPin *iface, REFIID riid, void **ppv)
|
||||
{
|
||||
SourceFilter *This = impl_from_SourceFilter_IPin(iface);
|
||||
|
@ -986,6 +1122,8 @@ static HRESULT WINAPI SourcePin_QueryInterface(IPin *iface, REFIID riid, void **
|
|||
*ppv = &This->IPin_iface;
|
||||
} else if(IsEqualIID(riid, &IID_IPin)) {
|
||||
*ppv = &This->IPin_iface;
|
||||
} else if(IsEqualIID(riid, &IID_IKsPropertySet)) {
|
||||
*ppv = &This->IKsPropertySet_iface;
|
||||
} else {
|
||||
trace("no interface for %s\n", wine_dbgstr_guid(riid));
|
||||
*ppv = NULL;
|
||||
|
@ -1011,6 +1149,7 @@ static HRESULT WINAPI SourcePin_Connect(IPin *iface, IPin *pReceivePin, const AM
|
|||
{
|
||||
SourceFilter *This = impl_from_SourceFilter_IPin(iface);
|
||||
HRESULT hr;
|
||||
|
||||
if (pmt && !IsEqualGUID(&pmt->majortype, &GUID_NULL) && !IsEqualGUID(&pmt->majortype, &MEDIATYPE_Video))
|
||||
return VFW_E_TYPE_NOT_ACCEPTED;
|
||||
if (pmt && !IsEqualGUID(&pmt->subtype, &GUID_NULL) && !IsEqualGUID(&pmt->subtype, &MEDIASUBTYPE_RGB32))
|
||||
|
@ -1168,7 +1307,14 @@ static HRESULT WINAPI SourcePin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *pm
|
|||
|
||||
static HRESULT WINAPI SourcePin_EnumMediaTypes(IPin *iface, IEnumMediaTypes **ppEnum)
|
||||
{
|
||||
return VFW_E_NOT_CONNECTED;
|
||||
SourceFilter *This = impl_from_SourceFilter_IPin(iface);
|
||||
SourceEnumMediaTypes *sourceEnumMediaTypes = create_SourceEnumMediaTypes(This);
|
||||
if (sourceEnumMediaTypes) {
|
||||
*ppEnum = &sourceEnumMediaTypes->IEnumMediaTypes_iface;
|
||||
return S_OK;
|
||||
}
|
||||
else
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI SourcePin_QueryInternalConnections(IPin *iface, IPin **apPin, ULONG *nPin)
|
||||
|
@ -1218,6 +1364,75 @@ static const IPinVtbl SourcePinVtbl = {
|
|||
SourcePin_NewSegment
|
||||
};
|
||||
|
||||
static HRESULT WINAPI SourceKSP_QueryInterface(IKsPropertySet *iface, REFIID riid, LPVOID *ppv)
|
||||
{
|
||||
SourceFilter *This = impl_from_SourceFilter_IKsPropertySet(iface);
|
||||
return IPin_QueryInterface(&This->IPin_iface, riid, ppv);
|
||||
}
|
||||
|
||||
static ULONG WINAPI SourceKSP_AddRef(IKsPropertySet *iface)
|
||||
{
|
||||
SourceFilter *This = impl_from_SourceFilter_IKsPropertySet(iface);
|
||||
return IBaseFilter_AddRef(&This->IBaseFilter_iface);
|
||||
}
|
||||
|
||||
static ULONG WINAPI SourceKSP_Release(IKsPropertySet *iface)
|
||||
{
|
||||
SourceFilter *This = impl_from_SourceFilter_IKsPropertySet(iface);
|
||||
return IBaseFilter_Release(&This->IBaseFilter_iface);
|
||||
}
|
||||
|
||||
static HRESULT WINAPI SourceKSP_Set(IKsPropertySet *iface, REFGUID guidPropSet, DWORD dwPropID,
|
||||
LPVOID pInstanceData, DWORD cbInstanceData, LPVOID pPropData, DWORD cbPropData)
|
||||
{
|
||||
SourceFilter *This = impl_from_SourceFilter_IKsPropertySet(iface);
|
||||
trace("(%p)->(%s, %u, %p, %u, %p, %u): stub\n", This, wine_dbgstr_guid(guidPropSet),
|
||||
dwPropID, pInstanceData, cbInstanceData, pPropData, cbPropData);
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI SourceKSP_Get(IKsPropertySet *iface, REFGUID guidPropSet, DWORD dwPropID,
|
||||
LPVOID pInstanceData, DWORD cbInstanceData, LPVOID pPropData,
|
||||
DWORD cbPropData, DWORD *pcbReturned)
|
||||
{
|
||||
SourceFilter *This = impl_from_SourceFilter_IKsPropertySet(iface);
|
||||
trace("(%p)->(%s, %u, %p, %u, %p, %u, %p)\n", This, wine_dbgstr_guid(guidPropSet),
|
||||
dwPropID, pInstanceData, cbInstanceData, pPropData, cbPropData, pcbReturned);
|
||||
if (IsEqualIID(guidPropSet, &ROPSETID_Pin)) {
|
||||
if (pcbReturned)
|
||||
*pcbReturned = sizeof(GUID);
|
||||
if (pPropData) {
|
||||
LPGUID guid = pPropData;
|
||||
if (cbPropData >= sizeof(GUID))
|
||||
*guid = PIN_CATEGORY_CAPTURE;
|
||||
} else {
|
||||
if (!pcbReturned)
|
||||
return E_POINTER;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
return E_PROP_SET_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI SourceKSP_QuerySupported(IKsPropertySet *iface, REFGUID guidPropSet,
|
||||
DWORD dwPropID, DWORD *pTypeSupport)
|
||||
{
|
||||
SourceFilter *This = impl_from_SourceFilter_IKsPropertySet(iface);
|
||||
trace("(%p)->(%s, %u, %p): stub\n", This, wine_dbgstr_guid(guidPropSet),
|
||||
dwPropID, pTypeSupport);
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
static const IKsPropertySetVtbl SourceKSPVtbl =
|
||||
{
|
||||
SourceKSP_QueryInterface,
|
||||
SourceKSP_AddRef,
|
||||
SourceKSP_Release,
|
||||
SourceKSP_Set,
|
||||
SourceKSP_Get,
|
||||
SourceKSP_QuerySupported
|
||||
};
|
||||
|
||||
static SourceFilter* create_SourceFilter(void)
|
||||
{
|
||||
SourceFilter *This = NULL;
|
||||
|
@ -1227,35 +1442,68 @@ static SourceFilter* create_SourceFilter(void)
|
|||
This->IBaseFilter_iface.lpVtbl = &SourceFilterVtbl;
|
||||
This->ref = 1;
|
||||
This->IPin_iface.lpVtbl = &SourcePinVtbl;
|
||||
This->IKsPropertySet_iface.lpVtbl = &SourceKSPVtbl;
|
||||
InitializeCriticalSection(&This->cs);
|
||||
This->mediaType.majortype = MEDIATYPE_Video;
|
||||
This->mediaType.subtype = MEDIASUBTYPE_RGB32;
|
||||
This->mediaType.bFixedSizeSamples = FALSE;
|
||||
This->mediaType.bTemporalCompression = FALSE;
|
||||
This->mediaType.lSampleSize = 0;
|
||||
This->mediaType.formattype = FORMAT_VideoInfo;
|
||||
This->mediaType.pUnk = NULL;
|
||||
This->mediaType.cbFormat = sizeof(VIDEOINFOHEADER);
|
||||
This->mediaType.pbFormat = (BYTE*) &This->videoInfo;
|
||||
This->videoInfo.dwBitRate = 1000000;
|
||||
This->videoInfo.dwBitErrorRate = 0;
|
||||
This->videoInfo.AvgTimePerFrame = 400000;
|
||||
This->videoInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
This->videoInfo.bmiHeader.biWidth = 10;
|
||||
This->videoInfo.bmiHeader.biHeight = 10;
|
||||
This->videoInfo.bmiHeader.biPlanes = 1;
|
||||
This->videoInfo.bmiHeader.biBitCount = 32;
|
||||
This->videoInfo.bmiHeader.biCompression = BI_RGB;
|
||||
This->videoInfo.bmiHeader.biSizeImage = 0;
|
||||
This->videoInfo.bmiHeader.biXPelsPerMeter = 96;
|
||||
This->videoInfo.bmiHeader.biYPelsPerMeter = 96;
|
||||
This->videoInfo.bmiHeader.biClrUsed = 0;
|
||||
This->videoInfo.bmiHeader.biClrImportant = 0;
|
||||
return This;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static SourceFilter* create_video_SourceFilter(void)
|
||||
{
|
||||
SourceFilter *This = create_SourceFilter();
|
||||
if (!This)
|
||||
return NULL;
|
||||
This->mediaType.majortype = MEDIATYPE_Video;
|
||||
This->mediaType.subtype = MEDIASUBTYPE_RGB32;
|
||||
This->mediaType.bFixedSizeSamples = FALSE;
|
||||
This->mediaType.bTemporalCompression = FALSE;
|
||||
This->mediaType.lSampleSize = 0;
|
||||
This->mediaType.formattype = FORMAT_VideoInfo;
|
||||
This->mediaType.pUnk = NULL;
|
||||
This->mediaType.cbFormat = sizeof(VIDEOINFOHEADER);
|
||||
This->mediaType.pbFormat = (BYTE*) &This->videoInfo;
|
||||
This->videoInfo.dwBitRate = 1000000;
|
||||
This->videoInfo.dwBitErrorRate = 0;
|
||||
This->videoInfo.AvgTimePerFrame = 400000;
|
||||
This->videoInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
This->videoInfo.bmiHeader.biWidth = 10;
|
||||
This->videoInfo.bmiHeader.biHeight = 10;
|
||||
This->videoInfo.bmiHeader.biPlanes = 1;
|
||||
This->videoInfo.bmiHeader.biBitCount = 32;
|
||||
This->videoInfo.bmiHeader.biCompression = BI_RGB;
|
||||
This->videoInfo.bmiHeader.biSizeImage = 0;
|
||||
This->videoInfo.bmiHeader.biXPelsPerMeter = 96;
|
||||
This->videoInfo.bmiHeader.biYPelsPerMeter = 96;
|
||||
This->videoInfo.bmiHeader.biClrUsed = 0;
|
||||
This->videoInfo.bmiHeader.biClrImportant = 0;
|
||||
return This;
|
||||
}
|
||||
|
||||
static SourceFilter* create_audio_SourceFilter(void)
|
||||
{
|
||||
SourceFilter *This = create_SourceFilter();
|
||||
if (!This)
|
||||
return NULL;
|
||||
This->mediaType.majortype = MEDIATYPE_Audio;
|
||||
This->mediaType.subtype = MEDIASUBTYPE_PCM;
|
||||
This->mediaType.bFixedSizeSamples = FALSE;
|
||||
This->mediaType.bTemporalCompression = FALSE;
|
||||
This->mediaType.lSampleSize = 0;
|
||||
This->mediaType.formattype = FORMAT_WaveFormatEx;
|
||||
This->mediaType.pUnk = NULL;
|
||||
This->mediaType.cbFormat = sizeof(WAVEFORMATEX);
|
||||
This->mediaType.pbFormat = (BYTE*) &This->audioInfo;
|
||||
This->audioInfo.wFormatTag = WAVE_FORMAT_PCM;
|
||||
This->audioInfo.nChannels = 1;
|
||||
This->audioInfo.nSamplesPerSec = 8000;
|
||||
This->audioInfo.nAvgBytesPerSec = 16000;
|
||||
This->audioInfo.nBlockAlign = 2;
|
||||
This->audioInfo.wBitsPerSample = 16;
|
||||
This->audioInfo.cbSize = 0;
|
||||
return This;
|
||||
}
|
||||
|
||||
static BOOL has_interface(IUnknown *unknown, REFIID uuid)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
@ -1320,7 +1568,7 @@ static void test_smart_tee_filter_in_graph(IBaseFilter *smartTeeFilter, IPin *in
|
|||
hr = IGraphBuilder_Connect(graphBuilder, previewPin, &previewSinkFilter->IPin_iface);
|
||||
ok(hr == VFW_E_NOT_CONNECTED, "connecting Preview pin without first connecting Input pin returned 0x%08x\n", hr);
|
||||
|
||||
sourceFilter = create_SourceFilter();
|
||||
sourceFilter = create_video_SourceFilter();
|
||||
if (sourceFilter == NULL) {
|
||||
skip("couldn't create source filter\n");
|
||||
goto end;
|
||||
|
@ -1556,7 +1804,7 @@ end:
|
|||
|
||||
static void test_smart_tee_filter_aggregation(void)
|
||||
{
|
||||
SourceFilter *sourceFilter = create_SourceFilter();
|
||||
SourceFilter *sourceFilter = create_video_SourceFilter();
|
||||
if (sourceFilter) {
|
||||
IUnknown *unknown = NULL;
|
||||
HRESULT hr = CoCreateInstance(&CLSID_SmartTee, (IUnknown*)&sourceFilter->IBaseFilter_iface,
|
||||
|
@ -1569,6 +1817,207 @@ static void test_smart_tee_filter_aggregation(void)
|
|||
ok(0, "out of memory allocating SourceFilter for test\n");
|
||||
}
|
||||
|
||||
static HRESULT get_connected_filter_classid(IPin *pin, GUID *guid)
|
||||
{
|
||||
IPin *connectedPin = NULL;
|
||||
PIN_INFO connectedPinInfo;
|
||||
HRESULT hr = IPin_ConnectedTo(pin, &connectedPin);
|
||||
ok(SUCCEEDED(hr), "IPin_ConnectedTo() failed, hr=0x%08x\n", hr);
|
||||
if (FAILED(hr))
|
||||
goto end;
|
||||
hr = IPin_QueryPinInfo(connectedPin, &connectedPinInfo);
|
||||
ok(SUCCEEDED(hr), "IPin_QueryPinInfo() failed, hr=0x%08x\n", hr);
|
||||
if (FAILED(hr))
|
||||
goto end;
|
||||
if (connectedPinInfo.pFilter) {
|
||||
hr = IBaseFilter_GetClassID(connectedPinInfo.pFilter, guid);
|
||||
ok(SUCCEEDED(hr), "IBaseFilter_GetClassID() failed, hr=0x%08x\n", hr);
|
||||
IBaseFilter_Release(connectedPinInfo.pFilter);
|
||||
}
|
||||
end:
|
||||
if (connectedPin)
|
||||
IPin_Release(connectedPin);
|
||||
return hr;
|
||||
}
|
||||
|
||||
static void test_audio_preview(ICaptureGraphBuilder2 *captureGraphBuilder, IGraphBuilder *graphBuilder,
|
||||
SourceFilter *audioSource, IBaseFilter *nullRenderer)
|
||||
{
|
||||
GUID clsid;
|
||||
HRESULT hr = ICaptureGraphBuilder2_RenderStream(captureGraphBuilder, &PIN_CATEGORY_PREVIEW, &MEDIATYPE_Audio,
|
||||
(IUnknown*)&audioSource->IBaseFilter_iface, NULL, nullRenderer);
|
||||
ok(hr == VFW_S_NOPREVIEWPIN, "ICaptureGraphBuilder2_RenderStream() returned hr=0x%08x\n", hr);
|
||||
hr = get_connected_filter_classid(&audioSource->IPin_iface, &clsid);
|
||||
if (FAILED(hr))
|
||||
return;
|
||||
ok(IsEqualIID(&clsid, &CLSID_SmartTee), "unexpected connected filter %s\n",
|
||||
wine_dbgstr_guid(&clsid));
|
||||
}
|
||||
|
||||
static void test_audio_capture(ICaptureGraphBuilder2 *captureGraphBuilder, IGraphBuilder *graphBuilder,
|
||||
SourceFilter *audioSource, IBaseFilter *nullRenderer)
|
||||
{
|
||||
GUID clsid;
|
||||
HRESULT hr = ICaptureGraphBuilder2_RenderStream(captureGraphBuilder, &PIN_CATEGORY_CAPTURE, &MEDIATYPE_Audio,
|
||||
(IUnknown*)&audioSource->IBaseFilter_iface, NULL, nullRenderer);
|
||||
ok(hr == S_OK, "ICaptureGraphBuilder2_RenderStream() returned hr=0x%08x\n", hr);
|
||||
hr = get_connected_filter_classid(&audioSource->IPin_iface, &clsid);
|
||||
if (FAILED(hr))
|
||||
return;
|
||||
ok(IsEqualIID(&clsid, &CLSID_SmartTee), "unexpected connected filter %s\n",
|
||||
wine_dbgstr_guid(&clsid));
|
||||
}
|
||||
|
||||
static void test_video_preview(ICaptureGraphBuilder2 *captureGraphBuilder, IGraphBuilder *graphBuilder,
|
||||
SourceFilter *videoSource, IBaseFilter *nullRenderer)
|
||||
{
|
||||
GUID clsid;
|
||||
HRESULT hr = ICaptureGraphBuilder2_RenderStream(captureGraphBuilder, &PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video,
|
||||
(IUnknown*)&videoSource->IBaseFilter_iface, NULL, nullRenderer);
|
||||
ok(hr == VFW_S_NOPREVIEWPIN, "ICaptureGraphBuilder2_RenderStream() failed, hr=0x%08x\n", hr);
|
||||
hr = get_connected_filter_classid(&videoSource->IPin_iface, &clsid);
|
||||
if (FAILED(hr))
|
||||
return;
|
||||
ok(IsEqualIID(&clsid, &CLSID_SmartTee), "unexpected connected filter %s\n",
|
||||
wine_dbgstr_guid(&clsid));
|
||||
}
|
||||
|
||||
static void test_video_capture(ICaptureGraphBuilder2 *captureGraphBuilder, IGraphBuilder *graphBuilder,
|
||||
SourceFilter *videoSource, IBaseFilter *nullRenderer)
|
||||
{
|
||||
GUID clsid;
|
||||
HRESULT hr = ICaptureGraphBuilder2_RenderStream(captureGraphBuilder, &PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
|
||||
(IUnknown*)&videoSource->IBaseFilter_iface, NULL, nullRenderer);
|
||||
ok(hr == S_OK, "ICaptureGraphBuilder2_RenderStream() failed, hr=0x%08x\n", hr);
|
||||
hr = get_connected_filter_classid(&videoSource->IPin_iface, &clsid);
|
||||
if (FAILED(hr))
|
||||
return;
|
||||
ok(IsEqualIID(&clsid, &CLSID_SmartTee), "unexpected connected filter %s\n",
|
||||
wine_dbgstr_guid(&clsid));
|
||||
}
|
||||
|
||||
static void test_audio_smart_tee_filter_auto_insertion(
|
||||
void (*test_function)(ICaptureGraphBuilder2 *cgb, IGraphBuilder *gb,
|
||||
SourceFilter *audioSource, IBaseFilter *nullRenderer))
|
||||
{
|
||||
HRESULT hr;
|
||||
ICaptureGraphBuilder2 *captureGraphBuilder = NULL;
|
||||
IGraphBuilder *graphBuilder = NULL;
|
||||
IBaseFilter *nullRenderer = NULL;
|
||||
SourceFilter *audioSource = NULL;
|
||||
|
||||
hr = CoCreateInstance(&CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER,
|
||||
&IID_ICaptureGraphBuilder2, (void**)&captureGraphBuilder);
|
||||
ok(SUCCEEDED(hr), "couldn't create capture graph builder, hr=0x%08x\n", hr);
|
||||
if (FAILED(hr))
|
||||
goto end;
|
||||
|
||||
hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IGraphBuilder,
|
||||
(LPVOID*)&graphBuilder);
|
||||
ok(SUCCEEDED(hr), "couldn't create graph builder, hr=0x%08x\n", hr);
|
||||
if (FAILED(hr))
|
||||
goto end;
|
||||
|
||||
hr = ICaptureGraphBuilder2_SetFiltergraph(captureGraphBuilder, graphBuilder);
|
||||
ok(SUCCEEDED(hr), "ICaptureGraphBuilder2_SetFilterGraph() failed, hr=0x%08x\n", hr);
|
||||
if (FAILED(hr))
|
||||
goto end;
|
||||
|
||||
hr = CoCreateInstance(&CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER,
|
||||
&IID_IBaseFilter, (LPVOID*)&nullRenderer);
|
||||
ok(SUCCEEDED(hr) ||
|
||||
/* Windows 2008: http://stackoverflow.com/questions/29410348/initialize-nullrender-failed-with-error-regdb-e-classnotreg-on-win2008-r2 */
|
||||
broken(hr == REGDB_E_CLASSNOTREG), "couldn't create NullRenderer, hr=0x%08x\n", hr);
|
||||
if (FAILED(hr))
|
||||
goto end;
|
||||
hr = IGraphBuilder_AddFilter(graphBuilder, nullRenderer, NULL);
|
||||
ok(SUCCEEDED(hr), "IGraphBuilder_AddFilter() failed, hr=0x%08x\n", hr);
|
||||
if (FAILED(hr))
|
||||
goto end;
|
||||
|
||||
audioSource = create_audio_SourceFilter();
|
||||
ok(audioSource != NULL, "couldn't create audio source\n");
|
||||
if (audioSource == NULL)
|
||||
goto end;
|
||||
hr = IGraphBuilder_AddFilter(graphBuilder, &audioSource->IBaseFilter_iface, NULL);
|
||||
ok(SUCCEEDED(hr), "IGraphBuilder_AddFilter() failed, hr=0x%08x\n", hr);
|
||||
if (FAILED(hr))
|
||||
goto end;
|
||||
|
||||
test_function(captureGraphBuilder, graphBuilder, audioSource, nullRenderer);
|
||||
|
||||
end:
|
||||
if (nullRenderer)
|
||||
IBaseFilter_Release(nullRenderer);
|
||||
if (audioSource)
|
||||
IBaseFilter_Release(&audioSource->IBaseFilter_iface);
|
||||
if (captureGraphBuilder)
|
||||
ICaptureGraphBuilder2_Release(captureGraphBuilder);
|
||||
if (graphBuilder)
|
||||
IGraphBuilder_Release(graphBuilder);
|
||||
}
|
||||
|
||||
static void test_video_smart_tee_filter_auto_insertion(
|
||||
void (*test_function)(ICaptureGraphBuilder2 *cgb, IGraphBuilder *gb,
|
||||
SourceFilter *videoSource, IBaseFilter *nullRenderer))
|
||||
{
|
||||
HRESULT hr;
|
||||
ICaptureGraphBuilder2 *captureGraphBuilder = NULL;
|
||||
IGraphBuilder *graphBuilder = NULL;
|
||||
IBaseFilter *nullRenderer = NULL;
|
||||
SourceFilter *videoSource = NULL;
|
||||
|
||||
hr = CoCreateInstance(&CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER,
|
||||
&IID_ICaptureGraphBuilder2, (void**)&captureGraphBuilder);
|
||||
ok(SUCCEEDED(hr), "couldn't create capture graph builder, hr=0x%08x\n", hr);
|
||||
if (FAILED(hr))
|
||||
goto end;
|
||||
|
||||
hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IGraphBuilder,
|
||||
(LPVOID*)&graphBuilder);
|
||||
ok(SUCCEEDED(hr), "couldn't create graph builder, hr=0x%08x\n", hr);
|
||||
if (FAILED(hr))
|
||||
goto end;
|
||||
|
||||
hr = ICaptureGraphBuilder2_SetFiltergraph(captureGraphBuilder, graphBuilder);
|
||||
ok(SUCCEEDED(hr), "ICaptureGraphBuilder2_SetFilterGraph() failed, hr=0x%08x\n", hr);
|
||||
if (FAILED(hr))
|
||||
goto end;
|
||||
|
||||
hr = CoCreateInstance(&CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER,
|
||||
&IID_IBaseFilter, (LPVOID*)&nullRenderer);
|
||||
ok(SUCCEEDED(hr) ||
|
||||
/* Windows 2008: http://stackoverflow.com/questions/29410348/initialize-nullrender-failed-with-error-regdb-e-classnotreg-on-win2008-r2 */
|
||||
broken(hr == REGDB_E_CLASSNOTREG), "couldn't create NullRenderer, hr=0x%08x\n", hr);
|
||||
if (FAILED(hr))
|
||||
goto end;
|
||||
hr = IGraphBuilder_AddFilter(graphBuilder, nullRenderer, NULL);
|
||||
ok(SUCCEEDED(hr), "IGraphBuilder_AddFilter() failed, hr=0x%08x\n", hr);
|
||||
if (FAILED(hr))
|
||||
goto end;
|
||||
|
||||
videoSource = create_video_SourceFilter();
|
||||
ok(videoSource != NULL, "couldn't create audio source\n");
|
||||
if (videoSource == NULL)
|
||||
goto end;
|
||||
hr = IGraphBuilder_AddFilter(graphBuilder, &videoSource->IBaseFilter_iface, NULL);
|
||||
ok(SUCCEEDED(hr), "IGraphBuilder_AddFilter() failed, hr=0x%08x\n", hr);
|
||||
if (FAILED(hr))
|
||||
goto end;
|
||||
|
||||
test_function(captureGraphBuilder, graphBuilder, videoSource, nullRenderer);
|
||||
|
||||
end:
|
||||
if (nullRenderer)
|
||||
IBaseFilter_Release(nullRenderer);
|
||||
if (videoSource)
|
||||
IBaseFilter_Release(&videoSource->IBaseFilter_iface);
|
||||
if (captureGraphBuilder)
|
||||
ICaptureGraphBuilder2_Release(captureGraphBuilder);
|
||||
if (graphBuilder)
|
||||
IGraphBuilder_Release(graphBuilder);
|
||||
}
|
||||
|
||||
START_TEST(smartteefilter)
|
||||
{
|
||||
if (SUCCEEDED(CoInitialize(NULL)))
|
||||
|
@ -1577,6 +2026,13 @@ START_TEST(smartteefilter)
|
|||
if (event) {
|
||||
test_smart_tee_filter_aggregation();
|
||||
test_smart_tee_filter();
|
||||
|
||||
test_audio_smart_tee_filter_auto_insertion(test_audio_preview);
|
||||
test_audio_smart_tee_filter_auto_insertion(test_audio_capture);
|
||||
|
||||
test_video_smart_tee_filter_auto_insertion(test_video_preview);
|
||||
test_video_smart_tee_filter_auto_insertion(test_video_capture);
|
||||
|
||||
CloseHandle(event);
|
||||
} else
|
||||
skip("CreateEvent failed, error=%u\n", GetLastError());
|
||||
|
|
Loading…
Reference in New Issue