| /* |
| * Implementation of the SmartTee filter |
| * |
| * Copyright 2015 Damjan Jovanovic |
| * |
| * 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 <stdarg.h> |
| |
| #define COBJMACROS |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "wtypes.h" |
| #include "wingdi.h" |
| #include "winuser.h" |
| #include "dshow.h" |
| |
| #include "qcap_main.h" |
| |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(qcap); |
| |
| typedef struct { |
| IUnknown IUnknown_iface; |
| IUnknown *outerUnknown; |
| BaseFilter filter; |
| BaseInputPin *input; |
| BaseOutputPin *capture; |
| BaseOutputPin *preview; |
| } SmartTeeFilter; |
| |
| static inline SmartTeeFilter *impl_from_IUnknown(IUnknown *iface) |
| { |
| return CONTAINING_RECORD(iface, SmartTeeFilter, IUnknown_iface); |
| } |
| |
| static inline SmartTeeFilter *impl_from_BaseFilter(BaseFilter *filter) |
| { |
| return CONTAINING_RECORD(filter, SmartTeeFilter, filter); |
| } |
| |
| static inline SmartTeeFilter *impl_from_IBaseFilter(IBaseFilter *iface) |
| { |
| BaseFilter *filter = CONTAINING_RECORD(iface, BaseFilter, IBaseFilter_iface); |
| return impl_from_BaseFilter(filter); |
| } |
| |
| static inline SmartTeeFilter *impl_from_BasePin(BasePin *pin) |
| { |
| return impl_from_IBaseFilter(pin->pinInfo.pFilter); |
| } |
| |
| static inline SmartTeeFilter *impl_from_IPin(IPin *iface) |
| { |
| BasePin *bp = CONTAINING_RECORD(iface, BasePin, IPin_iface); |
| return impl_from_IBaseFilter(bp->pinInfo.pFilter); |
| } |
| |
| static HRESULT WINAPI Unknown_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) |
| { |
| SmartTeeFilter *This = impl_from_IUnknown(iface); |
| if (IsEqualIID(riid, &IID_IUnknown)) { |
| TRACE("(%p)->(IID_IUnknown, %p)\n", This, ppv); |
| *ppv = &This->IUnknown_iface; |
| } else if (IsEqualIID(riid, &IID_IPersist)) { |
| TRACE("(%p)->(IID_IPersist, %p)\n", This, ppv); |
| *ppv = &This->filter.IBaseFilter_iface; |
| } else if (IsEqualIID(riid, &IID_IMediaFilter)) { |
| TRACE("(%p)->(IID_IMediaFilter, %p)\n", This, ppv); |
| *ppv = &This->filter.IBaseFilter_iface; |
| } else if (IsEqualIID(riid, &IID_IBaseFilter)) { |
| TRACE("(%p)->(IID_IBaseFilter, %p)\n", This, ppv); |
| *ppv = &This->filter.IBaseFilter_iface; |
| } else { |
| FIXME("(%p): no interface for %s\n", This, debugstr_guid(riid)); |
| *ppv = NULL; |
| return E_NOINTERFACE; |
| } |
| IUnknown_AddRef((IUnknown*)*ppv); |
| return S_OK; |
| } |
| |
| static ULONG WINAPI Unknown_AddRef(IUnknown *iface) |
| { |
| SmartTeeFilter *This = impl_from_IUnknown(iface); |
| return BaseFilterImpl_AddRef(&This->filter.IBaseFilter_iface); |
| } |
| |
| static ULONG WINAPI Unknown_Release(IUnknown *iface) |
| { |
| SmartTeeFilter *This = impl_from_IUnknown(iface); |
| ULONG ref = BaseFilterImpl_Release(&This->filter.IBaseFilter_iface); |
| |
| TRACE("(%p)->() ref=%d\n", This, ref); |
| |
| if (!ref) { |
| if(This->input) |
| BaseInputPinImpl_Release(&This->input->pin.IPin_iface); |
| if(This->capture) |
| BaseOutputPinImpl_Release(&This->capture->pin.IPin_iface); |
| if(This->preview) |
| BaseOutputPinImpl_Release(&This->preview->pin.IPin_iface); |
| CoTaskMemFree(This); |
| } |
| return ref; |
| } |
| |
| static const IUnknownVtbl UnknownVtbl = { |
| Unknown_QueryInterface, |
| Unknown_AddRef, |
| Unknown_Release |
| }; |
| |
| static HRESULT WINAPI SmartTeeFilter_QueryInterface(IBaseFilter *iface, REFIID riid, void **ppv) |
| { |
| SmartTeeFilter *This = impl_from_IBaseFilter(iface); |
| return IUnknown_QueryInterface(This->outerUnknown, riid, ppv); |
| } |
| |
| static ULONG WINAPI SmartTeeFilter_AddRef(IBaseFilter *iface) |
| { |
| SmartTeeFilter *This = impl_from_IBaseFilter(iface); |
| return IUnknown_AddRef(This->outerUnknown); |
| } |
| |
| static ULONG WINAPI SmartTeeFilter_Release(IBaseFilter *iface) |
| { |
| SmartTeeFilter *This = impl_from_IBaseFilter(iface); |
| return IUnknown_Release(This->outerUnknown); |
| } |
| |
| static HRESULT WINAPI SmartTeeFilter_Stop(IBaseFilter *iface) |
| { |
| SmartTeeFilter *This = impl_from_IBaseFilter(iface); |
| TRACE("(%p)\n", This); |
| EnterCriticalSection(&This->filter.csFilter); |
| This->filter.state = State_Stopped; |
| LeaveCriticalSection(&This->filter.csFilter); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI SmartTeeFilter_Pause(IBaseFilter *iface) |
| { |
| SmartTeeFilter *This = impl_from_IBaseFilter(iface); |
| FIXME("(%p): stub\n", This); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI SmartTeeFilter_Run(IBaseFilter *iface, REFERENCE_TIME tStart) |
| { |
| SmartTeeFilter *This = impl_from_IBaseFilter(iface); |
| HRESULT hr = S_OK; |
| TRACE("(%p, %s)\n", This, wine_dbgstr_longlong(tStart)); |
| EnterCriticalSection(&This->filter.csFilter); |
| if(This->filter.state != State_Running) { |
| /* We share an allocator among all pins, an allocator can only get committed |
| * once, state transitions occur in upstream order, and only output pins |
| * commit allocators, so let the filter attached to the input pin worry about it. */ |
| if (This->input->pin.pConnectedTo) |
| This->filter.state = State_Running; |
| else |
| hr = VFW_E_NOT_CONNECTED; |
| } |
| LeaveCriticalSection(&This->filter.csFilter); |
| return hr; |
| } |
| |
| static HRESULT WINAPI SmartTeeFilter_FindPin(IBaseFilter *iface, LPCWSTR Id, IPin **ppPin) |
| { |
| SmartTeeFilter *This = impl_from_IBaseFilter(iface); |
| TRACE("(%p)->(%s, %p)\n", This, debugstr_w(Id), ppPin); |
| if (lstrcmpW(Id, This->input->pin.pinInfo.achName) == 0) { |
| *ppPin = &This->input->pin.IPin_iface; |
| IPin_AddRef(*ppPin); |
| return S_OK; |
| } else if (lstrcmpW(Id, This->capture->pin.pinInfo.achName) == 0) { |
| *ppPin = &This->capture->pin.IPin_iface; |
| IPin_AddRef(*ppPin); |
| return S_OK; |
| } else if (lstrcmpW(Id, This->preview->pin.pinInfo.achName) == 0) { |
| *ppPin = &This->preview->pin.IPin_iface; |
| IPin_AddRef(*ppPin); |
| return S_OK; |
| } |
| return VFW_E_NOT_FOUND; |
| } |
| |
| static const IBaseFilterVtbl SmartTeeFilterVtbl = { |
| SmartTeeFilter_QueryInterface, |
| SmartTeeFilter_AddRef, |
| SmartTeeFilter_Release, |
| BaseFilterImpl_GetClassID, |
| SmartTeeFilter_Stop, |
| SmartTeeFilter_Pause, |
| SmartTeeFilter_Run, |
| BaseFilterImpl_GetState, |
| BaseFilterImpl_SetSyncSource, |
| BaseFilterImpl_GetSyncSource, |
| BaseFilterImpl_EnumPins, |
| SmartTeeFilter_FindPin, |
| BaseFilterImpl_QueryFilterInfo, |
| BaseFilterImpl_JoinFilterGraph, |
| BaseFilterImpl_QueryVendorInfo |
| }; |
| |
| static IPin* WINAPI SmartTeeFilter_GetPin(BaseFilter *iface, int pos) |
| { |
| SmartTeeFilter *This = impl_from_BaseFilter(iface); |
| IPin *ret; |
| |
| TRACE("(%p)->(%d)\n", This, pos); |
| |
| switch(pos) { |
| case 0: |
| ret = &This->input->pin.IPin_iface; |
| break; |
| case 1: |
| ret = &This->capture->pin.IPin_iface; |
| break; |
| case 2: |
| ret = &This->preview->pin.IPin_iface; |
| break; |
| default: |
| TRACE("No pin %d\n", pos); |
| return NULL; |
| } |
| |
| IPin_AddRef(ret); |
| return ret; |
| } |
| |
| static LONG WINAPI SmartTeeFilter_GetPinCount(BaseFilter *iface) |
| { |
| return 3; |
| } |
| static const BaseFilterFuncTable SmartTeeFilterFuncs = { |
| SmartTeeFilter_GetPin, |
| SmartTeeFilter_GetPinCount |
| }; |
| |
| static ULONG WINAPI SmartTeeFilterInput_AddRef(IPin *iface) |
| { |
| SmartTeeFilter *This = impl_from_IPin(iface); |
| return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface); |
| } |
| |
| static ULONG WINAPI SmartTeeFilterInput_Release(IPin *iface) |
| { |
| SmartTeeFilter *This = impl_from_IPin(iface); |
| return IBaseFilter_Release(&This->filter.IBaseFilter_iface); |
| } |
| |
| |
| static const IPinVtbl SmartTeeFilterInputVtbl = { |
| BaseInputPinImpl_QueryInterface, |
| SmartTeeFilterInput_AddRef, |
| SmartTeeFilterInput_Release, |
| BaseInputPinImpl_Connect, |
| BaseInputPinImpl_ReceiveConnection, |
| BasePinImpl_Disconnect, |
| BasePinImpl_ConnectedTo, |
| BasePinImpl_ConnectionMediaType, |
| BasePinImpl_QueryPinInfo, |
| BasePinImpl_QueryDirection, |
| BasePinImpl_QueryId, |
| BasePinImpl_QueryAccept, |
| BasePinImpl_EnumMediaTypes, |
| BasePinImpl_QueryInternalConnections, |
| BaseInputPinImpl_EndOfStream, |
| BaseInputPinImpl_BeginFlush, |
| BaseInputPinImpl_EndFlush, |
| BaseInputPinImpl_NewSegment |
| }; |
| |
| static HRESULT WINAPI SmartTeeFilterInput_CheckMediaType(BasePin *base, const AM_MEDIA_TYPE *pmt) |
| { |
| SmartTeeFilter *This = impl_from_BasePin(base); |
| TRACE("(%p, AM_MEDIA_TYPE(%p))\n", This, pmt); |
| dump_AM_MEDIA_TYPE(pmt); |
| if (!pmt) |
| return VFW_E_TYPE_NOT_ACCEPTED; |
| /* We'll take any media type, but the output pins will later |
| * struggle to connect downstream. */ |
| return S_OK; |
| } |
| |
| static LONG WINAPI SmartTeeFilterInput_GetMediaTypeVersion(BasePin *base) |
| { |
| return 0; |
| } |
| |
| static HRESULT WINAPI SmartTeeFilterInput_GetMediaType(BasePin *base, int iPosition, AM_MEDIA_TYPE *amt) |
| { |
| SmartTeeFilter *This = impl_from_BasePin(base); |
| HRESULT hr; |
| TRACE("(%p)->(%d, %p)\n", This, iPosition, amt); |
| if (iPosition) |
| return S_FALSE; |
| EnterCriticalSection(&This->filter.csFilter); |
| if (This->input->pin.pConnectedTo) { |
| CopyMediaType(amt, &This->input->pin.mtCurrent); |
| hr = S_OK; |
| } else |
| hr = S_FALSE; |
| LeaveCriticalSection(&This->filter.csFilter); |
| return hr; |
| } |
| |
| static HRESULT copy_sample(IMediaSample *inputSample, IMemAllocator *allocator, IMediaSample **pOutputSample) |
| { |
| REFERENCE_TIME startTime, endTime; |
| BOOL haveStartTime = TRUE, haveEndTime = TRUE; |
| IMediaSample *outputSample = NULL; |
| BYTE *ptrIn, *ptrOut; |
| AM_MEDIA_TYPE *mediaType = NULL; |
| HRESULT hr; |
| |
| hr = IMediaSample_GetTime(inputSample, &startTime, &endTime); |
| if (hr == S_OK) |
| ; |
| else if (hr == VFW_S_NO_STOP_TIME) |
| haveEndTime = FALSE; |
| else if (hr == VFW_E_SAMPLE_TIME_NOT_SET) |
| haveStartTime = haveEndTime = FALSE; |
| else |
| goto end; |
| |
| hr = IMemAllocator_GetBuffer(allocator, &outputSample, |
| haveStartTime ? &startTime : NULL, haveEndTime ? &endTime : NULL, 0); |
| if (FAILED(hr)) goto end; |
| if (IMediaSample_GetSize(outputSample) < IMediaSample_GetActualDataLength(inputSample)) { |
| ERR("insufficient space in sample\n"); |
| hr = VFW_E_BUFFER_OVERFLOW; |
| goto end; |
| } |
| |
| hr = IMediaSample_SetTime(outputSample, haveStartTime ? &startTime : NULL, haveEndTime ? &endTime : NULL); |
| if (FAILED(hr)) goto end; |
| |
| hr = IMediaSample_GetPointer(inputSample, &ptrIn); |
| if (FAILED(hr)) goto end; |
| hr = IMediaSample_GetPointer(outputSample, &ptrOut); |
| if (FAILED(hr)) goto end; |
| memcpy(ptrOut, ptrIn, IMediaSample_GetActualDataLength(inputSample)); |
| IMediaSample_SetActualDataLength(outputSample, IMediaSample_GetActualDataLength(inputSample)); |
| |
| hr = IMediaSample_SetDiscontinuity(outputSample, IMediaSample_IsDiscontinuity(inputSample) == S_OK); |
| if (FAILED(hr)) goto end; |
| |
| haveStartTime = haveEndTime = TRUE; |
| hr = IMediaSample_GetMediaTime(inputSample, &startTime, &endTime); |
| if (hr == S_OK) |
| ; |
| else if (hr == VFW_S_NO_STOP_TIME) |
| haveEndTime = FALSE; |
| else if (hr == VFW_E_MEDIA_TIME_NOT_SET) |
| haveStartTime = haveEndTime = FALSE; |
| else |
| goto end; |
| hr = IMediaSample_SetMediaTime(outputSample, haveStartTime ? &startTime : NULL, haveEndTime ? &endTime : NULL); |
| if (FAILED(hr)) goto end; |
| |
| hr = IMediaSample_GetMediaType(inputSample, &mediaType); |
| if (FAILED(hr)) goto end; |
| if (hr == S_OK) { |
| hr = IMediaSample_SetMediaType(outputSample, mediaType); |
| if (FAILED(hr)) goto end; |
| } |
| |
| hr = IMediaSample_SetPreroll(outputSample, IMediaSample_IsPreroll(inputSample) == S_OK); |
| if (FAILED(hr)) goto end; |
| |
| hr = IMediaSample_SetSyncPoint(outputSample, IMediaSample_IsSyncPoint(inputSample) == S_OK); |
| if (FAILED(hr)) goto end; |
| |
| end: |
| if (mediaType) |
| DeleteMediaType(mediaType); |
| if (FAILED(hr) && outputSample) { |
| IMediaSample_Release(outputSample); |
| outputSample = NULL; |
| } |
| *pOutputSample = outputSample; |
| return hr; |
| } |
| |
| static HRESULT WINAPI SmartTeeFilterInput_Receive(BaseInputPin *base, IMediaSample *inputSample) |
| { |
| SmartTeeFilter *This = impl_from_BasePin(&base->pin); |
| IMediaSample *captureSample = NULL; |
| IMediaSample *previewSample = NULL; |
| HRESULT hrCapture = VFW_E_NOT_CONNECTED, hrPreview = VFW_E_NOT_CONNECTED; |
| |
| TRACE("(%p)->(%p)\n", This, inputSample); |
| |
| /* Modifying the image coming out of one pin doesn't modify the image |
| * coming out of the other. MSDN claims the filter doesn't copy, |
| * but unless it somehow uses copy-on-write, I just don't see how |
| * that's possible. */ |
| |
| /* FIXME: we should ideally do each of these in a separate thread */ |
| EnterCriticalSection(&This->filter.csFilter); |
| if (This->capture->pin.pConnectedTo) |
| hrCapture = copy_sample(inputSample, This->capture->pAllocator, &captureSample); |
| LeaveCriticalSection(&This->filter.csFilter); |
| if (SUCCEEDED(hrCapture)) |
| hrCapture = BaseOutputPinImpl_Deliver(This->capture, captureSample); |
| if (captureSample) |
| IMediaSample_Release(captureSample); |
| |
| EnterCriticalSection(&This->filter.csFilter); |
| if (This->preview->pin.pConnectedTo) |
| hrPreview = copy_sample(inputSample, This->preview->pAllocator, &previewSample); |
| LeaveCriticalSection(&This->filter.csFilter); |
| /* No timestamps on preview stream: */ |
| if (SUCCEEDED(hrPreview)) |
| hrPreview = IMediaSample_SetTime(previewSample, NULL, NULL); |
| if (SUCCEEDED(hrPreview)) |
| hrPreview = BaseOutputPinImpl_Deliver(This->preview, previewSample); |
| if (previewSample) |
| IMediaSample_Release(previewSample); |
| |
| /* FIXME: how to merge the HRESULTs from the 2 pins? */ |
| if (SUCCEEDED(hrCapture)) |
| return hrCapture; |
| else |
| return hrPreview; |
| } |
| |
| static const BaseInputPinFuncTable SmartTeeFilterInputFuncs = { |
| { |
| SmartTeeFilterInput_CheckMediaType, |
| NULL, |
| SmartTeeFilterInput_GetMediaTypeVersion, |
| SmartTeeFilterInput_GetMediaType |
| }, |
| SmartTeeFilterInput_Receive |
| }; |
| |
| static ULONG WINAPI SmartTeeFilterCapture_AddRef(IPin *iface) |
| { |
| SmartTeeFilter *This = impl_from_IPin(iface); |
| return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface); |
| } |
| |
| static ULONG WINAPI SmartTeeFilterCapture_Release(IPin *iface) |
| { |
| SmartTeeFilter *This = impl_from_IPin(iface); |
| return IBaseFilter_Release(&This->filter.IBaseFilter_iface); |
| } |
| |
| static HRESULT WINAPI SmartTeeFilterCapture_EnumMediaTypes(IPin *iface, IEnumMediaTypes **ppEnum) |
| { |
| SmartTeeFilter *This = impl_from_IPin(iface); |
| HRESULT hr; |
| TRACE("(%p)->(%p)\n", This, ppEnum); |
| EnterCriticalSection(&This->filter.csFilter); |
| if (This->input->pin.pConnectedTo) { |
| hr = BasePinImpl_EnumMediaTypes(iface, ppEnum); |
| } else |
| hr = VFW_E_NOT_CONNECTED; |
| LeaveCriticalSection(&This->filter.csFilter); |
| return hr; |
| } |
| |
| static const IPinVtbl SmartTeeFilterCaptureVtbl = { |
| BaseOutputPinImpl_QueryInterface, |
| SmartTeeFilterCapture_AddRef, |
| SmartTeeFilterCapture_Release, |
| BaseOutputPinImpl_Connect, |
| BaseOutputPinImpl_ReceiveConnection, |
| BaseOutputPinImpl_Disconnect, |
| BasePinImpl_ConnectedTo, |
| BasePinImpl_ConnectionMediaType, |
| BasePinImpl_QueryPinInfo, |
| BasePinImpl_QueryDirection, |
| BasePinImpl_QueryId, |
| BasePinImpl_QueryAccept, |
| SmartTeeFilterCapture_EnumMediaTypes, |
| BasePinImpl_QueryInternalConnections, |
| BaseOutputPinImpl_EndOfStream, |
| BaseOutputPinImpl_BeginFlush, |
| BaseOutputPinImpl_EndFlush, |
| BasePinImpl_NewSegment |
| }; |
| |
| static LONG WINAPI SmartTeeFilterCapture_GetMediaTypeVersion(BasePin *base) |
| { |
| SmartTeeFilter *This = impl_from_BasePin(base); |
| TRACE("(%p)\n", This); |
| return 0; |
| } |
| |
| static HRESULT WINAPI SmartTeeFilterCapture_GetMediaType(BasePin *base, int iPosition, AM_MEDIA_TYPE *amt) |
| { |
| SmartTeeFilter *This = impl_from_BasePin(base); |
| TRACE("(%p, %d, %p)\n", This, iPosition, amt); |
| if (iPosition == 0) { |
| CopyMediaType(amt, &This->input->pin.mtCurrent); |
| return S_OK; |
| } else |
| return S_FALSE; |
| } |
| |
| static HRESULT WINAPI SmartTeeFilterCapture_DecideAllocator(BaseOutputPin *base, IMemInputPin *pPin, IMemAllocator **pAlloc) |
| { |
| SmartTeeFilter *This = impl_from_BasePin(&base->pin); |
| TRACE("(%p, %p, %p)\n", This, pPin, pAlloc); |
| *pAlloc = This->input->pAllocator; |
| IMemAllocator_AddRef(This->input->pAllocator); |
| return IMemInputPin_NotifyAllocator(pPin, This->input->pAllocator, TRUE); |
| } |
| |
| static HRESULT WINAPI SmartTeeFilterCapture_BreakConnect(BaseOutputPin *base) |
| { |
| SmartTeeFilter *This = impl_from_BasePin(&base->pin); |
| FIXME("(%p): stub\n", This); |
| return E_NOTIMPL; |
| } |
| |
| static const BaseOutputPinFuncTable SmartTeeFilterCaptureFuncs = { |
| { |
| NULL, |
| BaseOutputPinImpl_AttemptConnection, |
| SmartTeeFilterCapture_GetMediaTypeVersion, |
| SmartTeeFilterCapture_GetMediaType |
| }, |
| NULL, |
| SmartTeeFilterCapture_DecideAllocator, |
| SmartTeeFilterCapture_BreakConnect |
| }; |
| |
| static ULONG WINAPI SmartTeeFilterPreview_AddRef(IPin *iface) |
| { |
| SmartTeeFilter *This = impl_from_IPin(iface); |
| return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface); |
| } |
| |
| static ULONG WINAPI SmartTeeFilterPreview_Release(IPin *iface) |
| { |
| SmartTeeFilter *This = impl_from_IPin(iface); |
| return IBaseFilter_Release(&This->filter.IBaseFilter_iface); |
| } |
| |
| static HRESULT WINAPI SmartTeeFilterPreview_EnumMediaTypes(IPin *iface, IEnumMediaTypes **ppEnum) |
| { |
| SmartTeeFilter *This = impl_from_IPin(iface); |
| HRESULT hr; |
| TRACE("(%p)->(%p)\n", This, ppEnum); |
| EnterCriticalSection(&This->filter.csFilter); |
| if (This->input->pin.pConnectedTo) { |
| hr = BasePinImpl_EnumMediaTypes(iface, ppEnum); |
| } else |
| hr = VFW_E_NOT_CONNECTED; |
| LeaveCriticalSection(&This->filter.csFilter); |
| return hr; |
| } |
| |
| static const IPinVtbl SmartTeeFilterPreviewVtbl = { |
| BaseOutputPinImpl_QueryInterface, |
| SmartTeeFilterPreview_AddRef, |
| SmartTeeFilterPreview_Release, |
| BaseOutputPinImpl_Connect, |
| BaseOutputPinImpl_ReceiveConnection, |
| BaseOutputPinImpl_Disconnect, |
| BasePinImpl_ConnectedTo, |
| BasePinImpl_ConnectionMediaType, |
| BasePinImpl_QueryPinInfo, |
| BasePinImpl_QueryDirection, |
| BasePinImpl_QueryId, |
| BasePinImpl_QueryAccept, |
| SmartTeeFilterPreview_EnumMediaTypes, |
| BasePinImpl_QueryInternalConnections, |
| BaseOutputPinImpl_EndOfStream, |
| BaseOutputPinImpl_BeginFlush, |
| BaseOutputPinImpl_EndFlush, |
| BasePinImpl_NewSegment |
| }; |
| |
| static LONG WINAPI SmartTeeFilterPreview_GetMediaTypeVersion(BasePin *base) |
| { |
| SmartTeeFilter *This = impl_from_BasePin(base); |
| TRACE("(%p)\n", This); |
| return 0; |
| } |
| |
| static HRESULT WINAPI SmartTeeFilterPreview_GetMediaType(BasePin *base, int iPosition, AM_MEDIA_TYPE *amt) |
| { |
| SmartTeeFilter *This = impl_from_BasePin(base); |
| TRACE("(%p, %d, %p)\n", This, iPosition, amt); |
| if (iPosition == 0) { |
| CopyMediaType(amt, &This->input->pin.mtCurrent); |
| return S_OK; |
| } else |
| return S_FALSE; |
| } |
| |
| static HRESULT WINAPI SmartTeeFilterPreview_DecideAllocator(BaseOutputPin *base, IMemInputPin *pPin, IMemAllocator **pAlloc) |
| { |
| SmartTeeFilter *This = impl_from_BasePin(&base->pin); |
| TRACE("(%p, %p, %p)\n", This, pPin, pAlloc); |
| *pAlloc = This->input->pAllocator; |
| IMemAllocator_AddRef(This->input->pAllocator); |
| return IMemInputPin_NotifyAllocator(pPin, This->input->pAllocator, TRUE); |
| } |
| |
| static HRESULT WINAPI SmartTeeFilterPreview_BreakConnect(BaseOutputPin *base) |
| { |
| SmartTeeFilter *This = impl_from_BasePin(&base->pin); |
| FIXME("(%p): stub\n", This); |
| return E_NOTIMPL; |
| } |
| |
| static const BaseOutputPinFuncTable SmartTeeFilterPreviewFuncs = { |
| { |
| NULL, |
| BaseOutputPinImpl_AttemptConnection, |
| SmartTeeFilterPreview_GetMediaTypeVersion, |
| SmartTeeFilterPreview_GetMediaType |
| }, |
| NULL, |
| SmartTeeFilterPreview_DecideAllocator, |
| SmartTeeFilterPreview_BreakConnect |
| }; |
| IUnknown* WINAPI QCAP_createSmartTeeFilter(IUnknown *outer, HRESULT *phr) |
| { |
| PIN_INFO inputPinInfo = {NULL, PINDIR_INPUT, {'I','n','p','u','t',0}}; |
| PIN_INFO capturePinInfo = {NULL, PINDIR_OUTPUT, {'C','a','p','t','u','r','e',0}}; |
| PIN_INFO previewPinInfo = {NULL, PINDIR_OUTPUT, {'P','r','e','v','i','e','w',0}}; |
| HRESULT hr; |
| SmartTeeFilter *This = NULL; |
| |
| TRACE("(%p, %p)\n", outer, phr); |
| |
| This = CoTaskMemAlloc(sizeof(*This)); |
| if (This == NULL) { |
| hr = E_OUTOFMEMORY; |
| goto end; |
| } |
| memset(This, 0, sizeof(*This)); |
| This->IUnknown_iface.lpVtbl = &UnknownVtbl; |
| if (outer) |
| This->outerUnknown = outer; |
| else |
| This->outerUnknown = &This->IUnknown_iface; |
| |
| BaseFilter_Init(&This->filter, &SmartTeeFilterVtbl, &CLSID_SmartTee, |
| (DWORD_PTR)(__FILE__ ": SmartTeeFilter.csFilter"), &SmartTeeFilterFuncs); |
| |
| inputPinInfo.pFilter = &This->filter.IBaseFilter_iface; |
| hr = BaseInputPin_Construct(&SmartTeeFilterInputVtbl, sizeof(BaseInputPin), &inputPinInfo, |
| &SmartTeeFilterInputFuncs, &This->filter.csFilter, NULL, (IPin**)&This->input); |
| if (FAILED(hr)) |
| goto end; |
| hr = CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC_SERVER, |
| &IID_IMemAllocator, (void**)&This->input->pAllocator); |
| if (FAILED(hr)) |
| goto end; |
| |
| capturePinInfo.pFilter = &This->filter.IBaseFilter_iface; |
| hr = BaseOutputPin_Construct(&SmartTeeFilterCaptureVtbl, sizeof(BaseOutputPin), &capturePinInfo, |
| &SmartTeeFilterCaptureFuncs, &This->filter.csFilter, (IPin**)&This->capture); |
| if (FAILED(hr)) |
| goto end; |
| |
| previewPinInfo.pFilter = &This->filter.IBaseFilter_iface; |
| hr = BaseOutputPin_Construct(&SmartTeeFilterPreviewVtbl, sizeof(BaseOutputPin), &previewPinInfo, |
| &SmartTeeFilterPreviewFuncs, &This->filter.csFilter, (IPin**)&This->preview); |
| |
| end: |
| *phr = hr; |
| if (SUCCEEDED(hr)) { |
| if (outer) |
| return &This->IUnknown_iface; |
| else |
| return (IUnknown*)&This->filter.IBaseFilter_iface; |
| } else { |
| if (This) |
| IBaseFilter_Release(&This->filter.IBaseFilter_iface); |
| return NULL; |
| } |
| } |