mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-06-25 20:29:33 +00:00
479 lines
9.2 KiB
C++
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;
|
|
}
|
|
|
|
}
|
|
}
|