/* * 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 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; yCapDesc.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; yCapDesc.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;xIsFormatEnabled (puAudioFormatList[x],&bRecv,&bSend); bAllEnabled &= bRecv; } for (x=0;xIsFormatEnabled (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;xwLength;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;ypTermCapDescriptorArray[x]->CapDesc.SimCapArray[0].AltCaps[y]=(USHORT)puAudioFormatList[y]; } for (y=0;ypTermCapDescriptorArray[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;ypTermCapDescriptorArray[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;ypTermCapDescriptorArray[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;xpTermCapDescriptorArray[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;iLength;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)&&(uLength);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) && (iuSize); 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;iuColumns;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;iuColumns;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;ipResolvedPairs = 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;iuCurrentIndex = 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;iuSize) { 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;ipResolvedPairs) 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; }