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.
1191 lines
30 KiB
1191 lines
30 KiB
/*******************************************************************************
|
|
|
|
Module Name:
|
|
|
|
bgapp.cpp
|
|
|
|
Abstract:
|
|
|
|
Implements class CBridgeApp
|
|
|
|
Author:
|
|
|
|
Qianbo Huai (qhuai) Jan 27 2000
|
|
|
|
*******************************************************************************/
|
|
|
|
#include "stdafx.h"
|
|
#include <bridge.h>
|
|
|
|
extern LPSTR glpCmdLine;
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////////
|
|
hard coded SDP
|
|
////*/
|
|
const WCHAR * const MySDP = L"\
|
|
v=0\n\
|
|
o=qhuai 0 0 IN IP4 157.55.89.115\n\
|
|
s=BridgeTestConf\n\
|
|
c=IN IP4 239.9.20.26/15\n\
|
|
t=0 0\n\
|
|
m=video 20000 RTP/AVP 34 31\n\
|
|
m=audio 20040 RTP/AVP 0 4\n\
|
|
";
|
|
|
|
const WCHAR * const MySDP2 = L"\
|
|
v=0\n\
|
|
o=qhuai 0 0 IN IP4 157.55.89.115\n\
|
|
s=BridgeTestConf2\n\
|
|
c=IN IP4 239.9.20.26/15\n\
|
|
t=0 0\n\
|
|
m=video 20000 RTP/AVP 34 31\n\
|
|
m=audio 20040 RTP/AVP 3\n\
|
|
";
|
|
|
|
WCHAR *SelfAlias = L"Conference";
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////////
|
|
initiates tapi and listens at h323 address
|
|
////*/
|
|
CBridgeApp::CBridgeApp (HRESULT *phr)
|
|
{
|
|
ENTER_FUNCTION ("CBridgeApp::CBridgeApp");
|
|
LOG ((BG_TRACE, "%s entered", __fxName));
|
|
|
|
*phr = S_OK;
|
|
|
|
// init members
|
|
m_pTapi = NULL;
|
|
m_pH323Addr = NULL;
|
|
m_pSDPAddr = NULL;
|
|
m_pList = new CBridgeItemList ();
|
|
if (NULL == m_pList)
|
|
{
|
|
*phr = E_FAIL;
|
|
return;
|
|
}
|
|
|
|
// create tapi
|
|
*phr = CoCreateInstance (
|
|
CLSID_TAPI,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_ITTAPI,
|
|
(LPVOID *)&m_pTapi
|
|
);
|
|
if (FAILED(*phr))
|
|
return;
|
|
|
|
// tapi initiate
|
|
*phr = m_pTapi->Initialize ();
|
|
if (FAILED(*phr))
|
|
return;
|
|
|
|
// associate event with listener
|
|
CTAPIEventNotification *pEventNotif = NULL;
|
|
IConnectionPointContainer *pContainer = NULL;
|
|
IConnectionPoint *pPoint = NULL;
|
|
IH323LineEx *pIH323LineEx = NULL;
|
|
ULONG ulTapiEventAdvise;
|
|
long lCallNotif;
|
|
BSTR bstrAddrName = NULL;
|
|
|
|
// create event notification
|
|
pEventNotif = new CTAPIEventNotification;
|
|
if (!pEventNotif)
|
|
{
|
|
*phr = E_OUTOFMEMORY;
|
|
goto Error;
|
|
}
|
|
|
|
// get pointer container from tapi
|
|
*phr = m_pTapi->QueryInterface (
|
|
IID_IConnectionPointContainer,
|
|
(void **)&pContainer
|
|
);
|
|
if (FAILED(*phr))
|
|
goto Error;
|
|
|
|
// get connection point from container
|
|
*phr = pContainer->FindConnectionPoint (
|
|
IID_ITTAPIEventNotification,
|
|
&pPoint
|
|
);
|
|
if (FAILED(*phr))
|
|
goto Error;
|
|
|
|
// advise event notification on connection pointer
|
|
*phr = pPoint->Advise (
|
|
pEventNotif,
|
|
&ulTapiEventAdvise
|
|
);
|
|
if (FAILED(*phr))
|
|
goto Error;
|
|
|
|
// put event filter on tapi
|
|
*phr = m_pTapi->put_EventFilter (
|
|
TE_CALLNOTIFICATION |
|
|
TE_CALLSTATE |
|
|
TE_CALLMEDIA |
|
|
TE_PRIVATE
|
|
);
|
|
if (FAILED(*phr))
|
|
goto Error;
|
|
|
|
// find h323 address
|
|
bstrAddrName = SysAllocString (L"H323 Line");
|
|
*phr = FindAddress (
|
|
0,
|
|
bstrAddrName,
|
|
TAPIMEDIATYPE_AUDIO,
|
|
&m_pH323Addr
|
|
);
|
|
SysFreeString (bstrAddrName);
|
|
if (FAILED(*phr))
|
|
goto Error;
|
|
|
|
// check if it supports video
|
|
BOOL fSupportsVideo;
|
|
|
|
if (AddressSupportsMediaType (m_pH323Addr, TAPIMEDIATYPE_VIDEO))
|
|
m_lH323MediaType = TAPIMEDIATYPE_AUDIO | TAPIMEDIATYPE_VIDEO;
|
|
else
|
|
m_lH323MediaType = TAPIMEDIATYPE_AUDIO;
|
|
|
|
*phr = m_pH323Addr->QueryInterface(&pIH323LineEx);
|
|
if (SUCCEEDED(*phr))
|
|
{
|
|
*phr = pIH323LineEx->SetExternalT120Address(TRUE, INADDR_ANY, 1503);
|
|
|
|
H245_CAPABILITY Capabilities[] =
|
|
{HC_G711, HC_G723, HC_H263QCIF, HC_H261QCIF};
|
|
DWORD Weights[] = {200, 100, 100, 0};
|
|
|
|
*phr = pIH323LineEx->SetDefaultCapabilityPreferrence(
|
|
4, Capabilities, Weights
|
|
);
|
|
|
|
*phr = pIH323LineEx->SetAlias (SelfAlias, wcslen (SelfAlias));
|
|
}
|
|
|
|
// register call notification
|
|
*phr = m_pTapi->RegisterCallNotifications (
|
|
m_pH323Addr,
|
|
VARIANT_TRUE,
|
|
VARIANT_TRUE,
|
|
m_lH323MediaType,
|
|
ulTapiEventAdvise,
|
|
&lCallNotif
|
|
);
|
|
if (FAILED(*phr))
|
|
goto Error;
|
|
|
|
// find sdp address
|
|
*phr = FindAddress (
|
|
LINEADDRESSTYPE_SDP,
|
|
NULL,
|
|
TAPIMEDIATYPE_AUDIO,
|
|
&m_pSDPAddr
|
|
);
|
|
if (FAILED(*phr))
|
|
goto Error;
|
|
|
|
// check if it supports video
|
|
if (AddressSupportsMediaType (m_pSDPAddr, TAPIMEDIATYPE_VIDEO))
|
|
m_lSDPMediaType = TAPIMEDIATYPE_AUDIO | TAPIMEDIATYPE_VIDEO;
|
|
else
|
|
m_lSDPMediaType = TAPIMEDIATYPE_AUDIO;
|
|
|
|
Cleanup:
|
|
if (pEventNotif)
|
|
pEventNotif->Release ();
|
|
if (pPoint)
|
|
pPoint->Release ();
|
|
if (pContainer)
|
|
pContainer->Release ();
|
|
if (pIH323LineEx)
|
|
pIH323LineEx->Release ();
|
|
|
|
LOG ((BG_TRACE, "%s returns", __fxName));
|
|
return;
|
|
|
|
Error:
|
|
if (m_pH323Addr)
|
|
{
|
|
m_pH323Addr->Release ();
|
|
m_pH323Addr = NULL;
|
|
}
|
|
if (m_pSDPAddr)
|
|
{
|
|
m_pSDPAddr->Release ();
|
|
m_pSDPAddr = NULL;
|
|
}
|
|
if (m_pTapi)
|
|
{
|
|
m_pTapi->Release ();
|
|
m_pTapi = NULL;
|
|
}
|
|
if (m_pList)
|
|
{
|
|
delete m_pList;
|
|
}
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////////
|
|
////*/
|
|
CBridgeApp::~CBridgeApp ()
|
|
{
|
|
if (m_pList)
|
|
{
|
|
// all calls should already been disconnected
|
|
delete m_pList;
|
|
}
|
|
if (m_pSDPAddr)
|
|
{
|
|
m_pSDPAddr->Release ();
|
|
}
|
|
if (m_pH323Addr)
|
|
{
|
|
m_pH323Addr->Release ();
|
|
}
|
|
if (m_pTapi)
|
|
{
|
|
m_pTapi->Release ();
|
|
}
|
|
}
|
|
|
|
/*///////////////////////////////////////////////////////////////////////////////
|
|
////*/
|
|
HRESULT
|
|
CBridgeApp::CreateH323Call (IDispatch *pEvent)
|
|
{
|
|
ENTER_FUNCTION ("CBridgeApp::CreateH323Call");
|
|
LOG ((BG_TRACE, "%s entered", __fxName));
|
|
|
|
HRESULT hr;
|
|
BSTR bstrID = NULL;
|
|
BSTR bstrName = NULL;
|
|
BSTR CallerIDNumber = NULL;
|
|
|
|
ITCallNotificationEvent *pNotify = NULL;
|
|
ITCallInfo *pCallInfo = NULL;
|
|
ITBasicCallControl *pCallControl = NULL;
|
|
IUnknown *pIUnknown = NULL;
|
|
|
|
CBridgeItem *pItem = NULL;
|
|
|
|
// check privilege
|
|
CALL_PRIVILEGE privilege;
|
|
|
|
// get call event interface
|
|
hr = pEvent->QueryInterface (
|
|
IID_ITCallNotificationEvent,
|
|
(void **)&pNotify
|
|
);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// get call info
|
|
hr = pNotify->get_Call (&pCallInfo);
|
|
if (FAILED(hr))
|
|
goto Error;
|
|
|
|
// if we own the call
|
|
hr = pCallInfo->get_Privilege (&privilege);
|
|
if (FAILED(hr))
|
|
goto Error;
|
|
|
|
if (CP_OWNER != privilege)
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
goto Error;
|
|
}
|
|
|
|
// get call info string
|
|
hr = pCallInfo->get_CallInfoString(CIS_CALLERIDNAME, &bstrName);
|
|
if (FAILED (hr))
|
|
goto Error;
|
|
|
|
hr = pCallInfo->get_CallInfoString(CIS_CALLERIDNUMBER, &CallerIDNumber);
|
|
if (FAILED(hr))
|
|
goto Error;
|
|
|
|
// construct the caller id
|
|
bstrID = SysAllocStringLen(NULL,
|
|
SysStringLen(bstrName) + SysStringLen(CallerIDNumber) + 2);
|
|
|
|
wsprintfW(bstrID, L"%ws@%ws", bstrName, CallerIDNumber);
|
|
|
|
hr = pCallInfo->QueryInterface (
|
|
IID_ITBasicCallControl,
|
|
(void **)&pCallControl
|
|
);
|
|
if (FAILED(hr))
|
|
goto Error;
|
|
|
|
// check if there is an item with same id
|
|
if (FAILED (hr = pCallInfo->QueryInterface (IID_IUnknown, (void**)&pIUnknown)))
|
|
goto Error;
|
|
pItem = m_pList->FindByH323 (pIUnknown);
|
|
pIUnknown->Release ();
|
|
pIUnknown = NULL;
|
|
|
|
if (NULL != pItem)
|
|
{
|
|
// @@ we are already in a call from the same ID
|
|
// @@ should have some debug info and feedback?
|
|
hr = pCallControl->Disconnect (DC_REJECTED);
|
|
// don't care the return value of diconnect
|
|
|
|
hr = E_ABORT;
|
|
goto Error;
|
|
}
|
|
|
|
// everything is right, store the call
|
|
pItem = new CBridgeItem;
|
|
if (NULL == pItem)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Error;
|
|
}
|
|
|
|
pItem->bstrID = bstrID;
|
|
pItem->bstrName = bstrName;
|
|
pItem->pCallH323 = pCallControl;
|
|
|
|
m_pList->Append (pItem);
|
|
|
|
Cleanup:
|
|
if (pNotify) pNotify->Release ();
|
|
if (pCallInfo) pCallInfo->Release();
|
|
if (CallerIDNumber) SysFreeString(CallerIDNumber);
|
|
|
|
LOG ((BG_TRACE, "%s returns, %x", __fxName, hr));
|
|
return hr;
|
|
|
|
Error:
|
|
if (bstrID) SysFreeString (bstrID);
|
|
if (bstrName) SysFreeString (bstrName);
|
|
if (pCallControl) pCallControl->Release ();
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
/*///////////////////////////////////////////////////////////////////////////////
|
|
////*/
|
|
HRESULT
|
|
CBridgeApp::CreateSDPCall (CBridgeItem *pItem)
|
|
{
|
|
ENTER_FUNCTION ("CBridgeApp::CreateSDPCall");
|
|
LOG ((BG_TRACE, "%s entered", __fxName));
|
|
|
|
HRESULT hr;
|
|
|
|
// create call, ignore bstrDestAddr, hardcode it here
|
|
ITBasicCallControl *pCall = NULL;
|
|
BSTR bstrFixedDest;
|
|
|
|
if (glpCmdLine[0] == '\0')
|
|
bstrFixedDest = SysAllocString (MySDP);
|
|
else
|
|
bstrFixedDest = SysAllocString (MySDP2);
|
|
|
|
hr = m_pSDPAddr->CreateCall (
|
|
bstrFixedDest, // bstrDestAddr,
|
|
LINEADDRESSTYPE_SDP,
|
|
m_lSDPMediaType,
|
|
&pCall
|
|
);
|
|
SysFreeString (bstrFixedDest);
|
|
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// store the call
|
|
pItem->pCallSDP = pCall;
|
|
|
|
LOG ((BG_TRACE, "%s returns", __fxName));
|
|
return hr;
|
|
}
|
|
|
|
/*///////////////////////////////////////////////////////////////////////////////
|
|
////*/
|
|
HRESULT
|
|
CBridgeApp::BridgeCalls (CBridgeItem *pItem)
|
|
{
|
|
ENTER_FUNCTION ("CBridgeApp::BridgeCalls");
|
|
LOG ((BG_TRACE, "%s entered", __fxName));
|
|
|
|
HRESULT hr;
|
|
|
|
hr = SetupParticipantInfo (pItem);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = SetMulticastMode (pItem);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (FAILED (hr = CreateBridgeTerminals (pItem)))
|
|
return hr;
|
|
|
|
if (FAILED (hr = GetStreams (pItem)))
|
|
return hr;
|
|
|
|
if (FAILED (hr = SelectBridgeTerminals (pItem)))
|
|
return hr;
|
|
|
|
// connect h323 call
|
|
if (FAILED (hr = pItem->pCallH323->Answer ()))
|
|
return hr;
|
|
|
|
// connect sdp call
|
|
if (FAILED (hr = pItem->pCallSDP->Connect (VARIANT_TRUE)))
|
|
{
|
|
pItem->pCallH323->Disconnect (DC_NORMAL);
|
|
return hr;
|
|
}
|
|
|
|
LOG ((BG_TRACE, "%s returns", __fxName));
|
|
return S_OK;
|
|
}
|
|
|
|
/*///////////////////////////////////////////////////////////////////////////////
|
|
////*/
|
|
HRESULT
|
|
CBridgeApp::DisconnectCall (CBridgeItem *pItem, DISCONNECT_CODE dc)
|
|
{
|
|
// disconnect
|
|
if (pItem->pCallH323)
|
|
pItem->pCallH323->Disconnect (dc);
|
|
if (pItem->pCallSDP)
|
|
pItem->pCallSDP->Disconnect (dc);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*///////////////////////////////////////////////////////////////////////////////
|
|
////*/
|
|
HRESULT
|
|
CBridgeApp::DisconnectAllCalls (DISCONNECT_CODE dc)
|
|
{
|
|
// i should have a better way to traverse each call
|
|
CBridgeItem ** pItemArray;
|
|
int num, i;
|
|
|
|
// out of memory
|
|
if (!m_pList->GetAllItems (&pItemArray, &num))
|
|
return E_OUTOFMEMORY;
|
|
|
|
// no calls
|
|
if (num == 0)
|
|
return S_OK;
|
|
|
|
for (i=0; i<num; i++)
|
|
{
|
|
// disconnect each call
|
|
if (pItemArray[i]->pCallH323)
|
|
pItemArray[i]->pCallH323->Disconnect (dc);
|
|
if (pItemArray[i]->pCallSDP)
|
|
pItemArray[i]->pCallSDP->Disconnect (dc);
|
|
// do not delete item
|
|
}
|
|
|
|
free (pItemArray);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////////
|
|
////*/
|
|
HRESULT
|
|
CBridgeApp::RemoveCall (CBridgeItem *pItem)
|
|
{
|
|
m_pList->TakeOut (pItem);
|
|
return S_OK;
|
|
}
|
|
|
|
/*///////////////////////////////////////////////////////////////////////////////
|
|
////*/
|
|
HRESULT
|
|
CBridgeApp::HasH323Call (IDispatch *pEvent, CBridgeItem **ppItem)
|
|
{
|
|
HRESULT hr;
|
|
|
|
ITCallStateEvent *pState = NULL;
|
|
ITCallInfo *pCallInfo = NULL;
|
|
|
|
IUnknown * pIUnknown = NULL;
|
|
|
|
// ignore null checking
|
|
if (*ppItem)
|
|
{
|
|
delete *ppItem;
|
|
*ppItem = NULL;
|
|
}
|
|
|
|
// get call state event
|
|
hr = pEvent->QueryInterface (
|
|
IID_ITCallStateEvent,
|
|
(void **)&pState
|
|
);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// check privilege
|
|
CALL_PRIVILEGE privilege;
|
|
|
|
// get call event interface
|
|
hr = pState->get_Call (&pCallInfo);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// if we own the call
|
|
hr = pCallInfo->get_Privilege (&privilege);
|
|
if (FAILED(hr))
|
|
goto Error;
|
|
|
|
if (CP_OWNER != privilege)
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
goto Error;
|
|
}
|
|
|
|
// get IUnknown
|
|
if (FAILED (hr = pCallInfo->QueryInterface (IID_IUnknown, (void **)&pIUnknown)))
|
|
goto Error;
|
|
*ppItem = m_pList->FindByH323 (pIUnknown);
|
|
|
|
Cleanup:
|
|
if (pCallInfo) pCallInfo->Release ();
|
|
if (pIUnknown) pIUnknown->Release ();
|
|
if (pState) pState->Release ();
|
|
|
|
return hr;
|
|
|
|
Error:
|
|
goto Cleanup;
|
|
}
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////////
|
|
////*/
|
|
HRESULT
|
|
CBridgeApp::HasCalls ()
|
|
{
|
|
if (m_pList->IsEmpty ())
|
|
return S_FALSE;
|
|
else
|
|
return S_OK;
|
|
}
|
|
|
|
/*///////////////////////////////////////////////////////////////////////////////
|
|
////*/
|
|
HRESULT
|
|
CBridgeApp::CreateBridgeTerminals (CBridgeItem *pItem)
|
|
{
|
|
HRESULT hr;
|
|
IConfBridge *pConfBridge = NULL;
|
|
|
|
// create CConfBridge
|
|
hr = CoCreateInstance (
|
|
__uuidof(ConfBridge),
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IConfBridge,
|
|
(LPVOID *)&pConfBridge
|
|
);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// create terminal: video H323->SDP
|
|
hr = pConfBridge->CreateBridgeTerminal (
|
|
TAPIMEDIATYPE_VIDEO,
|
|
&(pItem->pTermHSVid)
|
|
);
|
|
if (FAILED(hr))
|
|
goto Error;
|
|
|
|
// create terminal: audio H323->SDP
|
|
hr = pConfBridge->CreateBridgeTerminal (
|
|
TAPIMEDIATYPE_AUDIO,
|
|
&(pItem->pTermHSAud)
|
|
);
|
|
if (FAILED(hr))
|
|
goto Error;
|
|
|
|
// create terminal: video SDP->H323
|
|
hr = pConfBridge->CreateBridgeTerminal (
|
|
TAPIMEDIATYPE_VIDEO,
|
|
&(pItem->pTermSHVid)
|
|
);
|
|
if (FAILED(hr))
|
|
goto Error;
|
|
|
|
// create terminal: audio SDP->H323
|
|
hr = pConfBridge->CreateBridgeTerminal (
|
|
TAPIMEDIATYPE_AUDIO,
|
|
&(pItem->pTermSHAud)
|
|
);
|
|
if (FAILED(hr))
|
|
goto Error;
|
|
|
|
Cleanup:
|
|
pConfBridge->Release ();
|
|
pConfBridge = NULL;
|
|
|
|
return hr;
|
|
|
|
Error:
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
/*///////////////////////////////////////////////////////////////////////////////
|
|
////*/
|
|
HRESULT
|
|
CBridgeApp::GetStreams (CBridgeItem *pItem)
|
|
{
|
|
ITStreamControl *pStreamControl = NULL;
|
|
IEnumStream *pEnumStreams = NULL;
|
|
ITStream *pStream = NULL;
|
|
|
|
// get stream control on H323
|
|
HRESULT hr = pItem->pCallH323->QueryInterface (
|
|
IID_ITStreamControl,
|
|
(void **)&pStreamControl
|
|
);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// get enum stream on H323
|
|
hr = pStreamControl->EnumerateStreams (&pEnumStreams);
|
|
pStreamControl->Release ();
|
|
pStreamControl = NULL;
|
|
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// iterate each stream on H323
|
|
while (S_OK == pEnumStreams->Next (1, &pStream, NULL))
|
|
{
|
|
if (IsStream (pStream, TAPIMEDIATYPE_VIDEO, TD_CAPTURE))
|
|
pItem->pStreamHVidCap = pStream;
|
|
|
|
else if (IsStream (pStream, TAPIMEDIATYPE_VIDEO, TD_RENDER))
|
|
{
|
|
pItem->pStreamHVidRen = pStream;
|
|
|
|
IKeyFrameControl* pIKeyFrameControl = NULL;
|
|
hr = pStream->QueryInterface(&pIKeyFrameControl);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pIKeyFrameControl->PeriodicUpdatePicture(TRUE, 5);
|
|
pIKeyFrameControl->Release();
|
|
}
|
|
}
|
|
|
|
else if (IsStream (pStream, TAPIMEDIATYPE_AUDIO, TD_CAPTURE))
|
|
pItem->pStreamHAudCap = pStream;
|
|
|
|
else if (IsStream (pStream, TAPIMEDIATYPE_AUDIO, TD_RENDER))
|
|
pItem->pStreamHAudRen = pStream;
|
|
|
|
else
|
|
{
|
|
pEnumStreams->Release ();
|
|
// @@ IsStream doesn't return hresult
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
// don't release pStream, it's stored in pItem
|
|
pEnumStreams->Release ();
|
|
pEnumStreams = NULL;
|
|
|
|
//========================================
|
|
|
|
// get stream control on SDP
|
|
hr = pItem->pCallSDP->QueryInterface (
|
|
IID_ITStreamControl,
|
|
(void **)&pStreamControl
|
|
);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// get enum stream on SDP
|
|
hr = pStreamControl->EnumerateStreams (&pEnumStreams);
|
|
pStreamControl->Release ();
|
|
pStreamControl = NULL;
|
|
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// iterate each stream on SDP
|
|
while (S_OK == pEnumStreams->Next (1, &pStream, NULL))
|
|
{
|
|
if (IsStream (pStream, TAPIMEDIATYPE_VIDEO, TD_CAPTURE))
|
|
pItem->pStreamSVidCap = pStream;
|
|
|
|
else if (IsStream (pStream, TAPIMEDIATYPE_VIDEO, TD_RENDER))
|
|
pItem->pStreamSVidRen = pStream;
|
|
|
|
else if (IsStream (pStream, TAPIMEDIATYPE_AUDIO, TD_CAPTURE))
|
|
pItem->pStreamSAudCap = pStream;
|
|
|
|
else if (IsStream (pStream, TAPIMEDIATYPE_AUDIO, TD_RENDER))
|
|
pItem->pStreamSAudRen = pStream;
|
|
|
|
else
|
|
{
|
|
pEnumStreams->Release ();
|
|
// @@ IsStream doesn't return hresult
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
// don't release pStream, it's stored in pItem
|
|
pEnumStreams->Release ();
|
|
pEnumStreams = NULL;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*///////////////////////////////////////////////////////////////////////////////
|
|
////*/
|
|
HRESULT
|
|
CBridgeApp::SelectBridgeTerminals (CBridgeItem *pItem)
|
|
{
|
|
HRESULT hr;
|
|
|
|
// sdp->h323 audio pair
|
|
if (FAILED (hr = pItem->pStreamHAudCap->SelectTerminal (pItem->pTermSHAud)))
|
|
return hr;
|
|
if (FAILED (hr = pItem->pStreamSAudRen->SelectTerminal (pItem->pTermSHAud)))
|
|
return hr;
|
|
|
|
// h323->sdp audio pair
|
|
if (FAILED (hr = pItem->pStreamSAudCap->SelectTerminal (pItem->pTermHSAud)))
|
|
return hr;
|
|
if (FAILED (hr = pItem->pStreamHAudRen->SelectTerminal (pItem->pTermHSAud)))
|
|
return hr;
|
|
|
|
// sdp->h323 video pair
|
|
if (FAILED (hr = pItem->pStreamHVidCap->SelectTerminal (pItem->pTermSHVid)))
|
|
return hr;
|
|
if (FAILED (hr = pItem->pStreamSVidRen->SelectTerminal (pItem->pTermSHVid)))
|
|
return hr;
|
|
|
|
// h323->sdp video pair
|
|
if (FAILED (hr = pItem->pStreamSVidCap->SelectTerminal (pItem->pTermHSVid)))
|
|
return hr;
|
|
if (FAILED (hr = pItem->pStreamHVidRen->SelectTerminal (pItem->pTermHSVid)))
|
|
return hr;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/*///////////////////////////////////////////////////////////////////////////////
|
|
////*/
|
|
HRESULT
|
|
CBridgeApp::SetupParticipantInfo (CBridgeItem *pItem)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ITLocalParticipant *pLocalParticipant = NULL;
|
|
|
|
// set the CName on the SDP side.
|
|
hr = pItem->pCallSDP->QueryInterface(&pLocalParticipant);
|
|
if (FAILED(hr)) goto Cleanup;
|
|
|
|
hr = pLocalParticipant->put_LocalParticipantTypedInfo(
|
|
PTI_CANONICALNAME, pItem->bstrID
|
|
);
|
|
if (FAILED(hr)) goto Cleanup;
|
|
|
|
hr = pLocalParticipant->put_LocalParticipantTypedInfo(
|
|
PTI_NAME, pItem->bstrName
|
|
);
|
|
|
|
if (FAILED(hr)) goto Cleanup;
|
|
|
|
Cleanup:
|
|
if (pLocalParticipant) pLocalParticipant->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*///////////////////////////////////////////////////////////////////////////////
|
|
////*/
|
|
HRESULT
|
|
CBridgeApp::SetMulticastMode (CBridgeItem *pItem)
|
|
{
|
|
IMulticastControl * pIMulticastControl = NULL;
|
|
|
|
HRESULT hr = pItem->pCallSDP->QueryInterface(&pIMulticastControl);
|
|
if (FAILED(hr)) return hr;
|
|
|
|
hr = pIMulticastControl->put_LoopbackMode(MM_SELECTIVE_LOOPBACK);
|
|
|
|
pIMulticastControl->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*///////////////////////////////////////////////////////////////////////////////
|
|
////*/
|
|
HRESULT
|
|
CBridgeApp::FindAddress (long dwAddrType, BSTR bstrAddrName, long lMediaType, ITAddress **ppAddr)
|
|
{
|
|
HRESULT hr;
|
|
IEnumAddress *pEnumAddr = NULL;
|
|
ITAddress *pAddr = NULL;
|
|
ITAddressCapabilities *pAddrCaps = NULL;
|
|
|
|
BOOL fFound = false;
|
|
long lTypeFound;
|
|
BSTR bstrAddrNameFound = NULL;
|
|
|
|
// clear output address
|
|
if ((*ppAddr))
|
|
{
|
|
(*ppAddr)->Release ();
|
|
(*ppAddr) = NULL;
|
|
}
|
|
|
|
// enumerate the address
|
|
hr = m_pTapi->EnumerateAddresses (&pEnumAddr);
|
|
if (FAILED(hr))
|
|
{
|
|
// @@ should have some debug info here
|
|
goto Error;
|
|
}
|
|
// loop to find the right address
|
|
while (!fFound)
|
|
{
|
|
// next address
|
|
if (pAddr)
|
|
{
|
|
pAddr->Release ();
|
|
pAddr = NULL;
|
|
}
|
|
hr = pEnumAddr->Next (1, &pAddr, NULL);
|
|
if (S_OK != hr)
|
|
break;
|
|
|
|
if (dwAddrType != 0)
|
|
{
|
|
// addr type is valid, ignore addr name
|
|
if (pAddrCaps)
|
|
{
|
|
pAddrCaps->Release ();
|
|
pAddrCaps = NULL;
|
|
}
|
|
hr = pAddr->QueryInterface (
|
|
IID_ITAddressCapabilities,
|
|
(void **)&pAddrCaps
|
|
);
|
|
if (FAILED(hr))
|
|
{
|
|
// @@ debug info here
|
|
// DoMessage (L"Failed to retrieve address capabilities");
|
|
goto Error;
|
|
}
|
|
|
|
// find address type supported
|
|
hr = pAddrCaps->get_AddressCapability (AC_ADDRESSTYPES, &lTypeFound);
|
|
if (FAILED(hr))
|
|
{
|
|
// DoMessage (L"Failed to get address type");
|
|
goto Error;
|
|
}
|
|
|
|
// check if the type we wanted
|
|
if (dwAddrType != lTypeFound)
|
|
continue;
|
|
}
|
|
else if (bstrAddrName != NULL)
|
|
{
|
|
hr = pAddr->get_AddressName (&bstrAddrNameFound);
|
|
if (FAILED(hr))
|
|
{
|
|
// DoMessage (L"Failed to get address name");
|
|
goto Error;
|
|
}
|
|
if (wcscmp(bstrAddrName, bstrAddrNameFound) != 0)
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// DoMessage (L"Both address type and name are null. Internal error");
|
|
hr = E_UNEXPECTED;
|
|
goto Error;
|
|
}
|
|
|
|
// now check media type
|
|
if (AddressSupportsMediaType (pAddr, lMediaType))
|
|
fFound = true;
|
|
} // end of while (!fFound)
|
|
|
|
if (fFound)
|
|
{
|
|
(*ppAddr) = pAddr;
|
|
(*ppAddr)->AddRef ();
|
|
}
|
|
|
|
Cleanup:
|
|
if (pAddrCaps)
|
|
pAddrCaps->Release ();
|
|
if (pAddr)
|
|
pAddr->Release ();
|
|
if (pEnumAddr)
|
|
pEnumAddr->Release ();
|
|
return hr;
|
|
|
|
Error:
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
/*///////////////////////////////////////////////////////////////////////////////
|
|
////*/
|
|
BOOL
|
|
CBridgeApp::AddressSupportsMediaType (ITAddress *pAddr, long lMediaType)
|
|
{
|
|
VARIANT_BOOL vbSupport = VARIANT_FALSE;
|
|
ITMediaSupport * pMediaSupport;
|
|
|
|
if (SUCCEEDED(pAddr->QueryInterface (IID_ITMediaSupport, (void**)&pMediaSupport)))
|
|
{
|
|
pMediaSupport->QueryMediaType (lMediaType, &vbSupport);
|
|
pMediaSupport->Release ();
|
|
}
|
|
return (vbSupport==VARIANT_TRUE);
|
|
}
|
|
|
|
/*///////////////////////////////////////////////////////////////////////////////
|
|
////*/
|
|
BOOL
|
|
CBridgeApp::IsStream (ITStream *pStream, long lMediaType, TERMINAL_DIRECTION tdDirection)
|
|
{
|
|
long mediatype;
|
|
TERMINAL_DIRECTION direction;
|
|
|
|
if (FAILED (pStream->get_Direction(&direction)))
|
|
return false;
|
|
if (FAILED (pStream->get_MediaType(&mediatype)))
|
|
return false;
|
|
return ((direction == tdDirection) &&
|
|
(mediatype == lMediaType));
|
|
}
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////////
|
|
////*/
|
|
HRESULT
|
|
CBridgeApp::NextSubStream ()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CBridgeItem **ItemArray = NULL;
|
|
int num, i;
|
|
|
|
ITSubStreamControl *pSubControl = NULL;
|
|
IEnumSubStream *pEnumSub = NULL;
|
|
ULONG fetched;
|
|
ITSubStream *pSubStream = NULL;
|
|
BOOL fActive = FALSE; // if active stream found
|
|
ITSubStream *pSubInactive = NULL;
|
|
ITSubStream *pSubFirstInactive = NULL;
|
|
|
|
IEnumTerminal *pEnumTerminal = NULL;
|
|
ITParticipantSubStreamControl *pSwitcher = NULL;
|
|
|
|
// get all stored call items
|
|
if (FAILED (hr = m_pList->GetAllItems (&ItemArray, &num)))
|
|
return hr;
|
|
|
|
if (num == 0)
|
|
return S_OK;
|
|
|
|
// for each call item
|
|
for (i=0; i<num; i++)
|
|
{
|
|
// get substream control
|
|
if (NULL == ItemArray[i]->pStreamSVidRen)
|
|
continue;
|
|
if (FAILED (hr = ItemArray[i]->pStreamSVidRen->QueryInterface (&pSubControl)))
|
|
goto Error;
|
|
|
|
// get substreams on sdp video render
|
|
if (FAILED (hr = pSubControl->EnumerateSubStreams (&pEnumSub)))
|
|
goto Error;
|
|
|
|
pSubControl->Release ();
|
|
pSubControl = NULL;
|
|
|
|
// for each substream, if !(both active & inactive substream stored)
|
|
// the algo tries to be as fair as possible in switching.
|
|
// it switches the inactive substream just after the active one
|
|
// if the active one is the last in the enum, the first inactive one is chosen
|
|
while (!pSubInactive &&
|
|
(S_OK == (hr = pEnumSub->Next (1, &pSubStream, &fetched)))
|
|
)
|
|
{
|
|
// get terminal enumerator
|
|
if (FAILED (hr = pSubStream->EnumerateTerminals (&pEnumTerminal)))
|
|
goto Error;
|
|
|
|
// if the substream active, store the substream
|
|
if (S_OK == pEnumTerminal->Skip (1))
|
|
{
|
|
if (fActive)
|
|
;
|
|
// printf ("oops, another active substream on SDP video render stream");
|
|
else
|
|
fActive = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// if inactive, store the substream
|
|
if (!pSubFirstInactive)
|
|
{
|
|
// the first inactive substream
|
|
pSubFirstInactive = pSubStream;
|
|
pSubFirstInactive->AddRef ();
|
|
}
|
|
else
|
|
{
|
|
// store the inactive only if the active was found
|
|
if (fActive)
|
|
{
|
|
pSubInactive = pSubStream;
|
|
pSubInactive->AddRef ();
|
|
}
|
|
}
|
|
}
|
|
|
|
// release
|
|
pEnumTerminal->Release ();
|
|
pEnumTerminal = NULL;
|
|
|
|
pSubStream->Release ();
|
|
pSubStream = NULL;
|
|
}
|
|
|
|
pEnumSub->Release ();
|
|
pEnumSub = NULL;
|
|
|
|
// if only first inactive is found
|
|
if (pSubFirstInactive && !pSubInactive)
|
|
{
|
|
pSubInactive = pSubFirstInactive;
|
|
pSubFirstInactive = NULL;
|
|
}
|
|
|
|
// if not found two substreams, do nothing
|
|
if (pSubInactive && ItemArray[i]->pStreamSVidRen && ItemArray[i]->pTermSHVid)
|
|
{
|
|
if (FAILED (hr = ItemArray[i]->pStreamSVidRen->QueryInterface (&pSwitcher)))
|
|
goto Error;
|
|
|
|
// switch terminal on substream
|
|
if (FAILED (hr = pSwitcher->SwitchTerminalToSubStream
|
|
(ItemArray[i]->pTermSHVid, pSubInactive)))
|
|
goto Error;
|
|
|
|
pSwitcher->Release ();
|
|
pSwitcher = NULL;
|
|
}
|
|
|
|
if (pSubFirstInactive)
|
|
{
|
|
pSubFirstInactive->Release ();
|
|
pSubFirstInactive = NULL;
|
|
}
|
|
if (pSubInactive)
|
|
{
|
|
pSubInactive->Release ();
|
|
pSubInactive = NULL;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
if (ItemArray) free (ItemArray);
|
|
return hr;
|
|
|
|
Error:
|
|
if (pSubControl) pSubControl->Release ();
|
|
if (pEnumSub) pEnumSub->Release ();
|
|
|
|
if (pSubStream) pSubStream->Release ();
|
|
if (pSubInactive) pSubInactive->Release ();
|
|
if (pSubFirstInactive) pSubFirstInactive->Release ();
|
|
|
|
if (pEnumTerminal) pEnumTerminal->Release ();
|
|
if (pSwitcher) pSwitcher->Release ();
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////////
|
|
////*/
|
|
HRESULT
|
|
CBridgeApp::ShowParticipant (ITBasicCallControl *pSDPCall, ITParticipant *pParticipant)
|
|
{
|
|
ENTER_FUNCTION ("CBridgeApp::ShowParticipant");
|
|
|
|
HRESULT hr;
|
|
IUnknown *pIUnknown = NULL;
|
|
CBridgeItem *pItem = NULL;
|
|
ITParticipantSubStreamControl *pSwitcher = NULL;
|
|
ITSubStream *pSubStream = NULL;
|
|
|
|
// get IUnknown
|
|
if (FAILED (hr = pSDPCall->QueryInterface (IID_IUnknown, (void**)&pIUnknown)))
|
|
{
|
|
LOG ((BG_ERROR, "%s failed to query interface IUnknown, %x", __fxName, hr));
|
|
return hr;
|
|
}
|
|
|
|
// find the item matches pSDPCall
|
|
pItem = m_pList->FindBySDP (pIUnknown);
|
|
pIUnknown->Release ();
|
|
pIUnknown = NULL;
|
|
|
|
// oops, no match
|
|
if (NULL == pItem)
|
|
return S_FALSE;
|
|
|
|
// get participant substream control interface
|
|
if (NULL == pItem->pStreamSVidRen)
|
|
return S_OK;
|
|
if (FAILED (hr = pItem->pStreamSVidRen->QueryInterface (&pSwitcher)))
|
|
{
|
|
LOG ((BG_ERROR, "%s failed to query interface ITParticipantSubStreamControl, %x", __fxName, hr));
|
|
return hr;
|
|
}
|
|
|
|
// get substream from participant
|
|
if (FAILED (hr = pSwitcher->get_SubStreamFromParticipant (pParticipant, &pSubStream)))
|
|
{
|
|
pSwitcher->Release ();
|
|
pSwitcher = NULL;
|
|
LOG ((BG_WARN, "%s failed to get substream from participant, %x", __fxName, hr));
|
|
// stream from h323 side does not have substream, report false
|
|
return S_FALSE;
|
|
}
|
|
|
|
// switch
|
|
if (pItem->pTermSHVid)
|
|
hr = pSwitcher->SwitchTerminalToSubStream (pItem->pTermSHVid, pSubStream);
|
|
|
|
pSubStream->Release ();
|
|
pSubStream = NULL;
|
|
|
|
pSwitcher->Release ();
|
|
pSwitcher = NULL;
|
|
|
|
return hr;
|
|
}
|