628 lines
17 KiB
C++
628 lines
17 KiB
C++
//------------------------------------------------------------------------------
|
|
// File: Crossbar.cpp
|
|
//
|
|
// Desc: A class for controlling video crossbars.
|
|
//
|
|
// This class creates a single object which encapsulates all connected
|
|
// crossbars, enumerates all unique inputs which can be reached from
|
|
// a given starting pin, and automatically routes audio when a video
|
|
// source is selected.
|
|
//
|
|
// The class supports an arbitrarily complex graph of crossbars,
|
|
// which can be cascaded and disjoint, that is not all inputs need
|
|
// to traverse the same set of crossbars.
|
|
//
|
|
// Given a starting input pin (typically the analog video input to
|
|
// the capture filter), the class recursively traces upstream
|
|
// searching for all viable inputs. An input is considered viable if
|
|
// it is a video pin and is either:
|
|
//
|
|
// - unconnected
|
|
// - connects to a filter which does not support IAMCrossbar
|
|
//
|
|
// Methods:
|
|
//
|
|
// CCrossbar (IPin *pPin);
|
|
// ~CCrossbar();
|
|
//
|
|
// HRESULT GetInputCount (LONG *pCount);
|
|
// HRESULT GetInputType (LONG Index, LONG * PhysicalType);
|
|
// HRESULT GetInputName (LONG Index, TCHAR * pName, LONG NameSize);
|
|
// HRESULT SetInputIndex (LONG Index);
|
|
// HRESULT GetInputIndex (LONG *Index);
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include <streams.h>
|
|
|
|
#include "crossbar.h"
|
|
#include <strsafe.h>
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CCrossbar::CCrossbar()
|
|
// Desc: Constructor for the CCrossbar class
|
|
//------------------------------------------------------------------------------
|
|
CCrossbar::CCrossbar(
|
|
IPin *pStartingInputPin,
|
|
HRESULT *phr
|
|
)
|
|
: m_pStartingPin (pStartingInputPin)
|
|
, m_CurrentRoutingIndex (0)
|
|
, m_RoutingList (NULL)
|
|
|
|
{
|
|
HRESULT hr;
|
|
ASSERT(phr);
|
|
|
|
DbgLog((LOG_TRACE,3,TEXT("CCrossbar Constructor")));
|
|
|
|
ASSERT (pStartingInputPin != NULL);
|
|
|
|
// Init everything to zero
|
|
ZeroMemory (&m_RoutingRoot, sizeof (m_RoutingRoot));
|
|
|
|
m_RoutingList = new CRoutingList (TEXT("RoutingList"), 5);
|
|
|
|
if (m_RoutingList)
|
|
hr = BuildRoutingList(pStartingInputPin, &m_RoutingRoot, 0 /* Depth */);
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
// Return an error/success code from the constructor
|
|
if (phr)
|
|
*phr = hr;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CCrossbar::CCrossbar()
|
|
// Desc: Destructor for the CCrossbar class
|
|
//------------------------------------------------------------------------------
|
|
CCrossbar::~CCrossbar()
|
|
{
|
|
DbgLog((LOG_TRACE,3,TEXT("CCrossbar Destructor")));
|
|
|
|
HRESULT hr = DestroyRoutingList ();
|
|
|
|
delete m_RoutingList;
|
|
}
|
|
|
|
|
|
//
|
|
// This function is called recursively, every time a new crossbar is
|
|
// entered as we search upstream.
|
|
//
|
|
// Return values:
|
|
//
|
|
// S_OK - Returned on final exit after recursive search if at least
|
|
// one routing is possible
|
|
// S_FALSE - Normal return indicating we've reached the end of a
|
|
// recursive search, so save the current path
|
|
// E_FAIL - Unable to route anything
|
|
|
|
HRESULT CCrossbar::BuildRoutingList (
|
|
IPin *pStartingInputPin,
|
|
CRouting *pRouting,
|
|
int Depth
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
LONG InputIndexRelated, OutputIndexRelated;
|
|
LONG InputPhysicalType, OutputPhysicalType;
|
|
LONG Inputs, Outputs, InputIndex, OutputIndex;
|
|
|
|
IPin *pPin=0;
|
|
IPin *pStartingOutputPin=0;
|
|
PIN_INFO pinInfo;
|
|
CRouting RoutingNext;
|
|
IAMCrossbar *pXbar=0;
|
|
|
|
|
|
ASSERT (pStartingInputPin != NULL);
|
|
ASSERT (pRouting != NULL);
|
|
|
|
if (!pStartingInputPin || !pRouting)
|
|
return E_POINTER;
|
|
|
|
//
|
|
// If the pin isn't connected, then it's a terminal pin
|
|
//
|
|
|
|
hr = pStartingInputPin->ConnectedTo (&pStartingOutputPin);
|
|
if (hr != S_OK)
|
|
return (Depth == 0) ? E_FAIL : S_FALSE;
|
|
|
|
//
|
|
// It is connected, so now find out if the filter supports
|
|
// IAMCrossbar
|
|
//
|
|
|
|
if (S_OK == pStartingOutputPin->QueryPinInfo(&pinInfo))
|
|
{
|
|
ASSERT (pinInfo.dir == PINDIR_OUTPUT);
|
|
|
|
hr = pinInfo.pFilter->QueryInterface(IID_IAMCrossbar, (void **)&pXbar);
|
|
if (hr == S_OK)
|
|
{
|
|
EXECUTE_ASSERT (S_OK == pXbar->get_PinCounts(&Outputs, &Inputs));
|
|
|
|
EXECUTE_ASSERT (S_OK == GetCrossbarIndexFromIPin (
|
|
pXbar,
|
|
&OutputIndex,
|
|
FALSE, // Input ?
|
|
pStartingOutputPin));
|
|
|
|
EXECUTE_ASSERT (S_OK == pXbar->get_CrossbarPinInfo(
|
|
FALSE, // Input ?
|
|
OutputIndex,
|
|
&OutputIndexRelated,
|
|
&OutputPhysicalType));
|
|
|
|
//
|
|
// for all input pins
|
|
//
|
|
|
|
for (InputIndex = 0; InputIndex < Inputs; InputIndex++)
|
|
{
|
|
EXECUTE_ASSERT (S_OK == pXbar->get_CrossbarPinInfo(
|
|
TRUE, // Input?
|
|
InputIndex,
|
|
&InputIndexRelated,
|
|
&InputPhysicalType));
|
|
|
|
//
|
|
// Is the pin a video pin?
|
|
//
|
|
if (InputPhysicalType < PhysConn_Audio_Tuner)
|
|
{
|
|
//
|
|
// Can we route it?
|
|
//
|
|
if (S_OK == pXbar->CanRoute(OutputIndex, InputIndex))
|
|
{
|
|
|
|
EXECUTE_ASSERT (S_OK == GetCrossbarIPinAtIndex (
|
|
pXbar,
|
|
InputIndex,
|
|
TRUE, // Input
|
|
&pPin));
|
|
|
|
//
|
|
// We've found a route through this crossbar
|
|
// so save our state before recusively searching
|
|
// again.
|
|
//
|
|
ZeroMemory (&RoutingNext, sizeof (RoutingNext));
|
|
|
|
// doubly linked list
|
|
RoutingNext.pRightRouting = pRouting;
|
|
pRouting->pLeftRouting = &RoutingNext;
|
|
|
|
pRouting->pXbar = pXbar;
|
|
pRouting->VideoInputIndex = InputIndex;
|
|
pRouting->VideoOutputIndex = OutputIndex;
|
|
pRouting->AudioInputIndex = InputIndexRelated;
|
|
pRouting->AudioOutputIndex = OutputIndexRelated;
|
|
pRouting->InputPhysicalType = InputPhysicalType;
|
|
pRouting->OutputPhysicalType = OutputPhysicalType;
|
|
pRouting->Depth = Depth;
|
|
|
|
hr = BuildRoutingList (pPin, &RoutingNext, Depth + 1);
|
|
|
|
if (hr == S_FALSE)
|
|
{
|
|
pRouting->pLeftRouting = NULL;
|
|
SaveRouting (pRouting);
|
|
}
|
|
} // if we can route
|
|
|
|
} // if its a video pin
|
|
|
|
} // for all input pins
|
|
|
|
pXbar->Release();
|
|
}
|
|
else
|
|
{
|
|
// The filter doesn't support IAMCrossbar, so this
|
|
// is a terminal pin
|
|
pinInfo.pFilter->Release();
|
|
pStartingOutputPin->Release ();
|
|
|
|
return (Depth == 0) ? E_FAIL : S_FALSE;
|
|
}
|
|
|
|
pinInfo.pFilter->Release();
|
|
}
|
|
|
|
pStartingOutputPin->Release ();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Make a copy of the current routing, and AddRef the IAMCrossbar
|
|
// interfaces.
|
|
//
|
|
|
|
HRESULT CCrossbar::SaveRouting (CRouting *pRoutingNew)
|
|
{
|
|
int Depth = pRoutingNew->Depth + 1;
|
|
CRouting *pr=0;
|
|
CRouting *pCurrent = pRoutingNew;
|
|
|
|
if (!pRoutingNew || !m_RoutingList)
|
|
return E_POINTER;
|
|
|
|
DbgLog((LOG_TRACE,3,TEXT("CCrossbar::SaveRouting, Depth=%d, NumberOfRoutings=%d"),
|
|
Depth, m_RoutingList->GetCount() + 1));
|
|
|
|
pr = new CRouting[Depth];
|
|
if (pr == NULL)
|
|
return E_FAIL;
|
|
|
|
m_RoutingList->AddTail (pr);
|
|
|
|
for (int j = 0; j < Depth; j++, pr++)
|
|
{
|
|
*pr = *pCurrent;
|
|
ASSERT (pCurrent->pXbar != NULL);
|
|
|
|
//
|
|
// We're holding onto this interface, so AddRef
|
|
//
|
|
pCurrent->pXbar->AddRef();
|
|
pCurrent = pCurrent->pRightRouting;
|
|
|
|
//
|
|
// Pointers were stack based during recursive search, so update them
|
|
// in the allocated array
|
|
//
|
|
pr->pLeftRouting = pr - 1;
|
|
pr->pRightRouting = pr + 1;
|
|
|
|
if (j == 0) { // first element
|
|
pr->pLeftRouting = NULL;
|
|
}
|
|
if (j == (Depth - 1)) { // last element
|
|
pr->pRightRouting = NULL;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CCrossbar::DestroyRoutingList()
|
|
{
|
|
int k, Depth;
|
|
CRouting *pCurrent=0, *pFirst=0;
|
|
|
|
if (!m_RoutingList)
|
|
return E_POINTER;
|
|
|
|
DbgLog((LOG_TRACE,3,TEXT("DestroyRoutingList")));
|
|
|
|
while (m_RoutingList->GetCount())
|
|
{
|
|
pCurrent = pFirst = m_RoutingList->RemoveHead();
|
|
|
|
if (pCurrent)
|
|
{
|
|
Depth = pCurrent->Depth + 1;
|
|
|
|
for (k = 0; k < Depth; k++)
|
|
{
|
|
ASSERT (pCurrent->pXbar != NULL);
|
|
|
|
// Release the crossbar interface
|
|
pCurrent->pXbar->Release();
|
|
|
|
// Move to the next node in the list
|
|
pCurrent = pCurrent->pRightRouting;
|
|
}
|
|
}
|
|
|
|
delete [] pFirst;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//
|
|
// Does not AddRef the returned *Pin
|
|
//
|
|
HRESULT CCrossbar::GetCrossbarIPinAtIndex(
|
|
IAMCrossbar *pXbar,
|
|
LONG PinIndex,
|
|
BOOL IsInputPin,
|
|
IPin ** ppPin)
|
|
{
|
|
LONG cntInPins, cntOutPins;
|
|
IPin *pP = 0;
|
|
IBaseFilter *pFilter = NULL;
|
|
IEnumPins *pins=0;
|
|
ULONG n;
|
|
HRESULT hr;
|
|
|
|
if (!pXbar || !ppPin)
|
|
return E_POINTER;
|
|
|
|
*ppPin = 0;
|
|
|
|
if(S_OK != pXbar->get_PinCounts(&cntOutPins, &cntInPins))
|
|
return E_FAIL;
|
|
|
|
LONG TrueIndex = IsInputPin ? PinIndex : PinIndex + cntInPins;
|
|
|
|
hr = pXbar->QueryInterface(IID_IBaseFilter, (void **)&pFilter);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
if(SUCCEEDED(pFilter->EnumPins(&pins)))
|
|
{
|
|
LONG i=0;
|
|
while(pins->Next(1, &pP, &n) == S_OK)
|
|
{
|
|
pP->Release();
|
|
if (i == TrueIndex)
|
|
{
|
|
*ppPin = pP;
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
pins->Release();
|
|
}
|
|
pFilter->Release();
|
|
}
|
|
|
|
return *ppPin ? S_OK : E_FAIL;
|
|
}
|
|
|
|
|
|
//
|
|
// Find corresponding index of an IPin on a crossbar
|
|
//
|
|
HRESULT CCrossbar::GetCrossbarIndexFromIPin (
|
|
IAMCrossbar * pXbar,
|
|
LONG * PinIndex,
|
|
BOOL IsInputPin,
|
|
IPin * pPin)
|
|
{
|
|
LONG cntInPins, cntOutPins;
|
|
IPin *pP = 0;
|
|
IBaseFilter *pFilter = NULL;
|
|
IEnumPins *pins = 0;
|
|
ULONG n;
|
|
BOOL fOK = FALSE;
|
|
HRESULT hr;
|
|
|
|
if (!pXbar || !PinIndex || !pPin)
|
|
return E_POINTER;
|
|
|
|
if(S_OK != pXbar->get_PinCounts(&cntOutPins, &cntInPins))
|
|
return E_FAIL;
|
|
|
|
hr = pXbar->QueryInterface(IID_IBaseFilter, (void **)&pFilter);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
if(SUCCEEDED(pFilter->EnumPins(&pins)))
|
|
{
|
|
LONG i=0;
|
|
|
|
while(pins->Next(1, &pP, &n) == S_OK)
|
|
{
|
|
pP->Release();
|
|
if (pPin == pP)
|
|
{
|
|
*PinIndex = IsInputPin ? i : i - cntInPins;
|
|
fOK = TRUE;
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
pins->Release();
|
|
}
|
|
pFilter->Release();
|
|
}
|
|
|
|
return fOK ? S_OK : E_FAIL;
|
|
}
|
|
|
|
|
|
//
|
|
// How many unique video inputs can be selected?
|
|
//
|
|
HRESULT CCrossbar::GetInputCount (LONG *pCount)
|
|
{
|
|
if (pCount && m_RoutingList)
|
|
{
|
|
*pCount = m_RoutingList->GetCount();
|
|
return S_OK;
|
|
}
|
|
else
|
|
return E_POINTER;
|
|
}
|
|
|
|
|
|
//
|
|
// What is the physical type of a given input?
|
|
//
|
|
HRESULT CCrossbar::GetInputType (
|
|
LONG Index,
|
|
LONG * plPhysicalType)
|
|
{
|
|
if (!plPhysicalType || !m_RoutingList)
|
|
return E_POINTER;
|
|
|
|
CRouting *pCurrent = m_RoutingList->GetHead();
|
|
|
|
if (Index >= m_RoutingList->GetCount()) {
|
|
return E_FAIL;
|
|
}
|
|
|
|
POSITION pos = m_RoutingList->GetHeadPosition();
|
|
|
|
for (int j = 0; j <= Index; j++) {
|
|
pCurrent = m_RoutingList->GetNext(pos);
|
|
}
|
|
ASSERT (pCurrent != NULL);
|
|
|
|
*plPhysicalType = pCurrent->InputPhysicalType;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//
|
|
// Converts a PinType into a String
|
|
//
|
|
BOOL CCrossbar::StringFromPinType (TCHAR *pc, int nSize, long lType)
|
|
{
|
|
TCHAR *pcT;
|
|
BOOL bSuccess;
|
|
|
|
if (!pc || !nSize)
|
|
return FALSE;
|
|
|
|
switch (lType)
|
|
{
|
|
case PhysConn_Video_Tuner: pcT = TEXT("Video Tuner\0"); break;
|
|
case PhysConn_Video_Composite: pcT = TEXT("Video Composite\0"); break;
|
|
case PhysConn_Video_SVideo: pcT = TEXT("Video SVideo\0"); break;
|
|
case PhysConn_Video_RGB: pcT = TEXT("Video RGB\0"); break;
|
|
case PhysConn_Video_YRYBY: pcT = TEXT("Video YRYBY\0"); break;
|
|
case PhysConn_Video_SerialDigital: pcT = TEXT("Video SerialDigital\0"); break;
|
|
case PhysConn_Video_ParallelDigital: pcT = TEXT("Video ParallelDigital\0");break;
|
|
case PhysConn_Video_SCSI: pcT = TEXT("Video SCSI\0"); break;
|
|
case PhysConn_Video_AUX: pcT = TEXT("Video AUX\0"); break;
|
|
case PhysConn_Video_1394: pcT = TEXT("Video 1394\0"); break;
|
|
case PhysConn_Video_USB: pcT = TEXT("Video USB\0"); break;
|
|
case PhysConn_Video_VideoDecoder: pcT = TEXT("Video Decoder\0"); break;
|
|
case PhysConn_Video_VideoEncoder: pcT = TEXT("Video Encoder\0"); break;
|
|
|
|
case PhysConn_Audio_Tuner: pcT = TEXT("Audio Tuner\0"); break;
|
|
case PhysConn_Audio_Line: pcT = TEXT("Audio Line\0"); break;
|
|
case PhysConn_Audio_Mic: pcT = TEXT("Audio Mic\0"); break;
|
|
case PhysConn_Audio_AESDigital: pcT = TEXT("Audio AESDigital\0"); break;
|
|
case PhysConn_Audio_SPDIFDigital: pcT = TEXT("Audio SPDIFDigital\0"); break;
|
|
case PhysConn_Audio_SCSI: pcT = TEXT("Audio SCSI\0"); break;
|
|
case PhysConn_Audio_AUX: pcT = TEXT("Audio AUX\0"); break;
|
|
case PhysConn_Audio_1394: pcT = TEXT("Audio 1394\0"); break;
|
|
case PhysConn_Audio_USB: pcT = TEXT("Audio USB\0"); break;
|
|
case PhysConn_Audio_AudioDecoder: pcT = TEXT("Audio Decoder\0"); break;
|
|
|
|
default:
|
|
pcT = TEXT("Unknown\0");
|
|
break;
|
|
}
|
|
|
|
// return TRUE on sucessful copy
|
|
if (SUCCEEDED(StringCbCopy(pc, nSize, pcT)))
|
|
bSuccess = TRUE;
|
|
else
|
|
bSuccess = FALSE;
|
|
|
|
return (bSuccess);
|
|
}
|
|
|
|
|
|
//
|
|
// Get a text version of an input
|
|
//
|
|
// Return S_OK if the buffer is large enough to copy the string name
|
|
//
|
|
HRESULT CCrossbar::GetInputName (
|
|
LONG Index,
|
|
TCHAR *pName,
|
|
LONG Size)
|
|
{
|
|
if (!m_RoutingList)
|
|
return E_POINTER;
|
|
|
|
CRouting *pCurrent = m_RoutingList->GetHead();
|
|
|
|
if ((Index >= m_RoutingList->GetCount()) || (pName == NULL)) {
|
|
return E_FAIL;
|
|
}
|
|
|
|
POSITION pos = m_RoutingList->GetHeadPosition();
|
|
|
|
for (int j = 0; j <= Index; j++) {
|
|
pCurrent = m_RoutingList->GetNext(pos);
|
|
}
|
|
ASSERT (pCurrent != NULL);
|
|
|
|
return (StringFromPinType (pName, Size, pCurrent->InputPhysicalType) ?
|
|
S_OK : E_FAIL);
|
|
}
|
|
|
|
|
|
//
|
|
// Select an input
|
|
//
|
|
HRESULT CCrossbar::SetInputIndex (
|
|
LONG Index)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (!m_RoutingList)
|
|
return E_POINTER;
|
|
|
|
CRouting *pCurrent = m_RoutingList->GetHead();
|
|
int j;
|
|
|
|
if (Index >= m_RoutingList->GetCount())
|
|
return hr;
|
|
|
|
POSITION pos = m_RoutingList->GetHeadPosition();
|
|
for (j = 0; j <= Index; j++) {
|
|
pCurrent = m_RoutingList->GetNext(pos);
|
|
}
|
|
|
|
ASSERT (pCurrent != NULL);
|
|
|
|
int Depth= pCurrent->Depth + 1;
|
|
|
|
for (j = 0; j < Depth; j++)
|
|
{
|
|
hr = pCurrent->pXbar->Route (pCurrent->VideoOutputIndex, pCurrent->VideoInputIndex);
|
|
ASSERT (S_OK == hr);
|
|
|
|
if ((pCurrent->AudioOutputIndex != -1) && (pCurrent->AudioInputIndex != -1)) {
|
|
EXECUTE_ASSERT (S_OK == pCurrent->pXbar->Route (pCurrent->AudioOutputIndex,
|
|
pCurrent->AudioInputIndex));
|
|
}
|
|
|
|
DbgLog((LOG_TRACE,3,TEXT("CCrossbar::Routing, VideoOutIndex=%d VideoInIndex=%d"),
|
|
pCurrent->VideoOutputIndex, pCurrent->VideoInputIndex));
|
|
|
|
pCurrent++;
|
|
}
|
|
|
|
m_CurrentRoutingIndex = Index;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// What input is currently selected?
|
|
//
|
|
HRESULT CCrossbar::GetInputIndex (
|
|
LONG *plIndex)
|
|
{
|
|
if (plIndex)
|
|
{
|
|
*plIndex = m_CurrentRoutingIndex;
|
|
return S_OK;
|
|
}
|
|
else
|
|
return E_POINTER;
|
|
}
|
|
|