/* DirectShow Sample Grabber object (QEDIT.DLL) * * Copyright 2009 Paul Chitescu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #define COBJMACROS #include "windef.h" #include "winbase.h" #include "winuser.h" #include "ole2.h" #include "qedit_private.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(qedit); static const WCHAR vendor_name[] = { 'W', 'i', 'n', 'e', 0 }; static const WCHAR pin_in_name[] = { 'I', 'n', 0 }; static const WCHAR pin_out_name[] = { 'O', 'u', 't', 0 }; static IEnumPins *pinsenum_create(IBaseFilter *filter, IPin **pins, ULONG pinCount); static IEnumMediaTypes *mediaenum_create(const AM_MEDIA_TYPE *mtype); /* Fixed pins enumerator, holds filter referenced */ typedef struct _PE_Impl { IEnumPins pe; IBaseFilter *filter; LONG refCount; ULONG numPins; ULONG index; IPin *pins[1]; } PE_Impl; /* IEnumPins interface implementation */ /* IUnknown */ static ULONG WINAPI Fixed_IEnumPins_AddRef(IEnumPins *iface) { PE_Impl *This = (PE_Impl *)iface; ULONG refCount = InterlockedIncrement(&This->refCount); TRACE("(%p) new ref = %u\n", This, refCount); return refCount; } /* IUnknown */ static ULONG WINAPI Fixed_IEnumPins_Release(IEnumPins *iface) { PE_Impl *This = (PE_Impl *)iface; ULONG refCount = InterlockedDecrement(&This->refCount); TRACE("(%p) new ref = %u\n", This, refCount); if (refCount == 0) { IBaseFilter_Release(This->filter); CoTaskMemFree(This); return 0; } return refCount; } /* IUnknown */ static HRESULT WINAPI Fixed_IEnumPins_QueryInterface(IEnumPins *iface, REFIID riid, void **ret_iface) { TRACE("(%p)->(%s %p)\n", iface, debugstr_guid(riid), ret_iface); if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IEnumPins)) { IEnumPins_AddRef(iface); *ret_iface = iface; return S_OK; } *ret_iface = NULL; WARN("(%p, %s, %p): not found\n", iface, debugstr_guid(riid), ret_iface); return E_NOINTERFACE; } /* IEnumPins */ static HRESULT WINAPI Fixed_IEnumPins_Next(IEnumPins *iface, ULONG nPins, IPin **pins, ULONG *fetched) { PE_Impl *This = (PE_Impl *)iface; ULONG count = 0; TRACE("(%p)->(%u, %p, %p) index = %u\n", This, nPins, pins, fetched, This->index); if (!nPins) return E_INVALIDARG; if (!pins || ((nPins != 1) && !fetched)) return E_POINTER; while ((count < nPins) && (This->index < This->numPins)) { IPin *pin = This->pins[This->index++]; IPin_AddRef(pin); pins[count++] = pin; } if (fetched) *fetched = count; return (count == nPins) ? S_OK : S_FALSE; } /* IEnumPins */ static HRESULT WINAPI Fixed_IEnumPins_Skip(IEnumPins *iface, ULONG nPins) { PE_Impl *This = (PE_Impl *)iface; TRACE("(%p)->(%u) index = %u\n", This, nPins, This->index); nPins += This->index; if (nPins >= This->numPins) { This->index = This->numPins; return S_FALSE; } This->index = nPins; return S_OK; } /* IEnumPins */ static HRESULT WINAPI Fixed_IEnumPins_Reset(IEnumPins *iface) { PE_Impl *This = (PE_Impl *)iface; TRACE("(%p)->() index = %u\n", This, This->index); This->index = 0; return S_OK; } /* IEnumPins */ static HRESULT WINAPI Fixed_IEnumPins_Clone(IEnumPins *iface, IEnumPins **pins) { PE_Impl *This = (PE_Impl *)iface; TRACE("(%p)->(%p) index = %u\n", This, pins, This->index); if (!pins) return E_POINTER; *pins = pinsenum_create(This->filter, This->pins, This->numPins); if (!*pins) return E_OUTOFMEMORY; ((PE_Impl *)*pins)->index = This->index; return S_OK; } /* Virtual tables and constructor */ static const IEnumPinsVtbl IEnumPins_VTable = { Fixed_IEnumPins_QueryInterface, Fixed_IEnumPins_AddRef, Fixed_IEnumPins_Release, Fixed_IEnumPins_Next, Fixed_IEnumPins_Skip, Fixed_IEnumPins_Reset, Fixed_IEnumPins_Clone, }; static IEnumPins *pinsenum_create(IBaseFilter *filter, IPin **pins, ULONG pinCount) { PE_Impl *obj; ULONG len = offsetof(PE_Impl, pins[pinCount]); ULONG i; obj = CoTaskMemAlloc(len); if (!obj) return NULL; ZeroMemory(obj, len); obj->pe.lpVtbl = &IEnumPins_VTable; obj->refCount = 1; obj->filter = filter; obj->numPins = pinCount; obj->index = 0; for (i = 0; i < pinCount; i++) obj->pins[i] = pins[i]; IBaseFilter_AddRef(filter); return &obj->pe; } /* Single media type enumerator */ typedef struct _ME_Impl { IEnumMediaTypes me; LONG refCount; BOOL past; AM_MEDIA_TYPE mtype; } ME_Impl; /* IEnumMediaTypes interface implementation */ /* IUnknown */ static ULONG WINAPI Single_IEnumMediaTypes_AddRef(IEnumMediaTypes *iface) { ME_Impl *This = (ME_Impl *)iface; ULONG refCount = InterlockedIncrement(&This->refCount); TRACE("(%p) new ref = %u\n", This, refCount); return refCount; } /* IUnknown */ static ULONG WINAPI Single_IEnumMediaTypes_Release(IEnumMediaTypes *iface) { ME_Impl *This = (ME_Impl *)iface; ULONG refCount = InterlockedDecrement(&This->refCount); TRACE("(%p) new ref = %u\n", This, refCount); if (refCount == 0) { if (This->mtype.pbFormat) CoTaskMemFree(This->mtype.pbFormat); CoTaskMemFree(This); return 0; } return refCount; } /* IUnknown */ static HRESULT WINAPI Single_IEnumMediaTypes_QueryInterface(IEnumMediaTypes *iface, REFIID riid, void **ppvObject) { ME_Impl *This = (ME_Impl *)iface; TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject); if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IEnumMediaTypes)) { Single_IEnumMediaTypes_AddRef(iface); *ppvObject = &(This->me); return S_OK; } *ppvObject = NULL; WARN("(%p, %s,%p): not found\n", This, debugstr_guid(riid), ppvObject); return E_NOINTERFACE; } /* IEnumMediaTypes */ static HRESULT WINAPI Single_IEnumMediaTypes_Next(IEnumMediaTypes *iface, ULONG nTypes, AM_MEDIA_TYPE **types, ULONG *fetched) { ME_Impl *This = (ME_Impl *)iface; ULONG count = 0; TRACE("(%p)->(%u, %p, %p)\n", This, nTypes, types, fetched); if (!nTypes) return E_INVALIDARG; if (!types || ((nTypes != 1) && !fetched)) return E_POINTER; if (!This->past && !IsEqualGUID(&This->mtype.majortype,&GUID_NULL)) { AM_MEDIA_TYPE *mtype = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE)); *mtype = This->mtype; if (mtype->cbFormat) { mtype->pbFormat = CoTaskMemAlloc(mtype->cbFormat); CopyMemory(mtype->pbFormat, This->mtype.pbFormat, mtype->cbFormat); } *types = mtype; This->past = TRUE; count = 1; } if (fetched) *fetched = count; return (count == nTypes) ? S_OK : S_FALSE; } /* IEnumMediaTypes */ static HRESULT WINAPI Single_IEnumMediaTypes_Skip(IEnumMediaTypes *iface, ULONG nTypes) { ME_Impl *This = (ME_Impl *)iface; TRACE("(%p)->(%u)\n", This, nTypes); if (nTypes) This->past = TRUE; return This->past ? S_FALSE : S_OK; } /* IEnumMediaTypes */ static HRESULT WINAPI Single_IEnumMediaTypes_Reset(IEnumMediaTypes *iface) { ME_Impl *This = (ME_Impl *)iface; TRACE("(%p)->()\n", This); This->past = FALSE; return S_OK; } /* IEnumMediaTypes */ static HRESULT WINAPI Single_IEnumMediaTypes_Clone(IEnumMediaTypes *iface, IEnumMediaTypes **me) { ME_Impl *This = (ME_Impl *)iface; TRACE("(%p)->(%p)\n", This, me); if (!me) return E_POINTER; *me = mediaenum_create(&This->mtype); if (!*me) return E_OUTOFMEMORY; ((ME_Impl *)*me)->past = This->past; return S_OK; } /* Virtual tables and constructor */ static const IEnumMediaTypesVtbl IEnumMediaTypes_VTable = { Single_IEnumMediaTypes_QueryInterface, Single_IEnumMediaTypes_AddRef, Single_IEnumMediaTypes_Release, Single_IEnumMediaTypes_Next, Single_IEnumMediaTypes_Skip, Single_IEnumMediaTypes_Reset, Single_IEnumMediaTypes_Clone, }; static IEnumMediaTypes *mediaenum_create(const AM_MEDIA_TYPE *mtype) { ME_Impl *obj = CoTaskMemAlloc(sizeof(ME_Impl)); if (obj) { ZeroMemory(obj, sizeof(ME_Impl)); obj->me.lpVtbl = &IEnumMediaTypes_VTable; obj->refCount = 1; obj->past = FALSE; if (mtype) { obj->mtype = *mtype; obj->mtype.pUnk = NULL; if (mtype->cbFormat) { obj->mtype.pbFormat = CoTaskMemAlloc(mtype->cbFormat); CopyMemory(obj->mtype.pbFormat, mtype->pbFormat, mtype->cbFormat); } else obj->mtype.pbFormat = NULL; } else obj->mtype.majortype = GUID_NULL; } return &obj->me; } /* Sample Grabber pin implementation */ typedef struct _SG_Pin { IPin IPin_iface; PIN_DIRECTION dir; WCHAR const *name; struct _SG_Impl *sg; IPin *pair; } SG_Pin; static inline SG_Pin *impl_from_IPin(IPin *iface) { return CONTAINING_RECORD(iface, SG_Pin, IPin_iface); } /* Sample Grabber filter implementation */ typedef struct _SG_Impl { IUnknown IUnknown_inner; IBaseFilter IBaseFilter_iface; ISampleGrabber ISampleGrabber_iface; /* IMediaSeeking and IMediaPosition are implemented by ISeekingPassThru */ IUnknown* seekthru_unk; IUnknown *outer_unk; LONG ref; CRITICAL_SECTION critSect; FILTER_INFO info; FILTER_STATE state; AM_MEDIA_TYPE mtype; SG_Pin pin_in; SG_Pin pin_out; IMemInputPin IMemInputPin_iface; IMemAllocator *allocator; IReferenceClock *refClock; IMemInputPin *memOutput; ISampleGrabberCB *grabberIface; LONG grabberMethod; LONG oneShot; LONG bufferLen; void* bufferData; } SG_Impl; enum { OneShot_None, OneShot_Wait, OneShot_Past, }; static inline SG_Impl *impl_from_IUnknown(IUnknown *iface) { return CONTAINING_RECORD(iface, SG_Impl, IUnknown_inner); } static inline SG_Impl *impl_from_IBaseFilter(IBaseFilter *iface) { return CONTAINING_RECORD(iface, SG_Impl, IBaseFilter_iface); } static inline SG_Impl *impl_from_ISampleGrabber(ISampleGrabber *iface) { return CONTAINING_RECORD(iface, SG_Impl, ISampleGrabber_iface); } static inline SG_Impl *impl_from_IMemInputPin(IMemInputPin *iface) { return CONTAINING_RECORD(iface, SG_Impl, IMemInputPin_iface); } /* Cleanup at end of life */ static void SampleGrabber_cleanup(SG_Impl *This) { TRACE("(%p)\n", This); if (This->info.pGraph) WARN("(%p) still joined to filter graph %p\n", This, This->info.pGraph); if (This->allocator) 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); if (This->mtype.pbFormat) CoTaskMemFree(This->mtype.pbFormat); if (This->bufferData) CoTaskMemFree(This->bufferData); if(This->seekthru_unk) IUnknown_Release(This->seekthru_unk); This->critSect.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&This->critSect); } /* SampleGrabber inner IUnknown */ static HRESULT WINAPI SampleGrabber_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) { SG_Impl *This = impl_from_IUnknown(iface); TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); *ppv = NULL; if (IsEqualIID(riid, &IID_IUnknown)) *ppv = &This->IUnknown_inner; else if (IsEqualIID(riid, &IID_IPersist) || IsEqualIID(riid, &IID_IMediaFilter) || IsEqualIID(riid, &IID_IBaseFilter)) *ppv = &This->IBaseFilter_iface; else if (IsEqualIID(riid, &IID_ISampleGrabber)) *ppv = &This->ISampleGrabber_iface; else if (IsEqualIID(riid, &IID_IMediaPosition)) return IUnknown_QueryInterface(This->seekthru_unk, riid, ppv); else if (IsEqualIID(riid, &IID_IMediaSeeking)) return IUnknown_QueryInterface(This->seekthru_unk, riid, ppv); else WARN("(%p, %s,%p): not found\n", This, debugstr_guid(riid), ppv); if (!*ppv) return E_NOINTERFACE; IUnknown_AddRef((IUnknown*)*ppv); return S_OK; } static ULONG WINAPI SampleGrabber_AddRef(IUnknown *iface) { SG_Impl *This = impl_from_IUnknown(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p) new ref = %u\n", This, ref); return ref; } static ULONG WINAPI SampleGrabber_Release(IUnknown *iface) { SG_Impl *This = impl_from_IUnknown(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p) new ref = %u\n", This, ref); if (ref == 0) { SampleGrabber_cleanup(This); CoTaskMemFree(This); return 0; } return ref; } static const IUnknownVtbl samplegrabber_vtbl = { SampleGrabber_QueryInterface, SampleGrabber_AddRef, SampleGrabber_Release, }; /* Helper that buffers data and/or calls installed sample callbacks */ static void SampleGrabber_callback(SG_Impl *This, IMediaSample *sample) { double time = 0.0; REFERENCE_TIME tStart, tEnd; if (This->bufferLen >= 0) { BYTE *data = 0; LONG size = IMediaSample_GetActualDataLength(sample); if (size >= 0 && SUCCEEDED(IMediaSample_GetPointer(sample, &data))) { if (!data) size = 0; EnterCriticalSection(&This->critSect); if (This->bufferLen != size) { if (This->bufferData) CoTaskMemFree(This->bufferData); This->bufferData = size ? CoTaskMemAlloc(size) : NULL; This->bufferLen = size; } if (size) CopyMemory(This->bufferData, data, size); LeaveCriticalSection(&This->critSect); } } if (!This->grabberIface) return; 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 %d\n", This->grabberMethod); /* do not bother us again */ This->grabberMethod = -1; } } /* SampleGrabber implementation of IBaseFilter interface */ /* IUnknown */ static HRESULT WINAPI SampleGrabber_IBaseFilter_QueryInterface(IBaseFilter *iface, REFIID riid, void **ppv) { SG_Impl *This = impl_from_IBaseFilter(iface); return IUnknown_QueryInterface(This->outer_unk, riid, ppv); } /* IUnknown */ static ULONG WINAPI SampleGrabber_IBaseFilter_AddRef(IBaseFilter *iface) { SG_Impl *This = impl_from_IBaseFilter(iface); return IUnknown_AddRef(This->outer_unk); } /* IUnknown */ static ULONG WINAPI SampleGrabber_IBaseFilter_Release(IBaseFilter *iface) { SG_Impl *This = impl_from_IBaseFilter(iface); return IUnknown_Release(This->outer_unk); } /* IPersist */ static HRESULT WINAPI SampleGrabber_IBaseFilter_GetClassID(IBaseFilter *iface, CLSID *pClassID) { TRACE("(%p)\n", pClassID); if (!pClassID) return E_POINTER; *pClassID = CLSID_SampleGrabber; return S_OK; } /* IMediaFilter */ static HRESULT WINAPI SampleGrabber_IBaseFilter_Stop(IBaseFilter *iface) { SG_Impl *This = impl_from_IBaseFilter(iface); TRACE("(%p)\n", This); This->state = State_Stopped; return S_OK; } /* IMediaFilter */ static HRESULT WINAPI SampleGrabber_IBaseFilter_Pause(IBaseFilter *iface) { SG_Impl *This = impl_from_IBaseFilter(iface); TRACE("(%p)\n", This); This->state = State_Paused; return S_OK; } /* IMediaFilter */ static HRESULT WINAPI SampleGrabber_IBaseFilter_Run(IBaseFilter *iface, REFERENCE_TIME tStart) { SG_Impl *This = impl_from_IBaseFilter(iface); TRACE("(%p)\n", This); This->state = State_Running; return S_OK; } /* IMediaFilter */ static HRESULT WINAPI SampleGrabber_IBaseFilter_GetState(IBaseFilter *iface, DWORD msTout, FILTER_STATE *state) { SG_Impl *This = impl_from_IBaseFilter(iface); TRACE("(%p)->(%u, %p)\n", This, msTout, state); if (!state) return E_POINTER; *state = This->state; return S_OK; } /* IMediaFilter */ static HRESULT WINAPI SampleGrabber_IBaseFilter_SetSyncSource(IBaseFilter *iface, IReferenceClock *clock) { SG_Impl *This = impl_from_IBaseFilter(iface); TRACE("(%p)->(%p)\n", This, clock); if (clock != This->refClock) { if (clock) IReferenceClock_AddRef(clock); if (This->refClock) IReferenceClock_Release(This->refClock); This->refClock = clock; } return S_OK; } /* IMediaFilter */ static HRESULT WINAPI SampleGrabber_IBaseFilter_GetSyncSource(IBaseFilter *iface, IReferenceClock **clock) { SG_Impl *This = impl_from_IBaseFilter(iface); TRACE("(%p)->(%p)\n", This, clock); if (!clock) return E_POINTER; if (This->refClock) IReferenceClock_AddRef(This->refClock); *clock = This->refClock; return S_OK; } /* IBaseFilter */ static HRESULT WINAPI SampleGrabber_IBaseFilter_EnumPins(IBaseFilter *iface, IEnumPins **pins) { SG_Impl *This = impl_from_IBaseFilter(iface); IPin *pin[2]; TRACE("(%p)->(%p)\n", This, pins); if (!pins) return E_POINTER; pin[0] = &This->pin_in.IPin_iface; pin[1] = &This->pin_out.IPin_iface; *pins = pinsenum_create(iface, pin, 2); return *pins ? S_OK : E_OUTOFMEMORY; } /* IBaseFilter */ static HRESULT WINAPI SampleGrabber_IBaseFilter_FindPin(IBaseFilter *iface, LPCWSTR id, IPin **pin) { SG_Impl *This = impl_from_IBaseFilter(iface); TRACE("(%p)->(%s, %p)\n", This, debugstr_w(id), pin); if (!id || !pin) return E_POINTER; if (!lstrcmpiW(id,pin_in_name)) { *pin = &This->pin_in.IPin_iface; IPin_AddRef(*pin); return S_OK; } else if (!lstrcmpiW(id,pin_out_name)) { *pin = &This->pin_out.IPin_iface; IPin_AddRef(*pin); return S_OK; } *pin = NULL; return VFW_E_NOT_FOUND; } /* IBaseFilter */ static HRESULT WINAPI SampleGrabber_IBaseFilter_QueryFilterInfo(IBaseFilter *iface, FILTER_INFO *info) { SG_Impl *This = impl_from_IBaseFilter(iface); TRACE("(%p)->(%p)\n", This, info); if (!info) return E_POINTER; if (This->info.pGraph) IFilterGraph_AddRef(This->info.pGraph); *info = This->info; return S_OK; } /* IBaseFilter */ static HRESULT WINAPI SampleGrabber_IBaseFilter_JoinFilterGraph(IBaseFilter *iface, IFilterGraph *graph, LPCWSTR name) { SG_Impl *This = impl_from_IBaseFilter(iface); TRACE("(%p)->(%p, %s)\n", This, graph, debugstr_w(name)); This->info.pGraph = graph; if (name) lstrcpynW(This->info.achName,name,MAX_FILTER_NAME); This->oneShot = OneShot_None; return S_OK; } /* IBaseFilter */ static HRESULT WINAPI SampleGrabber_IBaseFilter_QueryVendorInfo(IBaseFilter *iface, LPWSTR *vendor) { TRACE("(%p)\n", vendor); if (!vendor) return E_POINTER; *vendor = CoTaskMemAlloc(sizeof(vendor_name)); CopyMemory(*vendor, vendor_name, sizeof(vendor_name)); return S_OK; } /* SampleGrabber implementation of ISampleGrabber interface */ /* IUnknown */ static HRESULT WINAPI SampleGrabber_ISampleGrabber_QueryInterface(ISampleGrabber *iface, REFIID riid, void **ppv) { SG_Impl *This = impl_from_ISampleGrabber(iface); return IUnknown_QueryInterface(This->outer_unk, riid, ppv); } /* IUnknown */ static ULONG WINAPI SampleGrabber_ISampleGrabber_AddRef(ISampleGrabber *iface) { SG_Impl *This = impl_from_ISampleGrabber(iface); return IUnknown_AddRef(This->outer_unk); } /* IUnknown */ static ULONG WINAPI SampleGrabber_ISampleGrabber_Release(ISampleGrabber *iface) { SG_Impl *This = impl_from_ISampleGrabber(iface); return IUnknown_Release(This->outer_unk); } /* ISampleGrabber */ static HRESULT WINAPI SampleGrabber_ISampleGrabber_SetOneShot(ISampleGrabber *iface, BOOL oneShot) { SG_Impl *This = impl_from_ISampleGrabber(iface); TRACE("(%p)->(%u)\n", This, oneShot); This->oneShot = oneShot ? OneShot_Wait : OneShot_None; return S_OK; } /* ISampleGrabber */ static HRESULT WINAPI SampleGrabber_ISampleGrabber_SetMediaType(ISampleGrabber *iface, const AM_MEDIA_TYPE *type) { SG_Impl *This = impl_from_ISampleGrabber(iface); TRACE("(%p)->(%p)\n", This, type); if (!type) return E_POINTER; TRACE("Media type: %s/%s ssize: %u format: %s (%u bytes)\n", debugstr_guid(&type->majortype), debugstr_guid(&type->subtype), type->lSampleSize, debugstr_guid(&type->formattype), type->cbFormat); if (This->mtype.pbFormat) CoTaskMemFree(This->mtype.pbFormat); This->mtype = *type; This->mtype.pUnk = NULL; if (type->cbFormat) { This->mtype.pbFormat = CoTaskMemAlloc(type->cbFormat); CopyMemory(This->mtype.pbFormat, type->pbFormat, type->cbFormat); } else This->mtype.pbFormat = NULL; return S_OK; } /* ISampleGrabber */ static HRESULT WINAPI SampleGrabber_ISampleGrabber_GetConnectedMediaType(ISampleGrabber *iface, AM_MEDIA_TYPE *type) { SG_Impl *This = impl_from_ISampleGrabber(iface); TRACE("(%p)->(%p)\n", This, type); if (!type) return E_POINTER; if (!This->pin_in.pair) return VFW_E_NOT_CONNECTED; *type = This->mtype; if (type->cbFormat) { type->pbFormat = CoTaskMemAlloc(type->cbFormat); CopyMemory(type->pbFormat, This->mtype.pbFormat, type->cbFormat); } return S_OK; } /* ISampleGrabber */ static HRESULT WINAPI SampleGrabber_ISampleGrabber_SetBufferSamples(ISampleGrabber *iface, BOOL bufferEm) { SG_Impl *This = impl_from_ISampleGrabber(iface); TRACE("(%p)->(%u)\n", This, bufferEm); EnterCriticalSection(&This->critSect); if (bufferEm) { if (This->bufferLen < 0) This->bufferLen = 0; } else This->bufferLen = -1; LeaveCriticalSection(&This->critSect); return S_OK; } /* ISampleGrabber */ static HRESULT WINAPI SampleGrabber_ISampleGrabber_GetCurrentBuffer(ISampleGrabber *iface, LONG *bufSize, LONG *buffer) { SG_Impl *This = impl_from_ISampleGrabber(iface); HRESULT ret = S_OK; TRACE("(%p)->(%p, %p)\n", This, bufSize, buffer); if (!bufSize) return E_POINTER; EnterCriticalSection(&This->critSect); if (!This->pin_in.pair) ret = VFW_E_NOT_CONNECTED; else if (This->bufferLen < 0) ret = E_INVALIDARG; else if (This->bufferLen == 0) ret = VFW_E_WRONG_STATE; else { if (buffer) { if (*bufSize >= This->bufferLen) CopyMemory(buffer, This->bufferData, This->bufferLen); else ret = E_OUTOFMEMORY; } *bufSize = This->bufferLen; } LeaveCriticalSection(&This->critSect); return ret; } /* ISampleGrabber */ static HRESULT WINAPI SampleGrabber_ISampleGrabber_GetCurrentSample(ISampleGrabber *iface, IMediaSample **sample) { /* MS doesn't implement it either, no one should call it */ WARN("(%p): not implemented\n", sample); return E_NOTIMPL; } /* ISampleGrabber */ static HRESULT WINAPI SampleGrabber_ISampleGrabber_SetCallback(ISampleGrabber *iface, ISampleGrabberCB *cb, LONG whichMethod) { SG_Impl *This = impl_from_ISampleGrabber(iface); 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 **ppv) { SG_Impl *This = impl_from_IMemInputPin(iface); return IUnknown_QueryInterface(This->outer_unk, riid, ppv); } /* IUnknown */ static ULONG WINAPI SampleGrabber_IMemInputPin_AddRef(IMemInputPin *iface) { SG_Impl *This = impl_from_IMemInputPin(iface); return IUnknown_AddRef(This->outer_unk); } /* IUnknown */ static ULONG WINAPI SampleGrabber_IMemInputPin_Release(IMemInputPin *iface) { SG_Impl *This = impl_from_IMemInputPin(iface); return IUnknown_Release(This->outer_unk); } /* 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; 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); LONG idx; 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; 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) { SG_Pin *This = impl_from_IPin(iface); return ISampleGrabber_AddRef(&This->sg->ISampleGrabber_iface); } /* IUnknown */ static ULONG WINAPI SampleGrabber_IPin_Release(IPin *iface) { SG_Pin *This = impl_from_IPin(iface); return ISampleGrabber_Release(&This->sg->ISampleGrabber_iface); } /* IUnknown */ static HRESULT WINAPI SampleGrabber_IPin_QueryInterface(IPin *iface, REFIID riid, void **ppv) { SG_Pin *This = impl_from_IPin(iface); TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); *ppv = NULL; if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IPin)) *ppv = iface; else if (IsEqualIID(riid, &IID_IMemInputPin)) *ppv = &This->sg->IMemInputPin_iface; else if (IsEqualIID(riid, &IID_IMediaSeeking)) return IUnknown_QueryInterface(&This->sg->IUnknown_inner, riid, ppv); else if (IsEqualIID(riid, &IID_IMediaPosition)) return IUnknown_QueryInterface(&This->sg->IUnknown_inner, riid, ppv); else { WARN("(%p, %s,%p): not found\n", This, debugstr_guid(riid), ppv); return E_NOINTERFACE; } IUnknown_AddRef((IUnknown*)*ppv); return S_OK; } /* 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 = impl_from_IPin(iface); HRESULT hr; 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; if (type) { TRACE("Media type: %s/%s ssize: %u format: %s (%u bytes)\n", debugstr_guid(&type->majortype), debugstr_guid(&type->subtype), type->lSampleSize, debugstr_guid(&type->formattype), type->cbFormat); if (!IsEqualGUID(&This->sg->mtype.majortype,&GUID_NULL) && !IsEqualGUID(&This->sg->mtype.majortype,&type->majortype)) return VFW_E_TYPE_NOT_ACCEPTED; if (!IsEqualGUID(&This->sg->mtype.subtype,&MEDIASUBTYPE_None) && !IsEqualGUID(&This->sg->mtype.subtype,&type->subtype)) return VFW_E_TYPE_NOT_ACCEPTED; if (!IsEqualGUID(&This->sg->mtype.formattype,&GUID_NULL) && !IsEqualGUID(&This->sg->mtype.formattype,&FORMAT_None) && !IsEqualGUID(&This->sg->mtype.formattype,&type->formattype)) return VFW_E_TYPE_NOT_ACCEPTED; } else type = &This->sg->mtype; if (!IsEqualGUID(&type->formattype, &FORMAT_None) && !IsEqualGUID(&type->formattype, &GUID_NULL) && !type->pbFormat) return VFW_E_TYPE_NOT_ACCEPTED; hr = IPin_ReceiveConnection(receiver, &This->IPin_iface, type); if (FAILED(hr)) return hr; This->pair = receiver; if (This->sg->memOutput) { IMemInputPin_Release(This->sg->memOutput); This->sg->memOutput = NULL; } IPin_QueryInterface(receiver,&IID_IMemInputPin,(void **)&(This->sg->memOutput)); TRACE("(%p) Accepted IPin %p, IMemInputPin %p\n", This, receiver, This->sg->memOutput); return S_OK; } /* IPin - input pin */ static HRESULT WINAPI SampleGrabber_In_IPin_ReceiveConnection(IPin *iface, IPin *connector, const AM_MEDIA_TYPE *type) { SG_Pin *This = impl_from_IPin(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; if (type) { TRACE("Media type: %s/%s ssize: %u format: %s (%u bytes)\n", debugstr_guid(&type->majortype), debugstr_guid(&type->subtype), type->lSampleSize, debugstr_guid(&type->formattype), type->cbFormat); if (!IsEqualGUID(&type->formattype, &FORMAT_None) && !IsEqualGUID(&type->formattype, &GUID_NULL) && !type->pbFormat) return VFW_E_INVALIDMEDIATYPE; if (!IsEqualGUID(&This->sg->mtype.majortype,&GUID_NULL) && !IsEqualGUID(&This->sg->mtype.majortype,&type->majortype)) return VFW_E_TYPE_NOT_ACCEPTED; if (!IsEqualGUID(&This->sg->mtype.subtype,&MEDIASUBTYPE_None) && !IsEqualGUID(&This->sg->mtype.subtype,&type->subtype)) return VFW_E_TYPE_NOT_ACCEPTED; if (!IsEqualGUID(&This->sg->mtype.formattype,&GUID_NULL) && !IsEqualGUID(&This->sg->mtype.formattype,&FORMAT_None) && !IsEqualGUID(&This->sg->mtype.formattype,&type->formattype)) return VFW_E_TYPE_NOT_ACCEPTED; if (This->sg->mtype.pbFormat) CoTaskMemFree(This->sg->mtype.pbFormat); This->sg->mtype = *type; This->sg->mtype.pUnk = NULL; if (type->cbFormat) { This->sg->mtype.pbFormat = CoTaskMemAlloc(type->cbFormat); CopyMemory(This->sg->mtype.pbFormat, type->pbFormat, type->cbFormat); } else This->sg->mtype.pbFormat = NULL; } This->pair = connector; TRACE("(%p) Accepted IPin %p\n", This, connector); return S_OK; } /* 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 = impl_from_IPin(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 = impl_from_IPin(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 = impl_from_IPin(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 = impl_from_IPin(iface); TRACE("(%p)->(%p)\n", This, mtype); if (!mtype) return E_POINTER; if (!This->pair) return VFW_E_NOT_CONNECTED; *mtype = This->sg->mtype; if (mtype->cbFormat) { mtype->pbFormat = CoTaskMemAlloc(mtype->cbFormat); CopyMemory(mtype->pbFormat, This->sg->mtype.pbFormat, mtype->cbFormat); } return S_OK; } /* IPin */ static HRESULT WINAPI SampleGrabber_IPin_QueryPinInfo(IPin *iface, PIN_INFO *info) { SG_Pin *This = impl_from_IPin(iface); TRACE("(%p)->(%p)\n", This, info); if (!info) return E_POINTER; IBaseFilter_AddRef(&This->sg->IBaseFilter_iface); info->pFilter = &This->sg->IBaseFilter_iface; 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 = impl_from_IPin(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 = impl_from_IPin(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 = impl_from_IPin(iface); TRACE("(%p)->(%p)\n", This, mtypes); if (!mtypes) return E_POINTER; *mtypes = mediaenum_create(This->sg->pin_in.pair ? &This->sg->mtype : NULL); return *mtypes ? S_OK : E_OUTOFMEMORY; } /* IPin - input pin */ static HRESULT WINAPI SampleGrabber_In_IPin_QueryInternalConnections(IPin *iface, IPin **pins, ULONG *nPins) { SG_Pin *This = impl_from_IPin(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(&This->sg->pin_out.IPin_iface); *pins = &This->sg->pin_out.IPin_iface; *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 */ static const IBaseFilterVtbl IBaseFilter_VTable = { SampleGrabber_IBaseFilter_QueryInterface, SampleGrabber_IBaseFilter_AddRef, SampleGrabber_IBaseFilter_Release, SampleGrabber_IBaseFilter_GetClassID, SampleGrabber_IBaseFilter_Stop, SampleGrabber_IBaseFilter_Pause, SampleGrabber_IBaseFilter_Run, SampleGrabber_IBaseFilter_GetState, SampleGrabber_IBaseFilter_SetSyncSource, SampleGrabber_IBaseFilter_GetSyncSource, SampleGrabber_IBaseFilter_EnumPins, SampleGrabber_IBaseFilter_FindPin, SampleGrabber_IBaseFilter_QueryFilterInfo, SampleGrabber_IBaseFilter_JoinFilterGraph, SampleGrabber_IBaseFilter_QueryVendorInfo, }; static const ISampleGrabberVtbl ISampleGrabber_VTable = { SampleGrabber_ISampleGrabber_QueryInterface, SampleGrabber_ISampleGrabber_AddRef, SampleGrabber_ISampleGrabber_Release, SampleGrabber_ISampleGrabber_SetOneShot, SampleGrabber_ISampleGrabber_SetMediaType, SampleGrabber_ISampleGrabber_GetConnectedMediaType, SampleGrabber_ISampleGrabber_SetBufferSamples, SampleGrabber_ISampleGrabber_GetCurrentBuffer, SampleGrabber_ISampleGrabber_GetCurrentSample, 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; ISeekingPassThru *passthru; HRESULT hr; TRACE("(%p,%p)\n", ppv, pUnkOuter); obj = CoTaskMemAlloc(sizeof(SG_Impl)); if (NULL == obj) { *ppv = NULL; return E_OUTOFMEMORY; } ZeroMemory(obj, sizeof(SG_Impl)); obj->ref = 1; obj->IUnknown_inner.lpVtbl = &samplegrabber_vtbl; obj->IBaseFilter_iface.lpVtbl = &IBaseFilter_VTable; obj->ISampleGrabber_iface.lpVtbl = &ISampleGrabber_VTable; obj->IMemInputPin_iface.lpVtbl = &IMemInputPin_VTable; obj->pin_in.IPin_iface.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.IPin_iface.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; InitializeCriticalSection(&obj->critSect); obj->critSect.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": SG_Impl.critSect"); obj->info.achName[0] = 0; obj->info.pGraph = NULL; obj->state = State_Stopped; obj->mtype.majortype = GUID_NULL; obj->mtype.subtype = MEDIASUBTYPE_None; obj->mtype.formattype = FORMAT_None; obj->allocator = NULL; obj->refClock = NULL; obj->memOutput = NULL; obj->grabberIface = NULL; obj->grabberMethod = -1; obj->oneShot = OneShot_None; obj->bufferLen = -1; obj->bufferData = NULL; if (pUnkOuter) obj->outer_unk = pUnkOuter; else obj->outer_unk = &obj->IUnknown_inner; hr = CoCreateInstance(&CLSID_SeekingPassThru, (IUnknown*)obj, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void**)&obj->seekthru_unk); if(hr) return hr; IUnknown_QueryInterface(obj->seekthru_unk, &IID_ISeekingPassThru, (void**)&passthru); ISeekingPassThru_Init(passthru, FALSE, &obj->pin_in.IPin_iface); ISeekingPassThru_Release(passthru); *ppv = &obj->IUnknown_inner; return S_OK; }