|
|
/*++
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
|