Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

5372 lines
134 KiB

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
confvid.cpp
Abstract:
This module contains implementation of the video send and receive
stream implementations.
Author:
Mu Han (muhan) 15-September-1999
--*/
#include "stdafx.h"
/////////////////////////////////////////////////////////////////////////////
//
// CStreamVideoRecv
//
/////////////////////////////////////////////////////////////////////////////
CStreamVideoRecv::CStreamVideoRecv()
: CIPConfMSPStream()
{
m_szName = L"VideoRecv";
}
HRESULT CStreamVideoRecv::Init(
IN HANDLE hAddress,
IN CMSPCallBase * pMSPCall,
IN IMediaEvent * pIGraphBuilder,
IN DWORD dwMediaType,
IN TERMINAL_DIRECTION Direction
)
/*++
Routine Description:
Init our substream array and then call the base class' Init.
Arguments:
hAddress - a handle to the address, used in identify terminals.
pMSPCall - the call object that owns the stream.
pIGraphBuilder - the filter graph object.
dwMediaType - the mediatype of this stream.
Direction - the direction of this stream.
Return Value:
S_OK,
E_OUTOFMEMORY
--*/
{
LOG((MSP_TRACE, "CStreamVideoRecvVideoSend::Init - enter"));
// initialize the stream array so that the array is not NULL.
if (!m_SubStreams.Grow())
{
LOG((MSP_TRACE, "CStreamVideoRecvVideoSend::Init - return out of memory"));
return E_OUTOFMEMORY;
}
return CIPConfMSPStream::Init(
hAddress, pMSPCall, pIGraphBuilder,dwMediaType, Direction
);
}
HRESULT CStreamVideoRecv::ShutDown()
/*++
Routine Description:
Shut down the stream.
Arguments:
Return Value:
S_OK
--*/
{
CLock lock(m_lock);
// if there are terminals
BOOL fHasTerminal = FALSE;
if (m_Terminals.GetSize() > 0)
{
fHasTerminal = TRUE;
}
// if graph is running
HRESULT hr;
OAFilterState FilterState = State_Stopped;
if (m_pIMediaControl)
{
if (FAILED (hr = m_pIMediaControl->GetState(0, &FilterState)))
{
LOG ((MSP_ERROR, "CStreamAudioRecv::ShutDown failed to query filter state. %d", hr));
FilterState = State_Stopped;
}
}
// if there are branches and configured, we need to disconnect
// the terminals and remove the branches.
if (m_Branches.GetSize() > 0)
{
// Stop the graph before disconnecting the terminals.
hr = CMSPStream::StopStream();
if (FAILED(hr))
{
LOG((MSP_ERROR,
"stream %ws %p failed to stop, %x", m_szName, this, hr));
return hr;
}
for (int i = 0; i < m_Branches.GetSize(); i ++)
{
RemoveOneBranch(&m_Branches[i]);
}
m_Branches.RemoveAll();
}
// release all the substream objects.
for (int i = 0; i < m_SubStreams.GetSize(); i ++)
{
m_SubStreams[i]->Release();
}
m_SubStreams.RemoveAll();
// fire event
if (fHasTerminal && FilterState == State_Running)
{
SendStreamEvent(CALL_STREAM_INACTIVE, CALL_CAUSE_LOCAL_REQUEST, 0, NULL);
}
return CIPConfMSPStream::ShutDown();
}
HRESULT CStreamVideoRecv::InternalCreateSubStream(
OUT ITSubStream ** ppSubStream
)
/*++
Routine Description:
This method creat a substream object and add it into out list.
Arguments:
ppSubStream - the memory location that will store the returned SubStream.
Return Value:
S_OK
E_OUTOFMEMORY
E_NOINTERFACE
--*/
{
CComObject<CSubStreamVideoRecv> * pCOMSubStream;
HRESULT hr;
hr = ::CreateCComObjectInstance(&pCOMSubStream);
if (NULL == pCOMSubStream)
{
LOG((MSP_ERROR, "could not create video recv sub stream:%x", hr));
return hr;
}
ITSubStream* pSubStream;
// get the interface pointer.
hr = pCOMSubStream->_InternalQueryInterface(
__uuidof(ITSubStream),
(void **)&pSubStream
);
if (FAILED(hr))
{
LOG((MSP_ERROR, "Create VideoRecv Substream QueryInterface failed: %x", hr));
delete pCOMSubStream;
return hr;
}
// Initialize the object.
hr = pCOMSubStream->Init(this);
if (FAILED(hr))
{
LOG((MSP_ERROR, "CreateMSPSubStream:call init failed: %x", hr));
pSubStream->Release();
return hr;
}
// Add the SubStream into our list of SubStreams. This takes a refcount.
if (!m_SubStreams.Add(pSubStream))
{
pSubStream->Release();
LOG((MSP_ERROR, "out of memory in adding a SubStream."));
return E_OUTOFMEMORY;
}
// AddRef the interface pointer and return it.
pSubStream->AddRef();
*ppSubStream = pSubStream;
return S_OK;
}
// ITStream method
STDMETHODIMP CStreamVideoRecv::StopStream ()
{
ENTER_FUNCTION ("CStreamVideoRecv::StopStream");
HRESULT hr;
CLock lock (m_lock);
// copy stopstream from ipconfmsp because
// we want to generate unmap event before stream inactive event
// if there is no terminal selected
if (m_Terminals.GetSize() == 0)
{
LOG((MSP_INFO, "stream %ws %p needs terminal", m_szName, this));
// Enter stopped state. (SO)
m_dwState = STRM_STOPPED;
return S_OK;
}
if (!m_fIsConfigured)
{
LOG((MSP_INFO, "stream %ws %p is not configured yet", m_szName, this));
// Enter stopped state. (SO, ST)
m_dwState = STRM_STOPPED;
return S_OK;
}
// Stop the graph.
if (FAILED (hr = CMSPStream::StopStream()))
{
LOG((MSP_ERROR, "stream %ws %p failed to stop, %x", m_szName, this, hr));
return hr;
}
// check if we have filter chain
CComPtr <IFilterChain> pIFilterChain;
// Query IFilterChain
hr = m_pIMediaControl->QueryInterface(
__uuidof(IFilterChain),
(void**)&pIFilterChain
);
if (FAILED (hr) && (hr != E_NOINTERFACE))
{
LOG ((MSP_ERROR, "stream %ws %p failted to get filter chain. %x", m_szName, this, hr));
return hr;
}
if (pIFilterChain)
{
DWORD dwSSRC = 0;
ITParticipant *pParticipant = NULL;
INT count, next;
next = m_SubStreams.GetSize ();
// generate participant leave
while ((count = next) > 0)
{
if (!((CSubStreamVideoRecv*)m_SubStreams[0])->GetCurrentParticipant (&dwSSRC, &pParticipant))
{
LOG ((MSP_ERROR, "%s failed to get current participant on %p", __fxName, m_SubStreams[0]));
return E_UNEXPECTED;
}
pParticipant->Release ();
if (FAILED (hr = ProcessParticipantLeave (dwSSRC)))
{
LOG ((MSP_ERROR, "%s failed to process participant leave. ssrc=%x, hr=%x", __fxName, dwSSRC, hr));
return hr;
}
next = m_SubStreams.GetSize ();
if (next >= count)
{
// no substream was removed. we have big trouble
LOG ((MSP_ERROR, "%s: not substream was removed", __fxName));
return E_UNEXPECTED;
}
}
for (int i = 0; i < m_Branches.GetSize(); i ++)
{
if (!m_Branches[i].pITSubStream) continue;
if (FAILED (hr = ProcessPinUnmapEvent (
m_Branches[i].dwSSRC, m_Branches[i].pIPin)))
{
LOG ((MSP_ERROR, "%s (%ws) failed to process pin unmap event. %x", __fxName, m_szName, hr));
}
}
}
SendStreamEvent(CALL_STREAM_INACTIVE, CALL_CAUSE_LOCAL_REQUEST, 0, NULL);
LOG((MSP_INFO, "stream %ws %p stopped", m_szName, this));
// Enter stopped state.(ST)
m_dwState = STRM_STOPPED;
return S_OK;
}
// ITSubStreamControl methods, called by the app.
STDMETHODIMP CStreamVideoRecv::CreateSubStream(
IN OUT ITSubStream ** ppSubStream
)
/*++
Routine Description:
This method creates a new substream on this video receive stream. Since
the substreams are created based on the participants, this function
returns only TAPI_E_NOTSUPPORTED.
Arguments:
ppSubStream - the memory location that will store the returned SubStream.
Return Value:
TAPI_E_NOTSUPPORTED
--*/
{
return TAPI_E_NOTSUPPORTED;
}
STDMETHODIMP CStreamVideoRecv::RemoveSubStream(
IN ITSubStream * pSubStream
)
/*++
Routine Description:
This method remove substream on this video receive stream. Since
the substreams are created based on the participants, this function
returns only TAPI_E_NOTSUPPORTED.
Arguments:
pSubStream - the SubStream to be removed.
Return Value:
TAPI_E_NOTSUPPORTED
--*/
{
return TAPI_E_NOTSUPPORTED;
}
STDMETHODIMP CStreamVideoRecv::EnumerateSubStreams(
OUT IEnumSubStream ** ppEnumSubStream
)
/*++
Routine Description:
This method returns an enumerator of the substreams.
Arguments:
ppEnumSubStream - the memory location to store the returned pointer.
Return Value:
S_OK
E_POINTER
E_UNEXPECTED
E_OUTOFMEMORY
--*/
{
LOG((MSP_TRACE,
"EnumerateSubStreams entered. ppEnumSubStream:%x", ppEnumSubStream));
//
// Check parameters.
//
if (IsBadWritePtr(ppEnumSubStream, sizeof(VOID *)))
{
LOG((MSP_ERROR, "CMSPCallBase::EnumerateSubStreams - "
"bad pointer argument - exit E_POINTER"));
return E_POINTER;
}
//
// First see if this call has been shut down.
// acquire the lock before accessing the SubStream object list.
//
CLock lock(m_lock);
if (m_SubStreams.GetData() == NULL)
{
LOG((MSP_ERROR, "CMSPCallBase::EnumerateSubStreams - "
"call appears to have been shut down - exit E_UNEXPECTED"));
// This call has been shut down.
return E_UNEXPECTED;
}
//
// Create an enumerator object.
//
HRESULT hr;
typedef _CopyInterface<ITSubStream> CCopy;
typedef CSafeComEnum<IEnumSubStream, &__uuidof(IEnumSubStream),
ITSubStream *, CCopy> CEnumerator;
CComObject<CEnumerator> *pEnum = NULL;
hr = ::CreateCComObjectInstance(&pEnum);
if (pEnum == NULL)
{
LOG((MSP_ERROR, "CMSPCallBase::EnumerateSubStreams - "
"Could not create enumerator object, %x", hr));
return hr;
}
//
// query for the __uuidof(IEnumSubStream) i/f
//
IEnumSubStream * pEnumSubStream;
hr = pEnum->_InternalQueryInterface(__uuidof(IEnumSubStream), (void**)&pEnumSubStream);
if (FAILED(hr))
{
LOG((MSP_ERROR, "CMSPCallBase::EnumerateSubStreams - "
"query enum interface failed, %x", hr));
delete pEnum;
return hr;
}
//
// Init the enumerator object. The CSafeComEnum can handle zero-sized array.
//
hr = pEnum->Init(
m_SubStreams.GetData(), // the begin itor
m_SubStreams.GetData() + m_SubStreams.GetSize(), // the end itor,
NULL, // IUnknown
AtlFlagCopy // copy the data.
);
if (FAILED(hr))
{
LOG((MSP_ERROR, "CMSPCallBase::EnumerateSubStreams - "
"init enumerator object failed, %x", hr));
pEnumSubStream->Release();
return hr;
}
LOG((MSP_TRACE, "CMSPCallBase::EnumerateSubStreams - exit S_OK"));
*ppEnumSubStream = pEnumSubStream;
return hr;
}
STDMETHODIMP CStreamVideoRecv::get_SubStreams(
OUT VARIANT * pVariant
)
/*++
Routine Description:
This method returns a collection of the substreams.
Arguments:
pVariant - a variant structure.
Return Value:
S_OK
E_POINTER
E_UNEXPECTED
E_OUTOFMEMORY
--*/
{
LOG((MSP_TRACE, "CStreamVideoRecv::get_SubStreams - enter"));
//
// Check parameters.
//
if ( IsBadWritePtr(pVariant, sizeof(VARIANT) ) )
{
LOG((MSP_ERROR, "CStreamVideoRecv::get_SubStreams - "
"bad pointer argument - exit E_POINTER"));
return E_POINTER;
}
//
// See if this call has been shut down. Acquire the lock before accessing
// the SubStream object list.
//
CLock lock(m_lock);
if (m_SubStreams.GetData() == NULL)
{
LOG((MSP_ERROR, "CStreamVideoRecv::get_SubStreams - "
"call appears to have been shut down - exit E_UNEXPECTED"));
// This call has been shut down.
return E_UNEXPECTED;
}
//
// create the collection object - see mspcoll.h
//
typedef CTapiIfCollection< ITSubStream * > SubStreamCollection;
CComObject<SubStreamCollection> * pCollection;
HRESULT hr;
hr = ::CreateCComObjectInstance(&pCollection);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, "CStreamVideoRecv::get_SubStreams - "
"can't create collection - exit 0x%08x", hr));
return hr;
}
//
// get the Collection's IDispatch interface
//
IDispatch * pDispatch;
hr = pCollection->_InternalQueryInterface(__uuidof(IDispatch),
(void **) &pDispatch );
if ( FAILED(hr) )
{
LOG((MSP_ERROR, "CStreamVideoRecv::get_SubStreams - "
"QI for IDispatch on collection failed - exit 0x%08x", hr));
delete pCollection;
return hr;
}
//
// Init the collection using an iterator -- pointers to the beginning and
// the ending element plus one.
//
hr = pCollection->Initialize( m_SubStreams.GetSize(),
m_SubStreams.GetData(),
m_SubStreams.GetData() + m_SubStreams.GetSize() );
if (FAILED(hr))
{
LOG((MSP_ERROR, "CStreamVideoRecv::get_SubStreams - "
"Initialize on collection failed - exit 0x%08x", hr));
pDispatch->Release();
return hr;
}
//
// put the IDispatch interface pointer into the variant
//
VariantInit(pVariant);
pVariant->vt = VT_DISPATCH;
pVariant->pdispVal = pDispatch;
LOG((MSP_TRACE, "CStreamVideoRecv::get_SubStreams - exit S_OK"));
return S_OK;
}
HRESULT CStreamVideoRecv::CheckTerminalTypeAndDirection(
IN ITTerminal * pTerminal
)
/*++
Routine Description:
Check to see if the terminal is allowed on this stream. Only video
render terminal is allowed.
Arguments:
pTerminal - the terminal.
Return value:
S_OK
TAPI_E_INVALIDTERMINAL
*/
{
LOG((MSP_TRACE, "VideoRecv.CheckTerminalTypeAndDirection"));
// check the media type of this terminal.
long lMediaType;
HRESULT hr = pTerminal->get_MediaType(&lMediaType);
if (FAILED(hr))
{
LOG((MSP_ERROR, "can't get terminal media type. %x", hr));
return TAPI_E_INVALIDTERMINAL;
}
if ((DWORD)lMediaType != m_dwMediaType)
{
return TAPI_E_INVALIDTERMINAL;
}
// check the direction of this terminal.
TERMINAL_DIRECTION Direction;
hr = pTerminal->get_Direction(&Direction);
if (FAILED(hr))
{
LOG((MSP_ERROR, "can't get terminal direction. %x", hr));
return TAPI_E_INVALIDTERMINAL;
}
if (Direction != TD_BIDIRECTIONAL && Direction != m_Direction)
{
return TAPI_E_INVALIDTERMINAL;
}
return S_OK;
}
HRESULT CStreamVideoRecv::SubStreamSelectTerminal(
IN ITSubStream * pITSubStream,
IN ITTerminal * pITTerminal
)
/*++
Routine Description:
handle terminals being selected on the sub streams. It gives the terminal
to one free branch and then sets up a mapping between the branch and the
substream, so that the participant in the substream is displayed on the
terminal selected.
Arguments:
pITSubStream - the Substream that got a terminal selected.
pITTerminal - the terminal object.
Return Value:
S_OK
--*/
{
LOG((MSP_TRACE, "VideoRecv SubStreamSelectTerminal"));
HRESULT hr;
CLock lock(m_lock);
// Call the base class's select terminal first. The terminal will be put
// into the terminal pool and a branch of filters will be created for it.
hr = CIPConfMSPStream::SelectTerminal(pITTerminal);
if (FAILED(hr))
{
return hr;
}
// Find out which branch got the terminal.
int i;
for (i = 0; i < m_Branches.GetSize(); i ++)
{
if (m_Branches[i].pITTerminal == pITTerminal)
{
break;
}
}
_ASSERTE(i < m_Branches.GetSize());
if (i >= m_Branches.GetSize())
{
return E_UNEXPECTED;
}
// Find out the participant on the SubStream.
ITParticipant *pITParticipant = NULL;
DWORD dwSSRC;
if ((static_cast<CSubStreamVideoRecv*>(pITSubStream))->GetCurrentParticipant(
&dwSSRC,
&pITParticipant
) == FALSE)
{
return E_UNEXPECTED;
}
pITParticipant->Release();
if (m_pIRTPDemux == NULL)
{
LOG((MSP_ERROR, "no demux filter"));
return E_UNEXPECTED;
}
// map the pin to this SSRC only.
hr = m_pIRTPDemux->SetMappingState(-1, m_Branches[i].pIPin, dwSSRC, TRUE);
if (FAILED(hr))
{
LOG((MSP_ERROR, "map SSRC %x to pin %p returned %x",
dwSSRC, m_Branches[i].pIPin, hr));
return hr;
}
_ASSERTE(m_Branches[i].pITSubStream == NULL);
pITSubStream->AddRef();
m_Branches[i].pITSubStream = pITSubStream;
m_Branches[i].dwSSRC = dwSSRC;
return hr;
}
HRESULT CStreamVideoRecv::ConfigureRTPFormats(
IN IBaseFilter * pIRTPFilter,
IN IStreamConfig * pIStreamConfig
)
/*++
Routine Description:
Configure the RTP filter with RTP<-->AM media type mappings.
Arguments:
pIRTPFilter - The source RTP Filter.
pIStreamConfig - The stream config interface that has the media info.
Return Value:
HRESULT.
--*/
{
ENTER_FUNCTION("VideoRecv::ConfigureRTPFormats");
LOG((MSP_TRACE, "%s enters", __fxName));
HRESULT hr;
CComPtr<IRtpMediaControl> pIRtpMediaControl;
hr = pIRTPFilter->QueryInterface(&pIRtpMediaControl);
if (FAILED(hr))
{
LOG((MSP_ERROR, "%s adding source filter. %x", __fxName, hr));
return hr;
}
// find the number of capabilities supported.
DWORD dwCount;
hr = pIStreamConfig->GetNumberOfCapabilities(&dwCount);
if (FAILED(hr))
{
LOG((MSP_ERROR, "%s GetNumberOfCapabilities. %x", __fxName, hr));
return hr;
}
BOOL fFound = FALSE;
for (int i = dwCount - 1; i >= 0; i --)
{
// TODO, a new interface is needed to resolve RTP to MediaType.
AM_MEDIA_TYPE *pMediaType;
DWORD dwPayloadType;
hr = pIStreamConfig->GetStreamCaps(
i, &pMediaType, NULL, &dwPayloadType
);
if (FAILED(hr))
{
LOG((MSP_ERROR, "%s GetStreamCaps. %x", __fxName, hr));
return hr;
}
BITMAPINFOHEADER *pHeader = HEADER(pMediaType->pbFormat);
if (pHeader == NULL)
{
MSPDeleteMediaType(pMediaType);
continue;
}
// check the image size
if (m_Settings.fCIF)
{
if (pHeader->biWidth != CIFWIDTH)
{
MSPDeleteMediaType(pMediaType);
continue;
}
}
else
{
if (pHeader->biWidth != QCIFWIDTH)
{
MSPDeleteMediaType(pMediaType);
continue;
}
}
for (DWORD dw2 = 0; dw2 < m_Settings.dwNumPayloadTypes; dw2 ++)
{
if (dwPayloadType == m_Settings.PayloadTypes[dw2])
{
hr = pIRtpMediaControl->SetFormatMapping(
dwPayloadType,
90000, // default video clock rate.
pMediaType
);
if (FAILED(hr))
{
MSPDeleteMediaType(pMediaType);
LOG((MSP_ERROR, "%s SetFormatMapping. %x", __fxName, hr));
return hr;
}
else
{
LOG((MSP_INFO, "%s Configured payload:%d", __fxName, dwPayloadType));
}
}
}
MSPDeleteMediaType(pMediaType);
}
return S_OK;
}
HRESULT CStreamVideoRecv::SetUpInternalFilters()
/*++
Routine Description:
set up the filters used in the stream.
RTP->DECODER->Render terminal
This function only creates the RTP and demux filter and the rest of the
graph is connected in ConnectTerminal.
Arguments:
Return Value:
HRESULT.
--*/
{
ENTER_FUNCTION("CStreamVideoRecv::SetUpInternalFilters");
LOG((MSP_TRACE, "%s entered.", __fxName));
HRESULT hr = S_OK;
if (m_pIRTPDemux == NULL)
{
CComPtr<IBaseFilter> pSourceFilter;
if (m_pIRTPSession == NULL)
{
// create and add the source fitler.
if (FAILED(hr = ::AddFilter(
m_pIGraphBuilder,
__uuidof(MSRTPSourceFilter),
L"RtpSource",
&pSourceFilter)))
{
LOG((MSP_ERROR, "%s, adding source filter. %x", __fxName, hr));
return hr;
}
if (FAILED(hr = ConfigureRTPFilter(pSourceFilter)))
{
LOG((MSP_ERROR, "%s, configure RTP source filter. %x", __fxName, hr));
return hr;
}
}
else
{
if (FAILED (hr = m_pIRTPSession->QueryInterface (&pSourceFilter)))
{
LOG ((MSP_ERROR, "%s failed to get filter from rtp session. %x", __fxName, hr));
return hr;
}
if (FAILED (hr = m_pIGraphBuilder->AddFilter ((IBaseFilter *)pSourceFilter, L"RtpSource")))
{
LOG ((MSP_ERROR, "%s failed to add filter to graph. %x", __fxName, hr));
return hr;
}
}
// get the Demux interface pointer.
hr = pSourceFilter->QueryInterface(&m_pIRTPDemux);
if (FAILED(hr))
{
LOG((MSP_ERROR, "%s query IRtpDemux failed. %x", __fxName, hr));
return hr;
}
}
// hr = m_pIRTPDemux->SetPinCount(m_Terminals.GetSize(), RTPDMXMODE_AUTO);
#define DEFAULT_PIN_SIZE 4
int isize = m_Terminals.GetSize();
if (isize < DEFAULT_PIN_SIZE)
{
isize = DEFAULT_PIN_SIZE;
}
hr = m_pIRTPDemux->SetPinCount(isize, RTPDMXMODE_AUTO);
if (FAILED(hr))
{
LOG((MSP_ERROR, "%s query IRtpDemux failed. %x", __fxName, hr));
return hr;
}
return hr;
}
HRESULT CStreamVideoRecv::AddOneBranch(
BRANCH * pBranch,
BOOL fFirstBranch,
BOOL fDirectRTP
)
/*++
Routine Description:
Create a new branch of filters off the demux.
Arguments:
pBranch - a pointer to a structure that remembers the info about the branch.
fFirstBranch - whether this is the first branch.
fDirectRTP - whether to output RTP directly.
Return Value:
HRESULT.
--*/
{
ENTER_FUNCTION("CStreamVideoRecv::AddOneBranch");
LOG((MSP_TRACE, "%s entered.", __fxName));
HRESULT hr;
_ASSERT(m_pIRTPDemux != NULL);
CComPtr<IBaseFilter> pRTPFilter;
hr = m_pIRTPDemux->QueryInterface(
__uuidof(IBaseFilter), (void**)&pRTPFilter);
if (FAILED(hr))
{
LOG((MSP_ERROR, "%s, query IBaseFilter failed, %x", __fxName, hr));
return hr;
}
// Find the next output pin on the demux fitler.
CComPtr<IPin> pIPinOutput;
if (FAILED(hr = ::FindPin(
(IBaseFilter *)pRTPFilter,
(IPin**)&pIPinOutput,
PINDIR_OUTPUT
)))
{
LOG((MSP_ERROR, "%s, find free pin on demux, %x", __fxName, hr));
return hr;
}
// create and add the video decoder filter.
CComPtr<IBaseFilter> pCodecFilter;
if (fDirectRTP)
{
// only create the decoder and ask questions
if (FAILED(hr = CoCreateInstance(
__uuidof(TAPIVideoDecoder),
NULL,
CLSCTX_INPROC_SERVER | CLSCTX_NO_CODE_DOWNLOAD,
__uuidof(IBaseFilter),
(void **) &pCodecFilter
)))
{
LOG((MSP_ERROR, "%s, create filter %x", __fxName, hr));
return hr;
}
}
else
{
// create the decoder and add it into the graph.
if (FAILED(hr = ::AddFilter(
m_pIGraphBuilder,
__uuidof(TAPIVideoDecoder),
L"codec",
&pCodecFilter
)))
{
LOG((MSP_ERROR, "%s, add Codec filter. %x", __fxName, hr));
return hr;
}
}
CComPtr<IPin> pIPinInput;
if (FAILED(hr = ::FindPin(pCodecFilter, &pIPinInput, PINDIR_INPUT, TRUE)))
{
LOG((MSP_ERROR,
"%s, find input pin on pCodecFilter failed. hr=%x", __fxName, hr));
return hr;
}
if (fFirstBranch)
{
CComPtr<IStreamConfig> pIStreamConfig;
hr = pIPinInput->QueryInterface(&pIStreamConfig);
if (FAILED(hr))
{
LOG((MSP_ERROR, "%s, query IStreamConfig failed", __fxName));
return hr;
}
// configure the format info on the RTP filter
if (FAILED(hr = ConfigureRTPFormats(pRTPFilter, pIStreamConfig)))
{
LOG((MSP_ERROR, "%s configure RTP formats. %x", __fxName, hr));
return hr;
}
}
if (!fDirectRTP)
{
// Connect the decoder to the output pin of the source filter.
if (FAILED(hr = ::ConnectFilters(
m_pIGraphBuilder,
(IPin *)pIPinOutput,
(IBaseFilter *)pCodecFilter
)))
{
LOG((MSP_ERROR, "%s, connect RTP filter and codec. %x", __fxName, hr));
m_pIGraphBuilder->RemoveFilter(pCodecFilter);
return hr;
}
pBranch->pCodecFilter = pCodecFilter;
pBranch->pCodecFilter->AddRef();
}
pBranch->pIPin = pIPinOutput;
pBranch->pIPin->AddRef();
// retrieve IBitrateControl
if (FAILED (hr = pIPinInput->QueryInterface (&(pBranch->pBitrateControl))))
{
LOG((MSP_ERROR, "%, query IBitrateControl failed. %x", __fxName, hr));
pBranch->pBitrateControl = NULL;
// return hr;
}
LOG((MSP_TRACE, "%s, AddOneBranch exits ok.", __fxName));
return S_OK;
}
HRESULT CStreamVideoRecv::RemoveOneBranch(
BRANCH * pBranch
)
/*++
Routine Description:
Remove all the filters in a branch and release all the pointers.
the caller of this function should not use any member of this branch
after this function call.
Arguments:
pBranch - a pointer to a structure that has the info about the branch.
Return Value:
HRESULT.
--*/
{
ENTER_FUNCTION("VideoRecv::RemoveOneBranch");
LOG((MSP_TRACE, "%s entered", __fxName));
if (pBranch->pBitrateControl)
{
pBranch->pBitrateControl->Release();
}
if (pBranch->pIPin)
{
pBranch->pIPin->Release();
}
if (pBranch->pCodecFilter)
{
// #ifdef DYNGRAPH
HRESULT hr;
OAFilterState FilterState;
CComPtr <IFilterChain> pIFilterChain;
// Query IFilterChain
hr = m_pIMediaControl->QueryInterface(
__uuidof(IFilterChain),
(void**)&pIFilterChain
);
if (FAILED (hr) && (hr != E_NOINTERFACE))
{
LOG ((MSP_ERROR, "stream %ws %p failted to get filter chain. %x", m_szName, this, hr));
// return hr;
}
if (pIFilterChain)
{
hr = m_pIMediaControl->GetState(0, &FilterState);
if (FAILED(hr))
{
LOG((MSP_ERROR, "%s get filter graph state failed, %x", __fxName, hr));
}
else
{
// stop the chain before removing filters.
if (FilterState == State_Running)
{
// stop the chain if the graph is in running state.
hr = pIFilterChain->StopChain(pBranch->pCodecFilter, NULL);
if (FAILED(hr))
{
LOG((MSP_ERROR, "%s stop chain failed. hr=%x", __fxName, hr));
}
}
}
}
// #endif
m_pIGraphBuilder->RemoveFilter(pBranch->pCodecFilter);
pBranch->pCodecFilter->Release();
}
if (pBranch->pITTerminal)
{
// get the terminal control interface.
CComQIPtr<ITTerminalControl, &__uuidof(ITTerminalControl)>
pTerminal(pBranch->pITTerminal);
_ASSERTE(pTerminal != NULL);
if (pTerminal != NULL)
{
HRESULT hr = pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
LOG((MSP_TRACE,
"%s, terminal %p is disonnected. hr:%x",
__fxName, pBranch->pITTerminal, hr));
}
pBranch->pITTerminal->Release();
}
if (pBranch->pITSubStream)
{
((CSubStreamVideoRecv*)pBranch->pITSubStream)->
ClearCurrentTerminal();
pBranch->pITSubStream->Release();
}
LOG((MSP_TRACE, "%s, RemoveOneBranch exits ok.", __fxName));
return S_OK;
}
HRESULT CStreamVideoRecv::ConnectPinToTerminal(
IN IPin * pOutputPin,
IN ITTerminal * pITTerminal
)
/*++
Routine Description:
Connect the codec filter to the render filter inside the terminal.
Arguments:
pOutputPin - The last pin before the terminal.
pITTerminal - the terminal object.
Return Value:
HRESULT.
--*/
{
// get the terminal control interface.
CComQIPtr<ITTerminalControl, &__uuidof(ITTerminalControl)>
pTerminal(pITTerminal);
if (pTerminal == NULL)
{
LOG((MSP_ERROR, "can't get Terminal Control interface"));
SendStreamEvent(CALL_TERMINAL_FAIL,
CALL_CAUSE_BAD_DEVICE, E_NOINTERFACE, pITTerminal);
return E_NOINTERFACE;
}
const DWORD MAXPINS = 8;
DWORD dwNumPins = MAXPINS;
IPin * Pins[MAXPINS];
HRESULT hr = pTerminal->ConnectTerminal(
m_pIGraphBuilder, TD_RENDER, &dwNumPins, Pins
);
if (FAILED(hr))
{
LOG((MSP_ERROR, "can't connect to terminal, %x", hr));
SendStreamEvent(CALL_TERMINAL_FAIL,
CALL_CAUSE_CONNECT_FAIL, hr, pITTerminal);
return hr;
}
// the number of pins should never be 0.
if (dwNumPins == 0)
{
LOG((MSP_ERROR, "terminal has no pins."));
SendStreamEvent(CALL_TERMINAL_FAIL,
CALL_CAUSE_BAD_DEVICE, hr, pITTerminal);
pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
return E_UNEXPECTED;
}
if (IsBadReadPtr (Pins, dwNumPins * sizeof (IPin*)))
{
LOG((MSP_ERROR, "terminal returned bad pin array"));
SendStreamEvent(CALL_TERMINAL_FAIL, CALL_CAUSE_BAD_DEVICE, hr, pITTerminal);
pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
return E_POINTER;
}
for (DWORD i = 0; i < dwNumPins; i++)
{
if (IsBadReadPtr (Pins[i], sizeof (IPin)))
{
LOG((MSP_ERROR, "terminal returned bad pin. # %d", i));
SendStreamEvent(CALL_TERMINAL_FAIL, CALL_CAUSE_BAD_DEVICE, hr, pITTerminal);
pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
return E_POINTER;
}
}
// Connect the codec filter to the video render terminal.
hr = ::ConnectFilters(
m_pIGraphBuilder,
(IBaseFilter *)pOutputPin,
(IPin *)Pins[0],
FALSE // use Connect instead of ConnectDirect.
);
// release the refcounts on the pins.
for (DWORD i = 0; i < dwNumPins; i ++)
{
Pins[i]->Release();
}
if (FAILED(hr))
{
LOG((MSP_ERROR, "connect the pin to the terminal. %x", hr));
pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
return hr;
}
//
// Now we are actually connected. Update our state and perform postconnection
// (ignore postconnection error code).
//
pTerminal->CompleteConnectTerminal();
return hr;
}
HRESULT CStreamVideoRecv::ConnectTerminal(
IN ITTerminal * pITTerminal
)
/*++
Routine Description:
connect video render terminal.
Arguments:
pITTerminal - The terminal to be connected.
Return Value:
HRESULT.
--*/
{
ENTER_FUNCTION("VideoRecv::ConnectTerminal");
LOG((MSP_TRACE, "%s enters, pTerminal %p", __fxName, pITTerminal));
HRESULT hr;
// #ifdef DYNGRAPH
OAFilterState FilterState;
CComPtr <IFilterChain> pIFilterChain;
// Query IFilterChain
hr = m_pIMediaControl->QueryInterface(
__uuidof(IFilterChain),
(void**)&pIFilterChain
);
if (FAILED (hr) && (hr != E_NOINTERFACE))
{
LOG ((MSP_ERROR, "stream %ws %p failted to get filter chain. %x", m_szName, this, hr));
return hr;
}
hr = m_pIMediaControl->GetState(0, &FilterState);
if (FAILED(hr))
{
LOG((MSP_ERROR, "%s get filter graph state failed, %x", __fxName, hr));
return hr;
}
// #endif
hr = SetUpInternalFilters();
if (FAILED(hr))
{
LOG((MSP_ERROR, "%s Set up internal filter failed, %x", __fxName, hr));
CleanUpFilters();
return hr;
}
// get the terminal control interface.
CComQIPtr<ITTerminalControl, &__uuidof(ITTerminalControl)>
pTerminal(pITTerminal);
if (pTerminal == NULL)
{
LOG((MSP_ERROR, "can't get Terminal Control interface"));
SendStreamEvent(CALL_TERMINAL_FAIL,
CALL_CAUSE_BAD_DEVICE, E_NOINTERFACE, pITTerminal);
return E_NOINTERFACE;
}
const DWORD MAXPINS = 8;
DWORD dwNumPins = MAXPINS;
IPin * Pins[MAXPINS];
hr = pTerminal->ConnectTerminal(
m_pIGraphBuilder, TD_RENDER, &dwNumPins, Pins
);
if (FAILED(hr))
{
LOG((MSP_ERROR, "can't connect to terminal, %x", hr));
SendStreamEvent(CALL_TERMINAL_FAIL,
CALL_CAUSE_CONNECT_FAIL, hr, pITTerminal);
return hr;
}
// the number of pins should never be 0.
if (dwNumPins == 0)
{
LOG((MSP_ERROR, "terminal has no pins."));
SendStreamEvent(CALL_TERMINAL_FAIL,
CALL_CAUSE_BAD_DEVICE, hr, pITTerminal);
pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
return E_UNEXPECTED;
}
if (IsBadReadPtr (Pins, dwNumPins * sizeof (IPin*)))
{
LOG((MSP_ERROR, "terminal returned bad pin array"));
SendStreamEvent(CALL_TERMINAL_FAIL, CALL_CAUSE_BAD_DEVICE, hr, pITTerminal);
pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
return E_POINTER;
}
DWORD i;
for (i = 0; i < dwNumPins; i++)
{
if (IsBadReadPtr (Pins[i], sizeof (IPin)))
{
LOG((MSP_ERROR, "terminal returned bad pin. # %d", i));
SendStreamEvent(CALL_TERMINAL_FAIL, CALL_CAUSE_BAD_DEVICE, hr, pITTerminal);
pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
return E_POINTER;
}
}
// check the media type supported by input pin on terminal
BOOL fDirectRTP = FALSE;
if (S_OK == ::PinSupportsMediaType (
Pins[0], __uuidof(MEDIATYPE_RTP_Single_Stream)
))
{
fDirectRTP = TRUE;
}
// first create the branch structure needed before the terminal.
BRANCH aBranch;
ZeroMemory(&aBranch, sizeof BRANCH);
hr = AddOneBranch(&aBranch, (m_Branches.GetSize() == 0), fDirectRTP);
if (FAILED(hr))
{
LOG((MSP_ERROR, "%s Set up a new decode branch failed, %x", __fxName, hr));
pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
return hr;
}
CComPtr <IPin> pOutputPin;
if (fDirectRTP)
{
// connect the RTP output pin to the terminal's input pin.
hr = m_pIGraphBuilder->ConnectDirect(aBranch.pIPin, Pins[0], NULL);
if (FAILED(hr))
{
LOG((MSP_ERROR, "%s connecting codec to terminal failed, %x", __fxName, hr));
pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
goto cleanup;
}
}
else
{
// connect the codec to the terminal
hr = ConnectFilters(m_pIGraphBuilder, aBranch.pCodecFilter, Pins[0]);
if (FAILED(hr))
{
LOG((MSP_ERROR, "%s connecting codec to terminal failed, %x", __fxName, hr));
pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
goto cleanup;
}
}
// #ifdef DYNGRAPH
if (pIFilterChain)
{
if (FilterState == State_Running)
{
if (fDirectRTP)
{
hr = E_UNEXPECTED;
LOG((MSP_ERROR, "%s can't support this. %x", __fxName, hr));
goto cleanup;
}
hr = pIFilterChain->StartChain(aBranch.pCodecFilter, NULL);
if (FAILED(hr))
{
LOG((MSP_ERROR, "%s start chain failed. hr=%x", __fxName, hr));
goto cleanup;
}
}
}
// #endif
pITTerminal->AddRef();
aBranch.pITTerminal = pITTerminal;
if (!m_Branches.Add(aBranch))
{
LOG((MSP_ERROR, "%s out of mem.", __fxName));
hr = E_OUTOFMEMORY;
goto cleanup;
}
// release the refcounts on the pins.
for (i = 0; i < dwNumPins; i ++)
{
Pins[i]->Release();
}
//
// Now we are actually connected. Update our state and perform postconnection
// (ignore postconnection error code).
//
pTerminal->CompleteConnectTerminal();
return S_OK;
cleanup:
// release the refcounts on the pins.
for (i = 0; i < dwNumPins; i ++)
{
Pins[i]->Release();
}
// remove the added filters from the graph and disconnect the terminal.
RemoveOneBranch(&aBranch);
return hr;
}
HRESULT CStreamVideoRecv::DisconnectTerminal(
IN ITTerminal * pITTerminal
)
/*++
Routine Description:
Disconnect a terminal. It will remove its filters from the graph and
also release its references to the graph. A branch of filters is also
released.
Arguments:
pITTerminal - the terminal.
Return Value:
HRESULT.
--*/
{
for (int i = 0; i < m_Branches.GetSize(); i ++)
{
if (m_Branches[i].pITTerminal == pITTerminal)
{
break;
}
}
if (i < m_Branches.GetSize())
{
RemoveOneBranch(&m_Branches[i]);
m_Branches.RemoveAt(i);
}
return S_OK;
}
HRESULT CStreamVideoRecv::SetUpFilters()
/*++
Routine Description:
Insert filters into the graph and connect to the terminals.
Arguments:
Return Value:
HRESULT.
--*/
{
LOG((MSP_TRACE, "VideoRecv.SetUpFilters"));
HRESULT hr = SetUpInternalFilters();
if (FAILED(hr))
{
LOG((MSP_ERROR, "Set up internal filter failed, %x", hr));
CleanUpFilters();
return hr;
}
for (int i = 0; i < m_Terminals.GetSize(); i ++)
{
HRESULT hr = ConnectTerminal(m_Terminals[i]);
if (FAILED(hr))
{
return hr;
}
}
return S_OK;
}
// ITParticipantSubStreamControl methods, called by the app.
STDMETHODIMP CStreamVideoRecv::get_SubStreamFromParticipant(
IN ITParticipant * pITParticipant,
OUT ITSubStream ** ppITSubStream
)
/*++
Routine Description:
Find out which substream is rendering the participant.
Arguments:
pITParticipant - the participant.
ppITSubStream - the returned sub stream.
Return Value:
S_OK,
TAPI_E_NOITEMS,
E_UNEXPECTED
--*/
{
LOG((MSP_TRACE, "get substream from participant:%p", pITParticipant));
if (IsBadWritePtr(ppITSubStream, sizeof(VOID *)))
{
LOG((MSP_ERROR, "ppITSubStream is a bad pointer"));
return E_POINTER;
}
CLock lock(m_lock);
ITSubStream * pITSubStream = NULL;
// find out which substream has the participant.
for (int i = 0; i < m_SubStreams.GetSize(); i ++)
{
ITParticipant *pTempParticipant = NULL;
DWORD dwSSRC;
((CSubStreamVideoRecv*)m_SubStreams[i])->GetCurrentParticipant(
&dwSSRC, &pTempParticipant
);
_ASSERTE(pTempParticipant != NULL);
pTempParticipant->Release(); // we dont' need the ref here.
if (pITParticipant == pTempParticipant)
{
pITSubStream = m_SubStreams[i];
pITSubStream->AddRef();
break;
}
}
if (pITSubStream == NULL)
{
return TAPI_E_NOITEMS;
}
*ppITSubStream = pITSubStream;
return S_OK;
}
STDMETHODIMP CStreamVideoRecv::get_ParticipantFromSubStream(
IN ITSubStream * pITSubStream,
OUT ITParticipant ** ppITParticipant
)
/*++
Routine Description:
Find out which participant the substream is rendering.
Arguments:
pITSubStream - the sub stream.
ppITParticipant - the returned participant
Return Value:
S_OK,
TAPI_E_NOITEMS,
E_UNEXPECTED
--*/
{
LOG((MSP_TRACE, "get participant from substream:%p", pITSubStream));
if (IsBadWritePtr(ppITParticipant, sizeof(VOID *)))
{
LOG((MSP_ERROR, "ppITParticipant is a bad pointer"));
return E_POINTER;
}
CLock lock(m_lock);
int i;
// check to see if the substream is in our list.
if ((i = m_SubStreams.Find(pITSubStream)) < 0)
{
LOG((MSP_ERROR, "wrong SubStream handle %p", pITSubStream));
return E_INVALIDARG;
}
ITParticipant *pITParticipant;
DWORD dwSSRC;
if (((CSubStreamVideoRecv*)m_SubStreams[i])->GetCurrentParticipant(
&dwSSRC, &pITParticipant
) == FALSE)
{
return TAPI_E_NOITEMS;
}
*ppITParticipant = pITParticipant;
return S_OK;
}
STDMETHODIMP CStreamVideoRecv::SwitchTerminalToSubStream(
IN ITTerminal * pITTerminal,
IN ITSubStream * pITSubStream
)
/*++
Routine Description:
Switch terminal to a substream to display the participant that is on the
substream.
Arguments:
pITTerminal - the terminal.
pITSubStream - the sub stream.
Return Value:
S_OK,
E_INVALIDARG,
E_UNEXPECTED
--*/
{
LOG((MSP_TRACE, "switch terminal %p to substream:%p",
pITTerminal, pITSubStream));
CLock lock(m_lock);
if (m_pIRTPDemux == NULL)
{
LOG((MSP_ERROR, "the demux filter doesn't exist."));
return E_UNEXPECTED;
}
// first, find out which branch has the terminal now.
for (int i = 0; i < m_Branches.GetSize(); i ++)
{
if (m_Branches[i].pITTerminal == pITTerminal)
{
break;
}
}
if (i >= m_Branches.GetSize())
{
LOG((MSP_TRACE, "terminal %p doesn't exist", pITTerminal));
return E_INVALIDARG;
}
// second, find out if the substream exists.
if (m_SubStreams.Find(pITSubStream) < 0)
{
LOG((MSP_TRACE, "SubStream %p doesn't exist", pITSubStream));
return E_INVALIDARG;
}
// thrid, find the participant on the substream and configure the demux
// filter to render the participant on the chosen branch.
ITParticipant *pITParticipant = NULL;
DWORD dwSSRC;
((CSubStreamVideoRecv*)pITSubStream)->GetCurrentParticipant(
&dwSSRC, &pITParticipant
) ;
_ASSERTE(pITParticipant != NULL);
// we don't need the reference here.
pITParticipant->Release();
// map the pin to this SSRC only.
HRESULT hr = m_pIRTPDemux->SetMappingState(
-1, m_Branches[i].pIPin, dwSSRC, TRUE
);
if (FAILED(hr))
{
LOG((MSP_ERROR, "map SSRC %x to pin %p returned %x",
dwSSRC, m_Branches[i].pIPin, hr));
return hr;
}
DWORD dwOldSSRC = 0;
// Finally, set up the mappings among the branch, the substream and
// the terminal
// release the refcount on the old branch that the substream was on.
for (int j = 0; j < m_Branches.GetSize(); j ++)
{
if (m_Branches[j].pITSubStream == pITSubStream)
{
m_Branches[j].pITSubStream->Release();
m_Branches[j].pITSubStream = NULL;
break;
}
}
if (m_Branches[i].pITSubStream != NULL)
{
((CSubStreamVideoRecv*)m_Branches[i].pITSubStream)->
ClearCurrentTerminal();
m_Branches[i].pITSubStream->Release();
dwOldSSRC = m_Branches[i].dwSSRC;
}
pITSubStream->AddRef();
m_Branches[i].pITSubStream = pITSubStream;
m_Branches[i].dwSSRC = dwSSRC;
((CSubStreamVideoRecv*)pITSubStream)->ClearCurrentTerminal();
((CSubStreamVideoRecv*)pITSubStream)->SetCurrentTerminal(
m_Branches[i].pITTerminal
);
// After all the steps, we still have to change QOS reservation.
if (dwOldSSRC != 0)
{
// cancel QOS for the old participant.
if (FAILED(hr = m_pIRTPSession->SetQosState(dwOldSSRC, FALSE)))
{
LOG((MSP_ERROR, "disabling QOS for %x. hr:%x", dwOldSSRC, hr));
}
else
{
LOG((MSP_INFO, "disabled video QOS for %x.", dwOldSSRC));
}
}
// reserve QOS for the new participant.
if (FAILED(hr = m_pIRTPSession->SetQosState(dwSSRC, TRUE)))
{
LOG((MSP_ERROR, "enabling video QOS for %x. hr:%x", dwSSRC, hr));
}
else
{
LOG((MSP_INFO, "enabled video QOS for %x.", dwSSRC));
}
return S_OK;
}
HRESULT CStreamVideoRecv::ProcessTalkingEvent(
IN DWORD dwSSRC
)
/*++
Routine Description:
A sender has just joined. A substream needs to be created for the
participant.
A pin mapped event might have happended when we didn't have the
participant's name so it was queued in a list. Now that we have a new
participant, let's check if this is the same participant. If it is,
we complete the pin mapped event by sending the app an notification.
Arguments:
dwSSRC - the SSRC of the participant.
pITParticipant - the participant object.
Return Value:
S_OK,
E_UNEXPECTED
--*/
{
ENTER_FUNCTION("CStreamVideoRecv::ProcessTalkingEvent");
LOG((MSP_TRACE, "%s entered. %x", __fxName, dwSSRC));
CLock lock(m_lock);
if (m_pIRTPSession == NULL)
{
LOG((MSP_ERROR, "the network filter doesn't exist."));
return E_UNEXPECTED;
}
// first find out if this participant object exists.
ITParticipant * pITParticipant = NULL;
int i;
// find the SSRC in our participant list.
for (i = 0; i < m_Participants.GetSize(); i ++)
{
if (((CParticipant *)m_Participants[i])->
HasSSRC((ITStream *)this, dwSSRC))
{
pITParticipant = m_Participants[i];
break;
}
}
// if the participant is not there yet, just return. It will be checked
// later when CName is available.
if (!pITParticipant)
{
LOG((MSP_TRACE, "%s participant not exist", __fxName));
return S_OK;
}
// Find out if a substream has been created for this participant when we
// processed PinMapped event and receiver reports.
for (i = 0; i < m_SubStreams.GetSize(); i ++)
{
ITParticipant *pTempParticipant;
DWORD dwSSRC;
((CSubStreamVideoRecv*)m_SubStreams[i])->GetCurrentParticipant(
&dwSSRC, &pTempParticipant
);
_ASSERTE(pTempParticipant != NULL);
pTempParticipant->Release(); // we dont' need the ref here.
if (pITParticipant == pTempParticipant)
{
// the participant has been created.
return S_OK;
}
}
ITSubStream * pITSubStream;
HRESULT hr = InternalCreateSubStream(&pITSubStream);
if (FAILED(hr))
{
LOG((MSP_ERROR, "%ls can't create a SubStream, %x", m_szName, hr));
return hr;
}
((CSubStreamVideoRecv*)pITSubStream)->SetCurrentParticipant(
dwSSRC, pITParticipant
);
((CIPConfMSPCall *)m_pMSPCall)->SendParticipantEvent(
PE_NEW_SUBSTREAM,
pITParticipant,
pITSubStream
);
// look at the pending SSRC list and find out if this report
// fits in the list.
IPin *pIPin = NULL;
for (i = 0; i < m_PinMappedEvents.GetSize(); i ++)
{
if (m_PinMappedEvents[i].dwSSRC == dwSSRC)
{
pIPin = m_PinMappedEvents[i].pIPin;
break;
}
}
if (!pIPin)
{
// the SSRC is not in the list of pending PinMappedEvents.
LOG((MSP_TRACE, "the SSRC %x is not in the pending list", dwSSRC));
pITSubStream->Release();
return S_OK;;
}
// get rid of the peding event.
m_PinMappedEvents.RemoveAt(i);
// reserve QOS since we are rendering this sender.
if (FAILED(hr = m_pIRTPSession->SetQosState(dwSSRC, TRUE)))
{
LOG((MSP_ERROR, "enabling video QOS for %x. hr:%x", dwSSRC, hr));
}
else
{
LOG((MSP_INFO, "enabled video QOS for %x.", dwSSRC));
}
// tell the app about the newly mapped sender.
for (i = 0; i < m_Branches.GetSize(); i ++)
{
if (m_Branches[i].pIPin == pIPin)
{
if (m_Branches[i].pITSubStream != NULL)
{
((CSubStreamVideoRecv*)m_Branches[i].pITSubStream)
->ClearCurrentTerminal();
m_Branches[i].pITSubStream->Release();
}
m_Branches[i].dwSSRC = dwSSRC;
m_Branches[i].pITSubStream = pITSubStream;
pITSubStream->AddRef();
((CSubStreamVideoRecv*)pITSubStream)->
SetCurrentTerminal(m_Branches[i].pITTerminal);
((CIPConfMSPCall *)m_pMSPCall)->SendParticipantEvent(
PE_SUBSTREAM_MAPPED,
pITParticipant,
pITSubStream
);
break;
}
}
pITSubStream->Release();
return S_OK;
}
HRESULT CStreamVideoRecv::ProcessSilentEvent(
IN DWORD dwSSRC
)
/*++
Routine Description:
When participant left the session, remove the stream from the participant
object's list of streams. If all streams are removed, remove the
participant from the call object's list too.
Arguments:
dwSSRC - the SSRC of the participant left.
Return Value:
HRESULT.
--*/
{
LOG((MSP_TRACE, "%ls ProcessSilentEvent, SSRC: %x", m_szName, dwSSRC));
CLock lock(m_lock);
if (m_pIRTPSession == NULL)
{
LOG((MSP_ERROR, "the network filter doesn't exist."));
return E_UNEXPECTED;
}
// first find out if this participant object exists.
ITParticipant * pITParticipant = NULL;
int i;
// find the SSRC in our participant list.
for (i = 0; i < m_Participants.GetSize(); i ++)
{
if (((CParticipant *)m_Participants[i])->
HasSSRC((ITStream *)this, dwSSRC))
{
pITParticipant = m_Participants[i];
break;
}
}
// if the participant is not there, just return.
if (!pITParticipant)
{
return S_OK;
}
HRESULT hr;
// cancel QOS for this participant.
if (FAILED(hr = m_pIRTPSession->SetQosState(dwSSRC, FALSE)))
{
LOG((MSP_ERROR, "disabling QOS for %x. hr:%x", dwSSRC, hr));
}
else
{
LOG((MSP_INFO, "disabled video QOS for %x.", dwSSRC));
}
// find out which substream is going away.
ITSubStream * pITSubStream = NULL;
for (i = 0; i < m_SubStreams.GetSize(); i ++)
{
// Find out the participant on the SubStream.
ITParticipant *pTempParticipant;
DWORD dwSSRC;
((CSubStreamVideoRecv*)m_SubStreams[i])->GetCurrentParticipant(
&dwSSRC, &pTempParticipant
);
_ASSERTE(pTempParticipant != NULL);
pTempParticipant->Release(); // we dont' need the ref here.
if (pTempParticipant == pITParticipant)
{
pITSubStream = m_SubStreams[i];
break;
}
}
if (pITSubStream)
{
// remove the mapping if the substream was mapped to a branch.
for (int i = 0; i < m_Branches.GetSize(); i ++)
{
if (m_Branches[i].pITSubStream == pITSubStream)
{
m_Branches[i].pITSubStream->Release();
m_Branches[i].pITSubStream = NULL;
m_Branches[i].dwSSRC = 0;
// fire an event to tell the app that the substream is not used.
((CIPConfMSPCall *)m_pMSPCall)->SendParticipantEvent(
PE_SUBSTREAM_UNMAPPED,
pITParticipant,
pITSubStream
);
break;
}
}
((CIPConfMSPCall *)m_pMSPCall)->SendParticipantEvent(
PE_SUBSTREAM_REMOVED,
pITParticipant,
pITSubStream
);
if (m_SubStreams.Remove(pITSubStream))
{
pITSubStream->Release();
}
}
return S_OK;
}
HRESULT CStreamVideoRecv::NewParticipantPostProcess(
IN DWORD dwSSRC,
IN ITParticipant *pITParticipant
)
/*++
Routine Description:
A pin mapped event might have happended when we didn't have the
participant's name so it was queued in a list. Now that we have a new
participant, let's check if this is the same participant. If it is,
we complete the pin mapped event by creating a substream and send
the app a notification.
Arguments:
dwSSRC - the SSRC of the participant.
pITParticipant - the participant object.
Return Value:
S_OK,
E_UNEXPECTED
--*/
{
LOG((MSP_TRACE, "%ls Check pending mapped event, dwSSRC: %x", m_szName, dwSSRC));
// look at the pending SSRC list and find out if this report
// fits in the list.
IPin *pIPin = NULL;
for (int i = 0; i < m_PinMappedEvents.GetSize(); i ++)
{
if (m_PinMappedEvents[i].dwSSRC == dwSSRC)
{
pIPin = m_PinMappedEvents[i].pIPin;
break;
}
}
if (!pIPin)
{
// the SSRC is not in the list of pending PinMappedEvents.
LOG((MSP_TRACE, "the SSRC %x is not in the pending list", dwSSRC));
// Find out if the participant is talking.
// if (ParticipantIsNotTalking)
{
return S_OK;;
}
}
ITSubStream * pITSubStream;
HRESULT hr = InternalCreateSubStream(&pITSubStream);
if (FAILED(hr))
{
LOG((MSP_ERROR, "%ls can't create a SubStream, %x", m_szName, hr));
return hr;
}
((CSubStreamVideoRecv*)pITSubStream)->SetCurrentParticipant(
dwSSRC, pITParticipant
);
((CIPConfMSPCall *)m_pMSPCall)->SendParticipantEvent(
PE_NEW_SUBSTREAM,
pITParticipant,
pITSubStream
);
if (pIPin)
{
// we got here because we had a pending mapped event.
// get rid of the peding event.
m_PinMappedEvents.RemoveAt(i);
if (FAILED(hr = m_pIRTPSession->SetQosState(dwSSRC, TRUE)))
{
LOG((MSP_ERROR, "enabling video QOS for %x. hr:%x", dwSSRC, hr));
}
else
{
LOG((MSP_INFO, "enabled video QOS for %x.", dwSSRC));
}
// Now we get the participant, the substream, and the pin. Establish a mapping
// between the decoding branch and the substream.
for (i = 0; i < m_Branches.GetSize(); i ++)
{
if (m_Branches[i].pIPin == pIPin)
{
if (m_Branches[i].pITSubStream != NULL)
{
((CSubStreamVideoRecv*)m_Branches[i].pITSubStream)
->ClearCurrentTerminal();
m_Branches[i].pITSubStream->Release();
}
m_Branches[i].dwSSRC = dwSSRC;
m_Branches[i].pITSubStream = pITSubStream;
pITSubStream->AddRef();
((CSubStreamVideoRecv*)pITSubStream)->
SetCurrentTerminal(m_Branches[i].pITTerminal);
((CIPConfMSPCall *)m_pMSPCall)->SendParticipantEvent(
PE_SUBSTREAM_MAPPED,
pITParticipant,
pITSubStream
);
break;
}
}
_ASSERT(i < m_Branches.GetSize());
}
pITSubStream->Release();
return S_OK;
}
HRESULT CStreamVideoRecv::ProcessPinMappedEvent(
IN DWORD dwSSRC,
IN IPin * pIPin
)
/*++
Routine Description:
A pin just got a new SSRC mapped to it. If the participant doesn't exist,
put the event in a pending queue and wait for a RTCP report that has the
participant's name. If the participant exists, check to see if a SubStream
has been created for the stream. If not, a SubStream is created. Then a
Particiapnt substream event is fired.
Arguments:
dwSSRC - the SSRC of the participant.
pIPin - the output pin of the demux filter that just got a new SSRC.
Return Value:
S_OK,
E_UNEXPECTED
--*/
{
LOG((MSP_TRACE, "%ls Process pin mapped event, pIPin: %p", m_szName, pIPin));
CLock lock(m_lock);
if (m_pIRTPDemux == NULL)
{
LOG((MSP_ERROR, "the demux filter doesn't exist."));
return E_UNEXPECTED;
}
for (int iBranch = 0; iBranch < m_Branches.GetSize(); iBranch ++)
{
if (m_Branches[iBranch].pIPin == pIPin)
{
break;
}
}
LOG((MSP_INFO, "Branch %d has the pin", iBranch));
if (iBranch >= m_Branches.GetSize())
{
LOG((MSP_ERROR, "Wrong pin is mapped. %p", pIPin));
return E_UNEXPECTED;
}
// sometimes we might get a mapped event for branches that are still
// in use.
if (m_Branches[iBranch].pITSubStream != NULL)
{
LOG((MSP_ERROR, "ProcessPinMappedEvent: Branch still in use"));
// sometimes we might get duplicated map events
if (m_Branches[iBranch].dwSSRC == dwSSRC)
{
// LOG((MSP_WARNING, "ProcessPinMappedEvent: Branch still in use"));
LOG((MSP_ERROR, "The same pin mapped twice. %p", pIPin));
return E_UNEXPECTED;
}
else
{
LOG((MSP_ERROR, "The branch is in use. Cleaning up."));
((CSubStreamVideoRecv*)m_Branches[iBranch].pITSubStream)->
ClearCurrentTerminal();
// cancel QOS for the old participant.
m_pIRTPSession->SetQosState(m_Branches[iBranch].dwSSRC, FALSE);
m_Branches[iBranch].pITSubStream->Release();
m_Branches[iBranch].pITSubStream = NULL;
m_Branches[iBranch].dwSSRC = 0;
}
}
ITParticipant * pITParticipant = NULL;
// find the SSRC in our participant list.
for (int i = 0; i < m_Participants.GetSize(); i ++)
{
if (((CParticipant *)m_Participants[i])->
HasSSRC((ITStream *)this, dwSSRC))
{
pITParticipant = m_Participants[i];
break;
}
}
// if the participant is not there yet, put the event in a queue and it
// will be fired when we have the CName for the participant.
if (!pITParticipant)
{
LOG((MSP_INFO, "can't find a participant that has SSRC %x", dwSSRC));
PINMAPEVENT Event;
Event.pIPin = pIPin;
Event.dwSSRC = dwSSRC;
m_PinMappedEvents.Add(Event);
LOG((MSP_INFO, "added the event to pending list, new list size:%d",
m_PinMappedEvents.GetSize()));
return S_OK;
}
HRESULT hr;
// Enable QOS for the participant since it is being rendered.
if (FAILED(hr = m_pIRTPSession->SetQosState(dwSSRC, TRUE)))
{
LOG((MSP_ERROR, "enabling vidoe QOS for %x. hr:%x", dwSSRC, hr));
}
else
{
LOG((MSP_INFO, "enabled video QOS for %x.", dwSSRC));
}
// Find out if a substream has been created for this participant who might
// have been a receiver only and hasn't got a substream.
ITSubStream * pITSubStream = NULL;
for (i = 0; i < m_SubStreams.GetSize(); i ++)
{
ITParticipant *pTempParticipant;
DWORD dwSSRC;
((CSubStreamVideoRecv*)m_SubStreams[i])->GetCurrentParticipant(
&dwSSRC, &pTempParticipant
);
_ASSERTE(pTempParticipant != NULL);
pTempParticipant->Release(); // we dont' need the ref here.
if (pITParticipant == pTempParticipant)
{
pITSubStream = m_SubStreams[i];
pITSubStream->AddRef();
break;
}
}
if (pITSubStream == NULL)
{
// we need to create a substream for this participant since he has
// started sending.
hr = InternalCreateSubStream(&pITSubStream);
if (FAILED(hr))
{
LOG((MSP_ERROR, "%ls can't create a SubStream, %x", m_szName, hr));
return hr;
}
((CSubStreamVideoRecv*)pITSubStream)->SetCurrentParticipant(
dwSSRC, pITParticipant
);
((CIPConfMSPCall *)m_pMSPCall)->SendParticipantEvent(
PE_NEW_SUBSTREAM,
pITParticipant,
pITSubStream
);
}
if (((CSubStreamVideoRecv*)pITSubStream)->ClearCurrentTerminal())
{
// The substrem has a terminal before. This is an error.
// _ASSERT(!"SubStream has a terminal already");
LOG((MSP_ERROR, "SubStream %p has already got a terminal", pITSubStream));
// remove the mapping if the substream was mapped to a branch.
for (i = 0; i < m_Branches.GetSize(); i ++)
{
if (m_Branches[i].pITSubStream == pITSubStream)
{
// cancel QOS for the old participant.
m_pIRTPSession->SetQosState(m_Branches[i].dwSSRC, FALSE);
m_Branches[i].pITSubStream->Release();
m_Branches[i].pITSubStream = NULL;
m_Branches[i].dwSSRC = 0;
LOG((MSP_ERROR, "SubStream %p was mapped to branch %d", i));
break;
}
}
}
// Now we get the participant, the substream, and the pin. Establish a mapping
// between the decoding branch and the substream.
m_Branches[iBranch].dwSSRC = dwSSRC;
m_Branches[iBranch].pITSubStream = pITSubStream;
pITSubStream->AddRef();
((CSubStreamVideoRecv*)pITSubStream)->
SetCurrentTerminal(m_Branches[iBranch].pITTerminal);
((CIPConfMSPCall *)m_pMSPCall)->SendParticipantEvent(
PE_SUBSTREAM_MAPPED,
pITParticipant,
pITSubStream
);
pITSubStream->Release();
return S_OK;
}
HRESULT CStreamVideoRecv::ProcessPinUnmapEvent(
IN DWORD dwSSRCOnPin,
IN IPin * pIPin
)
/*++
Routine Description:
A pin just got unmapped by the demux. Notify the app which substream
is not going to have any data.
Arguments:
dwSSRCOnPin - the SSRC of the participant.
pIPin - the output pin of the demux filter
Return Value:
S_OK,
E_UNEXPECTED
--*/
{
LOG((MSP_TRACE, "%ls Proces pin unmapped event, pIPin: %p", m_szName, pIPin));
CLock lock(m_lock);
if (m_pIRTPSession == NULL)
{
LOG((MSP_ERROR, "the demux filter doesn't exist."));
return E_UNEXPECTED;
}
// look at the pending SSRC list and find out if the pin is in the
// pending list.
for (int i = 0; i < m_PinMappedEvents.GetSize(); i ++)
{
if (m_PinMappedEvents[i].pIPin == pIPin)
{
break;
}
}
// if the pin is in the pending list, just remove it.
if (i < m_PinMappedEvents.GetSize())
{
m_PinMappedEvents.RemoveAt(i);
return S_OK;
}
// find out which substream got unmapped.
ITSubStream * pITSubStream = NULL;
for (i = 0; i < m_Branches.GetSize(); i ++)
{
if (m_Branches[i].pIPin == pIPin)
{
pITSubStream = m_Branches[i].pITSubStream;
if (pITSubStream)
{
// Don't release the ref until the end of this function.
m_Branches[i].pITSubStream = NULL;
m_Branches[i].dwSSRC = 0;
}
break;
}
}
if (!pITSubStream)
{
LOG((MSP_ERROR, "can't find a substream that got unmapped."));
return TAPI_E_NOITEMS;
}
((CSubStreamVideoRecv*)pITSubStream)->ClearCurrentTerminal();
ITParticipant *pITParticipant = NULL;
DWORD dwSSRC;
((CSubStreamVideoRecv*)pITSubStream)->GetCurrentParticipant(
&dwSSRC, &pITParticipant
) ;
_ASSERTE(pITParticipant != NULL);
if (dwSSRCOnPin != dwSSRC)
{
LOG((MSP_ERROR, "SSRCs don't match, pin's SSRC:%x, mine:%x",
dwSSRCOnPin, dwSSRC));
}
if (pITParticipant != NULL)
{
// fire an event to tell the app that the substream is not used.
((CIPConfMSPCall *)m_pMSPCall)->SendParticipantEvent(
PE_SUBSTREAM_UNMAPPED,
pITParticipant,
pITSubStream
);
pITParticipant->Release();
// cancel QOS for this participant.
HRESULT hr = m_pIRTPSession->SetQosState(dwSSRC, FALSE);
if (FAILED(hr))
{
LOG((MSP_ERROR, "disabling QOS for %x. hr:%x", dwSSRC, hr));
}
else
{
LOG((MSP_INFO, "disabled video QOS for %x.", dwSSRC));
}
}
pITSubStream->Release();
return S_OK;
}
HRESULT CStreamVideoRecv::ProcessParticipantLeave(
IN DWORD dwSSRC
)
/*++
Routine Description:
When participant left the session, remove the stream from the participant
object's list of streams. If all streams are removed, remove the
participant from the call object's list too.
Arguments:
dwSSRC - the SSRC of the participant left.
Return Value:
HRESULT.
--*/
{
LOG((MSP_TRACE, "%ls ProcessParticipantLeave, SSRC: %x", m_szName, dwSSRC));
CLock lock(m_lock);
if (m_pIRTPSession == NULL)
{
LOG((MSP_ERROR, "the network filter doesn't exist."));
return E_UNEXPECTED;
}
CParticipant *pParticipant;
BOOL fLast = FALSE;
HRESULT hr = E_FAIL;
// first try to find the SSRC in our participant list.
for (int iParticipant = 0;
iParticipant < m_Participants.GetSize(); iParticipant ++)
{
pParticipant = (CParticipant *)m_Participants[iParticipant];
hr = pParticipant->RemoveStream(
(ITStream *)this,
dwSSRC,
&fLast
);
if (SUCCEEDED(hr))
{
break;
}
}
// if the participant is not found
if (FAILED(hr))
{
LOG((MSP_TRACE, "SSRC:%x had been removed.", dwSSRC));
return hr;
}
ITParticipant *pITParticipant = m_Participants[iParticipant];
// cancel QOS for this participant.
if (FAILED(hr = m_pIRTPSession->SetQosState(dwSSRC, FALSE)))
{
// the stream might already been stopped
// so we just put a warning here
LOG((MSP_WARN, "disabling QOS for %x. hr:%x", dwSSRC, hr));
}
else
{
LOG((MSP_INFO, "disabled video QOS for %x.", dwSSRC));
}
// find out which substream is going away.
ITSubStream * pITSubStream = NULL;
for (int i = 0; i < m_SubStreams.GetSize(); i ++)
{
// Find out the participant on the SubStream.
ITParticipant *pTempParticipant;
DWORD dwSSRC;
((CSubStreamVideoRecv*)m_SubStreams[i])->GetCurrentParticipant(
&dwSSRC, &pTempParticipant
);
_ASSERTE(pTempParticipant != NULL);
pTempParticipant->Release(); // we dont' need the ref here.
if (pTempParticipant == pITParticipant)
{
pITSubStream = m_SubStreams[i];
break;
}
}
if (pITSubStream)
{
// remove the mapping if the substream was mapped to a branch.
for (int i = 0; i < m_Branches.GetSize(); i ++)
{
if (m_Branches[i].pITSubStream == pITSubStream)
{
m_Branches[i].pITSubStream->Release();
m_Branches[i].pITSubStream = NULL;
m_Branches[i].dwSSRC = 0;
// fire an event to tell the app that the substream is not used.
((CIPConfMSPCall *)m_pMSPCall)->SendParticipantEvent(
PE_SUBSTREAM_UNMAPPED,
pITParticipant,
pITSubStream
);
break;
}
}
((CIPConfMSPCall *)m_pMSPCall)->SendParticipantEvent(
PE_SUBSTREAM_REMOVED,
pITParticipant,
pITSubStream
);
if (m_SubStreams.Remove(pITSubStream))
{
pITSubStream->Release();
}
}
m_Participants.RemoveAt(iParticipant);
// if this stream is the last stream that the participant is on,
// tell the call object to remove it from its list.
if (fLast)
{
((CIPConfMSPCall *)m_pMSPCall)->ParticipantLeft(pITParticipant);
}
pITParticipant->Release();
return S_OK;
}
//
// ITStreamQualityControl methods
//
STDMETHODIMP CStreamVideoRecv::Set (
IN StreamQualityProperty Property,
IN long lValue,
IN TAPIControlFlags lFlags
)
{
return E_NOTIMPL;
}
STDMETHODIMP CStreamVideoRecv::Get(
IN InnerStreamQualityProperty property,
OUT LONG *plValue,
OUT TAPIControlFlags *plFlags
)
/*++
Routine Description:
Get the value for a quality control property. Delegated to the quality
controller.
Arguments:
Return Value:
HRESULT.
--*/
{
ENTER_FUNCTION("CStreamVideoRecv::Get(QualityControl)");
HRESULT hr;
int i;
LONG totalbps, bps;
CLock lock(m_lock);
switch (property)
{
case InnerStreamQuality_MaxBitrate:
bps = 0;
totalbps = 0;
for (i=0; i<m_Branches.GetSize (); i++)
{
if (NULL == m_Branches[i].pBitrateControl)
continue;
if (FAILED (hr = m_Branches[i].pBitrateControl->Get (BitrateControl_Maximum, &bps, plFlags, LAYERID)))
LOG ((MSP_ERROR, "%s failed to get maximum bitrate, %x", __fxName, hr));
else
totalbps += bps;
}
*plValue = totalbps;
hr = S_OK;
break;
case InnerStreamQuality_CurrBitrate:
bps = 0;
totalbps = 0;
for (i=0; i<m_Branches.GetSize (); i++)
{
if (NULL == m_Branches[i].pBitrateControl)
continue;
if (FAILED (hr = m_Branches[i].pBitrateControl->Get (BitrateControl_Current, &bps, plFlags, LAYERID)))
LOG ((MSP_ERROR, "%s failed to get current bitrate, %x", __fxName, hr));
else
totalbps += bps;
}
*plValue = totalbps;
hr = S_OK;
break;
default:
hr = CIPConfMSPStream::Get (property, plValue, plFlags);
break;
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
//
// CStreamVideoSend
//
/////////////////////////////////////////////////////////////////////////////
CStreamVideoSend::CStreamVideoSend()
: CIPConfMSPStream(),
m_pCaptureTerminal(NULL),
m_pPreviewTerminal(NULL),
m_pCaptureFilter(NULL),
m_pCapturePin(NULL),
m_pPreviewPin(NULL),
m_pRTPPin(NULL),
m_pCaptureBitrateControl(NULL),
m_pCaptureFrameRateControl(NULL),
m_pPreviewFrameRateControl(NULL)
{
m_szName = L"VideoSend";
}
CStreamVideoSend::~CStreamVideoSend()
{
CleanupCachedInterface();
}
void CStreamVideoSend::CleanupCachedInterface()
{
if (m_pCaptureFilter)
{
m_pCaptureFilter->Release();
m_pCaptureFilter = NULL;
}
if (m_pCapturePin)
{
m_pCapturePin->Release();
m_pCapturePin = NULL;
}
if (m_pIStreamConfig)
{
m_pIStreamConfig->Release();
m_pIStreamConfig = NULL;
}
if (m_pPreviewPin)
{
m_pPreviewPin->Release();
m_pPreviewPin = NULL;
}
if (m_pRTPPin)
{
m_pRTPPin->Release();
m_pRTPPin = NULL;
}
if (m_pCaptureFrameRateControl)
{
m_pCaptureFrameRateControl->Release();
m_pCaptureFrameRateControl = NULL;
}
if (m_pCaptureBitrateControl)
{
m_pCaptureBitrateControl->Release();
m_pCaptureBitrateControl = NULL;
}
if (m_pPreviewFrameRateControl)
{
m_pPreviewFrameRateControl->Release();
m_pPreviewFrameRateControl = NULL;
}
}
HRESULT CStreamVideoSend::ShutDown()
/*++
Routine Description:
Shut down the stream. Release our members and then calls the base class's
ShutDown method.
Arguments:
Return Value:
S_OK
--*/
{
CLock lock(m_lock);
// if there are terminals
BOOL fHasTerminal = FALSE;
if (m_Terminals.GetSize() > 0)
{
fHasTerminal = TRUE;
}
// if graph is running
HRESULT hr;
OAFilterState FilterState = State_Stopped;
if (m_pIMediaControl)
{
if (FAILED (hr = m_pIMediaControl->GetState(0, &FilterState)))
{
LOG ((MSP_ERROR, "CStreamAudioRecv::ShutDown failed to query filter state. %d", hr));
FilterState = State_Stopped;
}
}
if (m_pCaptureTerminal)
{
m_pCaptureTerminal->Release();
m_pCaptureTerminal = NULL;
}
if (m_pPreviewTerminal)
{
m_pPreviewTerminal->Release();
m_pPreviewTerminal = NULL;
}
CleanupCachedInterface();
// fire event
if (fHasTerminal && FilterState == State_Running)
{
SendStreamEvent(CALL_STREAM_INACTIVE, CALL_CAUSE_LOCAL_REQUEST, 0, NULL);
}
return CIPConfMSPStream::ShutDown();
}
HRESULT
SetVideoFormat(
IN IUnknown * pIUnknown,
IN BOOL bCIF,
IN DWORD dwFramesPerSecond
)
/*++
Routine Description:
Set the video format to be CIF or QCIF and also set the frames per second.
Arguments:
pIUnknown - a capture terminal.
bCIF - CIF or QCIF.
dwFramesPerSecond - Frames per second.
Return Value:
HRESULT
--*/
{
LOG((MSP_TRACE, "SetVideoFormat"));
HRESULT hr;
// first get eht IAMStreamConfig interface.
CComPtr<IAMStreamConfig> pIAMStreamConfig;
if (FAILED(hr = pIUnknown->QueryInterface(
__uuidof(IAMStreamConfig),
(void **)&pIAMStreamConfig
)))
{
LOG((MSP_ERROR, "Can't get IAMStreamConfig interface.%8x", hr));
return hr;
}
// get the current format of the video capture terminal.
AM_MEDIA_TYPE *pmt;
if (FAILED(hr = pIAMStreamConfig->GetFormat(&pmt)))
{
LOG((MSP_ERROR, "GetFormat returns error: %8x", hr));
return hr;
}
VIDEOINFO *pVideoInfo = (VIDEOINFO *)pmt->pbFormat;
if (pVideoInfo == NULL)
{
MSPDeleteMediaType(pmt);
return E_UNEXPECTED;
}
BITMAPINFOHEADER *pHeader = HEADER(pmt->pbFormat);
if (pHeader == NULL)
{
MSPDeleteMediaType(pmt);
return E_UNEXPECTED;
}
LOG((MSP_INFO,
"Video capture: Format BitRate: %d, TimePerFrame: %d",
pVideoInfo->dwBitRate,
pVideoInfo->AvgTimePerFrame));
LOG((MSP_INFO, "Video capture: Format Compression:%c%c%c%c %dbit %dx%d",
(DWORD)pHeader->biCompression & 0xff,
((DWORD)pHeader->biCompression >> 8) & 0xff,
((DWORD)pHeader->biCompression >> 16) & 0xff,
((DWORD)pHeader->biCompression >> 24) & 0xff,
pHeader->biBitCount,
pHeader->biWidth,
pHeader->biHeight));
// The time is in 100ns unit.
pVideoInfo->AvgTimePerFrame = (DWORD) 1e7 / dwFramesPerSecond;
if (bCIF)
{
pHeader->biWidth = CIFWIDTH;
pHeader->biHeight = CIFHEIGHT;
}
else
{
pHeader->biWidth = QCIFWIDTH;
pHeader->biHeight = QCIFHEIGHT;
}
#if defined(ALPHA)
// update bmiSize with new Width/Height
pHeader->biSizeImage = DIBSIZE( ((VIDEOINFOHEADER *)pmt->pbFormat)->bmiHeader );
#endif
if (FAILED(hr = pIAMStreamConfig->SetFormat(pmt)))
{
LOG((MSP_ERROR, "putMediaFormat returns error: %8x", hr));
}
else
{
LOG((MSP_INFO,
"Video capture: Format BitRate: %d, TimePerFrame: %d",
pVideoInfo->dwBitRate,
pVideoInfo->AvgTimePerFrame));
LOG((MSP_INFO, "Video capture: Format Compression:%c%c%c%c %dbit %dx%d",
(DWORD)pHeader->biCompression & 0xff,
((DWORD)pHeader->biCompression >> 8) & 0xff,
((DWORD)pHeader->biCompression >> 16) & 0xff,
((DWORD)pHeader->biCompression >> 24) & 0xff,
pHeader->biBitCount,
pHeader->biWidth,
pHeader->biHeight));
}
MSPDeleteMediaType(pmt);
return hr;
}
HRESULT
SetVideoBufferSize(
IN IUnknown *pIUnknown
)
/*++
Routine Description:
Set the video capture terminal's buffersize.
Arguments:
pIUnknown - a capture terminal.
Return Value:
HRESULT
--*/
{
// The number of capture buffers is four for now.
#define NUMCAPTUREBUFFER 4
LOG((MSP_TRACE, "SetVideoBufferSize"));
HRESULT hr;
CComPtr<IAMBufferNegotiation> pBN;
if (FAILED(hr = pIUnknown->QueryInterface(
__uuidof(IAMBufferNegotiation),
(void **)&pBN
)))
{
LOG((MSP_ERROR, "Can't get buffer negotiation interface.%8x", hr));
return hr;
}
ALLOCATOR_PROPERTIES prop;
#if 0 // Get allocator property is not working.
if (FAILED(hr = pBN->GetAllocatorProperties(&prop)))
{
LOG((MSP_ERROR, "GetAllocatorProperties returns error: %8x", hr));
return hr;
}
// Set the number of buffers.
if (prop.cBuffers > NUMCAPTUREBUFFER)
{
prop.cBuffers = NUMCAPTUREBUFFER;
}
#endif
DWORD dwBuffers = NUMCAPTUREBUFFER;
GetRegValue(gszNumVideoCaptureBuffers, &dwBuffers);
prop.cBuffers = dwBuffers;
prop.cbBuffer = -1;
prop.cbAlign = -1;
prop.cbPrefix = -1;
if (FAILED(hr = pBN->SuggestAllocatorProperties(&prop)))
{
LOG((MSP_ERROR, "SuggestAllocatorProperties returns error: %8x", hr));
}
else
{
LOG((MSP_INFO,
"SetVidedobuffersize"
" buffers: %d, buffersize: %d, align: %d, Prefix: %d",
prop.cBuffers,
prop.cbBuffer,
prop.cbAlign,
prop.cbPrefix
));
}
return hr;
}
HRESULT CStreamVideoSend::FindPreviewInputPin(
IN ITTerminalControl* pTerminal,
OUT IPin ** ppIPin
)
/*++
Routine Description:
Find the input pin on a preview terminal.
Arguments:
pTerminal - a video render terminal.
ppIPin - the address to store a pointer to a IPin interface.
Return Value:
HRESULT
--*/
{
LOG((MSP_TRACE, "VideoSend.FindPreviewInputPin, pTerminal %x", pTerminal));
/*
// try to disable DDraw because we want to use DDraw for receive stream.
HRESULT hr2;
IDrawVideoImage *pIDrawVideoImage;
hr2 = pTerminal->QueryInterface(__uuidof(IDrawVideoImage), (void **)&pIDrawVideoImage);
if (SUCCEEDED(hr2))
{
hr2 = pIDrawVideoImage->DrawVideoImageBegin();
if (FAILED(hr2))
{
LOG((MSP_WARN, "Can't disable DDraw. %x", hr2));
}
else
{
LOG((MSP_INFO, "DDraw disabled."));
}
pIDrawVideoImage->Release();
}
else
{
LOG((MSP_WARN, "Can't get IDrawVideoImage. %x", hr2));
}
*/
// Get the pins from the first terminal because we only use on terminal
// on this stream.
const DWORD MAXPINS = 8;
DWORD dwNumPins = MAXPINS;
IPin * Pins[MAXPINS];
HRESULT hr = pTerminal->ConnectTerminal(
m_pIGraphBuilder, TD_RENDER, &dwNumPins, Pins
);
if (FAILED(hr))
{
LOG((MSP_ERROR, "can't connect to terminal, %x", hr));
return hr;
}
if (dwNumPins == 0)
{
LOG((MSP_ERROR, "terminal has no pins."));
return hr;
}
if (IsBadReadPtr (Pins, dwNumPins * sizeof (IPin*)))
{
LOG((MSP_ERROR, "terminal returned bad pin array"));
return E_POINTER;
}
for (DWORD i = 0; i < dwNumPins; i++)
{
if (IsBadReadPtr (Pins[i], sizeof (IPin)))
{
LOG((MSP_ERROR, "terminal returned bad pin. # %d", i));
return E_POINTER;
}
}
// Save the first pin and release the others.
CComPtr <IPin> pIPin = Pins[0];
for (DWORD i = 0; i < dwNumPins; i ++)
{
Pins[i]->Release();
}
pIPin.p->AddRef();
*ppIPin = pIPin;
return hr;
}
HRESULT CStreamVideoSend::CheckTerminalTypeAndDirection(
IN ITTerminal * pTerminal
)
/*++
Routine Description:
Check if the terminal is allowed on this stream.
VideoSend allows both a capture terminal and a preivew terminal.
Arguments:
pTerminal - the terminal.
Return value:
HRESULT.
S_OK means the terminal is OK.
*/
{
LOG((MSP_TRACE, "VideoSend.CheckTerminalTypeAndDirection"));
// This stream only support one capture + one preview terminal
if (m_Terminals.GetSize() > 1)
{
return TAPI_E_MAXTERMINALS;
}
// check the media type of this terminal.
long lMediaType;
HRESULT hr = pTerminal->get_MediaType(&lMediaType);
if (FAILED(hr))
{
LOG((MSP_ERROR, "can't get terminal media type. %x", hr));
return TAPI_E_INVALIDTERMINAL;
}
if ((DWORD)lMediaType != m_dwMediaType)
{
return TAPI_E_INVALIDTERMINAL;
}
// check the direction of this terminal.
TERMINAL_DIRECTION Direction;
hr = pTerminal->get_Direction(&Direction);
if (FAILED(hr))
{
LOG((MSP_ERROR, "can't get terminal direction. %x", hr));
return TAPI_E_INVALIDTERMINAL;
}
if (m_Terminals.GetSize() > 0)
{
// check the direction of this terminal.
TERMINAL_DIRECTION Direction2;
hr = m_Terminals[0]->get_Direction(&Direction2);
if (FAILED(hr))
{
LOG((MSP_ERROR, "can't get terminal direction. %x", hr));
return TAPI_E_INVALIDTERMINAL;
}
if (Direction == Direction2)
{
LOG((MSP_ERROR,
"can't have two terminals with the same direction. %x", hr));
return TAPI_E_MAXTERMINALS;
}
}
return S_OK;
}
HRESULT CStreamVideoSend::SetUpFilters()
/*++
Routine Description:
Insert filters into the graph and connect to the terminals.
Arguments:
Return Value:
HRESULT.
--*/
{
LOG((MSP_TRACE, "VideoSend.SetUpFilters"));
// we only support one capture terminal and one preview
// window on this stream.
if (m_Terminals.GetSize() > 2)
{
return E_UNEXPECTED;
}
int iCaptureIndex = -1, iPreviewIndex = -1;
// Find out which terminal is capture and which is preview.
HRESULT hr;
for (int i = 0; i < m_Terminals.GetSize(); i ++)
{
TERMINAL_DIRECTION Direction;
hr = m_Terminals[i]->get_Direction(&Direction);
if (FAILED(hr))
{
LOG((MSP_ERROR, "can't get terminal direction. %x", hr));
SendStreamEvent(CALL_TERMINAL_FAIL, CALL_CAUSE_BAD_DEVICE, hr, m_Terminals[i]);
return hr;
}
if (Direction == TD_CAPTURE || Direction == TD_BIDIRECTIONAL)
{
iCaptureIndex = i;
}
else
{
iPreviewIndex = i;
}
}
// the stream will not work without a capture terminal.
if (iCaptureIndex == -1)
{
LOG((MSP_ERROR, "no capture terminal selected."));
return E_UNEXPECTED;
}
// Connect the capture filter to the terminal.
if (FAILED(hr = ConnectTerminal(
m_Terminals[iCaptureIndex]
)))
{
LOG((MSP_ERROR, "connect the codec filter to terminal. %x", hr));
return hr;
}
if (iPreviewIndex != -1)
{
// Connect the preview filter to the terminal.
if (FAILED(hr = ConnectTerminal(
m_Terminals[iPreviewIndex]
)))
{
LOG((MSP_ERROR, "connect the codec filter to terminal. %x", hr));
return hr;
}
}
return hr;
}
HRESULT CStreamVideoSend::GetVideoCapturePins(
IN ITTerminalControl* pTerminal,
OUT BOOL *pfDirectRTP
)
/*++
Routine Description:
Given a video capture terminal, find all the pins we need, which will be
the capture pin, preview pin, and the RTP packetization pin.
Side effect: It changes the m_pCapturePin, m_pPreviewPin, m_pRTPPin
members, which needs to be cleaned up if the terminal is disconnected.
Arguments:
pTerminal - a pointer to the ITTerminalControl interface.
pfDirectRTP - whether this terminal support RTP directly.
Return Value:
HRESULT
--*/
{
ENTER_FUNCTION("CStreamVideoSend::GetVideoCapturePins");
LOG((MSP_TRACE, "%s enters", __fxName));
const DWORD MAXPINS = 4;
DWORD dwNumPins = MAXPINS;
IPin * Pins[MAXPINS];
HRESULT hr = pTerminal->ConnectTerminal(
m_pIGraphBuilder, TD_CAPTURE, &dwNumPins, Pins
);
if (FAILED(hr))
{
LOG((MSP_ERROR, "%s, can't connect to terminal, hr=%x", __fxName, hr));
return hr;
}
_ASSERT(m_pCapturePin == NULL && m_pPreviewPin == NULL && m_pRTPPin == NULL);
if (IsBadReadPtr (Pins, dwNumPins * sizeof (IPin*)))
{
LOG((MSP_ERROR, "terminal returned bad pin array"));
return E_POINTER;
}
// find the pins we need.
for (DWORD i = 0; i < dwNumPins; i ++)
{
if (IsBadReadPtr (Pins[i], sizeof (IPin)))
{
LOG((MSP_ERROR, "terminal returned bad pin. # %d", i));
hr = E_POINTER;
break;
}
PIN_INFO PinInfo;
hr = Pins[i]->QueryPinInfo(&PinInfo);
if (FAILED(hr))
{
LOG((MSP_ERROR, "%s, can't get pin info, hr=%x", __fxName, hr));
break;
}
if (lstrcmpW(PinInfo.achName, PNAME_CAPTURE) == 0)
{
m_pCapturePin = Pins[i];
// remember the capture filter as well.
m_pCaptureFilter = PinInfo.pFilter;
m_pCaptureFilter->AddRef();
}
else if (lstrcmpW(PinInfo.achName, PNAME_PREVIEW) == 0)
{
m_pPreviewPin = Pins[i];
}
else if (lstrcmpW(PinInfo.achName, PNAME_RTPPD) == 0)
{
m_pRTPPin = Pins[i];
}
else if (PinInfo.dir == PINDIR_OUTPUT)
{
// this must be the capture filter of some third party terminal.
m_pCapturePin = Pins[i];
// remember the capture filter as well.
m_pCaptureFilter = PinInfo.pFilter;
m_pCaptureFilter->AddRef();
}
else
{
Pins[i]->Release();
}
// we don't need the filter here.
PinInfo.pFilter->Release();
}
// check if we have got all the pins we need.
if (m_pCapturePin == NULL ||
m_pPreviewPin == NULL ||
m_pRTPPin == NULL)
{
if ((m_pCapturePin != NULL)
&& (hr = ::PinSupportsMediaType(
m_pCapturePin, __uuidof(MEDIATYPE_RTP_Single_Stream))) == S_OK)
{
// This terminal generates RTP directly.
*pfDirectRTP = TRUE;
return S_OK;
}
LOG((MSP_ERROR,
"%s, can't find all the pins, Capture:%p, Preview:%p, RTP:%P",
__fxName, m_pCapturePin, m_pPreviewPin, m_pRTPPin));
hr = E_UNEXPECTED;
}
if (hr != S_OK)
{
// something is wrong, clean up
pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
CleanupCachedInterface();
return hr;
}
// now get the optional video interfaces.
_ASSERT(m_pIStreamConfig == NULL);
hr = m_pCapturePin->QueryInterface(&m_pIStreamConfig);
if (FAILED(hr))
{
LOG((MSP_WARN, "%s, queryinterface failed", __fxName));
}
hr = m_pCapturePin->QueryInterface(&m_pCaptureFrameRateControl);
if (FAILED(hr))
{
LOG((MSP_WARN,
"%s:query capture pin's IFrameRateControl failed, hr=%x",
__fxName, hr));
}
hr = m_pCapturePin->QueryInterface(&m_pCaptureBitrateControl);
if (FAILED(hr))
{
LOG((MSP_WARN,
"%s:query capture pin's IBitRateControl failed, hr=%x",
__fxName, hr));
}
hr = m_pPreviewPin->QueryInterface(&m_pPreviewFrameRateControl);
if (FAILED(hr))
{
LOG((MSP_WARN,
"%s:query preview pin's IFrameRateControl failed, hr=%x",
__fxName, hr));
}
return S_OK;
}
HRESULT CStreamVideoSend::ConnectCaptureTerminal(
IN ITTerminal * pITTerminal
)
/*++
Routine Description:
The stream needs its capture pin, preview pin, and RTP packetization pin.
The capture pin and the preview pin are connected to the RTP sink filter
and the preview pin is connected to the preivew terminal. If the preview
terminal doesn't exist yet, the preview pin is remembered and used later
when the preview terminal is selected.
Arguments:
pITTerminal - the terminal being connected.
Return Value:
HRESULT.
--*/
{
ENTER_FUNCTION("CStreamVideoSend::ConnectCaptureTerminal");
LOG((MSP_TRACE, "%s enters, pITTerminal:%p", __fxName, pITTerminal));
// Get the TerminalControl interface on the terminal
CComPtr<ITTerminalControl> pTerminal;
HRESULT hr = pITTerminal->QueryInterface(&pTerminal);
if (FAILED(hr))
{
LOG((MSP_ERROR,
"%s, can't get Terminal Control interface", __fxName));
SendStreamEvent(CALL_TERMINAL_FAIL,
CALL_CAUSE_BAD_DEVICE, E_NOINTERFACE, pITTerminal);
return E_NOINTERFACE;
}
// Find the pins on the capture terminal. The pins will be stored in
// m_pCapturePin, m_pPreviewPin, m_pRTPPin
BOOL fDirectRTP = FALSE;
hr = GetVideoCapturePins(pTerminal, &fDirectRTP);
if (FAILED(hr))
{
LOG((MSP_ERROR,
"%s, Get capture pins failed. hr=%x", __fxName, hr));
SendStreamEvent(CALL_TERMINAL_FAIL,
CALL_CAUSE_BAD_DEVICE, hr, pITTerminal);
return hr;
}
hr = CreateSendFilters(m_pCapturePin, m_pRTPPin, fDirectRTP);
if (FAILED(hr))
{
LOG((MSP_ERROR,
"%s, Create video send filters failed. hr=%x", __fxName, hr));
goto cleanup;
}
//
// Now we are actually connected. Update our state and perform
// postconnection.
//
hr = pTerminal->CompleteConnectTerminal();
if (FAILED(hr))
{
LOG((MSP_ERROR,
"%s, Create video send filters failed. hr=%x", __fxName, hr));
SendStreamEvent(CALL_TERMINAL_FAIL,
CALL_CAUSE_BAD_DEVICE, hr, pITTerminal);
goto cleanup;
}
if (m_pPreviewTerminal != NULL)
{
// errors will be fired as events.
ConnectPreviewTerminal(m_pPreviewTerminal);
}
return S_OK;
cleanup:
// disconnect the terminal.
pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
CleanupCachedInterface();
// clean up internal filters as well.
CleanUpFilters();
return hr;
}
HRESULT CStreamVideoSend::ConnectPreviewTerminal(
IN ITTerminal * pITTerminal
)
/*++
Routine Description:
If the capture terminal has been connected, this function connects the
capture terminal's preview pin with the preview terminal. Otherwise, the
preview terminal is just remembered and wait for the capture terminal.
Arguments:
pITTerminal - the terminal being connected.
Return Value:
HRESULT.
--*/
{
ENTER_FUNCTION("CStreamVideoSend::ConnectPreviewTerminal");
LOG((MSP_TRACE, "%s enters, pITTerminal:%p", __fxName, pITTerminal));
if (!m_pCapturePin)
{
LOG ((MSP_TRACE, "%s capture pin is null.", __fxName));
return E_FAIL;
}
if (!m_pPreviewPin)
{
// the capture terminal is not selected yet. We will just wait.
LOG((MSP_TRACE, "%s, capture is not ready yet.", __fxName));
return S_OK;
}
// Get the TerminalControl interface on the terminal
CComPtr<ITTerminalControl> pTerminal;
HRESULT hr = pITTerminal->QueryInterface(&pTerminal);
if (FAILED(hr))
{
LOG((MSP_ERROR,
"%s, can't get Terminal Control interface", __fxName));
SendStreamEvent(CALL_TERMINAL_FAIL,
CALL_CAUSE_BAD_DEVICE, E_NOINTERFACE, pITTerminal);
return E_NOINTERFACE;
}
// find the input pin on the preview window.
CComPtr<IPin> pPreviewInputPin;
hr = FindPreviewInputPin(pTerminal, &pPreviewInputPin);
if (FAILED(hr))
{
LOG((MSP_ERROR,
"%s, find preview input pin failed. hr=%x", __fxName, hr));
SendStreamEvent(CALL_TERMINAL_FAIL,
CALL_CAUSE_BAD_DEVICE, hr, pITTerminal);
return hr;
}
// connect the pins together.
hr = m_pIGraphBuilder->Connect(m_pPreviewPin, pPreviewInputPin);
if (FAILED(hr))
{
LOG((MSP_ERROR,
"%s, connect preview pins failed. hr=%x", __fxName, hr));
return hr;
}
//
// Now we are actually connected, perform postconnection.
//
hr = pTerminal->CompleteConnectTerminal();
if (FAILED(hr))
{
LOG((MSP_ERROR,
"%s, complete connect terminal failed. hr=%x", __fxName, hr));
SendStreamEvent(CALL_TERMINAL_FAIL,
CALL_CAUSE_BAD_DEVICE, hr, pITTerminal);
// disconnect the terminal.
pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
return hr;
}
return S_OK;
}
HRESULT CStreamVideoSend::ConnectTerminal(
IN ITTerminal * pITTerminal
)
/*++
Routine Description:
connect the video terminals to the stream.
Arguments:
Return Value:
HRESULT.
--*/
{
ENTER_FUNCTION("CStreamVideoSend::ConnectTerminal");
LOG((MSP_TRACE, "%s enters, pITTerminal:%p", __fxName, pITTerminal));
// Find out the direction of the terminal.
TERMINAL_DIRECTION Direction;
HRESULT hr = pITTerminal->get_Direction(&Direction);
if (FAILED(hr))
{
LOG((MSP_ERROR,
"%s, can't get terminal direction. hr=%x", __fxName, hr));
SendStreamEvent(CALL_TERMINAL_FAIL,
CALL_CAUSE_BAD_DEVICE, hr, pITTerminal);
return hr;
}
if (Direction != TD_RENDER)
{
hr = ConnectCaptureTerminal(pITTerminal);
if (SUCCEEDED(hr))
{
// save the capture terminal.
_ASSERT(m_pCaptureTerminal == NULL);
m_pCaptureTerminal = pITTerminal;
m_pCaptureTerminal->AddRef();
}
}
else
{
hr = ConnectPreviewTerminal(pITTerminal);
if (SUCCEEDED(hr))
{
// save the preview terminal.
_ASSERT(m_pPreviewTerminal == NULL);
m_pPreviewTerminal = pITTerminal;
m_pPreviewTerminal->AddRef();
}
}
return hr;
}
HRESULT CStreamVideoSend::DisconnectTerminal(
IN ITTerminal * pITTerminal
)
/*++
Routine Description:
Disconnect a terminal. It will remove its filters from the graph and
also release its references to the graph.
If it is the capture terminal being disconnected, all the pins that the
stream cached need to be released too.
Arguments:
pITTerminal - the terminal.
Return Value:
HRESULT.
--*/
{
ENTER_FUNCTION("CStreamVideoSend::DisconnectTerminal");
LOG((MSP_TRACE, "%s enters, pITTerminal:%p", __fxName, pITTerminal));
HRESULT hr = CIPConfMSPStream::DisconnectTerminal(pITTerminal);
if (pITTerminal == m_pCaptureTerminal)
{
// release all the capture pins we cached.
CleanupCachedInterface();
m_pCaptureTerminal->Release();
m_pCaptureTerminal = NULL;
CleanUpFilters ();
// disconnect preview term as well
// when we connect capture,
// we always try to connect preview if one is available
if (m_pPreviewTerminal)
{
CIPConfMSPStream::DisconnectTerminal(m_pPreviewTerminal);
}
}
else if (pITTerminal == m_pPreviewTerminal)
{
m_pPreviewTerminal->Release();
m_pPreviewTerminal = NULL;
}
return hr;
}
HRESULT CStreamVideoSend::ConnectRTPFilter(
IN IGraphBuilder *pIGraphBuilder,
IN IPin *pCapturePin,
IN IPin *pRTPPin,
IN IBaseFilter *pRTPFilter
)
{
ENTER_FUNCTION("CStreamVideoSend::ConnectRTPFilters");
LOG((MSP_TRACE, "%s enters", __fxName));
HRESULT hr;
// find the capture pin on the RTP filter.
CComPtr <IPin> pRTPCapturePin;
hr = pRTPFilter->FindPin(PNAME_CAPTURE, &pRTPCapturePin);
if (FAILED(hr))
{
LOG((MSP_ERROR,
"%s, find capture pin on rtp filter. %x", __fxName, hr));
return hr;
}
// Connect the capture pin of the video capture filter with the capture pin
// of the rTP filter.
hr = pIGraphBuilder->ConnectDirect(pCapturePin, pRTPCapturePin, NULL);
if (FAILED(hr))
{
LOG((MSP_ERROR,
"%s, can't connect capture pins. %x", __fxName, hr));
return hr;
}
if (pRTPPin)
{
// find the packetization pin on the RTP filter.
CComPtr <IPin> pRTPRTPPin;
hr = pRTPFilter->FindPin(PNAME_RTPPD, &pRTPRTPPin);
if (FAILED(hr))
{
LOG((MSP_ERROR,
"%s, find capture pin on rtp filter. %x", __fxName, hr));
pIGraphBuilder->Disconnect(pRTPPin);
return hr;
}
// Connect the RTP pin of the video capture filter with the RTP pin
// of the rTP filter.
hr = pIGraphBuilder->ConnectDirect(pRTPPin, pRTPRTPPin, NULL);
if (FAILED(hr))
{
LOG((MSP_ERROR,
"%s, can't connect capture pins. %x", __fxName, hr));
pIGraphBuilder->Disconnect(pRTPPin);
return hr;
}
}
return hr;
}
HRESULT CStreamVideoSend::ConfigureRTPFormats(
IN IBaseFilter * pIRTPFilter,
IN IStreamConfig * pIStreamConfig
)
/*++
Routine Description:
Configure the RTP filter with RTP<-->AM media type mappings.
Arguments:
pIRTPFilter - The source RTP Filter.
pIStreamConfig - The stream config interface that has the media info.
Return Value:
HRESULT.
--*/
{
ENTER_FUNCTION("VideoSend::ConfigureRTPFormats");
LOG((MSP_TRACE, "%s enters", __fxName));
HRESULT hr;
CComPtr<IRtpMediaControl> pIRtpMediaControl;
hr = pIRTPFilter->QueryInterface(&pIRtpMediaControl);
if (FAILED(hr))
{
LOG((MSP_ERROR, "%s adding source filter. %x", __fxName, hr));
return hr;
}
// find the number of capabilities supported.
DWORD dwCount;
hr = pIStreamConfig->GetNumberOfCapabilities(&dwCount);
if (FAILED(hr))
{
LOG((MSP_ERROR, "%s GetNumberOfCapabilities. %x", __fxName, hr));
return hr;
}
BOOL fFound = FALSE;
BOOL fFormatSet = FALSE;
for (DWORD dw = 0; dw < dwCount; dw ++)
{
// TODO, a new interface is needed to resolve RTP to MediaType.
AM_MEDIA_TYPE *pMediaType;
DWORD dwPayloadType;
hr = pIStreamConfig->GetStreamCaps(
dw, &pMediaType, NULL, &dwPayloadType
);
if (FAILED(hr))
{
LOG((MSP_ERROR, "%s GetStreamCaps. %x", __fxName, hr));
return hr;
}
BITMAPINFOHEADER *pHeader = HEADER(pMediaType->pbFormat);
if (pHeader == NULL)
{
MSPDeleteMediaType(pMediaType);
continue;
}
// check the image size
if (m_Settings.fCIF)
{
if (pHeader->biWidth != CIFWIDTH)
{
MSPDeleteMediaType(pMediaType);
continue;
}
}
else
{
if (pHeader->biWidth != QCIFWIDTH)
{
MSPDeleteMediaType(pMediaType);
continue;
}
}
for (DWORD dw2 = 0; dw2 < m_Settings.dwNumPayloadTypes; dw2 ++)
{
if (dwPayloadType == m_Settings.PayloadTypes[dw2])
{
hr = pIRtpMediaControl->SetFormatMapping(
dwPayloadType,
90000, // default video clock rate.
pMediaType
);
if (FAILED(hr))
{
MSPDeleteMediaType(pMediaType);
LOG((MSP_ERROR, "%s SetFormatMapping. %x", __fxName, hr));
return hr;
}
else
{
LOG((MSP_INFO, "%s Configured payload:%d", __fxName, dwPayloadType));
}
if (dw2 == 0 && !fFormatSet)
{
// tell the encoder to use this format.
// TODO, cache all the allowed mediatypes in the conference for
// future enumerations. It would be nice that we can get the SDP blob
// when the call object is created.
hr = pIStreamConfig->SetFormat(dwPayloadType, pMediaType);
if (FAILED(hr))
{
MSPDeleteMediaType(pMediaType);
LOG((MSP_ERROR, "%s SetFormat. %x", __fxName, hr));
return hr;
}
fFormatSet = TRUE;
}
}
}
MSPDeleteMediaType(pMediaType);
}
return S_OK;
}
HRESULT CStreamVideoSend::CreateSendFilters(
IN IPin *pCapturePin,
IN IPin *pRTPPin,
IN BOOL fDirectRTP
)
/*++
Routine Description:
Insert filters into the graph and connect to the capture pin.
Capturepin->[Encoder]->RTPRender
Arguments:
pCapturePin - the capture pin on the capture filter.
pRTPPin - the RTP packetization pin.
fDirectRTP - the capture pin supports RTP directly.
Return Value:
HRESULT.
--*/
{
ENTER_FUNCTION("CStreamVideoSend::CreateSendFilters");
LOG((MSP_TRACE, "%s enters", __fxName));
HRESULT hr;
// Create the RTP render filter and add it into the graph.
CComPtr<IBaseFilter> pRenderFilter;
if (m_pIRTPSession == NULL)
{
if (FAILED(hr = ::AddFilter(
m_pIGraphBuilder,
__uuidof(MSRTPRenderFilter),
L"RtpRender",
&pRenderFilter)))
{
LOG((MSP_ERROR, "%s, adding render filter. hr=%x", __fxName, hr));
return hr;
}
if (FAILED(hr = ConfigureRTPFilter(pRenderFilter)))
{
LOG((MSP_ERROR, "%s, configure RTP render filter failed. %x", __fxName, hr));
return hr;
}
}
else
{
if (FAILED (hr = m_pIRTPSession->QueryInterface (&pRenderFilter)))
{
LOG ((MSP_ERROR, "%s failed to get filter from rtp session. %x", __fxName, hr));
return hr;
}
if (FAILED (hr = m_pIGraphBuilder->AddFilter ((IBaseFilter *)pRenderFilter, L"RtpRender")))
{
LOG ((MSP_ERROR, "%s failed to add filter to graph. %x", __fxName, hr));
return hr;
}
}
if (!fDirectRTP)
{
CComPtr<IStreamConfig> pIStreamConfig;
hr = pCapturePin->QueryInterface(&pIStreamConfig);
if (FAILED(hr))
{
LOG((MSP_ERROR, "%s, query IStreamConfig. %x", __fxName, hr));
return hr;
}
// configure the format info on the RTP filter
if (FAILED(hr = ConfigureRTPFormats(pRenderFilter, pIStreamConfig)))
{
LOG((MSP_ERROR, "%s, configure RTP formats. %x", __fxName, hr));
return hr;
}
}
else
{
// configure RTP_SINGLE_STREAM to on the RTP filter.
}
if (FAILED(hr = ConnectRTPFilter(
m_pIGraphBuilder,
pCapturePin,
pRTPPin,
pRenderFilter
)))
{
LOG((MSP_ERROR,
"%s, connect capture pin and the Render filter. %x", __fxName, hr));
return hr;
}
return S_OK;
}
//
// IInnerStreamQualityControl methods.
//
STDMETHODIMP CStreamVideoSend::GetRange(
IN InnerStreamQualityProperty property,
OUT LONG *plMin,
OUT LONG *plMax,
OUT LONG *plSteppingDelta,
OUT LONG *plDefault,
OUT TAPIControlFlags *plFlags
)
/*++
Routine Description:
Get the range for a quality control property. Delegated to capture filter
for now.
Arguments:
Return Value:
HRESULT.
--*/
{
ENTER_FUNCTION("CStreamVideoSend::GetRange (InnerStreamQualityControl)");
HRESULT hr;
static BOOL fReported = FALSE;
CLock lock(m_lock);
switch (property)
{
case InnerStreamQuality_MinFrameInterval:
if (m_pCaptureFrameRateControl == NULL)
{
if (!fReported)
{
LOG((MSP_WARN, "%s, m_pCaptureFrameRateControl is NULL", __fxName));
fReported = TRUE;
}
hr = E_NOTIMPL;
}
else
{
hr = m_pCaptureFrameRateControl->GetRange(
FrameRateControl_Maximum, plMin, plMax, plSteppingDelta, plDefault, plFlags
);
}
break;
case InnerStreamQuality_AvgFrameInterval:
if (m_pCaptureFrameRateControl == NULL)
{
if (!fReported)
{
LOG((MSP_WARN, "%s, m_pCaptureFrameRateControl is NULL", __fxName));
fReported = TRUE;
}
hr = E_NOTIMPL;
}
else
{
hr = m_pCaptureFrameRateControl->GetRange(
FrameRateControl_Current, plMin, plMax, plSteppingDelta, plDefault, plFlags
);
}
break;
case InnerStreamQuality_MaxBitrate:
if (m_pCaptureBitrateControl == NULL)
{
if (!fReported)
{
LOG((MSP_WARN, "%s, m_pCaptureBitrateControl is NULL", __fxName));
fReported = TRUE;
}
hr = E_NOTIMPL;
}
else
{
hr = m_pCaptureBitrateControl->GetRange(
BitrateControl_Maximum, plMin, plMax, plSteppingDelta, plDefault, plFlags, LAYERID
);
if (S_OK == hr)
{
if (*plMax < QCLIMIT_MIN_BITRATE)
{
LOG ((MSP_WARN, "%s: max bitrate %d too low", __fxName, *plMax));
hr = E_UNEXPECTED;
}
else
{
// adjust the min and default value
if (*plMin < QCLIMIT_MIN_BITRATE)
*plMin = QCLIMIT_MIN_BITRATE;
if (*plDefault < QCLIMIT_MIN_BITRATE)
*plDefault = QCLIMIT_MIN_BITRATE;
}
}
}
break;
case InnerStreamQuality_CurrBitrate:
if (m_pCaptureBitrateControl == NULL)
{
if (!fReported)
{
LOG((MSP_WARN, "%s, m_pCaptureBitrateControl is NULL", __fxName));
fReported = TRUE;
}
hr = E_NOTIMPL;
}
else
{
hr = m_pCaptureBitrateControl->GetRange(
BitrateControl_Current, plMin, plMax, plSteppingDelta, plDefault, plFlags, LAYERID
);
if (S_OK == hr)
{
if (*plMax < QCLIMIT_MIN_BITRATE)
{
LOG ((MSP_WARN, "%s: max bitrate %d too low", __fxName, *plMax));
hr = E_UNEXPECTED;
}
else
{
// adjust the min and default value
if (*plMin < QCLIMIT_MIN_BITRATE)
*plMin = QCLIMIT_MIN_BITRATE;
if (*plDefault < QCLIMIT_MIN_BITRATE)
*plDefault = QCLIMIT_MIN_BITRATE;
}
}
}
break;
default:
hr = CIPConfMSPStream::GetRange (property, plMin, plMax, plSteppingDelta, plDefault, plFlags);
break;
}
return hr;
}
STDMETHODIMP CStreamVideoSend::Get(
IN InnerStreamQualityProperty property,
OUT LONG *plValue,
OUT TAPIControlFlags *plFlags
)
/*++
Routine Description:
Get the value for a quality control property. Delegated to the quality
controller.
Arguments:
Return Value:
HRESULT.
--*/
{
ENTER_FUNCTION("CStreamVideoSend::Get(QualityControl)");
HRESULT hr;
static BOOL fReported = FALSE;
CLock lock(m_lock);
switch (property)
{
case InnerStreamQuality_MinFrameInterval:
if (m_pCaptureFrameRateControl == NULL)
{
if (!fReported)
{
LOG((MSP_WARN, "%s, m_pCaptureFrameRateControl is NULL", __fxName));
fReported = TRUE;
}
hr = E_NOTIMPL;
}
else
{
hr = m_pCaptureFrameRateControl->Get(FrameRateControl_Maximum, plValue, plFlags);
}
break;
case InnerStreamQuality_AvgFrameInterval:
if (m_pCaptureFrameRateControl == NULL)
{
if (!fReported)
{
LOG((MSP_WARN, "%s, m_pCaptureFrameRateControl is NULL", __fxName));
fReported = TRUE;
}
hr = E_NOTIMPL;
}
else
{
hr = m_pCaptureFrameRateControl->Get(FrameRateControl_Current, plValue, plFlags);
}
break;
case InnerStreamQuality_MaxBitrate:
if( m_pCaptureBitrateControl == NULL )
{
if (!fReported)
{
LOG((MSP_WARN, "%s, m_pICaptureBitrateControl is NULL", __fxName));
fReported = TRUE;
}
hr = E_NOTIMPL;
}
else
{
hr = m_pCaptureBitrateControl->Get(BitrateControl_Maximum, plValue, plFlags, LAYERID);
}
break;
case InnerStreamQuality_CurrBitrate:
if (m_pCaptureBitrateControl == NULL)
{
if (!fReported)
{
LOG((MSP_WARN, "%s, m_pCaptureBitrateControl is NULL", __fxName));
fReported = TRUE;
}
hr = E_NOTIMPL;
}
else
{
hr = m_pCaptureBitrateControl->Get(BitrateControl_Current, plValue, plFlags, LAYERID);
}
break;
default:
hr = CIPConfMSPStream::Get (property, plValue, plFlags);
break;
}
return hr;
}
STDMETHODIMP CStreamVideoSend::Set(
IN InnerStreamQualityProperty property,
IN LONG lValue,
IN TAPIControlFlags lFlags
)
/*++
Routine Description:
Set the value for a quality control property. Delegated to the quality
controller.
Arguments:
Return Value:
HRESULT.
--*/
{
ENTER_FUNCTION("CStreamVideoSend::Set(InnerStreamQualityControl)");
CLock lock(m_lock);
HRESULT hr;
LONG l;
static BOOL fReported = FALSE;
LONG min, max, delta, Default;
TAPIControlFlags flags;
switch (property)
{
// adjusted frame rate by call qc
case InnerStreamQuality_AdjMinFrameInterval:
if (m_pCaptureFrameRateControl == NULL &&
m_pPreviewFrameRateControl == NULL)
{
if (!fReported)
{
LOG((MSP_WARN, "%s, Capture/Preview FrameRateControl is NULL", __fxName));
fReported = TRUE;
}
hr = E_NOTIMPL;
break;
}
// set capture frame rate control
if (m_pCaptureFrameRateControl)
{
// get valid range
if (FAILED (hr = m_pCaptureFrameRateControl->GetRange (
FrameRateControl_Current,
&min, &max, &delta, &Default, &flags)))
{
LOG ((MSP_ERROR, "%s failed to getrange on capture frame rate control. %x", __fxName, hr));
}
else
{
// adjust value
l = lValue;
// use current value - max - if input value not set
if (l==QCDEFAULT_QUALITY_UNSET) l = max;
else if (l<min) l = min;
else if (l>max) l = max;
// remember the value
m_pStreamQCRelay->Set (property, l, lFlags);
if (FAILED (hr = m_pCaptureFrameRateControl->Set(FrameRateControl_Maximum, l, lFlags)))
{
LOG ((MSP_ERROR, "%s failed to set on capture frame rate control. value %d, hr %x", __fxName, l, hr));
}
}
}
// set Preview frame rate control
if (m_pPreviewFrameRateControl)
{
// get valid range
if (FAILED (hr = m_pPreviewFrameRateControl->GetRange (
FrameRateControl_Current,
&min, &max, &delta, &Default, &flags)))
{
LOG ((MSP_ERROR, "%s failed to getrange on Preview frame rate control. %x", __fxName, hr));
}
else
{
// adjust value
l = lValue;
// use current value - max - if input value not set
if (l==QCDEFAULT_QUALITY_UNSET) l = max;
else if (l<min) l = min;
else if (l>max) l = max;
// remember the value
m_pStreamQCRelay->Set (property, l, lFlags);
if (FAILED (hr = m_pPreviewFrameRateControl->Set(FrameRateControl_Maximum, l, lFlags)))
{
LOG ((MSP_ERROR, "%s failed to set on Preview frame rate control. value %d, hr %x", __fxName, l, hr));
}
}
}
break;
// adjusted bitrate by call qc
case InnerStreamQuality_AdjMaxBitrate:
if (m_pCaptureBitrateControl == NULL)
{
if (!fReported)
{
LOG((MSP_WARN, "%s, Capture BitrateControl is NULL", __fxName));
fReported = TRUE;
}
hr = E_NOTIMPL;
break;
}
// set capture bitrate control
if (m_pCaptureBitrateControl)
{
// get valid range
if (FAILED (hr = m_pCaptureBitrateControl->GetRange (
BitrateControl_Current,
&min, &max, &delta, &Default, &flags, LAYERID)))
{
LOG ((MSP_ERROR, "%s failed to getrange on capture bitrate control. %x", __fxName, hr));
}
else
{
// adjust value
l = lValue;
if (!m_pStreamQCRelay->m_fQOSAllowedToSend)
if (l > QCLIMIT_MAX_QOSNOTALLOWEDTOSEND)
l = QCLIMIT_MAX_QOSNOTALLOWEDTOSEND;
// use current value - max - if input value not set
if (l==QCDEFAULT_QUALITY_UNSET) l = max;
else if (l<min) l = min;
else if (l>max) l = max;
// remember the value
m_pStreamQCRelay->Set (property, l, lFlags);
if (FAILED (hr = m_pCaptureBitrateControl->Set(BitrateControl_Maximum, l, lFlags, LAYERID)))
{
LOG ((MSP_ERROR, "%s failed to set on capture bit rate control. value %d, hr %x", __fxName, l, hr));
}
}
}
break;
case InnerStreamQuality_PrefMaxBitrate:
// check input value
if (m_pCaptureBitrateControl)
{
// get valid range
if (FAILED (hr = m_pCaptureBitrateControl->GetRange (
BitrateControl_Current,
&min, &max, &delta, &Default, &flags, LAYERID)))
{
LOG ((MSP_ERROR, "%s failed to getrange on capture bitrate control. %x", __fxName, hr));
}
else
{
if (lValue < min || lValue > max)
return E_INVALIDARG;
}
}
else
{
LOG((MSP_WARN, "%s no bitratecontrol to check bitrate input.", __fxName));
}
hr = CIPConfMSPStream::Set (property, lValue, lFlags);
break;
case InnerStreamQuality_PrefMinFrameInterval:
// check input value
if (m_pCaptureFrameRateControl)
{
// get valid range
if (FAILED (hr = m_pCaptureFrameRateControl->GetRange (
FrameRateControl_Current,
&min, &max, &delta, &Default, &flags)))
{
LOG ((MSP_ERROR, "%s failed to getrange on capture frame rate control. %x", __fxName, hr));
}
else
{
if (lValue < min || lValue > max)
return E_INVALIDARG;
}
}
else
{
LOG((MSP_WARN, "%s no framerate cntl to check input.", __fxName));
}
hr = CIPConfMSPStream::Set (property, lValue, lFlags);
break;
default:
hr = CIPConfMSPStream::Set (property, lValue, lFlags);
break;
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
//
// CSubStreamVideoRecv
//
/////////////////////////////////////////////////////////////////////////////
CSubStreamVideoRecv::CSubStreamVideoRecv()
: m_pFTM(NULL),
m_pStream(NULL),
m_pCurrentParticipant(NULL)
{
}
// methods called by the videorecv object.
HRESULT CSubStreamVideoRecv::Init(
IN CStreamVideoRecv * pStream
)
/*++
Routine Description:
Initialize the substream object.
Arguments:
pStream - The pointer to the stream that owns this substream.
Return Value:
HRESULT.
--*/
{
LOG((MSP_TRACE,
"CSubStreamVideoRecv::Init, pStream %p", pStream));
// This method is called only once when the object is created. No other
// method will be called until this function succeeds. No need to lock.
_ASSERTE(m_pStream == NULL);
// initialize the terminal array so that the array is not NULL. Used for
// generating an empty enumerator if no terminal is selected.
if (!m_Terminals.Grow())
{
LOG((MSP_ERROR, "CSubStreamVideoRecv::Init - exit E_OUTOFMEMORY"));
return E_OUTOFMEMORY;
}
// create the marshaler.
HRESULT hr;
hr = CoCreateFreeThreadedMarshaler(GetControllingUnknown(), &m_pFTM);
if (FAILED(hr))
{
LOG((MSP_ERROR, "create marshaler failed, %x", hr));
return hr;
}
// save the stream reference.
m_pStream = pStream;
(pStream->GetControllingUnknown())->AddRef();
LOG((MSP_TRACE, "CSubStreamVideoRecv::Init returns S_OK"));
return S_OK;
}
#ifdef DEBUG_REFCOUNT
ULONG CSubStreamVideoRecv::InternalAddRef()
{
ULONG lRef = CComObjectRootEx<CComMultiThreadModelNoCS>::InternalAddRef();
LOG((MSP_TRACE, "SubStreamVideoRecv %p Addref, ref = %d", this, lRef));
return lRef;
}
ULONG CSubStreamVideoRecv::InternalRelease()
{
ULONG lRef = CComObjectRootEx<CComMultiThreadModelNoCS>::InternalRelease();
LOG((MSP_TRACE, "SubStreamVideoRecv %p Release, ref = %d", this, lRef));
return lRef;
}
#endif
void CSubStreamVideoRecv::FinalRelease()
/*++
Routine Description:
release everything before being deleted.
Arguments:
Return Value:
--*/
{
LOG((MSP_TRACE, "CSubStreamVideoRecv::FinalRelease - enter"));
if (m_pCurrentParticipant)
{
m_pCurrentParticipant->Release();
}
for ( int i = 0; i < m_Terminals.GetSize(); i ++ )
{
m_Terminals[i]->Release();
}
m_Terminals.RemoveAll();
if (m_pStream)
{
(m_pStream->GetControllingUnknown())->Release();
}
if (m_pFTM)
{
m_pFTM->Release();
}
LOG((MSP_TRACE, "CSubStreamVideoRecv::FinalRelease - exit"));
}
STDMETHODIMP CSubStreamVideoRecv::SelectTerminal(
IN ITTerminal * pTerminal
)
/*++
Routine Description:
Select a terminal on this substream. This method calls the same method
on the stream object to handle that.
Arguments:
pTerminal - the terminal to be selected.
Return Value:
--*/
{
LOG((MSP_TRACE,
"CSubStreamVideoRecv::SelectTerminal, pTerminal %p", pTerminal));
HRESULT hr;
m_lock.Lock();
if (m_Terminals.GetSize() > 0)
{
m_lock.Unlock();
return TAPI_E_MAXTERMINALS;
}
BOOL bFlag = m_Terminals.Add(pTerminal);
_ASSERTE(bFlag);
m_lock.Unlock();
if (!bFlag)
{
return E_OUTOFMEMORY;
}
// This is the refcount for the pointer in m_Terminals.
pTerminal->AddRef();
// Call the stream's select terminal to handle the state changes and also
// make sure that locking happens only from the stream to substream.
hr = m_pStream->SubStreamSelectTerminal(this, pTerminal);
if (FAILED(hr))
{
LOG((MSP_ERROR,
"CSubStreamVideoRecv::SelectTerminal failed, hr:%x", hr));
m_lock.Lock();
m_Terminals.Remove(pTerminal);
pTerminal->Release();
m_lock.Unlock();
}
return hr;
}
STDMETHODIMP CSubStreamVideoRecv::UnselectTerminal(
IN ITTerminal * pTerminal
)
/*++
Routine Description:
Unselect a terminal on this substream. This method calls the same method
on the stream object to handle that.
Arguments:
pTerminal - the terminal to be unselected.
Return Value:
--*/
{
LOG((MSP_TRACE,
"CSubStreamVideoRecv::UnSelectTerminal, pTerminal %p", pTerminal));
m_lock.Lock();
if (!m_Terminals.Remove(pTerminal))
{
m_lock.Unlock();
LOG((MSP_ERROR, "SubStreamVideoRecv::UnselectTerminal, invalid terminal."));
return TAPI_E_INVALIDTERMINAL;
}
pTerminal->Release();
m_lock.Unlock();
HRESULT hr;
// Call the stream's unselect terminal to handle the state changes and also
// make sure that locking happens only from the stream to substream.
hr = m_pStream->UnselectTerminal(pTerminal);
if (FAILED(hr))
{
LOG((MSP_ERROR,
"CSubStreamVideoRecv::UnSelectTerminal failed, hr:%x", hr));
}
return hr;
}
STDMETHODIMP CSubStreamVideoRecv::EnumerateTerminals(
OUT IEnumTerminal ** ppEnumTerminal
)
{
LOG((MSP_TRACE,
"EnumerateTerminals entered. ppEnumTerminal:%x", ppEnumTerminal));
if (IsBadWritePtr(ppEnumTerminal, sizeof(VOID *)))
{
LOG((MSP_ERROR, "ppEnumTerminal is a bad pointer"));
return E_POINTER;
}
// acquire the lock before accessing the Terminal object list.
CLock lock(m_lock);
if (m_Terminals.GetData() == NULL)
{
LOG((MSP_ERROR, "CSubStreamVideoRecv::EnumerateTerminals - "
"stream appears to have been shut down - exit E_UNEXPECTED"));
return E_UNEXPECTED;
}
typedef _CopyInterface<ITTerminal> CCopy;
typedef CSafeComEnum<IEnumTerminal, &__uuidof(IEnumTerminal),
ITTerminal *, CCopy> CEnumerator;
HRESULT hr;
CMSPComObject<CEnumerator> *pEnum = NULL;
hr = ::CreateCComObjectInstance(&pEnum);
if (pEnum == NULL)
{
LOG((MSP_ERROR, "Could not create enumerator object, %x", hr));
return hr;
}
// query for the __uuidof(IEnumTerminal) i/f
IEnumTerminal * pEnumTerminal;
hr = pEnum->_InternalQueryInterface(__uuidof(IEnumTerminal), (void**)&pEnumTerminal);
if (FAILED(hr))
{
LOG((MSP_ERROR, "query enum interface failed, %x", hr));
delete pEnum;
return hr;
}
// The CSafeComEnum can handle zero-sized array.
hr = pEnum->Init(
m_Terminals.GetData(), // the begin itor
m_Terminals.GetData() + m_Terminals.GetSize(), // the end itor,
NULL, // IUnknown
AtlFlagCopy // copy the data.
);
if (FAILED(hr))
{
LOG((MSP_ERROR, "init enumerator object failed, %x", hr));
pEnumTerminal->Release();
return hr;
}
LOG((MSP_TRACE, "CSubStreamVideoRecv::EnumerateTerminals - exit S_OK"));
*ppEnumTerminal = pEnumTerminal;
return hr;
}
STDMETHODIMP CSubStreamVideoRecv::get_Terminals(
OUT VARIANT * pVariant
)
{
LOG((MSP_TRACE, "CSubStreamVideoRecv::get_Terminals - enter"));
//
// Check parameters.
//
if ( IsBadWritePtr(pVariant, sizeof(VARIANT) ) )
{
LOG((MSP_ERROR, "CSubStreamVideoRecv::get_Terminals - "
"bad pointer argument - exit E_POINTER"));
return E_POINTER;
}
//
// See if this stream has been shut down. Acquire the lock before accessing
// the terminal object list.
//
CLock lock(m_lock);
if (m_Terminals.GetData() == NULL)
{
LOG((MSP_ERROR, "CSubStreamVideoRecv::get_Terminals - "
"stream appears to have been shut down - exit E_UNEXPECTED"));
return E_UNEXPECTED;
}
//
// create the collection object - see mspcoll.h
//
HRESULT hr;
typedef CTapiIfCollection< ITTerminal * > TerminalCollection;
CComObject<TerminalCollection> * pCollection;
hr = ::CreateCComObjectInstance(&pCollection);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, "CSubStreamVideoRecv::get_Terminals - "
"can't create collection - exit 0x%08x", hr));
return hr;
}
//
// get the Collection's IDispatch interface
//
IDispatch * pDispatch;
hr = pCollection->_InternalQueryInterface(__uuidof(IDispatch),
(void **) &pDispatch );
if ( FAILED(hr) )
{
LOG((MSP_ERROR, "CSubStreamVideoRecv::get_Terminals - "
"QI for IDispatch on collection failed - exit 0x%08x", hr));
delete pCollection;
return hr;
}
//
// Init the collection using an iterator -- pointers to the beginning and
// the ending element plus one.
//
hr = pCollection->Initialize( m_Terminals.GetSize(),
m_Terminals.GetData(),
m_Terminals.GetData() + m_Terminals.GetSize() );
if (FAILED(hr))
{
LOG((MSP_ERROR, "CSubStreamVideoRecv::get_Terminals - "
"Initialize on collection failed - exit 0x%08x", hr));
pDispatch->Release();
return hr;
}
//
// put the IDispatch interface pointer into the variant
//
LOG((MSP_ERROR, "CSubStreamVideoRecv::get_Terminals - "
"placing IDispatch value %08x in variant", pDispatch));
VariantInit(pVariant);
pVariant->vt = VT_DISPATCH;
pVariant->pdispVal = pDispatch;
LOG((MSP_TRACE, "CSubStreamVideoRecv::get_Terminals - exit S_OK"));
return S_OK;
}
STDMETHODIMP CSubStreamVideoRecv::get_Stream (
OUT ITStream ** ppITStream
)
{
LOG((MSP_TRACE,
"VideoRecvSubStream.get_Stream, ppITStream %x", ppITStream));
if (IsBadWritePtr(ppITStream, sizeof (VOID *)))
{
LOG((MSP_ERROR, "Bad pointer, ppITStream:%x",ppITStream));
return E_POINTER;
}
ITStream * pITStream;
HRESULT hr = m_pStream->_InternalQueryInterface(
__uuidof(ITStream),
(void **)&pITStream
);
if (FAILED(hr))
{
LOG((MSP_ERROR, "get_Stream:QueryInterface failed: %x", hr));
return hr;
}
*ppITStream = pITStream;
return S_OK;
}
STDMETHODIMP CSubStreamVideoRecv::StartSubStream()
{
return TAPI_E_NOTSUPPORTED;
}
STDMETHODIMP CSubStreamVideoRecv::PauseSubStream()
{
return TAPI_E_NOTSUPPORTED;
}
STDMETHODIMP CSubStreamVideoRecv::StopSubStream()
{
return TAPI_E_NOTSUPPORTED;
}
BOOL CSubStreamVideoRecv::GetCurrentParticipant(
DWORD * pdwSSRC,
ITParticipant** ppITParticipant
)
{
CLock lock(m_lock);
if (m_pCurrentParticipant)
{
m_pCurrentParticipant->AddRef();
*ppITParticipant = m_pCurrentParticipant;
((CParticipant *)m_pCurrentParticipant)->GetSSRC(
(ITStream*)m_pStream,
pdwSSRC
);
return TRUE;
}
return FALSE;
}
VOID CSubStreamVideoRecv::SetCurrentParticipant(
DWORD dwSSRC,
ITParticipant * pParticipant
)
{
CLock lock(m_lock);
if (m_pCurrentParticipant)
{
m_pCurrentParticipant->Release();
}
m_pCurrentParticipant = pParticipant;
if (m_pCurrentParticipant)
{
m_pCurrentParticipant->AddRef();
}
}
BOOL CSubStreamVideoRecv::ClearCurrentTerminal()
{
CLock lock(m_lock);
if (m_Terminals.GetSize() > 0)
{
m_Terminals[0]->Release();
m_Terminals.RemoveAt(0);
return TRUE;
}
return FALSE;
}
BOOL CSubStreamVideoRecv::SetCurrentTerminal(ITTerminal * pTerminal)
{
CLock lock(m_lock);
if (m_Terminals.GetSize() > 0)
{
_ASSERTE(FALSE);
return FALSE;
}
BOOL bFlag = m_Terminals.Add(pTerminal);
// This should never fail since the terminal array has been grown
// at the init time.
_ASSERTE(bFlag);
if (bFlag)
{
pTerminal->AddRef();
return TRUE;
}
return FALSE;
}