https://git.reactos.org/?p=reactos.git;a=commitdiff;h=56aa5137e7d582388c842d...
commit 56aa5137e7d582388c842d798a3c55dc10eab0ed Author: Oleg Dubinskiy oleg.dubinskij2013@yandex.ua AuthorDate: Sat Mar 7 21:42:35 2020 +0200 Commit: Hermès Bélusca-Maïto hermes.belusca-maito@reactos.org CommitDate: Fri Apr 3 23:59:12 2020 +0200
[QCAP] Import qcap.dll from Wine Staging 3.3. CORE-16350 (#2421)
Fix also MSVC build.
[DOC] Add qcap entry in README.WINE
Co-Authored-By: Thomas Faber 18138474+ThFabba@users.noreply.github.com --- dll/directx/wine/CMakeLists.txt | 1 + dll/directx/wine/qcap/CMakeLists.txt | 28 + dll/directx/wine/qcap/audiorecord.c | 302 ++++ dll/directx/wine/qcap/avico.c | 738 ++++++++++ dll/directx/wine/qcap/avimux.c | 2421 ++++++++++++++++++++++++++++++++ dll/directx/wine/qcap/capture.h | 37 + dll/directx/wine/qcap/capturegraph.c | 881 ++++++++++++ dll/directx/wine/qcap/enummedia.c | 44 + dll/directx/wine/qcap/qcap.spec | 4 + dll/directx/wine/qcap/qcap_main.c | 198 +++ dll/directx/wine/qcap/qcap_main.h | 62 + dll/directx/wine/qcap/smartteefilter.c | 697 +++++++++ dll/directx/wine/qcap/v4l.c | 978 +++++++++++++ dll/directx/wine/qcap/version.rc | 26 + dll/directx/wine/qcap/vfwcapture.c | 834 +++++++++++ dll/directx/wine/qcap/yuv.c | 200 +++ media/doc/README.WINE | 1 + 17 files changed, 7452 insertions(+)
diff --git a/dll/directx/wine/CMakeLists.txt b/dll/directx/wine/CMakeLists.txt index 9cccbb14662..ee7f69896ef 100644 --- a/dll/directx/wine/CMakeLists.txt +++ b/dll/directx/wine/CMakeLists.txt @@ -36,6 +36,7 @@ add_subdirectory(dpnhpast) add_subdirectory(dsound) add_subdirectory(dxdiagn) add_subdirectory(msdmo) +add_subdirectory(qcap) add_subdirectory(qedit) add_subdirectory(quartz) add_subdirectory(wined3d) diff --git a/dll/directx/wine/qcap/CMakeLists.txt b/dll/directx/wine/qcap/CMakeLists.txt new file mode 100644 index 00000000000..cbd24d39bcc --- /dev/null +++ b/dll/directx/wine/qcap/CMakeLists.txt @@ -0,0 +1,28 @@ + +add_definitions(-D__WINESRC__) + +include_directories(${REACTOS_SOURCE_DIR}/sdk/include/reactos/wine) +spec2def(qcap.dll qcap.spec) + +list(APPEND SOURCE + audiorecord.c + avico.c + avimux.c + capturegraph.c + enummedia.c + qcap_main.c + smartteefilter.c + v4l.c + vfwcapture.c + yuv.c) + +add_library(qcap MODULE ${SOURCE} + ${CMAKE_CURRENT_BINARY_DIR}/qcap_stubs.c + ${CMAKE_CURRENT_BINARY_DIR}/qcap.def + version.rc) + +set_module_type(qcap win32dll) +target_link_libraries(qcap strmbase strmiids uuid wine) +add_importlibs(qcap ole32 oleaut32 gdi32 advapi32 advapi32_vista msvcrt kernel32 ntdll) +add_delay_importlibs(qcap msvfw32) +add_cd_file(TARGET qcap DESTINATION reactos/system32 FOR all) diff --git a/dll/directx/wine/qcap/audiorecord.c b/dll/directx/wine/qcap/audiorecord.c new file mode 100644 index 00000000000..1187d4afab2 --- /dev/null +++ b/dll/directx/wine/qcap/audiorecord.c @@ -0,0 +1,302 @@ +/* Implementation of the Audio Capture Filter (CLSID_AudioRecord) + * + * 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; + IPersistPropertyBag IPersistPropertyBag_iface; + BaseOutputPin *output; +} AudioRecord; + +static inline AudioRecord *impl_from_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, AudioRecord, IUnknown_iface); +} + +static inline AudioRecord *impl_from_BaseFilter(BaseFilter *filter) +{ + return CONTAINING_RECORD(filter, AudioRecord, filter); +} + +static inline AudioRecord *impl_from_IBaseFilter(IBaseFilter *iface) +{ + BaseFilter *filter = CONTAINING_RECORD(iface, BaseFilter, IBaseFilter_iface); + return impl_from_BaseFilter(filter); +} + +static inline AudioRecord *impl_from_IPersistPropertyBag(IPersistPropertyBag *iface) +{ + return CONTAINING_RECORD(iface, AudioRecord, IPersistPropertyBag_iface); +} + +static HRESULT WINAPI Unknown_QueryInterface(IUnknown *iface, REFIID riid, LPVOID *ppv) +{ + AudioRecord *This = impl_from_IUnknown(iface); + if (IsEqualIID(riid, &IID_IUnknown)) { + TRACE("(%p)->(IID_IUnknown, %p)\n", This, ppv); + *ppv = &This->filter.IBaseFilter_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 if (IsEqualIID(riid, &IID_IPersistPropertyBag)) { + TRACE("(%p)->(IID_IPersistPropertyBag, %p)\n", This, ppv); + *ppv = &This->IPersistPropertyBag_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) +{ + AudioRecord *This = impl_from_IUnknown(iface); + return BaseFilterImpl_AddRef(&This->filter.IBaseFilter_iface); +} + +static ULONG WINAPI Unknown_Release(IUnknown *iface) +{ + AudioRecord *This = impl_from_IUnknown(iface); + ULONG ref = BaseFilterImpl_Release(&This->filter.IBaseFilter_iface); + TRACE("(%p/%p)->() ref=%d\n", iface, This, ref); + if (!ref) { + CoTaskMemFree(This); + } + return ref; +} + +static const IUnknownVtbl UnknownVtbl = { + Unknown_QueryInterface, + Unknown_AddRef, + Unknown_Release +}; + +static HRESULT WINAPI AudioRecord_QueryInterface(IBaseFilter *iface, REFIID riid, void **ppv) +{ + AudioRecord *This = impl_from_IBaseFilter(iface); + return IUnknown_QueryInterface(This->outerUnknown, riid, ppv); +} + +static ULONG WINAPI AudioRecord_AddRef(IBaseFilter *iface) +{ + AudioRecord *This = impl_from_IBaseFilter(iface); + return IUnknown_AddRef(This->outerUnknown); +} + +static ULONG WINAPI AudioRecord_Release(IBaseFilter *iface) +{ + AudioRecord *This = impl_from_IBaseFilter(iface); + return IUnknown_Release(This->outerUnknown); +} + +static HRESULT WINAPI AudioRecord_Stop(IBaseFilter *iface) +{ + AudioRecord *This = impl_from_IBaseFilter(iface); + FIXME("(%p): stub\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioRecord_Pause(IBaseFilter *iface) +{ + AudioRecord *This = impl_from_IBaseFilter(iface); + FIXME("(%p): stub\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioRecord_Run(IBaseFilter *iface, REFERENCE_TIME tStart) +{ + AudioRecord *This = impl_from_IBaseFilter(iface); + FIXME("(%p, %s): stub\n", This, wine_dbgstr_longlong(tStart)); + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioRecord_FindPin(IBaseFilter *iface, LPCWSTR Id, IPin **ppPin) +{ + AudioRecord *This = impl_from_IBaseFilter(iface); + FIXME("(%p)->(%s, %p): stub\n", This, debugstr_w(Id), ppPin); + return E_NOTIMPL; +} + +static const IBaseFilterVtbl AudioRecordVtbl = { + AudioRecord_QueryInterface, + AudioRecord_AddRef, + AudioRecord_Release, + BaseFilterImpl_GetClassID, + AudioRecord_Stop, + AudioRecord_Pause, + AudioRecord_Run, + BaseFilterImpl_GetState, + BaseFilterImpl_SetSyncSource, + BaseFilterImpl_GetSyncSource, + BaseFilterImpl_EnumPins, + AudioRecord_FindPin, + BaseFilterImpl_QueryFilterInfo, + BaseFilterImpl_JoinFilterGraph, + BaseFilterImpl_QueryVendorInfo +}; + +static IPin* WINAPI AudioRecord_GetPin(BaseFilter *iface, int pos) +{ + AudioRecord *This = impl_from_BaseFilter(iface); + FIXME("(%p, %d): stub\n", This, pos); + return NULL; +} + +static LONG WINAPI AudioRecord_GetPinCount(BaseFilter *iface) +{ + AudioRecord *This = impl_from_BaseFilter(iface); + FIXME("(%p): stub\n", This); + return 0; +} + +static const BaseFilterFuncTable AudioRecordFuncs = { + AudioRecord_GetPin, + AudioRecord_GetPinCount +}; + +static HRESULT WINAPI PPB_QueryInterface(IPersistPropertyBag *iface, REFIID riid, LPVOID *ppv) +{ + AudioRecord *This = impl_from_IPersistPropertyBag(iface); + return IUnknown_QueryInterface(This->outerUnknown, riid, ppv); +} + +static ULONG WINAPI PPB_AddRef(IPersistPropertyBag *iface) +{ + AudioRecord *This = impl_from_IPersistPropertyBag(iface); + return IUnknown_AddRef(This->outerUnknown); +} + +static ULONG WINAPI PPB_Release(IPersistPropertyBag *iface) +{ + AudioRecord *This = impl_from_IPersistPropertyBag(iface); + return IUnknown_Release(This->outerUnknown); +} + +static HRESULT WINAPI PPB_GetClassID(IPersistPropertyBag *iface, CLSID *pClassID) +{ + AudioRecord *This = impl_from_IPersistPropertyBag(iface); + TRACE("(%p/%p)->(%p)\n", iface, This, pClassID); + return IBaseFilter_GetClassID(&This->filter.IBaseFilter_iface, pClassID); +} + +static HRESULT WINAPI PPB_InitNew(IPersistPropertyBag *iface) +{ + AudioRecord *This = impl_from_IPersistPropertyBag(iface); + FIXME("(%p/%p)->(): stub\n", iface, This); + return E_NOTIMPL; +} + +static HRESULT WINAPI PPB_Load(IPersistPropertyBag *iface, IPropertyBag *pPropBag, + IErrorLog *pErrorLog) +{ + AudioRecord *This = impl_from_IPersistPropertyBag(iface); + HRESULT hr; + VARIANT var; + static const WCHAR WaveInIDW[] = {'W','a','v','e','I','n','I','D',0}; + + TRACE("(%p/%p)->(%p, %p)\n", iface, This, pPropBag, pErrorLog); + + V_VT(&var) = VT_I4; + hr = IPropertyBag_Read(pPropBag, WaveInIDW, &var, pErrorLog); + if (SUCCEEDED(hr)) + { + FIXME("FIXME: implement opening waveIn device %d\n", V_I4(&var)); + } + + return hr; +} + +static HRESULT WINAPI PPB_Save(IPersistPropertyBag *iface, IPropertyBag *pPropBag, + BOOL fClearDirty, BOOL fSaveAllProperties) +{ + AudioRecord *This = impl_from_IPersistPropertyBag(iface); + FIXME("(%p/%p)->(%p, %u, %u): stub\n", iface, This, pPropBag, fClearDirty, fSaveAllProperties); + return E_NOTIMPL; +} + +static const IPersistPropertyBagVtbl PersistPropertyBagVtbl = +{ + PPB_QueryInterface, + PPB_AddRef, + PPB_Release, + PPB_GetClassID, + PPB_InitNew, + PPB_Load, + PPB_Save +}; + +IUnknown* WINAPI QCAP_createAudioCaptureFilter(IUnknown *outer, HRESULT *phr) +{ + HRESULT hr; + AudioRecord *This = NULL; + + FIXME("(%p, %p): the entire CLSID_AudioRecord implementation is just stubs\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; + This->IPersistPropertyBag_iface.lpVtbl = &PersistPropertyBagVtbl; + if (outer) + This->outerUnknown = outer; + else + This->outerUnknown = &This->IUnknown_iface; + + hr = BaseFilter_Init(&This->filter, &AudioRecordVtbl, &CLSID_AudioRecord, + (DWORD_PTR)(__FILE__ ": AudioRecord.csFilter"), &AudioRecordFuncs); + +end: + *phr = hr; + if (SUCCEEDED(hr)) { + return (IUnknown*)&This->filter.IBaseFilter_iface; + } else { + if (This) + IBaseFilter_Release(&This->filter.IBaseFilter_iface); + return NULL; + } +} diff --git a/dll/directx/wine/qcap/avico.c b/dll/directx/wine/qcap/avico.c new file mode 100644 index 00000000000..32f3b56e7a5 --- /dev/null +++ b/dll/directx/wine/qcap/avico.c @@ -0,0 +1,738 @@ +/* + * Copyright 2013 Jacek Caban for CodeWeavers + * + * 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 "dshow.h" +#include "vfw.h" +#include "aviriff.h" + +#include "qcap_main.h" + +#include "wine/debug.h" +#include "wine/heap.h" + +WINE_DEFAULT_DEBUG_CHANNEL(qcap); + +typedef struct { + BaseFilter filter; + IPersistPropertyBag IPersistPropertyBag_iface; + + BaseInputPin *in; + BaseOutputPin *out; + + DWORD fcc_handler; + HIC hic; + + VIDEOINFOHEADER *videoinfo; + size_t videoinfo_size; + DWORD driver_flags; + DWORD max_frame_size; + + DWORD frame_cnt; +} AVICompressor; + +static inline AVICompressor *impl_from_BaseFilter(BaseFilter *filter) +{ + return CONTAINING_RECORD(filter, AVICompressor, filter); +} + +static inline AVICompressor *impl_from_IBaseFilter(IBaseFilter *iface) +{ + BaseFilter *filter = CONTAINING_RECORD(iface, BaseFilter, IBaseFilter_iface); + return impl_from_BaseFilter(filter); +} + +static inline AVICompressor *impl_from_BasePin(BasePin *pin) +{ + return impl_from_IBaseFilter(pin->pinInfo.pFilter); +} + +static HRESULT ensure_driver(AVICompressor *This) +{ + if(This->hic) + return S_OK; + + This->hic = ICOpen(FCC('v','i','d','c'), This->fcc_handler, ICMODE_COMPRESS); + if(!This->hic) { + FIXME("ICOpen failed\n"); + return E_FAIL; + } + + return S_OK; +} + +static HRESULT fill_format_info(AVICompressor *This, VIDEOINFOHEADER *src_videoinfo) +{ + DWORD size; + ICINFO icinfo; + HRESULT hres; + + hres = ensure_driver(This); + if(hres != S_OK) + return hres; + + size = ICGetInfo(This->hic, &icinfo, sizeof(icinfo)); + if(size != sizeof(icinfo)) + return E_FAIL; + + size = ICCompressGetFormatSize(This->hic, &src_videoinfo->bmiHeader); + if(!size) { + FIXME("ICCompressGetFormatSize failed\n"); + return E_FAIL; + } + + size += FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader); + This->videoinfo = heap_alloc(size); + if(!This->videoinfo) + return E_OUTOFMEMORY; + + This->videoinfo_size = size; + This->driver_flags = icinfo.dwFlags; + memset(This->videoinfo, 0, sizeof(*This->videoinfo)); + ICCompressGetFormat(This->hic, &src_videoinfo->bmiHeader, &This->videoinfo->bmiHeader); + + This->videoinfo->dwBitRate = 10000000/src_videoinfo->AvgTimePerFrame * This->videoinfo->bmiHeader.biSizeImage * 8; + This->videoinfo->AvgTimePerFrame = src_videoinfo->AvgTimePerFrame; + This->max_frame_size = This->videoinfo->bmiHeader.biSizeImage; + return S_OK; +} + +static HRESULT WINAPI AVICompressor_QueryInterface(IBaseFilter *iface, REFIID riid, void **ppv) +{ + AVICompressor *This = impl_from_IBaseFilter(iface); + + if(IsEqualIID(riid, &IID_IUnknown)) { + TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); + *ppv = &This->filter.IBaseFilter_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 if(IsEqualIID(riid, &IID_IPersistPropertyBag)) { + TRACE("(%p)->(IID_IPersistPropertyBag %p)\n", This, ppv); + *ppv = &This->IPersistPropertyBag_iface; + }else { + FIXME("no interface for %s\n", debugstr_guid(riid)); + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; + +} + +static ULONG WINAPI AVICompressor_Release(IBaseFilter *iface) +{ + AVICompressor *This = impl_from_IBaseFilter(iface); + ULONG ref = BaseFilterImpl_Release(&This->filter.IBaseFilter_iface); + + TRACE("(%p) ref=%d\n", This, ref); + + if(!ref) { + if(This->hic) + ICClose(This->hic); + heap_free(This->videoinfo); + if(This->in) + BaseInputPinImpl_Release(&This->in->pin.IPin_iface); + if(This->out) + BaseOutputPinImpl_Release(&This->out->pin.IPin_iface); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI AVICompressor_Stop(IBaseFilter *iface) +{ + AVICompressor *This = impl_from_IBaseFilter(iface); + + TRACE("(%p)\n", This); + + if(This->filter.state == State_Stopped) + return S_OK; + + ICCompressEnd(This->hic); + This->filter.state = State_Stopped; + return S_OK; +} + +static HRESULT WINAPI AVICompressor_Pause(IBaseFilter *iface) +{ + AVICompressor *This = impl_from_IBaseFilter(iface); + FIXME("(%p)\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI AVICompressor_Run(IBaseFilter *iface, REFERENCE_TIME tStart) +{ + AVICompressor *This = impl_from_IBaseFilter(iface); + HRESULT hres; + + TRACE("(%p)->(%s)\n", This, wine_dbgstr_longlong(tStart)); + + if(This->filter.state == State_Running) + return S_OK; + + hres = IMemAllocator_Commit(This->out->pAllocator); + if(FAILED(hres)) { + FIXME("Commit failed: %08x\n", hres); + return hres; + } + + This->frame_cnt = 0; + + This->filter.state = State_Running; + return S_OK; +} + +static HRESULT WINAPI AVICompressor_FindPin(IBaseFilter *iface, LPCWSTR Id, IPin **ppPin) +{ + AVICompressor *This = impl_from_IBaseFilter(iface); + FIXME("(%p)->(%s %p)\n", This, debugstr_w(Id), ppPin); + return VFW_E_NOT_FOUND; +} + +static HRESULT WINAPI AVICompressor_QueryFilterInfo(IBaseFilter *iface, FILTER_INFO *pInfo) +{ + AVICompressor *This = impl_from_IBaseFilter(iface); + FIXME("(%p)->(%p)\n", This, pInfo); + return E_NOTIMPL; +} + +static HRESULT WINAPI AVICompressor_QueryVendorInfo(IBaseFilter *iface, LPWSTR *pVendorInfo) +{ + AVICompressor *This = impl_from_IBaseFilter(iface); + FIXME("(%p)->(%p)\n", This, pVendorInfo); + return E_NOTIMPL; +} + +static const IBaseFilterVtbl AVICompressorVtbl = { + AVICompressor_QueryInterface, + BaseFilterImpl_AddRef, + AVICompressor_Release, + BaseFilterImpl_GetClassID, + AVICompressor_Stop, + AVICompressor_Pause, + AVICompressor_Run, + BaseFilterImpl_GetState, + BaseFilterImpl_SetSyncSource, + BaseFilterImpl_GetSyncSource, + BaseFilterImpl_EnumPins, + AVICompressor_FindPin, + AVICompressor_QueryFilterInfo, + BaseFilterImpl_JoinFilterGraph, + AVICompressor_QueryVendorInfo +}; + +static IPin* WINAPI AVICompressor_GetPin(BaseFilter *iface, int pos) +{ + AVICompressor *This = impl_from_BaseFilter(iface); + IPin *ret; + + TRACE("(%p)->(%d)\n", This, pos); + + switch(pos) { + case 0: + ret = &This->in->pin.IPin_iface; + break; + case 1: + ret = &This->out->pin.IPin_iface; + break; + default: + TRACE("No pin %d\n", pos); + return NULL; + }; + + IPin_AddRef(ret); + return ret; +} + +static LONG WINAPI AVICompressor_GetPinCount(BaseFilter *iface) +{ + return 2; +} + +static const BaseFilterFuncTable filter_func_table = { + AVICompressor_GetPin, + AVICompressor_GetPinCount +}; + +static AVICompressor *impl_from_IPersistPropertyBag(IPersistPropertyBag *iface) +{ + return CONTAINING_RECORD(iface, AVICompressor, IPersistPropertyBag_iface); +} + +static HRESULT WINAPI AVICompressorPropertyBag_QueryInterface(IPersistPropertyBag *iface, REFIID riid, void **ppv) +{ + AVICompressor *This = impl_from_IPersistPropertyBag(iface); + return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv); +} + +static ULONG WINAPI AVICompressorPropertyBag_AddRef(IPersistPropertyBag *iface) +{ + AVICompressor *This = impl_from_IPersistPropertyBag(iface); + return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface); +} + +static ULONG WINAPI AVICompressorPropertyBag_Release(IPersistPropertyBag *iface) +{ + AVICompressor *This = impl_from_IPersistPropertyBag(iface); + return IBaseFilter_Release(&This->filter.IBaseFilter_iface); +} + +static HRESULT WINAPI AVICompressorPropertyBag_GetClassID(IPersistPropertyBag *iface, CLSID *pClassID) +{ + AVICompressor *This = impl_from_IPersistPropertyBag(iface); + return IBaseFilter_GetClassID(&This->filter.IBaseFilter_iface, pClassID); +} + +static HRESULT WINAPI AVICompressorPropertyBag_InitNew(IPersistPropertyBag *iface) +{ + AVICompressor *This = impl_from_IPersistPropertyBag(iface); + FIXME("(%p)->()\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI AVICompressorPropertyBag_Load(IPersistPropertyBag *iface, IPropertyBag *pPropBag, IErrorLog *pErrorLog) +{ + AVICompressor *This = impl_from_IPersistPropertyBag(iface); + BSTR str; + VARIANT v; + HRESULT hres; + + static const WCHAR fcc_handlerW[] = {'F','c','c','H','a','n','d','l','e','r',0}; + + TRACE("(%p)->(%p %p)\n", This, pPropBag, pErrorLog); + + V_VT(&v) = VT_EMPTY; + hres = IPropertyBag_Read(pPropBag, fcc_handlerW, &v, NULL); + if(FAILED(hres)) { + WARN("Could not read FccHandler: %08x\n", hres); + return hres; + } + + if(V_VT(&v) != VT_BSTR) { + FIXME("Got vt %d\n", V_VT(&v)); + VariantClear(&v); + return E_FAIL; + } + + str = V_BSTR(&v); + TRACE("FccHandler = %s\n", debugstr_w(str)); + if(SysStringLen(str) != 4) { + FIXME("Invalid FccHandler len\n"); + SysFreeString(str); + return E_FAIL; + } + + This->fcc_handler = FCC(str[0], str[1], str[2], str[3]); + SysFreeString(str); + return S_OK; +} + +static HRESULT WINAPI AVICompressorPropertyBag_Save(IPersistPropertyBag *iface, IPropertyBag *pPropBag, + BOOL fClearDirty, BOOL fSaveAllProperties) +{ + AVICompressor *This = impl_from_IPersistPropertyBag(iface); + FIXME("(%p)->(%p %x %x)\n", This, pPropBag, fClearDirty, fSaveAllProperties); + return E_NOTIMPL; +} + +static const IPersistPropertyBagVtbl PersistPropertyBagVtbl = { + AVICompressorPropertyBag_QueryInterface, + AVICompressorPropertyBag_AddRef, + AVICompressorPropertyBag_Release, + AVICompressorPropertyBag_GetClassID, + AVICompressorPropertyBag_InitNew, + AVICompressorPropertyBag_Load, + AVICompressorPropertyBag_Save +}; + +static inline AVICompressor *impl_from_IPin(IPin *iface) +{ + BasePin *bp = CONTAINING_RECORD(iface, BasePin, IPin_iface); + return impl_from_IBaseFilter(bp->pinInfo.pFilter); +} + +static HRESULT WINAPI AVICompressorIn_QueryInterface(IPin *iface, REFIID riid, void **ppv) +{ + return BaseInputPinImpl_QueryInterface(iface, riid, ppv); +} + +static ULONG WINAPI AVICompressorIn_AddRef(IPin *iface) +{ + AVICompressor *This = impl_from_IPin(iface); + return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface); +} + +static ULONG WINAPI AVICompressorIn_Release(IPin *iface) +{ + AVICompressor *This = impl_from_IPin(iface); + return IBaseFilter_Release(&This->filter.IBaseFilter_iface); +} + +static HRESULT WINAPI AVICompressorIn_ReceiveConnection(IPin *iface, + IPin *pConnector, const AM_MEDIA_TYPE *pmt) +{ + AVICompressor *This = impl_from_IPin(iface); + HRESULT hres; + + TRACE("(%p)->(%p AM_MEDIA_TYPE(%p))\n", This, pConnector, pmt); + dump_AM_MEDIA_TYPE(pmt); + + hres = BaseInputPinImpl_ReceiveConnection(iface, pConnector, pmt); + if(FAILED(hres)) + return hres; + + hres = fill_format_info(This, (VIDEOINFOHEADER*)pmt->pbFormat); + if(FAILED(hres)) + BasePinImpl_Disconnect(iface); + return hres; +} + +static HRESULT WINAPI AVICompressorIn_Disconnect(IPin *iface) +{ + AVICompressor *This = impl_from_IPin(iface); + HRESULT hres; + + TRACE("(%p)\n", This); + + hres = BasePinImpl_Disconnect(iface); + if(FAILED(hres)) + return hres; + + heap_free(This->videoinfo); + This->videoinfo = NULL; + return S_OK; +} + +static const IPinVtbl AVICompressorInputPinVtbl = { + AVICompressorIn_QueryInterface, + AVICompressorIn_AddRef, + AVICompressorIn_Release, + BaseInputPinImpl_Connect, + AVICompressorIn_ReceiveConnection, + AVICompressorIn_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 AVICompressorIn_CheckMediaType(BasePin *base, const AM_MEDIA_TYPE *pmt) +{ + AVICompressor *This = impl_from_BasePin(base); + VIDEOINFOHEADER *videoinfo; + HRESULT hres; + DWORD res; + + TRACE("(%p)->(AM_MEDIA_TYPE(%p))\n", base, pmt); + dump_AM_MEDIA_TYPE(pmt); + + if(!IsEqualIID(&pmt->majortype, &MEDIATYPE_Video)) + return S_FALSE; + + if(!IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo)) { + FIXME("formattype %s unsupported\n", debugstr_guid(&pmt->formattype)); + return S_FALSE; + } + + hres = ensure_driver(This); + if(hres != S_OK) + return hres; + + videoinfo = (VIDEOINFOHEADER*)pmt->pbFormat; + res = ICCompressQuery(This->hic, &videoinfo->bmiHeader, NULL); + return res == ICERR_OK ? S_OK : S_FALSE; +} + +static LONG WINAPI AVICompressorIn_GetMediaTypeVersion(BasePin *base) +{ + return 0; +} + +static HRESULT WINAPI AVICompressorIn_GetMediaType(BasePin *base, int iPosition, AM_MEDIA_TYPE *amt) +{ + TRACE("(%p)->(%d %p)\n", base, iPosition, amt); + return S_FALSE; +} + +static HRESULT WINAPI AVICompressorIn_Receive(BaseInputPin *base, IMediaSample *pSample) +{ + AVICompressor *This = impl_from_BasePin(&base->pin); + VIDEOINFOHEADER *src_videoinfo; + REFERENCE_TIME start, stop; + IMediaSample *out_sample; + AM_MEDIA_TYPE *mt; + IMediaSample2 *sample2; + DWORD comp_flags = 0; + BOOL is_preroll; + BOOL sync_point; + BYTE *ptr, *buf; + DWORD res; + HRESULT hres; + + TRACE("(%p)->(%p)\n", base, pSample); + + if(!This->hic) { + FIXME("Driver not loaded\n"); + return E_UNEXPECTED; + } + + hres = IMediaSample_QueryInterface(pSample, &IID_IMediaSample2, (void**)&sample2); + if(SUCCEEDED(hres)) { + FIXME("Use IMediaSample2\n"); + IMediaSample2_Release(sample2); + } + + is_preroll = IMediaSample_IsPreroll(pSample) == S_OK; + sync_point = IMediaSample_IsSyncPoint(pSample) == S_OK; + + hres = IMediaSample_GetTime(pSample, &start, &stop); + if(FAILED(hres)) { + WARN("GetTime failed: %08x\n", hres); + return hres; + } + + hres = IMediaSample_GetMediaType(pSample, &mt); + if(FAILED(hres)) + return hres; + + hres = IMediaSample_GetPointer(pSample, &ptr); + if(FAILED(hres)) { + WARN("GetPointer failed: %08x\n", hres); + return hres; + } + + hres = BaseOutputPinImpl_GetDeliveryBuffer(This->out, &out_sample, &start, &stop, 0); + if(FAILED(hres)) + return hres; + + hres = IMediaSample_GetPointer(out_sample, &buf); + if(FAILED(hres)) + return hres; + + if((This->driver_flags & VIDCF_TEMPORAL) && !(This->driver_flags & VIDCF_FASTTEMPORALC)) + FIXME("Unsupported temporal compression\n"); + + src_videoinfo = (VIDEOINFOHEADER*)This->in->pin.mtCurrent.pbFormat; + This->videoinfo->bmiHeader.biSizeImage = This->max_frame_size; + res = ICCompress(This->hic, sync_point ? ICCOMPRESS_KEYFRAME : 0, &This->videoinfo->bmiHeader, buf, + &src_videoinfo->bmiHeader, ptr, 0, &comp_flags, This->frame_cnt, 0, 0, NULL, NULL); + if(res != ICERR_OK) { + WARN("ICCompress failed: %d\n", res); + IMediaSample_Release(out_sample); + return E_FAIL; + } + + IMediaSample_SetActualDataLength(out_sample, This->videoinfo->bmiHeader.biSizeImage); + IMediaSample_SetPreroll(out_sample, is_preroll); + IMediaSample_SetSyncPoint(out_sample, (comp_flags&AVIIF_KEYFRAME) != 0); + IMediaSample_SetDiscontinuity(out_sample, (IMediaSample_IsDiscontinuity(pSample) == S_OK)); + + if (IMediaSample_GetMediaTime(pSample, &start, &stop) == S_OK) + IMediaSample_SetMediaTime(out_sample, &start, &stop); + else + IMediaSample_SetMediaTime(out_sample, NULL, NULL); + + hres = BaseOutputPinImpl_Deliver(This->out, out_sample); + if(FAILED(hres)) + WARN("Deliver failed: %08x\n", hres); + + IMediaSample_Release(out_sample); + This->frame_cnt++; + return hres; +} + +static const BaseInputPinFuncTable AVICompressorBaseInputPinVtbl = { + { + AVICompressorIn_CheckMediaType, + NULL, + AVICompressorIn_GetMediaTypeVersion, + AVICompressorIn_GetMediaType + }, + AVICompressorIn_Receive +}; + +static HRESULT WINAPI AVICompressorOut_QueryInterface(IPin *iface, REFIID riid, void **ppv) +{ + return BaseInputPinImpl_QueryInterface(iface, riid, ppv); +} + +static ULONG WINAPI AVICompressorOut_AddRef(IPin *iface) +{ + AVICompressor *This = impl_from_IPin(iface); + return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface); +} + +static ULONG WINAPI AVICompressorOut_Release(IPin *iface) +{ + AVICompressor *This = impl_from_IPin(iface); + return IBaseFilter_Release(&This->filter.IBaseFilter_iface); +} + +static const IPinVtbl AVICompressorOutputPinVtbl = { + AVICompressorOut_QueryInterface, + AVICompressorOut_AddRef, + AVICompressorOut_Release, + BaseOutputPinImpl_Connect, + BaseOutputPinImpl_ReceiveConnection, + BaseOutputPinImpl_Disconnect, + BasePinImpl_ConnectedTo, + BasePinImpl_ConnectionMediaType, + BasePinImpl_QueryPinInfo, + BasePinImpl_QueryDirection, + BasePinImpl_QueryId, + BasePinImpl_QueryAccept, + BasePinImpl_EnumMediaTypes, + BasePinImpl_QueryInternalConnections, + BaseOutputPinImpl_EndOfStream, + BaseOutputPinImpl_BeginFlush, + BaseOutputPinImpl_EndFlush, + BasePinImpl_NewSegment +}; + +static LONG WINAPI AVICompressorOut_GetMediaTypeVersion(BasePin *base) +{ + FIXME("(%p)\n", base); + return 0; +} + +static HRESULT WINAPI AVICompressorOut_GetMediaType(BasePin *base, int iPosition, AM_MEDIA_TYPE *amt) +{ + AVICompressor *This = impl_from_IBaseFilter(base->pinInfo.pFilter); + + TRACE("(%p)->(%d %p)\n", base, iPosition, amt); + + if(iPosition || !This->videoinfo) + return S_FALSE; + + amt->majortype = MEDIATYPE_Video; + amt->subtype = MEDIASUBTYPE_PCM; + amt->bFixedSizeSamples = FALSE; + amt->bTemporalCompression = (This->driver_flags & VIDCF_TEMPORAL) != 0; + amt->lSampleSize = This->in->pin.mtCurrent.lSampleSize; + amt->formattype = FORMAT_VideoInfo; + amt->pUnk = NULL; + amt->cbFormat = This->videoinfo_size; + amt->pbFormat = (BYTE*)This->videoinfo; + return S_OK; +} + +static HRESULT WINAPI AVICompressorOut_DecideBufferSize(BaseOutputPin *base, IMemAllocator *alloc, ALLOCATOR_PROPERTIES *ppropInputRequest) +{ + AVICompressor *This = impl_from_BasePin(&base->pin); + ALLOCATOR_PROPERTIES actual; + + TRACE("(%p)\n", This); + + if (!ppropInputRequest->cBuffers) + ppropInputRequest->cBuffers = 1; + if (ppropInputRequest->cbBuffer < This->max_frame_size) + ppropInputRequest->cbBuffer = This->max_frame_size; + if (!ppropInputRequest->cbAlign) + ppropInputRequest->cbAlign = 1; + + return IMemAllocator_SetProperties(alloc, ppropInputRequest, &actual); +} + +static HRESULT WINAPI AVICompressorOut_DecideAllocator(BaseOutputPin *base, + IMemInputPin *pPin, IMemAllocator **pAlloc) +{ + TRACE("(%p)->(%p %p)\n", base, pPin, pAlloc); + return BaseOutputPinImpl_DecideAllocator(base, pPin, pAlloc); +} + +static HRESULT WINAPI AVICompressorOut_BreakConnect(BaseOutputPin *base) +{ + FIXME("(%p)\n", base); + return E_NOTIMPL; +} + +static const BaseOutputPinFuncTable AVICompressorBaseOutputPinVtbl = { + { + NULL, + BaseOutputPinImpl_AttemptConnection, + AVICompressorOut_GetMediaTypeVersion, + AVICompressorOut_GetMediaType + }, + AVICompressorOut_DecideBufferSize, + AVICompressorOut_DecideAllocator, + AVICompressorOut_BreakConnect +}; + +IUnknown* WINAPI QCAP_createAVICompressor(IUnknown *outer, HRESULT *phr) +{ + PIN_INFO in_pin_info = {NULL, PINDIR_INPUT, {'I','n','p','u','t',0}}; + PIN_INFO out_pin_info = {NULL, PINDIR_OUTPUT, {'O','u','t','p','u','t',0}}; + AVICompressor *compressor; + HRESULT hres; + + TRACE("\n"); + + compressor = heap_alloc_zero(sizeof(*compressor)); + if(!compressor) { + *phr = E_NOINTERFACE; + return NULL; + } + + BaseFilter_Init(&compressor->filter, &AVICompressorVtbl, &CLSID_AVICo, + (DWORD_PTR)(__FILE__ ": AVICompressor.csFilter"), &filter_func_table); + + compressor->IPersistPropertyBag_iface.lpVtbl = &PersistPropertyBagVtbl; + + in_pin_info.pFilter = &compressor->filter.IBaseFilter_iface; + hres = BaseInputPin_Construct(&AVICompressorInputPinVtbl, sizeof(BaseInputPin), &in_pin_info, + &AVICompressorBaseInputPinVtbl, &compressor->filter.csFilter, NULL, (IPin**)&compressor->in); + if(FAILED(hres)) { + IBaseFilter_Release(&compressor->filter.IBaseFilter_iface); + *phr = hres; + return NULL; + } + + out_pin_info.pFilter = &compressor->filter.IBaseFilter_iface; + hres = BaseOutputPin_Construct(&AVICompressorOutputPinVtbl, sizeof(BaseOutputPin), &out_pin_info, + &AVICompressorBaseOutputPinVtbl, &compressor->filter.csFilter, (IPin**)&compressor->out); + if(FAILED(hres)) { + IBaseFilter_Release(&compressor->filter.IBaseFilter_iface); + *phr = hres; + return NULL; + } + + *phr = S_OK; + return (IUnknown*)&compressor->filter.IBaseFilter_iface; +} diff --git a/dll/directx/wine/qcap/avimux.c b/dll/directx/wine/qcap/avimux.c new file mode 100644 index 00000000000..1b655dea228 --- /dev/null +++ b/dll/directx/wine/qcap/avimux.c @@ -0,0 +1,2421 @@ +/* + * Copyright (C) 2013 Piotr Caban for CodeWeavers + * + * 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> +#include <stdio.h> + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "wtypes.h" +#include "dshow.h" +#include "vfw.h" +#include "aviriff.h" + +#include "qcap_main.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(qcap); + +#define MAX_PIN_NO 128 +#define AVISUPERINDEX_ENTRIES 2000 +#define AVISTDINDEX_ENTRIES 4000 +#define ALIGN(x) ((x+1)/2*2) + +typedef struct { + BaseOutputPin pin; + IQualityControl IQualityControl_iface; + + int cur_stream; + LONGLONG cur_time; + + int buf_pos; + BYTE buf[65536]; + + int movi_off; + int out_pos; + int size; + IStream *stream; +} AviMuxOut; + +typedef struct { + BaseInputPin pin; + IAMStreamControl IAMStreamControl_iface; + IPropertyBag IPropertyBag_iface; + IQualityControl IQualityControl_iface; + + REFERENCE_TIME avg_time_per_frame; + REFERENCE_TIME stop; + int stream_id; + LONGLONG stream_time; + + /* strl chunk */ + AVISTREAMHEADER strh; + struct { + FOURCC fcc; + DWORD cb; + BYTE data[1]; + } *strf; + AVISUPERINDEX *indx; +#ifdef __REACTOS__ + BYTE indx_data[FIELD_OFFSET(AVISUPERINDEX, aIndex) + AVISUPERINDEX_ENTRIES * sizeof(struct _avisuperindex_entry)]; +#else + BYTE indx_data[FIELD_OFFSET(AVISUPERINDEX, aIndex[AVISUPERINDEX_ENTRIES])]; +#endif + + /* movi chunk */ + int ix_off; + AVISTDINDEX *ix; +#ifdef __REACTOS__ + BYTE ix_data[FIELD_OFFSET(AVISTDINDEX, aIndex) + AVISTDINDEX_ENTRIES * sizeof(struct _avisuperindex_entry)]; +#else + BYTE ix_data[FIELD_OFFSET(AVISTDINDEX, aIndex[AVISTDINDEX_ENTRIES])]; +#endif + + IMediaSample *samples_head; + IMemAllocator *samples_allocator; +} AviMuxIn; + +typedef struct { + BaseFilter filter; + IConfigAviMux IConfigAviMux_iface; + IConfigInterleaving IConfigInterleaving_iface; + IMediaSeeking IMediaSeeking_iface; + IPersistMediaPropertyBag IPersistMediaPropertyBag_iface; + ISpecifyPropertyPages ISpecifyPropertyPages_iface; + + InterleavingMode mode; + REFERENCE_TIME interleave; + REFERENCE_TIME preroll; + + AviMuxOut *out; + int input_pin_no; + AviMuxIn *in[MAX_PIN_NO-1]; + + REFERENCE_TIME start, stop; + AVIMAINHEADER avih; + + int idx1_entries; + int idx1_size; + AVIINDEXENTRY *idx1; +} AviMux; + +static HRESULT create_input_pin(AviMux*); + +static inline AviMux* impl_from_BaseFilter(BaseFilter *filter) +{ + return CONTAINING_RECORD(filter, AviMux, filter); +} + +static IPin* WINAPI AviMux_GetPin(BaseFilter *iface, int pos) +{ + AviMux *This = impl_from_BaseFilter(iface); + + TRACE("(%p)->(%d)\n", This, pos); + + if(pos == 0) { + IPin_AddRef(&This->out->pin.pin.IPin_iface); + return &This->out->pin.pin.IPin_iface; + }else if(pos>0 && pos<=This->input_pin_no) { + IPin_AddRef(&This->in[pos-1]->pin.pin.IPin_iface); + return &This->in[pos-1]->pin.pin.IPin_iface; + } + + return NULL; +} + +static LONG WINAPI AviMux_GetPinCount(BaseFilter *iface) +{ + AviMux *This = impl_from_BaseFilter(iface); + TRACE("(%p)\n", This); + return This->input_pin_no+1; +} + +static const BaseFilterFuncTable filter_func_table = { + AviMux_GetPin, + AviMux_GetPinCount +}; + +static inline AviMux* impl_from_IBaseFilter(IBaseFilter *iface) +{ + BaseFilter *filter = CONTAINING_RECORD(iface, BaseFilter, IBaseFilter_iface); + return impl_from_BaseFilter(filter); +} + +static HRESULT WINAPI AviMux_QueryInterface(IBaseFilter *iface, REFIID riid, void **ppv) +{ + AviMux *This = impl_from_IBaseFilter(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); + + if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IPersist) || + IsEqualIID(riid, &IID_IMediaFilter) || IsEqualIID(riid, &IID_IBaseFilter)) + *ppv = &This->filter.IBaseFilter_iface; + else if(IsEqualIID(riid, &IID_IConfigAviMux)) + *ppv = &This->IConfigAviMux_iface; + else if(IsEqualIID(riid, &IID_IConfigInterleaving)) + *ppv = &This->IConfigInterleaving_iface; + else if(IsEqualIID(riid, &IID_IMediaSeeking)) + *ppv = &This->IMediaSeeking_iface; + else if(IsEqualIID(riid, &IID_IPersistMediaPropertyBag)) + *ppv = &This->IPersistMediaPropertyBag_iface; + else if(IsEqualIID(riid, &IID_ISpecifyPropertyPages)) + *ppv = &This->ISpecifyPropertyPages_iface; + else { + FIXME("no interface for %s\n", debugstr_guid(riid)); + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI AviMux_Release(IBaseFilter *iface) +{ + AviMux *This = impl_from_IBaseFilter(iface); + ULONG ref = BaseFilterImpl_Release(iface); + + TRACE("(%p) new refcount: %u\n", This, ref); + + if(!ref) { + int i; + + BaseOutputPinImpl_Release(&This->out->pin.pin.IPin_iface); + + for(i=0; i<This->input_pin_no; i++) { + IPin_Disconnect(&This->in[i]->pin.pin.IPin_iface); + IMemAllocator_Release(This->in[i]->samples_allocator); + This->in[i]->samples_allocator = NULL; + BaseInputPinImpl_Release(&This->in[i]->pin.pin.IPin_iface); + } + + HeapFree(GetProcessHeap(), 0, This->idx1); + HeapFree(GetProcessHeap(), 0, This); + ObjectRefCount(FALSE); + } + return ref; +} + +static HRESULT out_flush(AviMux *This) +{ + ULONG written; + HRESULT hr; + + if(!This->out->buf_pos) + return S_OK; + + hr = IStream_Write(This->out->stream, This->out->buf, This->out->buf_pos, &written); + if(FAILED(hr)) + return hr; + if(written != This->out->buf_pos) + return E_FAIL; + + This->out->buf_pos = 0; + return S_OK; +} + +static HRESULT out_seek(AviMux *This, int pos) +{ + LARGE_INTEGER li; + HRESULT hr; + + hr = out_flush(This); + if(FAILED(hr)) + return hr; + + li.QuadPart = pos; + hr = IStream_Seek(This->out->stream, li, STREAM_SEEK_SET, NULL); + if(FAILED(hr)) + return hr; + + This->out->out_pos = pos; + if(This->out->out_pos > This->out->size) + This->out->size = This->out->out_pos; + return hr; +} + +static HRESULT out_write(AviMux *This, const void *data, int size) +{ + int chunk_size; + HRESULT hr; + + while(1) { + if(size > sizeof(This->out->buf)-This->out->buf_pos) + chunk_size = sizeof(This->out->buf)-This->out->buf_pos; + else + chunk_size = size; + + memcpy(This->out->buf + This->out->buf_pos, data, chunk_size); + size -= chunk_size; + data = (const BYTE*)data + chunk_size; + This->out->buf_pos += chunk_size; + This->out->out_pos += chunk_size; + if(This->out->out_pos > This->out->size) + This->out->size = This->out->out_pos; + + if(!size) + break; + hr = out_flush(This); + if(FAILED(hr)) + return hr; + } + + return S_OK; +} + +static inline HRESULT idx1_add_entry(AviMux *avimux, DWORD ckid, DWORD flags, DWORD off, DWORD len) +{ + if(avimux->idx1_entries == avimux->idx1_size) { + AVIINDEXENTRY *new_idx = HeapReAlloc(GetProcessHeap(), 0, avimux->idx1, + sizeof(*avimux->idx1)*2*avimux->idx1_size); + if(!new_idx) + return E_OUTOFMEMORY; + + avimux->idx1_size *= 2; + avimux->idx1 = new_idx; + } + + avimux->idx1[avimux->idx1_entries].ckid = ckid; + avimux->idx1[avimux->idx1_entries].dwFlags = flags; + avimux->idx1[avimux->idx1_entries].dwChunkOffset = off; + avimux->idx1[avimux->idx1_entries].dwChunkLength = len; + avimux->idx1_entries++; + return S_OK; +} + +static HRESULT flush_queue(AviMux *avimux, AviMuxIn *avimuxin, BOOL closing) +{ + IMediaSample *sample, **prev, **head_prev; + BYTE *data; + RIFFCHUNK rf; + DWORD size; + DWORD flags; + HRESULT hr; + + if(avimux->out->cur_stream != avimuxin->stream_id) + return S_OK; + + while(avimuxin->samples_head) { + hr = IMediaSample_GetPointer(avimuxin->samples_head, (BYTE**)&head_prev); + if(FAILED(hr)) + return hr; + head_prev--; + + hr = IMediaSample_GetPointer(*head_prev, (BYTE**)&prev); + if(FAILED(hr)) + return hr; + prev--; + + sample = *head_prev; + size = IMediaSample_GetActualDataLength(sample); + hr = IMediaSample_GetPointer(sample, &data); + if(FAILED(hr)) + return hr; + flags = IMediaSample_IsDiscontinuity(sample)==S_OK ? AM_SAMPLE_TIMEDISCONTINUITY : 0; + if(IMediaSample_IsSyncPoint(sample) == S_OK) + flags |= AM_SAMPLE_SPLICEPOINT; + + if(avimuxin->stream_time + (closing ? 0 : avimuxin->strh.dwScale) > avimux->out->cur_time && + !(flags & AM_SAMPLE_TIMEDISCONTINUITY)) { + if(closing) + break; + + avimux->out->cur_stream++; + if(avimux->out->cur_stream >= avimux->input_pin_no-1) { + avimux->out->cur_time += avimux->interleave; + avimux->out->cur_stream = 0; + } + avimuxin = avimux->in[avimux->out->cur_stream]; + continue; + } + + if(avimuxin->ix->nEntriesInUse == AVISTDINDEX_ENTRIES) { + /* TODO: use output pins Deliver/Receive method */ + hr = out_seek(avimux, avimuxin->ix_off); + if(FAILED(hr)) + return hr; + hr = out_write(avimux, avimuxin->ix, sizeof(avimuxin->ix_data)); + if(FAILED(hr)) + return hr; + + avimuxin->indx->aIndex[avimuxin->indx->nEntriesInUse].qwOffset = avimuxin->ix_off; + avimuxin->indx->aIndex[avimuxin->indx->nEntriesInUse].dwSize = sizeof(avimuxin->ix_data); + avimuxin->indx->aIndex[avimuxin->indx->nEntriesInUse].dwDuration = AVISTDINDEX_ENTRIES; + avimuxin->indx->nEntriesInUse++; + + memset(avimuxin->ix->aIndex, 0, sizeof(avimuxin->ix->aIndex)*avimuxin->ix->nEntriesInUse); + avimuxin->ix->nEntriesInUse = 0; + avimuxin->ix->qwBaseOffset = 0; + + avimuxin->ix_off = avimux->out->size; + avimux->out->size += sizeof(avimuxin->ix_data); + } + + if(*head_prev == avimuxin->samples_head) + avimuxin->samples_head = NULL; + else + *head_prev = *prev; + + avimuxin->stream_time += avimuxin->strh.dwScale; + avimuxin->strh.dwLength++; + if(!(flags & AM_SAMPLE_TIMEDISCONTINUITY)) { + if(!avimuxin->ix->qwBaseOffset) + avimuxin->ix->qwBaseOffset = avimux->out->size; + avimuxin->ix->aIndex[avimuxin->ix->nEntriesInUse].dwOffset = avimux->out->size + + sizeof(RIFFCHUNK) - avimuxin->ix->qwBaseOffset; + + hr = out_seek(avimux, avimux->out->size); + if(FAILED(hr)) { + IMediaSample_Release(sample); + return hr; + } + } + avimuxin->ix->aIndex[avimuxin->ix->nEntriesInUse].dwSize = size | + (flags & AM_SAMPLE_SPLICEPOINT ? 0 : AVISTDINDEX_DELTAFRAME); + avimuxin->ix->nEntriesInUse++; + + rf.fcc = FCC('0'+avimuxin->stream_id/10, '0'+avimuxin->stream_id%10, + 'd', flags & AM_SAMPLE_SPLICEPOINT ? 'b' : 'c'); + rf.cb = size; + hr = idx1_add_entry(avimux, rf.fcc, flags & AM_SAMPLE_SPLICEPOINT ? AVIIF_KEYFRAME : 0, + flags & AM_SAMPLE_TIMEDISCONTINUITY ? + avimux->idx1[avimux->idx1_entries-1].dwChunkOffset : avimux->out->size, size); + if(FAILED(hr)) { + IMediaSample_Release(sample); + return hr; + } + + if(!(flags & AM_SAMPLE_TIMEDISCONTINUITY)) { + hr = out_write(avimux, &rf, sizeof(rf)); + if(FAILED(hr)) { + IMediaSample_Release(sample); + return hr; + } + hr = out_write(avimux, data, size); + if(FAILED(hr)) { + IMediaSample_Release(sample); + return hr; + } + flags = 0; + hr = out_write(avimux, &flags, ALIGN(rf.cb)-rf.cb); + if(FAILED(hr)) { + IMediaSample_Release(sample); + return hr; + } + } + IMediaSample_Release(sample); + } + return S_OK; +} + +static HRESULT queue_sample(AviMux *avimux, AviMuxIn *avimuxin, IMediaSample *sample) +{ + IMediaSample **prev, **head_prev; + HRESULT hr; + + hr = IMediaSample_GetPointer(sample, (BYTE**)&prev); + if(FAILED(hr)) + return hr; + prev--; + + if(avimuxin->samples_head) { + hr = IMediaSample_GetPointer(avimuxin->samples_head, (BYTE**)&head_prev); + if(FAILED(hr)) + return hr; + head_prev--; + + *prev = *head_prev; + *head_prev = sample; + }else { + *prev = sample; + } + avimuxin->samples_head = sample; + IMediaSample_AddRef(sample); + + return flush_queue(avimux, avimuxin, FALSE); +} + +static HRESULT WINAPI AviMux_Stop(IBaseFilter *iface) +{ + AviMux *This = impl_from_IBaseFilter(iface); + HRESULT hr; + int i; + + TRACE("(%p)\n", This); + + if(This->filter.state == State_Stopped) + return S_OK; + + if(This->out->stream) { + AVIEXTHEADER dmlh; + RIFFCHUNK rc; + RIFFLIST rl; + int idx1_off, empty_stream; + + empty_stream = This->out->cur_stream; + for(i=empty_stream+1; ; i++) { + if(i >= This->input_pin_no-1) + i = 0; + if(i == empty_stream) + break; + + This->out->cur_stream = i; + hr = flush_queue(This, This->in[This->out->cur_stream], TRUE); + if(FAILED(hr)) + return hr; + } + + idx1_off = This->out->size; + rc.fcc = ckidAVIOLDINDEX; + rc.cb = This->idx1_entries * sizeof(*This->idx1); + hr = out_write(This, &rc, sizeof(rc)); + if(FAILED(hr)) + return hr; + hr = out_write(This, This->idx1, This->idx1_entries * sizeof(*This->idx1)); + if(FAILED(hr)) + return hr; + /* native writes 8 '\0' characters after the end of RIFF data */ + i = 0; + hr = out_write(This, &i, sizeof(i)); + if(FAILED(hr)) + return hr; + hr = out_write(This, &i, sizeof(i)); + if(FAILED(hr)) + return hr; + + for(i=0; i<This->input_pin_no; i++) { + if(!This->in[i]->pin.pin.pConnectedTo) + continue; + + hr = out_seek(This, This->in[i]->ix_off); + if(FAILED(hr)) + return hr; + + This->in[i]->indx->aIndex[This->in[i]->indx->nEntriesInUse].qwOffset = This->in[i]->ix_off; + This->in[i]->indx->aIndex[This->in[i]->indx->nEntriesInUse].dwSize = sizeof(This->in[i]->ix_data); + This->in[i]->indx->aIndex[This->in[i]->indx->nEntriesInUse].dwDuration = This->in[i]->strh.dwLength; + if(This->in[i]->indx->nEntriesInUse) { + This->in[i]->indx->aIndex[This->in[i]->indx->nEntriesInUse].dwDuration -= + This->in[i]->indx->aIndex[This->in[i]->indx->nEntriesInUse-1].dwDuration; + } + This->in[i]->indx->nEntriesInUse++; + hr = out_write(This, This->in[i]->ix, sizeof(This->in[i]->ix_data)); + if(FAILED(hr)) + return hr; + } + + hr = out_seek(This, 0); + if(FAILED(hr)) + return hr; + + rl.fcc = FCC('R','I','F','F'); + rl.cb = This->out->size-sizeof(RIFFCHUNK)-2*sizeof(int); + rl.fccListType = FCC('A','V','I',' '); + hr = out_write(This, &rl, sizeof(rl)); + if(FAILED(hr)) + return hr; + + rl.fcc = FCC('L','I','S','T'); + rl.cb = This->out->movi_off - sizeof(RIFFLIST) - sizeof(RIFFCHUNK); + rl.fccListType = FCC('h','d','r','l'); + hr = out_write(This, &rl, sizeof(rl)); + if(FAILED(hr)) + return hr; + + /* FIXME: set This->avih.dwMaxBytesPerSec value */ + This->avih.dwTotalFrames = (This->stop-This->start) / 10 / This->avih.dwMicroSecPerFrame; + hr = out_write(This, &This->avih, sizeof(This->avih)); + if(FAILED(hr)) + return hr; + + for(i=0; i<This->input_pin_no; i++) { + if(!This->in[i]->pin.pin.pConnectedTo) + continue; + + rl.cb = sizeof(FOURCC) + sizeof(AVISTREAMHEADER) + sizeof(RIFFCHUNK) + + This->in[i]->strf->cb + sizeof(This->in[i]->indx_data); + rl.fccListType = ckidSTREAMLIST; + hr = out_write(This, &rl, sizeof(rl)); + if(FAILED(hr)) + return hr; + + hr = out_write(This, &This->in[i]->strh, sizeof(AVISTREAMHEADER)); + if(FAILED(hr)) + return hr; + + hr = out_write(This, This->in[i]->strf, sizeof(RIFFCHUNK) + This->in[i]->strf->cb); + if(FAILED(hr)) + return hr; + + hr = out_write(This, This->in[i]->indx, sizeof(This->in[i]->indx_data)); + if(FAILED(hr)) + return hr; + } + + rl.cb = sizeof(dmlh) + sizeof(FOURCC); + rl.fccListType = ckidODML; + hr = out_write(This, &rl, sizeof(rl)); + if(FAILED(hr)) + return hr; + + memset(&dmlh, 0, sizeof(dmlh)); + dmlh.fcc = ckidAVIEXTHEADER; + dmlh.cb = sizeof(dmlh) - sizeof(RIFFCHUNK); + dmlh.dwGrandFrames = This->in[0]->strh.dwLength; + hr = out_write(This, &dmlh, sizeof(dmlh)); + + rl.cb = idx1_off - This->out->movi_off - sizeof(RIFFCHUNK); + rl.fccListType = FCC('m','o','v','i'); + out_write(This, &rl, sizeof(rl)); + out_flush(This); + + IStream_Release(This->out->stream); + This->out->stream = NULL; + } + + This->filter.state = State_Stopped; + return S_OK; +} + +static HRESULT WINAPI AviMux_Pause(IBaseFilter *iface) +{ + AviMux *This = impl_from_IBaseFilter(iface); + FIXME("(%p)\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI AviMux_Run(IBaseFilter *iface, REFERENCE_TIME tStart) +{ + AviMux *This = impl_from_IBaseFilter(iface); + HRESULT hr; + int i, stream_id; + + TRACE("(%p)->(%s)\n", This, wine_dbgstr_longlong(tStart)); + + if(This->filter.state == State_Running) + return S_OK; + + if(This->mode != INTERLEAVE_FULL) { + FIXME("mode not supported (%d)\n", This->mode); + return E_NOTIMPL; + } + + if(tStart) + FIXME("tStart parameter ignored\n"); + + for(i=0; i<This->input_pin_no; i++) { + IMediaSeeking *ms; + LONGLONG cur, stop; + + if(!This->in[i]->pin.pin.pConnectedTo) + continue; + + hr = IPin_QueryInterface(This->in[i]->pin.pin.pConnectedTo, + &IID_IMediaSeeking, (void**)&ms); + if(FAILED(hr)) + continue; + + hr = IMediaSeeking_GetPositions(ms, &cur, &stop); + if(FAILED(hr)) { + IMediaSeeking_Release(ms); + continue; + } + + FIXME("Use IMediaSeeking to fill stream header\n"); + IMediaSeeking_Release(ms); + } + + if(This->out->pin.pMemInputPin) { + hr = IMemInputPin_QueryInterface(This->out->pin.pMemInputPin, + &IID_IStream, (void**)&This->out->stream); + if(FAILED(hr)) + return hr; + } + + This->idx1_entries = 0; + if(!This->idx1_size) { + This->idx1_size = 1024; + This->idx1 = HeapAlloc(GetProcessHeap(), 0, sizeof(*This->idx1)*This->idx1_size); + if(!This->idx1) + return E_OUTOFMEMORY; + } + + This->out->size = 3*sizeof(RIFFLIST) + sizeof(AVIMAINHEADER) + sizeof(AVIEXTHEADER); + This->start = -1; + This->stop = -1; + memset(&This->avih, 0, sizeof(This->avih)); + for(i=0; i<This->input_pin_no; i++) { + if(!This->in[i]->pin.pin.pConnectedTo) + continue; + + This->avih.dwStreams++; + This->out->size += sizeof(RIFFLIST) + sizeof(AVISTREAMHEADER) + sizeof(RIFFCHUNK) + + This->in[i]->strf->cb + sizeof(This->in[i]->indx_data); + + This->in[i]->strh.dwScale = MulDiv(This->in[i]->avg_time_per_frame, This->interleave, 10000000); + This->in[i]->strh.dwRate = This->interleave; + + hr = IMemAllocator_Commit(This->in[i]->pin.pAllocator); + if(FAILED(hr)) { + if(This->out->stream) { + IStream_Release(This->out->stream); + This->out->stream = NULL; + } + return hr; + } + } + + This->out->movi_off = This->out->size; + This->out->size += sizeof(RIFFLIST); + + idx1_add_entry(This, FCC('7','F','x','x'), 0, This->out->movi_off+sizeof(RIFFLIST), 0); + + stream_id = 0; + for(i=0; i<This->input_pin_no; i++) { + if(!This->in[i]->pin.pin.pConnectedTo) + continue; + + This->in[i]->ix_off = This->out->size; + This->out->size += sizeof(This->in[i]->ix_data); + This->in[i]->ix->fcc = FCC('i','x','0'+stream_id/10,'0'+stream_id%10); + This->in[i]->ix->cb = sizeof(This->in[i]->ix_data) - sizeof(RIFFCHUNK); + This->in[i]->ix->wLongsPerEntry = 2; + This->in[i]->ix->bIndexSubType = 0; + This->in[i]->ix->bIndexType = AVI_INDEX_OF_CHUNKS; + This->in[i]->ix->dwChunkId = FCC('0'+stream_id/10,'0'+stream_id%10,'d','b'); + This->in[i]->ix->qwBaseOffset = 0; + + This->in[i]->indx->fcc = ckidAVISUPERINDEX; + This->in[i]->indx->cb = sizeof(This->in[i]->indx_data) - sizeof(RIFFCHUNK); + This->in[i]->indx->wLongsPerEntry = 4; + This->in[i]->indx->bIndexSubType = 0; + This->in[i]->indx->bIndexType = AVI_INDEX_OF_INDEXES; + This->in[i]->indx->dwChunkId = This->in[i]->ix->dwChunkId; + This->in[i]->stream_id = stream_id++; + } + + This->out->buf_pos = 0; + This->out->out_pos = 0; + + This->avih.fcc = ckidMAINAVIHEADER; + This->avih.cb = sizeof(AVIMAINHEADER) - sizeof(RIFFCHUNK); + /* TODO: Use first video stream */ + This->avih.dwMicroSecPerFrame = This->in[0]->avg_time_per_frame/10; + This->avih.dwPaddingGranularity = 1; + This->avih.dwFlags = AVIF_TRUSTCKTYPE | AVIF_HASINDEX; + This->avih.dwWidth = ((BITMAPINFOHEADER*)This->in[0]->strf->data)->biWidth; + This->avih.dwHeight = ((BITMAPINFOHEADER*)This->in[0]->strf->data)->biHeight; + + This->filter.state = State_Running; + return S_OK; +} + +static HRESULT WINAPI AviMux_EnumPins(IBaseFilter *iface, IEnumPins **ppEnum) +{ + AviMux *This = impl_from_IBaseFilter(iface); + TRACE("(%p)->(%p)\n", This, ppEnum); + return BaseFilterImpl_EnumPins(iface, ppEnum); +} + +static HRESULT WINAPI AviMux_FindPin(IBaseFilter *iface, LPCWSTR Id, IPin **ppPin) +{ + AviMux *This = impl_from_IBaseFilter(iface); + int i; + + TRACE("(%p)->(%s %p)\n", This, debugstr_w(Id), ppPin); + + if(!Id || !ppPin) + return E_POINTER; + + if(!lstrcmpiW(Id, This->out->pin.pin.pinInfo.achName)) { + IPin_AddRef(&This->out->pin.pin.IPin_iface); + *ppPin = &This->out->pin.pin.IPin_iface; + return S_OK; + } + + for(i=0; i<This->input_pin_no; i++) { + if(lstrcmpiW(Id, This->in[i]->pin.pin.pinInfo.achName)) + continue; + + IPin_AddRef(&This->in[i]->pin.pin.IPin_iface); + *ppPin = &This->in[i]->pin.pin.IPin_iface; + return S_OK; + } + + return VFW_E_NOT_FOUND; +} + +static HRESULT WINAPI AviMux_QueryFilterInfo(IBaseFilter *iface, FILTER_INFO *pInfo) +{ + AviMux *This = impl_from_IBaseFilter(iface); + FIXME("(%p)->(%p)\n", This, pInfo); + return E_NOTIMPL; +} + +static HRESULT WINAPI AviMux_QueryVendorInfo(IBaseFilter *iface, LPWSTR *pVendorInfo) +{ + AviMux *This = impl_from_IBaseFilter(iface); + FIXME("(%p)->(%p)\n", This, pVendorInfo); + return E_NOTIMPL; +} + +static const IBaseFilterVtbl AviMuxVtbl = { + AviMux_QueryInterface, + BaseFilterImpl_AddRef, + AviMux_Release, + BaseFilterImpl_GetClassID, + AviMux_Stop, + AviMux_Pause, + AviMux_Run, + BaseFilterImpl_GetState, + BaseFilterImpl_SetSyncSource, + BaseFilterImpl_GetSyncSource, + AviMux_EnumPins, + AviMux_FindPin, + AviMux_QueryFilterInfo, + BaseFilterImpl_JoinFilterGraph, + AviMux_QueryVendorInfo +}; + +static inline AviMux* impl_from_IConfigAviMux(IConfigAviMux *iface) +{ + return CONTAINING_RECORD(iface, AviMux, IConfigAviMux_iface); +} + +static HRESULT WINAPI ConfigAviMux_QueryInterface( + IConfigAviMux *iface, REFIID riid, void **ppv) +{ + AviMux *This = impl_from_IConfigAviMux(iface); + return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv); +} + +static ULONG WINAPI ConfigAviMux_AddRef(IConfigAviMux *iface) +{ + AviMux *This = impl_from_IConfigAviMux(iface); + return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface); +} + +static ULONG WINAPI ConfigAviMux_Release(IConfigAviMux *iface) +{ + AviMux *This = impl_from_IConfigAviMux(iface); + return IBaseFilter_Release(&This->filter.IBaseFilter_iface); +} + +static HRESULT WINAPI ConfigAviMux_SetMasterStream(IConfigAviMux *iface, LONG iStream) +{ + AviMux *This = impl_from_IConfigAviMux(iface); + FIXME("(%p)->(%d)\n", This, iStream); + return E_NOTIMPL; +} + +static HRESULT WINAPI ConfigAviMux_GetMasterStream(IConfigAviMux *iface, LONG *pStream) +{ + AviMux *This = impl_from_IConfigAviMux(iface); + FIXME("(%p)->(%p)\n", This, pStream); + return E_NOTIMPL; +} + +static HRESULT WINAPI ConfigAviMux_SetOutputCompatibilityIndex( + IConfigAviMux *iface, BOOL fOldIndex) +{ + AviMux *This = impl_from_IConfigAviMux(iface); + FIXME("(%p)->(%x)\n", This, fOldIndex); + return E_NOTIMPL; +} + +static HRESULT WINAPI ConfigAviMux_GetOutputCompatibilityIndex( + IConfigAviMux *iface, BOOL *pfOldIndex) +{ + AviMux *This = impl_from_IConfigAviMux(iface); + FIXME("(%p)->(%p)\n", This, pfOldIndex); + return E_NOTIMPL; +} + +static const IConfigAviMuxVtbl ConfigAviMuxVtbl = { + ConfigAviMux_QueryInterface, + ConfigAviMux_AddRef, + ConfigAviMux_Release, + ConfigAviMux_SetMasterStream, + ConfigAviMux_GetMasterStream, + ConfigAviMux_SetOutputCompatibilityIndex, + ConfigAviMux_GetOutputCompatibilityIndex +}; + +static inline AviMux* impl_from_IConfigInterleaving(IConfigInterleaving *iface) +{ + return CONTAINING_RECORD(iface, AviMux, IConfigInterleaving_iface); +} + +static HRESULT WINAPI ConfigInterleaving_QueryInterface( + IConfigInterleaving *iface, REFIID riid, void **ppv) +{ + AviMux *This = impl_from_IConfigInterleaving(iface); + return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv); +} + +static ULONG WINAPI ConfigInterleaving_AddRef(IConfigInterleaving *iface) +{ + AviMux *This = impl_from_IConfigInterleaving(iface); + return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface); +} + +static ULONG WINAPI ConfigInterleaving_Release(IConfigInterleaving *iface) +{ + AviMux *This = impl_from_IConfigInterleaving(iface); + return IBaseFilter_Release(&This->filter.IBaseFilter_iface); +} + +static HRESULT WINAPI ConfigInterleaving_put_Mode( + IConfigInterleaving *iface, InterleavingMode mode) +{ + AviMux *This = impl_from_IConfigInterleaving(iface); + + TRACE("(%p)->(%d)\n", This, mode); + + if(mode>INTERLEAVE_NONE_BUFFERED) + return E_INVALIDARG; + + if(This->mode != mode) { + if(This->out->pin.pin.pConnectedTo) { + HRESULT hr = IFilterGraph_Reconnect(This->filter.filterInfo.pGraph, + &This->out->pin.pin.IPin_iface); + if(FAILED(hr)) + return hr; + } + + This->mode = mode; + } + + return S_OK; +} + +static HRESULT WINAPI ConfigInterleaving_get_Mode( + IConfigInterleaving *iface, InterleavingMode *pMode) +{ + AviMux *This = impl_from_IConfigInterleaving(iface); + FIXME("(%p)->(%p)\n", This, pMode); + return E_NOTIMPL; +} + +static HRESULT WINAPI ConfigInterleaving_put_Interleaving(IConfigInterleaving *iface, + const REFERENCE_TIME *prtInterleave, const REFERENCE_TIME *prtPreroll) +{ + AviMux *This = impl_from_IConfigInterleaving(iface); + + TRACE("(%p)->(%p %p)\n", This, prtInterleave, prtPreroll); + + if(prtInterleave) + This->interleave = *prtInterleave; + if(prtPreroll) + This->preroll = *prtPreroll; + return S_OK; +} + +static HRESULT WINAPI ConfigInterleaving_get_Interleaving(IConfigInterleaving *iface, + REFERENCE_TIME *prtInterleave, REFERENCE_TIME *prtPreroll) +{ + AviMux *This = impl_from_IConfigInterleaving(iface); + FIXME("(%p)->(%p %p)\n", This, prtInterleave, prtPreroll); + return E_NOTIMPL; +} + +static const IConfigInterleavingVtbl ConfigInterleavingVtbl = { + ConfigInterleaving_QueryInterface, + ConfigInterleaving_AddRef, + ConfigInterleaving_Release, + ConfigInterleaving_put_Mode, + ConfigInterleaving_get_Mode, + ConfigInterleaving_put_Interleaving, + ConfigInterleaving_get_Interleaving +}; + +static inline AviMux* impl_from_IMediaSeeking(IMediaSeeking *iface) +{ + return CONTAINING_RECORD(iface, AviMux, IMediaSeeking_iface); +} + +static HRESULT WINAPI MediaSeeking_QueryInterface( + IMediaSeeking *iface, REFIID riid, void **ppv) +{ + AviMux *This = impl_from_IMediaSeeking(iface); + return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv); +} + +static ULONG WINAPI MediaSeeking_AddRef(IMediaSeeking *iface) +{ + AviMux *This = impl_from_IMediaSeeking(iface); + return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface); +} + +static ULONG WINAPI MediaSeeking_Release(IMediaSeeking *iface) +{ + AviMux *This = impl_from_IMediaSeeking(iface); + return IBaseFilter_Release(&This->filter.IBaseFilter_iface); +} + +static HRESULT WINAPI MediaSeeking_GetCapabilities( + IMediaSeeking *iface, DWORD *pCapabilities) +{ + AviMux *This = impl_from_IMediaSeeking(iface); + FIXME("(%p)->(%p)\n", This, pCapabilities); + return E_NOTIMPL; +} + +static HRESULT WINAPI MediaSeeking_CheckCapabilities( + IMediaSeeking *iface, DWORD *pCapabilities) +{ + AviMux *This = impl_from_IMediaSeeking(iface); + FIXME("(%p)->(%p)\n", This, pCapabilities); + return E_NOTIMPL; +} + +static HRESULT WINAPI MediaSeeking_IsFormatSupported( + IMediaSeeking *iface, const GUID *pFormat) +{ + AviMux *This = impl_from_IMediaSeeking(iface); + FIXME("(%p)->(%s)\n", This, debugstr_guid(pFormat)); + return E_NOTIMPL; +} + +static HRESULT WINAPI MediaSeeking_QueryPreferredFormat( + IMediaSeeking *iface, GUID *pFormat) +{ + AviMux *This = impl_from_IMediaSeeking(iface); + FIXME("(%p)->(%p)\n", This, pFormat); + return E_NOTIMPL; +} + +static HRESULT WINAPI MediaSeeking_GetTimeFormat( + IMediaSeeking *iface, GUID *pFormat) +{ + AviMux *This = impl_from_IMediaSeeking(iface); + FIXME("(%p)->(%p)\n", This, pFormat); + return E_NOTIMPL; +} + +static HRESULT WINAPI MediaSeeking_IsUsingTimeFormat( + IMediaSeeking *iface, const GUID *pFormat) +{ + AviMux *This = impl_from_IMediaSeeking(iface); + FIXME("(%p)->(%s)\n", This, debugstr_guid(pFormat)); + return E_NOTIMPL; +} + +static HRESULT WINAPI MediaSeeking_SetTimeFormat( + IMediaSeeking *iface, const GUID *pFormat) +{ + AviMux *This = impl_from_IMediaSeeking(iface); + FIXME("(%p)->(%s)\n", This, debugstr_guid(pFormat)); + return E_NOTIMPL; +} + +static HRESULT WINAPI MediaSeeking_GetDuration( + IMediaSeeking *iface, LONGLONG *pDuration) +{ + AviMux *This = impl_from_IMediaSeeking(iface); + FIXME("(%p)->(%p)\n", This, pDuration); + return E_NOTIMPL; +} + +static HRESULT WINAPI MediaSeeking_GetStopPosition( + IMediaSeeking *iface, LONGLONG *pStop) +{ + AviMux *This = impl_from_IMediaSeeking(iface); + FIXME("(%p)->(%p)\n", This, pStop); + return E_NOTIMPL; +} + +static HRESULT WINAPI MediaSeeking_GetCurrentPosition( + IMediaSeeking *iface, LONGLONG *pCurrent) +{ + AviMux *This = impl_from_IMediaSeeking(iface); + FIXME("(%p)->(%p)\n", This, pCurrent); + return E_NOTIMPL; +} + +static HRESULT WINAPI MediaSeeking_ConvertTimeFormat(IMediaSeeking *iface, LONGLONG *pTarget, + const GUID *pTargetFormat, LONGLONG Source, const GUID *pSourceFormat) +{ + AviMux *This = impl_from_IMediaSeeking(iface); + FIXME("(%p)->(%p %s %s %s)\n", This, pTarget, debugstr_guid(pTargetFormat), + wine_dbgstr_longlong(Source), debugstr_guid(pSourceFormat)); + return E_NOTIMPL; +} + +static HRESULT WINAPI MediaSeeking_SetPositions(IMediaSeeking *iface, LONGLONG *pCurrent, + DWORD dwCurrentFlags, LONGLONG *pStop, DWORD dwStopFlags) +{ + AviMux *This = impl_from_IMediaSeeking(iface); + FIXME("(%p)->(%p %x %p %x)\n", This, pCurrent, dwCurrentFlags, pStop, dwStopFlags); + return E_NOTIMPL; +} + +static HRESULT WINAPI MediaSeeking_GetPositions(IMediaSeeking *iface, + LONGLONG *pCurrent, LONGLONG *pStop) +{ + AviMux *This = impl_from_IMediaSeeking(iface); + FIXME("(%p)->(%p %p)\n", This, pCurrent, pStop); + return E_NOTIMPL; +} + +static HRESULT WINAPI MediaSeeking_GetAvailable(IMediaSeeking *iface, + LONGLONG *pEarliest, LONGLONG *pLatest) +{ + AviMux *This = impl_from_IMediaSeeking(iface); + FIXME("(%p)->(%p %p)\n", This, pEarliest, pLatest); + return E_NOTIMPL; +} + +static HRESULT WINAPI MediaSeeking_SetRate(IMediaSeeking *iface, double dRate) +{ + AviMux *This = impl_from_IMediaSeeking(iface); + FIXME("(%p)->(%lf)\n", This, dRate); + return E_NOTIMPL; +} + +static HRESULT WINAPI MediaSeeking_GetRate(IMediaSeeking *iface, double *pdRate) +{ + AviMux *This = impl_from_IMediaSeeking(iface); + FIXME("(%p)->(%p)\n", This, pdRate); + return E_NOTIMPL; +} + +static HRESULT WINAPI MediaSeeking_GetPreroll(IMediaSeeking *iface, LONGLONG *pllPreroll) +{ + AviMux *This = impl_from_IMediaSeeking(iface); + FIXME("(%p)->(%p)\n", This, pllPreroll); + return E_NOTIMPL; +} + +static const IMediaSeekingVtbl MediaSeekingVtbl = { + MediaSeeking_QueryInterface, + MediaSeeking_AddRef, + MediaSeeking_Release, + MediaSeeking_GetCapabilities, + MediaSeeking_CheckCapabilities, + MediaSeeking_IsFormatSupported, + MediaSeeking_QueryPreferredFormat, + MediaSeeking_GetTimeFormat, + MediaSeeking_IsUsingTimeFormat, + MediaSeeking_SetTimeFormat, + MediaSeeking_GetDuration, + MediaSeeking_GetStopPosition, + MediaSeeking_GetCurrentPosition, + MediaSeeking_ConvertTimeFormat, + MediaSeeking_SetPositions, + MediaSeeking_GetPositions, + MediaSeeking_GetAvailable, + MediaSeeking_SetRate, + MediaSeeking_GetRate, + MediaSeeking_GetPreroll +}; + +static inline AviMux* impl_from_IPersistMediaPropertyBag(IPersistMediaPropertyBag *iface) +{ + return CONTAINING_RECORD(iface, AviMux, IPersistMediaPropertyBag_iface); +} + +static HRESULT WINAPI PersistMediaPropertyBag_QueryInterface( + IPersistMediaPropertyBag *iface, REFIID riid, void **ppv) +{ + AviMux *This = impl_from_IPersistMediaPropertyBag(iface); + return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv); +} + +static ULONG WINAPI PersistMediaPropertyBag_AddRef(IPersistMediaPropertyBag *iface) +{ + AviMux *This = impl_from_IPersistMediaPropertyBag(iface); + return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface); +} + +static ULONG WINAPI PersistMediaPropertyBag_Release(IPersistMediaPropertyBag *iface) +{ + AviMux *This = impl_from_IPersistMediaPropertyBag(iface); + return IBaseFilter_Release(&This->filter.IBaseFilter_iface); +} + +static HRESULT WINAPI PersistMediaPropertyBag_GetClassID( + IPersistMediaPropertyBag *iface, CLSID *pClassID) +{ + AviMux *This = impl_from_IPersistMediaPropertyBag(iface); + return IBaseFilter_GetClassID(&This->filter.IBaseFilter_iface, pClassID); +} + +static HRESULT WINAPI PersistMediaPropertyBag_InitNew(IPersistMediaPropertyBag *iface) +{ + AviMux *This = impl_from_IPersistMediaPropertyBag(iface); + FIXME("(%p)->()\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI PersistMediaPropertyBag_Load(IPersistMediaPropertyBag *iface, + IMediaPropertyBag *pPropBag, IErrorLog *pErrorLog) +{ + AviMux *This = impl_from_IPersistMediaPropertyBag(iface); + FIXME("(%p)->()\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI PersistMediaPropertyBag_Save(IPersistMediaPropertyBag *iface, + IMediaPropertyBag *pPropBag, BOOL fClearDirty, BOOL fSaveAllProperties) +{ + AviMux *This = impl_from_IPersistMediaPropertyBag(iface); + FIXME("(%p)->()\n", This); + return E_NOTIMPL; +} + +static const IPersistMediaPropertyBagVtbl PersistMediaPropertyBagVtbl = { + PersistMediaPropertyBag_QueryInterface, + PersistMediaPropertyBag_AddRef, + PersistMediaPropertyBag_Release, + PersistMediaPropertyBag_GetClassID, + PersistMediaPropertyBag_InitNew, + PersistMediaPropertyBag_Load, + PersistMediaPropertyBag_Save +}; + +static inline AviMux* impl_from_ISpecifyPropertyPages(ISpecifyPropertyPages *iface) +{ + return CONTAINING_RECORD(iface, AviMux, ISpecifyPropertyPages_iface); +} + +static HRESULT WINAPI SpecifyPropertyPages_QueryInterface( + ISpecifyPropertyPages *iface, REFIID riid, void **ppv) +{ + AviMux *This = impl_from_ISpecifyPropertyPages(iface); + return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv); +} + +static ULONG WINAPI SpecifyPropertyPages_AddRef(ISpecifyPropertyPages *iface) +{ + AviMux *This = impl_from_ISpecifyPropertyPages(iface); + return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface); +} + +static ULONG WINAPI SpecifyPropertyPages_Release(ISpecifyPropertyPages *iface) +{ + AviMux *This = impl_from_ISpecifyPropertyPages(iface); + return IBaseFilter_Release(&This->filter.IBaseFilter_iface); +} + +static HRESULT WINAPI SpecifyPropertyPages_GetPages( + ISpecifyPropertyPages *iface, CAUUID *pPages) +{ + AviMux *This = impl_from_ISpecifyPropertyPages(iface); + FIXME("(%p)->(%p)\n", This, pPages); + return E_NOTIMPL; +} + +static const ISpecifyPropertyPagesVtbl SpecifyPropertyPagesVtbl = { + SpecifyPropertyPages_QueryInterface, + SpecifyPropertyPages_AddRef, + SpecifyPropertyPages_Release, + SpecifyPropertyPages_GetPages +}; + +static HRESULT WINAPI AviMuxOut_AttemptConnection(BasePin *base, + IPin *pReceivePin, const AM_MEDIA_TYPE *pmt) +{ + PIN_DIRECTION dir; + HRESULT hr; + + TRACE("(%p)->(%p AM_MEDIA_TYPE(%p))\n", base, pReceivePin, pmt); + dump_AM_MEDIA_TYPE(pmt); + + hr = IPin_QueryDirection(pReceivePin, &dir); + if(hr==S_OK && dir!=PINDIR_INPUT) + return VFW_E_INVALID_DIRECTION; + + return BaseOutputPinImpl_AttemptConnection(base, pReceivePin, pmt); +} + +static LONG WINAPI AviMuxOut_GetMediaTypeVersion(BasePin *base) +{ + return 0; +} + +static HRESULT WINAPI AviMuxOut_GetMediaType(BasePin *base, int iPosition, AM_MEDIA_TYPE *amt) +{ + TRACE("(%p)->(%d %p)\n", base, iPosition, amt); + + if(iPosition < 0) + return E_INVALIDARG; + if(iPosition > 0) + return VFW_S_NO_MORE_ITEMS; + + amt->majortype = MEDIATYPE_Stream; + amt->subtype = MEDIASUBTYPE_Avi; + amt->bFixedSizeSamples = TRUE; + amt->bTemporalCompression = FALSE; + amt->lSampleSize = 1; + amt->formattype = GUID_NULL; + amt->pUnk = NULL; + amt->cbFormat = 0; + amt->pbFormat = NULL; + return S_OK; +} + +static HRESULT WINAPI AviMuxOut_DecideAllocator(BaseOutputPin *base, + IMemInputPin *pPin, IMemAllocator **pAlloc) +{ + ALLOCATOR_PROPERTIES req, actual; + HRESULT hr; + + TRACE("(%p)->(%p %p)\n", base, pPin, pAlloc); + + hr = BaseOutputPinImpl_InitAllocator(base, pAlloc); + if(FAILED(hr)) + return hr; + + hr = IMemInputPin_GetAllocatorRequirements(pPin, &req); + if(FAILED(hr)) + req.cbAlign = 1; + req.cBuffers = 32; + req.cbBuffer = 0; + req.cbPrefix = 0; + + hr = IMemAllocator_SetProperties(*pAlloc, &req, &actual); + if(FAILED(hr)) + return hr; + + return IMemInputPin_NotifyAllocator(pPin, *pAlloc, TRUE); +} + +static HRESULT WINAPI AviMuxOut_BreakConnect(BaseOutputPin *base) +{ + FIXME("(%p)\n", base); + return E_NOTIMPL; +} + +static const BaseOutputPinFuncTable AviMuxOut_BaseOutputFuncTable = { + { + NULL, + AviMuxOut_AttemptConnection, + AviMuxOut_GetMediaTypeVersion, + AviMuxOut_GetMediaType + }, + NULL, + AviMuxOut_DecideAllocator, + AviMuxOut_BreakConnect +}; + +static inline AviMux* impl_from_out_IPin(IPin *iface) +{ + BasePin *bp = CONTAINING_RECORD(iface, BasePin, IPin_iface); + IBaseFilter *bf = bp->pinInfo.pFilter; + + return impl_from_IBaseFilter(bf); +} + +static HRESULT WINAPI AviMuxOut_QueryInterface(IPin *iface, REFIID riid, void **ppv) +{ + AviMux *This = impl_from_out_IPin(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); + + if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IPin)) + *ppv = iface; + else if(IsEqualIID(riid, &IID_IQualityControl)) + *ppv = &This->out->IQualityControl_iface; + else { + FIXME("no interface for %s\n", debugstr_guid(riid)); + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI AviMuxOut_AddRef(IPin *iface) +{ + AviMux *This = impl_from_out_IPin(iface); + return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface); +} + +static ULONG WINAPI AviMuxOut_Release(IPin *iface) +{ + AviMux *This = impl_from_out_IPin(iface); + return IBaseFilter_Release(&This->filter.IBaseFilter_iface); +} + +static HRESULT WINAPI AviMuxOut_Connect(IPin *iface, + IPin *pReceivePin, const AM_MEDIA_TYPE *pmt) +{ + AviMux *This = impl_from_out_IPin(iface); + HRESULT hr; + int i; + + TRACE("(%p)->(%p AM_MEDIA_TYPE(%p))\n", This, pReceivePin, pmt); + dump_AM_MEDIA_TYPE(pmt); + + hr = BaseOutputPinImpl_Connect(iface, pReceivePin, pmt); + if(FAILED(hr)) + return hr; + + for(i=0; i<This->input_pin_no; i++) { + if(!This->in[i]->pin.pin.pConnectedTo) + continue; + + hr = IFilterGraph_Reconnect(This->filter.filterInfo.pGraph, &This->in[i]->pin.pin.IPin_iface); + if(FAILED(hr)) { + BaseOutputPinImpl_Disconnect(iface); + break; + } + } + + if(hr == S_OK) + IBaseFilter_AddRef(&This->filter.IBaseFilter_iface); + return hr; +} + +static HRESULT WINAPI AviMuxOut_ReceiveConnection(IPin *iface, + IPin *pConnector, const AM_MEDIA_TYPE *pmt) +{ + AviMux *This = impl_from_out_IPin(iface); + TRACE("(%p)->(%p AM_MEDIA_TYPE(%p)\n", This, pConnector, pmt); + dump_AM_MEDIA_TYPE(pmt); + return BaseOutputPinImpl_ReceiveConnection(iface, pConnector, pmt); +} + +static HRESULT WINAPI AviMuxOut_Disconnect(IPin *iface) +{ + AviMux *This = impl_from_out_IPin(iface); + HRESULT hr; + + TRACE("(%p)\n", This); + + hr = BaseOutputPinImpl_Disconnect(iface); + if(hr == S_OK) + IBaseFilter_Release(&This->filter.IBaseFilter_iface); + return hr; +} + +static HRESULT WINAPI AviMuxOut_ConnectedTo(IPin *iface, IPin **pPin) +{ + AviMux *This = impl_from_out_IPin(iface); + TRACE("(%p)->(%p)\n", This, pPin); + return BasePinImpl_ConnectedTo(iface, pPin); +} + +static HRESULT WINAPI AviMuxOut_ConnectionMediaType(IPin *iface, AM_MEDIA_TYPE *pmt) +{ + AviMux *This = impl_from_out_IPin(iface); + TRACE("(%p)->(%p)\n", This, pmt); + return BasePinImpl_ConnectionMediaType(iface, pmt); +} + +static HRESULT WINAPI AviMuxOut_QueryPinInfo(IPin *iface, PIN_INFO *pInfo) +{ + AviMux *This = impl_from_out_IPin(iface); + TRACE("(%p)->(%p)\n", This, pInfo); + return BasePinImpl_QueryPinInfo(iface, pInfo); +} + +static HRESULT WINAPI AviMuxOut_QueryDirection(IPin *iface, PIN_DIRECTION *pPinDir) +{ + AviMux *This = impl_from_out_IPin(iface); + TRACE("(%p)->(%p)\n", This, pPinDir); + return BasePinImpl_QueryDirection(iface, pPinDir); +} + +static HRESULT WINAPI AviMuxOut_QueryId(IPin *iface, LPWSTR *Id) +{ + AviMux *This = impl_from_out_IPin(iface); + TRACE("(%p)->(%p)\n", This, Id); + return BasePinImpl_QueryId(iface, Id); +} + +static HRESULT WINAPI AviMuxOut_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *pmt) +{ + AviMux *This = impl_from_out_IPin(iface); + TRACE("(%p)->(AM_MEDIA_TYPE(%p))\n", This, pmt); + dump_AM_MEDIA_TYPE(pmt); + return BasePinImpl_QueryAccept(iface, pmt); +} + +static HRESULT WINAPI AviMuxOut_EnumMediaTypes(IPin *iface, IEnumMediaTypes **ppEnum) +{ + AviMux *This = impl_from_out_IPin(iface); + TRACE("(%p)->(%p)\n", This, ppEnum); + return BasePinImpl_EnumMediaTypes(iface, ppEnum); +} + +static HRESULT WINAPI AviMuxOut_QueryInternalConnections( + IPin *iface, IPin **apPin, ULONG *nPin) +{ + AviMux *This = impl_from_out_IPin(iface); + FIXME("(%p)->(%p %p)\n", This, apPin, nPin); + return E_NOTIMPL; +} + +static HRESULT WINAPI AviMuxOut_EndOfStream(IPin *iface) +{ + AviMux *This = impl_from_out_IPin(iface); + TRACE("(%p)\n", This); + return BaseOutputPinImpl_EndOfStream(iface); +} + +static HRESULT WINAPI AviMuxOut_BeginFlush(IPin *iface) +{ + AviMux *This = impl_from_out_IPin(iface); + TRACE("(%p)\n", This); + return BaseOutputPinImpl_BeginFlush(iface); +} + +static HRESULT WINAPI AviMuxOut_EndFlush(IPin *iface) +{ + AviMux *This = impl_from_out_IPin(iface); + TRACE("(%p)\n", This); + return BaseOutputPinImpl_EndFlush(iface); +} + +static HRESULT WINAPI AviMuxOut_NewSegment(IPin *iface, REFERENCE_TIME tStart, + REFERENCE_TIME tStop, double dRate) +{ + AviMux *This = impl_from_out_IPin(iface); + TRACE("(%p)->(%s %s %f)\n", This, wine_dbgstr_longlong(tStart), wine_dbgstr_longlong(tStop), dRate); + return BasePinImpl_NewSegment(iface, tStart, tStop, dRate); +} + +static const IPinVtbl AviMuxOut_PinVtbl = { + AviMuxOut_QueryInterface, + AviMuxOut_AddRef, + AviMuxOut_Release, + AviMuxOut_Connect, + AviMuxOut_ReceiveConnection, + AviMuxOut_Disconnect, + AviMuxOut_ConnectedTo, + AviMuxOut_ConnectionMediaType, + AviMuxOut_QueryPinInfo, + AviMuxOut_QueryDirection, + AviMuxOut_QueryId, + AviMuxOut_QueryAccept, + AviMuxOut_EnumMediaTypes, + AviMuxOut_QueryInternalConnections, + AviMuxOut_EndOfStream, + AviMuxOut_BeginFlush, + AviMuxOut_EndFlush, + AviMuxOut_NewSegment +}; + +static inline AviMux* impl_from_out_IQualityControl(IQualityControl *iface) +{ + AviMuxOut *amo = CONTAINING_RECORD(iface, AviMuxOut, IQualityControl_iface); + return impl_from_IBaseFilter(amo->pin.pin.pinInfo.pFilter); +} + +static HRESULT WINAPI AviMuxOut_QualityControl_QueryInterface( + IQualityControl *iface, REFIID riid, void **ppv) +{ + AviMux *This = impl_from_out_IQualityControl(iface); + return IPin_QueryInterface(&This->out->pin.pin.IPin_iface, riid, ppv); +} + +static ULONG WINAPI AviMuxOut_QualityControl_AddRef(IQualityControl *iface) +{ + AviMux *This = impl_from_out_IQualityControl(iface); + return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface); +} + +static ULONG WINAPI AviMuxOut_QualityControl_Release(IQualityControl *iface) +{ + AviMux *This = impl_from_out_IQualityControl(iface); + return IBaseFilter_Release(&This->filter.IBaseFilter_iface); +} + +static HRESULT WINAPI AviMuxOut_QualityControl_Notify(IQualityControl *iface, + IBaseFilter *pSelf, Quality q) +{ + AviMux *This = impl_from_out_IQualityControl(iface); + FIXME("(%p)->(%p { 0x%x %u %s %s })\n", This, pSelf, + q.Type, q.Proportion, + wine_dbgstr_longlong(q.Late), + wine_dbgstr_longlong(q.TimeStamp)); + return E_NOTIMPL; +} + +static HRESULT WINAPI AviMuxOut_QualityControl_SetSink( + IQualityControl *iface, IQualityControl *piqc) +{ + AviMux *This = impl_from_out_IQualityControl(iface); + FIXME("(%p)->(%p)\n", This, piqc); + return E_NOTIMPL; +} + +static const IQualityControlVtbl AviMuxOut_QualityControlVtbl = { + AviMuxOut_QualityControl_QueryInterface, + AviMuxOut_QualityControl_AddRef, + AviMuxOut_QualityControl_Release, + AviMuxOut_QualityControl_Notify, + AviMuxOut_QualityControl_SetSink +}; + +static HRESULT WINAPI AviMuxIn_CheckMediaType(BasePin *base, const AM_MEDIA_TYPE *pmt) +{ + TRACE("(%p:%s)->(AM_MEDIA_TYPE(%p))\n", base, debugstr_w(base->pinInfo.achName), pmt); + dump_AM_MEDIA_TYPE(pmt); + + if(IsEqualIID(&pmt->majortype, &MEDIATYPE_Audio) && + IsEqualIID(&pmt->formattype, &FORMAT_WaveFormatEx)) + return S_OK; + if(IsEqualIID(&pmt->majortype, &MEDIATYPE_Interleaved) && + IsEqualIID(&pmt->formattype, &FORMAT_DvInfo)) + return S_OK; + if(IsEqualIID(&pmt->majortype, &MEDIATYPE_Video) && + (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo) || + IsEqualIID(&pmt->formattype, &FORMAT_DvInfo))) + return S_OK; + return S_FALSE; +} + +static LONG WINAPI AviMuxIn_GetMediaTypeVersion(BasePin *base) +{ + return 0; +} + +static HRESULT WINAPI AviMuxIn_GetMediaType(BasePin *base, int iPosition, AM_MEDIA_TYPE *amt) +{ + return S_FALSE; +} + +static HRESULT WINAPI AviMuxIn_Receive(BaseInputPin *base, IMediaSample *pSample) +{ + AviMuxIn *avimuxin = CONTAINING_RECORD(base, AviMuxIn, pin); + AviMux *avimux = impl_from_IBaseFilter(base->pin.pinInfo.pFilter); + REFERENCE_TIME start, stop; + IMediaSample *sample; + int frames_no; + IMediaSample2 *ms2; + BYTE *frame, *buf; + DWORD max_size, size; + DWORD flags; + HRESULT hr; + + TRACE("(%p:%s)->(%p)\n", base, debugstr_w(base->pin.pinInfo.achName), pSample); + + hr = IMediaSample_QueryInterface(pSample, &IID_IMediaSample2, (void**)&ms2); + if(SUCCEEDED(hr)) { + AM_SAMPLE2_PROPERTIES props; + + memset(&props, 0, sizeof(props)); + hr = IMediaSample2_GetProperties(ms2, sizeof(props), (BYTE*)&props); + IMediaSample2_Release(ms2); + if(FAILED(hr)) + return hr; + + flags = props.dwSampleFlags; + frame = props.pbBuffer; + size = props.lActual; + }else { + flags = IMediaSample_IsSyncPoint(pSample) == S_OK ? AM_SAMPLE_SPLICEPOINT : 0; + hr = IMediaSample_GetPointer(pSample, &frame); + if(FAILED(hr)) + return hr; + size = IMediaSample_GetActualDataLength(pSample); + } + + if(!avimuxin->pin.pin.mtCurrent.bTemporalCompression) + flags |= AM_SAMPLE_SPLICEPOINT; + + hr = IMediaSample_GetTime(pSample, &start, &stop); + if(FAILED(hr)) + return hr; + + if(avimuxin->stop>stop) + return VFW_E_START_TIME_AFTER_END; + + if(avimux->start == -1) + avimux->start = start; + if(avimux->stop < stop) + avimux->stop = stop; + + if(avimux->avih.dwSuggestedBufferSize < ALIGN(size)+sizeof(RIFFCHUNK)) + avimux->avih.dwSuggestedBufferSize = ALIGN(size) + sizeof(RIFFCHUNK); + if(avimuxin->strh.dwSuggestedBufferSize < ALIGN(size)+sizeof(RIFFCHUNK)) + avimuxin->strh.dwSuggestedBufferSize = ALIGN(size) + sizeof(RIFFCHUNK); + + frames_no = 1; + if(avimuxin->stop!=-1 && start > avimuxin->stop) { + frames_no += (double)(start - avimuxin->stop) / 10000000 + * avimuxin->strh.dwRate / avimuxin->strh.dwScale + 0.5; + } + avimuxin->stop = stop; + + while(--frames_no) { + /* TODO: store all control frames in one buffer */ + hr = IMemAllocator_GetBuffer(avimuxin->samples_allocator, &sample, NULL, NULL, 0); + if(FAILED(hr)) + return hr; + hr = IMediaSample_SetActualDataLength(sample, 0); + if(SUCCEEDED(hr)) + hr = IMediaSample_SetDiscontinuity(sample, TRUE); + if(SUCCEEDED(hr)) + hr = IMediaSample_SetSyncPoint(sample, FALSE); + if(SUCCEEDED(hr)) + hr = queue_sample(avimux, avimuxin, sample); + IMediaSample_Release(sample); + if(FAILED(hr)) + return hr; + } + + hr = IMemAllocator_GetBuffer(avimuxin->samples_allocator, &sample, NULL, NULL, 0); + if(FAILED(hr)) + return hr; + max_size = IMediaSample_GetSize(sample); + if(size > max_size) + size = max_size; + hr = IMediaSample_SetActualDataLength(sample, size); + if(SUCCEEDED(hr)) + hr = IMediaSample_SetDiscontinuity(sample, FALSE); + if(SUCCEEDED(hr)) + hr = IMediaSample_SetSyncPoint(sample, flags & AM_SAMPLE_SPLICEPOINT); + /* TODO: avoid unnecessary copying */ + if(SUCCEEDED(hr)) + hr = IMediaSample_GetPointer(sample, &buf); + if(SUCCEEDED(hr)) { + memcpy(buf, frame, size); + hr = queue_sample(avimux, avimuxin, sample); + } + IMediaSample_Release(sample); + + return hr; +} + +static const BaseInputPinFuncTable AviMuxIn_BaseInputFuncTable = { + { + AviMuxIn_CheckMediaType, + NULL, + AviMuxIn_GetMediaTypeVersion, + AviMuxIn_GetMediaType + }, + AviMuxIn_Receive +}; + +static inline AviMux* impl_from_in_IPin(IPin *iface) +{ + BasePin *bp = CONTAINING_RECORD(iface, BasePin, IPin_iface); + IBaseFilter *bf = bp->pinInfo.pFilter; + + return impl_from_IBaseFilter(bf); +} + +static inline AviMuxIn* AviMuxIn_from_IPin(IPin *iface) +{ + BasePin *bp = CONTAINING_RECORD(iface, BasePin, IPin_iface); + BaseInputPin *bip = CONTAINING_RECORD(bp, BaseInputPin, pin); + return CONTAINING_RECORD(bip, AviMuxIn, pin); +} + +static HRESULT WINAPI AviMuxIn_QueryInterface(IPin *iface, REFIID riid, void **ppv) +{ + AviMux *This = impl_from_in_IPin(iface); + AviMuxIn *avimuxin = AviMuxIn_from_IPin(iface); + + TRACE("(%p:%s)->(%s %p)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName), + debugstr_guid(riid), ppv); + + if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IPin)) + *ppv = &avimuxin->pin.pin.IPin_iface; + else if(IsEqualIID(riid, &IID_IAMStreamControl)) + *ppv = &avimuxin->IAMStreamControl_iface; + else if(IsEqualIID(riid, &IID_IMemInputPin)) + *ppv = &avimuxin->pin.IMemInputPin_iface; + else if(IsEqualIID(riid, &IID_IPropertyBag)) + *ppv = &avimuxin->IPropertyBag_iface; + else if(IsEqualIID(riid, &IID_IQualityControl)) + *ppv = &avimuxin->IQualityControl_iface; + else { + FIXME("no interface for %s\n", debugstr_guid(riid)); + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI AviMuxIn_AddRef(IPin *iface) +{ + AviMux *This = impl_from_in_IPin(iface); + return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface); +} + +static ULONG WINAPI AviMuxIn_Release(IPin *iface) +{ + AviMux *This = impl_from_in_IPin(iface); + return IBaseFilter_Release(&This->filter.IBaseFilter_iface); +} + +static HRESULT WINAPI AviMuxIn_Connect(IPin *iface, + IPin *pReceivePin, const AM_MEDIA_TYPE *pmt) +{ + AviMux *This = impl_from_in_IPin(iface); + AviMuxIn *avimuxin = AviMuxIn_from_IPin(iface); + TRACE("(%p:%s)->(%p AM_MEDIA_TYPE(%p))\n", This, + debugstr_w(avimuxin->pin.pin.pinInfo.achName), pReceivePin, pmt); + dump_AM_MEDIA_TYPE(pmt); + return BaseInputPinImpl_Connect(iface, pReceivePin, pmt); +} + +static HRESULT WINAPI AviMuxIn_ReceiveConnection(IPin *iface, + IPin *pConnector, const AM_MEDIA_TYPE *pmt) +{ + AviMux *This = impl_from_in_IPin(iface); + AviMuxIn *avimuxin = AviMuxIn_from_IPin(iface); + HRESULT hr; + + TRACE("(%p:%s)->(%p AM_MEDIA_TYPE(%p))\n", This, + debugstr_w(avimuxin->pin.pin.pinInfo.achName), pConnector, pmt); + dump_AM_MEDIA_TYPE(pmt); + + if(!pmt) + return E_POINTER; + + hr = BaseInputPinImpl_ReceiveConnection(iface, pConnector, pmt); + if(FAILED(hr)) + return hr; + + if(IsEqualIID(&pmt->majortype, &MEDIATYPE_Video) && + IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo)) { + ALLOCATOR_PROPERTIES req, act; + VIDEOINFOHEADER *vih; + int size; + + vih = (VIDEOINFOHEADER*)pmt->pbFormat; + avimuxin->strh.fcc = ckidSTREAMHEADER; + avimuxin->strh.cb = sizeof(AVISTREAMHEADER) - FIELD_OFFSET(AVISTREAMHEADER, fccType); + avimuxin->strh.fccType = streamtypeVIDEO; + /* FIXME: fccHandler should be set differently */ + avimuxin->strh.fccHandler = vih->bmiHeader.biCompression ? + vih->bmiHeader.biCompression : FCC('D','I','B',' '); + avimuxin->avg_time_per_frame = vih->AvgTimePerFrame; + avimuxin->stop = -1; + + req.cBuffers = 32; + req.cbBuffer = vih->bmiHeader.biSizeImage; + req.cbAlign = 1; + req.cbPrefix = sizeof(void*); + hr = IMemAllocator_SetProperties(avimuxin->samples_allocator, &req, &act); + if(SUCCEEDED(hr)) + hr = IMemAllocator_Commit(avimuxin->samples_allocator); + if(FAILED(hr)) { + BasePinImpl_Disconnect(iface); + return hr; + } + + size = pmt->cbFormat - FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader); + avimuxin->strf = CoTaskMemAlloc(sizeof(RIFFCHUNK) + ALIGN(FIELD_OFFSET(BITMAPINFO, bmiColors[vih->bmiHeader.biClrUsed]))); + avimuxin->strf->fcc = ckidSTREAMFORMAT; + avimuxin->strf->cb = FIELD_OFFSET(BITMAPINFO, bmiColors[vih->bmiHeader.biClrUsed]); + if(size > avimuxin->strf->cb) + size = avimuxin->strf->cb; + memcpy(avimuxin->strf->data, &vih->bmiHeader, size); + }else { + FIXME("format not supported: %s %s\n", debugstr_guid(&pmt->majortype), + debugstr_guid(&pmt->formattype)); + return E_NOTIMPL; + } + + return create_input_pin(This); +} + +static HRESULT WINAPI AviMuxIn_Disconnect(IPin *iface) +{ + AviMux *This = impl_from_in_IPin(iface); + AviMuxIn *avimuxin = AviMuxIn_from_IPin(iface); + IMediaSample **prev, *cur; + HRESULT hr; + + TRACE("(%p:%s)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName)); + + hr = BasePinImpl_Disconnect(iface); + if(FAILED(hr)) + return hr; + + IMemAllocator_Decommit(avimuxin->samples_allocator); + while(avimuxin->samples_head) { + cur = avimuxin->samples_head; + hr = IMediaSample_GetPointer(cur, (BYTE**)&prev); + if(FAILED(hr)) + break; + prev--; + + cur = avimuxin->samples_head; + avimuxin->samples_head = *prev; + IMediaSample_Release(cur); + + if(cur == avimuxin->samples_head) + avimuxin->samples_head = NULL; + } + CoTaskMemFree(avimuxin->strf); + avimuxin->strf = NULL; + return hr; +} + +static HRESULT WINAPI AviMuxIn_ConnectedTo(IPin *iface, IPin **pPin) +{ + AviMux *This = impl_from_in_IPin(iface); + AviMuxIn *avimuxin = AviMuxIn_from_IPin(iface); + TRACE("(%p:%s)->(%p)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName), pPin); + return BasePinImpl_ConnectedTo(iface, pPin); +} + +static HRESULT WINAPI AviMuxIn_ConnectionMediaType(IPin *iface, AM_MEDIA_TYPE *pmt) +{ + AviMux *This = impl_from_in_IPin(iface); + AviMuxIn *avimuxin = AviMuxIn_from_IPin(iface); + TRACE("(%p:%s)->(%p)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName), pmt); + return BasePinImpl_ConnectionMediaType(iface, pmt); +} + +static HRESULT WINAPI AviMuxIn_QueryPinInfo(IPin *iface, PIN_INFO *pInfo) +{ + AviMux *This = impl_from_in_IPin(iface); + AviMuxIn *avimuxin = AviMuxIn_from_IPin(iface); + TRACE("(%p:%s)->(%p)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName), pInfo); + return BasePinImpl_QueryPinInfo(iface, pInfo); +} + +static HRESULT WINAPI AviMuxIn_QueryDirection(IPin *iface, PIN_DIRECTION *pPinDir) +{ + AviMux *This = impl_from_in_IPin(iface); + AviMuxIn *avimuxin = AviMuxIn_from_IPin(iface); + TRACE("(%p:%s)->(%p)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName), pPinDir); + return BasePinImpl_QueryDirection(iface, pPinDir); +} + +static HRESULT WINAPI AviMuxIn_QueryId(IPin *iface, LPWSTR *Id) +{ + AviMux *This = impl_from_in_IPin(iface); + AviMuxIn *avimuxin = AviMuxIn_from_IPin(iface); + TRACE("(%p:%s)->(%p)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName), Id); + return BasePinImpl_QueryId(iface, Id); +} + +static HRESULT WINAPI AviMuxIn_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *pmt) +{ + AviMux *This = impl_from_in_IPin(iface); + AviMuxIn *avimuxin = AviMuxIn_from_IPin(iface); + TRACE("(%p:%s)->(AM_MEDIA_TYPE(%p))\n", This, + debugstr_w(avimuxin->pin.pin.pinInfo.achName), pmt); + dump_AM_MEDIA_TYPE(pmt); + return BasePinImpl_QueryAccept(iface, pmt); +} + +static HRESULT WINAPI AviMuxIn_EnumMediaTypes(IPin *iface, IEnumMediaTypes **ppEnum) +{ + AviMux *This = impl_from_in_IPin(iface); + AviMuxIn *avimuxin = AviMuxIn_from_IPin(iface); + TRACE("(%p:%s)->(%p)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName), ppEnum); + return BasePinImpl_EnumMediaTypes(iface, ppEnum); +} + +static HRESULT WINAPI AviMuxIn_QueryInternalConnections( + IPin *iface, IPin **apPin, ULONG *nPin) +{ + AviMux *This = impl_from_in_IPin(iface); + AviMuxIn *avimuxin = AviMuxIn_from_IPin(iface); + TRACE("(%p:%s)->(%p %p)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName), apPin, nPin); + return BasePinImpl_QueryInternalConnections(iface, apPin, nPin); +} + +static HRESULT WINAPI AviMuxIn_EndOfStream(IPin *iface) +{ + AviMux *This = impl_from_in_IPin(iface); + AviMuxIn *avimuxin = AviMuxIn_from_IPin(iface); + TRACE("(%p:%s)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName)); + return BaseInputPinImpl_EndOfStream(iface); +} + +static HRESULT WINAPI AviMuxIn_BeginFlush(IPin *iface) +{ + AviMux *This = impl_from_in_IPin(iface); + AviMuxIn *avimuxin = AviMuxIn_from_IPin(iface); + TRACE("(%p:%s)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName)); + return BaseInputPinImpl_BeginFlush(iface); +} + +static HRESULT WINAPI AviMuxIn_EndFlush(IPin *iface) +{ + AviMux *This = impl_from_in_IPin(iface); + AviMuxIn *avimuxin = AviMuxIn_from_IPin(iface); + TRACE("(%p:%s)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName)); + return BaseInputPinImpl_EndFlush(iface); +} + +static HRESULT WINAPI AviMuxIn_NewSegment(IPin *iface, REFERENCE_TIME tStart, + REFERENCE_TIME tStop, double dRate) +{ + AviMux *This = impl_from_in_IPin(iface); + AviMuxIn *avimuxin = AviMuxIn_from_IPin(iface); + TRACE("(%p:%s)->(%s %s %f)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName), + wine_dbgstr_longlong(tStart), wine_dbgstr_longlong(tStop), dRate); + return BasePinImpl_NewSegment(iface, tStart, tStop, dRate); +} + +static const IPinVtbl AviMuxIn_PinVtbl = { + AviMuxIn_QueryInterface, + AviMuxIn_AddRef, + AviMuxIn_Release, + AviMuxIn_Connect, + AviMuxIn_ReceiveConnection, + AviMuxIn_Disconnect, + AviMuxIn_ConnectedTo, + AviMuxIn_ConnectionMediaType, + AviMuxIn_QueryPinInfo, + AviMuxIn_QueryDirection, + AviMuxIn_QueryId, + AviMuxIn_QueryAccept, + AviMuxIn_EnumMediaTypes, + AviMuxIn_QueryInternalConnections, + AviMuxIn_EndOfStream, + AviMuxIn_BeginFlush, + AviMuxIn_EndFlush, + AviMuxIn_NewSegment +}; + +static inline AviMuxIn* AviMuxIn_from_IAMStreamControl(IAMStreamControl *iface) +{ + return CONTAINING_RECORD(iface, AviMuxIn, IAMStreamControl_iface); +} + +static HRESULT WINAPI AviMuxIn_AMStreamControl_QueryInterface( + IAMStreamControl *iface, REFIID riid, void **ppv) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IAMStreamControl(iface); + return IPin_QueryInterface(&avimuxin->pin.pin.IPin_iface, riid, ppv); +} + +static ULONG WINAPI AviMuxIn_AMStreamControl_AddRef(IAMStreamControl *iface) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IAMStreamControl(iface); + AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface); + return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface); +} + +static ULONG WINAPI AviMuxIn_AMStreamControl_Release(IAMStreamControl *iface) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IAMStreamControl(iface); + AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface); + return IBaseFilter_Release(&This->filter.IBaseFilter_iface); +} + +static HRESULT WINAPI AviMuxIn_AMStreamControl_StartAt(IAMStreamControl *iface, + const REFERENCE_TIME *ptStart, DWORD dwCookie) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IAMStreamControl(iface); + AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface); + FIXME("(%p:%s)->(%p %x)\n", This, + debugstr_w(avimuxin->pin.pin.pinInfo.achName), ptStart, dwCookie); + return E_NOTIMPL; +} + +static HRESULT WINAPI AviMuxIn_AMStreamControl_StopAt(IAMStreamControl *iface, + const REFERENCE_TIME *ptStop, BOOL bSendExtra, DWORD dwCookie) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IAMStreamControl(iface); + AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface); + FIXME("(%p:%s)->(%p %x %x)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName), + ptStop, bSendExtra, dwCookie); + return E_NOTIMPL; +} + +static HRESULT WINAPI AviMuxIn_AMStreamControl_GetInfo( + IAMStreamControl *iface, AM_STREAM_INFO *pInfo) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IAMStreamControl(iface); + AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface); + FIXME("(%p:%s)->(%p)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName), pInfo); + return E_NOTIMPL; +} + +static const IAMStreamControlVtbl AviMuxIn_AMStreamControlVtbl = { + AviMuxIn_AMStreamControl_QueryInterface, + AviMuxIn_AMStreamControl_AddRef, + AviMuxIn_AMStreamControl_Release, + AviMuxIn_AMStreamControl_StartAt, + AviMuxIn_AMStreamControl_StopAt, + AviMuxIn_AMStreamControl_GetInfo +}; + +static inline AviMuxIn* AviMuxIn_from_IMemInputPin(IMemInputPin *iface) +{ + BaseInputPin *bip = CONTAINING_RECORD(iface, BaseInputPin, IMemInputPin_iface); + return CONTAINING_RECORD(bip, AviMuxIn, pin); +} + +static HRESULT WINAPI AviMuxIn_MemInputPin_QueryInterface( + IMemInputPin *iface, REFIID riid, void **ppv) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IMemInputPin(iface); + return IPin_QueryInterface(&avimuxin->pin.pin.IPin_iface, riid, ppv); +} + +static ULONG WINAPI AviMuxIn_MemInputPin_AddRef(IMemInputPin *iface) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IMemInputPin(iface); + AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface); + return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface); +} + +static ULONG WINAPI AviMuxIn_MemInputPin_Release(IMemInputPin *iface) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IMemInputPin(iface); + AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface); + return IBaseFilter_Release(&This->filter.IBaseFilter_iface); +} + +static HRESULT WINAPI AviMuxIn_MemInputPin_GetAllocator( + IMemInputPin *iface, IMemAllocator **ppAllocator) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IMemInputPin(iface); + AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface); + + TRACE("(%p:%s)->(%p)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName), ppAllocator); + + if(!ppAllocator) + return E_POINTER; + + IMemAllocator_AddRef(avimuxin->pin.pAllocator); + *ppAllocator = avimuxin->pin.pAllocator; + return S_OK; +} + +static HRESULT WINAPI AviMuxIn_MemInputPin_NotifyAllocator( + IMemInputPin *iface, IMemAllocator *pAllocator, BOOL bReadOnly) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IMemInputPin(iface); + AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface); + ALLOCATOR_PROPERTIES props; + HRESULT hr; + + TRACE("(%p:%s)->(%p %x)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName), + pAllocator, bReadOnly); + + if(!pAllocator) + return E_POINTER; + + memset(&props, 0, sizeof(props)); + hr = IMemAllocator_GetProperties(pAllocator, &props); + if(FAILED(hr)) + return hr; + + props.cbAlign = 1; + props.cbPrefix = 8; + return IMemAllocator_SetProperties(avimuxin->pin.pAllocator, &props, &props); +} + +static HRESULT WINAPI AviMuxIn_MemInputPin_GetAllocatorRequirements( + IMemInputPin *iface, ALLOCATOR_PROPERTIES *pProps) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IMemInputPin(iface); + AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface); + + TRACE("(%p:%s)->(%p)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName), pProps); + + if(!pProps) + return E_POINTER; + + pProps->cbAlign = 1; + pProps->cbPrefix = 8; + return S_OK; +} + +static HRESULT WINAPI AviMuxIn_MemInputPin_Receive( + IMemInputPin *iface, IMediaSample *pSample) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IMemInputPin(iface); + AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface); + + TRACE("(%p:%s)->(%p)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName), pSample); + + return avimuxin->pin.pFuncsTable->pfnReceive(&avimuxin->pin, pSample); +} + +static HRESULT WINAPI AviMuxIn_MemInputPin_ReceiveMultiple(IMemInputPin *iface, + IMediaSample **pSamples, LONG nSamples, LONG *nSamplesProcessed) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IMemInputPin(iface); + AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface); + HRESULT hr = S_OK; + + TRACE("(%p:%s)->(%p %d %p)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName), + pSamples, nSamples, nSamplesProcessed); + + for(*nSamplesProcessed=0; *nSamplesProcessed<nSamples; (*nSamplesProcessed)++) + { + hr = avimuxin->pin.pFuncsTable->pfnReceive(&avimuxin->pin, pSamples[*nSamplesProcessed]); + if(hr != S_OK) + break; + } + + return hr; +} + +static HRESULT WINAPI AviMuxIn_MemInputPin_ReceiveCanBlock(IMemInputPin *iface) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IMemInputPin(iface); + AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface); + HRESULT hr; + + TRACE("(%p:%s)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName)); + + if(!This->out->pin.pMemInputPin) + return S_FALSE; + + hr = IMemInputPin_ReceiveCanBlock(This->out->pin.pMemInputPin); + return hr != S_FALSE ? S_OK : S_FALSE; +} + +static const IMemInputPinVtbl AviMuxIn_MemInputPinVtbl = { + AviMuxIn_MemInputPin_QueryInterface, + AviMuxIn_MemInputPin_AddRef, + AviMuxIn_MemInputPin_Release, + AviMuxIn_MemInputPin_GetAllocator, + AviMuxIn_MemInputPin_NotifyAllocator, + AviMuxIn_MemInputPin_GetAllocatorRequirements, + AviMuxIn_MemInputPin_Receive, + AviMuxIn_MemInputPin_ReceiveMultiple, + AviMuxIn_MemInputPin_ReceiveCanBlock +}; + +static inline AviMuxIn* AviMuxIn_from_IPropertyBag(IPropertyBag *iface) +{ + return CONTAINING_RECORD(iface, AviMuxIn, IPropertyBag_iface); +} + +static HRESULT WINAPI AviMuxIn_PropertyBag_QueryInterface( + IPropertyBag *iface, REFIID riid, void **ppv) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IPropertyBag(iface); + return IPin_QueryInterface(&avimuxin->pin.pin.IPin_iface, riid, ppv); +} + +static ULONG WINAPI AviMuxIn_PropertyBag_AddRef(IPropertyBag *iface) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IPropertyBag(iface); + AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface); + return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface); +} + +static ULONG WINAPI AviMuxIn_PropertyBag_Release(IPropertyBag *iface) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IPropertyBag(iface); + AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface); + return IBaseFilter_Release(&This->filter.IBaseFilter_iface); +} + +static HRESULT WINAPI AviMuxIn_PropertyBag_Read(IPropertyBag *iface, + LPCOLESTR pszPropName, VARIANT *pVar, IErrorLog *pErrorLog) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IPropertyBag(iface); + AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface); + FIXME("(%p:%s)->(%s %p %p)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName), + debugstr_w(pszPropName), pVar, pErrorLog); + return E_NOTIMPL; +} + +static HRESULT WINAPI AviMuxIn_PropertyBag_Write(IPropertyBag *iface, + LPCOLESTR pszPropName, VARIANT *pVar) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IPropertyBag(iface); + AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface); + FIXME("(%p:%s)->(%s %p)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName), + debugstr_w(pszPropName), pVar); + return E_NOTIMPL; +} + +static const IPropertyBagVtbl AviMuxIn_PropertyBagVtbl = { + AviMuxIn_PropertyBag_QueryInterface, + AviMuxIn_PropertyBag_AddRef, + AviMuxIn_PropertyBag_Release, + AviMuxIn_PropertyBag_Read, + AviMuxIn_PropertyBag_Write +}; + +static inline AviMuxIn* AviMuxIn_from_IQualityControl(IQualityControl *iface) +{ + return CONTAINING_RECORD(iface, AviMuxIn, IQualityControl_iface); +} + +static HRESULT WINAPI AviMuxIn_QualityControl_QueryInterface( + IQualityControl *iface, REFIID riid, void **ppv) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IQualityControl(iface); + return IPin_QueryInterface(&avimuxin->pin.pin.IPin_iface, riid, ppv); +} + +static ULONG WINAPI AviMuxIn_QualityControl_AddRef(IQualityControl *iface) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IQualityControl(iface); + AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface); + return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface); +} + +static ULONG WINAPI AviMuxIn_QualityControl_Release(IQualityControl *iface) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IQualityControl(iface); + AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface); + return IBaseFilter_Release(&This->filter.IBaseFilter_iface); +} + +static HRESULT WINAPI AviMuxIn_QualityControl_Notify(IQualityControl *iface, + IBaseFilter *pSelf, Quality q) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IQualityControl(iface); + AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface); + FIXME("(%p:%s)->(%p { 0x%x %u %s %s })\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName), pSelf, + q.Type, q.Proportion, + wine_dbgstr_longlong(q.Late), + wine_dbgstr_longlong(q.TimeStamp)); + return E_NOTIMPL; +} + +static HRESULT WINAPI AviMuxIn_QualityControl_SetSink( + IQualityControl *iface, IQualityControl *piqc) +{ + AviMuxIn *avimuxin = AviMuxIn_from_IQualityControl(iface); + AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface); + FIXME("(%p:%s)->(%p)\n", This, debugstr_w(avimuxin->pin.pin.pinInfo.achName), piqc); + return E_NOTIMPL; +} + +static const IQualityControlVtbl AviMuxIn_QualityControlVtbl = { + AviMuxIn_QualityControl_QueryInterface, + AviMuxIn_QualityControl_AddRef, + AviMuxIn_QualityControl_Release, + AviMuxIn_QualityControl_Notify, + AviMuxIn_QualityControl_SetSink +}; + +static HRESULT create_input_pin(AviMux *avimux) +{ + static const WCHAR name[] = {'I','n','p','u','t',' ','0','0',0}; + PIN_INFO info; + HRESULT hr; + + if(avimux->input_pin_no >= MAX_PIN_NO-1) + return E_FAIL; + + info.dir = PINDIR_INPUT; + info.pFilter = &avimux->filter.IBaseFilter_iface; + memcpy(info.achName, name, sizeof(name)); + info.achName[7] = '0' + (avimux->input_pin_no+1) % 10; + info.achName[6] = '0' + (avimux->input_pin_no+1) / 10; + + hr = BaseInputPin_Construct(&AviMuxIn_PinVtbl, sizeof(AviMuxIn), &info, + &AviMuxIn_BaseInputFuncTable, &avimux->filter.csFilter, NULL, (IPin**)&avimux->in[avimux->input_pin_no]); + if(FAILED(hr)) + return hr; + avimux->in[avimux->input_pin_no]->pin.IMemInputPin_iface.lpVtbl = &AviMuxIn_MemInputPinVtbl; + avimux->in[avimux->input_pin_no]->IAMStreamControl_iface.lpVtbl = &AviMuxIn_AMStreamControlVtbl; + avimux->in[avimux->input_pin_no]->IPropertyBag_iface.lpVtbl = &AviMuxIn_PropertyBagVtbl; + avimux->in[avimux->input_pin_no]->IQualityControl_iface.lpVtbl = &AviMuxIn_QualityControlVtbl; + + avimux->in[avimux->input_pin_no]->samples_head = NULL; + hr = CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC_SERVER, + &IID_IMemAllocator, (void**)&avimux->in[avimux->input_pin_no]->samples_allocator); + if(FAILED(hr)) { + BaseInputPinImpl_Release(&avimux->in[avimux->input_pin_no]->pin.pin.IPin_iface); + return hr; + } + + hr = CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC_SERVER, + &IID_IMemAllocator, (void**)&avimux->in[avimux->input_pin_no]->pin.pAllocator); + if(FAILED(hr)) { + IMemAllocator_Release(avimux->in[avimux->input_pin_no]->samples_allocator); + BaseInputPinImpl_Release(&avimux->in[avimux->input_pin_no]->pin.pin.IPin_iface); + return hr; + } + + avimux->in[avimux->input_pin_no]->stream_time = 0; + memset(&avimux->in[avimux->input_pin_no]->strh, 0, sizeof(avimux->in[avimux->input_pin_no]->strh)); + avimux->in[avimux->input_pin_no]->strf = NULL; + memset(&avimux->in[avimux->input_pin_no]->indx_data, 0, sizeof(avimux->in[avimux->input_pin_no]->indx_data)); + memset(&avimux->in[avimux->input_pin_no]->ix_data, 0, sizeof(avimux->in[avimux->input_pin_no]->ix_data)); + avimux->in[avimux->input_pin_no]->indx = (AVISUPERINDEX*)&avimux->in[avimux->input_pin_no]->indx_data; + avimux->in[avimux->input_pin_no]->ix = (AVISTDINDEX*)avimux->in[avimux->input_pin_no]->ix_data; + + avimux->input_pin_no++; + return S_OK; +} + +IUnknown* WINAPI QCAP_createAVIMux(IUnknown *pUnkOuter, HRESULT *phr) +{ + static const WCHAR output_name[] = {'A','V','I',' ','O','u','t',0}; + + AviMux *avimux; + PIN_INFO info; + HRESULT hr; + + TRACE("(%p)\n", pUnkOuter); + + if(pUnkOuter) { + *phr = CLASS_E_NOAGGREGATION; + return NULL; + } + + avimux = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AviMux)); + if(!avimux) { + *phr = E_OUTOFMEMORY; + return NULL; + } + + BaseFilter_Init(&avimux->filter, &AviMuxVtbl, &CLSID_AviDest, + (DWORD_PTR)(__FILE__ ": AviMux.csFilter"), &filter_func_table); + avimux->IConfigAviMux_iface.lpVtbl = &ConfigAviMuxVtbl; + avimux->IConfigInterleaving_iface.lpVtbl = &ConfigInterleavingVtbl; + avimux->IMediaSeeking_iface.lpVtbl = &MediaSeekingVtbl; + avimux->IPersistMediaPropertyBag_iface.lpVtbl = &PersistMediaPropertyBagVtbl; + avimux->ISpecifyPropertyPages_iface.lpVtbl = &SpecifyPropertyPagesVtbl; + + info.dir = PINDIR_OUTPUT; + info.pFilter = &avimux->filter.IBaseFilter_iface; + lstrcpyW(info.achName, output_name); + hr = BaseOutputPin_Construct(&AviMuxOut_PinVtbl, sizeof(AviMuxOut), &info, + &AviMuxOut_BaseOutputFuncTable, &avimux->filter.csFilter, (IPin**)&avimux->out); + if(FAILED(hr)) { + BaseFilterImpl_Release(&avimux->filter.IBaseFilter_iface); + HeapFree(GetProcessHeap(), 0, avimux); + *phr = hr; + return NULL; + } + avimux->out->IQualityControl_iface.lpVtbl = &AviMuxOut_QualityControlVtbl; + avimux->out->cur_stream = 0; + avimux->out->cur_time = 0; + avimux->out->stream = NULL; + + hr = create_input_pin(avimux); + if(FAILED(hr)) { + BaseOutputPinImpl_Release(&avimux->out->pin.pin.IPin_iface); + BaseFilterImpl_Release(&avimux->filter.IBaseFilter_iface); + HeapFree(GetProcessHeap(), 0, avimux); + *phr = hr; + return NULL; + } + + avimux->interleave = 10000000; + + ObjectRefCount(TRUE); + *phr = S_OK; + return (IUnknown*)&avimux->filter.IBaseFilter_iface; +} diff --git a/dll/directx/wine/qcap/capture.h b/dll/directx/wine/qcap/capture.h new file mode 100644 index 00000000000..65ed2dfc271 --- /dev/null +++ b/dll/directx/wine/qcap/capture.h @@ -0,0 +1,37 @@ +/* DirectShow private capture header (QCAP.DLL) + * + * 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 + */ + +#ifndef __QCAP_CAPTURE_H__ +#define __QCAP_CAPTURE_H__ + +struct _Capture; +typedef struct _Capture Capture; + +Capture *qcap_driver_init(IPin*,USHORT) DECLSPEC_HIDDEN; +HRESULT qcap_driver_destroy(Capture*) DECLSPEC_HIDDEN; +HRESULT qcap_driver_set_format(Capture*,AM_MEDIA_TYPE*) DECLSPEC_HIDDEN; +HRESULT qcap_driver_get_format(const Capture*,AM_MEDIA_TYPE**) DECLSPEC_HIDDEN; +HRESULT qcap_driver_get_prop_range(Capture*,VideoProcAmpProperty,LONG*,LONG*,LONG*,LONG*,LONG*) DECLSPEC_HIDDEN; +HRESULT qcap_driver_get_prop(Capture*,VideoProcAmpProperty,LONG*,LONG*) DECLSPEC_HIDDEN; +HRESULT qcap_driver_set_prop(Capture*,VideoProcAmpProperty,LONG,LONG) DECLSPEC_HIDDEN; +HRESULT qcap_driver_run(Capture*,FILTER_STATE*) DECLSPEC_HIDDEN; +HRESULT qcap_driver_pause(Capture*,FILTER_STATE*) DECLSPEC_HIDDEN; +HRESULT qcap_driver_stop(Capture*,FILTER_STATE*) DECLSPEC_HIDDEN; + +#endif /* __QCAP_CAPTURE_H__ */ diff --git a/dll/directx/wine/qcap/capturegraph.c b/dll/directx/wine/qcap/capturegraph.c new file mode 100644 index 00000000000..bae75f83725 --- /dev/null +++ b/dll/directx/wine/qcap/capturegraph.c @@ -0,0 +1,881 @@ +/* Capture Graph Builder, Minimal edition + * + * Copyright 2005 Maarten Lankhorst + * Copyright 2005 Rolf Kalbermatter + * + * 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 "config.h" + +#include <stdio.h> +#include <stdarg.h> + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winerror.h" +#include "objbase.h" + +#include "evcode.h" +#include "strmif.h" +#include "control.h" +#include "vfwmsgs.h" +/* + *#include "amvideo.h" + *#include "mmreg.h" + *#include "dshow.h" + *#include "ddraw.h" + */ +#include "uuids.h" +#include "qcap_main.h" + +#include "wine/unicode.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(qcap); + +/*********************************************************************** +* ICaptureGraphBuilder & ICaptureGraphBuilder2 implementation +*/ +typedef struct CaptureGraphImpl +{ + ICaptureGraphBuilder2 ICaptureGraphBuilder2_iface; + ICaptureGraphBuilder ICaptureGraphBuilder_iface; + LONG ref; + IGraphBuilder *mygraph; + CRITICAL_SECTION csFilter; +} CaptureGraphImpl; + +static const ICaptureGraphBuilderVtbl builder_Vtbl; +static const ICaptureGraphBuilder2Vtbl builder2_Vtbl; + +static inline CaptureGraphImpl *impl_from_ICaptureGraphBuilder(ICaptureGraphBuilder *iface) +{ + return CONTAINING_RECORD(iface, CaptureGraphImpl, ICaptureGraphBuilder_iface); +} + +static inline CaptureGraphImpl *impl_from_ICaptureGraphBuilder2(ICaptureGraphBuilder2 *iface) +{ + return CONTAINING_RECORD(iface, CaptureGraphImpl, ICaptureGraphBuilder2_iface); +} + + +IUnknown * CALLBACK QCAP_createCaptureGraphBuilder2(IUnknown *pUnkOuter, + HRESULT *phr) +{ + CaptureGraphImpl * pCapture = NULL; + + TRACE("(%p, %p)\n", pUnkOuter, phr); + + *phr = CLASS_E_NOAGGREGATION; + if (pUnkOuter) + { + return NULL; + } + *phr = E_OUTOFMEMORY; + + pCapture = CoTaskMemAlloc(sizeof(CaptureGraphImpl)); + if (pCapture) + { + pCapture->ICaptureGraphBuilder2_iface.lpVtbl = &builder2_Vtbl; + pCapture->ICaptureGraphBuilder_iface.lpVtbl = &builder_Vtbl; + pCapture->ref = 1; + pCapture->mygraph = NULL; + InitializeCriticalSection(&pCapture->csFilter); + pCapture->csFilter.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": CaptureGraphImpl.csFilter"); + *phr = S_OK; + ObjectRefCount(TRUE); + } + return (IUnknown *)&pCapture->ICaptureGraphBuilder_iface; +} + +static HRESULT WINAPI +fnCaptureGraphBuilder2_QueryInterface(ICaptureGraphBuilder2 * iface, + REFIID riid, + LPVOID * ppv) +{ + CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder2(iface); + + TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv); + + *ppv = NULL; + if (IsEqualIID(riid, &IID_IUnknown)) + *ppv = &This->ICaptureGraphBuilder2_iface; + else if (IsEqualIID(riid, &IID_ICaptureGraphBuilder)) + *ppv = &This->ICaptureGraphBuilder_iface; + else if (IsEqualIID(riid, &IID_ICaptureGraphBuilder2)) + *ppv = &This->ICaptureGraphBuilder2_iface; + + if (*ppv) + { + IUnknown_AddRef((IUnknown *)(*ppv)); + TRACE ("-- Interface = %p\n", *ppv); + return S_OK; + } + + TRACE ("-- Interface: E_NOINTERFACE\n"); + return E_NOINTERFACE; +} + +static ULONG WINAPI +fnCaptureGraphBuilder2_AddRef(ICaptureGraphBuilder2 * iface) +{ + CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder2(iface); + DWORD ref = InterlockedIncrement(&This->ref); + + TRACE("(%p/%p)->() AddRef from %d\n", This, iface, ref - 1); + return ref; +} + +static ULONG WINAPI fnCaptureGraphBuilder2_Release(ICaptureGraphBuilder2 * iface) +{ + CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder2(iface); + DWORD ref = InterlockedDecrement(&This->ref); + + TRACE("(%p/%p)->() Release from %d\n", This, iface, ref + 1); + + if (!ref) + { + This->csFilter.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&This->csFilter); + if (This->mygraph) + IGraphBuilder_Release(This->mygraph); + CoTaskMemFree(This); + ObjectRefCount(FALSE); + } + return ref; +} + +static HRESULT WINAPI +fnCaptureGraphBuilder2_SetFilterGraph(ICaptureGraphBuilder2 * iface, + IGraphBuilder *pfg) +{ +/* The graph builder will automatically create a filter graph if you don't call + this method. If you call this method after the graph builder has created its + own filter graph, the call will fail. */ + IMediaEvent *pmev; + CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder2(iface); + + TRACE("(%p/%p)->(%p)\n", This, iface, pfg); + + if (This->mygraph) + return E_UNEXPECTED; + + if (!pfg) + return E_POINTER; + + This->mygraph = pfg; + IGraphBuilder_AddRef(This->mygraph); + if (SUCCEEDED(IGraphBuilder_QueryInterface(This->mygraph, + &IID_IMediaEvent, (LPVOID *)&pmev))) + { + IMediaEvent_CancelDefaultHandling(pmev, EC_REPAINT); + IMediaEvent_Release(pmev); + } + return S_OK; +} + +static HRESULT WINAPI +fnCaptureGraphBuilder2_GetFilterGraph(ICaptureGraphBuilder2 * iface, + IGraphBuilder **pfg) +{ + CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder2(iface); + + TRACE("(%p/%p)->(%p)\n", This, iface, pfg); + + if (!pfg) + return E_POINTER; + + *pfg = This->mygraph; + if (!This->mygraph) + { + TRACE("(%p) Getting NULL filtergraph\n", iface); + return E_UNEXPECTED; + } + + IGraphBuilder_AddRef(This->mygraph); + + TRACE("(%p) return filtergraph %p\n", iface, *pfg); + return S_OK; +} + +static HRESULT WINAPI +fnCaptureGraphBuilder2_SetOutputFileName(ICaptureGraphBuilder2 * iface, + const GUID *pType, + LPCOLESTR lpstrFile, + IBaseFilter **ppf, + IFileSinkFilter **ppSink) +{ + CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder2(iface); + + FIXME("(%p/%p)->(%s, %s, %p, %p) Stub!\n", This, iface, + debugstr_guid(pType), debugstr_w(lpstrFile), ppf, ppSink); + + return E_NOTIMPL; +} + +static HRESULT WINAPI +fnCaptureGraphBuilder2_FindInterface(ICaptureGraphBuilder2 * iface, + const GUID *pCategory, + const GUID *pType, + IBaseFilter *pf, + REFIID riid, + void **ppint) +{ + CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder2(iface); + + FIXME("(%p/%p)->(%s, %s, %p, %s, %p) - workaround stub!\n", This, iface, + debugstr_guid(pCategory), debugstr_guid(pType), + pf, debugstr_guid(riid), ppint); + + return IBaseFilter_QueryInterface(pf, riid, ppint); + /* Looks for the specified interface on the filter, upstream and + * downstream from the filter, and, optionally, only on the output + * pin of the given category. + */ +} + +static HRESULT match_smart_tee_pin(CaptureGraphImpl *This, + const GUID *pCategory, + const GUID *pType, + IUnknown *pSource, + IPin **source_out) +{ + static const WCHAR inputW[] = {'I','n','p','u','t',0}; + static const WCHAR captureW[] = {'C','a','p','t','u','r','e',0}; + static const WCHAR previewW[] = {'P','r','e','v','i','e','w',0}; + IPin *capture = NULL; + IPin *preview = NULL; + IPin *peer = NULL; + IBaseFilter *smartTee = NULL; + BOOL needSmartTee = FALSE; + HRESULT hr; + + TRACE("(%p, %s, %s, %p, %p)\n", This, debugstr_guid(pCategory), debugstr_guid(pType), pSource, source_out); + hr = ICaptureGraphBuilder2_FindPin(&This->ICaptureGraphBuilder2_iface, pSource, + PINDIR_OUTPUT, &PIN_CATEGORY_CAPTURE, pType, FALSE, 0, &capture); + if (SUCCEEDED(hr)) { + hr = ICaptureGraphBuilder2_FindPin(&This->ICaptureGraphBuilder2_iface, pSource, + PINDIR_OUTPUT, &PIN_CATEGORY_PREVIEW, pType, FALSE, 0, &preview); + if (FAILED(hr)) + needSmartTee = TRUE; + } else { + hr = E_INVALIDARG; + goto end; + } + if (!needSmartTee) { + if (IsEqualIID(pCategory, &PIN_CATEGORY_CAPTURE)) { + hr = IPin_ConnectedTo(capture, &peer); + if (hr == VFW_E_NOT_CONNECTED) { + *source_out = capture; + IPin_AddRef(*source_out); + hr = S_OK; + } else + hr = E_INVALIDARG; + } else { + hr = IPin_ConnectedTo(preview, &peer); + if (hr == VFW_E_NOT_CONNECTED) { + *source_out = preview; + IPin_AddRef(*source_out); + hr = S_OK; + } else + hr = E_INVALIDARG; + } + goto end; + } + hr = IPin_ConnectedTo(capture, &peer); + if (SUCCEEDED(hr)) { + PIN_INFO pinInfo; + GUID classID; + hr = IPin_QueryPinInfo(peer, &pinInfo); + if (SUCCEEDED(hr)) { + hr = IBaseFilter_GetClassID(pinInfo.pFilter, &classID); + if (SUCCEEDED(hr)) { + if (IsEqualIID(&classID, &CLSID_SmartTee)) { + smartTee = pinInfo.pFilter; + IBaseFilter_AddRef(smartTee); + } + } + IBaseFilter_Release(pinInfo.pFilter); + } + if (!smartTee) { + hr = E_INVALIDARG; + goto end; + } + } else if (hr == VFW_E_NOT_CONNECTED) { + hr = CoCreateInstance(&CLSID_SmartTee, NULL, CLSCTX_INPROC_SERVER, + &IID_IBaseFilter, (LPVOID*)&smartTee); + if (SUCCEEDED(hr)) { + hr = IGraphBuilder_AddFilter(This->mygraph, smartTee, NULL); + if (SUCCEEDED(hr)) { + IPin *smartTeeInput = NULL; + hr = IBaseFilter_FindPin(smartTee, inputW, &smartTeeInput); + if (SUCCEEDED(hr)) { + hr = IGraphBuilder_ConnectDirect(This->mygraph, capture, smartTeeInput, NULL); + IPin_Release(smartTeeInput); + } + } + } + if (FAILED(hr)) { + TRACE("adding SmartTee failed with hr=0x%08x\n", hr); + hr = E_INVALIDARG; + goto end; + } + } else { + hr = E_INVALIDARG; + goto end; + } + if (IsEqualIID(pCategory, &PIN_CATEGORY_CAPTURE)) + hr = IBaseFilter_FindPin(smartTee, captureW, source_out); + else { + hr = IBaseFilter_FindPin(smartTee, previewW, source_out); + if (SUCCEEDED(hr)) + hr = VFW_S_NOPREVIEWPIN; + } + +end: + if (capture) + IPin_Release(capture); + if (preview) + IPin_Release(preview); + if (peer) + IPin_Release(peer); + if (smartTee) + IBaseFilter_Release(smartTee); + TRACE("for %s returning hr=0x%08x, *source_out=%p\n", IsEqualIID(pCategory, &PIN_CATEGORY_CAPTURE) ? "capture" : "preview", hr, source_out ? *source_out : 0); + return hr; +} + +static HRESULT find_unconnected_pin(CaptureGraphImpl *This, + const GUID *pCategory, const GUID *pType, IUnknown *pSource, IPin **out_pin) +{ + int index = 0; + IPin *source_out; + HRESULT hr; + BOOL usedSmartTeePreviewPin = FALSE; + + /* depth-first search the graph for the first unconnected pin that matches + * the given category and type */ + for(;;){ + IPin *nextpin; + + if (pCategory && (IsEqualIID(pCategory, &PIN_CATEGORY_CAPTURE) || IsEqualIID(pCategory, &PIN_CATEGORY_PREVIEW))){ + IBaseFilter *sourceFilter = NULL; + hr = IUnknown_QueryInterface(pSource, &IID_IBaseFilter, (void**)&sourceFilter); + if (SUCCEEDED(hr)) { + hr = match_smart_tee_pin(This, pCategory, pType, pSource, &source_out); + if (hr == VFW_S_NOPREVIEWPIN) + usedSmartTeePreviewPin = TRUE; + IBaseFilter_Release(sourceFilter); + } else { + hr = ICaptureGraphBuilder2_FindPin(&This->ICaptureGraphBuilder2_iface, pSource, PINDIR_OUTPUT, pCategory, pType, FALSE, index, &source_out); + } + if (FAILED(hr)) + return E_INVALIDARG; + } else { + hr = ICaptureGraphBuilder2_FindPin(&This->ICaptureGraphBuilder2_iface, pSource, PINDIR_OUTPUT, pCategory, pType, FALSE, index, &source_out); + if (FAILED(hr)) + return E_INVALIDARG; + } + + hr = IPin_ConnectedTo(source_out, &nextpin); + if(SUCCEEDED(hr)){ + PIN_INFO info; + + IPin_Release(source_out); + + hr = IPin_QueryPinInfo(nextpin, &info); + if(FAILED(hr) || !info.pFilter){ + WARN("QueryPinInfo failed: %08x\n", hr); + return hr; + } + + hr = find_unconnected_pin(This, pCategory, pType, (IUnknown*)info.pFilter, out_pin); + + IBaseFilter_Release(info.pFilter); + + if(SUCCEEDED(hr)) + return hr; + }else{ + *out_pin = source_out; + if(usedSmartTeePreviewPin) + return VFW_S_NOPREVIEWPIN; + return S_OK; + } + + index++; + } +} + +static HRESULT WINAPI +fnCaptureGraphBuilder2_RenderStream(ICaptureGraphBuilder2 * iface, + const GUID *pCategory, + const GUID *pType, + IUnknown *pSource, + IBaseFilter *pfCompressor, + IBaseFilter *pfRenderer) +{ + CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder2(iface); + IPin *source_out = NULL, *renderer_in; + BOOL rendererNeedsRelease = FALSE; + HRESULT hr, return_hr = S_OK; + + FIXME("(%p/%p)->(%s, %s, %p, %p, %p) semi-stub!\n", This, iface, + debugstr_guid(pCategory), debugstr_guid(pType), + pSource, pfCompressor, pfRenderer); + + if (!This->mygraph) + { + FIXME("Need a capture graph\n"); + return E_UNEXPECTED; + } + + if (pCategory && IsEqualIID(pCategory, &PIN_CATEGORY_VBI)) { + FIXME("Tee/Sink-to-Sink filter not supported\n"); + return E_NOTIMPL; + } + + hr = find_unconnected_pin(This, pCategory, pType, pSource, &source_out); + if (FAILED(hr)) + return hr; + return_hr = hr; + + if (!pfRenderer) + { + IEnumMediaTypes *enumMedia = NULL; + hr = IPin_EnumMediaTypes(source_out, &enumMedia); + if (SUCCEEDED(hr)) { + AM_MEDIA_TYPE *mediaType; + hr = IEnumMediaTypes_Next(enumMedia, 1, &mediaType, NULL); + if (SUCCEEDED(hr)) { + if (IsEqualIID(&mediaType->majortype, &MEDIATYPE_Video)) { + hr = CoCreateInstance(&CLSID_VideoRenderer, NULL, CLSCTX_INPROC_SERVER, + &IID_IBaseFilter, (void**)&pfRenderer); + } else if (IsEqualIID(&mediaType->majortype, &MEDIATYPE_Audio)) { + hr = CoCreateInstance(&CLSID_DSoundRender, NULL, CLSCTX_INPROC_SERVER, + &IID_IBaseFilter, (void**)&pfRenderer); + } else { + FIXME("cannot automatically load renderer for majortype %s\n", debugstr_guid(&mediaType->majortype)); + hr = E_FAIL; + } + if (SUCCEEDED(hr)) { + rendererNeedsRelease = TRUE; + hr = IGraphBuilder_AddFilter(This->mygraph, pfRenderer, NULL); + } + DeleteMediaType(mediaType); + } + IEnumMediaTypes_Release(enumMedia); + } + if (FAILED(hr)) { + if (rendererNeedsRelease) + IBaseFilter_Release(pfRenderer); + IPin_Release(source_out); + return hr; + } + } + + hr = ICaptureGraphBuilder2_FindPin(iface, (IUnknown*)pfRenderer, PINDIR_INPUT, NULL, NULL, TRUE, 0, &renderer_in); + if (FAILED(hr)) + { + if (rendererNeedsRelease) + IBaseFilter_Release(pfRenderer); + IPin_Release(source_out); + return hr; + } + + if (!pfCompressor) + hr = IGraphBuilder_Connect(This->mygraph, source_out, renderer_in); + else + { + IPin *compressor_in, *compressor_out; + + hr = ICaptureGraphBuilder2_FindPin(iface, (IUnknown*)pfCompressor, + PINDIR_INPUT, NULL, NULL, TRUE, 0, &compressor_in); + if (SUCCEEDED(hr)) + { + hr = IGraphBuilder_Connect(This->mygraph, source_out, compressor_in); + IPin_Release(compressor_in); + } + + if (SUCCEEDED(hr)) + { + hr = ICaptureGraphBuilder2_FindPin(iface, (IUnknown*)pfCompressor, + PINDIR_OUTPUT, NULL, NULL, TRUE, 0, &compressor_out); + if (SUCCEEDED(hr)) + { + hr = IGraphBuilder_Connect(This->mygraph, compressor_out, renderer_in); + IPin_Release(compressor_out); + } + } + } + + IPin_Release(source_out); + IPin_Release(renderer_in); + if (rendererNeedsRelease) + IBaseFilter_Release(pfRenderer); + if (SUCCEEDED(hr)) + return return_hr; + return hr; +} + +static HRESULT WINAPI +fnCaptureGraphBuilder2_ControlStream(ICaptureGraphBuilder2 * iface, + const GUID *pCategory, + const GUID *pType, + IBaseFilter *pFilter, + REFERENCE_TIME *pstart, + REFERENCE_TIME *pstop, + WORD wStartCookie, + WORD wStopCookie) +{ + CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder2(iface); + + FIXME("(%p/%p)->(%s, %s, %p, %p, %p, %i, %i) Stub!\n", This, iface, + debugstr_guid(pCategory), debugstr_guid(pType), + pFilter, pstart, pstop, wStartCookie, wStopCookie); + + return E_NOTIMPL; +} + +static HRESULT WINAPI +fnCaptureGraphBuilder2_AllocCapFile(ICaptureGraphBuilder2 * iface, + LPCOLESTR lpwstr, + DWORDLONG dwlSize) +{ + CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder2(iface); + + FIXME("(%p/%p)->(%s, 0x%s) Stub!\n", This, iface, + debugstr_w(lpwstr), wine_dbgstr_longlong(dwlSize)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI +fnCaptureGraphBuilder2_CopyCaptureFile(ICaptureGraphBuilder2 * iface, + LPOLESTR lpwstrOld, + LPOLESTR lpwstrNew, + int fAllowEscAbort, + IAMCopyCaptureFileProgress *pCallback) +{ + CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder2(iface); + + FIXME("(%p/%p)->(%s, %s, %i, %p) Stub!\n", This, iface, + debugstr_w(lpwstrOld), debugstr_w(lpwstrNew), + fAllowEscAbort, pCallback); + + return E_NOTIMPL; +} + +static HRESULT pin_matches(IPin *pin, PIN_DIRECTION direction, const GUID *cat, const GUID *type, BOOL unconnected) +{ + IPin *partner; + PIN_DIRECTION pindir; + HRESULT hr; + + hr = IPin_QueryDirection(pin, &pindir); + + if (unconnected && IPin_ConnectedTo(pin, &partner) == S_OK && partner!=NULL) + { + IPin_Release(partner); + TRACE("No match, %p already connected to %p\n", pin, partner); + return FAILED(hr) ? hr : S_FALSE; + } + + if (FAILED(hr)) + return hr; + if (SUCCEEDED(hr) && pindir != direction) + return S_FALSE; + + if (cat) + { + IKsPropertySet *props; + GUID category; + DWORD fetched; + + hr = IPin_QueryInterface(pin, &IID_IKsPropertySet, (void**)&props); + if (FAILED(hr)) + return S_FALSE; + + hr = IKsPropertySet_Get(props, &ROPSETID_Pin, 0, NULL, + 0, &category, sizeof(category), &fetched); + IKsPropertySet_Release(props); + if (FAILED(hr) || !IsEqualIID(&category, cat)) + return S_FALSE; + } + + if (type) + { + IEnumMediaTypes *types; + AM_MEDIA_TYPE *media_type; + ULONG fetched; + + hr = IPin_EnumMediaTypes(pin, &types); + if (FAILED(hr)) + return S_FALSE; + + IEnumMediaTypes_Reset(types); + while (1) { + if (IEnumMediaTypes_Next(types, 1, &media_type, &fetched) != S_OK || fetched != 1) + { + IEnumMediaTypes_Release(types); + return S_FALSE; + } + + if (IsEqualIID(&media_type->majortype, type)) + { + DeleteMediaType(media_type); + break; + } + DeleteMediaType(media_type); + } + IEnumMediaTypes_Release(types); + } + + TRACE("Pin matched\n"); + return S_OK; +} + +static HRESULT WINAPI +fnCaptureGraphBuilder2_FindPin(ICaptureGraphBuilder2 * iface, + IUnknown *pSource, + PIN_DIRECTION pindir, + const GUID *pCategory, + const GUID *pType, + BOOL fUnconnected, + INT num, + IPin **ppPin) +{ + HRESULT hr; + IEnumPins *enumpins = NULL; + IPin *pin; + CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder2(iface); + + TRACE("(%p/%p)->(%p, %x, %s, %s, %d, %i, %p)\n", This, iface, + pSource, pindir, debugstr_guid(pCategory), debugstr_guid(pType), + fUnconnected, num, ppPin); + + pin = NULL; + + hr = IUnknown_QueryInterface(pSource, &IID_IPin, (void**)&pin); + if (hr == E_NOINTERFACE) + { + IBaseFilter *filter = NULL; + int numcurrent = 0; + + hr = IUnknown_QueryInterface(pSource, &IID_IBaseFilter, (void**)&filter); + if (hr == E_NOINTERFACE) + { + WARN("Input not filter or pin?!\n"); + return E_NOINTERFACE; + } + + hr = IBaseFilter_EnumPins(filter, &enumpins); + if (FAILED(hr)) + { + WARN("Could not enumerate\n"); + IBaseFilter_Release(filter); + return hr; + } + + while (1) + { + ULONG fetched; + + hr = IEnumPins_Next(enumpins, 1, &pin, &fetched); + if (hr == VFW_E_ENUM_OUT_OF_SYNC) + { + numcurrent = 0; + IEnumPins_Reset(enumpins); + pin = NULL; + continue; + } + if (hr != S_OK) + break; + if (fetched != 1) + { + hr = E_FAIL; + break; + } + + TRACE("Testing match\n"); + hr = pin_matches(pin, pindir, pCategory, pType, fUnconnected); + if (hr == S_OK && numcurrent++ == num) + break; + IPin_Release(pin); + pin = NULL; + if (FAILED(hr)) + break; + } + IEnumPins_Release(enumpins); + IBaseFilter_Release(filter); + + if (hr != S_OK) + { + WARN("Could not find %s pin # %d\n", (pindir == PINDIR_OUTPUT ? "output" : "input"), numcurrent); + return E_FAIL; + } + } + else if (pin_matches(pin, pindir, pCategory, pType, fUnconnected) != S_OK) + { + IPin_Release(pin); + return E_FAIL; + } + + *ppPin = pin; + return S_OK; +} + +static const ICaptureGraphBuilder2Vtbl builder2_Vtbl = +{ + fnCaptureGraphBuilder2_QueryInterface, + fnCaptureGraphBuilder2_AddRef, + fnCaptureGraphBuilder2_Release, + fnCaptureGraphBuilder2_SetFilterGraph, + fnCaptureGraphBuilder2_GetFilterGraph, + fnCaptureGraphBuilder2_SetOutputFileName, + fnCaptureGraphBuilder2_FindInterface, + fnCaptureGraphBuilder2_RenderStream, + fnCaptureGraphBuilder2_ControlStream, + fnCaptureGraphBuilder2_AllocCapFile, + fnCaptureGraphBuilder2_CopyCaptureFile, + fnCaptureGraphBuilder2_FindPin +}; + + +static HRESULT WINAPI +fnCaptureGraphBuilder_QueryInterface(ICaptureGraphBuilder * iface, + REFIID riid, LPVOID * ppv) +{ + CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder(iface); + TRACE("%p --> Forwarding to v2 (%p)\n", iface, This); + return ICaptureGraphBuilder2_QueryInterface(&This->ICaptureGraphBuilder2_iface, riid, ppv); +} + +static ULONG WINAPI +fnCaptureGraphBuilder_AddRef(ICaptureGraphBuilder * iface) +{ + CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder(iface); + TRACE("%p --> Forwarding to v2 (%p)\n", iface, This); + return ICaptureGraphBuilder2_AddRef(&This->ICaptureGraphBuilder2_iface); +} + +static ULONG WINAPI +fnCaptureGraphBuilder_Release(ICaptureGraphBuilder * iface) +{ + CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder(iface); + TRACE("%p --> Forwarding to v2 (%p)\n", iface, This); + return ICaptureGraphBuilder2_Release(&This->ICaptureGraphBuilder2_iface); +} + +static HRESULT WINAPI +fnCaptureGraphBuilder_SetFiltergraph(ICaptureGraphBuilder * iface, + IGraphBuilder *pfg) +{ + CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder(iface); + TRACE("%p --> Forwarding to v2 (%p)\n", iface, This); + return ICaptureGraphBuilder2_SetFiltergraph(&This->ICaptureGraphBuilder2_iface, pfg); +} + +static HRESULT WINAPI +fnCaptureGraphBuilder_GetFiltergraph(ICaptureGraphBuilder * iface, + IGraphBuilder **pfg) +{ + CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder(iface); + TRACE("%p --> Forwarding to v2 (%p)\n", iface, This); + return ICaptureGraphBuilder2_GetFiltergraph(&This->ICaptureGraphBuilder2_iface, pfg); +} + +static HRESULT WINAPI +fnCaptureGraphBuilder_SetOutputFileName(ICaptureGraphBuilder * iface, + const GUID *pType, LPCOLESTR lpstrFile, + IBaseFilter **ppf, IFileSinkFilter **ppSink) +{ + CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder(iface); + TRACE("%p --> Forwarding to v2 (%p)\n", iface, This); + return ICaptureGraphBuilder2_SetOutputFileName(&This->ICaptureGraphBuilder2_iface, pType, + lpstrFile, ppf, ppSink); +} + +static HRESULT WINAPI +fnCaptureGraphBuilder_FindInterface(ICaptureGraphBuilder * iface, + const GUID *pCategory, IBaseFilter *pf, + REFIID riid, void **ppint) +{ + CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder(iface); + TRACE("%p --> Forwarding to v2 (%p)\n", iface, This); + return ICaptureGraphBuilder2_FindInterface(&This->ICaptureGraphBuilder2_iface, pCategory, NULL, + pf, riid, ppint); +} + +static HRESULT WINAPI +fnCaptureGraphBuilder_RenderStream(ICaptureGraphBuilder * iface, + const GUID *pCategory, IUnknown *pSource, + IBaseFilter *pfCompressor, IBaseFilter *pfRenderer) +{ + CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder(iface); + TRACE("%p --> Forwarding to v2 (%p)\n", iface, This); + return ICaptureGraphBuilder2_RenderStream(&This->ICaptureGraphBuilder2_iface, pCategory, NULL, + pSource, pfCompressor, pfRenderer); +} + +static HRESULT WINAPI +fnCaptureGraphBuilder_ControlStream(ICaptureGraphBuilder * iface, + const GUID *pCategory, IBaseFilter *pFilter, + REFERENCE_TIME *pstart, REFERENCE_TIME *pstop, + WORD wStartCookie, WORD wStopCookie) +{ + CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder(iface); + TRACE("%p --> Forwarding to v2 (%p)\n", iface, This); + return ICaptureGraphBuilder2_ControlStream(&This->ICaptureGraphBuilder2_iface, pCategory, NULL, + pFilter, pstart, pstop, wStartCookie, wStopCookie); +} + +static HRESULT WINAPI +fnCaptureGraphBuilder_AllocCapFile(ICaptureGraphBuilder * iface, + LPCOLESTR lpstr, DWORDLONG dwlSize) +{ + CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder(iface); + TRACE("%p --> Forwarding to v2 (%p)\n", iface, This); + return ICaptureGraphBuilder2_AllocCapFile(&This->ICaptureGraphBuilder2_iface, lpstr, dwlSize); +} + +static HRESULT WINAPI +fnCaptureGraphBuilder_CopyCaptureFile(ICaptureGraphBuilder * iface, + LPOLESTR lpwstrOld, LPOLESTR lpwstrNew, + int fAllowEscAbort, + IAMCopyCaptureFileProgress *pCallback) +{ + CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder(iface); + TRACE("%p --> Forwarding to v2 (%p)\n", iface, This); + return ICaptureGraphBuilder2_CopyCaptureFile(&This->ICaptureGraphBuilder2_iface, lpwstrOld, + lpwstrNew, fAllowEscAbort, pCallback); +} + +static const ICaptureGraphBuilderVtbl builder_Vtbl = +{ + fnCaptureGraphBuilder_QueryInterface, + fnCaptureGraphBuilder_AddRef, + fnCaptureGraphBuilder_Release, + fnCaptureGraphBuilder_SetFiltergraph, + fnCaptureGraphBuilder_GetFiltergraph, + fnCaptureGraphBuilder_SetOutputFileName, + fnCaptureGraphBuilder_FindInterface, + fnCaptureGraphBuilder_RenderStream, + fnCaptureGraphBuilder_ControlStream, + fnCaptureGraphBuilder_AllocCapFile, + fnCaptureGraphBuilder_CopyCaptureFile +}; diff --git a/dll/directx/wine/qcap/enummedia.c b/dll/directx/wine/qcap/enummedia.c new file mode 100644 index 00000000000..45552e16a46 --- /dev/null +++ b/dll/directx/wine/qcap/enummedia.c @@ -0,0 +1,44 @@ +/* + * Implementation of IEnumMediaTypes Interface + * + * Copyright 2003 Robert Shearman + * + * 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); + +void dump_AM_MEDIA_TYPE(const AM_MEDIA_TYPE * pmt) +{ + if (!pmt) + return; + TRACE("\t%s\n\t%s\n\t...\n\t%s\n", debugstr_guid(&pmt->majortype), + debugstr_guid(&pmt->subtype), debugstr_guid(&pmt->formattype)); +} diff --git a/dll/directx/wine/qcap/qcap.spec b/dll/directx/wine/qcap/qcap.spec new file mode 100644 index 00000000000..b16365d0c9f --- /dev/null +++ b/dll/directx/wine/qcap/qcap.spec @@ -0,0 +1,4 @@ +@ stdcall -private DllCanUnloadNow() +@ stdcall -private DllGetClassObject(ptr ptr ptr) +@ stdcall -private DllRegisterServer() +@ stdcall -private DllUnregisterServer() diff --git a/dll/directx/wine/qcap/qcap_main.c b/dll/directx/wine/qcap/qcap_main.c new file mode 100644 index 00000000000..bc0b903d0b1 --- /dev/null +++ b/dll/directx/wine/qcap/qcap_main.c @@ -0,0 +1,198 @@ +/* + * Qcap implementation, dllentry points + * + * Copyright (C) 2003 Dominik Strasser + * Copyright (C) 2005 Rolf Kalbermatter + * + * 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 "config.h" + +#include <assert.h> +#include <stdio.h> +#include <stdarg.h> + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winerror.h" +#include "objbase.h" +#include "uuids.h" +#include "strmif.h" + +#include "qcap_main.h" + +#include "wine/unicode.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(qcap); + +static LONG objects_ref = 0; + +static const WCHAR wAudioCaptureFilter[] = +{'A','u','d','i','o',' ','C','a','p','t','u','r','e',' ','F','i','l','t','e','r',0}; +static const WCHAR wAVICompressor[] = +{'A','V','I',' ','C','o','m','p','r','e','s','s','o','r',0}; +static const WCHAR wVFWCaptFilter[] = +{'V','F','W',' ','C','a','p','t','u','r','e',' ','F','i','l','t','e','r',0}; +static const WCHAR wVFWCaptFilterProp[] = +{'V','F','W',' ','C','a','p','t','u','r','e',' ','F','i','l','t','e','r',' ', + 'P','r','o','p','e','r','t','y',' ','P','a','g','e',0}; +static const WCHAR wAVIMux[] = +{'A','V','I',' ','m','u','x',0}; +static const WCHAR wAVIMuxPropPage[] = +{'A','V','I',' ','m','u','x',' ','P','r','o','p','e','r','t','y',' ','P','a','g','e',0}; +static const WCHAR wAVIMuxPropPage1[] = +{'A','V','I',' ','m','u','x',' ','P','r','o','p','e','r','t','y',' ','P','a','g','e','1',0}; +static const WCHAR wFileWriter[] = +{'F','i','l','e',' ','W','r','i','t','e','r',0}; +static const WCHAR wCaptGraphBuilder[] = +{'C','a','p','t','u','r','e',' ','G','r','a','p','h',' ','B','u','i','l','d','e','r',0}; +static const WCHAR wCaptGraphBuilder2[] = +{'C','a','p','t','u','r','e',' ','G','r','a','p','h',' ','B','u','i','l','d','e','r','2',0}; +static const WCHAR wInfPinTeeFilter[] = +{'I','n','f','i','n','i','t','e',' ','P','i','n',' ','T','e','e',' ','F','i', + 'l','t','e','r',0}; +static const WCHAR wSmartTeeFilter[] = +{'S','m','a','r','t',' ','T','e','e',' ','F','i','l','t','e','r',0}; +static const WCHAR wAudioInMixerProp[] = +{'A','u','d','i','o','I','n','p','u','t','M','i','x','e','r',' ','P','r','o', + 'p','e','r','t','y',' ','P','a','g','e',0}; + +FactoryTemplate const g_Templates[] = { + { + wAudioCaptureFilter, + &CLSID_AudioRecord, + QCAP_createAudioCaptureFilter, + NULL + },{ + wAVICompressor, + &CLSID_AVICo, + QCAP_createAVICompressor, + NULL + },{ + wVFWCaptFilter, + &CLSID_VfwCapture, + QCAP_createVFWCaptureFilter, + NULL + },{ + wVFWCaptFilterProp, + &CLSID_CaptureProperties, + NULL, /* FIXME: Implement QCAP_createVFWCaptureFilterPropertyPage */ + NULL + },{ + wAVIMux, + &CLSID_AviDest, + QCAP_createAVIMux, + NULL + },{ + wAVIMuxPropPage, + &CLSID_AviMuxProptyPage, + NULL, /* FIXME: Implement QCAP_createAVIMuxPropertyPage */ + NULL + },{ + wAVIMuxPropPage1, + &CLSID_AviMuxProptyPage1, + NULL, /* FIXME: Implement QCAP_createAVIMuxPropertyPage1 */ + NULL + },{ + wFileWriter, + &CLSID_FileWriter, + NULL, /* FIXME: Implement QCAP_createFileWriter */ + NULL + },{ + wCaptGraphBuilder, + &CLSID_CaptureGraphBuilder, + QCAP_createCaptureGraphBuilder2, + NULL + },{ + wCaptGraphBuilder2, + &CLSID_CaptureGraphBuilder2, + QCAP_createCaptureGraphBuilder2, + NULL + },{ + wInfPinTeeFilter, + &CLSID_InfTee, + NULL, /* FIXME: Implement QCAP_createInfinitePinTeeFilter */ + NULL + },{ + wSmartTeeFilter, + &CLSID_SmartTee, + QCAP_createSmartTeeFilter, + NULL + },{ + wAudioInMixerProp, + &CLSID_AudioInputMixerProperties, + NULL, /* FIXME: Implement QCAP_createAudioInputMixerPropertyPage */ + NULL + } +}; + +int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]); + +/*********************************************************************** + * Dll EntryPoint (QCAP.@) + */ +BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv) +{ + return STRMBASE_DllMain(hInstDLL,fdwReason,lpv); +} + +/*********************************************************************** + * DllGetClassObject + */ +HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) +{ + return STRMBASE_DllGetClassObject( rclsid, riid, ppv ); +} + +/*********************************************************************** + * DllRegisterServer (QCAP.@) + */ +HRESULT WINAPI DllRegisterServer(void) +{ + TRACE("()\n"); + return AMovieDllRegisterServer2(TRUE); +} + +/*********************************************************************** + * DllUnregisterServer (QCAP.@) + */ +HRESULT WINAPI DllUnregisterServer(void) +{ + TRACE("\n"); + return AMovieDllRegisterServer2(FALSE); +} + +/*********************************************************************** + * DllCanUnloadNow (QCAP.@) + */ +HRESULT WINAPI DllCanUnloadNow(void) +{ + TRACE("\n"); + + if (STRMBASE_DllCanUnloadNow() == S_OK && objects_ref == 0) + return S_OK; + return S_FALSE; +} + +DWORD ObjectRefCount(BOOL increment) +{ + if (increment) + return InterlockedIncrement(&objects_ref); + return InterlockedDecrement(&objects_ref); +} diff --git a/dll/directx/wine/qcap/qcap_main.h b/dll/directx/wine/qcap/qcap_main.h new file mode 100644 index 00000000000..69d94bfff69 --- /dev/null +++ b/dll/directx/wine/qcap/qcap_main.h @@ -0,0 +1,62 @@ +/* + * Qcap main header file + * + * Copyright (C) 2005 Rolf Kalbermatter + * + * 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 + */ +#ifndef _QCAP_MAIN_H_DEFINED +#define _QCAP_MAIN_H_DEFINED + +#include "wine/strmbase.h" + +extern DWORD ObjectRefCount(BOOL increment) DECLSPEC_HIDDEN; + +extern IUnknown * WINAPI QCAP_createAudioCaptureFilter(IUnknown *pUnkOuter, HRESULT *phr) DECLSPEC_HIDDEN; +extern IUnknown * WINAPI QCAP_createAVICompressor(IUnknown *pUnkOuter, HRESULT *phr) DECLSPEC_HIDDEN; +extern IUnknown * WINAPI QCAP_createVFWCaptureFilter(IUnknown *pUnkOuter, HRESULT *phr) DECLSPEC_HIDDEN; +extern IUnknown * WINAPI QCAP_createVFWCaptureFilterPropertyPage(IUnknown *pUnkOuter, HRESULT *phr) DECLSPEC_HIDDEN; +extern IUnknown * WINAPI QCAP_createAVICompressor(IUnknown*,HRESULT*) DECLSPEC_HIDDEN; +extern IUnknown * WINAPI QCAP_createAVIMux(IUnknown *pUnkOuter, HRESULT *phr) DECLSPEC_HIDDEN; +extern IUnknown * WINAPI QCAP_createAVIMuxPropertyPage(IUnknown *pUnkOuter, HRESULT *phr) DECLSPEC_HIDDEN; +extern IUnknown * WINAPI QCAP_createAVIMuxPropertyPage1(IUnknown *pUnkOuter, HRESULT *phr) DECLSPEC_HIDDEN; +extern IUnknown * WINAPI QCAP_createFileWriter(IUnknown *pUnkOuter, HRESULT *phr) DECLSPEC_HIDDEN; +extern IUnknown * WINAPI QCAP_createCaptureGraphBuilder2(IUnknown *pUnkOuter, HRESULT *phr) DECLSPEC_HIDDEN; +extern IUnknown * WINAPI QCAP_createInfinitePinTeeFilter(IUnknown *pUnkOuter, HRESULT *phr) DECLSPEC_HIDDEN; +extern IUnknown * WINAPI QCAP_createSmartTeeFilter(IUnknown *pUnkOuter, HRESULT *phr) DECLSPEC_HIDDEN; +extern IUnknown * WINAPI QCAP_createAudioInputMixerPropertyPage(IUnknown *pUnkOuter, HRESULT *phr) DECLSPEC_HIDDEN; + +void dump_AM_MEDIA_TYPE(const AM_MEDIA_TYPE * pmt) DECLSPEC_HIDDEN; + +enum YUV_Format { + /* Last 2 numbers give the skip info, the smaller they are the better + * Planar: + * HSKIP : VSKIP */ + YUVP_421, /* 2 : 1 */ + YUVP_422, /* 2 : 2 */ + YUVP_441, /* 4 : 1 */ + YUVP_444, /* 4 : 4 */ + ENDPLANAR, /* No format, just last planar item so we can check on it */ + + /* Non-planar */ + YUYV, /* Order: YUYV (Guess why it's named like that) */ + UYVY, /* Order: UYVY (Looks like someone got bored and swapped the Y's) */ + UYYVYY, /* YUV411 linux style, perhaps YUV420 is YYUYYV? */ +}; + +void YUV_Init(void) DECLSPEC_HIDDEN; +void YUV_To_RGB24(enum YUV_Format format, unsigned char *target, const unsigned char *source, int width, int height) DECLSPEC_HIDDEN; + +#endif /* _QCAP_MAIN_H_DEFINED */ diff --git a/dll/directx/wine/qcap/smartteefilter.c b/dll/directx/wine/qcap/smartteefilter.c new file mode 100644 index 00000000000..27aee4705ac --- /dev/null +++ b/dll/directx/wine/qcap/smartteefilter.c @@ -0,0 +1,697 @@ +/* + * 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) ... 2597 lines suppressed ...