/* Video For Windows Steering structure * * Copyright 2005 Maarten Lankhorst * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * */ #define COBJMACROS #include "config.h" #include #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" #include "capture.h" #include "uuids.h" #include "vfwmsgs.h" #include "amvideo.h" #include "strmif.h" #include "ddraw.h" #include "ocidl.h" #include "oleauto.h" WINE_DEFAULT_DEBUG_CHANNEL(qcap); typedef struct VfwCapture { struct strmbase_filter filter; IAMStreamConfig IAMStreamConfig_iface; IAMVideoControl IAMVideoControl_iface; IAMVideoProcAmp IAMVideoProcAmp_iface; IAMFilterMiscFlags IAMFilterMiscFlags_iface; IPersistPropertyBag IPersistPropertyBag_iface; BOOL init; Capture *driver_info; struct strmbase_source source; IKsPropertySet IKsPropertySet_iface; } VfwCapture; static inline VfwCapture *impl_from_strmbase_filter(struct strmbase_filter *iface) { return CONTAINING_RECORD(iface, VfwCapture, filter); } static inline VfwCapture *impl_from_IAMStreamConfig(IAMStreamConfig *iface) { return CONTAINING_RECORD(iface, VfwCapture, IAMStreamConfig_iface); } static inline VfwCapture *impl_from_IAMVideoControl(IAMVideoControl *iface) { return CONTAINING_RECORD(iface, VfwCapture, IAMVideoControl_iface); } static inline VfwCapture *impl_from_IAMVideoProcAmp(IAMVideoProcAmp *iface) { return CONTAINING_RECORD(iface, VfwCapture, IAMVideoProcAmp_iface); } static inline VfwCapture *impl_from_IAMFilterMiscFlags(IAMFilterMiscFlags *iface) { return CONTAINING_RECORD(iface, VfwCapture, IAMFilterMiscFlags_iface); } static inline VfwCapture *impl_from_IPersistPropertyBag(IPersistPropertyBag *iface) { return CONTAINING_RECORD(iface, VfwCapture, IPersistPropertyBag_iface); } static struct strmbase_pin *vfw_capture_get_pin(struct strmbase_filter *iface, unsigned int index) { VfwCapture *This = impl_from_strmbase_filter(iface); if (index >= 1) return NULL; return &This->source.pin; } static void vfw_capture_destroy(struct strmbase_filter *iface) { VfwCapture *filter = impl_from_strmbase_filter(iface); if (filter->init) { if (filter->filter.state != State_Stopped) qcap_driver_stop_stream(filter->driver_info); qcap_driver_destroy(filter->driver_info); } if (filter->source.pin.peer) { IPin_Disconnect(filter->source.pin.peer); IPin_Disconnect(&filter->source.pin.IPin_iface); } strmbase_source_cleanup(&filter->source); strmbase_filter_cleanup(&filter->filter); CoTaskMemFree(filter); ObjectRefCount(FALSE); } static HRESULT vfw_capture_query_interface(struct strmbase_filter *iface, REFIID iid, void **out) { VfwCapture *filter = impl_from_strmbase_filter(iface); if (IsEqualGUID(iid, &IID_IPersistPropertyBag)) *out = &filter->IPersistPropertyBag_iface; else if (IsEqualGUID(iid, &IID_IAMVideoControl)) *out = &filter->IAMVideoControl_iface; else if (IsEqualGUID(iid, &IID_IAMVideoProcAmp)) *out = &filter->IAMVideoProcAmp_iface; else if (IsEqualGUID(iid, &IID_IAMFilterMiscFlags)) *out = &filter->IAMFilterMiscFlags_iface; else return E_NOINTERFACE; IUnknown_AddRef((IUnknown *)*out); return S_OK; } static HRESULT vfw_capture_init_stream(struct strmbase_filter *iface) { VfwCapture *filter = impl_from_strmbase_filter(iface); qcap_driver_init_stream(filter->driver_info); return VFW_S_CANT_CUE; } static HRESULT vfw_capture_start_stream(struct strmbase_filter *iface, REFERENCE_TIME time) { VfwCapture *filter = impl_from_strmbase_filter(iface); qcap_driver_start_stream(filter->driver_info); return S_OK; } static HRESULT vfw_capture_stop_stream(struct strmbase_filter *iface) { VfwCapture *filter = impl_from_strmbase_filter(iface); qcap_driver_stop_stream(filter->driver_info); return VFW_S_CANT_CUE; } static HRESULT vfw_capture_cleanup_stream(struct strmbase_filter *iface) { VfwCapture *filter = impl_from_strmbase_filter(iface); qcap_driver_cleanup_stream(filter->driver_info); return S_OK; } static const struct strmbase_filter_ops filter_ops = { .filter_get_pin = vfw_capture_get_pin, .filter_destroy = vfw_capture_destroy, .filter_query_interface = vfw_capture_query_interface, .filter_init_stream = vfw_capture_init_stream, .filter_start_stream = vfw_capture_start_stream, .filter_stop_stream = vfw_capture_stop_stream, .filter_cleanup_stream = vfw_capture_cleanup_stream, }; static HRESULT WINAPI AMStreamConfig_QueryInterface(IAMStreamConfig *iface, REFIID iid, void **out) { VfwCapture *filter = impl_from_IAMStreamConfig(iface); return IPin_QueryInterface(&filter->source.pin.IPin_iface, iid, out); } static ULONG WINAPI AMStreamConfig_AddRef(IAMStreamConfig *iface) { VfwCapture *filter = impl_from_IAMStreamConfig(iface); return IPin_AddRef(&filter->source.pin.IPin_iface); } static ULONG WINAPI AMStreamConfig_Release(IAMStreamConfig *iface) { VfwCapture *filter = impl_from_IAMStreamConfig(iface); return IPin_Release(&filter->source.pin.IPin_iface); } static HRESULT WINAPI AMStreamConfig_SetFormat(IAMStreamConfig *iface, AM_MEDIA_TYPE *pmt) { HRESULT hr; VfwCapture *This = impl_from_IAMStreamConfig(iface); TRACE("filter %p, mt %p.\n", This, pmt); strmbase_dump_media_type(pmt); if (This->filter.state != State_Stopped) { TRACE("Returning not stopped error\n"); return VFW_E_NOT_STOPPED; } if (!pmt) { TRACE("pmt is NULL\n"); return E_POINTER; } if (!IsEqualGUID(&pmt->majortype, &MEDIATYPE_Video)) return E_FAIL; if (This->source.pin.peer) { hr = IPin_QueryAccept(This->source.pin.peer, pmt); TRACE("Would accept: %d\n", hr); if (hr == S_FALSE) return VFW_E_INVALIDMEDIATYPE; } hr = qcap_driver_set_format(This->driver_info, pmt); if (SUCCEEDED(hr) && This->filter.graph && This->source.pin.peer) { hr = IFilterGraph_Reconnect(This->filter.graph, &This->source.pin.IPin_iface); if (SUCCEEDED(hr)) TRACE("Reconnection completed, with new media format..\n"); } TRACE("Returning: %d\n", hr); return hr; } static HRESULT WINAPI AMStreamConfig_GetFormat( IAMStreamConfig *iface, AM_MEDIA_TYPE **pmt ) { VfwCapture *This = impl_from_IAMStreamConfig(iface); HRESULT hr; TRACE("%p -> (%p)\n", iface, pmt); hr = qcap_driver_get_format(This->driver_info, pmt); if (SUCCEEDED(hr)) strmbase_dump_media_type(*pmt); return hr; } static HRESULT WINAPI AMStreamConfig_GetNumberOfCapabilities(IAMStreamConfig *iface, int *count, int *size) { VfwCapture *filter = impl_from_IAMStreamConfig(iface); TRACE("filter %p, count %p, size %p.\n", filter, count, size); if (!count || !size) return E_POINTER; *count = qcap_driver_get_caps_count(filter->driver_info); *size = sizeof(VIDEO_STREAM_CONFIG_CAPS); return S_OK; } static HRESULT WINAPI AMStreamConfig_GetStreamCaps(IAMStreamConfig *iface, int index, AM_MEDIA_TYPE **pmt, BYTE *vscc) { VfwCapture *filter = impl_from_IAMStreamConfig(iface); TRACE("filter %p, index %d, pmt %p, vscc %p.\n", filter, index, pmt, vscc); return qcap_driver_get_caps(filter->driver_info, index, pmt, (VIDEO_STREAM_CONFIG_CAPS *)vscc); } static const IAMStreamConfigVtbl IAMStreamConfig_VTable = { AMStreamConfig_QueryInterface, AMStreamConfig_AddRef, AMStreamConfig_Release, AMStreamConfig_SetFormat, AMStreamConfig_GetFormat, AMStreamConfig_GetNumberOfCapabilities, AMStreamConfig_GetStreamCaps }; static HRESULT WINAPI AMVideoProcAmp_QueryInterface(IAMVideoProcAmp *iface, REFIID riid, void **ret_iface) { VfwCapture *This = impl_from_IAMVideoProcAmp(iface); return IUnknown_QueryInterface(This->filter.outer_unk, riid, ret_iface); } static ULONG WINAPI AMVideoProcAmp_AddRef(IAMVideoProcAmp * iface) { VfwCapture *This = impl_from_IAMVideoProcAmp(iface); return IUnknown_AddRef(This->filter.outer_unk); } static ULONG WINAPI AMVideoProcAmp_Release(IAMVideoProcAmp * iface) { VfwCapture *This = impl_from_IAMVideoProcAmp(iface); return IUnknown_Release(This->filter.outer_unk); } static HRESULT WINAPI AMVideoProcAmp_GetRange( IAMVideoProcAmp * iface, LONG Property, LONG *pMin, LONG *pMax, LONG *pSteppingDelta, LONG *pDefault, LONG *pCapsFlags ) { VfwCapture *This = impl_from_IAMVideoProcAmp(iface); return qcap_driver_get_prop_range( This->driver_info, Property, pMin, pMax, pSteppingDelta, pDefault, pCapsFlags ); } static HRESULT WINAPI AMVideoProcAmp_Set( IAMVideoProcAmp * iface, LONG Property, LONG lValue, LONG Flags ) { VfwCapture *This = impl_from_IAMVideoProcAmp(iface); return qcap_driver_set_prop(This->driver_info, Property, lValue, Flags); } static HRESULT WINAPI AMVideoProcAmp_Get( IAMVideoProcAmp * iface, LONG Property, LONG *lValue, LONG *Flags ) { VfwCapture *This = impl_from_IAMVideoProcAmp(iface); return qcap_driver_get_prop(This->driver_info, Property, lValue, Flags); } static const IAMVideoProcAmpVtbl IAMVideoProcAmp_VTable = { AMVideoProcAmp_QueryInterface, AMVideoProcAmp_AddRef, AMVideoProcAmp_Release, AMVideoProcAmp_GetRange, AMVideoProcAmp_Set, AMVideoProcAmp_Get, }; static HRESULT WINAPI PPB_QueryInterface(IPersistPropertyBag *iface, REFIID riid, void **ret_iface) { VfwCapture *This = impl_from_IPersistPropertyBag(iface); return IUnknown_QueryInterface(This->filter.outer_unk, riid, ret_iface); } static ULONG WINAPI PPB_AddRef(IPersistPropertyBag * iface) { VfwCapture *This = impl_from_IPersistPropertyBag(iface); return IUnknown_AddRef(This->filter.outer_unk); } static ULONG WINAPI PPB_Release(IPersistPropertyBag * iface) { VfwCapture *This = impl_from_IPersistPropertyBag(iface); return IUnknown_Release(This->filter.outer_unk); } static HRESULT WINAPI PPB_GetClassID( IPersistPropertyBag * iface, CLSID * pClassID ) { VfwCapture *This = impl_from_IPersistPropertyBag(iface); FIXME("%p - stub\n", This); return E_NOTIMPL; } static HRESULT WINAPI PPB_InitNew(IPersistPropertyBag * iface) { VfwCapture *This = impl_from_IPersistPropertyBag(iface); FIXME("%p - stub\n", This); return E_NOTIMPL; } static HRESULT WINAPI PPB_Load( IPersistPropertyBag * iface, IPropertyBag *pPropBag, IErrorLog *pErrorLog ) { static const OLECHAR VFWIndex[] = {'V','F','W','I','n','d','e','x',0}; VfwCapture *This = impl_from_IPersistPropertyBag(iface); HRESULT hr; VARIANT var; TRACE("%p/%p-> (%p, %p)\n", iface, This, pPropBag, pErrorLog); V_VT(&var) = VT_I4; hr = IPropertyBag_Read(pPropBag, VFWIndex, &var, pErrorLog); if (SUCCEEDED(hr)) { This->driver_info = qcap_driver_init(&This->source, V_I4(&var)); if (This->driver_info) { This->init = TRUE; hr = S_OK; } else hr = E_FAIL; } return hr; } static HRESULT WINAPI PPB_Save( IPersistPropertyBag * iface, IPropertyBag *pPropBag, BOOL fClearDirty, BOOL fSaveAllProperties ) { VfwCapture *This = impl_from_IPersistPropertyBag(iface); FIXME("%p - stub\n", This); return E_NOTIMPL; } static const IPersistPropertyBagVtbl IPersistPropertyBag_VTable = { PPB_QueryInterface, PPB_AddRef, PPB_Release, PPB_GetClassID, PPB_InitNew, PPB_Load, PPB_Save }; /* IKsPropertySet interface */ static inline VfwCapture *impl_from_IKsPropertySet(IKsPropertySet *iface) { return CONTAINING_RECORD(iface, VfwCapture, IKsPropertySet_iface); } static HRESULT WINAPI KSP_QueryInterface(IKsPropertySet * iface, REFIID riid, void **ret_iface) { VfwCapture *filter = impl_from_IKsPropertySet(iface); return IPin_QueryInterface(&filter->source.pin.IPin_iface, riid, ret_iface); } static ULONG WINAPI KSP_AddRef(IKsPropertySet * iface) { VfwCapture *filter = impl_from_IKsPropertySet(iface); return IPin_AddRef(&filter->source.pin.IPin_iface); } static ULONG WINAPI KSP_Release(IKsPropertySet * iface) { VfwCapture *filter = impl_from_IKsPropertySet(iface); return IPin_Release(&filter->source.pin.IPin_iface); } static HRESULT WINAPI KSP_Set( IKsPropertySet * iface, REFGUID guidPropSet, DWORD dwPropID, LPVOID pInstanceData, DWORD cbInstanceData, LPVOID pPropData, DWORD cbPropData ) { FIXME("%p: stub\n", iface); return E_NOTIMPL; } static HRESULT WINAPI KSP_Get( IKsPropertySet * iface, REFGUID guidPropSet, DWORD dwPropID, LPVOID pInstanceData, DWORD cbInstanceData, LPVOID pPropData, DWORD cbPropData, DWORD *pcbReturned ) { LPGUID pGuid; TRACE("()\n"); if (!IsEqualIID(guidPropSet, &ROPSETID_Pin)) return E_PROP_SET_UNSUPPORTED; if (pPropData == NULL && pcbReturned == NULL) return E_POINTER; if (pcbReturned) *pcbReturned = sizeof(GUID); if (pPropData == NULL) return S_OK; if (cbPropData < sizeof(GUID)) return E_UNEXPECTED; pGuid = pPropData; *pGuid = PIN_CATEGORY_CAPTURE; FIXME("() Not adding a pin with PIN_CATEGORY_PREVIEW\n"); return S_OK; } static HRESULT WINAPI KSP_QuerySupported( IKsPropertySet * iface, REFGUID guidPropSet, DWORD dwPropID, DWORD *pTypeSupport ) { FIXME("%p: stub\n", iface); return E_NOTIMPL; } static const IKsPropertySetVtbl IKsPropertySet_VTable = { KSP_QueryInterface, KSP_AddRef, KSP_Release, KSP_Set, KSP_Get, KSP_QuerySupported }; static inline VfwCapture *impl_from_strmbase_pin(struct strmbase_pin *pin) { return CONTAINING_RECORD(pin, VfwCapture, source.pin); } static HRESULT source_query_accept(struct strmbase_pin *pin, const AM_MEDIA_TYPE *mt) { VfwCapture *filter = impl_from_strmbase_pin(pin); return qcap_driver_check_format(filter->driver_info, mt); } static HRESULT source_get_media_type(struct strmbase_pin *pin, unsigned int index, AM_MEDIA_TYPE *pmt) { VfwCapture *filter = impl_from_strmbase_pin(pin); AM_MEDIA_TYPE *vfw_pmt; HRESULT hr; if (index >= qcap_driver_get_caps_count(filter->driver_info)) return VFW_S_NO_MORE_ITEMS; hr = qcap_driver_get_caps(filter->driver_info, index, &vfw_pmt, NULL); if (SUCCEEDED(hr)) { CopyMediaType(pmt, vfw_pmt); DeleteMediaType(vfw_pmt); } return hr; } static HRESULT source_query_interface(struct strmbase_pin *iface, REFIID iid, void **out) { VfwCapture *filter = impl_from_strmbase_pin(iface); if (IsEqualGUID(iid, &IID_IKsPropertySet)) *out = &filter->IKsPropertySet_iface; else if (IsEqualGUID(iid, &IID_IAMStreamConfig)) *out = &filter->IAMStreamConfig_iface; else return E_NOINTERFACE; IUnknown_AddRef((IUnknown *)*out); return S_OK; } static HRESULT WINAPI VfwPin_DecideBufferSize(struct strmbase_source *iface, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest) { ALLOCATOR_PROPERTIES actual; /* What we put here doesn't matter, the driver function should override it then commit */ if (!ppropInputRequest->cBuffers) ppropInputRequest->cBuffers = 3; if (!ppropInputRequest->cbBuffer) ppropInputRequest->cbBuffer = 230400; if (!ppropInputRequest->cbAlign) ppropInputRequest->cbAlign = 1; return IMemAllocator_SetProperties(pAlloc, ppropInputRequest, &actual); } static const struct strmbase_source_ops source_ops = { .base.pin_query_accept = source_query_accept, .base.pin_get_media_type = source_get_media_type, .base.pin_query_interface = source_query_interface, .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection, .pfnDecideBufferSize = VfwPin_DecideBufferSize, .pfnDecideAllocator = BaseOutputPinImpl_DecideAllocator, }; static HRESULT WINAPI misc_flags_QueryInterface(IAMFilterMiscFlags *iface, REFIID riid, void **ppv) { VfwCapture *filter = impl_from_IAMFilterMiscFlags(iface); return IUnknown_QueryInterface(filter->filter.outer_unk, riid, ppv); } static ULONG WINAPI misc_flags_AddRef(IAMFilterMiscFlags *iface) { VfwCapture *filter = impl_from_IAMFilterMiscFlags(iface); return IUnknown_AddRef(filter->filter.outer_unk); } static ULONG WINAPI misc_flags_Release(IAMFilterMiscFlags *iface) { VfwCapture *filter = impl_from_IAMFilterMiscFlags(iface); return IUnknown_Release(filter->filter.outer_unk); } static ULONG WINAPI misc_flags_GetMiscFlags(IAMFilterMiscFlags *iface) { return AM_FILTER_MISC_FLAGS_IS_SOURCE; } static const IAMFilterMiscFlagsVtbl IAMFilterMiscFlags_VTable = { misc_flags_QueryInterface, misc_flags_AddRef, misc_flags_Release, misc_flags_GetMiscFlags }; static HRESULT WINAPI video_control_QueryInterface(IAMVideoControl *iface, REFIID riid, void **ppv) { VfwCapture *filter = impl_from_IAMVideoControl(iface); return IUnknown_QueryInterface(filter->filter.outer_unk, riid, ppv); } static ULONG WINAPI video_control_AddRef(IAMVideoControl *iface) { VfwCapture *filter = impl_from_IAMVideoControl(iface); return IUnknown_AddRef(filter->filter.outer_unk); } static ULONG WINAPI video_control_Release(IAMVideoControl *iface) { VfwCapture *filter = impl_from_IAMVideoControl(iface); return IUnknown_Release(filter->filter.outer_unk); } static HRESULT WINAPI video_control_GetCaps(IAMVideoControl *iface, IPin *pin, LONG *flags) { VfwCapture *filter = impl_from_IAMVideoControl(iface); FIXME("filter %p, pin %p, flags %p: stub.\n", filter, pin, flags); return E_NOTIMPL; } static HRESULT WINAPI video_control_SetMode(IAMVideoControl *iface, IPin *pin, LONG mode) { VfwCapture *filter = impl_from_IAMVideoControl(iface); FIXME("filter %p, pin %p, mode %d: stub.\n", filter, pin, mode); return E_NOTIMPL; } static HRESULT WINAPI video_control_GetMode(IAMVideoControl *iface, IPin *pin, LONG *mode) { VfwCapture *filter = impl_from_IAMVideoControl(iface); FIXME("filter %p, pin %p, mode %p: stub.\n", filter, pin, mode); return E_NOTIMPL; } static HRESULT WINAPI video_control_GetCurrentActualFrameRate(IAMVideoControl *iface, IPin *pin, LONGLONG *frame_rate) { VfwCapture *filter = impl_from_IAMVideoControl(iface); FIXME("filter %p, pin %p, frame rate %p: stub.\n", filter, pin, frame_rate); return E_NOTIMPL; } static HRESULT WINAPI video_control_GetMaxAvailableFrameRate(IAMVideoControl *iface, IPin *pin, LONG index, SIZE dimensions, LONGLONG *frame_rate) { VfwCapture *filter = impl_from_IAMVideoControl(iface); FIXME("filter %p, pin %p, index %d, dimensions (%dx%d), frame rate %p: stub.\n", filter, pin, index, dimensions.cx, dimensions.cy, frame_rate); return E_NOTIMPL; } static HRESULT WINAPI video_control_GetFrameRateList(IAMVideoControl *iface, IPin *pin, LONG index, SIZE dimensions, LONG *list_size, LONGLONG **frame_rate) { VfwCapture *filter = impl_from_IAMVideoControl(iface); FIXME("filter %p, pin %p, index %d, dimensions (%dx%d), list size %p, frame rate: %p: stub.\n", filter, pin, index, dimensions.cx, dimensions.cy, list_size, frame_rate); return E_NOTIMPL; } static const IAMVideoControlVtbl IAMVideoControl_VTable = { video_control_QueryInterface, video_control_AddRef, video_control_Release, video_control_GetCaps, video_control_SetMode, video_control_GetMode, video_control_GetCurrentActualFrameRate, video_control_GetMaxAvailableFrameRate, video_control_GetFrameRateList }; HRESULT vfw_capture_create(IUnknown *outer, IUnknown **out) { static const WCHAR source_name[] = {'O','u','t','p','u','t',0}; VfwCapture *object; if (!(object = CoTaskMemAlloc(sizeof(*object)))) return E_OUTOFMEMORY; strmbase_filter_init(&object->filter, outer, &CLSID_VfwCapture, &filter_ops); object->IAMStreamConfig_iface.lpVtbl = &IAMStreamConfig_VTable; object->IAMVideoControl_iface.lpVtbl = &IAMVideoControl_VTable; object->IAMVideoProcAmp_iface.lpVtbl = &IAMVideoProcAmp_VTable; object->IAMFilterMiscFlags_iface.lpVtbl = &IAMFilterMiscFlags_VTable; object->IPersistPropertyBag_iface.lpVtbl = &IPersistPropertyBag_VTable; object->init = FALSE; strmbase_source_init(&object->source, &object->filter, source_name, &source_ops); object->IKsPropertySet_iface.lpVtbl = &IKsPropertySet_VTable; TRACE("Created VFW capture filter %p.\n", object); ObjectRefCount(TRUE); *out = &object->filter.IUnknown_inner; return S_OK; }