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

657 lines
14 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 "nsAutoPtr.h"
#include "BasePin.h"
namespace mozilla {
namespace media {
// Implements IEnumMediaTypes for nsBasePin::EnumMediaTypes().
// Does not support dynamic media types.
//
// Implements:
// * IEnumMediaTypes
// * IUnknown
//
class DECLSPEC_UUID("4de7a03c-6c3f-4314-949a-ee7e1ad05083")
EnumMediaTypes
: public IEnumMediaTypes
{
public:
EnumMediaTypes(BasePin* aPin)
: mPin(aPin)
, mIndex(0)
, mRefCnt(0)
{
}
EnumMediaTypes(EnumMediaTypes* aEnum)
: mPin(aEnum->mPin)
, mIndex(aEnum->mIndex)
, mRefCnt(0)
{
}
STDMETHODIMP QueryInterface(REFIID aIId, void **aInterface)
{
if (!aInterface) {
return E_POINTER;
}
if (aIId == IID_IEnumMediaTypes) {
*aInterface = static_cast<IEnumMediaTypes*>(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;
}
// IEnumMediaTypes
STDMETHODIMP Next(ULONG aCount,
AM_MEDIA_TYPE ** aMediaTypes,
ULONG * aNumFetched)
{
if (!aMediaTypes)
return E_POINTER;
if (aNumFetched) {
*aNumFetched = 0;
} else if (aCount > 1) {
// !aNumFetched && aCount>1, we can't report how many media types we read.
return E_INVALIDARG;
}
unsigned int numFetched = 0;
while (numFetched < aCount) {
// Get the media type
MediaType mediaType;
HRESULT hr = mPin->GetMediaType(mIndex++, &mediaType);
if (hr != S_OK) {
break;
}
// Create a copy of media type on the free store.
MediaType* m = new MediaType(mediaType);
if (!m)
return E_OUTOFMEMORY;
aMediaTypes[numFetched] = m;
if (!aMediaTypes[numFetched])
break;
numFetched++;
}
if (aNumFetched)
*aNumFetched = numFetched;
return (numFetched == aCount) ? S_OK : S_FALSE;
}
STDMETHODIMP Skip(ULONG aCount)
{
if (aCount == 0)
return S_OK;
// Advance the media type index by |aCount|.
mIndex += aCount;
// See if the new index is *past* the end by querying for a media type.
MediaType mediaType;
HRESULT hr = mPin->GetMediaType(mIndex-1, &mediaType);
// Forget mediaTypes pointers, so nsMediaType destructor it won't release them.
mediaType.Forget();
return (hr != S_OK) ? S_OK : S_FALSE;
}
STDMETHODIMP Reset()
{
mIndex = 0;
return S_OK;
}
STDMETHODIMP Clone(IEnumMediaTypes **aClone)
{
if (!aClone)
return E_POINTER;
EnumMediaTypes* e = new EnumMediaTypes(this);
if (!e)
return E_OUTOFMEMORY;
e->AddRef();
*aClone = static_cast<IEnumMediaTypes*>(e);
return S_OK;
}
private:
// Pin whose media types we are iterating.
BasePinPtr mPin;
// Index of our "iterator" through mPin's media types.
int mIndex;
unsigned long mRefCnt;
};
_COM_SMARTPTR_TYPEDEF(IEnumMediaTypes, __uuidof(IEnumMediaTypes));
BasePin::BasePin(BaseFilter* aFilter,
CriticalSection* aLock,
const wchar_t* aName,
PIN_DIRECTION aDirection)
: mFilter(aFilter)
, mLock(aLock)
, mName(aName)
, mDirection(aDirection)
, mQualitySink(NULL)
{
assert(aFilter != NULL);
assert(aLock != NULL);
}
STDMETHODIMP
BasePin::QueryInterface(REFIID aIID, void ** aInterface)
{
if (!aInterface) {
return E_POINTER;
}
if (aIID == IID_IPin || aIID == IID_IUnknown) {
*aInterface = static_cast<IPin*>(this);
} else if (aIID == IID_IQualityControl) {
*aInterface = static_cast<IQualityControl*>(this);
} else {
*aInterface = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
STDMETHODIMP
BasePin::QueryPinInfo(PIN_INFO * aInfo)
{
if (!aInfo)
return E_POINTER;
memset(aInfo, 0, sizeof(PIN_INFO));
aInfo->pFilter = mFilter;
if (mFilter) {
mFilter->AddRef();
}
if (!mName.empty()) {
// Copy at most (max_buffer_size - sizeof(WCHAR)). The -1 is there to
// ensure we always have a null terminator.
unsigned int len = PR_MIN((MAX_PIN_NAME-1)*sizeof(WCHAR), (sizeof(WCHAR)*mName.length()));
memcpy(aInfo->achName, mName.data(), len);
}
aInfo->dir = mDirection;
return NOERROR;
}
STDMETHODIMP
BasePin::QueryDirection(PIN_DIRECTION * aPinDir)
{
if (!aPinDir)
return E_POINTER;
*aPinDir = mDirection;
return S_OK;
}
STDMETHODIMP
BasePin::QueryId(LPWSTR * aId)
{
if (!aId)
return E_POINTER;
*aId = NULL;
WCHAR* str = NULL;
// Length not including null-terminator.
unsigned int sz = mName.length() * sizeof(WCHAR);
// Allocate memory for new string copy, plus 1 for null-terminator.
str = (LPWSTR)CoTaskMemAlloc(sz + sizeof(WCHAR));
if (!str)
return E_OUTOFMEMORY;
// Copy name.
memcpy(str, mName.data(), sz);
// Set null-terminator.
str[mName.length()] = 0;
*aId = str;
return S_OK;
}
STDMETHODIMP
BasePin::EnumMediaTypes(IEnumMediaTypes **aEnum)
{
if (!aEnum)
return E_POINTER;
*aEnum = new mozilla::media::EnumMediaTypes(this);
if (*aEnum == NULL)
return E_OUTOFMEMORY;
// Must addref, caller's responsibility to release.
NS_ADDREF(*aEnum);
return S_OK;
}
// Base class returns an error; we expect sub-classes to override this.
HRESULT
BasePin::GetMediaType(int, MediaType*)
{
return E_UNEXPECTED;
}
STDMETHODIMP
BasePin::QueryAccept(const AM_MEDIA_TYPE *aMediaType)
{
if (!aMediaType)
return E_POINTER;
// Defer to subclasses CheckMediaType() function. Map all errors to S_FALSE,
// so that we match the spec for QueryAccept().
if (FAILED(CheckMediaType((MediaType*)aMediaType)))
return S_FALSE;
return S_OK;
}
HRESULT
BasePin::Active(void)
{
return S_OK;
}
HRESULT
BasePin::Run(REFERENCE_TIME)
{
return S_OK;
}
HRESULT
BasePin::Inactive(void)
{
return S_OK;
}
STDMETHODIMP
BasePin::EndOfStream(void)
{
return S_OK;
}
STDMETHODIMP
BasePin::SetSink(IQualityControl* aQualitySink)
{
CriticalSectionAutoEnter monitor(*mLock);
mQualitySink = aQualitySink;
return S_OK;
}
STDMETHODIMP
BasePin::Notify(IBaseFilter *, Quality)
{
return E_NOTIMPL;
}
STDMETHODIMP
BasePin::NewSegment(REFERENCE_TIME aStartTime,
REFERENCE_TIME aStopTime,
double aRate)
{
return E_NOTIMPL;
}
// This pin attempts to connect to |aPin| with media type |aMediaType|.
// If |aMediaType| is fully specified, we must attempt to connect with
// that, else we just enumerate our types, then the other pin's type and
// try them, filtering them using |aMediaType| if it's paritally
// specificed. Used by Connect().
STDMETHODIMP
BasePin::Connect(IPin * aPin,
const AM_MEDIA_TYPE* aMediaType)
{
if (!aPin)
return E_POINTER;
CriticalSectionAutoEnter monitor(*mLock);
if (IsConnected())
return VFW_E_ALREADY_CONNECTED;
// Can't connect when filter is not stopped.
if (!IsStopped())
return VFW_E_NOT_STOPPED;
// Find a muatually acceptable media type. First try the media type
// suggested, then try our media types, then the other pin's media types.
const MediaType* mediaType = reinterpret_cast<const MediaType*>(aMediaType);
if (aMediaType && !mediaType->IsPartiallySpecified()) {
// Media type is supplied and not partially specified, we must try to
// connect with that.
return AttemptConnection(aPin, mediaType);
}
// Try this pin's media types...
IEnumMediaTypesPtr enumMediaTypes;
HRESULT hr = EnumMediaTypes(&enumMediaTypes);
assert(SUCCEEDED(hr));
if (enumMediaTypes) {
hr = TryMediaTypes(aPin, mediaType, enumMediaTypes);
if (SUCCEEDED(hr))
return S_OK;
}
// Can't connect with our media types, try other pins types...
enumMediaTypes = NULL;
hr = aPin->EnumMediaTypes(&enumMediaTypes);
assert(SUCCEEDED(hr));
if (enumMediaTypes) {
hr = TryMediaTypes(aPin, mediaType, enumMediaTypes);
if (SUCCEEDED(hr))
return S_OK;
}
// Nothing connects.
return VFW_E_NO_ACCEPTABLE_TYPES;
}
// Try the media types of |aEnum| when connecting to |aPin| with type limiter
// of |aFilter|. If |aFilter| is specified, we will only attempt to connect
// with media types from |aEnum| which partially match |aFilter|.
HRESULT
BasePin::TryMediaTypes(IPin *aPin,
const MediaType *aFilter,
IEnumMediaTypes *aEnum)
{
// Reset enumerator.
HRESULT hr = aEnum->Reset();
if (FAILED(hr))
return hr;
while (true) {
AM_MEDIA_TYPE* amt = NULL;
ULONG mediaCount = 0;
HRESULT hr = aEnum->Next(1, &amt, &mediaCount);
if (hr != S_OK) // No more media types...
return VFW_E_NO_ACCEPTABLE_TYPES;
assert(mediaCount == 1 && hr == S_OK);
MediaType* mediaType = reinterpret_cast<MediaType*>(amt);
if (!aFilter || mediaType->MatchesPartial(aFilter)) {
// Either there's no limiter, or we partially match it,
// attempt connection.
if (SUCCEEDED(AttemptConnection(aPin, mediaType)))
return S_OK;
}
}
}
// Checks if this pin can connect to |aPin|. We expect sub classes to
// override this method to support their own needs.
HRESULT
BasePin::CheckConnect(IPin* aPin)
{
PIN_DIRECTION otherPinsDirection;
aPin->QueryDirection(&otherPinsDirection);
// Can't connect pins with same direction together...
if (otherPinsDirection == mDirection) {
return VFW_E_INVALID_DIRECTION;
}
return S_OK;
}
// Called when we've made a connection to another pin. Returning failure
// triggers the caller to break the connection. Subclasses may want to
// override this.
HRESULT
BasePin::CompleteConnect(IPin *)
{
return S_OK;
}
// Sets the media type of the connection.
HRESULT
BasePin::SetMediaType(const MediaType *aMediaType)
{
return mMediaType.Assign(aMediaType);
}
STDMETHODIMP
BasePin::QueryInternalConnections(IPin**, ULONG*)
{
return E_NOTIMPL;
}
// This is called to release any resources needed for a connection.
HRESULT
BasePin::BreakConnect()
{
return S_OK;
}
STDMETHODIMP
BasePin::Disconnect()
{
CriticalSectionAutoEnter monitor(*mLock);
// Can't disconnect a non-stopped filter.
if (!IsStopped())
return VFW_E_NOT_STOPPED;
if (!IsConnected())
return S_FALSE;
HRESULT hr = BreakConnect();
mConnectedPin = NULL;
return hr;
}
// Attempt to connect this pin to |aPin| using given media type.
HRESULT
BasePin::AttemptConnection(IPin* aPin,
const MediaType* aMediaType)
{
CriticalSectionAutoEnter monitor(*mLock);
// Ensure we can connect to the other pin. Gives subclasses a chance
// to prevent connection.
HRESULT hr = CheckConnect(aPin);
if (FAILED(hr)) {
BreakConnect();
return hr;
}
// Ensure we can connect with this media type. This gives subclasses a
// chance to abort the connection.
hr = CheckMediaType(aMediaType);
if (FAILED(hr))
return hr;
hr = SetMediaType(aMediaType);
if (FAILED(hr))
return hr;
// Ask the other pin if it will accept a connection with our media type.
hr = aPin->ReceiveConnection(static_cast<IPin*>(this), aMediaType);
if (FAILED(hr))
return hr;
// Looks good so far, give subclass one final chance to refuse connection...
mConnectedPin = aPin;
hr = CompleteConnect(aPin);
if (FAILED(hr)) {
// Subclass refused the connection, inform the other pin that we're
// disconnecting, and break the connection.
aPin->Disconnect();
BreakConnect();
mConnectedPin = NULL;
mMediaType.Clear();
return VFW_E_TYPE_NOT_ACCEPTED;
}
// Otherwise, we're all good!
return S_OK;
}
STDMETHODIMP
BasePin::ReceiveConnection(IPin * aPin,
const AM_MEDIA_TYPE *aMediaType)
{
if (!aPin)
return E_POINTER;
if (!aMediaType)
E_POINTER;
CriticalSectionAutoEnter monitor(*mLock);
if (IsConnected())
return VFW_E_ALREADY_CONNECTED;
if (!IsStopped())
return VFW_E_NOT_STOPPED;
HRESULT hr = CheckConnect(aPin);
if (FAILED(hr)) {
BreakConnect();
return hr;
}
// See if subclass supports the specified media type.
const MediaType* mediaType = reinterpret_cast<const MediaType*>(aMediaType);
hr = CheckMediaType(mediaType);
if (FAILED(hr)) {
BreakConnect();
return hr;
}
// Supported, set it.
hr = SetMediaType(mediaType);
if (FAILED(hr))
return hr;
// Complete the connection.
mConnectedPin = aPin;
// Give the subclass one last chance to refuse the connection.
hr = CompleteConnect(aPin);
if (FAILED(hr)) {
// Subclass refused connection, fail...
mConnectedPin = NULL;
BreakConnect();
return hr;
}
// It's all good, we're connected.
return S_OK;
}
STDMETHODIMP
BasePin::ConnectedTo(IPin** aPin)
{
if (!aPin)
return E_POINTER;
if (!IsConnected())
return VFW_E_NOT_CONNECTED;
*aPin = mConnectedPin;
(*aPin)->AddRef();
return S_OK;
}
STDMETHODIMP
BasePin::ConnectionMediaType(AM_MEDIA_TYPE *aMediaType)
{
if (!aMediaType)
return E_POINTER;
CriticalSectionAutoEnter monitor(*mLock);
if (IsConnected()) {
reinterpret_cast<MediaType*>(aMediaType)->Assign(&mMediaType);
return S_OK;
} else {
memset(aMediaType, 0, sizeof(AM_MEDIA_TYPE));
return VFW_E_NOT_CONNECTED;
}
}
}
}