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.
1640 lines
52 KiB
1640 lines
52 KiB
/*
|
|
* File: capsctl.cpp
|
|
*
|
|
* capability control object implementations
|
|
*
|
|
*
|
|
* Revision History:
|
|
*
|
|
* 10/10/96 mikeg created
|
|
* 06/24/97 mikev - Added T.120 capability to serialized caps and simcaps (interim hack until a
|
|
* T120 resolver is implemented)
|
|
* - Retired ResolveEncodeFormat(Audio,Video) and implemented a data-independent
|
|
* resolution algorithm and exposed method ResolveFormats(). Added support
|
|
* routines ResolvePermutations(), TestSimultaneousCaps() and
|
|
* AreSimcaps().
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
UINT g_AudioPacketDurationMs = AUDIO_PACKET_DURATION_LONG; // preferred packet duration
|
|
BOOL g_fRegAudioPacketDuration = FALSE; // AudioPacketDurationMs from registry
|
|
|
|
|
|
PCC_TERMCAPDESCRIPTORS CapsCtl::pAdvertisedSets=NULL;
|
|
DWORD CapsCtl::dwConSpeed = 0;
|
|
UINT CapsCtl::uStaticGlobalRefCount=0;
|
|
UINT CapsCtl::uAdvertizedSize=0;
|
|
extern HRESULT WINAPI CreateMediaCapability(REFGUID, LPIH323MediaCap *);
|
|
|
|
LPIH323MediaCap CapsCtl::FindHostForID(MEDIA_FORMAT_ID id)
|
|
{
|
|
if(pAudCaps && pAudCaps->IsHostForCapID(id))
|
|
{
|
|
return (pAudCaps);
|
|
}
|
|
else if (pVidCaps && pVidCaps->IsHostForCapID(id))
|
|
{
|
|
return (pVidCaps);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
LPIH323MediaCap CapsCtl::FindHostForMediaType(PCC_TERMCAP pCapability)
|
|
{
|
|
if(pCapability->DataType == H245_DATA_AUDIO)
|
|
{
|
|
return (pAudCaps);
|
|
}
|
|
else if(pCapability->DataType == H245_DATA_VIDEO)
|
|
{
|
|
return (pVidCaps);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
LPIH323MediaCap CapsCtl::FindHostForMediaGuid(LPGUID pMediaGuid)
|
|
{
|
|
|
|
if(MEDIA_TYPE_H323VIDEO == *pMediaGuid)
|
|
{
|
|
return (pVidCaps);
|
|
}
|
|
else if(MEDIA_TYPE_H323AUDIO == *pMediaGuid)
|
|
{
|
|
return (pAudCaps);
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
ULONG CapsCtl::AddRef()
|
|
{
|
|
uRef++;
|
|
return uRef;
|
|
}
|
|
|
|
ULONG CapsCtl::Release()
|
|
{
|
|
uRef--;
|
|
if(uRef == 0)
|
|
{
|
|
delete this;
|
|
return 0;
|
|
}
|
|
return uRef;
|
|
}
|
|
|
|
STDMETHODIMP CapsCtl::QueryInterface( REFIID iid, void ** ppvObject)
|
|
{
|
|
// this breaks the rules for the official COM QueryInterface because
|
|
// the interfaces that are queried for are not necessarily real COM
|
|
// interfaces. The reflexive property of QueryInterface would be broken in
|
|
// that case.
|
|
|
|
HRESULT hr = E_NOINTERFACE;
|
|
if(!ppvObject)
|
|
return hr;
|
|
|
|
*ppvObject = 0;
|
|
if(iid == IID_IDualPubCap)// satisfy symmetric property of QI
|
|
{
|
|
*ppvObject = (IDualPubCap *)this;
|
|
hr = hrSuccess;
|
|
AddRef();
|
|
}
|
|
else if(iid == IID_IAppAudioCap )
|
|
{
|
|
if(pAudCaps)
|
|
{
|
|
return pAudCaps->QueryInterface(iid, ppvObject);
|
|
}
|
|
}
|
|
else if(iid == IID_IAppVidCap )
|
|
{
|
|
if(pVidCaps)
|
|
{
|
|
return pVidCaps->QueryInterface(iid, ppvObject);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
CapsCtl::CapsCtl () :
|
|
uRef(1),
|
|
pVidCaps(NULL),
|
|
pAudCaps(NULL),
|
|
pACapsBuf(NULL),
|
|
pVCapsBuf(NULL),
|
|
dwNumInUse(0),
|
|
bAudioPublicize(TRUE),
|
|
bVideoPublicize(TRUE),
|
|
bT120Publicize(TRUE),
|
|
m_localT120cap(INVALID_MEDIA_FORMAT),
|
|
m_remoteT120cap(INVALID_MEDIA_FORMAT),
|
|
m_remoteT120bitrate(0),
|
|
m_pAudTermCaps(NULL),
|
|
m_pVidTermCaps(NULL),
|
|
pSetIDs(NULL),
|
|
pRemAdvSets(NULL)
|
|
{
|
|
uStaticGlobalRefCount++;
|
|
}
|
|
|
|
CapsCtl::~CapsCtl ()
|
|
{
|
|
|
|
if (pACapsBuf) {
|
|
MemFree (pACapsBuf);
|
|
}
|
|
if (pVCapsBuf) {
|
|
MemFree (pVCapsBuf);
|
|
}
|
|
|
|
if (pAudCaps) {
|
|
pAudCaps->Release();
|
|
}
|
|
|
|
if (pVidCaps) {
|
|
pVidCaps->Release();
|
|
}
|
|
uStaticGlobalRefCount--;
|
|
if (uStaticGlobalRefCount == 0) {
|
|
//Free up the sim. caps array
|
|
if (pAdvertisedSets) {
|
|
while (pAdvertisedSets->wLength) {
|
|
//wLength is Zero based
|
|
MemFree ((VOID *)pAdvertisedSets->pTermCapDescriptorArray[--pAdvertisedSets->wLength]);
|
|
}
|
|
MemFree ((VOID *)pAdvertisedSets->pTermCapDescriptorArray);
|
|
pAdvertisedSets->pTermCapDescriptorArray = NULL;
|
|
MemFree ((void *) pAdvertisedSets);
|
|
pAdvertisedSets=NULL;
|
|
dwNumInUse=0;
|
|
}
|
|
}
|
|
|
|
//And the remote array
|
|
if (pRemAdvSets) {
|
|
while (pRemAdvSets->wLength) {
|
|
MemFree ((VOID *)pRemAdvSets->pTermCapDescriptorArray[--pRemAdvSets->wLength]);
|
|
}
|
|
MemFree ((void *) pRemAdvSets->pTermCapDescriptorArray);
|
|
pRemAdvSets->pTermCapDescriptorArray = NULL;
|
|
MemFree ((void *) pRemAdvSets);
|
|
pRemAdvSets=NULL;
|
|
}
|
|
MemFree (pSetIDs);
|
|
pSetIDs=NULL;
|
|
}
|
|
|
|
BOOL CapsCtl::Init()
|
|
{
|
|
HRESULT hrLast;
|
|
int iBase = 1;
|
|
|
|
if (g_capFlags & CAPFLAGS_AV_STREAMS)
|
|
{
|
|
hrLast = ::CreateMediaCapability(MEDIA_TYPE_H323AUDIO, &pAudCaps);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{
|
|
goto InitDone;
|
|
}
|
|
}
|
|
|
|
if (g_capFlags & CAPFLAGS_AV_STREAMS)
|
|
{
|
|
hrLast = ::CreateMediaCapability(MEDIA_TYPE_H323VIDEO, &pVidCaps);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{
|
|
goto InitDone;
|
|
}
|
|
}
|
|
|
|
if (pAudCaps)
|
|
{
|
|
// Base the capability IDs beginning at 1 (zero is an invalid capability ID!)
|
|
pAudCaps->SetCapIDBase(iBase);
|
|
iBase += pAudCaps->GetNumCaps();
|
|
}
|
|
|
|
if (pVidCaps)
|
|
{
|
|
pVidCaps->SetCapIDBase(iBase);
|
|
iBase += pVidCaps->GetNumCaps();
|
|
}
|
|
|
|
InitDone:
|
|
m_localT120cap = iBase;
|
|
return TRUE;
|
|
}
|
|
|
|
HRESULT CapsCtl::ReInitialize()
|
|
{
|
|
HRESULT hr = hrSuccess;
|
|
int iBase = 1;
|
|
|
|
if (pAudCaps && !pAudCaps->ReInit())
|
|
{
|
|
hr = CAPS_E_SYSTEM_ERROR;
|
|
goto EXIT;
|
|
}
|
|
|
|
if (pVidCaps && !pVidCaps->ReInit())
|
|
{
|
|
hr = CAPS_E_SYSTEM_ERROR;
|
|
goto EXIT;
|
|
}
|
|
|
|
// Base the capability IDs beginning at 1 (zero is an invalid capability ID!)
|
|
if (pAudCaps)
|
|
{
|
|
pAudCaps->SetCapIDBase(iBase);
|
|
iBase += pAudCaps->GetNumCaps();
|
|
}
|
|
|
|
if (pVidCaps)
|
|
{
|
|
pVidCaps->SetCapIDBase(iBase);
|
|
iBase += pVidCaps->GetNumCaps();
|
|
}
|
|
|
|
m_localT120cap = iBase;
|
|
|
|
EXIT:
|
|
return hr;
|
|
}
|
|
|
|
const char szNMProdNum[] = "Microsoft\256 NetMeeting(TM)\0";
|
|
const char szNM20VerNum[] = "Version 2.0\0";
|
|
|
|
HRESULT CapsCtl::AddRemoteDecodeCaps(PCC_TERMCAPLIST pTermCapList,PCC_TERMCAPDESCRIPTORS pTermCapDescriptors, PCC_VENDORINFO pVendorInfo)
|
|
{
|
|
FX_ENTRY("CapsCtl::AddRemoteDecodeCaps");
|
|
HRESULT hr;
|
|
void *pData=NULL;
|
|
UINT uSize,x,y,z;
|
|
|
|
|
|
//WLength is # of capabilities, not structure length
|
|
WORD wNDesc;
|
|
LPIH323MediaCap pMediaCap;
|
|
|
|
if(!pTermCapList && !pTermCapDescriptors) // additional capability descriptors may be added
|
|
{ // at any time
|
|
return CAPS_E_INVALID_PARAM;
|
|
}
|
|
// Check for NM version 2.0
|
|
m_fNM20 = FALSE;
|
|
ASSERT(pVendorInfo);
|
|
if (pVendorInfo->bCountryCode == USA_H221_COUNTRY_CODE
|
|
&& pVendorInfo->wManufacturerCode == MICROSOFT_H_221_MFG_CODE
|
|
&& pVendorInfo->pProductNumber && pVendorInfo->pVersionNumber
|
|
&& pVendorInfo->pProductNumber->wOctetStringLength == sizeof(szNMProdNum)
|
|
&& pVendorInfo->pVersionNumber->wOctetStringLength == sizeof(szNM20VerNum)
|
|
&& memcmp(pVendorInfo->pProductNumber->pOctetString, szNMProdNum, sizeof(szNMProdNum)) == 0
|
|
&& memcmp(pVendorInfo->pVersionNumber->pOctetString, szNM20VerNum, sizeof(szNM20VerNum)) == 0
|
|
)
|
|
{
|
|
m_fNM20 = TRUE;
|
|
}
|
|
|
|
// cleanup old term caps if term caps are being added and old caps exist
|
|
if (pAudCaps)
|
|
pAudCaps->FlushRemoteCaps();
|
|
if (pVidCaps)
|
|
pVidCaps->FlushRemoteCaps();
|
|
m_remoteT120cap = INVALID_MEDIA_FORMAT; // note there is no T120 cap resolver and
|
|
// this CapsCtl holds exactly one local and remote T120 cap
|
|
|
|
// Copy pTermcapDescriptors to a local copy, (and free any old one)
|
|
if (pRemAdvSets) {
|
|
while (pRemAdvSets->wLength) {
|
|
//0 based
|
|
MemFree ((VOID *)pRemAdvSets->pTermCapDescriptorArray[--pRemAdvSets->wLength]);
|
|
}
|
|
|
|
MemFree ((VOID *)pRemAdvSets->pTermCapDescriptorArray);
|
|
pRemAdvSets->pTermCapDescriptorArray = NULL;
|
|
MemFree ((VOID *)pRemAdvSets);
|
|
pRemAdvSets=NULL;
|
|
|
|
}
|
|
|
|
//Ok, walk through the PCC_TERMCAPDESCRIPTORS list, first, allocate memory for the Master PCC_TERMCAPDESCRIPTORS
|
|
//structure, then each simcap, and the altcaps therin, then copy the data.
|
|
|
|
if (!(pRemAdvSets=(PCC_TERMCAPDESCRIPTORS) MemAlloc (sizeof (CC_TERMCAPDESCRIPTORS) ))){
|
|
return CAPS_E_SYSTEM_ERROR;
|
|
}
|
|
|
|
//How many Descriptors?
|
|
pRemAdvSets->wLength=pTermCapDescriptors->wLength;
|
|
|
|
if (!(pRemAdvSets->pTermCapDescriptorArray=((H245_TOTCAPDESC_T **)MemAlloc (sizeof (H245_TOTCAPDESC_T*)*pTermCapDescriptors->wLength))) ) {
|
|
return CAPS_E_SYSTEM_ERROR;
|
|
}
|
|
|
|
//Once per descriptor...
|
|
for (x=0;x < pTermCapDescriptors->wLength;x++) {
|
|
//Allocate memory for the descriptor entry
|
|
if (!(pRemAdvSets->pTermCapDescriptorArray[x]=(H245_TOTCAPDESC_T *)MemAlloc (sizeof (H245_TOTCAPDESC_T)))) {
|
|
return CAPS_E_SYSTEM_ERROR;
|
|
}
|
|
|
|
//BUGBUG for beta 2 Copy en masse.
|
|
memcpy (pRemAdvSets->pTermCapDescriptorArray[x],pTermCapDescriptors->pTermCapDescriptorArray[x],sizeof (H245_TOTCAPDESC_T));
|
|
|
|
/* post beta 2?
|
|
//Copy the capability ID
|
|
pRemAdvSets->pTermCapDescriptorArray[x].CapID=pTermCapDescriptors[x].CapID
|
|
//Walk the simcaps, then altcaps and copy entries */
|
|
}
|
|
|
|
|
|
for (wNDesc=0;wNDesc <pTermCapList->wLength;wNDesc++) {
|
|
pData=NULL;
|
|
pMediaCap = FindHostForMediaType(pTermCapList->pTermCapArray[wNDesc]);
|
|
if(!pMediaCap)
|
|
{
|
|
// special case: there is no T120 resolver. THIS IS A TEMPORARY
|
|
// SITUATION. We cannot track bitrate limits on multiple T120 capability
|
|
// instances because of this. As of now, we (NetMeeting) do not advertise
|
|
// more than one T.120 capability.
|
|
|
|
//This code will keep the last T.120 capability encountered.
|
|
if(((pTermCapList->pTermCapArray[wNDesc])->DataType == H245_DATA_DATA)
|
|
&& ((pTermCapList->pTermCapArray[wNDesc])->Cap.H245Dat_T120.application.choice
|
|
== DACy_applctn_t120_chosen)
|
|
&& ((pTermCapList->pTermCapArray[wNDesc])->Cap.H245Dat_T120.application.u.DACy_applctn_t120.choice
|
|
== separateLANStack_chosen))
|
|
{
|
|
// it's data data
|
|
m_remoteT120cap = (pTermCapList->pTermCapArray[wNDesc])->CapId;
|
|
m_remoteT120bitrate =
|
|
(pTermCapList->pTermCapArray[wNDesc])->Cap.H245Dat_T120.maxBitRate;
|
|
}
|
|
|
|
// continue;
|
|
// handled it in-line
|
|
}
|
|
else if(pMediaCap->IsCapabilityRecognized(pTermCapList->pTermCapArray[wNDesc]))
|
|
{
|
|
hr = pMediaCap->AddRemoteDecodeFormat(pTermCapList->pTermCapArray[wNDesc]);
|
|
#ifdef DEBUG
|
|
if(!HR_SUCCEEDED(hr))
|
|
{
|
|
ERRORMESSAGE(("%s:AddRemoteDecodeFormat returned 0x%08lx\r\n",_fx_, hr));
|
|
}
|
|
#endif // DEBUG
|
|
}
|
|
}
|
|
return (hrSuccess);
|
|
}
|
|
|
|
|
|
HRESULT CapsCtl::CreateCapList(PCC_TERMCAPLIST *ppCapBuf, PCC_TERMCAPDESCRIPTORS *ppCombinations)
|
|
{
|
|
PCC_TERMCAPLIST pTermCapList = NULL, pTermListAud=NULL, pTermListVid=NULL;
|
|
PCC_TERMCAPDESCRIPTORS pCombinations = NULL;
|
|
UINT uCount = 0, uSize = 0, uT120Size = 0;
|
|
HRESULT hr;
|
|
WORD wc;
|
|
UINT x=0,y=0,z=0,uNumAud=0,uNumVid=0;
|
|
H245_TOTCAPDESC_T *pTotCaps, **ppThisDescriptor;
|
|
PPCC_TERMCAP ppCCThisTermCap;
|
|
PCC_TERMCAP pCCT120Cap = NULL;
|
|
|
|
uCount = GetNumCaps(TRUE);
|
|
|
|
ASSERT((NULL == m_pAudTermCaps) && (NULL == m_pVidTermCaps));
|
|
|
|
// calc size of CC_TERMCAPLIST header + CC_TERMCAPDESCRIPTORS + array of PCC_TERMCAP
|
|
// allocate mem for the master CC_TERMCAPLIST, including the array of pointers to all CC_TERMCAPs
|
|
uSize = sizeof(CC_TERMCAPLIST)
|
|
+ sizeof (CC_TERMCAPDESCRIPTORS) + (uCount * sizeof(PCC_TERMCAP));
|
|
if((m_localT120cap != INVALID_MEDIA_FORMAT) && bT120Publicize)
|
|
{
|
|
uSize += sizeof(CC_TERMCAP);
|
|
}
|
|
|
|
pTermCapList = (PCC_TERMCAPLIST)MemAlloc(uSize);
|
|
if(pTermCapList == NULL)
|
|
{
|
|
hr = CAPS_E_NOMEM;
|
|
goto ERROR_EXIT;
|
|
}
|
|
|
|
// divide up the buffer, CC_TERMCAPLIST first, followed by array of PCC_TERMCAP.
|
|
// The array of PCC_TERMCAP follows fixed size CC_TERMCAPLIST structure and the fixed size
|
|
// CC_TERMCAP structure that holds the one T.120 cap.
|
|
if((m_localT120cap != INVALID_MEDIA_FORMAT) && bT120Publicize)
|
|
{
|
|
pCCT120Cap = (PCC_TERMCAP)(((BYTE *)pTermCapList) + sizeof(CC_TERMCAPLIST));
|
|
ppCCThisTermCap = (PPCC_TERMCAP) (((BYTE *)pTermCapList) + sizeof(CC_TERMCAPLIST) +
|
|
sizeof(CC_TERMCAP));
|
|
}
|
|
else
|
|
ppCCThisTermCap = (PPCC_TERMCAP) (((BYTE *)pTermCapList) + sizeof(CC_TERMCAPLIST));
|
|
|
|
// allocate mem for the simultaneous caps
|
|
// get size of cached advertised sets if it exists and more than one media
|
|
// type is enabled for publication
|
|
if(bAudioPublicize && bVideoPublicize && pAdvertisedSets)
|
|
{
|
|
// use size of cached buffer
|
|
uSize = uAdvertizedSize;
|
|
}
|
|
else if (pAdvertisedSets)
|
|
{
|
|
// This case needs to be fixed. If media types are disabled, the simultaneous capability
|
|
// descriptors in pAdvertisedSets should be rebuilt at that time. There should be no need to test
|
|
// if(bAudioPublicize && bVideoPublicize && pAdvertisedSets)
|
|
|
|
// calculate size of capability descriptors and simultaneous capability structures.
|
|
|
|
#pragma message ("Figure out the size this needs to be...")
|
|
#define NUMBER_TERMCAP_DESCRIPTORS 1
|
|
uSize = sizeof(H245_TOTCAPDESC_T) * NUMBER_TERMCAP_DESCRIPTORS+
|
|
sizeof (CC_TERMCAPDESCRIPTORS)+NUMBER_TERMCAP_DESCRIPTORS*
|
|
sizeof (H245_TOTCAPDESC_T *);
|
|
}
|
|
else
|
|
{
|
|
uSize = 0;
|
|
}
|
|
|
|
|
|
if (uSize)
|
|
{
|
|
pCombinations = (PCC_TERMCAPDESCRIPTORS)MemAlloc(uSize);
|
|
// skip the CC_TERMCAPDESCRIPTORS, which has a variable length array of (H245_TOTCAPDESC_T *) following it
|
|
// the total size of that glob is uSimCapsSize
|
|
// The actual array of [H245_TOTCAPDESC_T *] follows the CC_TERMCAPDESCRIPTORS structure
|
|
// anchor the pCombinations->pTermCapDescriptorArray to this point.
|
|
if(pCombinations == NULL)
|
|
{
|
|
hr = CAPS_E_NOMEM;
|
|
goto ERROR_EXIT;
|
|
}
|
|
|
|
ppThisDescriptor = pCombinations->pTermCapDescriptorArray
|
|
= (H245_TOTCAPDESC_T **)((BYTE *)pCombinations + sizeof(CC_TERMCAPDESCRIPTORS));
|
|
// the first H245_TOTCAPDESC_T follows the array of [H245_TOTCAPDESC_T *]
|
|
pTotCaps = (H245_TOTCAPDESC_T *)((BYTE *)ppThisDescriptor + pCombinations->wLength*sizeof(H245_TOTCAPDESC_T **));
|
|
|
|
if(pAudCaps && bAudioPublicize)
|
|
{
|
|
hr=pAudCaps->CreateCapList((LPVOID *)&pTermListAud);
|
|
if(!HR_SUCCEEDED(hr))
|
|
goto ERROR_EXIT;
|
|
ASSERT(pTermListAud != NULL);
|
|
}
|
|
if(pVidCaps && bVideoPublicize)
|
|
{
|
|
hr=pVidCaps->CreateCapList((LPVOID *)&pTermListVid);
|
|
if(!HR_SUCCEEDED(hr))
|
|
goto ERROR_EXIT;
|
|
ASSERT(pTermListVid != NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pCombinations = NULL;
|
|
}
|
|
|
|
// fix pointers in the master caps list
|
|
|
|
// Now need to fixup the CC_TERMCAPLIST to refer to the individual capabilities
|
|
// Anchor the CC_TERMCAPLIST member pTermCapArray at the array of PCC_TERMCAP, and
|
|
// start partying on the array.
|
|
pTermCapList->wLength =0;
|
|
pTermCapList->pTermCapArray = ppCCThisTermCap;
|
|
|
|
if(pCCT120Cap)
|
|
{
|
|
*ppCCThisTermCap++ = pCCT120Cap;
|
|
// set T120 capability parameters
|
|
pCCT120Cap->DataType = H245_DATA_DATA;
|
|
pCCT120Cap->ClientType = H245_CLIENT_DAT_T120;
|
|
pCCT120Cap->Dir = H245_CAPDIR_LCLRXTX;
|
|
|
|
pCCT120Cap->Cap.H245Dat_T120.application.choice = DACy_applctn_t120_chosen;
|
|
pCCT120Cap->Cap.H245Dat_T120.application.u.DACy_applctn_t120.choice= separateLANStack_chosen;
|
|
pCCT120Cap->Cap.H245Dat_T120.maxBitRate = dwConSpeed;
|
|
|
|
pCCT120Cap->CapId = (H245_CAPID_T)m_localT120cap;
|
|
pTermCapList->wLength++;
|
|
}
|
|
if(pAudCaps && pTermListAud)
|
|
{
|
|
for(wc = 0; wc < pTermListAud->wLength; wc++)
|
|
{
|
|
// copy the array of "pointers to CC_TERMCAP"
|
|
*ppCCThisTermCap++ = pTermListAud->pTermCapArray[wc];
|
|
pTermCapList->wLength++;
|
|
}
|
|
}
|
|
if(pVidCaps && pTermListVid)
|
|
{
|
|
for(wc = 0; wc < pTermListVid->wLength; wc++)
|
|
{
|
|
// copy the array of "pointers to CC_TERMCAP"
|
|
*ppCCThisTermCap++ = pTermListVid->pTermCapArray[wc];
|
|
pTermCapList->wLength++;
|
|
}
|
|
|
|
}
|
|
// fixup the simultaneous capability descriptors
|
|
// Create a default set if necessary
|
|
//
|
|
|
|
if(bAudioPublicize && bVideoPublicize && pAdvertisedSets)
|
|
{
|
|
pCombinations->wLength = pAdvertisedSets->wLength;
|
|
// point pCombinations->pTermCapDescriptorArray past the header (CC_TERMCAPDESCRIPTORS)
|
|
pCombinations->pTermCapDescriptorArray
|
|
= (H245_TOTCAPDESC_T **)((BYTE *)pCombinations + sizeof(CC_TERMCAPDESCRIPTORS));
|
|
// the first H245_TOTCAPDESC_T follows the array of [H245_TOTCAPDESC_T *]
|
|
pTotCaps = (H245_TOTCAPDESC_T *)((BYTE *)pCombinations->pTermCapDescriptorArray +
|
|
pAdvertisedSets->wLength*sizeof(H245_TOTCAPDESC_T **));
|
|
|
|
for(x = 0; x < pAdvertisedSets->wLength; x++)
|
|
{
|
|
// write into the array of descriptor pointers. pointer[x] = this one
|
|
pCombinations->pTermCapDescriptorArray[x] = pTotCaps;
|
|
|
|
pTotCaps->CapDescId= pAdvertisedSets->pTermCapDescriptorArray[x]->CapDescId;
|
|
pTotCaps->CapDesc.Length=pAdvertisedSets->pTermCapDescriptorArray[x]->CapDesc.Length;
|
|
|
|
for(y = 0; y < pTotCaps->CapDesc.Length;y++)
|
|
{
|
|
//Copy the length field.
|
|
pTotCaps->CapDesc.SimCapArray[y].Length=
|
|
pAdvertisedSets->pTermCapDescriptorArray[x]->CapDesc.SimCapArray[y].Length;
|
|
|
|
for(z=0;
|
|
z < pAdvertisedSets->pTermCapDescriptorArray[x]->CapDesc.SimCapArray[y].Length;
|
|
z++)
|
|
{
|
|
pTotCaps->CapDesc.SimCapArray[y].AltCaps[z] =
|
|
pAdvertisedSets->pTermCapDescriptorArray[x]->CapDesc.SimCapArray[y].AltCaps[z];
|
|
}
|
|
}
|
|
pTotCaps++;
|
|
}
|
|
}
|
|
else if (pAdvertisedSets)
|
|
{
|
|
// descriptors in pAdvertisedSets should be rebuilt at that time. There should be no need to test
|
|
// if(bAudioPublicize && bVideoPublicize && pAdvertisedSets)
|
|
|
|
// HACK - put all audio or video caps in one AltCaps[], the T.120 cap in another AltCaps[]
|
|
// and put both of those in one single capability descriptor (H245_TOTCAPDESC_T)
|
|
// This hack will not extend past the assumption of one audio channel, one video channel, and
|
|
// one T.120 channel. If arbitrary media is supported, or multiple audio channels are supported,
|
|
// this code will be wrong
|
|
|
|
pCombinations->wLength=1;
|
|
// point pCombinations->pTermCapDescriptorArray past the header (CC_TERMCAPDESCRIPTORS)
|
|
pCombinations->pTermCapDescriptorArray
|
|
= (H245_TOTCAPDESC_T **)((BYTE *)pCombinations + sizeof(CC_TERMCAPDESCRIPTORS));
|
|
// the first H245_TOTCAPDESC_T follows the array of [H245_TOTCAPDESC_T *]
|
|
pTotCaps = (H245_TOTCAPDESC_T *)((BYTE *)pCombinations->pTermCapDescriptorArray +
|
|
pAdvertisedSets->wLength*sizeof(H245_TOTCAPDESC_T **));
|
|
pTotCaps->CapDescId=(H245_CAPDESCID_T)x;
|
|
pTotCaps->CapDesc.Length=0;
|
|
if(pTermListAud)
|
|
{
|
|
uNumAud = min(pTermListAud->wLength, H245_MAX_ALTCAPS);
|
|
pTotCaps->CapDesc.SimCapArray[x].Length=(unsigned short)uNumAud;
|
|
for(y = 0; y<uNumAud;y++)
|
|
{
|
|
pTotCaps->CapDesc.SimCapArray[x].AltCaps[y] = pTermListAud->pTermCapArray[y]->CapId;
|
|
}
|
|
x++;
|
|
pTotCaps->CapDesc.Length++;
|
|
}
|
|
|
|
if(pTermListVid && pTermListVid->wLength)
|
|
{
|
|
uNumVid = min(pTermListVid->wLength, H245_MAX_ALTCAPS);
|
|
pTotCaps->CapDesc.SimCapArray[x].Length=(unsigned short)uNumVid;
|
|
for(y = 0; y<uNumVid;y++)
|
|
{
|
|
pTotCaps->CapDesc.SimCapArray[x].AltCaps[y] = pTermListVid->pTermCapArray[y]->CapId;
|
|
}
|
|
x++;
|
|
pTotCaps->CapDesc.Length++;
|
|
}
|
|
// the T.120 cap
|
|
if((m_localT120cap != INVALID_MEDIA_FORMAT) && bT120Publicize)
|
|
{
|
|
pTotCaps->CapDesc.SimCapArray[x].Length=1;
|
|
pTotCaps->CapDesc.SimCapArray[x].AltCaps[0] = (H245_CAPID_T)m_localT120cap;
|
|
pTotCaps->CapDesc.Length++;
|
|
}
|
|
|
|
// write into the array of descriptor pointers. pointer[x] = this one
|
|
*ppThisDescriptor = pTotCaps;
|
|
|
|
}
|
|
m_pVidTermCaps = pTermListVid;
|
|
m_pAudTermCaps = pTermListAud;
|
|
|
|
*ppCapBuf = pTermCapList;
|
|
*ppCombinations = pCombinations;
|
|
return hrSuccess;
|
|
|
|
ERROR_EXIT:
|
|
m_pAudTermCaps = NULL;
|
|
m_pVidTermCaps = NULL;
|
|
if(pTermCapList)
|
|
MemFree(pTermCapList);
|
|
if(pCombinations)
|
|
MemFree(pCombinations);
|
|
|
|
if(pAudCaps && pTermListAud)
|
|
{
|
|
hr=pAudCaps->DeleteCapList(pTermListAud);
|
|
}
|
|
if(pVidCaps && pTermListVid)
|
|
{
|
|
hr=pVidCaps->DeleteCapList(pTermListVid);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CapsCtl::DeleteCapList(PCC_TERMCAPLIST pCapBuf, PCC_TERMCAPDESCRIPTORS pCombinations)
|
|
{
|
|
MemFree(pCapBuf);
|
|
MemFree(pCombinations);
|
|
if(m_pAudTermCaps && pAudCaps)
|
|
{
|
|
pAudCaps->DeleteCapList(m_pAudTermCaps);
|
|
}
|
|
if(m_pVidTermCaps)
|
|
{
|
|
pVidCaps->DeleteCapList(m_pVidTermCaps);
|
|
}
|
|
|
|
m_pAudTermCaps = NULL;
|
|
m_pVidTermCaps = NULL;
|
|
return hrSuccess;
|
|
}
|
|
|
|
HRESULT CapsCtl::GetEncodeParams(LPVOID pBufOut, UINT uBufSize,LPVOID pLocalParams, UINT uLocalSize,DWORD idRemote, DWORD idLocal)
|
|
{
|
|
LPIH323MediaCap pMediaCap = FindHostForID(idLocal);
|
|
if(!pMediaCap)
|
|
return CAPS_E_INVALID_PARAM;
|
|
|
|
// HACK
|
|
// Adjust audio packetization depending on call scenario
|
|
// unless there is an overriding registry setting
|
|
if (pMediaCap == pAudCaps)
|
|
{
|
|
VIDEO_FORMAT_ID vidLocal=INVALID_MEDIA_FORMAT, vidRemote=INVALID_MEDIA_FORMAT;
|
|
VIDEO_CHANNEL_PARAMETERS vidParams;
|
|
CC_TERMCAP vidCaps;
|
|
UINT audioPacketLength;
|
|
// modify the audio packetization parameters based on local bandwidth
|
|
// and presence of video
|
|
audioPacketLength = AUDIO_PACKET_DURATION_LONG;
|
|
// the registry setting overrides, if it is present
|
|
if (g_fRegAudioPacketDuration)
|
|
audioPacketLength = g_AudioPacketDurationMs;
|
|
else if (!m_fNM20) // dont try smaller packets for NM20 because it cant handle them
|
|
{
|
|
if (pVidCaps && pVidCaps->ResolveEncodeFormat(&vidLocal,&vidRemote) == S_OK
|
|
&& (pVidCaps->GetEncodeParams(&vidCaps,sizeof(vidCaps), &vidParams, sizeof(vidParams), vidRemote, vidLocal) == S_OK))
|
|
{
|
|
// we may potentially send video
|
|
if (vidParams.ns_params.maxBitRate*100 > BW_ISDN_BITS)
|
|
audioPacketLength = AUDIO_PACKET_DURATION_SHORT;
|
|
|
|
}
|
|
else
|
|
{
|
|
// no video
|
|
// since we dont know the actual connection bandwidth we use
|
|
// the local user setting.
|
|
// Note: if the remote is on a slow-speed net and the local is on a LAN
|
|
// we may end up with an inappropriate setting.
|
|
if (dwConSpeed > BW_288KBS_BITS)
|
|
audioPacketLength = AUDIO_PACKET_DURATION_SHORT;
|
|
else if (dwConSpeed > BW_144KBS_BITS)
|
|
audioPacketLength = AUDIO_PACKET_DURATION_MEDIUM;
|
|
}
|
|
}
|
|
// Setting the AudioPacketDurationMs affects the subsequent GetEncodeParams call
|
|
pMediaCap->SetAudioPacketDuration(audioPacketLength);
|
|
}
|
|
|
|
return pMediaCap->GetEncodeParams (pBufOut,uBufSize, pLocalParams,
|
|
uLocalSize,idRemote,idLocal);
|
|
|
|
}
|
|
|
|
HRESULT CapsCtl::GetPublicDecodeParams(LPVOID pBufOut, UINT uBufSize, VIDEO_FORMAT_ID id)
|
|
{
|
|
LPIH323MediaCap pMediaCap = FindHostForID(id);
|
|
if(!pMediaCap)
|
|
return CAPS_E_INVALID_PARAM;
|
|
|
|
return pMediaCap->GetPublicDecodeParams (pBufOut,uBufSize,id);
|
|
}
|
|
|
|
HRESULT CapsCtl::GetDecodeParams(PCC_RX_CHANNEL_REQUEST_CALLBACK_PARAMS pChannelParams,DWORD * pFormatID, LPVOID lpvBuf, UINT uBufSize)
|
|
{
|
|
LPIH323MediaCap pMediaCap = FindHostForMediaType(pChannelParams->pChannelCapability);
|
|
if(!pMediaCap)
|
|
return CAPS_E_INVALID_PARAM;
|
|
return pMediaCap->GetDecodeParams (pChannelParams,pFormatID,lpvBuf,uBufSize);
|
|
}
|
|
|
|
HRESULT CapsCtl::ResolveToLocalFormat(MEDIA_FORMAT_ID FormatIDLocal,
|
|
MEDIA_FORMAT_ID * pFormatIDRemote)
|
|
{
|
|
LPIH323MediaCap pMediaCap = FindHostForID(FormatIDLocal);
|
|
if(!pMediaCap)
|
|
return CAPS_E_INVALID_PARAM;
|
|
return pMediaCap->ResolveToLocalFormat (FormatIDLocal,pFormatIDRemote);
|
|
|
|
}
|
|
UINT CapsCtl::GetSimCapBufSize (BOOL bRxCaps)
|
|
{
|
|
UINT uSize;
|
|
|
|
// get size of cached advertised sets if it exists and more than one media
|
|
// type is enabled for publication
|
|
if(bAudioPublicize && bVideoPublicize && pAdvertisedSets)
|
|
{
|
|
// use size of cached buffer
|
|
uSize = uAdvertizedSize;
|
|
}
|
|
else
|
|
{
|
|
// calculate size of capability descriptors and simultaneous capability structures.
|
|
|
|
#pragma message ("Figure out the size this needs to be...")
|
|
#define NUMBER_TERMCAP_DESCRIPTORS 1
|
|
uSize = sizeof(H245_TOTCAPDESC_T) * NUMBER_TERMCAP_DESCRIPTORS+
|
|
sizeof (CC_TERMCAPDESCRIPTORS)+NUMBER_TERMCAP_DESCRIPTORS*
|
|
sizeof (H245_TOTCAPDESC_T *);
|
|
}
|
|
return uSize;
|
|
}
|
|
|
|
UINT CapsCtl::GetNumCaps(BOOL bRXCaps)
|
|
{
|
|
UINT u=0;
|
|
if(pAudCaps && bAudioPublicize)
|
|
{
|
|
u = pAudCaps->GetNumCaps(bRXCaps);
|
|
}
|
|
if(pVidCaps && bVideoPublicize)
|
|
{
|
|
u += pVidCaps->GetNumCaps(bRXCaps);
|
|
}
|
|
if(bT120Publicize)
|
|
u++;
|
|
return u;
|
|
}
|
|
|
|
UINT CapsCtl::GetLocalSendParamSize(MEDIA_FORMAT_ID dwID)
|
|
{
|
|
LPIH323MediaCap pMediaCap = FindHostForID(dwID);
|
|
if(!pMediaCap)
|
|
return 0;
|
|
return (pMediaCap->GetLocalSendParamSize(dwID));
|
|
}
|
|
UINT CapsCtl::GetLocalRecvParamSize(PCC_TERMCAP pCapability)
|
|
{
|
|
LPIH323MediaCap pMediaCap = FindHostForMediaType(pCapability);
|
|
if(!pMediaCap)
|
|
return 0;
|
|
return (pMediaCap->GetLocalRecvParamSize(pCapability));
|
|
}
|
|
|
|
STDMETHODIMP CapsCtl::GetEncodeFormatDetails(MEDIA_FORMAT_ID FormatID, VOID **ppFormat, UINT *puSize)
|
|
{
|
|
LPIH323MediaCap pMediaCap = FindHostForID(FormatID);
|
|
if(!pMediaCap)
|
|
{
|
|
*ppFormat = NULL;
|
|
*puSize = 0;
|
|
return E_INVALIDARG;
|
|
}
|
|
return pMediaCap->GetEncodeFormatDetails (FormatID, ppFormat, puSize);
|
|
}
|
|
|
|
STDMETHODIMP CapsCtl::GetDecodeFormatDetails(MEDIA_FORMAT_ID FormatID, VOID **ppFormat, UINT *puSize)
|
|
{
|
|
LPIH323MediaCap pMediaCap = FindHostForID(FormatID);
|
|
if(!pMediaCap)
|
|
{
|
|
*ppFormat = NULL;
|
|
*puSize = 0;
|
|
return E_INVALIDARG;
|
|
}
|
|
return pMediaCap->GetDecodeFormatDetails (FormatID, ppFormat, puSize);
|
|
}
|
|
|
|
//
|
|
// EnableMediaType controls whether or not capabilities for that media type
|
|
// are publicized. In a general implementation (next version?) w/ arbitrary
|
|
// number of media types, each of the media capability objects would keep
|
|
// track of their own state. This version of Capsctl tracks h323 audio and
|
|
// video only
|
|
//
|
|
|
|
HRESULT CapsCtl::EnableMediaType(BOOL bEnable, LPGUID pGuid)
|
|
{
|
|
if(!pGuid)
|
|
return CAPS_E_INVALID_PARAM;
|
|
|
|
if(*pGuid == MEDIA_TYPE_H323AUDIO)
|
|
{
|
|
bAudioPublicize = bEnable;
|
|
}
|
|
else if (*pGuid == MEDIA_TYPE_H323VIDEO)
|
|
{
|
|
bVideoPublicize = bEnable;
|
|
}
|
|
else
|
|
{
|
|
return CAPS_E_INVALID_PARAM;
|
|
}
|
|
return hrSuccess;
|
|
}
|
|
//
|
|
// Build the PCC_TERMCAPDESCRIPTORS list that we will advertise.
|
|
//
|
|
// puAudioFormatList/puVideoFormatList MUST BE sorted by preference!
|
|
//
|
|
//
|
|
|
|
|
|
|
|
HRESULT CapsCtl::AddCombinedEntry (MEDIA_FORMAT_ID *puAudioFormatList,UINT uAudNumEntries,MEDIA_FORMAT_ID *puVideoFormatList, UINT uVidNumEntries,DWORD *pIDOut)
|
|
{
|
|
static USHORT dwLastIDUsed;
|
|
DWORD x,y;
|
|
BOOL bAllEnabled=TRUE,bRecv,bSend;
|
|
unsigned short Length =0;
|
|
|
|
*pIDOut= (ULONG )CCO_E_SYSTEM_ERROR;
|
|
//Validate the Input
|
|
if ((!puAudioFormatList && uAudNumEntries > 0 ) || (!puVideoFormatList && uVidNumEntries > 0 ) || (uVidNumEntries == 0 && uAudNumEntries == 0 )) {
|
|
//What error code should we return here?
|
|
return CCO_E_SYSTEM_ERROR;
|
|
}
|
|
|
|
for (x=0;x<uAudNumEntries;x++)
|
|
{
|
|
ASSERT(pAudCaps);
|
|
pAudCaps->IsFormatEnabled (puAudioFormatList[x],&bRecv,&bSend);
|
|
bAllEnabled &= bRecv;
|
|
|
|
}
|
|
for (x=0;x<uVidNumEntries;x++) {
|
|
ASSERT(pVidCaps);
|
|
pVidCaps->IsFormatEnabled (puAudioFormatList[x],&bRecv,&bSend);
|
|
bAllEnabled &= bRecv;
|
|
}
|
|
|
|
if (!bAllEnabled) {
|
|
return CCO_E_INVALID_PARAM;
|
|
}
|
|
|
|
if (uAudNumEntries > H245_MAX_ALTCAPS || uVidNumEntries > H245_MAX_ALTCAPS) {
|
|
DEBUGMSG (1,("WARNING: Exceeding callcontrol limits!! \r\n"));
|
|
return CCO_E_INVALID_PARAM;
|
|
}
|
|
|
|
//If this is the first call, allocate space
|
|
if (!pAdvertisedSets){
|
|
pAdvertisedSets=(PCC_TERMCAPDESCRIPTORS)MemAlloc (sizeof (CC_TERMCAPDESCRIPTORS));
|
|
if (!pAdvertisedSets){
|
|
//Error code?
|
|
return CCO_E_SYSTEM_ERROR;
|
|
}
|
|
uAdvertizedSize = sizeof (CC_TERMCAPDESCRIPTORS);
|
|
|
|
//Allocate space of NUM_SIMCAP_SETS
|
|
pAdvertisedSets->pTermCapDescriptorArray=(H245_TOTCAPDESC_T **)
|
|
MemAlloc (sizeof (H245_TOTCAPDESC_T *)*NUM_SIMCAP_SETS);
|
|
if (!pAdvertisedSets->pTermCapDescriptorArray) {
|
|
//Error code?
|
|
return CCO_E_SYSTEM_ERROR;
|
|
}
|
|
|
|
//Update the indicies
|
|
uAdvertizedSize += sizeof (H245_TOTCAPDESC_T *)*NUM_SIMCAP_SETS;
|
|
dwNumInUse=NUM_SIMCAP_SETS;
|
|
pAdvertisedSets->wLength=0;
|
|
}
|
|
|
|
//Find an Index to use.
|
|
for (x=0;x<pAdvertisedSets->wLength;x++){
|
|
if (pAdvertisedSets->pTermCapDescriptorArray[x] == NULL){
|
|
break;
|
|
}
|
|
}
|
|
|
|
//Did we find space, or do we need a new one?
|
|
if (x >= dwNumInUse) {
|
|
//Increment the number in use
|
|
dwNumInUse++;
|
|
|
|
PVOID pTempTermCapDescriptorArray = NULL;
|
|
pTempTermCapDescriptorArray = MemReAlloc (pAdvertisedSets->pTermCapDescriptorArray,sizeof (H245_TOTCAPDESC_T *)*(dwNumInUse));
|
|
|
|
if(pTempTermCapDescriptorArray)
|
|
{
|
|
pAdvertisedSets->pTermCapDescriptorArray = (H245_TOTCAPDESC_T **)pTempTermCapDescriptorArray;
|
|
}
|
|
else
|
|
{
|
|
return CCO_E_SYSTEM_ERROR;
|
|
}
|
|
|
|
uAdvertizedSize += (sizeof (H245_TOTCAPDESC_T *)*(dwNumInUse))+sizeof (CC_TERMCAPDESCRIPTORS);
|
|
//Index is 0 based, point at the new entry
|
|
x=dwNumInUse-1;
|
|
}
|
|
|
|
|
|
//x is now the element we are using. Allocate space for a TermCapDescriptorArray
|
|
pAdvertisedSets->pTermCapDescriptorArray[x]=(H245_TOTCAPDESC_T *)MemAlloc (sizeof (H245_TOTCAPDESC_T));
|
|
if (!pAdvertisedSets->pTermCapDescriptorArray[x]){
|
|
return CCO_E_SYSTEM_ERROR;
|
|
}
|
|
uAdvertizedSize += sizeof (H245_TOTCAPDESC_T);
|
|
//Need to update the SetID. (start at 1)...
|
|
pAdvertisedSets->pTermCapDescriptorArray[x]->CapDescId=++dwLastIDUsed;
|
|
//Set the # of sets
|
|
|
|
if((m_localT120cap != INVALID_MEDIA_FORMAT) && bT120Publicize)
|
|
Length++;
|
|
if(uVidNumEntries)
|
|
Length++;
|
|
if(uAudNumEntries)
|
|
Length++;
|
|
pAdvertisedSets->pTermCapDescriptorArray[x]->CapDesc.Length= Length;
|
|
|
|
//Copy the Audio into SimCapArray[0], Video into SimCapArray[1] (if both)
|
|
|
|
if ((uVidNumEntries > 0 && uAudNumEntries > 0)) {
|
|
pAdvertisedSets->pTermCapDescriptorArray[x]->CapDesc.SimCapArray[0].Length=(unsigned short)uAudNumEntries;
|
|
pAdvertisedSets->pTermCapDescriptorArray[x]->CapDesc.SimCapArray[1].Length=(unsigned short)uVidNumEntries;
|
|
if((m_localT120cap != INVALID_MEDIA_FORMAT) && bT120Publicize)
|
|
pAdvertisedSets->pTermCapDescriptorArray[x]->CapDesc.SimCapArray[2].Length=1;
|
|
//Copy the format IDs
|
|
for (y=0;y<uAudNumEntries;y++) {
|
|
pAdvertisedSets->pTermCapDescriptorArray[x]->CapDesc.SimCapArray[0].AltCaps[y]=(USHORT)puAudioFormatList[y];
|
|
}
|
|
for (y=0;y<uVidNumEntries;y++) {
|
|
pAdvertisedSets->pTermCapDescriptorArray[x]->CapDesc.SimCapArray[1].AltCaps[y]=(USHORT)puVideoFormatList[y];
|
|
}
|
|
if((m_localT120cap != INVALID_MEDIA_FORMAT) && bT120Publicize)
|
|
pAdvertisedSets->pTermCapDescriptorArray[x]->CapDesc.SimCapArray[2].AltCaps[0]= (H245_CAPID_T)m_localT120cap;
|
|
|
|
|
|
} else {
|
|
if (uAudNumEntries > 0) {
|
|
pAdvertisedSets->pTermCapDescriptorArray[x]->CapDesc.SimCapArray[0].Length=(unsigned short)uAudNumEntries;
|
|
if((m_localT120cap != INVALID_MEDIA_FORMAT) && bT120Publicize)
|
|
pAdvertisedSets->pTermCapDescriptorArray[x]->CapDesc.SimCapArray[1].Length=1;
|
|
//Copy Audio only
|
|
for (y=0;y<uAudNumEntries;y++) {
|
|
pAdvertisedSets->pTermCapDescriptorArray[x]->CapDesc.SimCapArray[0].AltCaps[y]=(USHORT)puAudioFormatList[y];
|
|
}
|
|
if((m_localT120cap != INVALID_MEDIA_FORMAT) && bT120Publicize)
|
|
pAdvertisedSets->pTermCapDescriptorArray[x]->CapDesc.SimCapArray[1].AltCaps[0]= (H245_CAPID_T)m_localT120cap;
|
|
|
|
} else {
|
|
pAdvertisedSets->pTermCapDescriptorArray[x]->CapDesc.SimCapArray[0].Length=(unsigned short)uVidNumEntries;
|
|
if((m_localT120cap != INVALID_MEDIA_FORMAT) && bT120Publicize)
|
|
pAdvertisedSets->pTermCapDescriptorArray[x]->CapDesc.SimCapArray[1].Length=1;
|
|
|
|
//copy video entries
|
|
for (y=0;y<uVidNumEntries;y++) {
|
|
pAdvertisedSets->pTermCapDescriptorArray[x]->CapDesc.SimCapArray[0].AltCaps[y]=(USHORT)puVideoFormatList[y];
|
|
}
|
|
if((m_localT120cap != INVALID_MEDIA_FORMAT) && bT120Publicize)
|
|
pAdvertisedSets->pTermCapDescriptorArray[x]->CapDesc.SimCapArray[1].AltCaps[0]= (H245_CAPID_T)m_localT120cap;
|
|
}
|
|
}
|
|
|
|
//Need to update the wLength
|
|
pAdvertisedSets->wLength++;
|
|
*pIDOut=dwLastIDUsed;
|
|
|
|
return hrSuccess;
|
|
}
|
|
|
|
|
|
HRESULT CapsCtl::RemoveCombinedEntry (DWORD ID)
|
|
{
|
|
|
|
DWORD x;
|
|
|
|
if (!pAdvertisedSets) {
|
|
return CAPS_E_INVALID_PARAM;
|
|
}
|
|
|
|
for (x=0;x<dwNumInUse;x++) {
|
|
if (pAdvertisedSets->pTermCapDescriptorArray[x]) {
|
|
|
|
if (pAdvertisedSets->pTermCapDescriptorArray[x]->CapDescId == ID) {
|
|
//Found the one to remove
|
|
MemFree ((VOID *)pAdvertisedSets->pTermCapDescriptorArray[x]);
|
|
uAdvertizedSize -= sizeof (H245_TOTCAPDESC_T *);
|
|
if (x != (dwNumInUse -1)) {
|
|
//Not the last one, swap the two pointers
|
|
pAdvertisedSets->pTermCapDescriptorArray[x]=pAdvertisedSets->pTermCapDescriptorArray[dwNumInUse-1];
|
|
pAdvertisedSets->pTermCapDescriptorArray[dwNumInUse-1]=NULL;
|
|
}
|
|
|
|
//Decrement the number in use, and set the wLengthField
|
|
dwNumInUse--;
|
|
pAdvertisedSets->wLength--;
|
|
return hrSuccess;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//Shouldn't get here, unless it was not found.
|
|
return CAPS_E_NOCAPS;
|
|
}
|
|
|
|
|
|
// Given a sized list of capability IDs (pointer to array of H245_CAPID_T)
|
|
// and a sized list of alternate capabilities (AltCaps) within a single simultaneous
|
|
// capability set, (pointer to an array of pointers to H245_SIMCAP_T)
|
|
// Determine if the entire list of capability IDs can simultaneously coexist
|
|
// with respect to the given set of AltCaps.
|
|
|
|
BOOL CapsCtl::AreSimCaps(
|
|
H245_CAPID_T* pIDArray, UINT uIDArraySize,
|
|
H245_SIMCAP_T **ppAltCapArray,UINT uAltCapArraySize)
|
|
{
|
|
UINT i, u;
|
|
SHORT j;
|
|
BOOL bSim;
|
|
|
|
H245_SIMCAP_T *pAltCapEntry, *pFirstAltCapEntry;
|
|
|
|
// If there are fewer AltCaps than capabilities, doom is obvious. Don't bother searching.
|
|
if(uAltCapArraySize < uIDArraySize)
|
|
return FALSE;
|
|
|
|
// find an altcaps entry containing the first ID in the list
|
|
for (i=0;i<uAltCapArraySize;i++)
|
|
{
|
|
pAltCapEntry = *(ppAltCapArray+i);
|
|
// scan this altcaps entry for a matching ID
|
|
for(j=0;j<pAltCapEntry->Length;j++)
|
|
{
|
|
if(*pIDArray == pAltCapEntry->AltCaps[j])
|
|
{
|
|
// found a spot for this capability!
|
|
if(uIDArraySize ==1)
|
|
return TRUE; // Done! all the capabilities have been found to coexist
|
|
|
|
// Otherwise, look for the next capability in the *remaining* AltCaps
|
|
// *This* AltCaps contains the capability we were looking for
|
|
// So, we "used up" this AltCaps and can't select from it anymore
|
|
|
|
// Pack the array of H245_SIMCAP_T pointers in place so that
|
|
// "used" entries are at the beginning and "unused" at the end
|
|
// (a la shell sort swap pointers)
|
|
if(i != 0) // if not already the same, swap
|
|
{
|
|
pFirstAltCapEntry = *ppAltCapArray;
|
|
*ppAltCapArray = pAltCapEntry;
|
|
*(ppAltCapArray+i) = pFirstAltCapEntry;
|
|
}
|
|
// continue the quest using the remaining capabilities
|
|
// and the remaining AltCaps
|
|
bSim = AreSimCaps(pIDArray + 1, uIDArraySize - 1,
|
|
ppAltCapArray + 1, uAltCapArraySize - 1);
|
|
|
|
if(bSim)
|
|
{
|
|
return bSim;// success
|
|
}
|
|
else // why not? Either a fit does not exist (common), or the altcaps contain
|
|
// an odd pattern of multiple instances of some capability IDs, and another
|
|
// search order *might* fit. Do not blindly try all permutations of search
|
|
// order.
|
|
{
|
|
// If it failed simply because the recently grabbed slot in the altcaps
|
|
// (the one in *(ppAltCapArray+i)) could have been needed by subsequent
|
|
// capability IDs, give this one up and look for another instance.
|
|
// If not, we know for sure that the n! approach will not yield
|
|
// fruit and can be avoided.
|
|
for(u=1;(bSim == FALSE)&&(u<uAltCapArraySize);u++)
|
|
{
|
|
for(j=0;(bSim == FALSE)&&(j<pAltCapEntry->Length);j++)
|
|
{ // another capability needed the altcaps we grabbed ?
|
|
if(*(pIDArray+u) == pAltCapEntry->AltCaps[j])
|
|
{
|
|
bSim=TRUE;
|
|
break; // look no more here, bail to try again because a fit *might* exist
|
|
}
|
|
}
|
|
}
|
|
if(bSim) // going to continue searching - Swap pointers back if they were swapped above
|
|
{
|
|
if(i != 0) // if not the same, swap back
|
|
{
|
|
*ppAltCapArray = *(ppAltCapArray+i);
|
|
*(ppAltCapArray+i) = pAltCapEntry;
|
|
}
|
|
break; // next i
|
|
}
|
|
else // don't waste CPU - a fit does not exist
|
|
{
|
|
return bSim;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// Given a sized list of capability IDs (pointer to array of H245_CAPID_T)
|
|
// and a list of simultaneous capabilities, try each simultaneous capability
|
|
// and determine if the entire list of capability IDs can simultaneously coexist.
|
|
BOOL CapsCtl::TestSimultaneousCaps(H245_CAPID_T* pIDArray, UINT uIDArraySize,
|
|
PCC_TERMCAPDESCRIPTORS pTermCaps)
|
|
{
|
|
int iSimSet, iAltSet;
|
|
BOOL bResolved = FALSE;
|
|
H245_SIMCAP_T * pAltCapArray[H245_MAX_SIMCAPS];
|
|
|
|
if (!pAdvertisedSets)
|
|
return(TRUE);
|
|
|
|
// try each independent local SimCaps set (each descriptor) until success
|
|
for (iSimSet=0; (bResolved == FALSE) && (iSimSet < pTermCaps->wLength);iSimSet++)
|
|
{
|
|
// EXTRA STEP:
|
|
// Build a sortable representation of the AltCaps set. This step will not be necessary if
|
|
// and when we change the native representation of a capability descriptor to a variable
|
|
// length list of pointers to AltCaps. In the meantime, we know that there are no more
|
|
// than H245_MAX_SIMCAPS AltCaps in this SimCaps. This is imposed by the 2 dimensional
|
|
// arrays of hardcoded size forced upon us by CALLCONT.DLL.
|
|
for (iAltSet=0;iAltSet < pTermCaps->pTermCapDescriptorArray[iSimSet]->CapDesc.Length;iAltSet++)
|
|
{
|
|
pAltCapArray[iAltSet] = &pTermCaps->pTermCapDescriptorArray[iSimSet]->CapDesc.SimCapArray[iAltSet];
|
|
}
|
|
// do the work
|
|
bResolved = AreSimCaps(pIDArray, uIDArraySize,
|
|
(H245_SIMCAP_T **)pAltCapArray,
|
|
MAKELONG(pTermCaps->pTermCapDescriptorArray[iSimSet]->CapDesc.Length, 0));
|
|
}
|
|
|
|
return bResolved;
|
|
}
|
|
|
|
// Function: CapsCtl::ResolvePermutations(PRES_CONTEXT pResContext, UINT uNumFixedColumns)
|
|
//
|
|
// This functions as both a combination generator and a validation mechanism for the
|
|
// combinations it generates.
|
|
//
|
|
// Given a pointer to a resolution context and the number of fixed (i.e. not permutable,
|
|
// if "permutable" is even a real word) columns, generate one combination at a time.
|
|
// Try each combination until a working combination is found or until all combinations
|
|
// have been tried.
|
|
//
|
|
// The resolution context structure contains a variable number of columns of variable
|
|
// length media format ID lists. Each column tracks its current index. When this
|
|
// function returns TRUE, the winning combination is indicated by the current column
|
|
// indices.
|
|
//
|
|
// The caller can control which combinations are tried first by arranging the columns
|
|
// in descending importance.
|
|
//
|
|
// Incremental searches can be performed without redundant comparisons by adding 1 format
|
|
// at a time to a column, arranging the column order so that the appended column is
|
|
// first, and "fixing" that one column at the newly added format. For example,
|
|
// some calling function could force evaluations on a round-robin column basis by
|
|
// calling this function inside a loop which does the following:
|
|
// 1 - adds one format at a time to the rightmost column and sets the current index
|
|
// of that column to the new entry
|
|
// 2 - rotates the column order so that the rightmost column is now the leftmost
|
|
// 3 - fixing the new leftmost column before calling this function again
|
|
// The result will be that only the permutations which contain the newly added format
|
|
// will be generated.
|
|
|
|
|
|
BOOL CapsCtl::ResolvePermutations(PRES_CONTEXT pResContext, UINT uNumFixedColumns)
|
|
{
|
|
RES_PAIR *pResolvedPair;
|
|
BOOL bResolved = FALSE;
|
|
UINT i, uColumns;
|
|
UINT uPairIndex;
|
|
|
|
// converge on one combination in the permutation
|
|
if(uNumFixedColumns != pResContext->uColumns)
|
|
{
|
|
RES_PAIR_LIST *pThisColumn;
|
|
// take the first non-fixed column, make that column fixed and
|
|
// iterate on it (loop through indices), and try each sub-permutation
|
|
// of remaining columns. (until success or all permutations tried)
|
|
|
|
pThisColumn = *(pResContext->ppPairLists+uNumFixedColumns);
|
|
for (i=0; (bResolved == FALSE) && (i<pThisColumn->uSize); i++)
|
|
{
|
|
pThisColumn->uCurrentIndex = i;
|
|
bResolved = ResolvePermutations(pResContext, uNumFixedColumns+1);
|
|
}
|
|
return bResolved;
|
|
}
|
|
else
|
|
{
|
|
// Bottomed out on the final column. Test the viability of this combination
|
|
|
|
// Build array of local IDs that contians the combination and test the
|
|
// combination against local simultaneous capabilities, then against
|
|
// remote simultaneous capabilities
|
|
|
|
// NOTE: be sure to skip empty columns (which represent unresolvable
|
|
// or unsupported/nonexistent media types or unsupported additional
|
|
// instances of media types)
|
|
|
|
for(i=0, uColumns=0;i<pResContext->uColumns;i++)
|
|
{
|
|
if(((*pResContext->ppPairLists)+i)->uSize)
|
|
{
|
|
// get index (row #) for this column
|
|
uPairIndex = ((*pResContext->ppPairLists)+i)->uCurrentIndex;
|
|
// get the row
|
|
pResolvedPair = ((*pResContext->ppPairLists)+i)->pResolvedPairs+uPairIndex;
|
|
// add the ID to the array
|
|
*(pResContext->pIDScratch+uColumns) = (H245_CAPID_T)pResolvedPair->idPublicLocal;
|
|
uColumns++;
|
|
}
|
|
// else empty column
|
|
}
|
|
// Determine if this combination can exist simultaneously
|
|
if(TestSimultaneousCaps(pResContext->pIDScratch,
|
|
uColumns, pResContext->pTermCapsLocal))
|
|
{
|
|
// now test remote
|
|
// build array of remote IDs and test those against remote
|
|
// simultaneous capabilities
|
|
for(i=0, uColumns=0;i<pResContext->uColumns;i++)
|
|
{
|
|
if(((*pResContext->ppPairLists)+i)->uSize)
|
|
{
|
|
// get index (row #) for this column
|
|
uPairIndex = ((*pResContext->ppPairLists)+i)->uCurrentIndex;
|
|
// get the row
|
|
pResolvedPair = ((*pResContext->ppPairLists)+i)->pResolvedPairs+uPairIndex;
|
|
// add the ID to the array
|
|
*(pResContext->pIDScratch+uColumns) =(H245_CAPID_T) pResolvedPair->idRemote;
|
|
uColumns++;
|
|
}
|
|
// else empty column
|
|
}
|
|
bResolved = TestSimultaneousCaps(pResContext->pIDScratch,
|
|
uColumns, pResContext->pTermCapsRemote);
|
|
}
|
|
|
|
return bResolved;
|
|
// if(bResolved == TRUE)
|
|
// The resolved combination of pairs is indicated by the current indices
|
|
// of **ppPairList;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Given a counted list of desired instances of media, produce an output array of
|
|
// resolved media format IDs which correspond to the input media type IDs.
|
|
// This function returns success if at least one media instance is resolved.
|
|
// When an instance of media is unresolveable, the output corresponding to that
|
|
// instance contains the value INVALID_MEDIA_FORMAT for local and remote media
|
|
// format IDs.
|
|
//
|
|
// The input is treated as being in preferential order: permutations of the latter
|
|
// media type instance are varied first. If all permutations do not yield success,
|
|
// then one media type instance at a time is removed from the end.
|
|
//
|
|
|
|
HRESULT CapsCtl::ResolveFormats (LPGUID pMediaGuidArray, UINT uNumMedia,
|
|
PRES_PAIR pResOutput)
|
|
{
|
|
HRESULT hr = hrSuccess;
|
|
PRES_PAIR_LIST pResColumnArray = NULL;
|
|
PRES_PAIR_LIST *ppPairLists;
|
|
RES_PAIR *pResPair;
|
|
PRES_CONTEXT pResContext;
|
|
LPIH323MediaCap pMediaResolver;
|
|
UINT i;
|
|
UINT uMaxFormats = 0;
|
|
UINT uFixedColumns =0;
|
|
UINT uFailedMediaCount = 0;
|
|
BOOL bResolved = FALSE;
|
|
|
|
RES_PAIR UnresolvedPair = {INVALID_MEDIA_FORMAT, INVALID_MEDIA_FORMAT, INVALID_MEDIA_FORMAT};
|
|
// create a context structure for the resolution
|
|
pResContext = (PRES_CONTEXT)MemAlloc(sizeof(RES_CONTEXT)+ (uNumMedia*sizeof(H245_CAPID_T)));
|
|
if(!pResContext)
|
|
{
|
|
hr = CAPS_E_NOMEM;
|
|
goto ERROR_OUT;
|
|
}
|
|
// initialize resolution context
|
|
pResContext->uColumns = 0;
|
|
pResContext->pIDScratch = (H245_CAPID_T*)(pResContext+1);
|
|
pResContext->pTermCapsLocal = pAdvertisedSets;
|
|
pResContext->pTermCapsRemote = pRemAdvSets;
|
|
|
|
// allocate array of RES_PAIR_LIST (one per column/media type) and
|
|
// array of pointers to same
|
|
pResColumnArray = (PRES_PAIR_LIST)MemAlloc((sizeof(RES_PAIR_LIST) * uNumMedia)
|
|
+ (sizeof(PRES_PAIR_LIST) * uNumMedia));
|
|
if(!pResColumnArray)
|
|
{
|
|
hr = CAPS_E_NOMEM;
|
|
goto ERROR_OUT;
|
|
}
|
|
pResContext->ppPairLists = ppPairLists = (PRES_PAIR_LIST*)(pResColumnArray+uNumMedia);
|
|
|
|
// build columns of media capabilities
|
|
for(i=0;i<uNumMedia;i++)
|
|
{
|
|
// build array of pointers to RES_PAIR_LIST
|
|
*(ppPairLists+i) = pResColumnArray+i;
|
|
// initialize RES_PAIR_LIST members
|
|
(pResColumnArray+i)->pResolvedPairs = NULL;
|
|
(pResColumnArray+i)->uSize =0;
|
|
(pResColumnArray+i)->uCurrentIndex = 0;
|
|
|
|
// Get resolver for this media. Special case: there is no T120 resolver.
|
|
// T120 caps are handled right here in this object
|
|
if(MEDIA_TYPE_H323_T120 == *(pMediaGuidArray+i))
|
|
{
|
|
pMediaResolver = NULL;
|
|
if((m_localT120cap != INVALID_MEDIA_FORMAT) &&(m_remoteT120cap != INVALID_MEDIA_FORMAT) )
|
|
{
|
|
(pResColumnArray+i)->uSize =1;
|
|
uMaxFormats = 1; // only one T.120 cap
|
|
|
|
pResPair = (pResColumnArray+i)->pResolvedPairs =
|
|
(RES_PAIR *)MemAlloc(uMaxFormats * sizeof(RES_PAIR));
|
|
if(!pResPair)
|
|
{
|
|
hr = CAPS_E_NOMEM;
|
|
goto ERROR_OUT;
|
|
}
|
|
|
|
//
|
|
pResPair->idLocal = m_localT120cap;
|
|
pResPair->idRemote = m_remoteT120cap;
|
|
pResPair->idPublicLocal = pResPair->idLocal;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pMediaResolver = FindHostForMediaGuid(pMediaGuidArray+i);
|
|
}
|
|
|
|
pResContext->uColumns++;
|
|
(pResColumnArray+i)->pMediaResolver = pMediaResolver;
|
|
|
|
if(pMediaResolver)
|
|
{
|
|
uMaxFormats = pMediaResolver->GetNumCaps(FALSE); // get transmit format count
|
|
if(uMaxFormats)
|
|
{
|
|
pResPair = (pResColumnArray+i)->pResolvedPairs =
|
|
(RES_PAIR *)MemAlloc(uMaxFormats * sizeof(RES_PAIR));
|
|
if(!pResPair)
|
|
{
|
|
hr = CAPS_E_NOMEM;
|
|
goto ERROR_OUT;
|
|
}
|
|
|
|
// resolve the best choice for each media type (gotta start somewhere)
|
|
pResPair->idLocal = INVALID_MEDIA_FORMAT;
|
|
pResPair->idRemote = INVALID_MEDIA_FORMAT;
|
|
hr=pMediaResolver->ResolveEncodeFormat (&pResPair->idLocal,&pResPair->idRemote);
|
|
if(!HR_SUCCEEDED(hr))
|
|
{
|
|
if((hr == CAPS_W_NO_MORE_FORMATS)
|
|
|| (hr == CAPS_E_NOMATCH)
|
|
|| (hr == CAPS_E_NOCAPS))
|
|
{
|
|
// No resolved format for this media type. Remove this "column"
|
|
(pResColumnArray+i)->pResolvedPairs = NULL;
|
|
MemFree(pResPair);
|
|
(pResColumnArray+i)->uSize =0;
|
|
|
|
hr = hrSuccess;
|
|
}
|
|
else
|
|
{
|
|
goto ERROR_OUT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// this column has one resolved format
|
|
pResPair->idPublicLocal = pMediaResolver->GetPublicID(pResPair->idLocal);
|
|
(pResColumnArray+i)->uSize =1;
|
|
}
|
|
}
|
|
// else // No formats exist for this media type. this "column" has zero size
|
|
}
|
|
}
|
|
|
|
// Special case test simultaneous caps for the most preferred combination:
|
|
uFixedColumns = pResContext->uColumns; // << make all columns fixed
|
|
bResolved = ResolvePermutations(pResContext, uFixedColumns);
|
|
|
|
// if the single most preferred combination can't be used, need to handle
|
|
// the general case and try permutations until a workable combination is found
|
|
while(!bResolved)
|
|
{
|
|
// make one column at a time permutable, starting with the least-critical media
|
|
// type. (e.g. it would be typical for the last column to be video because
|
|
// audio+data are more important. Then we try less and less
|
|
// preferable video formats before doing anything that would degrade the audio)
|
|
|
|
if(uFixedColumns > 0) // if not already at the end of the rope...
|
|
{
|
|
uFixedColumns--; // make another column permutable
|
|
}
|
|
else
|
|
{
|
|
// wow - tried all permutations and still no luck ......
|
|
// nuke the least important remaining media type (e.g. try it w/o video)
|
|
if(pResContext->uColumns <= 1) // already down to one media type?
|
|
{
|
|
hr = CAPS_E_NOMATCH;
|
|
goto ERROR_OUT;
|
|
}
|
|
// Remove the end column (representing the least important media type)
|
|
// and try it with the remaining columns
|
|
uFixedColumns = --pResContext->uColumns; // one less column
|
|
|
|
// set the formats of the nuked column to the unresolved state
|
|
(pResColumnArray+uFixedColumns)->uSize =0;
|
|
(pResColumnArray+uFixedColumns)->uCurrentIndex =0;
|
|
pResPair = (pResColumnArray+uFixedColumns)->pResolvedPairs;
|
|
if (NULL != pResPair)
|
|
{
|
|
pResPair->idLocal = INVALID_MEDIA_FORMAT;
|
|
pResPair->idRemote = INVALID_MEDIA_FORMAT;
|
|
pResPair->idPublicLocal = INVALID_MEDIA_FORMAT;
|
|
}
|
|
|
|
uFailedMediaCount++; // track the nuking of a column to avoid
|
|
// redundantly grabbing all the formats again
|
|
// ... would not be here if all permutations
|
|
// had not been tried!
|
|
// reset the combination indices
|
|
for(i=0;i<uFixedColumns;i++)
|
|
{
|
|
(pResColumnArray+i)->uCurrentIndex = 0;
|
|
}
|
|
}
|
|
|
|
// get the rest of the formats for the last known fixed column, make that column
|
|
// permutable, etc.
|
|
pMediaResolver = (pResColumnArray+uFixedColumns)->pMediaResolver;
|
|
if(!pMediaResolver || ((pResColumnArray+uFixedColumns)->uSize ==0))
|
|
{
|
|
continue; // this media type has no further possibility
|
|
}
|
|
|
|
if(uFailedMediaCount ==0) // If all of the possible resolved pairs
|
|
// have not yet been obtained, get them!
|
|
{
|
|
// get resolved pair IDs for every mutual format of this media type
|
|
// first: get pointer to array of pair IDs, then use ResolveEncodeFormat()
|
|
// to fill up the array
|
|
pResPair = (pResColumnArray+uFixedColumns)->pResolvedPairs;
|
|
// Get total # of formats less the one that was already obtained
|
|
uMaxFormats = pMediaResolver->GetNumCaps(FALSE) -1;
|
|
|
|
while(uMaxFormats--) // never exceed the # of remaining local formats...
|
|
{
|
|
RES_PAIR *pResPairNext;
|
|
|
|
// recall that ResolveEncodeFormat parameters are I/O - the input
|
|
// is the local ID of the last resolved mutual format. (remote id
|
|
// is ignored as input). Fixup the input.
|
|
pResPairNext = pResPair+1;
|
|
// start where the previous resolve stopped
|
|
pResPairNext->idLocal = pResPair->idLocal;
|
|
// not necessary, ignored ->>> pResPairNext->idRemote = pResPair->idRemote
|
|
pResPair = pResPairNext;
|
|
hr=pMediaResolver->ResolveEncodeFormat (&pResPair->idLocal,&pResPair->idRemote);
|
|
if((hr == CAPS_W_NO_MORE_FORMATS)
|
|
|| (hr == CAPS_E_NOMATCH))
|
|
// got all of the formats, but not an error
|
|
{ // this is likely when less than 100% of local formats have a remote match
|
|
hr = hrSuccess;
|
|
break;
|
|
}
|
|
if(!HR_SUCCEEDED(hr))
|
|
goto ERROR_OUT;
|
|
|
|
// get the public ID of the local format (it's *usually* the same, but not always)
|
|
pResPair->idPublicLocal = pMediaResolver->GetPublicID(pResPair->idLocal);
|
|
// this column has another format - count it!
|
|
(pResColumnArray+uFixedColumns)->uSize++;
|
|
}
|
|
}
|
|
// now try the new permutations
|
|
bResolved = ResolvePermutations(pResContext, uFixedColumns);
|
|
}
|
|
if(bResolved)
|
|
{
|
|
// spew the output
|
|
for(i=0;i<uNumMedia;i++)
|
|
{
|
|
if((pResColumnArray+i)->uSize)
|
|
{
|
|
pResPair = (pResColumnArray+i)->pResolvedPairs
|
|
+ (pResColumnArray+i)->uCurrentIndex;
|
|
}
|
|
else
|
|
{
|
|
pResPair = &UnresolvedPair;
|
|
|
|
}
|
|
*(pResOutput+i) = *pResPair;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if there was some error, preserve that error code,
|
|
if(HR_SUCCEEDED(hr))
|
|
// otherwise the error is....
|
|
hr = CAPS_E_NOMATCH;
|
|
}
|
|
|
|
ERROR_OUT: // well, the success case falls out here too
|
|
if(pResColumnArray)
|
|
{
|
|
for(i=0;i<uNumMedia;i++)
|
|
{
|
|
if((pResColumnArray+i)->pResolvedPairs)
|
|
MemFree((pResColumnArray+i)->pResolvedPairs);
|
|
}
|
|
MemFree(pResColumnArray);
|
|
}
|
|
if(pResContext)
|
|
{
|
|
MemFree(pResContext);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CapsCtl::ResetCombinedEntries (void)
|
|
{
|
|
DWORD x;
|
|
|
|
if (pAdvertisedSets)
|
|
{
|
|
for (x = 0; x < pAdvertisedSets->wLength; x++)
|
|
{
|
|
if (pAdvertisedSets->pTermCapDescriptorArray[x])
|
|
{
|
|
MemFree (pAdvertisedSets->pTermCapDescriptorArray[x]);
|
|
}
|
|
}
|
|
MemFree (pAdvertisedSets->pTermCapDescriptorArray);
|
|
|
|
pAdvertisedSets->wLength=0;
|
|
MemFree (pAdvertisedSets);
|
|
pAdvertisedSets = NULL;
|
|
}
|
|
|
|
if (pSetIDs)
|
|
{
|
|
MemFree(pSetIDs);
|
|
pSetIDs = NULL;
|
|
}
|
|
|
|
dwNumInUse=0;
|
|
uAdvertizedSize=0;
|
|
|
|
return hrSuccess;
|
|
}
|
|
|
|
|
|
|
|
|
|
|