From 4457717d797cc3354b82c572219a458419e847c0 Mon Sep 17 00:00:00 2001 From: Paul Chitescu Date: Fri, 12 Feb 2010 21:09:02 +0200 Subject: [PATCH] qedit: Add pins, IMemInputPin implementation and grabbing to SampleGrabber. --- dlls/qedit/samplegrabber.c | 565 ++++++++++++++++++++++++++++++++++++- 1 file changed, 559 insertions(+), 6 deletions(-) diff --git a/dlls/qedit/samplegrabber.c b/dlls/qedit/samplegrabber.c index 4e80f24f4d5..f30543c1347 100644 --- a/dlls/qedit/samplegrabber.c +++ b/dlls/qedit/samplegrabber.c @@ -33,19 +33,43 @@ WINE_DEFAULT_DEBUG_CHANNEL(qedit); static WCHAR const vendor_name[] = { 'W', 'i', 'n', 'e', 0 }; +static WCHAR const pin_in_name[] = { 'I', 'n', 0 }; +static WCHAR const pin_out_name[] = { 'O', 'u', 't', 0 }; + +/* Sample Grabber pin implementation */ +typedef struct _SG_Pin { + const IPinVtbl* lpVtbl; + PIN_DIRECTION dir; + WCHAR const *name; + struct _SG_Impl *sg; + IPin *pair; +} SG_Pin; /* Sample Grabber filter implementation */ typedef struct _SG_Impl { const IBaseFilterVtbl* IBaseFilter_Vtbl; const ISampleGrabberVtbl* ISampleGrabber_Vtbl; + const IMemInputPinVtbl* IMemInputPin_Vtbl; /* TODO: IMediaPosition, IMediaSeeking, IQualityControl */ LONG refCount; FILTER_INFO info; FILTER_STATE state; + SG_Pin pin_in; + SG_Pin pin_out; IMemAllocator *allocator; IReferenceClock *refClock; + IMemInputPin *memOutput; + ISampleGrabberCB *grabberIface; + LONG grabberMethod; + LONG oneShot; } SG_Impl; +enum { + OneShot_None, + OneShot_Wait, + OneShot_Past, +}; + /* Get the SampleGrabber implementation This pointer from various interface pointers */ static inline SG_Impl *impl_from_IBaseFilter(IBaseFilter *iface) { @@ -57,6 +81,11 @@ static inline SG_Impl *impl_from_ISampleGrabber(ISampleGrabber *iface) return (SG_Impl *)((char*)iface - FIELD_OFFSET(SG_Impl, ISampleGrabber_Vtbl)); } +static inline SG_Impl *impl_from_IMemInputPin(IMemInputPin *iface) +{ + return (SG_Impl *)((char*)iface - FIELD_OFFSET(SG_Impl, IMemInputPin_Vtbl)); +} + /* Cleanup at end of life */ static void SampleGrabber_cleanup(SG_Impl *This) @@ -68,6 +97,10 @@ static void SampleGrabber_cleanup(SG_Impl *This) IMemAllocator_Release(This->allocator); if (This->refClock) IReferenceClock_Release(This->refClock); + if (This->memOutput) + IMemInputPin_Release(This->memOutput); + if (This->grabberIface) + ISampleGrabberCB_Release(This->grabberIface); } /* Common helper AddRef called from all interfaces */ @@ -110,8 +143,11 @@ static HRESULT SampleGrabber_query(SG_Impl *This, REFIID riid, void **ppvObject) *ppvObject = &(This->ISampleGrabber_Vtbl); return S_OK; } - else if (IsEqualIID(riid, &IID_IMemInputPin)) - FIXME("IMemInputPin not implemented\n"); + else if (IsEqualIID(riid, &IID_IMemInputPin)) { + SampleGrabber_addref(This); + *ppvObject = &(This->IMemInputPin_Vtbl); + return S_OK; + } else if (IsEqualIID(riid, &IID_IMediaPosition)) FIXME("IMediaPosition not implemented\n"); else if (IsEqualIID(riid, &IID_IMediaSeeking)) @@ -123,6 +159,45 @@ static HRESULT SampleGrabber_query(SG_Impl *This, REFIID riid, void **ppvObject) return E_NOINTERFACE; } +/* Helper that calls installed sample callbacks */ +static void SampleGrabber_callback(SG_Impl *This, IMediaSample *sample) +{ + double time = 0.0; + REFERENCE_TIME tStart, tEnd; + if (SUCCEEDED(IMediaSample_GetTime(sample, &tStart, &tEnd))) + time = 1e-7 * tStart; + switch (This->grabberMethod) { + case 0: + { + ULONG ref = IMediaSample_AddRef(sample); + ISampleGrabberCB_SampleCB(This->grabberIface, time, sample); + ref = IMediaSample_Release(sample) + 1 - ref; + if (ref) + { + ERR("(%p) Callback referenced sample %p by %u\n", This, sample, ref); + /* ugly as hell but some apps are sooo buggy */ + while (ref--) + IMediaSample_Release(sample); + } + } + break; + case 1: + { + BYTE *data = 0; + long size = IMediaSample_GetActualDataLength(sample); + if (size && SUCCEEDED(IMediaSample_GetPointer(sample, &data)) && data) + ISampleGrabberCB_BufferCB(This->grabberIface, time, data, size); + } + break; + case -1: + break; + default: + FIXME("unsupported method %ld\n", (long int)This->grabberMethod); + /* do not bother us again */ + This->grabberMethod = -1; + } +} + /* SampleGrabber implementation of IBaseFilter interface */ @@ -247,9 +322,21 @@ static HRESULT WINAPI SampleGrabber_IBaseFilter_FindPin(IBaseFilter *iface, LPCWSTR id, IPin **pin) { SG_Impl *This = impl_from_IBaseFilter(iface); - FIXME("(%p)->(%s, %p): stub\n", This, debugstr_w(id), pin); + TRACE("(%p)->(%s, %p)\n", This, debugstr_w(id), pin); if (!id || !pin) return E_POINTER; + if (!lstrcmpiW(id,pin_in_name)) + { + SampleGrabber_addref(This); + *pin = (IPin*)&(This->pin_in.lpVtbl); + return S_OK; + } + else if (!lstrcmpiW(id,pin_out_name)) + { + SampleGrabber_addref(This); + *pin = (IPin*)&(This->pin_out.lpVtbl); + return S_OK; + } *pin = NULL; return VFW_E_NOT_FOUND; } @@ -277,6 +364,7 @@ SampleGrabber_IBaseFilter_JoinFilterGraph(IBaseFilter *iface, IFilterGraph *grap This->info.pGraph = graph; if (name) lstrcpynW(This->info.achName,name,MAX_FILTER_NAME); + This->oneShot = OneShot_None; return S_OK; } @@ -321,8 +409,9 @@ static HRESULT WINAPI SampleGrabber_ISampleGrabber_SetOneShot(ISampleGrabber *iface, BOOL oneShot) { SG_Impl *This = impl_from_ISampleGrabber(iface); - FIXME("(%p)->(%u): stub\n", This, oneShot); - return E_NOTIMPL; + TRACE("(%p)->(%u)\n", This, oneShot); + This->oneShot = oneShot ? OneShot_Wait : OneShot_None; + return S_OK; } /* ISampleGrabber */ @@ -383,10 +472,402 @@ static HRESULT WINAPI SampleGrabber_ISampleGrabber_SetCallback(ISampleGrabber *iface, ISampleGrabberCB *cb, LONG whichMethod) { SG_Impl *This = impl_from_ISampleGrabber(iface); - FIXME("(%p)->(%p, %u): stub\n", This, cb, whichMethod); + TRACE("(%p)->(%p, %u)\n", This, cb, whichMethod); + if (This->grabberIface) + ISampleGrabberCB_Release(This->grabberIface); + This->grabberIface = cb; + This->grabberMethod = whichMethod; + if (cb) + ISampleGrabberCB_AddRef(cb); + return S_OK; +} + + +/* SampleGrabber implementation of IMemInputPin interface */ + +/* IUnknown */ +static HRESULT WINAPI +SampleGrabber_IMemInputPin_QueryInterface(IMemInputPin *iface, REFIID riid, void **ppvObject) +{ + return SampleGrabber_query(impl_from_IMemInputPin(iface), riid, ppvObject); +} + +/* IUnknown */ +static ULONG WINAPI +SampleGrabber_IMemInputPin_AddRef(IMemInputPin *iface) +{ + return SampleGrabber_addref(impl_from_IMemInputPin(iface)); +} + +/* IUnknown */ +static ULONG WINAPI +SampleGrabber_IMemInputPin_Release(IMemInputPin *iface) +{ + return SampleGrabber_release(impl_from_IMemInputPin(iface)); +} + +/* IMemInputPin */ +static HRESULT WINAPI +SampleGrabber_IMemInputPin_GetAllocator(IMemInputPin *iface, IMemAllocator **allocator) +{ + SG_Impl *This = impl_from_IMemInputPin(iface); + TRACE("(%p)->(%p) allocator = %p\n", This, allocator, This->allocator); + if (!allocator) + return E_POINTER; + *allocator = This->allocator; + if (!*allocator) + return VFW_E_NO_ALLOCATOR; + IMemAllocator_AddRef(*allocator); + return S_OK; +} + +/* IMemInputPin */ +static HRESULT WINAPI +SampleGrabber_IMemInputPin_NotifyAllocator(IMemInputPin *iface, IMemAllocator *allocator, BOOL readOnly) +{ + SG_Impl *This = impl_from_IMemInputPin(iface); + TRACE("(%p)->(%p, %u) allocator = %p\n", This, allocator, readOnly, This->allocator); + if (This->allocator == allocator) + return S_OK; + if (This->allocator) + IMemAllocator_Release(This->allocator); + This->allocator = allocator; + if (allocator) + IMemAllocator_AddRef(allocator); + return S_OK; +} + +/* IMemInputPin */ +static HRESULT WINAPI +SampleGrabber_IMemInputPin_GetAllocatorRequirements(IMemInputPin *iface, ALLOCATOR_PROPERTIES *props) +{ + SG_Impl *This = impl_from_IMemInputPin(iface); + FIXME("(%p)->(%p): semi-stub\n", This, props); + if (!props) + return E_POINTER; + return This->memOutput ? IMemInputPin_GetAllocatorRequirements(This->memOutput, props) : E_NOTIMPL; +} + +/* IMemInputPin */ +static HRESULT WINAPI +SampleGrabber_IMemInputPin_Receive(IMemInputPin *iface, IMediaSample *sample) +{ + SG_Impl *This = impl_from_IMemInputPin(iface); + HRESULT hr; + TRACE("(%p)->(%p) output = %p, grabber = %p\n", This, sample, This->memOutput, This->grabberIface); + if (!sample) + return E_POINTER; + if ((This->state != State_Running) || (This->oneShot == OneShot_Past)) + return S_FALSE; + if (This->grabberIface) + SampleGrabber_callback(This, sample); + hr = This->memOutput ? IMemInputPin_Receive(This->memOutput, sample) : S_OK; + if (This->oneShot == OneShot_Wait) { + This->oneShot = OneShot_Past; + hr = S_FALSE; + if (This->pin_out.pair) + IPin_EndOfStream(This->pin_out.pair); + } + return hr; +} + +/* IMemInputPin */ +static HRESULT WINAPI +SampleGrabber_IMemInputPin_ReceiveMultiple(IMemInputPin *iface, IMediaSample **samples, LONG nSamples, LONG *nProcessed) +{ + SG_Impl *This = impl_from_IMemInputPin(iface); + TRACE("(%p)->(%p, %u, %p) output = %p, grabber = %p\n", This, samples, nSamples, nProcessed, This->memOutput, This->grabberIface); + if (!samples || !nProcessed) + return E_POINTER; + if ((This->state != State_Running) || (This->oneShot == OneShot_Past)) + return S_FALSE; + if (This->grabberIface) { + LONG idx; + for (idx = 0; idx < nSamples; idx++) + SampleGrabber_callback(This, samples[idx]); + } + return This->memOutput ? IMemInputPin_ReceiveMultiple(This->memOutput, samples, nSamples, nProcessed) : S_OK; +} + +/* IMemInputPin */ +static HRESULT WINAPI +SampleGrabber_IMemInputPin_ReceiveCanBlock(IMemInputPin *iface) +{ + SG_Impl *This = impl_from_IMemInputPin(iface); + TRACE("(%p)\n", This); + return This->memOutput ? IMemInputPin_ReceiveCanBlock(This->memOutput) : S_OK; +} + + +/* SampleGrabber member pin implementation */ + +/* IUnknown */ +static ULONG WINAPI +SampleGrabber_IPin_AddRef(IPin *iface) +{ + return SampleGrabber_addref(((SG_Pin *)iface)->sg); +} + +/* IUnknown */ +static ULONG WINAPI +SampleGrabber_IPin_Release(IPin *iface) +{ + return SampleGrabber_release(((SG_Pin *)iface)->sg); +} + +/* IUnknown */ +static HRESULT WINAPI +SampleGrabber_IPin_QueryInterface(IPin *iface, REFIID riid, void **ppvObject) +{ + SG_Pin *This = (SG_Pin *)iface; + TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject); + + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IPin)) { + SampleGrabber_addref(This->sg); + *ppvObject = This; + return S_OK; + } + else if (IsEqualIID(riid, &IID_IMemInputPin)) { + SampleGrabber_addref(This->sg); + *ppvObject = &(This->sg->IMemInputPin_Vtbl); + return S_OK; + } + *ppvObject = NULL; + WARN("(%p, %s,%p): not found\n", This, debugstr_guid(riid), ppvObject); + return E_NOINTERFACE; +} + +/* IPin - input pin */ +static HRESULT WINAPI +SampleGrabber_In_IPin_Connect(IPin *iface, IPin *receiver, const AM_MEDIA_TYPE *mtype) +{ + WARN("(%p, %p): unexpected\n", receiver, mtype); + return E_UNEXPECTED; +} + +/* IPin - output pin */ +static HRESULT WINAPI +SampleGrabber_Out_IPin_Connect(IPin *iface, IPin *receiver, const AM_MEDIA_TYPE *type) +{ + SG_Pin *This = (SG_Pin *)iface; + TRACE("(%p)->(%p, %p)\n", This, receiver, type); + if (!receiver) + return E_POINTER; + if (This->pair) + return VFW_E_ALREADY_CONNECTED; + if (This->sg->state != State_Stopped) + return VFW_E_NOT_STOPPED; + return VFW_E_TYPE_NOT_ACCEPTED; +} + +/* IPin - input pin */ +static HRESULT WINAPI +SampleGrabber_In_IPin_ReceiveConnection(IPin *iface, IPin *connector, const AM_MEDIA_TYPE *type) +{ + SG_Pin *This = (SG_Pin *)iface; + TRACE("(%p)->(%p, %p)\n", This, connector, type); + if (!connector) + return E_POINTER; + if (This->pair) + return VFW_E_ALREADY_CONNECTED; + if (This->sg->state != State_Stopped) + return VFW_E_NOT_STOPPED; + return VFW_E_TYPE_NOT_ACCEPTED; +} + +/* IPin - output pin */ +static HRESULT WINAPI +SampleGrabber_Out_IPin_ReceiveConnection(IPin *iface, IPin *connector, const AM_MEDIA_TYPE *mtype) +{ + WARN("(%p, %p): unexpected\n", connector, mtype); + return E_UNEXPECTED; +} + +/* IPin - input pin */ +static HRESULT WINAPI +SampleGrabber_In_IPin_Disconnect(IPin *iface) +{ + SG_Pin *This = (SG_Pin *)iface; + TRACE("(%p)->() pair = %p\n", This, This->pair); + if (This->sg->state != State_Stopped) + return VFW_E_NOT_STOPPED; + if (This->pair) { + This->pair = NULL; + return S_OK; + } + return S_FALSE; +} + +/* IPin - output pin */ +static HRESULT WINAPI +SampleGrabber_Out_IPin_Disconnect(IPin *iface) +{ + SG_Pin *This = (SG_Pin *)iface; + TRACE("(%p)->() pair = %p\n", This, This->pair); + if (This->sg->state != State_Stopped) + return VFW_E_NOT_STOPPED; + if (This->pair) { + This->pair = NULL; + if (This->sg->memOutput) { + IMemInputPin_Release(This->sg->memOutput); + This->sg->memOutput = NULL; + } + return S_OK; + } + return S_FALSE; +} + +/* IPin */ +static HRESULT WINAPI +SampleGrabber_IPin_ConnectedTo(IPin *iface, IPin **pin) +{ + SG_Pin *This = (SG_Pin *)iface; + TRACE("(%p)->(%p) pair = %p\n", This, pin, This->pair); + if (!pin) + return E_POINTER; + *pin = This->pair; + if (*pin) { + IPin_AddRef(*pin); + return S_OK; + } + return VFW_E_NOT_CONNECTED; +} + +/* IPin */ +static HRESULT WINAPI +SampleGrabber_IPin_ConnectionMediaType(IPin *iface, AM_MEDIA_TYPE *mtype) +{ + SG_Pin *This = (SG_Pin *)iface; + TRACE("(%p)->(%p)\n", This, mtype); + if (!mtype) + return E_POINTER; + return VFW_E_NOT_CONNECTED; +} + +/* IPin */ +static HRESULT WINAPI +SampleGrabber_IPin_QueryPinInfo(IPin *iface, PIN_INFO *info) +{ + SG_Pin *This = (SG_Pin *)iface; + TRACE("(%p)->(%p)\n", This, info); + if (!info) + return E_POINTER; + SampleGrabber_addref(This->sg); + info->pFilter = (IBaseFilter *)This->sg; + info->dir = This->dir; + lstrcpynW(info->achName,This->name,MAX_PIN_NAME); + return S_OK; +} + +/* IPin */ +static HRESULT WINAPI +SampleGrabber_IPin_QueryDirection(IPin *iface, PIN_DIRECTION *dir) +{ + SG_Pin *This = (SG_Pin *)iface; + TRACE("(%p)->(%p)\n", This, dir); + if (!dir) + return E_POINTER; + *dir = This->dir; + return S_OK; +} + +/* IPin */ +static HRESULT WINAPI +SampleGrabber_IPin_QueryId(IPin *iface, LPWSTR *id) +{ + SG_Pin *This = (SG_Pin *)iface; + int len; + TRACE("(%p)->(%p)\n", This, id); + if (!id) + return E_POINTER; + len = sizeof(WCHAR)*(1+lstrlenW(This->name)); + *id = CoTaskMemAlloc(len); + CopyMemory(*id, This->name, len); + return S_OK; +} + +/* IPin */ +static HRESULT WINAPI +SampleGrabber_IPin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *mtype) +{ + TRACE("(%p)\n", mtype); + return S_OK; +} + +/* IPin */ +static HRESULT WINAPI +SampleGrabber_IPin_EnumMediaTypes(IPin *iface, IEnumMediaTypes **mtypes) +{ + SG_Pin *This = (SG_Pin *)iface; + FIXME("(%p)->(%p): stub\n", This, mtypes); + if (!mtypes) + return E_POINTER; + return E_OUTOFMEMORY; +} + +/* IPin - input pin */ +static HRESULT WINAPI +SampleGrabber_In_IPin_QueryInternalConnections(IPin *iface, IPin **pins, ULONG *nPins) +{ + SG_Pin *This = (SG_Pin *)iface; + TRACE("(%p)->(%p, %p) size = %u\n", This, pins, nPins, (nPins ? *nPins : 0)); + if (!nPins) + return E_POINTER; + if (*nPins) { + if (!pins) + return E_POINTER; + IPin_AddRef((IPin*)&This->sg->pin_out.lpVtbl); + *pins = (IPin*)&This->sg->pin_out.lpVtbl; + *nPins = 1; + return S_OK; + } + *nPins = 1; + return S_FALSE; +} + +/* IPin - output pin */ +static HRESULT WINAPI +SampleGrabber_Out_IPin_QueryInternalConnections(IPin *iface, IPin **pins, ULONG *nPins) +{ + WARN("(%p, %p): unexpected\n", pins, nPins); + if (nPins) + *nPins = 0; return E_NOTIMPL; } +/* IPin */ +static HRESULT WINAPI +SampleGrabber_IPin_EndOfStream(IPin *iface) +{ + FIXME(": stub\n"); + return S_OK; +} + +/* IPin */ +static HRESULT WINAPI +SampleGrabber_IPin_BeginFlush(IPin *iface) +{ + FIXME(": stub\n"); + return S_OK; +} + +/* IPin */ +static HRESULT WINAPI +SampleGrabber_IPin_EndFlush(IPin *iface) +{ + FIXME(": stub\n"); + return S_OK; +} + +/* IPin */ +static HRESULT WINAPI +SampleGrabber_IPin_NewSegment(IPin *iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double rate) +{ + FIXME(": stub\n"); + return S_OK; +} + /* SampleGrabber vtables and constructor */ @@ -423,6 +904,63 @@ static const ISampleGrabberVtbl ISampleGrabber_VTable = SampleGrabber_ISampleGrabber_SetCallback, }; +static const IMemInputPinVtbl IMemInputPin_VTable = +{ + SampleGrabber_IMemInputPin_QueryInterface, + SampleGrabber_IMemInputPin_AddRef, + SampleGrabber_IMemInputPin_Release, + SampleGrabber_IMemInputPin_GetAllocator, + SampleGrabber_IMemInputPin_NotifyAllocator, + SampleGrabber_IMemInputPin_GetAllocatorRequirements, + SampleGrabber_IMemInputPin_Receive, + SampleGrabber_IMemInputPin_ReceiveMultiple, + SampleGrabber_IMemInputPin_ReceiveCanBlock, +}; + +static const IPinVtbl IPin_In_VTable = +{ + SampleGrabber_IPin_QueryInterface, + SampleGrabber_IPin_AddRef, + SampleGrabber_IPin_Release, + SampleGrabber_In_IPin_Connect, + SampleGrabber_In_IPin_ReceiveConnection, + SampleGrabber_In_IPin_Disconnect, + SampleGrabber_IPin_ConnectedTo, + SampleGrabber_IPin_ConnectionMediaType, + SampleGrabber_IPin_QueryPinInfo, + SampleGrabber_IPin_QueryDirection, + SampleGrabber_IPin_QueryId, + SampleGrabber_IPin_QueryAccept, + SampleGrabber_IPin_EnumMediaTypes, + SampleGrabber_In_IPin_QueryInternalConnections, + SampleGrabber_IPin_EndOfStream, + SampleGrabber_IPin_BeginFlush, + SampleGrabber_IPin_EndFlush, + SampleGrabber_IPin_NewSegment, +}; + +static const IPinVtbl IPin_Out_VTable = +{ + SampleGrabber_IPin_QueryInterface, + SampleGrabber_IPin_AddRef, + SampleGrabber_IPin_Release, + SampleGrabber_Out_IPin_Connect, + SampleGrabber_Out_IPin_ReceiveConnection, + SampleGrabber_Out_IPin_Disconnect, + SampleGrabber_IPin_ConnectedTo, + SampleGrabber_IPin_ConnectionMediaType, + SampleGrabber_IPin_QueryPinInfo, + SampleGrabber_IPin_QueryDirection, + SampleGrabber_IPin_QueryId, + SampleGrabber_IPin_QueryAccept, + SampleGrabber_IPin_EnumMediaTypes, + SampleGrabber_Out_IPin_QueryInternalConnections, + SampleGrabber_IPin_EndOfStream, + SampleGrabber_IPin_BeginFlush, + SampleGrabber_IPin_EndFlush, + SampleGrabber_IPin_NewSegment, +}; + HRESULT SampleGrabber_create(IUnknown *pUnkOuter, LPVOID *ppv) { SG_Impl* obj = NULL; @@ -442,11 +980,26 @@ HRESULT SampleGrabber_create(IUnknown *pUnkOuter, LPVOID *ppv) obj->refCount = 1; obj->IBaseFilter_Vtbl = &IBaseFilter_VTable; obj->ISampleGrabber_Vtbl = &ISampleGrabber_VTable; + obj->IMemInputPin_Vtbl = &IMemInputPin_VTable; + obj->pin_in.lpVtbl = &IPin_In_VTable; + obj->pin_in.dir = PINDIR_INPUT; + obj->pin_in.name = pin_in_name; + obj->pin_in.sg = obj; + obj->pin_in.pair = NULL; + obj->pin_out.lpVtbl = &IPin_Out_VTable; + obj->pin_out.dir = PINDIR_OUTPUT; + obj->pin_out.name = pin_out_name; + obj->pin_out.sg = obj; + obj->pin_out.pair = NULL; obj->info.achName[0] = 0; obj->info.pGraph = NULL; obj->state = State_Stopped; obj->allocator = NULL; obj->refClock = NULL; + obj->memOutput = NULL; + obj->grabberIface = NULL; + obj->grabberMethod = -1; + obj->oneShot = OneShot_None; *ppv = obj; return S_OK;