windows-nt/Source/XPSP1/NT/net/tapi/skywalker/rcamsp/rcastrm.cpp
2020-09-26 16:20:57 +08:00

2445 lines
63 KiB
C++

/*++
Copyright (c) 1998-1999 Microsoft Corporation
Module Name:
rcastrm.cpp
Abstract:
This module contains implementation of CRCAMSPStream.
Author:
Zoltan Szilagyi (zoltans) September 7, 1998
--*/
#include "stdafx.h"
#include <initguid.h>
#include <g711uids.h>
#define ID_BRIDGE_PIN 0 // From rca.h
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
// Global string constants -- absolutely MUST NOT be localized!!!
//
// SZ_RCAFILTER identifies filter in enumeration -- very important!
#define SZ_RCAFILTER L"Raw Channel Access Capture/Render"
// Friendly names for the above when added to the graph -- for debugging only
#define SZ_RCAFILTER_FRIENDLY_CAPTURE L"The Stream's RCA capture (on line device)"
#define SZ_RCAFILTER_FRIENDLY_RENDER L"The Stream's RCA render (on line device)"
// SZ_G711FILTER is to name our g711 codec filter for debugging purpose only
#define SZ_G711FILTER L"G711 codec filter inserted by RCA MSP"
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
// Custom logging helper macro, usable only within this class.
//
#ifdef MSPLOG
#define STREAM_PREFIX(x) m_Direction == TD_RENDER ? \
"CRCAMSPStream(RENDER )::" x : \
"CRCAMSPStream(CAPTURE)::" x
#endif
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
CRCAMSPStream::CRCAMSPStream() : CMSPStream()
{
LOG((MSP_TRACE, STREAM_PREFIX("CRCAMSPStream entered.")));
m_fTerminalConnected = FALSE;
m_dwBufferSizeOnWire = 0xfeedface;
m_fHaveVCHandle = FALSE;
m_DesiredGraphState = State_Stopped;
m_pFilter = NULL;
m_pG711Filter = NULL;
LOG((MSP_TRACE, STREAM_PREFIX("CRCAMSPStream exited.")));
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
CRCAMSPStream::~CRCAMSPStream()
{
LOG((MSP_TRACE, STREAM_PREFIX("~CRCAMSPStream entered.")));
LOG((MSP_TRACE, STREAM_PREFIX("~CRCAMSPStream exited.")));
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
void CRCAMSPStream::FinalRelease()
{
LOG((MSP_TRACE, STREAM_PREFIX("FinalRelease entered.")));
//
// At this point we should have no terminals selected, since
// Shutdown is supposed to be called before we are destructed.
//
_ASSERTE( 0 == m_Terminals.GetSize() );
//
// Clean up our filters.
//
if ( m_fHaveVCHandle )
{
_ASSERTE( m_pFilter );
m_pIGraphBuilder->RemoveFilter( m_pFilter );
}
if ( m_pFilter )
{
m_pFilter->Release();
m_pFilter = NULL;
}
if ( m_pG711Filter )
{
m_pIGraphBuilder->RemoveFilter( m_pG711Filter );
m_pG711Filter->Release();
}
//
// Call the base class method to clean up everything else.
//
CMSPStream::FinalRelease();
LOG((MSP_TRACE, STREAM_PREFIX("FinalRelease exited.")));
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
// Override to allow us to create our filter on initialization.
//
HRESULT CRCAMSPStream::Init(
IN HANDLE hAddress,
IN CMSPCallBase * pMSPCall,
IN IMediaEvent * pGraph,
IN DWORD dwMediaType,
IN TERMINAL_DIRECTION Direction
)
{
LOG((MSP_TRACE, STREAM_PREFIX("Init - enter")));
HRESULT hr;
hr = CMSPStream::Init(hAddress,
pMSPCall,
pGraph,
dwMediaType,
Direction);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("Init - "
"base class Init failed - exit 0x%08x"), hr));
return hr;
}
//
// Create the RCA filter.
//
hr = CreateRCAFilter();
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("Init - "
"CreateRCAFilter failed - exit 0x%08x"), hr));
return hr;
}
//
// Create the g711 codec filter and add it to the graph.
//
// We could just wait to do this on connection, and in fact on connection
// we call the same method again to reset the G711 to a known state, but we
// go ahead and do this now, for a couple reasons:
//
// * If the G711 codec is not available on the system, we are never going
// to be able to do anything useful, so we might as well fail to init
// the stream, and hence fail to create the call.
// * If creating the G711 codec takes a while, it will be faster to
// connect the call when it's time to do so. In practice this is not
// a big deal.
//
hr = PrepareG711Filter();
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("Init - "
"PrepareG711Filter failed - exit 0x%08x"), hr));
return hr;
}
LOG((MSP_TRACE, STREAM_PREFIX("Init - exit S_OK")));
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
// If the G711 filter has not yet been created, then create the G711 filter
// and add it to the graph. Otherwise, remove it from the graph and readd it
// to the graph.
//
// (Note that all failure cases maintain the invariant that either we have
// a reference to the filter and it's in the graph or we don't have a
// reference to the filter and it's not in the graph.)
//
HRESULT CRCAMSPStream::PrepareG711Filter(void)
{
LOG((MSP_TRACE, STREAM_PREFIX("PrepareG711Filter - enter")));
HRESULT hr;
if ( m_pG711Filter == NULL )
{
//
// Create the G711 filter.
//
hr = CoCreateInstance(
CLSID_G711Codec,
NULL,
CLSCTX_INPROC_SERVER,
IID_IBaseFilter,
(void **) &m_pG711Filter
);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("PrepareG711Filter - "
"failed to create G711 codec - exit 0x%08x"), hr));
m_pG711Filter = NULL;
return hr;
}
}
else
{
//
// Remove the G711 codec filter from the graph, to break any previous
// connections, and simplify the connection code.
//
hr = m_pIGraphBuilder->RemoveFilter( m_pG711Filter );
if ( FAILED(hr) )
{
//
// This is very strange indeed. We fail to connect, and can come
// back and try again next time.
//
// (Note that failure case maintains the invariant that either we have
// a reference to the filter and it's in the graph or we don't have a
// reference to the filter and it's not in the graph.)
//
LOG((MSP_ERROR, STREAM_PREFIX("PrepareG711Filter - "
"failed to remove G711 filter from graph - 0x%08x"), hr));
return hr;
}
}
//
// Add the G711 filter to the graph.
//
hr = m_pIGraphBuilder->AddFilter(
m_pG711Filter,
SZ_G711FILTER
);
//
// The name should be unique as we always have only one of these in the
// graph at a time.
//
_ASSERTE( hr != VFW_S_DUPLICATE_NAME );
_ASSERTE( hr != VFW_E_DUPLICATE_NAME );
if ( FAILED(hr) )
{
//
// (Note that failure case maintains the invariant that either we have
// a reference to the filter and it's in the graph or we don't have a
// reference to the filter and it's not in the graph.)
//
LOG((MSP_ERROR, STREAM_PREFIX("CRCAMSPStream - "
"failed to add G711 filter to graph - exit 0x%08x"), hr));
m_pG711Filter->Release();
m_pG711Filter = NULL;
return hr;
}
LOG((MSP_TRACE, STREAM_PREFIX("PrepareG711Filter - exit S_OK")));
return S_OK;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// CreateRCAFilter
//
// Creates a filter based on the clsid.
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
HRESULT CRCAMSPStream::CreateRCAFilter()
{
LOG((MSP_TRACE, STREAM_PREFIX("CreateRCAFilter - enter")));
HRESULT hr;
VARIANT var;
ICreateDevEnum * pCreateDevEnum = NULL;
IEnumMoniker * pEnumMoniker = NULL;
IMoniker * pMon = NULL;
IPropertyBag * pPropBag = NULL;
hr = CoCreateInstance(
CLSID_SystemDeviceEnum,
NULL,
CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum,
(void**)&pCreateDevEnum
);
if(FAILED(hr))
{
LOG((MSP_ERROR, STREAM_PREFIX("CreateRCAFilter - "
"can't create system device enumerator - exit 0x%08x"), hr));
return hr;
}
hr = pCreateDevEnum->CreateClassEnumerator((m_Direction == TD_RENDER) ?
KSCATEGORY_CAPTURE :
KSCATEGORY_RENDER,
&pEnumMoniker,
0);
pCreateDevEnum->Release();
//
// here we MUST check against S_OK explicitly rather than just success
// or failure, because hr == S_FALSE means the category does not exist (so
// pEnumMoniker == NULL in that case and we would AV)
//
if ( hr != S_OK )
{
LOG((MSP_ERROR, STREAM_PREFIX("CreateRCAFilter - "
"can't create class enumerator - exit 0x%08x"), hr));
return hr;
}
pEnumMoniker->Reset();
while( NULL == m_pFilter )
{
hr = pEnumMoniker->Next(1, &pMon, NULL);
if( S_OK != hr )
{
break;
}
hr = pMon->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag );
if(FAILED(hr))
{
pMon->Release();
LOG((MSP_ERROR, STREAM_PREFIX("CreateRCAFilter - "
"BindToStorage failed - exit 0x%08x"), hr));
continue;
}
VariantInit(&var);
V_VT(&var) = VT_BSTR;
hr = pPropBag->Read(L"FriendlyName", &var, 0 );
pPropBag->Release();
if(FAILED(hr))
{
pMon->Release();
continue;
}
if(lstrcmpiW(var.bstrVal, SZ_RCAFILTER) != 0)
{
pMon->Release();
continue;
}
// Bind to selected device
hr = pMon->BindToObject( 0, 0, IID_IBaseFilter, (void**)&m_pFilter );
pMon->Release();
}
pEnumMoniker->Release();
if ( ( S_OK != hr ) || ( NULL == m_pFilter ) )
{
LOG((MSP_ERROR, STREAM_PREFIX("CreateRCAFilter - "
"didn't find the filter - exit E_FAIL")));
return E_FAIL;
}
LOG((MSP_TRACE, STREAM_PREFIX("CreateRCAFilter - exit S_OK")));
return S_OK;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
STDMETHODIMP CRCAMSPStream::get_Name (
OUT BSTR * ppName
)
{
LOG((MSP_TRACE, STREAM_PREFIX("get_Name - enter")));
//
// Check argument.
//
if ( IsBadWritePtr(ppName, sizeof(BSTR) ) )
{
LOG((MSP_TRACE, STREAM_PREFIX("get_Name - "
"bad return pointer - returning E_POINTER")));
return E_POINTER;
}
//
// Decide what string to return based on which stream this is.
//
ULONG ulID;
if ( m_Direction == TD_CAPTURE )
{
ulID = IDS_CAPTURE_STREAM;
}
else
{
ulID = IDS_RENDER_STREAM;
}
//
// Get the string from the string table.
//
const int ciAllocSize = 2048;
WCHAR wszName[ciAllocSize];
int iReturn = LoadStringW( _Module.GetModuleInstance(),
ulID,
wszName,
ciAllocSize - 1 );
if ( iReturn == 0 )
{
_ASSERTE( FALSE );
*ppName = NULL;
LOG((MSP_ERROR, STREAM_PREFIX("get_Name - "
"LoadString failed - returning E_UNEXPECTED")));
return E_UNEXPECTED;
}
//
// Convert to a BSTR and return the BSTR.
//
*ppName = SysAllocString(wszName);
if ( *ppName == NULL )
{
LOG((MSP_ERROR, STREAM_PREFIX("get_Name - "
"SysAllocString failed - returning E_OUTOFMEMORY")));
return E_OUTOFMEMORY;
}
LOG((MSP_TRACE, STREAM_PREFIX("get_Name - exit S_OK")));
return S_OK;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
STDMETHODIMP CRCAMSPStream::SelectTerminal(
IN ITTerminal * pTerminal
)
{
LOG((MSP_TRACE, STREAM_PREFIX("SelectTerminal - enter")));
//
// We are going to access the terminal list -- grab the lock
//
CLock lock(m_lock);
//
// Reject if we already have a terminal selected.
//
if ( 0 != m_Terminals.GetSize() )
{
LOG((MSP_ERROR, STREAM_PREFIX("SelectTerminal - "
"exit TAPI_E_MAXTERMINALS")));
return TAPI_E_MAXTERMINALS;
}
//
// Use base class method to add it to our list of terminals.
//
HRESULT hr = CMSPStream::SelectTerminal(pTerminal);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("SelectTerminal - "
"base class method failed - exit 0x%08x"), hr));
return hr;
}
//
// Re-pause or re-start the stream if needed.
//
if ( m_DesiredGraphState == State_Paused )
{
hr = PauseStream();
}
else if ( m_DesiredGraphState == State_Running )
{
hr = StartStream();
}
else
{
_ASSERTE( m_DesiredGraphState == State_Stopped );
hr = S_OK;
}
if ( FAILED(hr) )
{
LOG((MSP_TRACE, STREAM_PREFIX("SelectTerminal - "
"can't regain old graph state - unselecting terminal - "
"exit 0x%08x"), hr));
//
// Unselect it to undo all of the above.
//
UnselectTerminal(pTerminal);
return hr;
}
LOG((MSP_TRACE, STREAM_PREFIX("SelectTerminal - exit S_OK")));
return S_OK;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
STDMETHODIMP CRCAMSPStream::UnselectTerminal (
IN ITTerminal * pTerminal
)
{
LOG((MSP_TRACE, STREAM_PREFIX("UnselectTerminal - enter")));
//
// check the argument -- it has to be a reasonably good pointer
//
if (IsBadReadPtr(pTerminal, sizeof(ITTerminal)))
{
LOG((MSP_ERROR, STREAM_PREFIX("UnselectTerminal - "
"bad terminal pointer passed in. returning E_POINTER")));
return E_POINTER;
}
CLock lock(m_lock);
//
// check the argument -- it has to be in the array of terminals
//
if (m_Terminals.Find(pTerminal) < 0)
{
LOG((MSP_ERROR, STREAM_PREFIX("UnselectTerminal - "
"terminal [%p] is not selected on this stream. "
"returning TAPI_E_INVALIDTERMINAL"), pTerminal));
return TAPI_E_INVALIDTERMINAL;
}
//
// Add an extra reference to the terminal so it doesn't go away
// after we call CMSPStream::UnselectTerminal. We need it later
// in the function.
//
pTerminal->AddRef();
//
// Use base class method to remove terminal from our list of terminals.
//
HRESULT hr = CMSPStream::UnselectTerminal(pTerminal);
if (FAILED(hr))
{
LOG((MSP_ERROR, STREAM_PREFIX("UnselectTerminal - "
"base class method failed - exit 0x%08x"), hr));
pTerminal->Release();
return hr;
}
//
// If we've been given a vc handle then we may not be stopped.
// This does nothing if we are already stopped.
//
if ( m_fHaveVCHandle )
{
CMSPStream::StopStream();
}
//
// Disconnect the terminal if this call had it connected.
//
if ( m_fTerminalConnected )
{
//
// Get the ITTerminalControl interface.
//
ITTerminalControl * pTerminalControl;
hr = pTerminal->QueryInterface(IID_ITTerminalControl,
(void **) &pTerminalControl);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("UnselectTerminal - "
"QI for ITTerminalControl failed - exit 0x%08x"), hr));
pTerminal->Release();
return hr;
}
//
// Disconnect the terminal.
//
hr = pTerminalControl->DisconnectTerminal(m_pIGraphBuilder, 0);
pTerminalControl->Release();
m_fTerminalConnected = FALSE;
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("UnselectTerminal - "
"DisconnectTerminal failed - exit 0x%08x"), hr));
pTerminal->Release();
return hr;
}
}
pTerminal->Release();
LOG((MSP_TRACE, STREAM_PREFIX("UnselectTerminal - exit S_OK")));
return S_OK;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
STDMETHODIMP CRCAMSPStream::StartStream (void)
{
LOG((MSP_TRACE, STREAM_PREFIX("StartStream - enter")));
CLock lock(m_lock);
m_DesiredGraphState = State_Running;
//
// Can't start the stream if we don't know the vc handle.
// (We create our filters on discovery of the vc handle.)
//
if ( ! m_fHaveVCHandle )
{
LOG((MSP_WARN, STREAM_PREFIX("StartStream - "
"no vc handle so nothing to do yet - exit S_OK")));
return S_OK;
}
//
// Can't start the stream if no terminal has been selected.
//
if ( 0 == m_Terminals.GetSize() )
{
LOG((MSP_WARN, STREAM_PREFIX("StartStream - "
"no Terminal so nothing to do yet - exit S_OK")));
return S_OK;
}
//
// Connect the terminal. This does nothing if this call already
// connected the terminal and fails if another call has the
// terminal connected.
//
HRESULT hr;
hr = ConnectTerminal(m_Terminals[0]);
if ( FAILED(hr) )
{
FireEvent(CALL_TERMINAL_FAIL, hr, CALL_CAUSE_CONNECT_FAIL);
FireEvent(CALL_STREAM_FAIL, hr, CALL_CAUSE_CONNECT_FAIL);
LOG((MSP_ERROR, STREAM_PREFIX("StartStream - "
"our ConnectTerminal failed - exit 0x%08x"), hr));
return hr;
}
//
// Run the stream via the base class method.
//
hr = CMSPStream::StartStream();
if ( FAILED(hr) )
{
FireEvent(CALL_STREAM_FAIL, hr, CALL_CAUSE_UNKNOWN);
LOG((MSP_ERROR, STREAM_PREFIX("StartStream - "
"Run failed - exit 0x%08x"), hr));
return hr;
}
HRESULT hr2 = FireEvent(CALL_STREAM_ACTIVE, hr, CALL_CAUSE_LOCAL_REQUEST);
if ( FAILED(hr2) )
{
LOG((MSP_ERROR, STREAM_PREFIX("StartStream - "
"FireEvent failed - exit 0x%08x"), hr2));
return hr2;
}
LOG((MSP_TRACE, STREAM_PREFIX("StartStream - exit S_OK")));
return S_OK;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
STDMETHODIMP CRCAMSPStream::PauseStream (void)
{
LOG((MSP_TRACE, STREAM_PREFIX("PauseStream - enter")));
CLock lock(m_lock);
m_DesiredGraphState = State_Paused;
//
// Can't pause the stream if we don't know the vc handle.
// (We create our filters on discovery of the vc handle.)
//
if ( ! m_fHaveVCHandle )
{
LOG((MSP_WARN, STREAM_PREFIX("PauseStream - "
"no vc handle so nothing to do yet - exit S_OK")));
return S_OK;
}
//
// Can't pause the stream if no terminal has been selected.
//
if ( 0 == m_Terminals.GetSize() )
{
LOG((MSP_WARN, STREAM_PREFIX("PauseStream - "
"no Terminal so nothing to do yet - exit S_OK")));
return S_OK;
}
//
// Connect the terminal. This does nothing if this call already
// connected the terminal and fails if another call has the
// terminal connected.
//
HRESULT hr;
hr = ConnectTerminal(m_Terminals[0]);
if ( FAILED(hr) )
{
FireEvent(CALL_TERMINAL_FAIL, hr, CALL_CAUSE_CONNECT_FAIL);
FireEvent(CALL_STREAM_FAIL, hr, CALL_CAUSE_CONNECT_FAIL);
LOG((MSP_ERROR, STREAM_PREFIX("StartStream - "
"our ConnectTerminal failed - exit 0x%08x"), hr));
return hr;
}
//
// Pause the stream via the base class method.
//
hr = CMSPStream::PauseStream();
if ( FAILED(hr) )
{
FireEvent(CALL_STREAM_FAIL, hr, CALL_CAUSE_UNKNOWN);
LOG((MSP_ERROR, STREAM_PREFIX("PauseStream - "
"Pause failed - exit 0x%08x"), hr));
return hr;
}
HRESULT hr2 = FireEvent(CALL_STREAM_INACTIVE, hr, CALL_CAUSE_LOCAL_REQUEST);
if ( FAILED(hr2) )
{
LOG((MSP_ERROR, STREAM_PREFIX("PauseStream - "
"FireEvent failed - exit 0x%08x"), hr2));
return hr2;
}
LOG((MSP_TRACE, STREAM_PREFIX("PauseStream - exit S_OK")));
return S_OK;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
STDMETHODIMP CRCAMSPStream::StopStream (void)
{
LOG((MSP_TRACE, STREAM_PREFIX("StopStream - enter")));
CLock lock(m_lock);
m_DesiredGraphState = State_Stopped;
//
// Nothing to do if we don't know our vc handle.
//
if ( ! m_fHaveVCHandle )
{
LOG((MSP_WARN, STREAM_PREFIX("StopStream - "
"no vc handle - exit S_OK")));
return S_OK;
}
//
// Nothing to do if no terminal has been selected.
//
if ( 0 == m_Terminals.GetSize() )
{
LOG((MSP_WARN, STREAM_PREFIX("StopStream - "
"no Terminal - exit S_OK")));
return S_OK;
}
//
// Stop the stream via the base class method.
//
HRESULT hr;
hr = CMSPStream::StopStream();
if ( FAILED(hr) )
{
FireEvent(CALL_STREAM_FAIL, hr, CALL_CAUSE_UNKNOWN);
LOG((MSP_ERROR, STREAM_PREFIX("StopStream - "
"Stop failed - exit 0x%08x"), hr));
return hr;
}
HRESULT hr2 = FireEvent(CALL_STREAM_INACTIVE, hr, CALL_CAUSE_LOCAL_REQUEST);
if ( FAILED(hr2) )
{
LOG((MSP_ERROR, STREAM_PREFIX("StopStream - "
"FireEvent failed - exit 0x%08x"), hr2));
return hr2;
}
LOG((MSP_TRACE, STREAM_PREFIX("StopStream - exit S_OK")));
return S_OK;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
HRESULT CRCAMSPStream::SetVCHandle(DWORD dwVCHandle)
{
LOG((MSP_TRACE, STREAM_PREFIX("SetVCHandle - enter")));
CLock lock(m_lock);
HRESULT hr;
//
// Save VC handle in the filter
//
if ( (dwVCHandle == 0) || (dwVCHandle == 0xffffffff) )
{
LOG((MSP_ERROR, STREAM_PREFIX("SetVCHandle - "
"invalid vc handle %d - exit E_INVALIDARG"), dwVCHandle));
return E_INVALIDARG;
}
WCHAR wszFileName[10];
wsprintfW(wszFileName, L"%x", dwVCHandle);
hr = SetVCHandleOnPin(wszFileName,
KSDATAFORMAT_SPECIFIER_VC_ID);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("SetVCHandle - "
"SetVCHandleOnPin failed - exit 0x%08x"), hr));
return hr;
}
//
// Add the filter. Supply a name to make debugging easier.
//
WCHAR * pName = (m_Direction == TD_RENDER) ?
SZ_RCAFILTER_FRIENDLY_CAPTURE :
SZ_RCAFILTER_FRIENDLY_RENDER ;
hr = m_pIGraphBuilder->AddFilter(m_pFilter, pName);
//
// The name should be unique as we always have only one of these in the
// graph at a time.
//
_ASSERTE( hr != VFW_S_DUPLICATE_NAME );
_ASSERTE( hr != VFW_E_DUPLICATE_NAME );
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("SetVCHandle - "
"AddFilter failed - exit 0x%08x"), hr));
m_pFilter->Release();
m_pFilter = NULL;
return hr;
}
//
// We now have the vc handle.
//
m_fHaveVCHandle = TRUE;
LOG((MSP_TRACE, STREAM_PREFIX("SetVCHandle - exit S_OK")));
return S_OK;
}
HRESULT
CRCAMSPStream::SetVCHandleOnPin(
LPWSTR pszFileName,
REFGUID formattype
)
{
LOG((MSP_TRACE, STREAM_PREFIX("SetVCHandleOnPin - enter")));
HRESULT hr;
IEnumPins * pEnumPins;
IPin * pPin = NULL;
AM_MEDIA_TYPE * pAMT;
BOOL bFound = FALSE;
IEnumMediaTypes* pEMT;
// enumerate the pins on the filter
hr = m_pFilter->EnumPins( &pEnumPins );
if (!(SUCCEEDED(hr)))
{
LOG((MSP_ERROR, STREAM_PREFIX("SetVCHandleOnPin - "
"EnumPins failed - exit 0x%08x"), hr));
return hr;
}
// go through the pins
while ( S_OK == (hr = pEnumPins->Next( 1, &pPin, NULL ) ) )
{
PIN_DIRECTION pd;
// get the pin info
hr = pPin->QueryDirection( &pd );
if ( FAILED(hr) )
{
pPin->Release();
pEnumPins->Release();
LOG((MSP_ERROR, STREAM_PREFIX("SetVCHandleOnPin - "
"QueryDirection failed - exit 0x%08x"), hr));
return hr;
}
// does it meet the criteria?
if ( ( ( TD_CAPTURE == m_Direction ) && (pd == PINDIR_OUTPUT) ) ||
( ( TD_RENDER == m_Direction ) && (pd == PINDIR_INPUT ) ) )
{
// yes
hr = pPin->EnumMediaTypes(&pEMT);
if ( FAILED(hr) )
{
pPin->Release();
pEnumPins->Release();
LOG((MSP_ERROR, STREAM_PREFIX("SetVCHandleOnPin - "
"EnumMediaTypes failed - exit 0x%08x"), hr));
return hr;
}
while ( S_OK == (hr = pEMT->Next(1, &pAMT, NULL) ) )
{
if (IsEqualGUID(formattype, pAMT->formattype))
{
hr = SetMediaTypeFormat(
pAMT,
(BYTE*)(pszFileName),
(lstrlenW(pszFileName) + 1)
* sizeof(WCHAR)
);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("SetVCHandleOnPin - "
"SetMediaTypeFormat failed - exit 0x%08x"), hr));
::DeleteMediaType(pAMT);
continue;
}
hr = pPin->Connect( NULL, pAMT );
::DeleteMediaType(pAMT);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("SetVCHandleOnPin - "
"Connect failed - exit 0x%08x"), hr));
continue;
}
bFound = TRUE;
LOG((MSP_TRACE, STREAM_PREFIX("SetVCHandleOnPin - "
"About to call SetDataFormatOnPin\n")));
hr = SetDataFormatOnPin(pPin);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("SetVCHandleOnPin - "
"SetDataFormatOnPin failed with hr "
"== 0x%08x, don't know what to do "
"now..."), hr));
}
hr = GetBufferSizeFromPin( pPin,
& m_dwBufferSizeOnWire );
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("SetVCHandleOnPin - "
"GetBufferSizeFromPin failed - exit 0x%08x"), hr));
return hr;
}
break;
}
::DeleteMediaType(pAMT);
} //while
pEMT->Release();
if ( bFound )
{
pPin->Release();
break;
} // if found
} // if meets criteria
pPin->Release();
} // while got another pin
pEnumPins->Release();
if ( !bFound )
{
// error
LOG((MSP_ERROR, STREAM_PREFIX("SetVCHandleOnPin - "
"Couldn't find a pin - exit E_FAIL")));
return E_FAIL;
}
LOG((MSP_TRACE, STREAM_PREFIX("SetVCHandleOnPin - exit S_OK")));
return S_OK;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
HRESULT
CRCAMSPStream::GetBufferSizeFromPin(
IN IPin * pPin,
OUT DWORD * pdwSize
)
{
LOG((MSP_TRACE, STREAM_PREFIX("GetBufferSizeFromPin - enter")));
_ASSERTE( ! IsBadReadPtr (pPin, sizeof(IPin) ) );
_ASSERTE( ! IsBadWritePtr(pdwsize, sizeof(DWORD) ) );
//
// Get the IKsPropertySet interface on the pin.
//
HRESULT hr;
IKsPropertySet * pKsPropSet;
hr = pPin->QueryInterface(IID_IKsPropertySet,
(void **)&pKsPropSet);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("GetBufferSizeFromPin - "
"couldn't get IKsPropertySet interface - exit 0x%08x"), hr));
return hr;
}
//
// Get allocator framing info from the property set interface.
// This will fail if there is more than one framing item available, as
// we only pass in enough space for one framing item. That's good, as we
// don't know what to do with multiple framing items.
//
KSP_PIN InstanceData;
KSALLOCATOR_FRAMING_EX AllocFraming;
DWORD dwActualSize;
hr = pKsPropSet->Get(
KSPROPSETID_Connection, // [in] propset guid
KSPROPERTY_CONNECTION_ALLOCATORFRAMING_EX, // [in] property id in set
(LPVOID) & InstanceData, // [out] instance data
sizeof(KSP_PIN), // [in] buffer size of ^^^
(LPVOID) & AllocFraming, // [out] value of property
sizeof(KSALLOCATOR_FRAMING_EX), // [in] buffer size of ^^^
& dwActualSize); // [out] actual size of ^^^
pKsPropSet->Release();
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("GetBufferSizeFromPin - "
"cannot get allocator framing property - exit 0x%08x"), hr));
return hr;
}
//
// Return the size from the property data.
//
_ASSERTE( dwActualSize == sizeof(KSALLOCATOR_FRAMING_EX) );
_ASSERTE( AllocFraming.CountItems == 1 );
KS_FRAMING_RANGE * pRange;
pRange = & (AllocFraming.FramingItem[0].FramingRange.Range);
_ASSERTE( pRange->MaxFrameSize == pRange->MinFrameSize );
*pdwSize = pRange->MaxFrameSize;
LOG((MSP_TRACE, STREAM_PREFIX("GetBufferSizeFromPin - returned size %d"
" - exit S_OK"), *pdwSize));
return S_OK;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
HRESULT
CRCAMSPStream::SetDataFormatOnPin(
IPin *pBridgePin
)
{
LOG((MSP_TRACE, STREAM_PREFIX("SetDataFormatOnPin - enter")));
KSDATAFORMAT_WAVEFORMATEX PCMAudioFormat =
{
{
sizeof(KSDATAFORMAT_WAVEFORMATEX),
0,
0,
0,
STATIC_KSDATAFORMAT_TYPE_AUDIO,
UseMulaw() ? STATIC_KSDATAFORMAT_SUBTYPE_MULAW :
STATIC_KSDATAFORMAT_SUBTYPE_ALAW,
STATIC_KSDATAFORMAT_SPECIFIER_WAVEFORMATEX
},
{
// format tag, one of _PCM, _MULAW, _ALAW
UseMulaw() ? WAVE_FORMAT_MULAW :
WAVE_FORMAT_ALAW,
1, // number of channels (1 = mono)
8000, // number of samples per second
8000, // average bytes per second
1, // block alignment, in bytes, normally 1
8, // bits per sample (normally 8 or 16)
0 // size of extra format information, normally 0
}
};
KSP_PIN InstanceData;
IKsPropertySet *pKsPropSet = NULL;
HRESULT hr = S_OK;
InstanceData.Property.Set = KSPROPSETID_Pin;
InstanceData.Property.Id = KSPROPERTY_PIN_PROPOSEDATAFORMAT;
InstanceData.Property.Flags = KSPROPERTY_TYPE_SET;
InstanceData.PinId = ID_BRIDGE_PIN;
InstanceData.Reserved = 0;
//
// Bogus unreadable do-while accomplishes clever error logging economies.
//
do {
hr = pBridgePin->QueryInterface(IID_IKsPropertySet,
(void **)&pKsPropSet);
if (FAILED(hr))
{
LOG((MSP_ERROR, STREAM_PREFIX("SetDataFormatOnPin - "
"Couldn't get IKsPropertySet interface\n")));
break;
}
hr = pKsPropSet->Set(KSPROPSETID_Pin,
KSPROPERTY_PIN_PROPOSEDATAFORMAT,
(LPVOID)&InstanceData,
sizeof(KSP_PIN),
(LPVOID)&PCMAudioFormat,
sizeof(KSDATAFORMAT_WAVEFORMATEX));
pKsPropSet->Release();
if (FAILED(hr))
{
LOG((MSP_ERROR, STREAM_PREFIX("SetDataFormatOnPin - "
"Couldn't set KSPROPERTY_PIN_PROPOSEDATAFORMAT\n")));
break;
}
} while (FALSE);
LOG((MSP_TRACE, STREAM_PREFIX("SetDataFormatOnPin - exit 0x%08x\n"),
hr));
return hr;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
HRESULT
CRCAMSPStream::SetMediaTypeFormat(
AM_MEDIA_TYPE* pAmMediaType,
BYTE* pformat,
ULONG length)
{
// Is the current format buffer big enough?
if(pAmMediaType->cbFormat < length)
{
// allocate the new format buffer
BYTE *pNewFormat = (PBYTE)CoTaskMemAlloc(length);
if(pNewFormat == NULL)
{
return E_OUTOFMEMORY;
}
// delete the old format
if(pAmMediaType->pbFormat != NULL)
{
CoTaskMemFree((PVOID)pAmMediaType->pbFormat);
}
pAmMediaType->cbFormat = length;
pAmMediaType->pbFormat = pNewFormat;
}
memcpy(pAmMediaType->pbFormat, pformat, length);
return S_OK;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ConfigureCapture
//
// This is a helper function that sets up the allocator properties on the
// capture filter, given the output pin on the capture filter or terminal and
// the input pin on the render filter or terminal.
//
// we are already in a lock; no need to do locking here.
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
HRESULT CRCAMSPStream::ConfigureCapture( IPin * pOutputPin,
IPin * pInputPin )
{
LOG((MSP_TRACE, STREAM_PREFIX("ConfigureCapture - enter")));
//
// If our RCA filter is the capturer, then we don't have anything
// to do.
//
if ( m_Direction == TD_RENDER )
{
LOG((MSP_TRACE, STREAM_PREFIX("ConfigureCapture - "
"not a capture stream so nothing to do - exit S_OK")));
return S_OK;
}
//
// The correct buffer size was obtained from the RCA filter's bridge pin
// during the VC Handle TSP data handling. Since we already have the VC
// handle before we get here, we know that m_dwBuffferSizeOnWire is valid.
//
_ASSERTE( m_fHaveVCHandle );
//
// Get the buffer negotiation interface on the capturer's output pin.
//
HRESULT hr;
IAMBufferNegotiation * pNegotiation;
hr = pOutputPin->QueryInterface(IID_IAMBufferNegotiation,
(void **) &pNegotiation);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("ConfigureCapture - "
"IAMBufferNegotiation QI failed - hr = 0x%08x"), hr));
return hr;
}
//
// Suggest allocator properties on the capturer.
// Since we insert the G711 codec when we connect, we actually want
// buffers on the capturer that are twice as big as the buffers we
// want to send out on the wire.
//
ALLOCATOR_PROPERTIES props;
props.cBuffers = 32; // we use 32 to try to avoid starvation
props.cbAlign = -1; // -1 means "default"
props.cbPrefix = -1; // -1 means "default"
props.cbBuffer = m_dwBufferSizeOnWire * 2; // see note above
hr = pNegotiation->SuggestAllocatorProperties(&props);
pNegotiation->Release();
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("ConfigureCapture - "
"SuggestAllocatorProperties failed - exit 0x%08x"), hr));
return hr;
}
LOG((MSP_TRACE, STREAM_PREFIX("ConfigureCapture - exit S_OK")));
return S_OK;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
//
// This function is for debugging purposes only. It prints
// some debug spew telling you various information about
// media formats and allocator properties. It's called after
// connection has taken place. pPin is the output pin of the
// capture filter.
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
HRESULT CRCAMSPStream::ExamineCaptureSettings(IPin *pPin)
{
LOG((MSP_TRACE, STREAM_PREFIX("ExamineCaptureSettings - enter")));
HRESULT hr;
IAMBufferNegotiation * pNegotiation = NULL;
hr = pPin->QueryInterface(IID_IAMBufferNegotiation,
(void **) &pNegotiation
);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("ExamineCaptureSettings - "
"IAMBufferNegotiation QI failed on pin 0x%08x; hr = 0x%08x"),
pPin, hr));
return hr;
}
ALLOCATOR_PROPERTIES prop;
hr = pNegotiation->GetAllocatorProperties(&prop);
pNegotiation->Release();
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("ExamineCaptureSettings - "
"GetAllocatorProperties failed; hr = 0x%08x"),
hr));
return hr;
}
LOG((MSP_TRACE, "GetAllocatorProperties info:\n"
"buffer count: %d\n"
"size of each buffer: %d bytes\n"
"alignment multiple: %d\n"
"each buffer has a prefix: %d bytes",
prop.cBuffers,
prop.cbBuffer,
prop.cbAlign,
prop.cbPrefix
));
IAMStreamConfig * pConfig = NULL;
hr = pPin->QueryInterface(IID_IAMStreamConfig,
(void **) &pConfig
);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("ExamineCaptureSettings - "
"IAMStreamConfig QI failed on pin 0x%08x; hr = 0x%08x"), pPin, hr));
return hr;
}
AM_MEDIA_TYPE * pMediaType;
hr = pConfig->GetFormat(&pMediaType);
pConfig->Release();
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("ExamineCaptureSettings - "
"GetFormat failed; hr = 0x%08x"), hr));
return hr;
}
_ASSERTE( pMediaType->cbFormat >= sizeof(WAVEFORMATEX) );
LOG((MSP_TRACE, "GetFormat info:\n"
"sample size: %d bytes\n"
"channels: %d\n"
"samples per second: %d\n"
"bits per sample: %d\n",
pMediaType->lSampleSize,
((WAVEFORMATEX *) (pMediaType->pbFormat) )->nChannels,
((WAVEFORMATEX *) (pMediaType->pbFormat) )->nSamplesPerSec,
((WAVEFORMATEX *) (pMediaType->pbFormat) )->wBitsPerSample
));
DeleteMediaType(pMediaType);
LOG((MSP_TRACE, STREAM_PREFIX("ExamineCaptureSettings - "
"exit S_OK")));
return S_OK;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
// Add the terminal to the graph and connect it to our
// filters, if it is not already in use.
//
HRESULT CRCAMSPStream::ConnectTerminal(ITTerminal * pTerminal)
{
LOG((MSP_TRACE, STREAM_PREFIX("ConnectTerminal - enter")));
//
// Find out the terminal's internal state.
//
TERMINAL_STATE state;
HRESULT hr;
hr = pTerminal->get_State( &state );
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("ConnectTerminal - "
"get_State on terminal failed - exit 0x%08x"), hr));
return hr;
}
//
// If we've already connected the terminal on this stream, then
// there is nothing for us to do. Just assert that the terminal
// also thinks it's connected.
//
if ( m_fTerminalConnected )
{
_ASSERTE( state == TS_INUSE );
LOG((MSP_ERROR, STREAM_PREFIX("ConnectTerminal - "
"terminal already connected on this stream - exit S_OK")));
return S_OK;
}
//
// Otherwise we need to connect the terminal on this call. If the
// terminal is already connected on another call, we must fail. Note
// that since we are making several calls on the terminal here, the
// terminal could become connected on another call while we are
// in the process of doing this. If this happens, the we will just fail
// later.
//
if ( state == TS_INUSE )
{
LOG((MSP_ERROR, STREAM_PREFIX("ConnectTerminal - "
"terminal in use - exit TAPI_E_TERMINALINUSE")));
return TAPI_E_TERMINALINUSE;
}
//
// Get the ITTerminalControl interface.
//
ITTerminalControl * pTerminalControl;
hr = m_Terminals[0]->QueryInterface(IID_ITTerminalControl,
(void **) &pTerminalControl);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("ConnectTerminal - "
"QI for ITTerminalControl failed - exit 0x%08x"), hr));
return hr;
}
//
// Find out how many pins the terminal has. If not one then bail as
// we have no idea what to do with multiple-pin terminals at this point.
//
DWORD dwNumPinsAvailable;
hr = pTerminalControl->ConnectTerminal(m_pIGraphBuilder,
m_Direction,
&dwNumPinsAvailable,
NULL);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("ConnectTerminal - "
"query for number of terminal pins failed - exit 0x%08x"), hr));
pTerminalControl->Release();
return hr;
}
if ( 1 != dwNumPinsAvailable )
{
LOG((MSP_ERROR, STREAM_PREFIX("ConnectTerminal - "
"unsupported number of terminal pins - exit E_FAIL")));
pTerminalControl->Release();
return E_FAIL;
}
IPin * pTerminalPin;
//
// Actually connect the terminal.
//
hr = pTerminalControl->ConnectTerminal(m_pIGraphBuilder,
m_Direction,
&dwNumPinsAvailable,
&pTerminalPin);
if ( FAILED(hr) )
{
pTerminalControl->Release();
LOG((MSP_ERROR, STREAM_PREFIX("ConnectTerminal - "
"ConnectTerminal on terminal failed - exit 0x%08x"), hr));
return hr;
}
//
// also try to check if the terminal returned a bad pin.
//
if ( IsBadReadPtr(pTerminalPin, sizeof(IPin)) )
{
pTerminalControl->Release();
LOG((MSP_ERROR, STREAM_PREFIX("ConnectTerminal - "
"ConnectTerminal on terminal succeeded but returned a bad pin - "
"returning E_POINTER")));
return E_POINTER;
}
//
// Now make the connection between our filters and the terminal's pin.
//
hr = ConnectToTerminalPin(pTerminalPin);
pTerminalPin->Release();
if ( FAILED(hr) )
{
pTerminalControl->DisconnectTerminal(m_pIGraphBuilder, 0);
pTerminalControl->Release();
LOG((MSP_ERROR, STREAM_PREFIX("ConnectTerminal - "
"ConnectToTerminalPin failed - exit 0x%08x"), hr));
return hr;
}
//
// Now we are actually connected. Update our state and perform postconnection
// (ignore postconnection error code).
//
m_fTerminalConnected = TRUE;
pTerminalControl->CompleteConnectTerminal();
pTerminalControl->Release();
LOG((MSP_TRACE, STREAM_PREFIX("ConnectTerminal - exit S_OK")));
return S_OK;
}
HRESULT CRCAMSPStream::TryToConnect(
IPin * pOutputPin, // on the capture filter or terminal
IPin * pInputPin // on the render filter or terminal
)
{
LOG((MSP_TRACE, STREAM_PREFIX("TryToConnect - enter")));
//
// We must have our RCA filter at this point, because Init succeeded.
//
_ASSERTE( m_pFilter );
//
// Prepare the G711 filter (create and add or remove existing and readd).
//
HRESULT hr;
hr = PrepareG711Filter();
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("TryToConnect - "
"failed to prepare G711 - exit 0x%08x"), hr));
return hr;
}
_ASSERTE( m_pG711Filter != NULL );
//
// Now connect.
//
hr = ConnectUsingG711(pOutputPin, pInputPin);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("TryToConnect - "
"failed to connect - exit 0x%08x"), hr));
return hr;
}
LOG((MSP_TRACE, STREAM_PREFIX("TryToConnect - exit S_OK")));
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////
//
// ConnectUsingG711
//
// This method connects pOutputPin to pInputPin using the G711 codec and
// returns success if the connection was successful. If the connection was
// unsuccessful, it does its best to clean up its references, but makes no
// attempt to disconnect the filters, as we always break leftover connections
// by removing and readding the g711 filter.
//
// Assumptions:
// * direct connection has already failed
// * the g711 codec has been created and added to the graph
//
// Parameters:
// IN IPin * pOutputPin -- output pin on the capture filter or terminal
// IN IPin * pInputPin -- input pin on the render filter or terminal
//
//
HRESULT CRCAMSPStream::ConnectUsingG711(
IN IPin * pOutputPin,
IN IPin * pInputPin
)
{
HRESULT hr;
//
// Get the G711 codec filter's input pin.
//
IPin * pG711InputPin = NULL;
hr = FindPinInFilter(
false, // want input pin
m_pG711Filter,
&pG711InputPin
);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("ConnectUsingG711 - could not find "
"G711 codec's input pin - 0x%08x"), hr));
return hr;
}
AM_MEDIA_TYPE mt;
WAVEFORMATEX wfx;
AM_MEDIA_TYPE * pmt = NULL;
if ( m_Direction == TD_RENDER ) // capture filter is RCA
{
wfx.wFormatTag = UseMulaw() ? WAVE_FORMAT_MULAW :
WAVE_FORMAT_ALAW;
wfx.wBitsPerSample = 8;
wfx.nChannels = 1;
wfx.nSamplesPerSec = 8000;
wfx.nBlockAlign = wfx.wBitsPerSample * wfx.nChannels / 8;
wfx.nAvgBytesPerSec = ((DWORD) wfx.nBlockAlign * wfx.nSamplesPerSec);
wfx.cbSize = 0;
mt.majortype = MEDIATYPE_Audio;
mt.subtype = UseMulaw() ? MEDIASUBTYPE_MULAWAudio :
MEDIASUBTYPE_ALAWAudio;
mt.bFixedSizeSamples = TRUE;
mt.bTemporalCompression = FALSE;
mt.lSampleSize = 0;
mt.formattype = FORMAT_WaveFormatEx;
mt.pUnk = NULL;
mt.cbFormat = sizeof(WAVEFORMATEX);
mt.pbFormat = (BYTE*)&wfx;
pmt = &mt;
}
//
// Connect the capture filter's output pin to the G711 codec filter's
// input pin. Release reference to the pin when done.
//
hr = m_pIGraphBuilder->ConnectDirect(
pOutputPin,
pG711InputPin,
pmt
);
pG711InputPin->Release();
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("ConnectUsingG711 - could not connect "
"G711 codec's input pin - 0x%08x"), hr));
return hr;
}
//
// Get the G711 codec filter's output pin.
//
IPin * pG711OutputPin = NULL;
hr = FindPinInFilter(
true, // want output pin
m_pG711Filter,
&pG711OutputPin
);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("ConnectUsingG711 - could not find "
"G711 codec's input pin - 0x%08x"), hr));
return hr;
}
//
// Connect the G711 codec filter's output pin to the render filter's
// input pin. Release reference to the pin when done.
//
hr = m_pIGraphBuilder->ConnectDirect(
pG711OutputPin,
pInputPin,
NULL
);
pG711OutputPin->Release();
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("ConnectUsingG711 - could not connect "
"G711 codec's output pin - 0x%08x"), hr));
return hr;
}
LOG((MSP_TRACE, STREAM_PREFIX("ConnectUsingG711 - exit S_OK")));
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
HRESULT CRCAMSPStream::ConnectToTerminalPin(IPin * pTerminalPin)
{
LOG((MSP_TRACE, STREAM_PREFIX("ConnectToTerminalPin - enter")));
HRESULT hr = S_OK;
if ( m_Direction == TD_CAPTURE )
{
//
// First set the audio format on a capture terminal's pin. If it
// doesn't work, we know that we won't be able to connect, so it
// saves us from getting in too deep in the failure case. If it
// does work, it will significantly speed up format negotiation
// during connection.
//
hr = ::SetAudioFormat(pTerminalPin,
BITS_PER_SAMPLE_AT_TERMINAL,
SAMPLE_RATE_AT_TERMINAL);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("ConnectToTerminalPin - "
"could not set audio format - exit 0x%08x"), hr));
return hr;
}
}
//
// Get the right pin on our own RCA transport filter.
//
IPin * pMyPin;
hr = FindPin( &pMyPin );
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("ConnectToTerminalPin - "
"could not find pin - exit 0x%08x"), hr));
return hr;
}
//
// From here on, it's helpful to keep track of these pins according to
// directionality -- saves lots of conditionals later on.
// OUTPUT pin from CAPTURE filter; INPUT pin from RENDER filter
//
IPin * pOutputPin = ( m_Direction == TD_RENDER ) ? pMyPin : pTerminalPin;
IPin * pInputPin = ( m_Direction == TD_CAPTURE ) ? pMyPin : pTerminalPin;
//
// Configure the capturer with correct buffer sizes, etc.
//
hr = ConfigureCapture(pOutputPin,
pInputPin);
if ( SUCCEEDED(hr) )
{
//
// Connect the capturer to the renderer.
//
hr = TryToConnect(pOutputPin,
pInputPin);
if ( SUCCEEDED(hr) )
{
//
// dump some useful debug info
// don't care if this fails...
//
ExamineCaptureSettings(pOutputPin);
}
}
pMyPin->Release();
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("ConnectToTerminalPin - "
"connection failure - exit 0x%08x"), hr));
return hr;
}
LOG((MSP_TRACE, STREAM_PREFIX("ConnectToTerminalPin - exit S_OK")));
return S_OK;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
HRESULT CRCAMSPStream::FindPinInFilter(
BOOL bWantOutputPin, // IN: if false, we want the input pin
IBaseFilter * pFilter, // IN: the filter to examine
IPin ** ppPin // OUT: the pin we found
)
{
HRESULT hr;
IEnumPins * pEnumPins;
*ppPin = NULL;
// enumerate the pins on the filter
hr = pFilter->EnumPins( &pEnumPins );
if (!(SUCCEEDED(hr)))
{
return hr;
}
// go through the pins
while (TRUE)
{
PIN_DIRECTION pd;
hr = pEnumPins->Next( 1, ppPin, NULL );
if (S_OK != hr)
{
// didn't find a pin!
break;
}
// get the pin info
hr = (*ppPin)->QueryDirection( &pd );
// does it meet the criteria?
if (bWantOutputPin && (pd == PINDIR_OUTPUT))
{
// yes
break;
}
if ( ! bWantOutputPin && (pd == PINDIR_INPUT))
{
// yes
break;
}
(*ppPin)->Release();
*ppPin = NULL;
}
pEnumPins->Release();
if (NULL == *ppPin)
{
// error
return E_FAIL;
}
return S_OK;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// FindPin
//
// Finds the first pin in the filter that meets criteria.
// For TD_RENDER (render terminal, capture filter),
// the pin must be direction PINDIR_OUTPUT
// For TD_CAPTURE (capture terminal, render filter),
// the pin must be direction PINDIR_INPUT
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
HRESULT
CRCAMSPStream::FindPin(
IPin ** ppPin
)
{
return FindPinInFilter(m_Direction == TD_RENDER,
m_pFilter,
ppPin);
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// ProcessGraphEvent
//
// Sends an event to the app when we get an event from the filter graph.
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
HRESULT CRCAMSPStream::ProcessGraphEvent(
IN long lEventCode,
IN LONG_PTR lParam1,
IN LONG_PTR lParam2
)
{
LOG((MSP_EVENT, STREAM_PREFIX("ProcessGraphEvent - enter")));
HRESULT hr = S_OK;
switch (lEventCode)
{
case EC_COMPLETE:
hr = FireEvent(CALL_STREAM_INACTIVE, (HRESULT) lParam1, CALL_CAUSE_UNKNOWN);
break;
case EC_USERABORT:
hr = FireEvent(CALL_STREAM_INACTIVE, S_OK, CALL_CAUSE_UNKNOWN);
break;
case EC_ERRORABORT:
case EC_STREAM_ERROR_STOPPED:
case EC_STREAM_ERROR_STILLPLAYING:
case EC_ERROR_STILLPLAYING:
hr = FireEvent(CALL_STREAM_FAIL, (HRESULT) lParam1, CALL_CAUSE_UNKNOWN);
break;
default:
LOG((MSP_EVENT, STREAM_PREFIX("ProcessGraphEvent - "
"ignoring event code %d"), lEventCode));
break;
}
if ( FAILED(hr) )
{
LOG((MSP_ERROR, STREAM_PREFIX("ProcessGraphEvent - "
"FireEvent failed - exit 0x%08x"), hr));
return hr;
}
LOG((MSP_EVENT, STREAM_PREFIX("ProcessGraphEvent - exit S_OK")));
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
// FireEvent
//
// Fires an event to the application. Does its own locking.
//
HRESULT CRCAMSPStream::FireEvent(
IN MSP_CALL_EVENT type,
IN HRESULT hrError,
IN MSP_CALL_EVENT_CAUSE cause
)
{
LOG((MSP_EVENT, STREAM_PREFIX("FireEvent - enter")));
//
// First, need to check if the call is shutting down. This is important
// because UnselectTerminal can fire an event, and UnselectTerminal can
// be called within ITStream::Shutdown. We can safely discard such
// events because there is nothing the app can do with them anyway.
//
// Note on locking: It is convenient to check the m_pMSPCall here
// and we don't use it until the end of the method, so we simply lock
// during the entire method. This could be optimized at the expense of
// some code complexity; note that we also need to lock while accessing
// m_Terminals.
//
CLock lock(m_lock);
if ( m_pMSPCall == NULL )
{
LOG((MSP_EVENT, STREAM_PREFIX("FireEvent - "
"call is shutting down; dropping event - exit S_OK")));
return S_OK;
}
//
// Create the event structure. Must use "new" as it will be
// "delete"d later.
//
MSPEVENTITEM * pEventItem = AllocateEventItem();
if (pEventItem == NULL)
{
LOG((MSP_ERROR, STREAM_PREFIX("FireEvent - "
"can't create MSPEVENTITEM structure - exit E_OUTOFMEMORY")));
return E_OUTOFMEMORY;
}
//
// Fill in the necessary fields for the event structure.
//
pEventItem->MSPEventInfo.dwSize = sizeof(MSP_EVENT_INFO);
pEventItem->MSPEventInfo.Event = ME_CALL_EVENT;
ITTerminal * pTerminal = NULL;
if ( 0 != m_Terminals.GetSize() )
{
_ASSERTE( 1 == m_Terminals.GetSize() );
pTerminal = m_Terminals[0];
pTerminal->AddRef();
}
ITStream * pStream = (ITStream *) this;
pStream->AddRef();
pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.Type = type;
pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.Cause = cause;
pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.pStream = pStream;
pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.pTerminal = pTerminal;
pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.hrError = hrError;
//
// Send the event to the app.
//
HRESULT hr = m_pMSPCall->HandleStreamEvent(pEventItem);
if (FAILED(hr))
{
LOG((MSP_ERROR, STREAM_PREFIX("FireEvent - "
"HandleStreamEvent failed - returning 0x%08x"), hr));
pStream->Release();
pTerminal->Release();
FreeEventItem(pEventItem);
return hr;
}
LOG((MSP_EVENT, STREAM_PREFIX("FireEvent - exit S_OK")));
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
// UseMulaw
//
// Helper function called when we need to decide if to use Mulaw or Alaw.
// This is simply delegated to the call.
//
BOOL CRCAMSPStream::UseMulaw( void )
{
return ( (CRCAMSPCall *) m_pMSPCall )->UseMulaw();
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
// Helper functions -- non-class members.
//
HRESULT SetAudioFormat(
IN IUnknown* pIUnknown,
IN WORD wBitPerSample,
IN DWORD dwSampleRate
)
/*++
Routine Description:
Get the IAMStreamConfig interface on the object and config the
audio format by using WAVEFORMATEX.
Arguments:
pIPin - a capture terminal.
wBitPerSample - the number of bits in each sample.
dwSampleRate - number of samples per second.
Return Value:
HRESULT
--*/
{
LOG((MSP_TRACE, "SetAudioFormat - enter"));
HRESULT hr;
IAMStreamConfig * pIAMStreamConfig;
hr = pIUnknown->QueryInterface(
IID_IAMStreamConfig,
(void **)&pIAMStreamConfig
);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, "SetAudioFormat - "
"Can't get IAMStreamConfig interface - exit 0x%08x", hr));
return hr;
}
AM_MEDIA_TYPE mt;
WAVEFORMATEX wfx;
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.wBitsPerSample = wBitPerSample;
wfx.nChannels = 1;
wfx.nSamplesPerSec = dwSampleRate;
wfx.nBlockAlign = wfx.wBitsPerSample * wfx.nChannels / 8;
wfx.nAvgBytesPerSec = ((DWORD) wfx.nBlockAlign * wfx.nSamplesPerSec);
wfx.cbSize = 0;
mt.majortype = MEDIATYPE_Audio;
mt.subtype = MEDIASUBTYPE_PCM;
mt.bFixedSizeSamples = TRUE;
mt.bTemporalCompression = FALSE;
mt.lSampleSize = 0;
mt.formattype = FORMAT_WaveFormatEx;
mt.pUnk = NULL;
mt.cbFormat = sizeof(WAVEFORMATEX);
mt.pbFormat = (BYTE*)&wfx;
//
// set the format of the audio capture terminal.
//
hr = pIAMStreamConfig->SetFormat(&mt);
pIAMStreamConfig->Release();
if ( FAILED(hr) )
{
LOG((MSP_ERROR, "SetAudioFormat - SetFormat returns error: %8x", hr));
return hr;
}
LOG((MSP_TRACE, "SetAudioFormat - exit S_OK"));
return S_OK;
}
// eof