tenfourfox/media/webrtc/trunk/webrtc/modules/video_capture/windows/BaseFilter.cpp
Cameron Kaiser c9b2922b70 hello FPR
2017-04-19 00:56:45 -07:00

479 lines
9.2 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <assert.h>
#include "BaseFilter.h"
#include "BasePin.h"
namespace mozilla {
namespace media {
#define NS_IF_ADDREF(expr) \
if (expr) { \
(expr)->AddRef(); \
}
#define NS_IF_RELEASE(expr) \
if (expr) { \
(expr)->Release(); \
}
// Used by BaseFilter to enumerate pins on a DirectShow filter.
// If the number of pins on the filter changes while an EnumPins
// is enumerating pins on a filter, methods start failing with
// return value VFW_E_ENUM_OUT_OF_SYNC. Really only an issue when
// doing dynamic filter reconnections.
//
// Implements:
// * IEnumPins
// * IUnknown
//
class DECLSPEC_UUID("0e9924bd-1cb8-48ad-ba31-2fb831b162be")
EnumPins : public IEnumPins
{
public:
EnumPins(BaseFilter* aFilter)
: mFilter(aFilter)
, mRefCnt(0)
{
Reset();
}
EnumPins(EnumPins* aEnumPins)
: mFilter(aEnumPins->mFilter)
, mNumPins(aEnumPins->mNumPins)
, mPinIdx(aEnumPins->mPinIdx)
, mRefCnt(0)
{
}
virtual ~EnumPins() {}
STDMETHODIMP QueryInterface(REFIID aIId, void **aInterface)
{
if (!aInterface) {
return E_POINTER;
}
if (aIId == IID_IEnumPins) {
*aInterface = static_cast<IEnumPins*>(this);
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) AddRef()
{
return ::InterlockedIncrement(&mRefCnt);
}
STDMETHODIMP_(ULONG) Release()
{
unsigned long newRefCnt = ::InterlockedDecrement(&mRefCnt);
if (!newRefCnt) {
delete this;
}
return newRefCnt;
}
// IEnumPins methods.
STDMETHODIMP Next(ULONG aNumPins,
IPin** aPinArray,
ULONG* aNumFetched)
{
if (!aPinArray)
return E_POINTER;
if (!aNumFetched && aNumPins != 1)
return E_INVALIDARG;
if (IsOutOfSync())
return VFW_E_ENUM_OUT_OF_SYNC;
unsigned int numFetched = 0;
while (numFetched < aNumPins && mPinIdx < mNumPins) {
IPinPtr pin = mFilter->GetPin(mPinIdx);
aPinArray[numFetched] = pin;
assert(aPinArray[numFetched] != NULL);
if (!aPinArray[numFetched]) {
// Failure, release all pins.
for (unsigned int i=0; i<numFetched; i++) {
NS_IF_RELEASE(aPinArray[i]);
}
return VFW_E_ENUM_OUT_OF_SYNC;
}
NS_IF_ADDREF(aPinArray[numFetched]);
mPinIdx++;
numFetched++;
}
if (aNumFetched)
*aNumFetched = numFetched;
return (aNumPins == numFetched) ? S_OK : S_FALSE;
}
STDMETHODIMP Skip(ULONG aNumPins)
{
if (IsOutOfSync())
return VFW_E_ENUM_OUT_OF_SYNC;
if ((mPinIdx + aNumPins) > mNumPins)
return S_FALSE;
mPinIdx += aNumPins;
return S_OK;
}
STDMETHODIMP Reset()
{
mNumPins = mFilter->GetPinCount();
mPinIdx = 0;
return S_OK;
}
STDMETHODIMP Clone(IEnumPins** aEnum)
{
if (!aEnum)
return E_POINTER;
if (IsOutOfSync())
return VFW_E_ENUM_OUT_OF_SYNC;
EnumPins *p = new EnumPins(this);
*aEnum = p;
NS_IF_ADDREF(p);
return S_OK;
}
private:
bool IsOutOfSync() {
return mNumPins != mFilter->GetPinCount();
}
BaseFilterPtr mFilter;
unsigned int mNumPins;
unsigned int mPinIdx;
unsigned long mRefCnt;
};
_COM_SMARTPTR_TYPEDEF(IMediaEventSink, __uuidof(IMediaEventSink));
BaseFilter::BaseFilter(const wchar_t* aName,
REFCLSID aClsID)
: mClsId(aClsID)
, mState(State_Stopped)
, mLock("BaseFilter::mLock")
, mRefCnt(0)
{
mName = aName;
assert(mName == aName);
}
STDMETHODIMP BaseFilter::QueryInterface(REFIID riid,
void** aInterface)
{
if (!aInterface) {
return E_POINTER;
}
if (riid == IID_IBaseFilter || riid == IID_IUnknown) {
*aInterface = static_cast<IBaseFilter*>(this);
} else if (riid == IID_IMediaFilter) {
*aInterface = static_cast<IMediaFilter*>(this);
} else if (riid == IID_IPersist) {
*aInterface = static_cast<IPersist*>(this);
} else {
*aInterface = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
STDMETHODIMP
BaseFilter::GetClassID(CLSID* aClsId)
{
if (!aClsId)
return E_POINTER;
*aClsId = mClsId;
return NOERROR;
}
STDMETHODIMP
BaseFilter::GetState(DWORD, FILTER_STATE* aState)
{
if (!aState)
return E_POINTER;
*aState = mState;
return S_OK;
}
STDMETHODIMP
BaseFilter::SetSyncSource(IReferenceClock* aClock)
{
CriticalSectionAutoEnter monitor(mLock);
mClock = aClock;
return NOERROR;
}
STDMETHODIMP
BaseFilter::GetSyncSource(IReferenceClock** aClock)
{
if (!aClock)
return E_POINTER;
CriticalSectionAutoEnter monitor(mLock);
if (mClock) {
// returning an interface... addref it...
mClock->AddRef();
}
*aClock = mClock;
return NOERROR;
}
// Stop deactivates any active, connected pins on this filter.
STDMETHODIMP
BaseFilter::Stop()
{
CriticalSectionAutoEnter monitor(mLock);
HRESULT retval = S_OK;
if (mState == State_Stopped)
return S_OK;
int numPins = GetPinCount();
for (int i = 0; i < numPins; i++) {
BasePin* pin = GetPin(i);
if (NULL == pin) {
continue;
}
if (pin->IsConnected()) {
HRESULT hr = pin->Inactive();
if (FAILED(hr) && SUCCEEDED(retval)) {
retval = hr;
}
}
}
mState = State_Stopped;
return retval;
}
// Moves the filter into the paused state. If the graph is stopped, notify all
// the connected pins that they're active.
STDMETHODIMP
BaseFilter::Pause()
{
CriticalSectionAutoEnter monitor(mLock);
if (mState == State_Stopped) {
int numPins = GetPinCount();
for (int i = 0; i < numPins; i++) {
BasePin* pin = GetPin(i);
if (NULL == pin) {
break;
}
if (pin->IsConnected()) {
HRESULT hr = pin->Active();
if (FAILED(hr)) {
return hr;
}
}
}
}
mState = State_Paused;
return S_OK;
}
// Moves the filter into a running state. aStartTime is the offset to be added
// to the samples' stream time in order for the samples to appear in stream time.
STDMETHODIMP
BaseFilter::Run(REFERENCE_TIME aStartTime)
{
CriticalSectionAutoEnter monitor(mLock);
mStartTime = aStartTime;
if (mState == State_Running) {
return S_OK;
}
// First pause the filter if it's stopped.
if (mState == State_Stopped) {
HRESULT hr = Pause();
if (FAILED(hr)) {
return hr;
}
}
// Start all connected pins.
int numPins = GetPinCount();
for (int i = 0; i < numPins; i++) {
BasePin* pin = GetPin(i);
assert(pin != NULL);
if (!pin) {
continue;
}
if (pin->IsConnected()) {
HRESULT hr = pin->Run(aStartTime);
if (FAILED(hr)) {
return hr;
}
}
}
mState = State_Running;
return S_OK;
}
STDMETHODIMP
BaseFilter::EnumPins(IEnumPins** aEnum)
{
if (!aEnum)
return E_POINTER;
*aEnum = new mozilla::media::EnumPins(this);
if (!(*aEnum))
return E_OUTOFMEMORY;
NS_IF_ADDREF(*aEnum);
return S_OK;
}
STDMETHODIMP
BaseFilter::FindPin(LPCWSTR aId,
IPin** aPin)
{
if (!aPin)
return E_POINTER;
*aPin = NULL;
CriticalSectionAutoEnter monitor(mLock);
int numPins = GetPinCount();
for (int i = 0; i < numPins; i++) {
BasePin* pin = GetPin(i);
if (NULL == pin) {
assert(pin != NULL);
return VFW_E_NOT_FOUND;
}
if (!pin->Name().compare(aId)) {
// Found a pin with a matching name, AddRef() and return it.
*aPin = pin;
NS_IF_ADDREF(pin);
return S_OK;
}
}
return VFW_E_NOT_FOUND;
}
STDMETHODIMP
BaseFilter::QueryFilterInfo(FILTER_INFO* aInfo)
{
if (!aInfo)
return E_POINTER;
if (!mName.empty()) {
StringCchCopyW(aInfo->achName, NUMELMS(aInfo->achName), mName.data());
} else {
aInfo->achName[0] = L'\0';
}
aInfo->pGraph = mGraph;
NS_IF_ADDREF(mGraph);
return S_OK;
}
STDMETHODIMP
BaseFilter::JoinFilterGraph(IFilterGraph* aGraph,
LPCWSTR aName)
{
CriticalSectionAutoEnter monitor(mLock);
mGraph = aGraph;
IMediaEventSinkPtr sink = mGraph;
// Store weak ref to event sink. Graph holds strong reg to us, so if we hold
// strong ref to its event sink we'll create a cycle.
mEventSink = sink;
if (aGraph) {
mName = aName;
} else {
mName.resize(0);
}
return S_OK;
}
HRESULT
BaseFilter::NotifyEvent(long aEventCode,
LONG_PTR aEventParam1,
LONG_PTR aEventParam2)
{
IMediaEventSink* sink = mEventSink;
if (sink) {
if (EC_COMPLETE == aEventCode) {
aEventParam2 = (LONG_PTR)(static_cast<IBaseFilter*>(this));
}
return sink->Notify(aEventCode, aEventParam1, aEventParam2);
}
return E_NOTIMPL;
}
STDMETHODIMP_(ULONG)
BaseFilter::AddRef()
{
return ::InterlockedIncrement(&mRefCnt);
}
STDMETHODIMP_(ULONG)
BaseFilter::Release()
{
unsigned long newRefCnt = ::InterlockedDecrement(&mRefCnt);
if (!newRefCnt) {
delete this;
}
return newRefCnt;
}
}
}