[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>
This commit is contained in:
Oleg Dubinskiy 2020-03-07 21:42:35 +02:00 committed by Hermès Bélusca-Maïto
parent e9f5438eea
commit 56aa5137e7
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0
17 changed files with 7452 additions and 0 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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;
}
}

View file

@ -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;
}

File diff suppressed because it is too large Load diff

View file

@ -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__ */

View file

@ -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, &AMPROPSETID_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
};

View file

@ -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));
}

View file

@ -0,0 +1,4 @@
@ stdcall -private DllCanUnloadNow()
@ stdcall -private DllGetClassObject(ptr ptr ptr)
@ stdcall -private DllRegisterServer()
@ stdcall -private DllUnregisterServer()

View file

@ -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);
}

View file

@ -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 */

View file

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

978
dll/directx/wine/qcap/v4l.c Normal file
View file

@ -0,0 +1,978 @@
/*
* DirectShow capture services (QCAP.DLL)
*
* Copyright 2005 Maarten Lankhorst
*
* This file contains the part of the vfw capture interface that
* does the actual Video4Linux(1/2) stuff required for capturing
* and setting/getting media format..
*
* 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 "wine/port.h"
#define COBJMACROS
#include <stdarg.h>
#include <stdio.h>
#include <fcntl.h>
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#include <errno.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_ASM_TYPES_H
#include <asm/types.h>
#endif
#ifdef HAVE_LIBV4L1_H
#include <libv4l1.h>
#endif
#ifdef HAVE_LINUX_VIDEODEV_H
#include <linux/videodev.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "windef.h"
#include "winbase.h"
#include "wtypes.h"
#include "wingdi.h"
#include "winuser.h"
#include "dshow.h"
#include "vfwmsgs.h"
#include "amvideo.h"
#include "wine/debug.h"
#include "wine/library.h"
#include "capture.h"
#include "qcap_main.h"
WINE_DEFAULT_DEBUG_CHANNEL(qcap_v4l);
#ifdef VIDIOCMCAPTURE
static typeof(open) *video_open = open;
static typeof(close) *video_close = close;
static typeof(ioctl) *video_ioctl = ioctl;
static typeof(read) *video_read = read;
static typeof(mmap) *video_mmap = mmap;
static typeof(munmap) *video_munmap = munmap;
static void video_init(void)
{
#ifdef SONAME_LIBV4L1
static void *video_lib;
if (video_lib)
return;
video_lib = wine_dlopen(SONAME_LIBV4L1, RTLD_NOW, NULL, 0);
if (!video_lib)
return;
video_open = wine_dlsym(video_lib, "v4l1_open", NULL, 0);
video_close = wine_dlsym(video_lib, "v4l1_close", NULL, 0);
video_ioctl = wine_dlsym(video_lib, "v4l1_ioctl", NULL, 0);
video_read = wine_dlsym(video_lib, "v4l1_read", NULL, 0);
video_mmap = wine_dlsym(video_lib, "v4l1_mmap", NULL, 0);
video_munmap = wine_dlsym(video_lib, "v4l1_munmap", NULL, 0);
#endif
}
typedef void (* Renderer)(const Capture *, LPBYTE bufferin, const BYTE *stream);
struct _Capture
{
UINT width, height, bitDepth, fps, outputwidth, outputheight;
BOOL swresize;
CRITICAL_SECTION CritSect;
IPin *pOut;
int fd, mmap;
BOOL iscommitted, stopped;
struct video_picture pict;
int dbrightness, dhue, dcolour, dcontrast;
/* mmap (V4l1) */
struct video_mmap *grab_buf;
struct video_mbuf gb_buffers;
unsigned char *pmap;
int buffers;
/* read (V4l1) */
int imagesize;
char * grab_data;
int curframe;
HANDLE thread;
Renderer renderer;
};
struct renderlist
{
int depth;
const char* name;
Renderer renderer;
};
static void renderer_RGB(const Capture *capBox, LPBYTE bufferin, const BYTE *stream);
static void renderer_YUV(const Capture *capBox, LPBYTE bufferin, const BYTE *stream);
static const struct renderlist renderlist_V4l[] = {
{ 0, "NULL renderer", NULL },
{ 8, "Gray scales", NULL }, /* 1, Don't support */
{ 0, "High 240 cube (BT848)", NULL }, /* 2, Don't support */
{ 16, "16 bit RGB (565)", NULL }, /* 3, Don't support */
{ 24, "24 bit RGB values", renderer_RGB }, /* 4, Supported, */
{ 32, "32 bit RGB values", renderer_RGB }, /* 5, Supported */
{ 16, "15 bit RGB (555)", NULL }, /* 6, Don't support */
{ 16, "YUV 422 (Not P)", renderer_YUV }, /* 7, Supported */
{ 16, "YUYV (Not P)", renderer_YUV }, /* 8, Supported */
{ 16, "UYVY (Not P)", renderer_YUV }, /* 9, Supported */
{ 16, "YUV 420 (Not P)", NULL }, /* 10, Not supported, if I had to guess it's YYUYYV */
{ 12, "YUV 411 (Not P)", renderer_YUV }, /* 11, Supported */
{ 0, "Raw capturing (BT848)", NULL }, /* 12, Don't support */
{ 16, "YUV 422 (Planar)", renderer_YUV }, /* 13, Supported */
{ 12, "YUV 411 (Planar)", renderer_YUV }, /* 14, Supported */
{ 12, "YUV 420 (Planar)", renderer_YUV }, /* 15, Supported */
{ 10, "YUV 410 (Planar)", renderer_YUV }, /* 16, Supported */
/* FIXME: add YUV420 support */
{ 0, NULL, NULL },
};
static const int fallback_V4l[] = { 4, 5, 7, 8, 9, 13, 15, 14, 16, 11, -1 };
/* Fallback: First try raw formats (Should try yuv first perhaps?), then yuv */
/* static const Capture defbox; */
static int xioctl(int fd, int request, void * arg)
{
int r;
do {
r = video_ioctl (fd, request, arg);
} while (-1 == r && EINTR == errno);
return r;
}
/* Prepare the capture buffers */
static HRESULT V4l_Prepare(Capture *capBox)
{
TRACE("%p: Preparing for %dx%d resolution\n", capBox, capBox->width, capBox->height);
/* Try mmap */
capBox->mmap = 0;
if (xioctl(capBox->fd, VIDIOCGMBUF, &capBox->gb_buffers) != -1 &&
capBox->gb_buffers.frames)
{
capBox->buffers = capBox->gb_buffers.frames;
if (capBox->gb_buffers.frames > 1)
capBox->buffers = 1;
TRACE("%p: Using %d/%d buffers\n", capBox,
capBox->buffers, capBox->gb_buffers.frames);
capBox->pmap = video_mmap( 0, capBox->gb_buffers.size, PROT_READ|PROT_WRITE,
MAP_SHARED, capBox->fd, 0 );
if (capBox->pmap != MAP_FAILED)
{
int i;
capBox->grab_buf = CoTaskMemAlloc(sizeof(struct video_mmap) * capBox->buffers);
if (!capBox->grab_buf)
{
video_munmap(capBox->pmap, capBox->gb_buffers.size);
return E_OUTOFMEMORY;
}
/* Setup mmap capture buffers. */
for (i = 0; i < capBox->buffers; i++)
{
capBox->grab_buf[i].format = capBox->pict.palette;
capBox->grab_buf[i].frame = i;
capBox->grab_buf[i].width = capBox->width;
capBox->grab_buf[i].height = capBox->height;
}
capBox->mmap = 1;
}
}
if (!capBox->mmap)
{
capBox->buffers = 1;
capBox->imagesize = renderlist_V4l[capBox->pict.palette].depth *
capBox->height * capBox->width / 8;
capBox->grab_data = CoTaskMemAlloc(capBox->imagesize);
if (!capBox->grab_data)
return E_OUTOFMEMORY;
}
TRACE("Using mmap: %d\n", capBox->mmap);
return S_OK;
}
static void V4l_Unprepare(Capture *capBox)
{
if (capBox->mmap)
{
for (capBox->curframe = 0; capBox->curframe < capBox->buffers; capBox->curframe++)
xioctl(capBox->fd, VIDIOCSYNC, &capBox->grab_buf[capBox->curframe]);
video_munmap(capBox->pmap, capBox->gb_buffers.size);
CoTaskMemFree(capBox->grab_buf);
}
else
CoTaskMemFree(capBox->grab_data);
}
HRESULT qcap_driver_destroy(Capture *capBox)
{
TRACE("%p\n", capBox);
if( capBox->fd != -1 )
video_close(capBox->fd);
capBox->CritSect.DebugInfo->Spare[0] = 0;
DeleteCriticalSection(&capBox->CritSect);
CoTaskMemFree(capBox);
return S_OK;
}
HRESULT qcap_driver_set_format(Capture *capBox, AM_MEDIA_TYPE * mT)
{
int newheight, newwidth;
struct video_window window;
VIDEOINFOHEADER *format;
TRACE("%p\n", capBox);
format = (VIDEOINFOHEADER *) mT->pbFormat;
if (format->bmiHeader.biBitCount != 24 ||
format->bmiHeader.biCompression != BI_RGB)
{
FIXME("unsupported media type %d %d\n", format->bmiHeader.biBitCount,
format->bmiHeader.biCompression );
return VFW_E_INVALIDMEDIATYPE;
}
newwidth = format->bmiHeader.biWidth;
newheight = format->bmiHeader.biHeight;
TRACE("%p -> (%p) - %d %d\n", capBox, mT, newwidth, newheight);
if (capBox->height == newheight && capBox->width == newwidth)
return S_OK;
if(-1 == xioctl(capBox->fd, VIDIOCGWIN, &window))
{
ERR("ioctl(VIDIOCGWIN) failed (%d)\n", errno);
return E_FAIL;
}
window.width = newwidth;
window.height = newheight;
if (xioctl(capBox->fd, VIDIOCSWIN, &window) == -1)
{
TRACE("using software resize: %dx%d -> %dx%d\n",
window.width, window.height, capBox->width, capBox->height);
capBox->swresize = TRUE;
}
else
{
capBox->height = window.height;
capBox->width = window.width;
capBox->swresize = FALSE;
}
capBox->outputwidth = window.width;
capBox->outputheight = window.height;
return S_OK;
}
HRESULT qcap_driver_get_format(const Capture *capBox, AM_MEDIA_TYPE ** mT)
{
VIDEOINFOHEADER *vi;
mT[0] = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
if (!mT[0])
return E_OUTOFMEMORY;
vi = CoTaskMemAlloc(sizeof(VIDEOINFOHEADER));
mT[0]->cbFormat = sizeof(VIDEOINFOHEADER);
if (!vi)
{
CoTaskMemFree(mT[0]);
mT[0] = NULL;
return E_OUTOFMEMORY;
}
mT[0]->majortype = MEDIATYPE_Video;
mT[0]->subtype = MEDIASUBTYPE_RGB24;
mT[0]->formattype = FORMAT_VideoInfo;
mT[0]->bFixedSizeSamples = TRUE;
mT[0]->bTemporalCompression = FALSE;
mT[0]->pUnk = NULL;
mT[0]->lSampleSize = capBox->outputwidth * capBox->outputheight * capBox->bitDepth / 8;
TRACE("Output format: %dx%d - %d bits = %u KB\n", capBox->outputwidth,
capBox->outputheight, capBox->bitDepth, mT[0]->lSampleSize/1024);
vi->rcSource.left = 0; vi->rcSource.top = 0;
vi->rcTarget.left = 0; vi->rcTarget.top = 0;
vi->rcSource.right = capBox->width; vi->rcSource.bottom = capBox->height;
vi->rcTarget.right = capBox->outputwidth; vi->rcTarget.bottom = capBox->outputheight;
vi->dwBitRate = capBox->fps * mT[0]->lSampleSize;
vi->dwBitErrorRate = 0;
vi->AvgTimePerFrame = (LONGLONG)10000000.0 / (LONGLONG)capBox->fps;
vi->bmiHeader.biSize = 40;
vi->bmiHeader.biWidth = capBox->outputwidth;
vi->bmiHeader.biHeight = capBox->outputheight;
vi->bmiHeader.biPlanes = 1;
vi->bmiHeader.biBitCount = 24;
vi->bmiHeader.biCompression = BI_RGB;
vi->bmiHeader.biSizeImage = mT[0]->lSampleSize;
vi->bmiHeader.biClrUsed = vi->bmiHeader.biClrImportant = 0;
vi->bmiHeader.biXPelsPerMeter = 100;
vi->bmiHeader.biYPelsPerMeter = 100;
mT[0]->pbFormat = (void *)vi;
dump_AM_MEDIA_TYPE(mT[0]);
return S_OK;
}
HRESULT qcap_driver_get_prop_range( Capture *capBox,
VideoProcAmpProperty Property, LONG *pMin, LONG *pMax,
LONG *pSteppingDelta, LONG *pDefault, LONG *pCapsFlags )
{
TRACE("%p -> %d %p %p %p %p %p\n", capBox, Property,
pMin, pMax, pSteppingDelta, pDefault, pCapsFlags);
switch (Property)
{
case VideoProcAmp_Brightness:
*pDefault = capBox->dbrightness;
break;
case VideoProcAmp_Contrast:
*pDefault = capBox->dcontrast;
break;
case VideoProcAmp_Hue:
*pDefault = capBox->dhue;
break;
case VideoProcAmp_Saturation:
*pDefault = capBox->dcolour;
break;
default:
FIXME("Not implemented %d\n", Property);
return E_NOTIMPL;
}
*pMin = 0;
*pMax = 65535;
*pSteppingDelta = 65536/256;
*pCapsFlags = VideoProcAmp_Flags_Manual;
return S_OK;
}
HRESULT qcap_driver_get_prop( Capture *capBox,
VideoProcAmpProperty Property, LONG *lValue, LONG *Flags )
{
TRACE("%p -> %d %p %p\n", capBox, Property, lValue, Flags);
switch (Property)
{
case VideoProcAmp_Brightness:
*lValue = capBox->pict.brightness;
break;
case VideoProcAmp_Contrast:
*lValue = capBox->pict.contrast;
break;
case VideoProcAmp_Hue:
*lValue = capBox->pict.hue;
break;
case VideoProcAmp_Saturation:
*lValue = capBox->pict.colour;
break;
default:
FIXME("Not implemented %d\n", Property);
return E_NOTIMPL;
}
*Flags = VideoProcAmp_Flags_Manual;
return S_OK;
}
HRESULT qcap_driver_set_prop(Capture *capBox, VideoProcAmpProperty Property,
LONG lValue, LONG Flags)
{
TRACE("%p -> %d %d %d\n", capBox, Property, lValue, Flags);
switch (Property)
{
case VideoProcAmp_Brightness:
capBox->pict.brightness = lValue;
break;
case VideoProcAmp_Contrast:
capBox->pict.contrast = lValue;
break;
case VideoProcAmp_Hue:
capBox->pict.hue = lValue;
break;
case VideoProcAmp_Saturation:
capBox->pict.colour = lValue;
break;
default:
FIXME("Not implemented %d\n", Property);
return E_NOTIMPL;
}
if (xioctl(capBox->fd, VIDIOCSPICT, &capBox->pict) == -1)
{
ERR("ioctl(VIDIOCSPICT) failed (%d)\n",errno);
return E_FAIL;
}
return S_OK;
}
static void renderer_RGB(const Capture *capBox, LPBYTE bufferin, const BYTE *stream)
{
int depth = renderlist_V4l[capBox->pict.palette].depth;
int size = capBox->height * capBox->width * depth / 8;
int pointer, offset;
switch (depth)
{
case 24:
memcpy(bufferin, stream, size);
break;
case 32:
pointer = 0;
offset = 1;
while (pointer + offset <= size)
{
bufferin[pointer] = stream[pointer + offset];
pointer++;
bufferin[pointer] = stream[pointer + offset];
pointer++;
bufferin[pointer] = stream[pointer + offset];
pointer++;
offset++;
}
break;
default:
ERR("Unknown bit depth %d\n", depth);
return;
}
}
static void renderer_YUV(const Capture *capBox, LPBYTE bufferin, const BYTE *stream)
{
enum YUV_Format format;
switch (capBox->pict.palette)
{
case 7: /* YUV422 - same as YUYV */
case 8: /* YUYV */
format = YUYV;
break;
case 9: /* UYVY */
format = UYVY;
break;
case 11: /* YUV411 */
format = UYYVYY;
break;
case 13: /* YUV422P */
format = YUVP_421;
break;
case 14: /* YUV411P */
format = YUVP_441;
break;
case 15: /* YUV420P */
format = YUVP_422;
break;
case 16: /* YUV410P */
format = YUVP_444;
break;
default:
ERR("Unknown palette %d\n", capBox->pict.palette);
return;
}
YUV_To_RGB24(format, bufferin, stream, capBox->width, capBox->height);
}
static void Resize(const Capture * capBox, LPBYTE output, const BYTE *input)
{
/* the whole image needs to be reversed,
because the dibs are messed up in windows */
if (!capBox->swresize)
{
int depth = capBox->bitDepth / 8;
int inoffset = 0, outoffset = capBox->height * capBox->width * depth;
int ow = capBox->width * depth;
while (outoffset > 0)
{
int x;
outoffset -= ow;
for (x = 0; x < ow; x++)
output[outoffset + x] = input[inoffset + x];
inoffset += ow;
}
}
else
{
HDC dc_s, dc_d;
HBITMAP bmp_s, bmp_d;
int depth = capBox->bitDepth / 8;
int inoffset = 0, outoffset = (capBox->outputheight) * capBox->outputwidth * depth;
int ow = capBox->outputwidth * depth;
LPBYTE myarray;
/* FIXME: Improve software resizing: add error checks and optimize */
myarray = CoTaskMemAlloc(capBox->outputwidth * capBox->outputheight * depth);
dc_s = CreateCompatibleDC(NULL);
dc_d = CreateCompatibleDC(NULL);
bmp_s = CreateBitmap(capBox->width, capBox->height, 1, capBox->bitDepth, input);
bmp_d = CreateBitmap(capBox->outputwidth, capBox->outputheight, 1, capBox->bitDepth, NULL);
SelectObject(dc_s, bmp_s);
SelectObject(dc_d, bmp_d);
StretchBlt(dc_d, 0, 0, capBox->outputwidth, capBox->outputheight,
dc_s, 0, 0, capBox->width, capBox->height, SRCCOPY);
GetBitmapBits(bmp_d, capBox->outputwidth * capBox->outputheight * depth, myarray);
while (outoffset > 0)
{
int i;
outoffset -= ow;
for (i = 0; i < ow; i++)
output[outoffset + i] = myarray[inoffset + i];
inoffset += ow;
}
CoTaskMemFree(myarray);
DeleteObject(dc_s);
DeleteObject(dc_d);
DeleteObject(bmp_s);
DeleteObject(bmp_d);
}
}
static void V4l_GetFrame(Capture * capBox, unsigned char ** pInput)
{
if (capBox->mmap)
{
if (xioctl(capBox->fd, VIDIOCSYNC, &capBox->grab_buf[capBox->curframe]) == -1)
WARN("Syncing ioctl failed: %d\n", errno);
*pInput = capBox->pmap + capBox->gb_buffers.offsets[capBox->curframe];
}
else
{
int retval;
while ((retval = video_read(capBox->fd, capBox->grab_data, capBox->imagesize)) == -1)
if (errno != EAGAIN) break;
if (retval == -1)
WARN("Error occurred while reading from device: %s\n", strerror(errno));
*pInput = (unsigned char*) capBox->grab_data;
}
}
static void V4l_FreeFrame(Capture * capBox)
{
TRACE("\n");
if (capBox->mmap)
{
if (xioctl(capBox->fd, VIDIOCMCAPTURE, &capBox->grab_buf[capBox->curframe]) == -1)
ERR("Freeing frame for capture failed: %s\n", strerror(errno));
}
if (++capBox->curframe == capBox->buffers)
capBox->curframe = 0;
}
static DWORD WINAPI ReadThread(LPVOID lParam)
{
Capture * capBox = lParam;
HRESULT hr;
IMediaSample *pSample = NULL;
ULONG framecount = 0;
unsigned char *pTarget, *pInput, *pOutput;
hr = V4l_Prepare(capBox);
if (FAILED(hr))
{
ERR("Stop IFilterGraph: %x\n", hr);
capBox->thread = 0;
capBox->stopped = TRUE;
return 0;
}
pOutput = CoTaskMemAlloc(capBox->width * capBox->height * capBox->bitDepth / 8);
capBox->curframe = 0;
do {
V4l_FreeFrame(capBox);
} while (capBox->curframe != 0);
while (1)
{
EnterCriticalSection(&capBox->CritSect);
if (capBox->stopped)
break;
hr = BaseOutputPinImpl_GetDeliveryBuffer((BaseOutputPin *)capBox->pOut, &pSample, NULL, NULL, 0);
if (SUCCEEDED(hr))
{
int len;
if (!capBox->swresize)
len = capBox->height * capBox->width * capBox->bitDepth / 8;
else
len = capBox->outputheight * capBox->outputwidth * capBox->bitDepth / 8;
IMediaSample_SetActualDataLength(pSample, len);
len = IMediaSample_GetActualDataLength(pSample);
TRACE("Data length: %d KB\n", len / 1024);
IMediaSample_GetPointer(pSample, &pTarget);
/* FIXME: Check return values.. */
V4l_GetFrame(capBox, &pInput);
capBox->renderer(capBox, pOutput, pInput);
Resize(capBox, pTarget, pOutput);
hr = BaseOutputPinImpl_Deliver((BaseOutputPin *)capBox->pOut, pSample);
TRACE("%p -> Frame %u: %x\n", capBox, ++framecount, hr);
IMediaSample_Release(pSample);
V4l_FreeFrame(capBox);
}
if (FAILED(hr) && hr != VFW_E_NOT_CONNECTED)
{
TRACE("Return %x, stop IFilterGraph\n", hr);
V4l_Unprepare(capBox);
capBox->thread = 0;
capBox->stopped = TRUE;
break;
}
LeaveCriticalSection(&capBox->CritSect);
}
LeaveCriticalSection(&capBox->CritSect);
CoTaskMemFree(pOutput);
return 0;
}
HRESULT qcap_driver_run(Capture *capBox, FILTER_STATE *state)
{
HANDLE thread;
HRESULT hr;
TRACE("%p -> (%p)\n", capBox, state);
if (*state == State_Running) return S_OK;
EnterCriticalSection(&capBox->CritSect);
capBox->stopped = FALSE;
if (*state == State_Stopped)
{
*state = State_Running;
if (!capBox->iscommitted)
{
ALLOCATOR_PROPERTIES ap, actual;
BaseOutputPin *out;
capBox->iscommitted = TRUE;
ap.cBuffers = 3;
if (!capBox->swresize)
ap.cbBuffer = capBox->width * capBox->height;
else
ap.cbBuffer = capBox->outputwidth * capBox->outputheight;
ap.cbBuffer = (ap.cbBuffer * capBox->bitDepth) / 8;
ap.cbAlign = 1;
ap.cbPrefix = 0;
out = (BaseOutputPin *)capBox->pOut;
hr = IMemAllocator_SetProperties(out->pAllocator, &ap, &actual);
if (SUCCEEDED(hr))
hr = IMemAllocator_Commit(out->pAllocator);
TRACE("Committing allocator: %x\n", hr);
}
thread = CreateThread(NULL, 0, ReadThread, capBox, 0, NULL);
if (thread)
{
capBox->thread = thread;
SetThreadPriority(thread, THREAD_PRIORITY_LOWEST);
LeaveCriticalSection(&capBox->CritSect);
return S_OK;
}
ERR("Creating thread failed.. %u\n", GetLastError());
LeaveCriticalSection(&capBox->CritSect);
return E_FAIL;
}
ResumeThread(capBox->thread);
*state = State_Running;
LeaveCriticalSection(&capBox->CritSect);
return S_OK;
}
HRESULT qcap_driver_pause(Capture *capBox, FILTER_STATE *state)
{
TRACE("%p -> (%p)\n", capBox, state);
if (*state == State_Paused)
return S_OK;
if (*state == State_Stopped)
qcap_driver_run(capBox, state);
EnterCriticalSection(&capBox->CritSect);
*state = State_Paused;
SuspendThread(capBox->thread);
LeaveCriticalSection(&capBox->CritSect);
return S_OK;
}
HRESULT qcap_driver_stop(Capture *capBox, FILTER_STATE *state)
{
TRACE("%p -> (%p)\n", capBox, state);
if (*state == State_Stopped)
return S_OK;
EnterCriticalSection(&capBox->CritSect);
if (capBox->thread)
{
if (*state == State_Paused)
ResumeThread(capBox->thread);
capBox->stopped = TRUE;
capBox->thread = 0;
if (capBox->iscommitted)
{
BaseOutputPin *out;
HRESULT hr;
capBox->iscommitted = FALSE;
out = (BaseOutputPin*)capBox->pOut;
hr = IMemAllocator_Decommit(out->pAllocator);
if (hr != S_OK && hr != VFW_E_NOT_COMMITTED)
WARN("Decommitting allocator: %x\n", hr);
}
V4l_Unprepare(capBox);
}
*state = State_Stopped;
LeaveCriticalSection(&capBox->CritSect);
return S_OK;
}
Capture * qcap_driver_init( IPin *pOut, USHORT card )
{
Capture * capBox = NULL;
char device[20];
struct video_capability capa;
struct video_picture pict;
struct video_window window;
YUV_Init();
video_init();
capBox = CoTaskMemAlloc(sizeof(Capture));
if (!capBox)
goto error;
/* capBox->vtbl = &defboxVtbl; */
InitializeCriticalSection( &capBox->CritSect );
capBox->CritSect.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": Capture.CritSect");
sprintf(device, "/dev/video%i", card);
TRACE("opening %s\n", device);
#ifdef O_CLOEXEC
if ((capBox->fd = video_open(device, O_RDWR | O_NONBLOCK | O_CLOEXEC)) == -1 && errno == EINVAL)
#endif
capBox->fd = video_open(device, O_RDWR | O_NONBLOCK);
if (capBox->fd == -1)
{
WARN("open failed (%d)\n", errno);
goto error;
}
fcntl( capBox->fd, F_SETFD, FD_CLOEXEC ); /* in case O_CLOEXEC isn't supported */
memset(&capa, 0, sizeof(capa));
if (xioctl(capBox->fd, VIDIOCGCAP, &capa) == -1)
{
WARN("ioctl(VIDIOCGCAP) failed (%d)\n", errno);
goto error;
}
if (!(capa.type & VID_TYPE_CAPTURE))
{
WARN("not a video capture device\n");
goto error;
}
TRACE("%d inputs on %s\n", capa.channels, capa.name );
if (xioctl(capBox->fd, VIDIOCGPICT, &pict) == -1)
{
ERR("ioctl(VIDIOCGPICT) failed (%d)\n", errno );
goto error;
}
TRACE("depth %d palette %d (%s) hue %d color %d contrast %d\n",
pict.depth, pict.palette, renderlist_V4l[pict.palette].name,
pict.hue, pict.colour, pict.contrast );
capBox->dbrightness = pict.brightness;
capBox->dcolour = pict.colour;
capBox->dhue = pict.hue;
capBox->dcontrast = pict.contrast;
if (!renderlist_V4l[pict.palette].renderer)
{
int palet = pict.palette, i;
TRACE("No renderer available for %s, falling back to defaults\n",
renderlist_V4l[pict.palette].name);
capBox->renderer = NULL;
for (i = 0; fallback_V4l[i] >=0 ; i++)
{
int n = fallback_V4l[i];
if (renderlist_V4l[n].renderer == NULL)
continue;
pict.depth = renderlist_V4l[n].depth;
pict.palette = n;
if (xioctl(capBox->fd, VIDIOCSPICT, &pict) == -1)
{
TRACE("Could not render with %s (%d)\n",
renderlist_V4l[n].name, n);
continue;
}
TRACE("using renderer %s (%d)\n",
renderlist_V4l[n].name, n);
capBox->renderer = renderlist_V4l[n].renderer;
break;
}
if (!capBox->renderer)
{
ERR("video format %s isn't available\n",
renderlist_V4l[palet].name);
goto error;
}
}
else
{
TRACE("Using the suggested format\n");
capBox->renderer = renderlist_V4l[pict.palette].renderer;
}
memcpy(&capBox->pict, &pict, sizeof(struct video_picture));
memset(&window, 0, sizeof(window));
if (xioctl(capBox->fd, VIDIOCGWIN, &window) == -1)
{
WARN("VIDIOCGWIN failed (%d)\n", errno);
goto error;
}
capBox->height = capBox->outputheight = window.height;
capBox->width = capBox->outputwidth = window.width;
capBox->swresize = FALSE;
capBox->bitDepth = 24;
capBox->pOut = pOut;
capBox->fps = 3;
capBox->stopped = FALSE;
capBox->curframe = 0;
capBox->iscommitted = FALSE;
TRACE("format: %d bits - %d x %d\n", capBox->bitDepth, capBox->width, capBox->height);
return capBox;
error:
if (capBox)
qcap_driver_destroy( capBox );
return NULL;
}
#else
Capture * qcap_driver_init( IPin *pOut, USHORT card )
{
const char msg[] =
"The v4l headers were not available at compile time,\n"
"so video capture support is not available.\n";
MESSAGE(msg);
return NULL;
}
#define FAIL_WITH_ERR \
ERR("v4l absent: shouldn't be called\n"); \
return E_NOTIMPL
HRESULT qcap_driver_destroy(Capture *capBox)
{
FAIL_WITH_ERR;
}
HRESULT qcap_driver_set_format(Capture *capBox, AM_MEDIA_TYPE * mT)
{
FAIL_WITH_ERR;
}
HRESULT qcap_driver_get_format(const Capture *capBox, AM_MEDIA_TYPE ** mT)
{
FAIL_WITH_ERR;
}
HRESULT qcap_driver_get_prop_range( Capture *capBox,
VideoProcAmpProperty Property, LONG *pMin, LONG *pMax,
LONG *pSteppingDelta, LONG *pDefault, LONG *pCapsFlags )
{
FAIL_WITH_ERR;
}
HRESULT qcap_driver_get_prop(Capture *capBox,
VideoProcAmpProperty Property, LONG *lValue, LONG *Flags)
{
FAIL_WITH_ERR;
}
HRESULT qcap_driver_set_prop(Capture *capBox, VideoProcAmpProperty Property,
LONG lValue, LONG Flags)
{
FAIL_WITH_ERR;
}
HRESULT qcap_driver_run(Capture *capBox, FILTER_STATE *state)
{
FAIL_WITH_ERR;
}
HRESULT qcap_driver_pause(Capture *capBox, FILTER_STATE *state)
{
FAIL_WITH_ERR;
}
HRESULT qcap_driver_stop(Capture *capBox, FILTER_STATE *state)
{
FAIL_WITH_ERR;
}
#endif /* defined(VIDIOCMCAPTURE) */

View file

@ -0,0 +1,26 @@
/*
* Copyright 2004 Ivan Leo Puoti
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define WINE_FILEDESCRIPTION_STR "Wine qcap dll"
#define WINE_FILENAME_STR "qcap.dll"
#define WINE_FILEVERSION 6,5,1,900
#define WINE_FILEVERSION_STR "6.5.1.900"
#define WINE_PRODUCTVERSION 6,5,1,900
#define WINE_PRODUCTVERSION_STR "6.5"
#include "wine/wine_common_ver.rc"

View file

@ -0,0 +1,834 @@
/* Video For Windows Steering structure
*
* Copyright 2005 Maarten Lankhorst
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
*/
#define COBJMACROS
#include "config.h"
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "wtypes.h"
#include "wingdi.h"
#include "winuser.h"
#include "dshow.h"
#include "qcap_main.h"
#include "wine/debug.h"
#include "capture.h"
#include "uuids.h"
#include "vfwmsgs.h"
#include "amvideo.h"
#include "strmif.h"
#include "ddraw.h"
#include "ocidl.h"
#include "oleauto.h"
WINE_DEFAULT_DEBUG_CHANNEL(qcap);
static const IBaseFilterVtbl VfwCapture_Vtbl;
static const IAMStreamConfigVtbl IAMStreamConfig_VTable;
static const IAMVideoProcAmpVtbl IAMVideoProcAmp_VTable;
static const IPersistPropertyBagVtbl IPersistPropertyBag_VTable;
static const IPinVtbl VfwPin_Vtbl;
static HRESULT VfwPin_Construct( IBaseFilter *, LPCRITICAL_SECTION, IPin ** );
typedef struct VfwCapture
{
IUnknown IUnknown_inner;
BaseFilter filter;
IAMStreamConfig IAMStreamConfig_iface;
IAMVideoProcAmp IAMVideoProcAmp_iface;
IPersistPropertyBag IPersistPropertyBag_iface;
IUnknown *outer_unk;
BOOL init;
Capture *driver_info;
IPin * pOutputPin;
} VfwCapture;
static inline VfwCapture *impl_from_IUnknown(IUnknown *iface)
{
return CONTAINING_RECORD(iface, VfwCapture, IUnknown_inner);
}
static inline VfwCapture *impl_from_BaseFilter(BaseFilter *iface)
{
return CONTAINING_RECORD(iface, VfwCapture, filter);
}
static inline VfwCapture *impl_from_IBaseFilter(IBaseFilter *iface)
{
return CONTAINING_RECORD(iface, VfwCapture, filter.IBaseFilter_iface);
}
static inline VfwCapture *impl_from_IAMStreamConfig(IAMStreamConfig *iface)
{
return CONTAINING_RECORD(iface, VfwCapture, IAMStreamConfig_iface);
}
static inline VfwCapture *impl_from_IAMVideoProcAmp(IAMVideoProcAmp *iface)
{
return CONTAINING_RECORD(iface, VfwCapture, IAMVideoProcAmp_iface);
}
static inline VfwCapture *impl_from_IPersistPropertyBag(IPersistPropertyBag *iface)
{
return CONTAINING_RECORD(iface, VfwCapture, IPersistPropertyBag_iface);
}
/* VfwPin implementation */
typedef struct VfwPinImpl
{
BaseOutputPin pin;
IKsPropertySet IKsPropertySet_iface;
VfwCapture *parent;
} VfwPinImpl;
/* VfwCapture inner IUnknown */
static HRESULT WINAPI unknown_inner_QueryInterface(IUnknown *iface, REFIID riid, void **ret_iface)
{
VfwCapture *This = impl_from_IUnknown(iface);
TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ret_iface);
*ret_iface = NULL;
if (IsEqualIID(riid, &IID_IUnknown))
*ret_iface = &This->IUnknown_inner;
else if (IsEqualIID(riid, &IID_IPersist) || IsEqualIID(riid, &IID_IMediaFilter) ||
IsEqualIID(riid, &IID_IBaseFilter))
*ret_iface = &This->filter.IBaseFilter_iface;
else if (IsEqualIID(riid, &IID_IPersistPropertyBag))
*ret_iface = &This->IPersistPropertyBag_iface;
else if (IsEqualIID(riid, &IID_IAMFilterMiscFlags))
FIXME("IAMFilterMiscFlags not supported yet!\n");
else if (IsEqualIID(riid, &IID_ISpecifyPropertyPages))
FIXME("ISpecifyPropertyPages not supported yet!\n");
else if (IsEqualIID(riid, &IID_IAMVfwCaptureDialogs))
FIXME("IAMVfwCaptureDialogs not supported yet!\n");
else if (IsEqualIID(riid, &IID_IAMStreamConfig))
*ret_iface = &This->IAMStreamConfig_iface;
else if (IsEqualIID(riid, &IID_IAMVideoProcAmp))
*ret_iface = &This->IAMVideoProcAmp_iface;
else
WARN("(%p, %s, %p): not found\n", This, debugstr_guid(riid), ret_iface);
if (!*ret_iface)
return E_NOINTERFACE;
IUnknown_AddRef((IUnknown*)*ret_iface);
return S_OK;
}
static ULONG WINAPI unknown_inner_AddRef(IUnknown *iface)
{
VfwCapture *This = impl_from_IUnknown(iface);
ULONG ref = BaseFilterImpl_AddRef(&This->filter.IBaseFilter_iface);
TRACE("(%p) ref=%d\n", This, ref);
return ref;
}
static ULONG WINAPI unknown_inner_Release(IUnknown *iface)
{
VfwCapture *This = impl_from_IUnknown(iface);
ULONG ref = InterlockedDecrement(&This->filter.refCount);
TRACE("(%p) ref=%d\n", This, ref);
if (!ref)
{
IPin *conn = NULL;
TRACE("destroying everything\n");
if (This->init)
{
if (This->filter.state != State_Stopped)
qcap_driver_stop(This->driver_info, &This->filter.state);
qcap_driver_destroy(This->driver_info);
}
IPin_ConnectedTo(This->pOutputPin, &conn);
if (conn)
{
IPin_Disconnect(conn);
IPin_Disconnect(This->pOutputPin);
IPin_Release(conn);
}
IPin_Release(This->pOutputPin);
BaseFilter_Destroy(&This->filter);
CoTaskMemFree(This);
ObjectRefCount(FALSE);
}
return ref;
}
static const IUnknownVtbl unknown_inner_vtbl =
{
unknown_inner_QueryInterface,
unknown_inner_AddRef,
unknown_inner_Release,
};
static IPin* WINAPI VfwCapture_GetPin(BaseFilter *iface, int pos)
{
VfwCapture *This = impl_from_BaseFilter(iface);
if (pos >= 1 || pos < 0)
return NULL;
IPin_AddRef(This->pOutputPin);
return This->pOutputPin;
}
static LONG WINAPI VfwCapture_GetPinCount(BaseFilter *iface)
{
return 1;
}
static const BaseFilterFuncTable BaseFuncTable = {
VfwCapture_GetPin,
VfwCapture_GetPinCount
};
IUnknown * WINAPI QCAP_createVFWCaptureFilter(IUnknown *pUnkOuter, HRESULT *phr)
{
VfwCapture *pVfwCapture;
HRESULT hr;
TRACE("%p - %p\n", pUnkOuter, phr);
*phr = E_OUTOFMEMORY;
pVfwCapture = CoTaskMemAlloc( sizeof(VfwCapture) );
if (!pVfwCapture)
return NULL;
BaseFilter_Init(&pVfwCapture->filter, &VfwCapture_Vtbl, &CLSID_VfwCapture, (DWORD_PTR)(__FILE__ ": VfwCapture.csFilter"), &BaseFuncTable);
pVfwCapture->IUnknown_inner.lpVtbl = &unknown_inner_vtbl;
pVfwCapture->IAMStreamConfig_iface.lpVtbl = &IAMStreamConfig_VTable;
pVfwCapture->IAMVideoProcAmp_iface.lpVtbl = &IAMVideoProcAmp_VTable;
pVfwCapture->IPersistPropertyBag_iface.lpVtbl = &IPersistPropertyBag_VTable;
pVfwCapture->init = FALSE;
if (pUnkOuter)
pVfwCapture->outer_unk = pUnkOuter;
else
pVfwCapture->outer_unk = &pVfwCapture->IUnknown_inner;
hr = VfwPin_Construct(&pVfwCapture->filter.IBaseFilter_iface,
&pVfwCapture->filter.csFilter, &pVfwCapture->pOutputPin);
if (FAILED(hr))
{
CoTaskMemFree(pVfwCapture);
return NULL;
}
TRACE("-- created at %p\n", pVfwCapture);
ObjectRefCount(TRUE);
*phr = S_OK;
return &pVfwCapture->IUnknown_inner;
}
static HRESULT WINAPI VfwCapture_QueryInterface(IBaseFilter *iface, REFIID riid, void **ret_iface)
{
VfwCapture *This = impl_from_IBaseFilter(iface);
return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface);
}
static ULONG WINAPI VfwCapture_AddRef(IBaseFilter *iface)
{
VfwCapture *This = impl_from_IBaseFilter(iface);
return IUnknown_AddRef(This->outer_unk);
}
static ULONG WINAPI VfwCapture_Release(IBaseFilter * iface)
{
VfwCapture *This = impl_from_IBaseFilter(iface);
return IUnknown_Release(This->outer_unk);
}
/** IMediaFilter methods **/
static HRESULT WINAPI VfwCapture_Stop(IBaseFilter * iface)
{
VfwCapture *This = impl_from_IBaseFilter(iface);
TRACE("()\n");
return qcap_driver_stop(This->driver_info, &This->filter.state);
}
static HRESULT WINAPI VfwCapture_Pause(IBaseFilter * iface)
{
VfwCapture *This = impl_from_IBaseFilter(iface);
TRACE("()\n");
return qcap_driver_pause(This->driver_info, &This->filter.state);
}
static HRESULT WINAPI VfwCapture_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
{
VfwCapture *This = impl_from_IBaseFilter(iface);
TRACE("(%s)\n", wine_dbgstr_longlong(tStart));
return qcap_driver_run(This->driver_info, &This->filter.state);
}
/** IBaseFilter methods **/
static HRESULT WINAPI VfwCapture_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
{
FIXME("(%s, %p) - stub\n", debugstr_w(Id), ppPin);
return E_NOTIMPL;
}
static const IBaseFilterVtbl VfwCapture_Vtbl =
{
VfwCapture_QueryInterface,
VfwCapture_AddRef,
VfwCapture_Release,
BaseFilterImpl_GetClassID,
VfwCapture_Stop,
VfwCapture_Pause,
VfwCapture_Run,
BaseFilterImpl_GetState,
BaseFilterImpl_SetSyncSource,
BaseFilterImpl_GetSyncSource,
BaseFilterImpl_EnumPins,
VfwCapture_FindPin,
BaseFilterImpl_QueryFilterInfo,
BaseFilterImpl_JoinFilterGraph,
BaseFilterImpl_QueryVendorInfo
};
/* AMStreamConfig interface, we only need to implement {G,S}etFormat */
static HRESULT WINAPI AMStreamConfig_QueryInterface(IAMStreamConfig *iface, REFIID riid,
void **ret_iface)
{
VfwCapture *This = impl_from_IAMStreamConfig(iface);
return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface);
}
static ULONG WINAPI AMStreamConfig_AddRef( IAMStreamConfig * iface )
{
VfwCapture *This = impl_from_IAMStreamConfig(iface);
return IUnknown_AddRef(This->outer_unk);
}
static ULONG WINAPI AMStreamConfig_Release( IAMStreamConfig * iface )
{
VfwCapture *This = impl_from_IAMStreamConfig(iface);
return IUnknown_Release(This->outer_unk);
}
static HRESULT WINAPI
AMStreamConfig_SetFormat(IAMStreamConfig *iface, AM_MEDIA_TYPE *pmt)
{
HRESULT hr;
VfwCapture *This = impl_from_IAMStreamConfig(iface);
BasePin *pin;
TRACE("(%p): %p->%p\n", iface, pmt, pmt ? pmt->pbFormat : NULL);
if (This->filter.state != State_Stopped)
{
TRACE("Returning not stopped error\n");
return VFW_E_NOT_STOPPED;
}
if (!pmt)
{
TRACE("pmt is NULL\n");
return E_POINTER;
}
dump_AM_MEDIA_TYPE(pmt);
pin = (BasePin *)This->pOutputPin;
if (pin->pConnectedTo != NULL)
{
hr = IPin_QueryAccept(pin->pConnectedTo, pmt);
TRACE("Would accept: %d\n", hr);
if (hr == S_FALSE)
return VFW_E_INVALIDMEDIATYPE;
}
hr = qcap_driver_set_format(This->driver_info, pmt);
if (SUCCEEDED(hr) && This->filter.filterInfo.pGraph && pin->pConnectedTo )
{
hr = IFilterGraph_Reconnect(This->filter.filterInfo.pGraph, This->pOutputPin);
if (SUCCEEDED(hr))
TRACE("Reconnection completed, with new media format..\n");
}
TRACE("Returning: %d\n", hr);
return hr;
}
static HRESULT WINAPI
AMStreamConfig_GetFormat( IAMStreamConfig *iface, AM_MEDIA_TYPE **pmt )
{
VfwCapture *This = impl_from_IAMStreamConfig(iface);
TRACE("%p -> (%p)\n", iface, pmt);
return qcap_driver_get_format(This->driver_info, pmt);
}
static HRESULT WINAPI
AMStreamConfig_GetNumberOfCapabilities( IAMStreamConfig *iface, int *piCount,
int *piSize )
{
FIXME("%p: %p %p - stub, intentional\n", iface, piCount, piSize);
*piCount = 0;
return E_NOTIMPL; /* Not implemented for this interface */
}
static HRESULT WINAPI
AMStreamConfig_GetStreamCaps( IAMStreamConfig *iface, int iIndex,
AM_MEDIA_TYPE **pmt, BYTE *pSCC )
{
FIXME("%p: %d %p %p - stub, intentional\n", iface, iIndex, pmt, pSCC);
return E_NOTIMPL; /* Not implemented for this interface */
}
static const IAMStreamConfigVtbl IAMStreamConfig_VTable =
{
AMStreamConfig_QueryInterface,
AMStreamConfig_AddRef,
AMStreamConfig_Release,
AMStreamConfig_SetFormat,
AMStreamConfig_GetFormat,
AMStreamConfig_GetNumberOfCapabilities,
AMStreamConfig_GetStreamCaps
};
static HRESULT WINAPI AMVideoProcAmp_QueryInterface(IAMVideoProcAmp *iface, REFIID riid,
void **ret_iface)
{
VfwCapture *This = impl_from_IAMVideoProcAmp(iface);
return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface);
}
static ULONG WINAPI AMVideoProcAmp_AddRef(IAMVideoProcAmp * iface)
{
VfwCapture *This = impl_from_IAMVideoProcAmp(iface);
return IUnknown_AddRef(This->outer_unk);
}
static ULONG WINAPI AMVideoProcAmp_Release(IAMVideoProcAmp * iface)
{
VfwCapture *This = impl_from_IAMVideoProcAmp(iface);
return IUnknown_Release(This->outer_unk);
}
static HRESULT WINAPI
AMVideoProcAmp_GetRange( IAMVideoProcAmp * iface, LONG Property, LONG *pMin,
LONG *pMax, LONG *pSteppingDelta, LONG *pDefault, LONG *pCapsFlags )
{
VfwCapture *This = impl_from_IAMVideoProcAmp(iface);
return qcap_driver_get_prop_range( This->driver_info, Property, pMin, pMax,
pSteppingDelta, pDefault, pCapsFlags );
}
static HRESULT WINAPI
AMVideoProcAmp_Set( IAMVideoProcAmp * iface, LONG Property, LONG lValue,
LONG Flags )
{
VfwCapture *This = impl_from_IAMVideoProcAmp(iface);
return qcap_driver_set_prop(This->driver_info, Property, lValue, Flags);
}
static HRESULT WINAPI
AMVideoProcAmp_Get( IAMVideoProcAmp * iface, LONG Property, LONG *lValue,
LONG *Flags )
{
VfwCapture *This = impl_from_IAMVideoProcAmp(iface);
return qcap_driver_get_prop(This->driver_info, Property, lValue, Flags);
}
static const IAMVideoProcAmpVtbl IAMVideoProcAmp_VTable =
{
AMVideoProcAmp_QueryInterface,
AMVideoProcAmp_AddRef,
AMVideoProcAmp_Release,
AMVideoProcAmp_GetRange,
AMVideoProcAmp_Set,
AMVideoProcAmp_Get,
};
static HRESULT WINAPI PPB_QueryInterface(IPersistPropertyBag *iface, REFIID riid, void **ret_iface)
{
VfwCapture *This = impl_from_IPersistPropertyBag(iface);
return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface);
}
static ULONG WINAPI PPB_AddRef(IPersistPropertyBag * iface)
{
VfwCapture *This = impl_from_IPersistPropertyBag(iface);
return IUnknown_AddRef(This->outer_unk);
}
static ULONG WINAPI PPB_Release(IPersistPropertyBag * iface)
{
VfwCapture *This = impl_from_IPersistPropertyBag(iface);
return IUnknown_Release(This->outer_unk);
}
static HRESULT WINAPI
PPB_GetClassID( IPersistPropertyBag * iface, CLSID * pClassID )
{
VfwCapture *This = impl_from_IPersistPropertyBag(iface);
FIXME("%p - stub\n", This);
return E_NOTIMPL;
}
static HRESULT WINAPI PPB_InitNew(IPersistPropertyBag * iface)
{
VfwCapture *This = impl_from_IPersistPropertyBag(iface);
FIXME("%p - stub\n", This);
return E_NOTIMPL;
}
static HRESULT WINAPI
PPB_Load( IPersistPropertyBag * iface, IPropertyBag *pPropBag,
IErrorLog *pErrorLog )
{
VfwCapture *This = impl_from_IPersistPropertyBag(iface);
HRESULT hr;
VARIANT var;
const OLECHAR VFWIndex[] = {'V','F','W','I','n','d','e','x',0};
TRACE("%p/%p-> (%p, %p)\n", iface, This, pPropBag, pErrorLog);
V_VT(&var) = VT_I4;
hr = IPropertyBag_Read(pPropBag, VFWIndex, &var, pErrorLog);
if (SUCCEEDED(hr))
{
VfwPinImpl *pin;
This->driver_info = qcap_driver_init( This->pOutputPin,
var.__VARIANT_NAME_1.__VARIANT_NAME_2.__VARIANT_NAME_3.ulVal );
if (This->driver_info)
{
pin = (VfwPinImpl *)This->pOutputPin;
pin->parent = This;
This->init = TRUE;
hr = S_OK;
}
else
hr = E_FAIL;
}
return hr;
}
static HRESULT WINAPI
PPB_Save( IPersistPropertyBag * iface, IPropertyBag *pPropBag,
BOOL fClearDirty, BOOL fSaveAllProperties )
{
VfwCapture *This = impl_from_IPersistPropertyBag(iface);
FIXME("%p - stub\n", This);
return E_NOTIMPL;
}
static const IPersistPropertyBagVtbl IPersistPropertyBag_VTable =
{
PPB_QueryInterface,
PPB_AddRef,
PPB_Release,
PPB_GetClassID,
PPB_InitNew,
PPB_Load,
PPB_Save
};
/* IKsPropertySet interface */
static inline VfwPinImpl *impl_from_IKsPropertySet(IKsPropertySet *iface)
{
return CONTAINING_RECORD(iface, VfwPinImpl, IKsPropertySet_iface);
}
static HRESULT WINAPI KSP_QueryInterface(IKsPropertySet * iface, REFIID riid, void **ret_iface)
{
VfwPinImpl *This = impl_from_IKsPropertySet(iface);
return IPin_QueryInterface(&This->pin.pin.IPin_iface, riid, ret_iface);
}
static ULONG WINAPI KSP_AddRef(IKsPropertySet * iface)
{
VfwPinImpl *This = impl_from_IKsPropertySet(iface);
return IPin_AddRef(&This->pin.pin.IPin_iface);
}
static ULONG WINAPI KSP_Release(IKsPropertySet * iface)
{
VfwPinImpl *This = impl_from_IKsPropertySet(iface);
return IPin_Release(&This->pin.pin.IPin_iface);
}
static HRESULT WINAPI
KSP_Set( IKsPropertySet * iface, REFGUID guidPropSet, DWORD dwPropID,
LPVOID pInstanceData, DWORD cbInstanceData, LPVOID pPropData,
DWORD cbPropData )
{
FIXME("%p: stub\n", iface);
return E_NOTIMPL;
}
static HRESULT WINAPI
KSP_Get( IKsPropertySet * iface, REFGUID guidPropSet, DWORD dwPropID,
LPVOID pInstanceData, DWORD cbInstanceData, LPVOID pPropData,
DWORD cbPropData, DWORD *pcbReturned )
{
LPGUID pGuid;
TRACE("()\n");
if (!IsEqualIID(guidPropSet, &AMPROPSETID_Pin))
return E_PROP_SET_UNSUPPORTED;
if (pPropData == NULL && pcbReturned == NULL)
return E_POINTER;
if (pcbReturned)
*pcbReturned = sizeof(GUID);
if (pPropData == NULL)
return S_OK;
if (cbPropData < sizeof(GUID))
return E_UNEXPECTED;
pGuid = pPropData;
*pGuid = PIN_CATEGORY_CAPTURE;
FIXME("() Not adding a pin with PIN_CATEGORY_PREVIEW\n");
return S_OK;
}
static HRESULT WINAPI
KSP_QuerySupported( IKsPropertySet * iface, REFGUID guidPropSet,
DWORD dwPropID, DWORD *pTypeSupport )
{
FIXME("%p: stub\n", iface);
return E_NOTIMPL;
}
static const IKsPropertySetVtbl IKsPropertySet_VTable =
{
KSP_QueryInterface,
KSP_AddRef,
KSP_Release,
KSP_Set,
KSP_Get,
KSP_QuerySupported
};
static inline VfwPinImpl *impl_from_BasePin(BasePin *pin)
{
return CONTAINING_RECORD(pin, VfwPinImpl, pin.pin);
}
static HRESULT WINAPI VfwPin_GetMediaType(BasePin *pin, int iPosition, AM_MEDIA_TYPE *pmt)
{
VfwPinImpl *This = impl_from_BasePin(pin);
AM_MEDIA_TYPE *vfw_pmt;
HRESULT hr;
if (iPosition < 0)
return E_INVALIDARG;
if (iPosition > 0)
return VFW_S_NO_MORE_ITEMS;
hr = qcap_driver_get_format(This->parent->driver_info, &vfw_pmt);
if (SUCCEEDED(hr)) {
CopyMediaType(pmt, vfw_pmt);
DeleteMediaType(vfw_pmt);
}
return hr;
}
static LONG WINAPI VfwPin_GetMediaTypeVersion(BasePin *iface)
{
return 1;
}
static HRESULT WINAPI VfwPin_DecideBufferSize(BaseOutputPin *iface, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
{
ALLOCATOR_PROPERTIES actual;
/* What we put here doesn't matter, the
driver function should override it then commit */
if (!ppropInputRequest->cBuffers)
ppropInputRequest->cBuffers = 3;
if (!ppropInputRequest->cbBuffer)
ppropInputRequest->cbBuffer = 230400;
if (!ppropInputRequest->cbAlign)
ppropInputRequest->cbAlign = 1;
return IMemAllocator_SetProperties(pAlloc, ppropInputRequest, &actual);
}
static const BaseOutputPinFuncTable output_BaseOutputFuncTable = {
{
NULL,
BaseOutputPinImpl_AttemptConnection,
VfwPin_GetMediaTypeVersion,
VfwPin_GetMediaType
},
VfwPin_DecideBufferSize,
BaseOutputPinImpl_DecideAllocator,
BaseOutputPinImpl_BreakConnect
};
static HRESULT
VfwPin_Construct( IBaseFilter * pBaseFilter, LPCRITICAL_SECTION pCritSec,
IPin ** ppPin )
{
static const WCHAR wszOutputPinName[] = { 'O','u','t','p','u','t',0 };
PIN_INFO piOutput;
HRESULT hr;
*ppPin = NULL;
piOutput.dir = PINDIR_OUTPUT;
piOutput.pFilter = pBaseFilter;
lstrcpyW(piOutput.achName, wszOutputPinName);
hr = BaseOutputPin_Construct(&VfwPin_Vtbl, sizeof(VfwPinImpl), &piOutput, &output_BaseOutputFuncTable, pCritSec, ppPin);
if (SUCCEEDED(hr))
{
VfwPinImpl *pPinImpl = (VfwPinImpl*)*ppPin;
pPinImpl->IKsPropertySet_iface.lpVtbl = &IKsPropertySet_VTable;
ObjectRefCount(TRUE);
}
return hr;
}
static inline VfwPinImpl *impl_from_IPin(IPin *iface)
{
return CONTAINING_RECORD(iface, VfwPinImpl, pin.pin.IPin_iface);
}
static HRESULT WINAPI VfwPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
{
VfwPinImpl *This = impl_from_IPin(iface);
TRACE("%s %p\n", debugstr_guid(riid), ppv);
*ppv = NULL;
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IPin))
*ppv = This;
else if (IsEqualIID(riid, &IID_IKsPropertySet))
*ppv = &This->IKsPropertySet_iface;
else if (IsEqualIID(riid, &IID_IAMStreamConfig))
return IUnknown_QueryInterface((IUnknown *)This->parent, riid, ppv);
if (*ppv)
{
IUnknown_AddRef((IUnknown *)(*ppv));
return S_OK;
}
FIXME("No interface for %s!\n", debugstr_guid(riid));
return E_NOINTERFACE;
}
static ULONG WINAPI
VfwPin_Release(IPin * iface)
{
VfwPinImpl *This = impl_from_IPin(iface);
ULONG refCount = InterlockedDecrement(&This->pin.pin.refCount);
TRACE("() -> new refcount: %u\n", refCount);
if (!refCount)
{
BaseOutputPin_Destroy(&This->pin);
ObjectRefCount(FALSE);
}
return refCount;
}
static HRESULT WINAPI
VfwPin_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
{
VfwPinImpl *This = impl_from_IPin(iface);
AM_MEDIA_TYPE *pmt;
HRESULT hr;
hr = qcap_driver_get_format(This->parent->driver_info, &pmt);
if (SUCCEEDED(hr)) {
hr = BasePinImpl_EnumMediaTypes(iface, ppEnum);
DeleteMediaType(pmt);
}
TRACE("%p -- %x\n", This, hr);
return hr;
}
static HRESULT WINAPI
VfwPin_QueryInternalConnections(IPin * iface, IPin ** apPin, ULONG * cPin)
{
TRACE("(%p)->(%p, %p)\n", iface, apPin, cPin);
return E_NOTIMPL;
}
static const IPinVtbl VfwPin_Vtbl =
{
VfwPin_QueryInterface,
BasePinImpl_AddRef,
VfwPin_Release,
BaseOutputPinImpl_Connect,
BaseOutputPinImpl_ReceiveConnection,
BaseOutputPinImpl_Disconnect,
BasePinImpl_ConnectedTo,
BasePinImpl_ConnectionMediaType,
BasePinImpl_QueryPinInfo,
BasePinImpl_QueryDirection,
BasePinImpl_QueryId,
BasePinImpl_QueryAccept,
VfwPin_EnumMediaTypes,
VfwPin_QueryInternalConnections,
BaseOutputPinImpl_EndOfStream,
BaseOutputPinImpl_BeginFlush,
BaseOutputPinImpl_EndFlush,
BasePinImpl_NewSegment
};

200
dll/directx/wine/qcap/yuv.c Normal file
View file

@ -0,0 +1,200 @@
/* DirectShow capture services (QCAP.DLL)
*
* Copyright 2005 Maarten Lankhorst
*
* This file contains the part of the vfw capture interface that
* does the actual Video4Linux(1/2) stuff required for capturing
* and setting/getting media format..
*
* 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 <stdarg.h>
#include "windef.h"
#include "wingdi.h"
#include "objbase.h"
#include "strmif.h"
#include "qcap_main.h"
#include "wine/debug.h"
/* This is not used if V4L support is missing */
#if defined(HAVE_LINUX_VIDEODEV_H) || defined(HAVE_LIBV4L1_H)
WINE_DEFAULT_DEBUG_CHANNEL(qcap);
static int yuv_xy[256]; /* Gray value */
static int yuv_gu[256]; /* Green U */
static int yuv_bu[256]; /* Blue U */
static int yuv_rv[256]; /* Red V */
static int yuv_gv[256]; /* Green V */
static BOOL initialised = FALSE;
static inline int ValidRange(int in) {
if (in > 255) in = 255;
if (in < 0) in = 0;
return in;
}
typedef struct RGB {
#if 0 /* For some reason I have to revert R and B, not sure why */
unsigned char r, g, b;
#else
unsigned char b, g, r;
#endif
} RGB;
static inline void YUV2RGB(const unsigned char y_, const unsigned char cb, const unsigned char cr, RGB* retval) {
retval->r = ValidRange(yuv_xy[y_] + yuv_rv[cr]);
retval->g = ValidRange(yuv_xy[y_] + yuv_gu[cb] + yuv_gv[cr]);
retval->b = ValidRange(yuv_xy[y_] + yuv_bu[cb]);
}
void YUV_Init(void) {
float y, u, v;
int y_, cb, cr;
if (initialised) return;
initialised = TRUE;
for (y_ = 0; y_ <= 255; y_++)
{
y = ((float) 255 / 219) * (y_ - 16);
yuv_xy[y_] = y;
}
for (cb = 0; cb <= 255; cb++)
{
u = ((float) 255 / 224) * (cb - 128);
yuv_gu[cb] = -0.344 * u;
yuv_bu[cb] = 1.772 * u;
}
for (cr = 0; cr <= 255; cr++)
{
v = ((float) 255 / 224) * (cr - 128);
yuv_rv[cr] = 1.402 * v;
yuv_gv[cr] = -0.714 * v;
}
TRACE("Filled hash table\n");
}
static void Parse_YUYV(unsigned char *destbuffer, const unsigned char *input, int width, int height)
{
const unsigned char *pY, *pCb, *pCr;
int togo = width * height / 2;
pY = input;
pCb = input+1;
pCr = input+3;
while (--togo) {
YUV2RGB(*pY, *pCb, *pCr, (RGB *)destbuffer);
pY += 2; destbuffer += 3;
YUV2RGB(*pY, *pCb, *pCr, (RGB *)destbuffer);
pY += 2; pCb += 4; pCr += 4; destbuffer += 3;
}
}
static void Parse_UYVY(unsigned char *destbuffer, const unsigned char *input, int width, int height)
{
const unsigned char *pY, *pCb, *pCr;
int togo = width * height / 2;
pY = input+1;
pCb = input;
pCr = input+2;
while (--togo) {
YUV2RGB(*pY, *pCb, *pCr, (RGB *)destbuffer);
pY += 2; destbuffer += 3;
YUV2RGB(*pY, *pCb, *pCr, (RGB *)destbuffer);
pY += 2; pCb += 4; pCr += 4; destbuffer += 3;
}
}
static void Parse_UYYVYY(unsigned char *destbuffer, const unsigned char *input, int width, int height)
{
const unsigned char *pY, *pCb, *pCr;
int togo = width * height / 4;
pY = input+1;
pCb = input;
pCr = input+4;
while (--togo) {
YUV2RGB(*pY, *pCb, *pCr, (RGB *)destbuffer);
destbuffer += 3; pY++;
YUV2RGB(*pY, *pCb, *pCr, (RGB *)destbuffer);
pY += 2; destbuffer += 3;
YUV2RGB(*pY, *pCb, *pCr, (RGB *)destbuffer);
destbuffer += 3; pY++;
YUV2RGB(*pY, *pCb, *pCr, (RGB *)destbuffer);
pY += 2; pCb += 6; pCr += 6; destbuffer += 3;
}
}
static void Parse_PYUV(unsigned char *destbuffer, const unsigned char *input, int width, int height, int wstep, int hstep)
{
/* We have 3 pointers, One to Y, one to Cb and 1 to Cr */
/* C19 *89* declaration block (Grr julliard for not allowing C99) */
int ysize, uvsize;
const unsigned char *pY, *pCb, *pCr;
int swstep = 0, shstep = 0;
int ypos = 0, xpos = 0;
int indexUV = 0, cUv;
/* End of Grr */
ysize = width * height;
uvsize = (width / wstep) * (height / hstep);
pY = input;
pCb = pY + ysize;
pCr = pCb + uvsize;
/* Bottom down DIB */
do {
swstep = 0;
cUv = indexUV;
for (xpos = 0; xpos < width; xpos++) {
YUV2RGB(*(pY++), pCb[cUv], pCr[cUv], (RGB *)destbuffer);
destbuffer += 3;
if (++swstep == wstep) {
cUv++;
swstep = 0;
}
}
if (++shstep == hstep) {
shstep = 0;
indexUV = cUv;
}
} while (++ypos < height);
}
void YUV_To_RGB24(enum YUV_Format format, unsigned char *target, const unsigned char *source, int width, int height) {
int wstep, hstep;
if (format < ENDPLANAR) {
switch (format) {
case YUVP_421: wstep = 2; hstep = 1; break;
case YUVP_422: wstep = 2; hstep = 2; break;
case YUVP_441: wstep = 4; hstep = 1; break;
case YUVP_444: wstep = 4; hstep = 4; break;
default: ERR("Unhandled format \"%d\"\n", format); return;
}
Parse_PYUV(target, source, width, height, wstep, hstep);
} else {
switch (format) {
case YUYV: Parse_YUYV(target, source, width, height); return;
case UYVY: Parse_UYVY(target, source, width, height); return;
case UYYVYY: Parse_UYYVYY(target, source, width, height); return;
default: ERR("Unhandled format \"%d\"\n", format); return;
}
}
}
#endif

View file

@ -39,6 +39,7 @@ dll/directx/wine/dpnhpast # Synced to WineStaging-4.18
dll/directx/wine/dsound # Synced to Wine-1.3.29
dll/directx/wine/dxdiagn # Synced to WineStaging-4.18
dll/directx/wine/msdmo # Synced to WineStaging-4.18
dll/directx/wine/qcap # Synced to WineStaging-3.3
dll/directx/wine/qedit # Synced to WineStaging-3.17
dll/directx/wine/quartz # Synced to WineStaging-3.9
dll/directx/wine/wined3d # Synced to WineStaging-3.3