1088 lines
27 KiB
C++
1088 lines
27 KiB
C++
/*++
|
|
|
|
Copyright (c) 1999 - 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
sampstrm.cpp
|
|
|
|
Abstract:
|
|
|
|
This module contains the implementation for a sample MSP stream class.
|
|
|
|
--*/
|
|
|
|
#include "stdafx.h"
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
CSampleMSPStream::CSampleMSPStream() : CMSPStream()
|
|
{
|
|
LOG((MSP_TRACE, "CSampleMSPStream::CSampleMSPStream entered."));
|
|
|
|
m_fTransportConfigured = FALSE;
|
|
m_fTerminalConnected = FALSE;
|
|
m_DesiredGraphState = State_Stopped;
|
|
m_ActualGraphState = State_Stopped;
|
|
|
|
//
|
|
// INSERT HERE: initialize other data members
|
|
//
|
|
|
|
LOG((MSP_TRACE, "CSampleMSPStream::CSampleMSPStream exited."));
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
CSampleMSPStream::~CSampleMSPStream()
|
|
{
|
|
LOG((MSP_TRACE, "CSampleMSPStream::~CSampleMSPStream entered."));
|
|
LOG((MSP_TRACE, "CSampleMSPStream::~CSampleMSPStream exited."));
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
void CSampleMSPStream::FinalRelease()
|
|
{
|
|
LOG((MSP_TRACE, "CSampleMSPStream::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() );
|
|
|
|
if ( m_fTransportConfigured )
|
|
{
|
|
//
|
|
// INSERT HERE: Remove our transport filters from the graph and
|
|
// release them.
|
|
//
|
|
}
|
|
|
|
//
|
|
// Call the base class method to clean up everything else.
|
|
//
|
|
|
|
CMSPStream::FinalRelease();
|
|
|
|
LOG((MSP_TRACE, "CSampleMSPStream::FinalRelease exited."));
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
STDMETHODIMP CSampleMSPStream::get_Name (
|
|
OUT BSTR * ppName
|
|
)
|
|
{
|
|
LOG((MSP_TRACE, "CSampleMSPStream::get_Name - enter"));
|
|
|
|
//
|
|
// Check argument.
|
|
//
|
|
|
|
if (!ppName)
|
|
{
|
|
LOG((MSP_TRACE, "CSampleMSPStream::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 = LoadString( _Module.GetModuleInstance(),
|
|
ulID,
|
|
wszName,
|
|
ciAllocSize - 1 );
|
|
|
|
if ( iReturn == 0 )
|
|
{
|
|
_ASSERTE( FALSE );
|
|
|
|
*ppName = NULL;
|
|
|
|
LOG((MSP_ERROR, "CSampleMSPStream::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, "CSampleMSPStream::get_Name - "
|
|
"SysAllocString failed - returning E_OUTOFMEMORY"));
|
|
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
LOG((MSP_TRACE, "CSampleMSPStream::get_Name - exit S_OK"));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// This implementation, typical of the simplest MSPs, limits each stream to
|
|
// having at most one terminal selected at a time.
|
|
//
|
|
|
|
STDMETHODIMP CSampleMSPStream::SelectTerminal(
|
|
IN ITTerminal * pTerminal
|
|
)
|
|
{
|
|
LOG((MSP_TRACE, "CSampleMSPStream::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, "CSampleMSPStream::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, "CSampleMSPStream::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, "CSampleMSPStream::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, "CSampleMSPStream::SelectTerminal - exit S_OK"));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
STDMETHODIMP CSampleMSPStream::UnselectTerminal (
|
|
IN ITTerminal * pTerminal
|
|
)
|
|
{
|
|
LOG((MSP_TRACE, "CSampleMSPStream::UnselectTerminal - enter"));
|
|
|
|
CLock lock(m_lock);
|
|
|
|
//
|
|
// Use base class method to remove terminal from our list of terminals.
|
|
//
|
|
// Note: the failure cases are messy here. In this sample implementation
|
|
// we choose to return an error code but release our reference to the
|
|
// terminal when disconnection fails. This is typically better than
|
|
// keeping our reference to the terminal, which would cause us to leak
|
|
// the terminal whenever disconnection fails.
|
|
//
|
|
|
|
HRESULT hr = CMSPStream::UnselectTerminal(pTerminal);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
LOG((MSP_ERROR, "CSampleMSPStream::UnselectTerminal - "
|
|
"base class method failed - exit 0x%08x", hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Stop the graph and disconnect the terminal if this call had it
|
|
// connected. (We need a stopped graph to disconnect properly, and
|
|
// couldn't have started the graph if the terminal isn't connected.)
|
|
//
|
|
|
|
if ( m_fTerminalConnected )
|
|
{
|
|
//
|
|
// At this point we need to make sure the stream is stopped.
|
|
// We can't use our own StopStream method because it
|
|
// (1) changes the desired graph state to Stopped and
|
|
// (2) does nothing if no terminal has been selected (which it now
|
|
// thinks is the case)
|
|
//
|
|
|
|
_ASSERTE( m_fTransportConfigured );
|
|
|
|
//
|
|
// Stop the stream via the base class method.
|
|
//
|
|
|
|
hr = CMSPStream::StopStream();
|
|
|
|
m_ActualGraphState = State_Stopped;
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
LOG((MSP_ERROR, "CSampleMSPStream::UnselectTerminal - "
|
|
"Stop failed - 0x%08x", hr));
|
|
|
|
// don't return hr -- we really want to continue and
|
|
// disconnect if we can!
|
|
}
|
|
|
|
//
|
|
// Get the ITTerminalControl interface.
|
|
//
|
|
|
|
ITTerminalControl * pTerminalControl;
|
|
|
|
hr = pTerminal->QueryInterface(IID_ITTerminalControl,
|
|
(void **) &pTerminalControl);
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
LOG((MSP_ERROR, "CSampleMSPStream::UnselectTerminal - "
|
|
"QI for ITTerminalControl failed - exit 0x%08x", hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Disconnect the terminal.
|
|
//
|
|
|
|
hr = pTerminalControl->DisconnectTerminal(m_pIGraphBuilder, 0);
|
|
|
|
pTerminalControl->Release();
|
|
|
|
m_fTerminalConnected = FALSE;
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
LOG((MSP_ERROR, "CSampleMSPStream::UnselectTerminal - "
|
|
"DisconnectTerminal failed - exit 0x%08x", hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// INSERT HERE: Additional disconnection may be needed, for example
|
|
// for transport fitlers or transform filters that need to be
|
|
// used for some terminals but not for others.
|
|
//
|
|
}
|
|
|
|
LOG((MSP_TRACE, "CSampleMSPStream::UnselectTerminal - exit S_OK"));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
STDMETHODIMP CSampleMSPStream::StartStream (void)
|
|
{
|
|
LOG((MSP_TRACE, "CSampleMSPStream::StartStream - enter"));
|
|
|
|
CLock lock(m_lock);
|
|
|
|
m_DesiredGraphState = State_Running;
|
|
|
|
//
|
|
// Can't start the stream if our transport filters are not configured.
|
|
//
|
|
|
|
if ( ! m_fTransportConfigured )
|
|
{
|
|
LOG((MSP_WARN, "CSampleMSPStream::PauseStream - "
|
|
"transport not configured 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, "CSampleMSPStream::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, "CSampleMSPStream::StartStream - "
|
|
"our ConnectTerminal failed - exit 0x%08x", hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Run the stream via the base class method.
|
|
//
|
|
|
|
hr = CMSPStream::StartStream();
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
//
|
|
// Failed to run -- tell the app.
|
|
//
|
|
|
|
FireEvent(CALL_STREAM_FAIL, hr, CALL_CAUSE_UNKNOWN);
|
|
|
|
LOG((MSP_ERROR, "CSampleMSPStream::StartStream - "
|
|
"Run failed - exit 0x%08x", hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Fire event if this just made us active.
|
|
//
|
|
|
|
if ( m_ActualGraphState != State_Running )
|
|
{
|
|
m_ActualGraphState = State_Running;
|
|
|
|
HRESULT hr2 = FireEvent(CALL_STREAM_ACTIVE, hr, CALL_CAUSE_LOCAL_REQUEST);
|
|
|
|
if ( FAILED(hr2) )
|
|
{
|
|
LOG((MSP_ERROR, "CSampleMSPStream::StartStream - "
|
|
"FireEvent failed - exit 0x%08x", hr2));
|
|
|
|
return hr2;
|
|
}
|
|
}
|
|
|
|
LOG((MSP_TRACE, "CSampleMSPStream::StartStream - exit S_OK"));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
STDMETHODIMP CSampleMSPStream::PauseStream (void)
|
|
{
|
|
LOG((MSP_TRACE, "CSampleMSPStream::PauseStream - enter"));
|
|
|
|
CLock lock(m_lock);
|
|
|
|
m_DesiredGraphState = State_Paused;
|
|
|
|
//
|
|
// Can't pause the stream if our transport filters are not configured.
|
|
//
|
|
|
|
if ( ! m_fTransportConfigured )
|
|
{
|
|
LOG((MSP_WARN, "CSampleMSPStream::PauseStream - "
|
|
"transport not configured 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, "CSampleMSPStream::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, "CSampleMSPStream::StartStream - "
|
|
"our ConnectTerminal failed - exit 0x%08x", hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Pause the stream via the base class method.
|
|
//
|
|
|
|
hr = CMSPStream::PauseStream();
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
//
|
|
// Failed to pause -- tell the app.
|
|
//
|
|
|
|
FireEvent(CALL_STREAM_FAIL, hr, CALL_CAUSE_UNKNOWN);
|
|
|
|
LOG((MSP_ERROR, "CSampleMSPStream::PauseStream - "
|
|
"Pause failed - exit 0x%08x", hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Fire event if this just made us inactive.
|
|
//
|
|
|
|
if ( m_ActualGraphState == State_Running )
|
|
{
|
|
HRESULT hr2 = FireEvent(CALL_STREAM_INACTIVE, hr, CALL_CAUSE_LOCAL_REQUEST);
|
|
|
|
if ( FAILED(hr2) )
|
|
{
|
|
m_ActualGraphState = State_Paused;
|
|
|
|
LOG((MSP_ERROR, "CSampleMSPStream::PauseStream - "
|
|
"FireEvent failed - exit 0x%08x", hr2));
|
|
|
|
return hr2;
|
|
}
|
|
}
|
|
|
|
m_ActualGraphState = State_Paused;
|
|
|
|
LOG((MSP_TRACE, "CSampleMSPStream::PauseStream - exit S_OK"));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
STDMETHODIMP CSampleMSPStream::StopStream (void)
|
|
{
|
|
LOG((MSP_TRACE, "CSampleMSPStream::StopStream - enter"));
|
|
|
|
CLock lock(m_lock);
|
|
|
|
m_DesiredGraphState = State_Stopped;
|
|
|
|
//
|
|
// Nothing to do if our transport is not configured.
|
|
//
|
|
|
|
if ( ! m_fTransportConfigured )
|
|
{
|
|
LOG((MSP_WARN, "CSampleMSPStream::StopStream - "
|
|
"transport not configured - exit S_OK"));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Nothing to do if no terminal has been selected.
|
|
//
|
|
|
|
if ( 0 == m_Terminals.GetSize() )
|
|
{
|
|
LOG((MSP_WARN, "CSampleMSPStream::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) )
|
|
{
|
|
//
|
|
// Failed to stop -- tell the app.
|
|
//
|
|
|
|
FireEvent(CALL_STREAM_FAIL, hr, CALL_CAUSE_UNKNOWN);
|
|
|
|
m_DesiredGraphState = m_ActualGraphState;
|
|
|
|
LOG((MSP_ERROR, "CSampleMSPStream::StopStream - "
|
|
"Stop failed - exit 0x%08x", hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Fire event if this just made us inactive.
|
|
//
|
|
|
|
if ( m_ActualGraphState == State_Running )
|
|
{
|
|
HRESULT hr2 = FireEvent(CALL_STREAM_INACTIVE, hr, CALL_CAUSE_LOCAL_REQUEST);
|
|
|
|
if ( FAILED(hr2) )
|
|
{
|
|
m_ActualGraphState = State_Stopped;
|
|
|
|
LOG((MSP_ERROR, "CSampleMSPStream::StopStream - "
|
|
"FireEvent failed - exit 0x%08x", hr2));
|
|
|
|
return hr2;
|
|
}
|
|
}
|
|
|
|
m_ActualGraphState = State_Stopped;
|
|
|
|
LOG((MSP_TRACE, "CSampleMSPStream::StopStream - exit S_OK"));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// A real MSP would have some arguments to this method, which would then be
|
|
// used to configure transport filters. The types of information passed here
|
|
// vary widely. For example, some MSPs may need to configure their transport
|
|
// filters with a GUID that indicates which device to use for streaming. This
|
|
// info would be passed to the MSP from the TSP via TAPI 3.0 (see sampcall.cpp).
|
|
//
|
|
|
|
HRESULT CSampleMSPStream::ConfigureTransport(void)
|
|
{
|
|
LOG((MSP_TRACE, "CSampleMSPStream::ConfigureTransport - enter"));
|
|
|
|
CLock lock(m_lock);
|
|
|
|
//
|
|
// INSERT HERE:
|
|
// * create the transport filter(s)
|
|
// * configure the transport filter(s) based on passe-in info
|
|
// * add filters to our graph
|
|
// * if some transport filters always need to be connected together
|
|
// (irrespective of what terminals are used, etc.) then they can be
|
|
// connected now).
|
|
//
|
|
|
|
m_fTransportConfigured = TRUE;
|
|
|
|
LOG((MSP_TRACE, "CSampleMSPStream::ConfigureTransport - 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 CSampleMSPStream::ConnectTerminal(ITTerminal * pTerminal)
|
|
{
|
|
LOG((MSP_TRACE, "CSampleMSPStream::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, "CSampleMSPStream::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, "CSampleMSPStream::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, "CSampleMSPStream::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, "CSampleMSPStream::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 (we don't
|
|
// expose any such terminals from this MSP).
|
|
//
|
|
|
|
DWORD dwNumPinsAvailable;
|
|
|
|
hr = pTerminalControl->ConnectTerminal(m_pIGraphBuilder,
|
|
0,
|
|
&dwNumPinsAvailable,
|
|
NULL);
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
LOG((MSP_ERROR, "CSampleMSPStream::ConnectTerminal - "
|
|
"query for number of terminal pins failed - exit 0x%08x", hr));
|
|
|
|
pTerminalControl->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
if ( 1 != dwNumPinsAvailable )
|
|
{
|
|
LOG((MSP_ERROR, "CSampleMSPStream::ConnectTerminal - "
|
|
"unsupported number of terminal pins - exit E_FAIL"));
|
|
|
|
pTerminalControl->Release();
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
//
|
|
// Actually connect the terminal.
|
|
//
|
|
|
|
IPin * pTerminalPin;
|
|
|
|
hr = pTerminalControl->ConnectTerminal(m_pIGraphBuilder,
|
|
0,
|
|
&dwNumPinsAvailable,
|
|
&pTerminalPin);
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
pTerminalControl->Release();
|
|
|
|
LOG((MSP_ERROR, "CSampleMSPStream::ConnectTerminal - "
|
|
"ConnectTerminal on terminal failed - exit 0x%08x", hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// 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, "CSampleMSPStream::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, "CSampleMSPStream::ConnectTerminal - exit S_OK"));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
HRESULT CSampleMSPStream::ConnectToTerminalPin(IPin * pTerminalPin)
|
|
{
|
|
LOG((MSP_TRACE, "CSampleMSPStream::ConnectToTerminalPin - enter"));
|
|
|
|
//
|
|
// INSERT HERE: Code to connect the terminal's pin to some pin on some
|
|
// transport filter belonging to the stream. See the documentation on
|
|
// DirectShow in the Windows SDK for help.
|
|
//
|
|
|
|
LOG((MSP_TRACE, "CSampleMSPStream::ConnectToTerminalPin - exit S_OK"));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ProcessGraphEvent
|
|
//
|
|
// Sends an event to the app when we get an event from the filter graph.
|
|
// Note: The DirectShow graph events that the MSP cares about will vary
|
|
// from MSP to MSP. Be sure to modify this code to correctly propagate the
|
|
// events you need, depending on what filters you are using.
|
|
//
|
|
|
|
HRESULT CSampleMSPStream::ProcessGraphEvent(
|
|
IN long lEventCode,
|
|
IN long lParam1,
|
|
IN long lParam2
|
|
)
|
|
{
|
|
LOG((MSP_EVENT, "CSampleMSPStream::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, "CSampleMSPStream::ProcessGraphEvent - "
|
|
"ignoring event code %d", lEventCode));
|
|
break;
|
|
}
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
LOG((MSP_ERROR, "CSampleMSPStream::ProcessGraphEvent - "
|
|
"FireEvent failed - exit 0x%08x", hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
LOG((MSP_EVENT, "CSampleMSPStream::ProcessGraphEvent - exit S_OK"));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FireEvent
|
|
//
|
|
// Sends an event to the application.
|
|
//
|
|
|
|
HRESULT CSampleMSPStream::FireEvent(
|
|
IN MSP_CALL_EVENT type,
|
|
IN HRESULT hrError,
|
|
IN MSP_CALL_EVENT_CAUSE cause
|
|
)
|
|
{
|
|
LOG((MSP_EVENT, "CSampleMSPStream::FireEvent - enter"));
|
|
|
|
//
|
|
// Create the event structure.
|
|
//
|
|
|
|
//
|
|
// Note:
|
|
//
|
|
// MSPEVENTITEMs are allocated with AllocateEventItem and freed with
|
|
// FreeEventItem
|
|
//
|
|
// AllocateEventItem takes an optional argument that specifies how many
|
|
// extra bytes need to be allocated in addition to the size of MSPEVENTITEM
|
|
//
|
|
|
|
MSPEVENTITEM * pEventItem = AllocateEventItem();
|
|
|
|
if (pEventItem == NULL)
|
|
{
|
|
LOG((MSP_ERROR, "CSampleMSPStream::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;
|
|
|
|
|
|
#ifdef MSPLOG
|
|
//
|
|
// Spew some debug output to indicate what this is.
|
|
//
|
|
|
|
char * pszType;
|
|
DWORD dwLevel;
|
|
|
|
switch (type)
|
|
{
|
|
case CALL_NEW_STREAM:
|
|
pszType = "CALL_NEW_STREAM (unexpected)";
|
|
dwLevel = MSP_ERROR;
|
|
break;
|
|
|
|
case CALL_STREAM_FAIL:
|
|
pszType = "CALL_STREAM_FAIL";
|
|
dwLevel = MSP_INFO;
|
|
break;
|
|
|
|
case CALL_TERMINAL_FAIL:
|
|
pszType = "CALL_TERMINAL_FAIL";
|
|
dwLevel = MSP_INFO;
|
|
break;
|
|
|
|
case CALL_STREAM_NOT_USED:
|
|
pszType = "CALL_STREAM_NOT_USED (unexpected)";
|
|
dwLevel = MSP_ERROR;
|
|
break;
|
|
|
|
case CALL_STREAM_ACTIVE:
|
|
pszType = "CALL_STREAM_ACTIVE";
|
|
dwLevel = MSP_INFO;
|
|
break;
|
|
|
|
case CALL_STREAM_INACTIVE:
|
|
pszType = "CALL_STREAM_INACTIVE";
|
|
dwLevel = MSP_INFO;
|
|
break;
|
|
|
|
default:
|
|
pszType = "UNKNOWN EVENT TYPE";
|
|
dwLevel = MSP_ERROR;
|
|
break;
|
|
}
|
|
|
|
LOG((dwLevel, "CSampleMSPStream::FireEvent - "
|
|
"EVENT DUMP: type = %s", pszType));
|
|
LOG((dwLevel, "CSampleMSPStream::FireEvent - "
|
|
"EVENT DUMP: pStream = %p", pStream));
|
|
LOG((dwLevel, "CSampleMSPStream::FireEvent - "
|
|
"EVENT DUMP: pTerminal = %p", pTerminal));
|
|
LOG((dwLevel, "CSampleMSPStream::FireEvent - "
|
|
"EVENT DUMP: hrError = %08x", hrError));
|
|
|
|
#endif // ifdef MSPLOG
|
|
|
|
//
|
|
// Send the event to the app.
|
|
//
|
|
|
|
HRESULT hr = m_pMSPCall->HandleStreamEvent(pEventItem);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
LOG((MSP_ERROR, "CSampleMSPStream::FireEvent - "
|
|
"HandleStreamEvent failed - returning 0x%08x", hr));
|
|
|
|
pStream->Release();
|
|
pTerminal->Release();
|
|
|
|
|
|
//
|
|
// Note:
|
|
//
|
|
// MSPEVENTITEMs are allocated with AllocateEventItem and freed with
|
|
// FreeEventItem
|
|
//
|
|
|
|
FreeEventItem(pEventItem);
|
|
pEventItem = NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
LOG((MSP_EVENT, "CSampleMSPStream::FireEvent - exit S_OK"));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// eof
|