453 lines
13 KiB
C++
453 lines
13 KiB
C++
/********************************************************************************
|
|
Copyright (C) 2012 Hugh Bailey <obs.jim@gmail.com>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
********************************************************************************/
|
|
|
|
|
|
#include "DShowPlugin.h"
|
|
|
|
|
|
#define FILTER_NAME L"Capture Filter"
|
|
#define VIDEO_PIN_NAME L"Video Capture"
|
|
#define AUDIO_PIN_NAME L"Audio Capture"
|
|
|
|
|
|
//========================================================================================================
|
|
|
|
CapturePin::CapturePin(CaptureFilter* filterIn, DeviceSource *sourceIn, const GUID &expectedMajorType, const GUID &expectedMediaType)
|
|
: filter(filterIn), source(sourceIn), refCount(1)
|
|
{
|
|
connectedMediaType.majortype = expectedMajorType;
|
|
connectedMediaType.subtype = GUID_NULL;
|
|
connectedMediaType.pbFormat = NULL;
|
|
connectedMediaType.cbFormat = 0;
|
|
connectedMediaType.pUnk = NULL;
|
|
this->expectedMediaType = expectedMediaType;
|
|
this->expectedMajorType = expectedMajorType;
|
|
}
|
|
|
|
CapturePin::~CapturePin()
|
|
{
|
|
FreeMediaType(connectedMediaType);
|
|
}
|
|
|
|
STDMETHODIMP CapturePin::QueryInterface(REFIID riid, void **ppv)
|
|
{
|
|
if(riid == IID_IUnknown || riid == IID_IPin)
|
|
{
|
|
AddRef();
|
|
*ppv = (IPin*)this;
|
|
return NOERROR;
|
|
}
|
|
else if(riid == IID_IMemInputPin)
|
|
{
|
|
AddRef();
|
|
*ppv = (IMemInputPin*)this;
|
|
return NOERROR;
|
|
}
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CapturePin::AddRef() {return ++refCount;}
|
|
STDMETHODIMP_(ULONG) CapturePin::Release() {if(!InterlockedDecrement(&refCount)) {delete this; return 0;} return refCount;}
|
|
|
|
// IPin methods
|
|
STDMETHODIMP CapturePin::Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
|
|
{
|
|
if(filter->state == State_Running)
|
|
return VFW_E_NOT_STOPPED;
|
|
if(connectedPin)
|
|
return VFW_E_ALREADY_CONNECTED;
|
|
if(!pmt)
|
|
return S_OK;
|
|
if(pmt->majortype != GUID_NULL && pmt->majortype != expectedMajorType)
|
|
return S_FALSE;
|
|
if(pmt->majortype == expectedMajorType && !IsValidMediaType(pmt))
|
|
return S_FALSE;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CapturePin::ReceiveConnection(IPin *pConnector, const AM_MEDIA_TYPE *pmt)
|
|
{
|
|
if(filter->state != State_Stopped)
|
|
return VFW_E_NOT_STOPPED;
|
|
if(!pConnector || !pmt)
|
|
return E_POINTER;
|
|
if(connectedPin)
|
|
return VFW_E_ALREADY_CONNECTED;
|
|
|
|
if(QueryAccept(pmt) != S_OK)
|
|
return VFW_E_TYPE_NOT_ACCEPTED;
|
|
|
|
connectedPin = pConnector;
|
|
connectedPin->AddRef();
|
|
|
|
FreeMediaType(connectedMediaType);
|
|
return CopyMediaType(&connectedMediaType, pmt);
|
|
}
|
|
|
|
STDMETHODIMP CapturePin::Disconnect()
|
|
{
|
|
if(!connectedPin)
|
|
return S_FALSE;
|
|
|
|
connectedPin->Release();
|
|
connectedPin = NULL;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CapturePin::ConnectedTo(IPin **pPin)
|
|
{
|
|
if(!connectedPin)
|
|
return VFW_E_NOT_CONNECTED;
|
|
|
|
connectedPin->AddRef();
|
|
*pPin = connectedPin;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CapturePin::ConnectionMediaType(AM_MEDIA_TYPE *pmt)
|
|
{
|
|
if(!connectedPin)
|
|
return VFW_E_NOT_CONNECTED;
|
|
|
|
return CopyMediaType(pmt, &connectedMediaType);
|
|
}
|
|
|
|
STDMETHODIMP CapturePin::QueryPinInfo(PIN_INFO *pInfo)
|
|
{
|
|
pInfo->pFilter = filter;
|
|
if(filter) filter->AddRef();
|
|
|
|
if(expectedMajorType == MEDIATYPE_Video)
|
|
mcpy(pInfo->achName, VIDEO_PIN_NAME, sizeof(VIDEO_PIN_NAME));
|
|
else
|
|
mcpy(pInfo->achName, AUDIO_PIN_NAME, sizeof(AUDIO_PIN_NAME));
|
|
|
|
pInfo->dir = PINDIR_INPUT;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP CapturePin::QueryDirection(PIN_DIRECTION *pPinDir) {*pPinDir = PINDIR_INPUT; return NOERROR;}
|
|
STDMETHODIMP CapturePin::QueryId(LPWSTR *lpId) {*lpId = L"Capture Pin"; return S_OK;}
|
|
STDMETHODIMP CapturePin::QueryAccept(const AM_MEDIA_TYPE *pmt)
|
|
{
|
|
if(pmt->majortype != expectedMajorType)
|
|
return S_FALSE;
|
|
if(!IsValidMediaType(pmt))
|
|
return S_FALSE;
|
|
|
|
if(connectedPin)
|
|
{
|
|
FreeMediaType(connectedMediaType);
|
|
CopyMediaType(&connectedMediaType, pmt);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CapturePin::EnumMediaTypes(IEnumMediaTypes **ppEnum)
|
|
{
|
|
*ppEnum = new CaptureEnumMediaTypes(this, NULL);
|
|
if(!*ppEnum)
|
|
return E_OUTOFMEMORY;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP CapturePin::QueryInternalConnections(IPin **apPin, ULONG *nPin) {return E_NOTIMPL;}
|
|
STDMETHODIMP CapturePin::EndOfStream() {return S_OK;}
|
|
STDMETHODIMP CapturePin::BeginFlush()
|
|
{
|
|
source->FlushSamples();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CapturePin::EndFlush()
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CapturePin::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) {return S_OK;}
|
|
|
|
// IMemInputPin methods
|
|
STDMETHODIMP CapturePin::GetAllocator(IMemAllocator **ppAllocator) {return VFW_E_NO_ALLOCATOR;}
|
|
STDMETHODIMP CapturePin::NotifyAllocator(IMemAllocator *pAllocator, BOOL bReadOnly) {return S_OK;}
|
|
STDMETHODIMP CapturePin::GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps) {return E_NOTIMPL;}
|
|
|
|
STDMETHODIMP CapturePin::Receive(IMediaSample *pSample)
|
|
{
|
|
if(pSample)
|
|
{
|
|
if(expectedMajorType == MEDIATYPE_Video)
|
|
source->ReceiveMediaSample(pSample, false);
|
|
else if(expectedMajorType == MEDIATYPE_Audio)
|
|
source->ReceiveMediaSample(pSample, true);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CapturePin::ReceiveMultiple(IMediaSample **pSamples, long nSamples, long *nSamplesProcessed)
|
|
{
|
|
for(long i=0; i<nSamples; i++)
|
|
Receive(pSamples[i]);
|
|
*nSamplesProcessed = nSamples;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CapturePin::ReceiveCanBlock() {return S_OK;}
|
|
|
|
bool CapturePin::IsValidMediaType(const AM_MEDIA_TYPE *pmt) const
|
|
{
|
|
if(pmt->pbFormat)
|
|
{
|
|
if(pmt->subtype != expectedMediaType || pmt->majortype != expectedMajorType)
|
|
return false;
|
|
|
|
if(expectedMajorType == MEDIATYPE_Video)
|
|
{
|
|
BITMAPINFOHEADER *bih = GetVideoBMIHeader(pmt);
|
|
|
|
if( bih->biHeight == 0 || bih->biWidth == 0)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//========================================================================================================
|
|
|
|
class CaptureFlags : public IAMFilterMiscFlags {
|
|
long refCount = 1;
|
|
public:
|
|
inline CaptureFlags() {}
|
|
STDMETHODIMP_(ULONG) AddRef() {return ++refCount;}
|
|
STDMETHODIMP_(ULONG) Release() {if (!--refCount) {delete this; return 0;} return refCount;}
|
|
STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
|
|
{
|
|
if (riid == IID_IUnknown)
|
|
{
|
|
AddRef();
|
|
*ppv = (void*)this;
|
|
return NOERROR;
|
|
}
|
|
|
|
*ppv = nullptr;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) GetMiscFlags() {return AM_FILTER_MISC_FLAGS_IS_RENDERER;}
|
|
};
|
|
|
|
CaptureFilter::CaptureFilter(DeviceSource *source, const GUID &expectedMajorType, const GUID &expectedMediaType)
|
|
: state(State_Stopped), refCount(1)
|
|
{
|
|
pin = new CapturePin(this, source, expectedMajorType, expectedMediaType);
|
|
flags = new CaptureFlags();
|
|
}
|
|
|
|
CaptureFilter::~CaptureFilter()
|
|
{
|
|
pin->Release();
|
|
flags->Release();
|
|
}
|
|
|
|
// IUnknown methods
|
|
STDMETHODIMP CaptureFilter::QueryInterface(REFIID riid, void **ppv)
|
|
{
|
|
if(riid == IID_IUnknown)
|
|
{
|
|
AddRef();
|
|
*ppv = (IUnknown*)this;
|
|
return NOERROR;
|
|
}
|
|
else if(riid == IID_IPersist)
|
|
{
|
|
AddRef();
|
|
*ppv = (IPersist*)this;
|
|
return NOERROR;
|
|
}
|
|
else if(riid == IID_IMediaFilter)
|
|
{
|
|
AddRef();
|
|
*ppv = (IMediaFilter*)this;
|
|
return NOERROR;
|
|
}
|
|
else if(riid == IID_IBaseFilter)
|
|
{
|
|
AddRef();
|
|
*ppv = (IBaseFilter*)this;
|
|
return NOERROR;
|
|
}
|
|
else if (riid == IID_IAMFilterMiscFlags)
|
|
{
|
|
flags->AddRef();
|
|
*ppv = flags;
|
|
return NOERROR;
|
|
}
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CaptureFilter::AddRef() {return ++refCount;}
|
|
STDMETHODIMP_(ULONG) CaptureFilter::Release() {if(!InterlockedDecrement(&refCount)) {delete this; return 0;} return refCount;}
|
|
|
|
// IPersist method
|
|
STDMETHODIMP CaptureFilter::GetClassID(CLSID *pClsID) {return E_NOTIMPL;}
|
|
|
|
// IMediaFilter methods
|
|
STDMETHODIMP CaptureFilter::GetState(DWORD dwMSecs, FILTER_STATE *State) {*State = state; return S_OK;}
|
|
STDMETHODIMP CaptureFilter::SetSyncSource(IReferenceClock *pClock) {return S_OK;}
|
|
STDMETHODIMP CaptureFilter::GetSyncSource(IReferenceClock **pClock) {*pClock = NULL; return NOERROR;}
|
|
STDMETHODIMP CaptureFilter::Stop() {pin->EndFlush(); state = State_Stopped; return S_OK;}
|
|
STDMETHODIMP CaptureFilter::Pause() {state = State_Paused; return S_OK;}
|
|
STDMETHODIMP CaptureFilter::Run(REFERENCE_TIME tStart) {state = State_Running; return S_OK;}
|
|
|
|
// IBaseFilter methods
|
|
STDMETHODIMP CaptureFilter::EnumPins(IEnumPins **ppEnum)
|
|
{
|
|
*ppEnum = new CaptureEnumPins(this, NULL);
|
|
return (*ppEnum == NULL) ? E_OUTOFMEMORY : NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP CaptureFilter::FindPin(LPCWSTR Id, IPin **ppPin) {return E_NOTIMPL;}
|
|
STDMETHODIMP CaptureFilter::QueryFilterInfo(FILTER_INFO *pInfo)
|
|
{
|
|
mcpy(pInfo->achName, FILTER_NAME, sizeof(FILTER_NAME));
|
|
|
|
pInfo->pGraph = graph;
|
|
if(graph) graph->AddRef();
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP CaptureFilter::JoinFilterGraph(IFilterGraph *pGraph, LPCWSTR pName) {graph = pGraph; return NOERROR;}
|
|
STDMETHODIMP CaptureFilter::QueryVendorInfo(LPWSTR *pVendorInfo) {return E_NOTIMPL;}
|
|
|
|
|
|
//========================================================================================================
|
|
|
|
CaptureEnumPins::CaptureEnumPins(CaptureFilter *filterIn, CaptureEnumPins *pEnum)
|
|
: filter(filterIn), refCount(1)
|
|
{
|
|
filter->AddRef();
|
|
curPin = (pEnum != NULL) ? pEnum->curPin : 0;
|
|
}
|
|
|
|
CaptureEnumPins::~CaptureEnumPins()
|
|
{
|
|
filter->Release();
|
|
}
|
|
|
|
// IUnknown
|
|
STDMETHODIMP CaptureEnumPins::QueryInterface(REFIID riid, void **ppv)
|
|
{
|
|
if(riid == IID_IUnknown || riid == IID_IEnumPins)
|
|
{
|
|
AddRef();
|
|
*ppv = (IEnumPins*)this;
|
|
return NOERROR;
|
|
}
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CaptureEnumPins::AddRef() {return ++refCount;}
|
|
STDMETHODIMP_(ULONG) CaptureEnumPins::Release() {if(!InterlockedDecrement(&refCount)) {delete this; return 0;} return refCount;}
|
|
|
|
// IEnumPins
|
|
STDMETHODIMP CaptureEnumPins::Next(ULONG cPins, IPin **ppPins, ULONG *pcFetched)
|
|
{
|
|
UINT nFetched = 0;
|
|
|
|
if(curPin == 0 && cPins > 0)
|
|
{
|
|
IPin *pPin = filter->GetCapturePin();
|
|
*ppPins = pPin;
|
|
pPin->AddRef();
|
|
nFetched = 1;
|
|
curPin++;
|
|
}
|
|
|
|
if(pcFetched) *pcFetched = nFetched;
|
|
|
|
return (nFetched == cPins) ? S_OK : S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP CaptureEnumPins::Skip(ULONG cPins) {return (++curPin > 1) ? S_FALSE : S_OK;}
|
|
STDMETHODIMP CaptureEnumPins::Reset() {curPin = 0; return S_OK;}
|
|
STDMETHODIMP CaptureEnumPins::Clone(IEnumPins **ppEnum)
|
|
{
|
|
*ppEnum = new CaptureEnumPins(filter, this);
|
|
return (*ppEnum == NULL) ? E_OUTOFMEMORY : NOERROR;
|
|
}
|
|
|
|
|
|
//========================================================================================================
|
|
|
|
CaptureEnumMediaTypes::CaptureEnumMediaTypes(CapturePin *pinIn, CaptureEnumMediaTypes *pEnum)
|
|
: pin(pinIn), refCount(1)
|
|
{
|
|
pin->AddRef();
|
|
}
|
|
|
|
CaptureEnumMediaTypes::~CaptureEnumMediaTypes()
|
|
{
|
|
pin->Release();
|
|
}
|
|
|
|
STDMETHODIMP CaptureEnumMediaTypes::QueryInterface(REFIID riid, void **ppv)
|
|
{
|
|
if(riid == IID_IUnknown || riid == IID_IEnumMediaTypes)
|
|
{
|
|
AddRef();
|
|
*ppv = (IEnumMediaTypes*)this;
|
|
return NOERROR;
|
|
}
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CaptureEnumMediaTypes::AddRef() {return ++refCount;}
|
|
STDMETHODIMP_(ULONG) CaptureEnumMediaTypes::Release() {if(!InterlockedDecrement(&refCount)) {delete this; return 0;} return refCount;}
|
|
|
|
// IEnumMediaTypes
|
|
STDMETHODIMP CaptureEnumMediaTypes::Next(ULONG cMediaTypes, AM_MEDIA_TYPE **ppMediaTypes, ULONG *pcFetched) {return S_FALSE;}
|
|
STDMETHODIMP CaptureEnumMediaTypes::Skip(ULONG cMediaTypes) {return S_FALSE;}
|
|
STDMETHODIMP CaptureEnumMediaTypes::Reset() {return S_OK;}
|
|
STDMETHODIMP CaptureEnumMediaTypes::Clone(IEnumMediaTypes **ppEnum)
|
|
{
|
|
*ppEnum = new CaptureEnumMediaTypes(pin, this);
|
|
return (*ppEnum == NULL) ? E_OUTOFMEMORY : NOERROR;
|
|
}
|