/* * File: msivcaps.cpp * * VCM implementation of Microsoft Network Video capability object. * * Revision History: * * 06/06/96 mikev created msiacaps.cpp * 07/28/96 philf created (added support for video) */ #define _MSIAV_ TRUE #include "precomp.h" BOOL GetFormatBuffer(); extern PVCMFORMATDETAILS pvfd_g; #define PREF_ORDER_UNASSIGNED 0xffff //External function (in msiacaps.cpp) to read reg info in one shot #ifdef DEBUG extern ULONG ReadRegistryFormats (LPCSTR lpszKeyName,CHAR ***pppName,BYTE ***pppData,PUINT pnFormats,DWORD dwDebugSize); #else extern ULONG ReadRegistryFormats (LPCSTR lpszKeyName,CHAR ***pppName,BYTE ***pppData,PUINT pnFormats); #endif //This can be used as an export, so give it a unique name! #ifndef _ALPHA_ VIDCAP_DETAILS default_vid_table[] = { #ifdef USE_BILINEAR_MSH26X {VIDEO_FORMAT_MSH263,STD_VID_TERMCAP(H245_CLIENT_VID_H263),STD_VID_PARAMS,{RTP_PAYLOAD_H263,0,30, 24, Small, 128, 96},0,TRUE,TRUE,1,245760*8,245760*8,10,10,5,0,NULL,0,NULL,"Microsoft H.263 Video Codec, vidc.M263, 24bit, 30fps, 128x096"}, {VIDEO_FORMAT_MSH263,STD_VID_TERMCAP(H245_CLIENT_VID_H263),STD_VID_PARAMS,{RTP_PAYLOAD_H263,0,30, 24, Medium, 176, 144},0,TRUE,TRUE,1,245760*8,245760*8,10,10,4,0,NULL,0,NULL,"Microsoft H.263 Video Codec, vidc.M263, 24bit, 30fps, 176x144"}, {VIDEO_FORMAT_MSH263,STD_VID_TERMCAP(H245_CLIENT_VID_H263),STD_VID_PARAMS,{RTP_PAYLOAD_H263,0,30, 24, Large, 352, 288},0,TRUE,TRUE,1,245760*8*4,245760*8*4,10,10,6,0,NULL,0,NULL,"Microsoft H.263 Video Codec, vidc.M263, 24bit, 30fps, 352x288"}, {VIDEO_FORMAT_MSH261,STD_VID_TERMCAP(H245_CLIENT_VID_H261),STD_VID_PARAMS,{RTP_PAYLOAD_H261,0,30, 24, Medium, 176, 144},0,TRUE,TRUE,1,245760*8,245760*8,10,10,7,0,NULL,0,NULL,"Microsoft H.261 Video Codec, vidc.M261, 24bit, 30fps, 176x144"}, {VIDEO_FORMAT_MSH261,STD_VID_TERMCAP(H245_CLIENT_VID_H261),STD_VID_PARAMS,{RTP_PAYLOAD_H261,0,30, 24, Large, 352, 288},0,TRUE,TRUE,1,245760*8*4,245760*8*4,10,10,8,0,NULL,0,NULL,"Microsoft H.261 Video Codec, vidc.M261, 24bit, 30fps, 352x288"}, {VIDEO_FORMAT_MSH26X,NONSTD_VID_TERMCAP,STD_VID_PARAMS,{RTP_DYNAMIC_MIN+1,0,24, Small, 80, 64},0,TRUE,TRUE,1,245760*8,245760*8,10,10,2,0,NULL,0,NULL,"Microsoft H.263 Video Codec, vidc.M26X, 24bit, 30fps, 080x064"}, {VIDEO_FORMAT_MSH26X,NONSTD_VID_TERMCAP,STD_VID_PARAMS,{RTP_DYNAMIC_MIN+1,0,30, 24, Medium, 128, 96},0,TRUE,TRUE,1,245760*8,245760*8,10,10,1,0,NULL,0,NULL,"Microsoft H.263 Video Codec, vidc.M26X, 24bit, 30fps, 128x096"}, {VIDEO_FORMAT_MSH26X,NONSTD_VID_TERMCAP,STD_VID_PARAMS,{RTP_DYNAMIC_MIN+1,0,24, Large, 176, 144},0,TRUE,TRUE,1,245760*8,245760*8,10,10,3,0,NULL,0,NULL,"Microsoft H.263 Video Codec, vidc.M26X, 24bit, 30fps, 176x144"} #else {VIDEO_FORMAT_MSH263,STD_VID_TERMCAP(H245_CLIENT_VID_H263),STD_VID_PARAMS,{RTP_PAYLOAD_H263,0,30, 24, Small, 128, 96},0,TRUE,TRUE,1,245760*8,245760*8,10,10,5,0,NULL,0,NULL,"Microsoft H.263 Video Codec, vidc.M263, 24bit, 30fps, 128x096"}, {VIDEO_FORMAT_MSH263,STD_VID_TERMCAP(H245_CLIENT_VID_H263),STD_VID_PARAMS,{RTP_PAYLOAD_H263,0,30, 24, Medium, 176, 144},0,TRUE,TRUE,1,245760*8,245760*8,10,10,2,0,NULL,0,NULL,"Microsoft H.263 Video Codec, vidc.M263, 24bit, 30fps, 176x144"}, {VIDEO_FORMAT_MSH263,STD_VID_TERMCAP(H245_CLIENT_VID_H263),STD_VID_PARAMS,{RTP_PAYLOAD_H263,0,30, 24, Large, 352, 288},0,TRUE,TRUE,1,245760*8*4,245760*8*4,10,10,14,0,NULL,0,NULL,"Microsoft H.263 Video Codec, vidc.M263, 24bit, 30fps, 352x288"}, {VIDEO_FORMAT_MSH261,STD_VID_TERMCAP(H245_CLIENT_VID_H261),STD_VID_PARAMS,{RTP_PAYLOAD_H261,0,30, 24, Medium, 176, 144},0,TRUE,TRUE,1,245760*8,245760*8,10,10,9,0,NULL,0,NULL,"Microsoft H.261 Video Codec, vidc.M261, 24bit, 30fps, 176x144"}, {VIDEO_FORMAT_MSH261,STD_VID_TERMCAP(H245_CLIENT_VID_H261),STD_VID_PARAMS,{RTP_PAYLOAD_H261,0,30, 24, Large, 352, 288},0,TRUE,TRUE,1,245760*8*4,245760*8*4,10,10,20,0,NULL,0,NULL,"Microsoft H.261 Video Codec, vidc.M261, 24bit, 30fps, 352x288"}, #endif }; #else VIDCAP_DETAILS default_vid_table[] = { {VIDEO_FORMAT_DECH263,STD_VID_TERMCAP(H245_CLIENT_VID_H263),STD_VID_PARAMS,{RTP_PAYLOAD_H263,0,30, 24, Small,128, 96},0,TRUE,TRUE,1,53760,53760,10,10,10,0,0,5,0,NULL,0,NULL, "Digital H263 Video CODEC, vidc.D263, 24bit, 30fps, 128x096"}, {VIDEO_FORMAT_DECH263,STD_VID_TERMCAP(H245_CLIENT_VID_H263),STD_VID_PARAMS,{RTP_PAYLOAD_H263,0,30, 24, Medium,176, 144},0,TRUE,TRUE,1,53760,53760,10,10,10,0,0,2,0,NULL,0,NULL,"Digital H263 Video Codec, vidc.D263, 24bit, 30fps, 176x144"}, {VIDEO_FORMAT_DECH263,STD_VID_TERMCAP(H245_CLIENT_VID_H263),STD_VID_PARAMS,{RTP_PAYLOAD_H263,0,30, 24, Large,352, 288},0,TRUE,TRUE,1,53760,53760,10,10,10,0,0,14,0,NULL,0,NULL,"Digital H263 Video Codec, vidc.D263, 24bit, 30fps, 352x288"}, {VIDEO_FORMAT_DECH261,STD_VID_TERMCAP(H245_CLIENT_VID_H261),STD_VID_PARAMS,{RTP_PAYLOAD_H261,0,30, 24, Medium,176, 144},0,TRUE,TRUE,1,53760,53760,10,10,10,0,0,9,0,NULL,0,NULL,"Digital H261 Video Codec, vidc.D261, 24bit, 30fps, 176x144"}, {VIDEO_FORMAT_DECH261,STD_VID_TERMCAP(H245_CLIENT_VID_H261),STD_VID_PARAMS,{RTP_PAYLOAD_H261,0,30, 24, Large,352, 288},0,TRUE,TRUE,1,53760,53760,10,10,10,0,0,20,0,NULL,0,NULL,"Digital H261 Video Codec, vidc.D261, 24bit, 30fps, 352x288"}, }; #endif static UINT uDefVidTableEntries = sizeof(default_vid_table) /sizeof(VIDCAP_DETAILS); static BOOL bCreateDefTable = FALSE; // // static members of CMsivCapability // MEDIA_FORMAT_ID CMsivCapability::IDsByRank[MAX_CAPS_PRESORT]; UINT CMsivCapability::uNumLocalFormats = 0; // # of active entries in pLocalFormats UINT CMsivCapability::uStaticRef = 0; // global ref count UINT CMsivCapability::uCapIDBase = 0; // rebase capability ID to index into IDsByRank UINT CMsivCapability::uLocalFormatCapacity = 0; // size of pLocalFormats (in multiples of AUDCAP_DETAILS) VIDCAP_DETAILS * CMsivCapability::pLocalFormats = NULL; CMsivCapability::CMsivCapability() :uRef(1), wMaxCPU(95), uNumRemoteDecodeFormats(0), uRemoteDecodeFormatCapacity(0), pRemoteDecodeFormats(NULL), bPublicizeTXCaps(FALSE), bPublicizeTSTradeoff(TRUE) { m_IAppVidCap.Init(this); } CMsivCapability::~CMsivCapability() { UINT u; VIDCAP_DETAILS *pDetails; // release global static memory (the local capabilities) if this is the last delete if(uStaticRef <= 1) { if (pLocalFormats) { pDetails = pLocalFormats; for(u=0; u lpLocalFormatDetails) { MEMFREE(pDetails->lpLocalFormatDetails); } // there really should never be remote details associated with the local // formats........ if(pDetails->lpRemoteFormatDetails) { MEMFREE(pDetails->lpRemoteFormatDetails); } pDetails++; } MEMFREE(pLocalFormats); pLocalFormats=NULL; uLocalFormatCapacity = 0; } uStaticRef--; } else { uStaticRef--; } if (pRemoteDecodeFormats) { pDetails = pRemoteDecodeFormats; for(u=0; u lpLocalFormatDetails) { MEMFREE(pDetails->lpLocalFormatDetails); } // there really should never be remote details associated with the local // formats........ if(pDetails->lpRemoteFormatDetails) { MEMFREE(pDetails->lpRemoteFormatDetails); } pDetails++; } MEMFREE(pRemoteDecodeFormats); pRemoteDecodeFormats=NULL; uRemoteDecodeFormatCapacity = 0; } } UINT CMsivCapability::GetNumCaps(BOOL bRXCaps) { UINT u, uOut=0; VIDCAP_DETAILS *pDecodeDetails = pLocalFormats; if(bRXCaps) { for(u=0; u bRecvEnabled) uOut++; pDecodeDetails++; } return uOut; } else { for(u=0; u bSendEnabled) uOut++; pDecodeDetails++; } return uOut; } } STDMETHODIMP CMsivCapability::QueryInterface( REFIID iid, void ** ppvObject) { HRESULT hr = E_NOINTERFACE; if(!ppvObject) return hr; *ppvObject = 0; if(iid == IID_IAppVidCap ) { *ppvObject = (LPAPPVIDCAPPIF)&m_IAppVidCap; AddRef(); hr = hrSuccess; } else if(iid == IID_IH323MediaCap) { *ppvObject = (IH323MediaCap *)this; AddRef(); hr = hrSuccess; } else if (iid == IID_IUnknown) { *ppvObject = this; AddRef(); hr = hrSuccess; } return hr; } ULONG CMsivCapability::AddRef() { uRef++; return uRef; } ULONG CMsivCapability::Release() { uRef--; if(uRef == 0) { delete this; return 0; } return uRef; } HRESULT CMsivCapability::GetNumFormats(UINT *puNumFmtOut) { *puNumFmtOut = uNumLocalFormats; return hrSuccess; } VOID CMsivCapability::FreeRegistryKeyName(LPTSTR lpszKeyName) { if (lpszKeyName) { LocalFree(lpszKeyName); } } LPTSTR CMsivCapability::AllocRegistryKeyName(LPTSTR lpDriverName, UINT uSampleRate, UINT uBitsPerSample, UINT uBitsPerSec,UINT uWidth,UINT uHeight) { FX_ENTRY(("AllocRegistryKeyName")); BOOL bRet = FALSE; LPTSTR lpszKeyName = NULL; if(!lpDriverName) { return NULL; } // build a subkey name (drivername_samplerate_bitspersample) // allow room for THREE underscore chars + 2x17 bytes of string returned // from _itoa // NOTE: use wsprintf instead of itoa - because of dependency on runtime lib //Added 2 UINTs for video... lpszKeyName = (LPTSTR)LocalAlloc (LPTR, lstrlen(lpDriverName) * sizeof(*lpDriverName) +5*20); if (!lpszKeyName) { ERRORMESSAGE(("%s: LocalAlloc failed\r\n",_fx_)); return(NULL); } // build a subkey name ("drivername_samplerate_bitspersample") wsprintf(lpszKeyName, "%s_%u_%u_%u_%u_%u", lpDriverName, uSampleRate, uBitsPerSample, uBitsPerSec, uWidth, uHeight); return (lpszKeyName); } VOID CMsivCapability::SortEncodeCaps(SortMode sortmode) { UINT iSorted=0; UINT iInsert = 0; UINT iCache=0; UINT iTemp =0; BOOL bInsert; VIDCAP_DETAILS *pDetails1, *pDetails2; if(!uNumLocalFormats) return; if(uNumLocalFormats ==1) { IDsByRank[0]=0; return; } // look at every cached format, build index array for(iCache=0;iCachewApplicationPrefOrder > pDetails1->wApplicationPrefOrder) bInsert = TRUE; break; default: break; } if(bInsert) { if(iSorted < MAX_CAPS_PRESORT) { iSorted++; } // make room, if there is something in the last element, // it gets overwritten for(iTemp = iSorted-1; iTemp > iInsert; iTemp--) { IDsByRank[iTemp] = IDsByRank[iTemp-1]; } // insert at iInsert IDsByRank[iInsert] = iCache; break; } } // check end boundary if((iInsert == iSorted) && (iInsert < MAX_CAPS_PRESORT)) { IDsByRank[iInsert] = iCache; iSorted++; } } } BOOL CMsivCapability::Init() { BOOL bRet; if(uStaticRef == 0) { if(bRet = ReInit()) { uStaticRef++; } } else { uStaticRef++; bRet = TRUE; } return bRet; } BOOL CMsivCapability::ReInit() { DWORD dwDisposition; BOOL bRet = TRUE; //CVcmCapability::ReInit(); // base class ReInit MUST ALWAYS BE CALLED SYSTEM_INFO si; ZeroMemory(&IDsByRank, sizeof(IDsByRank)); // LOOKLOOK - this supports a hack to disable CPU intensive codecs if not running on a pentium GetSystemInfo(&si); wMaxCPU = (si.dwProcessorType == PROCESSOR_INTEL_PENTIUM )? 100 : 95; UINT uNumRemoteDecodeFormats; // # of entries for remote decode capabilities UINT uRemoteDecodeFormatCapacity; // size of pRemoteDecodeFormats (in multiples of VIDCAP_DETAILS) if (pLocalFormats) { UINT u; VIDCAP_DETAILS *pDetails = pLocalFormats; for(u=0; u lpLocalFormatDetails) { MEMFREE(pDetails->lpLocalFormatDetails); } // there really should never be remote details associated with the local // formats........ if(pDetails->lpRemoteFormatDetails) { MEMFREE(pDetails->lpRemoteFormatDetails); } pDetails++; } MEMFREE(pLocalFormats); pLocalFormats = NULL; uLocalFormatCapacity = 0; } uNumLocalFormats = 0; uCapIDBase = 0; uLocalFormatCapacity =0; // m_pAppParam should be non-NULL only if we want to add a VCM format // and not for standard enumeration m_pAppParam = NULL; if(!FormatEnum(this, VCM_FORMATENUMF_APP)) { bRet = FALSE; goto RELEASE_AND_EXIT; } SortEncodeCaps(SortByAppPref); RELEASE_AND_EXIT: return bRet; } STDMETHODIMP CMsivCapability::GetDecodeFormatDetails(MEDIA_FORMAT_ID FormatID, VOID **ppFormat, UINT *puSize) { // validate input UINT uIndex = IDToIndex(FormatID); if(uIndex >= (UINT)uNumLocalFormats) { *puSize = 0; *ppFormat = NULL; return E_INVALIDARG; } *ppFormat = (pLocalFormats + uIndex)->lpLocalFormatDetails; *puSize = sizeof(VIDEOFORMATEX); return S_OK; } STDMETHODIMP CMsivCapability::GetEncodeFormatDetails(MEDIA_FORMAT_ID FormatID, VOID **ppFormat, UINT *puSize) { // same as GetDecodeFormatDetails return GetDecodeFormatDetails(FormatID, ppFormat, puSize); } VOID CMsivCapability::CalculateFormatProperties(VIDCAP_DETAILS *pFmtBuf, PVIDEOFORMATEX lpvfx) { if(!pFmtBuf) { return; } // Estimate input bit rate UINT uBitrateIn = lpvfx->nSamplesPerSec * WIDTHBYTES(lpvfx->bih.biWidth * lpvfx->bih.biBitCount) * lpvfx->bih.biHeight * 8; // set the maximum bitrate (uMaxBitrate). we're not setting the average bitrate (uAvgBitrate), // since the nAvgBytesPerSec reported by VCM is really worst case. uAvgBitrate will be set // from the hardcoded numbers for our known codecs and from the provided VIDCAP_INFO for // installable codecs pFmtBuf->uMaxBitrate = (lpvfx->nAvgBytesPerSec)? lpvfx->nAvgBytesPerSec*8:uBitrateIn; } VIDEO_FORMAT_ID CMsivCapability::AddFormat(VIDCAP_DETAILS *pFmtBuf, LPVOID lpvMappingData, UINT uSize) { VIDCAP_DETAILS *pTemp; VIDEO_PARAMS *pVidCapInfo; UINT format; if(!pFmtBuf) { return INVALID_VIDEO_FORMAT; } // check room if(uLocalFormatCapacity <= uNumLocalFormats) { // get more mem, realloc memory by CAP_CHUNK_SIZE for pLocalFormats pTemp = (VIDCAP_DETAILS *)MEMALLOC((uNumLocalFormats + CAP_CHUNK_SIZE)*sizeof(VIDCAP_DETAILS)); if(!pTemp) goto ERROR_EXIT; // remember how much capacity we now have uLocalFormatCapacity = uNumLocalFormats + CAP_CHUNK_SIZE; #ifdef DEBUG if((uNumLocalFormats && !pLocalFormats) || (!uNumLocalFormats && pLocalFormats)) { ERRORMESSAGE(("AddFormat:leak! uNumLocalFormats:0x%08lX, pLocalFormats:0x%08lX\r\n", uNumLocalFormats,pLocalFormats)); } #endif // copy old stuff, discard old mem if(uNumLocalFormats && pLocalFormats) { memcpy(pTemp, pLocalFormats, uNumLocalFormats*sizeof(VIDCAP_DETAILS)); MEMFREE(pLocalFormats); } pLocalFormats = pTemp; } // pTemp is where the stuff is cached pTemp = pLocalFormats+uNumLocalFormats; memcpy(pTemp, pFmtBuf, sizeof(VIDCAP_DETAILS)); pTemp->uLocalDetailsSize = 0; // clear this now if(uSize && lpvMappingData) { pTemp->lpLocalFormatDetails = MEMALLOC(uSize); if(pTemp->lpLocalFormatDetails) { memcpy(pTemp->lpLocalFormatDetails, lpvMappingData, uSize); pTemp->uLocalDetailsSize = uSize; } #ifdef DEBUG else { ERRORMESSAGE(("AddFormat:allocation failed!\r\n")); } #endif } else { } // LOOKLOOK NEED TO FIXUP channel parameters // pTemp->dwDefaultSamples // pTemp->nonstd_params.wFramesPerPkt // pTemp->nonstd_params.wFramesPerPktMax // pTemp->nonstd_params.wFramesPerPktMin // pTemp->nonstd_params.wDataRate // pTemp->nonstd_params.wFrameSize // fixup the H245 parameters. Use the index of the cap entry as the cap ID pTemp->H245Cap.CapId = (USHORT)IndexToId(uNumLocalFormats); if(pTemp->H245Cap.ClientType ==0 || pTemp->H245Cap.ClientType ==H245_CLIENT_VID_NONSTD) { pTemp->H245Cap.Cap.H245Vid_NONSTD.nonStandardIdentifier.choice = h221NonStandard_chosen; // NOTE: there is some question about the correct byte order // of the codes in the h221NonStandard structure pTemp->H245Cap.Cap.H245Vid_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35CountryCode = USA_H221_COUNTRY_CODE; pTemp->H245Cap.Cap.H245Vid_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35Extension = USA_H221_COUNTRY_EXTENSION; pTemp->H245Cap.Cap.H245Vid_NONSTD.nonStandardIdentifier.u.h221NonStandard.manufacturerCode = MICROSOFT_H_221_MFG_CODE; // Set the nonstandard data fields to null for now. The nonstandard cap data will be // created when capabilities are serialized. pTemp->H245Cap.Cap.H245Vid_NONSTD.data.length = 0; pTemp->H245Cap.Cap.H245Vid_NONSTD.data.value = NULL; } else { switch (pTemp->H245Cap.ClientType ) { case H245_CLIENT_VID_H263: { pVidCapInfo=&pTemp->video_params; format=get_format (pVidCapInfo->biWidth,pVidCapInfo->biHeight); switch (format) { case SQCIF: { pTemp->H245Cap.Cap.H245Vid_H263.bit_mask =H263VideoCapability_sqcifMPI_present; //MPI minimum interval in units of 1/29.97sec so 30/ (frames/sec) is reasonable pTemp->H245Cap.Cap.H245Vid_H263.sqcifMPI = 30/pVidCapInfo->uSamplesPerSec; pTemp->H245Cap.Cap.H245Vid_H263.H263VdCpblty_qcifMPI =0; pTemp->H245Cap.Cap.H245Vid_H263.H263VdCpblty_cifMPI =0; break; } case QCIF: { pTemp->H245Cap.Cap.H245Vid_H263.bit_mask =H263VideoCapability_qcifMPI_present; pTemp->H245Cap.Cap.H245Vid_H263.sqcifMPI = 0; pTemp->H245Cap.Cap.H245Vid_H263.H263VdCpblty_qcifMPI =30/pVidCapInfo->uSamplesPerSec; pTemp->H245Cap.Cap.H245Vid_H263.H263VdCpblty_cifMPI =0; break; } case CIF: { pTemp->H245Cap.Cap.H245Vid_H263.bit_mask =H263VideoCapability_cifMPI_present; pTemp->H245Cap.Cap.H245Vid_H263.sqcifMPI = 0; pTemp->H245Cap.Cap.H245Vid_H263.H263VdCpblty_qcifMPI =0; pTemp->H245Cap.Cap.H245Vid_H263.H263VdCpblty_cifMPI =30/pVidCapInfo->uSamplesPerSec; break; } default: break; } pTemp->H245Cap.Cap.H245Vid_H263.cif4MPI =0; pTemp->H245Cap.Cap.H245Vid_H263.cif16MPI =0; pTemp->H245Cap.Cap.H245Vid_H263.maxBitRate = pFmtBuf->uMaxBitrate / 100; // in units of 100 bits/s pTemp->H245Cap.Cap.H245Vid_H263.unrestrictedVector = FALSE; pTemp->H245Cap.Cap.H245Vid_H263.arithmeticCoding = FALSE; pTemp->H245Cap.Cap.H245Vid_H263.advancedPrediction = FALSE; pTemp->H245Cap.Cap.H245Vid_H263.pbFrames = FALSE; pTemp->H245Cap.Cap.H245Vid_H263.tmprlSptlTrdOffCpblty = FALSE; pTemp->H245Cap.Cap.H245Vid_H263.hrd_B = 0; pTemp->H245Cap.Cap.H245Vid_H263.bppMaxKb = 0; /* Optional, and not supported pTemp->H245Cap.Cap.H245Vid_H263.slowQcifMPI =0; pTemp->H245Cap.Cap.H245Vid_H263.slowSqcifMPI =0; pTemp->H245Cap.Cap.H245Vid_H263.slowCifMPI =0; pTemp->H245Cap.Cap.H245Vid_H263.slowCif4MPI =0; pTemp->H245Cap.Cap.H245Vid_H263.slowCif16MPI =0; */ pTemp->H245Cap.Cap.H245Vid_H263.H263VCy_errrCmpnstn = TRUE; break; } case H245_CLIENT_VID_H261: pVidCapInfo=&pTemp->video_params; format=get_format (pVidCapInfo->biWidth,pVidCapInfo->biHeight); switch (format) { case QCIF: { pTemp->H245Cap.Cap.H245Vid_H261.bit_mask =H261VdCpblty_qcifMPI_present; pTemp->H245Cap.Cap.H245Vid_H261.H261VdCpblty_qcifMPI =max (1,min (4,30/pVidCapInfo->uSamplesPerSec)); pTemp->H245Cap.Cap.H245Vid_H261.H261VdCpblty_cifMPI =0; break; } case CIF: { pTemp->H245Cap.Cap.H245Vid_H261.bit_mask =H261VdCpblty_cifMPI_present; pTemp->H245Cap.Cap.H245Vid_H261.H261VdCpblty_qcifMPI =0; pTemp->H245Cap.Cap.H245Vid_H261.H261VdCpblty_cifMPI =max (1,min(4,30/pVidCapInfo->uSamplesPerSec)); break; } default: break; } pTemp->H245Cap.Cap.H245Vid_H261.maxBitRate = pFmtBuf->uMaxBitrate / 100; // in units of 100 bits/s pTemp->H245Cap.Cap.H245Vid_H261.tmprlSptlTrdOffCpblty = FALSE; pTemp->H245Cap.Cap.H245Vid_H261.stillImageTransmission = FALSE; break; } } uNumLocalFormats++; return pTemp->H245Cap.CapId; ERROR_EXIT: return INVALID_VIDEO_FORMAT; } /*************************************************************************** Name : CMsivCapability::BuildFormatName Purpose : Builds a format name for a format, from the format name and the tag name Parameters: pVidcapDetails [out] - pointer to an VIDCAP_DETAILS structure, where the created value name will be stored pszDriverName [in] - pointer to the name of the driver pszFormatName [in] - pointer to name of the format Returns : BOOL Comment : ***************************************************************************/ BOOL CMsivCapability::BuildFormatName( PVIDCAP_DETAILS pVidcapDetails, WCHAR *pszDriverName, WCHAR *pszFormatName) { int iLen, iLen2; BOOL bRet=TRUE; char szTemp[260]; if (!pVidcapDetails || !pszDriverName || !pszFormatName) { bRet = FALSE; goto out; } // concatenate VCM strings to form the first part of the registry key - the // format is szFormatTag (actually pVidcapDetails->szFormat) // (the string which describes the format tag followed by szFormatDetails // (the string which describes parameters, e.g. sample rate) iLen2 = WideCharToMultiByte(GetACP(), 0, pszDriverName, -1, NULL, 0, NULL, NULL); WideCharToMultiByte(GetACP(), 0, pszDriverName, iLen2, szTemp, iLen2, NULL, NULL); lstrcpyn(pVidcapDetails->szFormat, szTemp, sizeof(pVidcapDetails->szFormat)); iLen = lstrlen(pVidcapDetails->szFormat); // if the format tag description string takes up all the space, don't // bother with the format details (need space for ", " also). // we're going to say that if we don't have room for 4 characters // of the format details string + " ,", then it's not worth it if the // point is generating a unique string -if it is not unique by now, it // will be because some VCM driver writer was misinformed if(iLen < (sizeof(pVidcapDetails->szFormat) + 8*sizeof(TCHAR))) { // ok to concatenate lstrcat(pVidcapDetails->szFormat,", "); // must check for truncation. so do the final concatenation via lstrcpyn // lstrcat(pFormatPrefsBuf->szFormat, pvfd->szFormat); iLen2 = WideCharToMultiByte(GetACP(), 0, pszFormatName, -1, NULL, 0, NULL, NULL); WideCharToMultiByte(GetACP(), 0, pszFormatName, iLen2, szTemp, iLen2, NULL, NULL); iLen = lstrlen(pVidcapDetails->szFormat); lstrcpyn(pVidcapDetails->szFormat+iLen, szTemp, sizeof(pVidcapDetails->szFormat) - iLen - sizeof(TCHAR)); } out: return bRet; } /*************************************************************************** Name : CMsivCapability::GetFormatName Purpose : Gets a driver and format info from VCM and builds a format name Parameters: pVidcapDetails [out] - pointer to an VIDCAP_DETAILS structure, where the created value name will be stored pvfx [in] - pointer to the VIDEOFORMATEX structure for which we need the driver name and the format name Returns : HRESULT Comment : ***************************************************************************/ HRESULT CMsivCapability::GetFormatName( PVIDCAP_DETAILS pVidcapDetails, PVIDEOFORMATEX pvfx) { VCMDRIVERDETAILS vdd; VCMFORMATDETAILS vfd; HRESULT hr=NOERROR; // get the driver details info in order to build correct format name vdd.fccHandler = pvfx->dwFormatTag; if (vcmDriverDetails(&vdd) != MMSYSERR_NOERROR) { ERRORMESSAGE(("CMsivCapability::GetFormatName: can't get the driver details\r\n")); hr = CAPS_E_NOMATCH; goto out; } // have the driver details. get the format details vfd.pvfx = pvfx; if (vcmFormatDetails(&vfd) != MMSYSERR_NOERROR) { ERRORMESSAGE(("CMsivCapability::GetFormatName: can't get the format details\r\n")); hr = CAPS_E_NOMATCH; goto out; } // have the format details too. build the name to store in the registry if (!BuildFormatName(pVidcapDetails, vdd.szDescription, vfd.szFormat)) { ERRORMESSAGE(("CMsivCapability::GetFormatName: can't build format name\r\n")); hr = CAPS_E_SYSTEM_ERROR; goto out; } out: return hr; } BOOL CMsivCapability::FormatEnumHandler(HVCMDRIVERID hvdid, PVCMFORMATDETAILS pvfd, VCMDRIVERDETAILS *pvdd, DWORD_PTR dwInstance) { CMsivCapability *pCapObject = (CMsivCapability *)dwInstance; VIDCAP_DETAILS vidcap_entry; UINT i; // evaluate the details if(IsFormatSpecified(pvfd->pvfx, pvfd, pvdd, &vidcap_entry)) { DEBUGMSG(ZONE_VCM,("FormatEnumHandler: tag 0x%08X\r\n", pvfd->pvfx->dwFormatTag)); DEBUGMSG(ZONE_VCM,("FormatEnumHandler: nSamplesPerSec 0x%08lX, nAvgBytesPerSec 0x%08lX,\r\n", pvfd->pvfx->nSamplesPerSec, pvfd->pvfx->nAvgBytesPerSec)); DEBUGMSG(ZONE_VCM,("FormatEnumHandler: nBlockAlign 0x%08X, wBitsPerSample 0x%04X\r\n", pvfd->pvfx->nBlockAlign, pvfd->pvfx->wBitsPerSample)); DEBUGMSG(ZONE_VCM,("FormatEnumHandler: szFormat %s,\r\n", pvfd->szFormat)); // done inside IsFormatSpecified and/or whatever it calls // CalculateFormatProperties(&audcap_details, pvfd->pvfx); i=AddFormat(&vidcap_entry, (LPVOID)pvfd->pvfx, (pvfd->pvfx) ? sizeof(VIDEOFORMATEX):0); if (i != INVALID_VIDEO_FORMAT) { //Set the Send/Recv Flags... //This now needs to set bSendEnabled, and bRecvEnabled, according to pvfd->dwFlags //So, we need to find the format, and update the flags accordingly. //OUTPUT IS RECV!!!! if (pvfd->dwFlags == VCM_FORMATENUMF_BOTH) { pLocalFormats[i].bSendEnabled=TRUE; pLocalFormats[i].bRecvEnabled=TRUE; }else { if(pvfd->dwFlags == VCM_FORMATENUMF_OUTPUT) { pLocalFormats[i].bSendEnabled=FALSE; pLocalFormats[i].bRecvEnabled=TRUE; } else { pLocalFormats[i].bSendEnabled=TRUE; pLocalFormats[i].bRecvEnabled=FALSE; } } } } return TRUE; } BOOL CMsivCapability::IsFormatSpecified(PVIDEOFORMATEX lpFormat, PVCMFORMATDETAILS pvfd, VCMDRIVERDETAILS *pvdd, VIDCAP_DETAILS *pVidcapDetails) { VIDCAP_DETAILS *pcap_entry; BOOL bRet = FALSE; LPTSTR lpszKeyName = NULL; DWORD dwRes; UINT i; if(!lpFormat || !pVidcapDetails) { return FALSE; } RtlZeroMemory((PVOID) pVidcapDetails, sizeof(VIDCAP_DETAILS)); // fixup the VIDEOFORMAT fields of video_params so that the key name can be built pVidcapDetails->video_params.uSamplesPerSec = lpFormat->nSamplesPerSec; pVidcapDetails->video_params.uBitsPerSample = MAKELONG(lpFormat->bih.biBitCount,0); pVidcapDetails->video_params.biWidth=lpFormat->bih.biWidth; pVidcapDetails->video_params.biHeight=lpFormat->bih.biHeight; pVidcapDetails->uMaxBitrate=lpFormat->nAvgBytesPerSec * 8; // build the name of the format out of the driver and the VCM format name if ((!pvdd) || !BuildFormatName(pVidcapDetails, pvdd->szDescription, pvfd->szFormat)) { ERRORMESSAGE(("IsFormatSpecified: Coludn't build format name\r\n")); return(FALSE); } lpszKeyName = AllocRegistryKeyName( pVidcapDetails->szFormat, pVidcapDetails->video_params.uSamplesPerSec, pVidcapDetails->video_params.uBitsPerSample, pVidcapDetails->uMaxBitrate, pVidcapDetails->video_params.biWidth, pVidcapDetails->video_params.biHeight); if (!lpszKeyName) { ERRORMESSAGE(("IsFormatSpecified: Alloc failed\r\n")); return(FALSE); } RegEntry reVidCaps(szRegInternetPhone TEXT("\\") szRegInternetPhoneVCMEncodings, HKEY_LOCAL_MACHINE, FALSE, KEY_READ); dwRes = reVidCaps.GetBinary(lpszKeyName, (PVOID *) &pcap_entry); // use current registry setting if it exists if(dwRes && (dwRes == sizeof(VIDCAP_DETAILS))) { // do a quick sanity check on the contents if((lpFormat->dwFormatTag == pcap_entry->dwFormatTag) && (lpFormat->nSamplesPerSec == (DWORD)pcap_entry->video_params.uSamplesPerSec) && (lpFormat->wBitsPerSample == LOWORD(pcap_entry->video_params.uBitsPerSample)) && (lpFormat->bih.biWidth == (LONG) pcap_entry->video_params.biWidth) && (lpFormat->bih.biHeight == (LONG) pcap_entry->video_params.biHeight)) { CopyMemory(pVidcapDetails, pcap_entry, sizeof(VIDCAP_DETAILS)); bRet = TRUE; } } else // check the static default table, and recreate the default entries { for(i=0;i< uDefVidTableEntries; i++) { if((lpFormat->dwFormatTag == default_vid_table[i].dwFormatTag) && (lpFormat->nSamplesPerSec == (DWORD)default_vid_table[i].video_params.uSamplesPerSec) && (lpFormat->wBitsPerSample == LOWORD(default_vid_table[i].video_params.uBitsPerSample)) && (lpFormat->bih.biWidth == (LONG) default_vid_table[i].video_params.biWidth) && (lpFormat->bih.biHeight == (LONG) default_vid_table[i].video_params.biHeight)) { // found matching default entry - copy stuff from table // (but don't overwrite the string) memcpy(pVidcapDetails, &default_vid_table[i], sizeof(VIDCAP_DETAILS) - sizeof(pVidcapDetails->szFormat)); // LOOKLOOK - test against CPU limitations. // this supports a hack to disable CPU intensive codecs if not running //on a pentium if(default_vid_table[i].wCPUUtilizationEncode > wMaxCPU) { pVidcapDetails->bSendEnabled = FALSE; pVidcapDetails->bRecvEnabled = FALSE; } // add this to the registry CalculateFormatProperties(pVidcapDetails, lpFormat); bRet = UpdateFormatInRegistry(pVidcapDetails); break; } } } if (lpszKeyName) { FreeRegistryKeyName(lpszKeyName); } return(bRet); } /*************************************************************************** Name : CMsivCapability::CopyVidcapInfo Purpose : Copies basic video info from an VIDCAP_INFO structure to an VIDCAP_DETAILS structure, or vice versa. VIDCAP_INFO is external representation. VIDCAP_DETAILS is internal one. Parameters: pDetails - pointer to an VIDCAP_DETAILS structure pInfo - pointer to an VIDCAP_INFO structure bDirection - 0 = ->, 1 = <- Returns : HRESULT Comment : ***************************************************************************/ HRESULT CMsivCapability::CopyVidcapInfo(PVIDCAP_DETAILS pDetails, PVIDCAP_INFO pInfo, BOOL bDirection) { WORD wSortIndex; VIDEO_FORMAT_ID Id; UINT uIndex; HRESULT hr=NOERROR; if(!pInfo || !pDetails) { hr = CAPS_E_INVALID_PARAM; goto out; } if (bDirection) { // VIDCAP_INFO -> VIDCAP_DETAILS // the caller cannot modify szFormat, Id, wSortIndex and uMaxBitrate, all calculated fields // nAvgBitrate can be provided, but will be overriden if the codec provided a non-zero // value in the VIDEOFORMATEX structure pDetails->dwFormatTag = pInfo->dwFormatTag; pDetails->uAvgBitrate = pInfo->uAvgBitrate; pDetails->wCPUUtilizationEncode = pInfo->wCPUUtilizationEncode; pDetails->wCPUUtilizationDecode = pInfo->wCPUUtilizationDecode; pDetails->bSendEnabled = pInfo->bSendEnabled; pDetails->bRecvEnabled = pInfo->bRecvEnabled; pDetails->video_params.enumVideoSize = pInfo->enumVideoSize; pDetails->video_params.biHeight = pInfo->bih.biHeight; pDetails->video_params.biWidth = pInfo->bih.biWidth; // lpLocalFormatDetails is updated in AddFormat // DO NOT overwrite any of the fields used to construct the regkey name // pDetails->video_params.uSamplesPerSec = pInfo->uFrameRate; pDetails->video_params.uBitsPerSample = pInfo->dwBitsPerSample; //Re-adjust to frame rate. MPI is Interval in units of 1/29.97 seconds //No div by zero error pInfo->uFrameRate= max(1,pInfo->uFrameRate); pDetails->nonstd_params.MPI = 30/pInfo->uFrameRate; } else { // VIDCAP_DETAILS -> VIDCAP_INFO PVIDEOFORMATEX pvfx = (PVIDEOFORMATEX) pDetails->lpLocalFormatDetails; // find the sort index. uIndex = (UINT)(pDetails - pLocalFormats); Id = IndexToId(uIndex); for(wSortIndex=0; wSortIndexdwFormatTag = pDetails->dwFormatTag; pInfo->Id = Id; memcpy(pInfo->szFormat, pDetails->szFormat, sizeof(pInfo->szFormat)); pInfo->wCPUUtilizationEncode = pDetails->wCPUUtilizationEncode; pInfo->wCPUUtilizationDecode = pDetails->wCPUUtilizationDecode; pInfo->bSendEnabled = pDetails->bSendEnabled; pInfo->bRecvEnabled = pDetails->bRecvEnabled; pInfo->wSortIndex = wSortIndex; pInfo->enumVideoSize = pDetails->video_params.enumVideoSize; if (pvfx) RtlCopyMemory(&pInfo->bih, &pvfx->bih, sizeof(BITMAPINFOHEADER)); //The h.323 nonstd params for bitrate is in units of 100 bits/sec pInfo->dwBitsPerSample = pDetails->video_params.uBitsPerSample; pInfo->uAvgBitrate = pDetails->uAvgBitrate; pInfo->uMaxBitrate = pDetails->nonstd_params.maxBitRate*100; //Re-adjust to frame rate. MPI is Interval in units of 1/29.97 seconds //No div by zero error pDetails->nonstd_params.MPI= max(1,pDetails->nonstd_params.MPI); pInfo->uFrameRate = min(30,30/pDetails->nonstd_params.MPI); } out: return hr; } HRESULT CMsivCapability::EnumCommonFormats(PBASIC_VIDCAP_INFO pFmtBuf, UINT uBufsize, UINT *uNumFmtOut, BOOL bTXCaps) { UINT u, uNumOut = 0;; HRESULT hr = hrSuccess; VIDCAP_DETAILS *pDetails = pLocalFormats; MEDIA_FORMAT_ID FormatIDRemote; HRESULT hrIsCommon; // validate input if(!pFmtBuf || !uNumFmtOut || (uBufsize < (sizeof(BASIC_VIDCAP_INFO)*uNumLocalFormats))) { return CAPS_E_INVALID_PARAM; } if(!uNumLocalFormats || !pDetails) { return CAPS_E_NOCAPS; } // temporary - enumerating requestable receive formats is not yet supported if(!bTXCaps) return CAPS_E_NOT_SUPPORTED; for(u=0; (u Id) >= uNumLocalFormats) { return CAPS_E_INVALID_PARAM; } // look for bad sort indices, duplicate sort indices and duplicate format IDs if(pTemp->wSortIndex >= uNumLocalFormats) return CAPS_E_INVALID_PARAM; for(v=u+1; v wSortIndex == pFormatPrefsBuf[v].wSortIndex) || (pTemp->Id == pFormatPrefsBuf[v].Id)) { ERRORMESSAGE(("%s invalid param: wSI1:0x%04x, wSI2:0x%04x, ID1:%d, ID2:%d\r\n", _fx_, pTemp->wSortIndex, pFormatPrefsBuf[v].wSortIndex, pTemp->Id, pFormatPrefsBuf[v].Id)); return CAPS_E_INVALID_PARAM; } } } // all seems well for(u=0; u Id); // identifies this local format // apply the new sort order pFmt->wApplicationPrefOrder = pTemp->wSortIndex; // update the updatable parameters (CPU utilization, bitrate) pFmt->bSendEnabled = pTemp->bSendEnabled; pFmt->bRecvEnabled = pTemp->bRecvEnabled; // DO NOT overwrite any of the fields used to construct the regkey name // pFmt->video_params.uSamplesPerSec = pTemp->uFrameRate; //Units of 100 bits/sec pFmt->nonstd_params.maxBitRate= (pTemp->uMaxBitrate/100); // pFmt->nonstd_params.maxBPP= 0; pFmt->nonstd_params.MPI= 30/max(pTemp->uFrameRate, 1); // only the tuning wizard or other profiling app can write wCPUUtilizationEncode, // wCPUUtilizationDecode, uAvgBitrate // update the registry UpdateFormatInRegistry(pFmt); // now update the sort order contained in VIDsByRank // note: recall that only MAX_CAPS_PRESORT are sorted and the rest are in random order. // LOOKLOOK - maybe need a separate sort order array? - the order in VIDsByRank // is being overriden here // the array holds the sorted indices into the array of formats in pLocalFormats if(pTemp->wSortIndex < MAX_CAPS_PRESORT) { // insert the format at the position indicated by the input IDsByRank[pTemp->wSortIndex] = (MEDIA_FORMAT_ID)(pFmt - pLocalFormats); } } return hrSuccess; } // update the registry BOOL CMsivCapability::UpdateFormatInRegistry(VIDCAP_DETAILS *pVidcapDetails) { FX_ENTRY(("CMsivCapability::UpdateFormatInRegistry")); LPTSTR lpszKeyName = NULL; BOOL bRet; if(!pVidcapDetails) { return FALSE; } lpszKeyName = AllocRegistryKeyName( pVidcapDetails->szFormat, pVidcapDetails->video_params.uSamplesPerSec, pVidcapDetails->video_params.uBitsPerSample, pVidcapDetails->uMaxBitrate, pVidcapDetails->video_params.biWidth, pVidcapDetails->video_params.biHeight); if (!lpszKeyName) { ERRORMESSAGE(("%s:Alloc failed\r\n",_fx_)); return(FALSE); } DEBUGMSG(ZONE_VCM,("%s:updating %s, wPref:0x%04x, bS:%d, bR:%d\r\n", _fx_, lpszKeyName, pVidcapDetails->wApplicationPrefOrder, pVidcapDetails->bSendEnabled, pVidcapDetails->bRecvEnabled)); // add this to the registry RegEntry reVidCaps(szRegInternetPhone TEXT("\\") szRegInternetPhoneVCMEncodings, HKEY_LOCAL_MACHINE); bRet = (ERROR_SUCCESS == reVidCaps.SetValue(lpszKeyName, pVidcapDetails, sizeof(VIDCAP_DETAILS))); FreeRegistryKeyName(lpszKeyName); return(bRet); } /*************************************************************************** Name : CMsivCapability::AddVCMFormat Purpose : Adds an VCM format to the list of formats we support Parameters: pvfx - pointer to the videoformat structure for the added codec pVidcapInfo - additional format info that is not in the videoformat structure Returns : HRESULT Comment : ***************************************************************************/ HRESULT CMsivCapability::AddVCMFormat (PVIDEOFORMATEX pvfx, PVIDCAP_INFO pVidcapInfo) { HRESULT hr = hrSuccess; // initialize cap entry with default values VIDCAP_DETAILS cap_entry = {VIDEO_FORMAT_UNKNOWN, NONSTD_VID_TERMCAP,STD_VID_PARAMS, {RTP_DYNAMIC_MIN+1, 0, 30, 7680, Small, 0, 0},0, TRUE, TRUE, 1, // default number of samples per packet 245760*8, // default to 16kbs bitrate 245760*8, // unknown average bitrate 10, 10, // default CPU utilization PREF_ORDER_UNASSIGNED, // unassigned sort order 0,NULL,0,NULL, ""}; if(!pvfx || !pVidcapInfo) { hr = CAPS_E_INVALID_PARAM; goto out; } /* * Build the VIDCAP_DETAILS structure for this format */ // now add VIDCAP_INFO information CopyVidcapInfo(&cap_entry, pVidcapInfo, 1); // calculate whatever parameters can be calculated // use actual bits per sample unless the bps field is zero, in which case // assume 16 bits (worst case). CalculateFormatProperties(&cap_entry, pvfx); // Make sure it's an upper case FourCC if (cap_entry.dwFormatTag > 256) CharUpperBuff((LPTSTR)&cap_entry.dwFormatTag, sizeof(DWORD)); // set the RTP payload number. We are using a random number from the dynamic range // for the installable codecs cap_entry.video_params.RTPPayload = RTP_DYNAMIC_MIN+1; // get the format name and driver name for this format from VCM and // build a format name to add to the registry hr = GetFormatName(&cap_entry, pvfx); if (FAILED(hr)) goto out; // add this to the registry if(!UpdateFormatInRegistry(&cap_entry)) { ERRORMESSAGE(("CMsivCapability::AddVCMFormat: can't update registry\r\n")); hr = CAPS_E_SYSTEM_ERROR; goto out; } // reinit to update the list of local formats if (!ReInit()) { hr = CAPS_E_SYSTEM_ERROR; goto out; } out: return hr; } /*************************************************************************** Name : CMsivCapability::RemoveVCMFormat Purpose : Removes an VCM format to the list of formats we support Parameters: pvfx - pointer to the videoformat structure for the added codec Returns : HRESULT Comment : ***************************************************************************/ HRESULT CMsivCapability::RemoveVCMFormat (PVIDEOFORMATEX pvfx) { HRESULT hr = hrSuccess; HKEY hKey = NULL; LPTSTR lpszValueName = NULL; DWORD dwErr; VIDCAP_DETAILS cap_entry; if(!pvfx) { return CAPS_E_INVALID_PARAM; } // get the format name and driver name for this format from VCM and // build a format name to add to the registry hr = GetFormatName(&cap_entry, pvfx); if (FAILED(hr)) goto out; lpszValueName = AllocRegistryKeyName(cap_entry.szFormat, pvfx->nSamplesPerSec, MAKELONG(pvfx->wBitsPerSample,0), pvfx->nAvgBytesPerSec*8, pvfx->bih.biWidth, pvfx->bih.biHeight); if (!lpszValueName) { ERRORMESSAGE(("CMsivCapability::RemoveVCMFormat: Alloc failed\r\n")); hr = CAPS_E_SYSTEM_ERROR; goto out; } // Get the key handle if (dwErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szRegInternetPhone TEXT("\\") szRegInternetPhoneVCMEncodings, 0, KEY_ALL_ACCESS, &hKey)) { ERRORMESSAGE(("CMsivCapability::RemoveVCMFormat: can't open key to delete\r\n")); hr = CAPS_E_SYSTEM_ERROR; goto out; } dwErr = RegDeleteValue(hKey, lpszValueName ); if(dwErr != ERROR_SUCCESS) { hr = CAPS_E_SYSTEM_ERROR; goto out; } // reinit to update the list of local formats if (!ReInit()) { hr = CAPS_E_SYSTEM_ERROR; goto out; } out: if (hKey) RegCloseKey(hKey); if(lpszValueName) MEMFREE(lpszValueName); return hr; } UINT CMsivCapability::GetLocalSendParamSize(MEDIA_FORMAT_ID dwID) { return (sizeof(VIDEO_CHANNEL_PARAMETERS)); } UINT CMsivCapability::GetLocalRecvParamSize(PCC_TERMCAP pCapability) { return (sizeof(VIDEO_CHANNEL_PARAMETERS)); } HRESULT CMsivCapability::CreateCapList(LPVOID *ppCapBuf) { HRESULT hr = hrSuccess; UINT u; VIDCAP_DETAILS *pDecodeDetails = pLocalFormats; PCC_TERMCAPLIST pTermCapList = NULL; PPCC_TERMCAP ppCCThisTermCap = NULL; PCC_TERMCAP pCCThisCap = NULL; PNSC_VIDEO_CAPABILITY pNSCapNext; PVIDEOFORMATEX lpvcd; VIDEO_PARAMS *pVidCapInfo; UINT format; FX_ENTRY ("CreateCapList"); // validate input if(!ppCapBuf) { hr = CAPS_E_INVALID_PARAM; goto ERROR_OUT; } *ppCapBuf = NULL; if(!uNumLocalFormats || !pDecodeDetails) { hr = CAPS_E_NOCAPS; goto ERROR_OUT; } pTermCapList = (PCC_TERMCAPLIST)MemAlloc(sizeof(CC_TERMCAPLIST)); if(!pTermCapList) { hr = CAPS_E_NOMEM; goto ERROR_OUT; } ppCCThisTermCap = (PPCC_TERMCAP)MemAlloc(uNumLocalFormats * sizeof(PCC_TERMCAP)); if(!ppCCThisTermCap) { hr = CAPS_E_NOMEM; goto ERROR_OUT; } pTermCapList->wLength = 0; // point the CC_TERMCAPLIST pTermCapArray at the array of PCC_TERMCAP pTermCapList->pTermCapArray = ppCCThisTermCap; /* CC_TERMCAPLIST PCC_TERMCAP CC_TERMCAP pTermCapList-> { wLength pTermCapArray--->pTermCap----------->{single capability.....} } pTermCap----------->{single capability.} pTermCap----------->{single capability...} */ for(u=0; u bRecvEnabled ) || (pDecodeDetails->dwPublicRefIndex)) { pDecodeDetails++; continue; } if(pDecodeDetails->H245Cap.ClientType ==0 || pDecodeDetails->H245Cap.ClientType ==H245_CLIENT_VID_NONSTD) { lpvcd = (PVIDEOFORMATEX)pDecodeDetails->lpLocalFormatDetails; if(!lpvcd) { pDecodeDetails++; continue; } // allocate for this one capability pCCThisCap = (PCC_TERMCAP)MemAlloc(sizeof(CC_TERMCAP)); pNSCapNext = (PNSC_VIDEO_CAPABILITY)MemAlloc(sizeof(NSC_VIDEO_CAPABILITY)); if((!pCCThisCap)|| (!pNSCapNext)) { hr = CAPS_E_NOMEM; goto ERROR_OUT; } // set type of nonstandard capability pNSCapNext->cvp_type = NSC_VCM_VIDEOFORMATEX; // stuff both chunks of nonstandard capability info into buffer // first stuff the "channel parameters" (the format independent communication options) memcpy(&pNSCapNext->cvp_params, &pDecodeDetails->nonstd_params, sizeof(NSC_CHANNEL_VIDEO_PARAMETERS)); // then the VCM stuff memcpy(&pNSCapNext->cvp_data.vfx, lpvcd, sizeof(VIDEOFORMATEX)); pCCThisCap->ClientType = H245_CLIENT_VID_NONSTD; pCCThisCap->DataType = H245_DATA_VIDEO; pCCThisCap->Dir = (pDecodeDetails->bSendEnabled && bPublicizeTXCaps) ? H245_CAPDIR_LCLRXTX :H245_CAPDIR_LCLRX; // LOOKLOOK use the index of the cap entry as the ID // The ID is already preset in local formats by AddCapabilityBase() // pCCThisCap->CapId = (USHORT)IndexToId(u); pCCThisCap->CapId = pDecodeDetails->H245Cap.CapId; // all nonstandard identifier fields are unsigned short // two possibilities for choice are "h221NonStandard_chosen" and "object_chosen" pCCThisCap->Cap.H245Vid_NONSTD.nonStandardIdentifier.choice = h221NonStandard_chosen; // NOTE: there is some question about the correct byte order // of the codes in the h221NonStandard structure pCCThisCap->Cap.H245Vid_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35CountryCode = USA_H221_COUNTRY_CODE; pCCThisCap->Cap.H245Vid_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35Extension = USA_H221_COUNTRY_EXTENSION; pCCThisCap->Cap.H245Vid_NONSTD.nonStandardIdentifier.u.h221NonStandard.manufacturerCode = MICROSOFT_H_221_MFG_CODE; // set size of buffer pCCThisCap->Cap.H245Vid_NONSTD.data.length = sizeof(NSC_VIDEO_CAPABILITY) - BMIH_SLOP_BYTES; pCCThisCap->Cap.H245Vid_NONSTD.data.value = (BYTE *)pNSCapNext; // point to nonstandard stuff // pNSCapNext is now referenced by the pTermCapList and will // be cleaned up via DeleteCapList(). Null the ptr so that error cleanup // won't try redundant cleanup. pNSCapNext = NULL; } else { // allocate for this one capability pCCThisCap = (PCC_TERMCAP)MemAlloc(sizeof(CC_TERMCAP)); if(!pCCThisCap) { hr = CAPS_E_NOMEM; goto ERROR_OUT; } pCCThisCap->ClientType = (H245_CLIENT_T)pDecodeDetails->H245Cap.ClientType; pCCThisCap->DataType = H245_DATA_VIDEO; pCCThisCap->Dir = H245_CAPDIR_LCLRX; // should this be H245_CAPDIR_LCLRX for receive caps? pCCThisCap->CapId = pDecodeDetails->H245Cap.CapId; pVidCapInfo=&pDecodeDetails->video_params; switch (pCCThisCap->ClientType ) { case H245_CLIENT_VID_H263: #pragma message ("Collapse H.263 formats") // refer to the hack that sets H245Vid_H263 parameters // when formats are enumerated. if that was always done right, then // all that needs to happen here is collapsing // This is where the formats need to collapse. H.263 probably // should not be collapsed into 1 format. Given M specific local // formats, collapse into N. format=get_format (pVidCapInfo->biWidth,pVidCapInfo->biHeight); switch (format) { case SQCIF: { pCCThisCap->Cap.H245Vid_H263.bit_mask =H263VideoCapability_sqcifMPI_present; //MPI minimum interval in units of 1/29.97sec so 30/ (frames/sec) is reasonable pCCThisCap->Cap.H245Vid_H263.sqcifMPI = max (1,pDecodeDetails->nonstd_params.MPI); //30/pVidCapInfo->uSamplesPerSec; pCCThisCap->Cap.H245Vid_H263.H263VdCpblty_qcifMPI =0; pCCThisCap->Cap.H245Vid_H263.H263VdCpblty_cifMPI =0; break; } case QCIF: { pCCThisCap->Cap.H245Vid_H263.bit_mask =H263VideoCapability_qcifMPI_present; pCCThisCap->Cap.H245Vid_H263.sqcifMPI = 0; pCCThisCap->Cap.H245Vid_H263.H263VdCpblty_qcifMPI =max (1,pDecodeDetails->nonstd_params.MPI);//30/pVidCapInfo->uSamplesPerSec; ;; pCCThisCap->Cap.H245Vid_H263.H263VdCpblty_cifMPI =0; break; } case CIF: { pCCThisCap->Cap.H245Vid_H263.bit_mask =H263VideoCapability_cifMPI_present; pCCThisCap->Cap.H245Vid_H263.sqcifMPI = 0; pCCThisCap->Cap.H245Vid_H263.H263VdCpblty_qcifMPI =0; pCCThisCap->Cap.H245Vid_H263.H263VdCpblty_cifMPI = max (1,pDecodeDetails->nonstd_params.MPI);//30/pVidCapInfo->uSamplesPerSec; break; } default: break; } pCCThisCap->Cap.H245Vid_H263.cif4MPI =0; pCCThisCap->Cap.H245Vid_H263.cif16MPI =0; pCCThisCap->Cap.H245Vid_H263.maxBitRate = pDecodeDetails->nonstd_params.maxBitRate; pCCThisCap->Cap.H245Vid_H263.unrestrictedVector = FALSE; pCCThisCap->Cap.H245Vid_H263.arithmeticCoding = FALSE; pCCThisCap->Cap.H245Vid_H263.advancedPrediction = FALSE; pCCThisCap->Cap.H245Vid_H263.pbFrames = FALSE; pCCThisCap->Cap.H245Vid_H263.tmprlSptlTrdOffCpblty = (ASN1bool_t)bPublicizeTSTradeoff; pCCThisCap->Cap.H245Vid_H263.hrd_B = 0; pCCThisCap->Cap.H245Vid_H263.bppMaxKb = pDecodeDetails->nonstd_params.maxBPP; /* Optional, and not supported pCCThisCap->Cap.H245Vid_H263.slowQcifMPI =0; pCCThisCap->Cap.H245Vid_H263.slowSqcifMPI =0; pCCThisCap->Cap.H245Vid_H263.slowCifMPI =0; pCCThisCap->Cap.H245Vid_H263.slowCif4MPI =0; pCCThisCap->Cap.H245Vid_H263.slowCif16MPI =0; */ pCCThisCap->Cap.H245Vid_H263.H263VCy_errrCmpnstn = TRUE; break; case H245_CLIENT_VID_H261: #pragma message ("Collapse H.261 formats") // refer to the hack that sets H245Vid_H261 parameters // when formats are enumerated. if that was always done right, then // all that needs to happen here is collapsing // This is where the formats need to collapse. H.261 probably // should not be collapsed into 1 format. Given M specific local // formats, collapse into N. format=get_format (pVidCapInfo->biWidth,pVidCapInfo->biHeight); switch (format) { case QCIF: { pCCThisCap->Cap.H245Vid_H261.bit_mask =H261VdCpblty_qcifMPI_present; pCCThisCap->Cap.H245Vid_H261.H261VdCpblty_qcifMPI =max (1,min(4,pDecodeDetails->nonstd_params.MPI));//30/pVidCapInfo->uSamplesPerSec; ;; pCCThisCap->Cap.H245Vid_H261.H261VdCpblty_cifMPI =0; break; } case CIF: { pCCThisCap->Cap.H245Vid_H261.bit_mask =H261VdCpblty_cifMPI_present; pCCThisCap->Cap.H245Vid_H261.H261VdCpblty_qcifMPI =0; pCCThisCap->Cap.H245Vid_H261.H261VdCpblty_cifMPI =max (1,min(4,pDecodeDetails->nonstd_params.MPI));//30/pVidCapInfo->uSamplesPerSec; break; } default: break; } pCCThisCap->Cap.H245Vid_H261.maxBitRate = (ASN1uint16_t)pDecodeDetails->nonstd_params.maxBitRate; pCCThisCap->Cap.H245Vid_H261.tmprlSptlTrdOffCpblty = (ASN1bool_t)bPublicizeTSTradeoff; pCCThisCap->Cap.H245Vid_H261.stillImageTransmission = FALSE; break; default: case H245_CLIENT_VID_NONSTD: break; } } pDecodeDetails++; *ppCCThisTermCap++ = pCCThisCap;// add ptr to this capability to the array pTermCapList->wLength++; // count this entry // pCCThisCap is now referenced by the pTermCapList and will // be cleaned up via DeleteCapList(). Null the ptr so that error cleanup // won't try redundant cleanup. pCCThisCap = NULL; } *ppCapBuf = pTermCapList; return hr; ERROR_OUT: if(pTermCapList) { DeleteCapList(pTermCapList); } if(pCCThisCap) MemFree(pCCThisCap); if(pNSCapNext) MemFree(pNSCapNext); return hr; } HRESULT CMsivCapability::DeleteCapList(LPVOID pCapBuf) { UINT u; PCC_TERMCAPLIST pTermCapList = (PCC_TERMCAPLIST)pCapBuf; PCC_TERMCAP pCCThisCap; PNSC_VIDEO_CAPABILITY pNSCap; if(!pTermCapList) { return CAPS_E_INVALID_PARAM; } if(pTermCapList->pTermCapArray) { while(pTermCapList->wLength--) { pCCThisCap = *(pTermCapList->pTermCapArray + pTermCapList->wLength); if(pCCThisCap) { if(pCCThisCap->ClientType == H245_CLIENT_VID_NONSTD) { if(pCCThisCap->Cap.H245Vid_NONSTD.data.value) { MemFree(pCCThisCap->Cap.H245Vid_NONSTD.data.value); } } MemFree(pCCThisCap); } } MemFree(pTermCapList->pTermCapArray); } MemFree(pTermCapList); return hrSuccess; } BOOL CMsivCapability::IsCapabilityRecognized(PCC_TERMCAP pCCThisCap) { FX_ENTRY ("CMsivCapability::IsCapabilityRecognized"); if(pCCThisCap->DataType != H245_DATA_VIDEO) return FALSE; if(pCCThisCap->ClientType == H245_CLIENT_VID_NONSTD) { // do we recognize this? if(pCCThisCap->Cap.H245Vid_NONSTD.nonStandardIdentifier.choice == h221NonStandard_chosen) { if((pCCThisCap->Cap.H245Vid_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35CountryCode == USA_H221_COUNTRY_CODE) && (pCCThisCap->Cap.H245Vid_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35Extension == USA_H221_COUNTRY_EXTENSION) && (pCCThisCap->Cap.H245Vid_NONSTD.nonStandardIdentifier.u.h221NonStandard.manufacturerCode == MICROSOFT_H_221_MFG_CODE)) { // ok, this is ours so far. Now what data type is contained therein? // welllll, lets keep a copy of this regardless ????. If we can't understand // future versions of ourselves, then what??? return TRUE; } else { // unrecognized nonstandard capability ERRORMESSAGE(("%s:unrecognized nonstd capability\r\n",_fx_)); #ifdef DEBUG VOID DumpNonstdParameters(PCC_TERMCAP , PCC_TERMCAP ); DumpNonstdParameters(NULL, pCCThisCap); #endif return FALSE; } } } return TRUE; } // the intent is to keep a copy of the channel parameters used to open a send channel // that the remote end can decode. VIDEO_FORMAT_ID CMsivCapability::AddRemoteDecodeFormat(PCC_TERMCAP pCCThisCap) { FX_ENTRY ("CMsivCapability::AddRemoteDecodeFormat"); VIDCAP_DETAILS vidcapdetails = {VIDEO_FORMAT_UNKNOWN,NONSTD_VID_TERMCAP, STD_VID_PARAMS, {RTP_DYNAMIC_MIN+1, 0, 30, 7680, Small, 0, 0},0, TRUE, TRUE, 1, 245760*8,245760*8,10,10,0,0,NULL,0,NULL,""}; VIDCAP_DETAILS *pTemp; LPVOID lpData = NULL; UINT uSize = 0; if(!pCCThisCap) { return INVALID_VIDEO_FORMAT; } // check room if(uRemoteDecodeFormatCapacity <= uNumRemoteDecodeFormats) { // get more mem, realloc memory by CAP_CHUNK_SIZE for pRemoteDecodeFormats pTemp = (VIDCAP_DETAILS *)MEMALLOC((uNumRemoteDecodeFormats + CAP_CHUNK_SIZE)*sizeof(VIDCAP_DETAILS)); if(!pTemp) goto ERROR_EXIT; // remember how much capacity we now have uRemoteDecodeFormatCapacity = uNumRemoteDecodeFormats + CAP_CHUNK_SIZE; #ifdef DEBUG if((uNumRemoteDecodeFormats && !pRemoteDecodeFormats) || (!uNumRemoteDecodeFormats && pRemoteDecodeFormats)) { ERRORMESSAGE(("%s:leak! uNumRemoteDecodeFormats:0x%08lX, pRemoteDecodeFormats:0x%08lX\r\n", _fx_, uNumRemoteDecodeFormats,pRemoteDecodeFormats)); } #endif // copy old stuff, discard old mem if(uNumRemoteDecodeFormats && pRemoteDecodeFormats) { memcpy(pTemp, pRemoteDecodeFormats, uNumRemoteDecodeFormats*sizeof(AUDCAP_DETAILS)); MEMFREE(pRemoteDecodeFormats); } pRemoteDecodeFormats = pTemp; } // pTemp is where the stuff is cached pTemp = pRemoteDecodeFormats+uNumRemoteDecodeFormats; // fixup the capability structure being added. First thing: initialize defaults memcpy(pTemp, &vidcapdetails, sizeof(VIDCAP_DETAILS)); // next, the H245 parameters memcpy(&pTemp->H245Cap, pCCThisCap, sizeof(pTemp->H245Cap)); // Note: if nonstandard data exists, the nonstd pointers need to be fixed up if(pCCThisCap->ClientType == H245_CLIENT_VID_NONSTD) { // do we recognize this? if(pCCThisCap->Cap.H245Vid_NONSTD.nonStandardIdentifier.choice == h221NonStandard_chosen) { if((pCCThisCap->Cap.H245Vid_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35CountryCode == USA_H221_COUNTRY_CODE) && (pCCThisCap->Cap.H245Vid_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35Extension == USA_H221_COUNTRY_EXTENSION) && (pCCThisCap->Cap.H245Vid_NONSTD.nonStandardIdentifier.u.h221NonStandard.manufacturerCode == MICROSOFT_H_221_MFG_CODE)) { // ok, this is ours so far. Now what data type is contained therein? // welllll, lets keep a copy of this regardless ????. If we can't understand // future versions of ourselves, then what??? uSize = pCCThisCap->Cap.H245Vid_NONSTD.data.length; lpData = pCCThisCap->Cap.H245Vid_NONSTD.data.value; } } } // this is not really necessary to set RTP payload type of what is received - it should // be obvious. else if (pCCThisCap->ClientType == H245_CLIENT_VID_H263 ) { pTemp->video_params.RTPPayload = RTP_PAYLOAD_H263; } else if(pCCThisCap->ClientType == H245_CLIENT_VID_H261) { pTemp->video_params.RTPPayload = RTP_PAYLOAD_H261; } pTemp->uLocalDetailsSize = 0; // we're not keeping another copy of local encode details pTemp->lpLocalFormatDetails =0; // we're not keeping another copy of local encode details pTemp->uRemoteDetailsSize = 0; // clear this now if(uSize && lpData) { pTemp->H245Cap.Cap.H245Vid_NONSTD.data.length = uSize; pTemp->H245Cap.Cap.H245Vid_NONSTD.data.value = (unsigned char *)lpData; pTemp->lpRemoteFormatDetails = MEMALLOC(uSize); if(pTemp->lpRemoteFormatDetails) { memcpy(pTemp->lpRemoteFormatDetails, lpData, uSize); pTemp->uRemoteDetailsSize = uSize; } #ifdef DEBUG else { ERRORMESSAGE(("%s:allocation failed!\r\n",_fx_)); } #endif } else { pTemp->lpRemoteFormatDetails = NULL; pTemp->uRemoteDetailsSize =0; } uNumRemoteDecodeFormats++; // use the index as the ID return (uNumRemoteDecodeFormats-1); ERROR_EXIT: return INVALID_VIDEO_FORMAT; } VOID CMsivCapability::FlushRemoteCaps() { if(pRemoteDecodeFormats) { MEMFREE(pRemoteDecodeFormats); pRemoteDecodeFormats = NULL; uNumRemoteDecodeFormats = 0; uRemoteDecodeFormatCapacity = 0; } } HRESULT CMsivCapability::AddRemoteDecodeCaps(PCC_TERMCAPLIST pTermCapList) { FX_ENTRY ("CMsivCapability::AddRemoteDecodeCaps"); HRESULT hr = hrSuccess; PPCC_TERMCAP ppCCThisCap; PCC_TERMCAP pCCThisCap; WORD wNumCaps; //ERRORMESSAGE(("%s,\r\n", _fx_)); if(!pTermCapList) // additional capability descriptors may be added { // at any time return CAPS_E_INVALID_PARAM; } // cleanup old term caps if term caps are being addded and old caps exist FlushRemoteCaps(); wNumCaps = pTermCapList->wLength; ppCCThisCap = pTermCapList->pTermCapArray; /* CC_TERMCAPLIST TERMCAPINFO CC_TERMCAP pTermCapList-> { wLength pTermCapInfo--->pTermCap----------->{single capability.....} } pTermCap----------->{single capability.} pTermCap----------->{single capability...} */ while(wNumCaps--) { if(!(pCCThisCap = *ppCCThisCap++)) { ERRORMESSAGE(("%s:null pTermCap, 0x%04x of 0x%04x\r\n", _fx_, pTermCapList->wLength - wNumCaps, pTermCapList->wLength)); continue; } if(!IsCapabilityRecognized(pCCThisCap)) { continue; } AddRemoteDecodeFormat(pCCThisCap); } return hr; } // Given the ID of a local format, gets the channel parameters that are sent to the // remote end as part of the capability exchange. This function is not used by the // capability exchange code (because it sends more than just these parameters). // However, this is useful information by itself - it can be used for validating the // parameters of channel open requests against the expected parameters HRESULT CMsivCapability::GetPublicDecodeParams(LPVOID pBufOut, UINT uBufSize, VIDEO_FORMAT_ID id) { UINT uIndex = IDToIndex(id); // validate input if(!pBufOut|| (uIndex >= (UINT)uNumLocalFormats)) { return CAPS_E_INVALID_PARAM; } if(uBufSize < sizeof(CC_TERMCAP)) { return CAPS_E_BUFFER_TOO_SMALL; } memcpy(pBufOut, &((pLocalFormats + uIndex)->H245Cap), sizeof(CC_TERMCAP)); return hrSuccess; } HRESULT CMsivCapability::SetAudioPacketDuration(UINT uPacketDuration) { return CAPS_E_INVALID_PARAM; } // Given the IDs of "matching" local and remote formats, gets the preferred channel parameters // that will be used in requests to open a channel for sending to the remote. HRESULT CMsivCapability::GetEncodeParams(LPVOID pBufOut, UINT uBufSize,LPVOID pLocalParams, UINT uSizeLocal, VIDEO_FORMAT_ID idRemote, VIDEO_FORMAT_ID idLocal) { UINT uLocalIndex = IDToIndex(idLocal); VIDCAP_DETAILS *pLocalDetails = pLocalFormats + uLocalIndex; VIDCAP_DETAILS *pFmtTheirs; VIDEO_CHANNEL_PARAMETERS local_params; UINT u; PCC_TERMCAP pTermCap = (PCC_TERMCAP)pBufOut; // validate input // AddCapabilityBase adds to the ID below. Make sure we're checking Video Formats if(!pBufOut) { return CAPS_E_INVALID_PARAM; } if(uBufSize < sizeof(CC_TERMCAP)) { return CAPS_E_BUFFER_TOO_SMALL; } if(!pLocalParams|| uSizeLocal < sizeof(VIDEO_CHANNEL_PARAMETERS) ||(uLocalIndex >= (UINT)uNumLocalFormats)) { return CAPS_E_INVALID_PARAM; } pFmtTheirs = pRemoteDecodeFormats; // start at the beginning of the remote formats for(u=0; uH245Cap.CapId == idRemote) { // copy CC_TERMCAP struct. Any data referenced by CC_TERMCAP now has // two references to it. i.e. pTermCap->extrablah is the same // location as pFmtTheirs->extrablah memcpy(pBufOut, &(pFmtTheirs->H245Cap), sizeof(CC_TERMCAP)); break; } pFmtTheirs++; // next entry in receiver's caps } // check for an unfound format if(u >= uNumRemoteDecodeFormats) goto ERROR_EXIT; // select channel parameters if appropriate. The audio formats that have variable parameters // are : #pragma message ("Are H.26? variable parameter formats?") // H245_CAP_H261 H245Vid_H261; // H245_CAP_H263 H245Vid_H263; // and of course all nonstandard formats // Select parameters based on local capability info if(pTermCap->ClientType == H245_CLIENT_VID_H263) { unsigned short bit_mask; // select frames per packet based on minimum latency value that is acceptable #define H263_QCIF 0x4000 #define H263_MAXBP 0x0200 //H263_QCIF | H263_MAXBP; pTermCap->Cap.H245Vid_H263.bit_mask= H263_MAXBP | pLocalDetails->H245Cap.Cap.H245Vid_H263.bit_mask; local_params.ns_params.maxBitRate = pTermCap->Cap.H245Vid_H263.maxBitRate = min (pLocalDetails->nonstd_params.maxBitRate , pFmtTheirs->H245Cap.Cap.H245Vid_H263.maxBitRate); local_params.ns_params.maxBPP = pTermCap->Cap.H245Vid_H263.bppMaxKb = min (pLocalDetails->nonstd_params.maxBPP, pFmtTheirs->H245Cap.Cap.H245Vid_H263.bppMaxKb); // we (the local end) need to know that actual MPI is going to be used! // like everywhere else in this module, the assumption is that local H.263 capabilities are // fanned out with one local cap entry per frame size. // MPI minimum interval in units of 1/29.97sec so take the longest interval // there is no pretty way to do this bit_mask = pLocalDetails->H245Cap.Cap.H245Vid_H263.bit_mask; if(bit_mask & H263VideoCapability_sqcifMPI_present) { local_params.ns_params.MPI = pTermCap->Cap.H245Vid_H263.sqcifMPI = max(pLocalDetails->nonstd_params.MPI, pTermCap->Cap.H245Vid_H263.sqcifMPI); } else if (bit_mask & H263VideoCapability_qcifMPI_present) { local_params.ns_params.MPI = pTermCap->Cap.H245Vid_H263.H263VdCpblty_qcifMPI = max(pLocalDetails->nonstd_params.MPI, pTermCap->Cap.H245Vid_H263.H263VdCpblty_qcifMPI); } else if (bit_mask & H263VideoCapability_cifMPI_present) { local_params.ns_params.MPI = pTermCap->Cap.H245Vid_H263.H263VdCpblty_cifMPI = max(pLocalDetails->nonstd_params.MPI, pTermCap->Cap.H245Vid_H263.H263VdCpblty_cifMPI); } else if (bit_mask & H263VideoCapability_cif4MPI_present) { local_params.ns_params.MPI = pTermCap->Cap.H245Vid_H263.cif4MPI = max(pLocalDetails->H245Cap.Cap.H245Vid_H263.cif4MPI, pTermCap->Cap.H245Vid_H263.cif4MPI); } else if (bit_mask & H263VideoCapability_cif16MPI_present) { local_params.ns_params.MPI = pTermCap->Cap.H245Vid_H263.cif16MPI = max(pLocalDetails->nonstd_params.MPI, pTermCap->Cap.H245Vid_H263.cif16MPI); } // else // impossible. Doom, as MikeG and JonT would say } else if(pTermCap->ClientType == H245_CLIENT_VID_H261) { unsigned short bit_mask; // select frames per packet based on minimum latency value that is acceptable pTermCap->Cap.H245Vid_H261.bit_mask= pLocalDetails->H245Cap.Cap.H245Vid_H261.bit_mask; local_params.ns_params.maxBitRate = pTermCap->Cap.H245Vid_H261.maxBitRate = min (pLocalDetails->nonstd_params.maxBitRate , pFmtTheirs->H245Cap.Cap.H245Vid_H261.maxBitRate); // we (the local end) need to know that actual MPI is going to be used! // like everywhere else in this module, the assumption is that local H.261 capabilities are // fanned out with one local cap entry per frame size. // MPI minimum interval in units of 1/29.97sec so take the longest interval // there is no pretty way to do this bit_mask = pLocalDetails->H245Cap.Cap.H245Vid_H261.bit_mask; if (bit_mask & H261VdCpblty_qcifMPI_present) { local_params.ns_params.MPI = pTermCap->Cap.H245Vid_H261.H261VdCpblty_qcifMPI = max(pLocalDetails->nonstd_params.MPI, pTermCap->Cap.H245Vid_H261.H261VdCpblty_qcifMPI); } else if (bit_mask & H261VdCpblty_cifMPI_present) { local_params.ns_params.MPI = pTermCap->Cap.H245Vid_H261.H261VdCpblty_cifMPI = max(pLocalDetails->nonstd_params.MPI, pTermCap->Cap.H245Vid_H261.H261VdCpblty_cifMPI); } // else // impossible. Doom, as MikeG and JonT would say } else if (pTermCap->ClientType == H245_CLIENT_VID_NONSTD) { // NOT YET IMPLEMENTED!!!!. even the nonstandard parameters need to be fixed // up here based on mutual maxes and mins memcpy(&local_params.ns_params, &pLocalDetails->nonstd_params, sizeof(NSC_CHANNEL_VIDEO_PARAMETERS)); } local_params.RTP_Payload = pLocalDetails->video_params.RTPPayload; //Fixup local memcpy(pLocalParams, &local_params, sizeof(VIDEO_CHANNEL_PARAMETERS)); return hrSuccess; ERROR_EXIT: return CAPS_E_INVALID_PARAM; } BOOL NonStandardCapsCompareV(VIDCAP_DETAILS *pFmtMine, PNSC_VIDEO_CAPABILITY pCap2, UINT uSize2) { PVIDEOFORMATEX lpvcd; if(!pFmtMine || !pCap2) return FALSE; if(!(lpvcd = (PVIDEOFORMATEX)pFmtMine->lpLocalFormatDetails)) return FALSE; if(pCap2->cvp_type == NSC_VCM_VIDEOFORMATEX) { // check sizes first if(lpvcd->bih.biSize != pCap2->cvp_data.vfx.bih.biSize) { return FALSE; } // compare structures, including extra bytes if(memcmp(lpvcd, &pCap2->cvp_data.vfx, sizeof(VIDEOFORMATEX) - BMIH_SLOP_BYTES)==0) { return TRUE; } } else if(pCap2->cvp_type == NSC_VCMABBREV) { if((LOWORD(pCap2->cvp_data.vcm_brief.dwFormatTag) == lpvcd->dwFormatTag) && (pCap2->cvp_data.vcm_brief.dwSamplesPerSec == lpvcd->nSamplesPerSec) && (LOWORD(pCap2->cvp_data.vcm_brief.dwBitsPerSample) == lpvcd->wBitsPerSample)) { return TRUE; } } return FALSE; } BOOL HasNonStandardCapsTS(VIDCAP_DETAILS *pFmtMine, PNSC_VIDEO_CAPABILITY pCap2) { PVIDEOFORMATEX lpvcd; if(!pFmtMine || !pCap2) return FALSE; if(!(lpvcd = (PVIDEOFORMATEX)pFmtMine->lpLocalFormatDetails)) return FALSE; if(pCap2->cvp_type == NSC_VCM_VIDEOFORMATEX) if(lpvcd->dwSupportTSTradeOff && pCap2->cvp_data.vfx.dwSupportTSTradeOff) return TRUE; return FALSE; } HRESULT CMsivCapability::ResolveToLocalFormat(MEDIA_FORMAT_ID FormatIDLocal, MEDIA_FORMAT_ID * pFormatIDRemote) { VIDCAP_DETAILS *pFmtLocal; VIDCAP_DETAILS *pFmtRemote; UINT format_mask; UINT uIndex = IDToIndex(FormatIDLocal); UINT i; if(!pFormatIDRemote || (FormatIDLocal == INVALID_MEDIA_FORMAT) || (uIndex >= (UINT)uNumLocalFormats)) { return CAPS_E_INVALID_PARAM; } pFmtLocal = pLocalFormats + uIndex; pFmtRemote = pRemoteDecodeFormats; // start at the beginning of the remote formats for(i=0; ibSendEnabled) continue; // compare capabilities - start by comparing the format tag. a.k.a. "ClientType" in H.245 land if(pFmtLocal->H245Cap.ClientType == pFmtRemote->H245Cap.ClientType) { // if this is a nonstandard cap, compare nonstandard parameters if(pFmtLocal->H245Cap.ClientType == H245_CLIENT_VID_NONSTD) { if(NonStandardCapsCompareV(pFmtLocal, (PNSC_VIDEO_CAPABILITY)pFmtRemote->H245Cap.Cap.H245Vid_NONSTD.data.value, pFmtRemote->H245Cap.Cap.H245Vid_NONSTD.data.length)) { goto RESOLVED_EXIT; } } else // compare standard parameters, if any { // well, so far, there aren't any parameters that are significant enough // to affect the match/no match decision if (pFmtLocal->H245Cap.ClientType == H245_CLIENT_VID_H263) { format_mask= H263VideoCapability_sqcifMPI_present | H263VideoCapability_qcifMPI_present | H263VideoCapability_cifMPI_present | H263VideoCapability_cif4MPI_present | H263VideoCapability_cif16MPI_present; if ((pFmtRemote->H245Cap.Cap.H245Vid_H263.bit_mask & format_mask) & (pFmtLocal->H245Cap.Cap.H245Vid_H263.bit_mask & format_mask)) { // compatible basic format goto RESOLVED_EXIT; } } else if (pFmtLocal->H245Cap.ClientType == H245_CLIENT_VID_H261) { format_mask= H261VdCpblty_qcifMPI_present | H261VdCpblty_cifMPI_present; if ((pFmtRemote->H245Cap.Cap.H245Vid_H261.bit_mask & format_mask) & (pFmtLocal->H245Cap.Cap.H245Vid_H261.bit_mask & format_mask)) { // compatible basic format goto RESOLVED_EXIT; } } else { //Some other standard format goto RESOLVED_EXIT; } } } pFmtRemote++; // next entry in remote caps } return CAPS_E_NOMATCH; RESOLVED_EXIT: // Match! // return ID of remote decoding (receive fmt) caps that match our // send caps *pFormatIDRemote = pFmtRemote->H245Cap.CapId; return hrSuccess; } // resolve using currently cached local and remote formats HRESULT CMsivCapability::ResolveEncodeFormat( VIDEO_FORMAT_ID *pIDEncodeOut, VIDEO_FORMAT_ID *pIDRemoteDecode) { UINT i,j=0,format_mask; VIDCAP_DETAILS *pFmtMine = pLocalFormats; VIDCAP_DETAILS *pFmtTheirs; if(!pIDEncodeOut || !pIDRemoteDecode) { return CAPS_E_INVALID_PARAM; } if(!uNumLocalFormats || !pLocalFormats) { *pIDEncodeOut = *pIDRemoteDecode = INVALID_VIDEO_FORMAT; return CAPS_E_NOCAPS; } if(!pRemoteDecodeFormats || !uNumRemoteDecodeFormats) { *pIDEncodeOut = *pIDRemoteDecode = INVALID_VIDEO_FORMAT; return CAPS_E_NOMATCH; } // decide how to encode. my caps are ordered by my preference according to // the contents of IDsByRank[] //If given a salt, find the position and add it if (*pIDEncodeOut != INVALID_MEDIA_FORMAT) { UINT uIndex = IDToIndex(*pIDEncodeOut); if (uIndex > uNumLocalFormats) { return CAPS_W_NO_MORE_FORMATS; } for(i=0; ibSendEnabled) continue; pFmtTheirs = pRemoteDecodeFormats; // start at the beginning of the remote formats for(j=0; jH245Cap.ClientType == pFmtTheirs->H245Cap.ClientType) { // if this is a nonstandard cap, compare nonstandard parameters if(pFmtMine->H245Cap.ClientType == H245_CLIENT_VID_NONSTD) { if(NonStandardCapsCompareV(pFmtMine, (PNSC_VIDEO_CAPABILITY)pFmtTheirs->H245Cap.Cap.H245Vid_NONSTD.data.value, pFmtTheirs->H245Cap.Cap.H245Vid_NONSTD.data.length)) { goto RESOLVED_EXIT; } } else // compare standard parameters, if any { // well, so far, there aren't any parameters that are significant enough // to affect the match/no match decision if (pFmtMine->H245Cap.ClientType == H245_CLIENT_VID_H263) { format_mask= H263VideoCapability_sqcifMPI_present| H263VideoCapability_qcifMPI_present |H263VdCpblty_cifMPI_present |H263VideoCapability_cif4MPI_present |H263VideoCapability_cif16MPI_present; if ((pFmtTheirs->H245Cap.Cap.H245Vid_H263.bit_mask & format_mask) & (pFmtMine->H245Cap.Cap.H245Vid_H263.bit_mask & format_mask)) { // compatible basic format goto RESOLVED_EXIT; } } else if (pFmtMine->H245Cap.ClientType == H245_CLIENT_VID_H261) { format_mask= H261VdCpblty_qcifMPI_present | H261VdCpblty_cifMPI_present; if ((pFmtTheirs->H245Cap.Cap.H245Vid_H261.bit_mask & format_mask) & (pFmtMine->H245Cap.Cap.H245Vid_H261.bit_mask & format_mask)) { // compatible basic format goto RESOLVED_EXIT; } } else { //Some other standard format goto RESOLVED_EXIT; } } } pFmtTheirs++; // next entry in receiver's caps } } return CAPS_E_NOMATCH; RESOLVED_EXIT: // Match! // return ID of our encoding (sending fmt) caps that match *pIDEncodeOut = pFmtMine->H245Cap.CapId; // return ID of remote decoding (receive fmt) caps that match our // send caps *pIDRemoteDecode = pFmtTheirs->H245Cap.CapId; return hrSuccess; } HRESULT CMsivCapability::GetDecodeParams(PCC_RX_CHANNEL_REQUEST_CALLBACK_PARAMS pChannelParams, VIDEO_FORMAT_ID * pFormatID, LPVOID lpvBuf, UINT uBufSize) { UINT i,j=0; VIDCAP_DETAILS *pFmtMine = pLocalFormats; VIDCAP_DETAILS *pFmtTheirs = pRemoteDecodeFormats; VIDEO_CHANNEL_PARAMETERS local_params; PNSC_CHANNEL_VIDEO_PARAMETERS pNSCap = &local_params.ns_params; PCC_TERMCAP pCapability; if(!pChannelParams || !(pCapability = pChannelParams->pChannelCapability) || !pFormatID || !lpvBuf || (uBufSize < sizeof(VIDEO_CHANNEL_PARAMETERS))) { return CAPS_E_INVALID_PARAM; } if(!uNumLocalFormats || !pLocalFormats) { return CAPS_E_NOCAPS; } local_params.TS_Tradeoff = FALSE; // initialize TS tradeoff for(i=0; iH245Cap.ClientType == pCapability->ClientType) { // if this is a nonstandard cap, compare nonstandard parameters if(pFmtMine->H245Cap.ClientType == H245_CLIENT_VID_NONSTD) { if(NonStandardCapsCompareV(pFmtMine, (PNSC_VIDEO_CAPABILITY)pCapability->Cap.H245Vid_NONSTD.data.value, pCapability->Cap.H245Vid_NONSTD.data.length)) { #pragma message ("someday may need need fixup of nonstd params") // for now, the remote & local nonstandard params are what we want // and the remote's version of NSC_CHANNEL_VIDEO_PARAMETERS will // be copied out pNSCap = (PNSC_CHANNEL_VIDEO_PARAMETERS) &((PNSC_VIDEO_CAPABILITY)pCapability->Cap.H245Vid_NONSTD.data.value)->cvp_params; // Does this format support temporal/spatial tradeoff if(HasNonStandardCapsTS(pFmtMine, (PNSC_VIDEO_CAPABILITY)pCapability->Cap.H245Vid_NONSTD.data.value)) local_params.TS_Tradeoff = TRUE; else local_params.TS_Tradeoff = FALSE; goto RESOLVED_EXIT; } } else // compare standard parameters, if any { switch (pFmtMine->H245Cap.ClientType) { unsigned short bit_mask, format_mask, usMyMPI, usTheirMPI; case H245_CLIENT_VID_H263: // like everywhere else in this module, the assumption is that // local H.263 capabilities are fanned out with one local cap entry // per frame size. format_mask= H263VideoCapability_sqcifMPI_present | H263VideoCapability_qcifMPI_present | H263VideoCapability_cifMPI_present | H263VideoCapability_cif4MPI_present | H263VideoCapability_cif16MPI_present; // bail out if no match or nonexistent frame size if (!((pCapability->Cap.H245Vid_H263.bit_mask & format_mask) & (pFmtMine->H245Cap.Cap.H245Vid_H263.bit_mask & format_mask))) continue; // get the maximum bitrate local_params.ns_params.maxBitRate = min(pFmtMine->H245Cap.Cap.H245Vid_H263.maxBitRate, pCapability->Cap.H245Vid_H263.maxBitRate); local_params.ns_params.maxBPP = min (pFmtMine->H245Cap.Cap.H245Vid_H263.bppMaxKb , pCapability->Cap.H245Vid_H263.bppMaxKb); // FIND THE MAXIMUM MPI!!!!. (minimum frame rate) // there is no pretty way to do this bit_mask = pFmtMine->H245Cap.Cap.H245Vid_H263.bit_mask; if(bit_mask & H263VideoCapability_sqcifMPI_present) { local_params.ns_params.MPI = max(pFmtMine->H245Cap.Cap.H245Vid_H263.sqcifMPI, pCapability->Cap.H245Vid_H263.sqcifMPI); } else if (bit_mask & H263VideoCapability_qcifMPI_present) { local_params.ns_params.MPI = max(pFmtMine->H245Cap.Cap.H245Vid_H263.H263VdCpblty_qcifMPI, pCapability->Cap.H245Vid_H263.H263VdCpblty_qcifMPI); } else if (bit_mask & H263VideoCapability_cifMPI_present) { local_params.ns_params.MPI = max(pFmtMine->H245Cap.Cap.H245Vid_H263.H263VdCpblty_cifMPI, pCapability->Cap.H245Vid_H263.H263VdCpblty_cifMPI); } else if (bit_mask & H263VideoCapability_cif4MPI_present) { local_params.ns_params.MPI = max(pFmtMine->H245Cap.Cap.H245Vid_H263.cif4MPI, pCapability->Cap.H245Vid_H263.cif4MPI); } else if (bit_mask & H263VideoCapability_cif16MPI_present) { local_params.ns_params.MPI = max(pFmtMine->H245Cap.Cap.H245Vid_H263.cif16MPI, pCapability->Cap.H245Vid_H263.cif16MPI); } else // impossible. Doom, as MikeG and JonT would say continue; // Fallout (And the format is found!) // And one more special thing: find out if the other end // advertised Temporal/Spatial tradeoff in it's send capabilities. // First try the obvious. Technically, it only makes sense for // transmit capabilities, but if the channel params have it, then // the other end must have the capability if(pCapability->Cap.H245Vid_H263.tmprlSptlTrdOffCpblty) { local_params.TS_Tradeoff = TRUE; } else { // Search for a H.263 SEND capability that has the T/S tradoff set for(j=0; jH245Cap.ClientType == H245_CLIENT_VID_H263) // exclude RX capabilities && (pFmtTheirs->H245Cap.Dir != H245_CAPDIR_LCLRX) && (pFmtTheirs->H245Cap.Dir != H245_CAPDIR_RMTRX)) { if ((pFmtTheirs->H245Cap.Cap.H245Vid_H263.bit_mask & format_mask) & (pFmtMine->H245Cap.Cap.H245Vid_H263.bit_mask & format_mask)) { local_params.TS_Tradeoff = TRUE; break; } } pFmtTheirs++; // next entry in receiver's caps } } goto RESOLVED_EXIT; break; case H245_CLIENT_VID_H261: // like everywhere else in this module, the assumption is that // local H.261 capabilities are fanned out with one local cap entry // per frame size. format_mask= H261VdCpblty_qcifMPI_present |H261VdCpblty_cifMPI_present; // bail out if no match or nonexistent frame size if (!((pCapability->Cap.H245Vid_H261.bit_mask & format_mask) & (pFmtMine->H245Cap.Cap.H245Vid_H261.bit_mask & format_mask))) continue; // get the maximum bitrate local_params.ns_params.maxBitRate = min(pFmtMine->H245Cap.Cap.H245Vid_H261.maxBitRate, pCapability->Cap.H245Vid_H261.maxBitRate); // FIND THE MAXIMUM MPI!!!!. (minimum frame rate) // there is no pretty way to do this bit_mask = pFmtMine->H245Cap.Cap.H245Vid_H261.bit_mask; if (bit_mask & H261VdCpblty_qcifMPI_present) { local_params.ns_params.MPI = max(pFmtMine->H245Cap.Cap.H245Vid_H261.H261VdCpblty_qcifMPI, pCapability->Cap.H245Vid_H261.H261VdCpblty_qcifMPI); } else if (bit_mask & H261VdCpblty_cifMPI_present) { local_params.ns_params.MPI = max(pFmtMine->H245Cap.Cap.H245Vid_H261.H261VdCpblty_cifMPI, pCapability->Cap.H245Vid_H261.H261VdCpblty_cifMPI); } else // impossible. Doom, as MikeG and JonT would say continue; // Fallout (And the format is found!) // And one more special thing: find out if the other end // advertised Temporal/Spatial tradeoff in it's send capabilities. // First try the obvious. Technically, it only makes sense for // transmit capabilities, but if the channel params have it, then // the other end must have the capability if(pCapability->Cap.H245Vid_H261.tmprlSptlTrdOffCpblty) { local_params.TS_Tradeoff = TRUE; } else { // Search for a H.261 SEND capability that has the T/S tradoff set for(j=0; jH245Cap.ClientType == H245_CLIENT_VID_H261) // exclude RX capabilities && (pFmtTheirs->H245Cap.Dir != H245_CAPDIR_LCLRX) && (pFmtTheirs->H245Cap.Dir != H245_CAPDIR_RMTRX)) { if ((pFmtTheirs->H245Cap.Cap.H245Vid_H261.bit_mask & format_mask) & (pFmtMine->H245Cap.Cap.H245Vid_H261.bit_mask & format_mask)) { local_params.TS_Tradeoff = TRUE; break; } } pFmtTheirs++; // next entry in receiver's caps } } goto RESOLVED_EXIT; break; default: goto RESOLVED_EXIT; break; } }// end else compare standard parameters, if any }// end if(pFmtMine->H245Cap.ClientType == pCapability->ClientType) } return CAPS_E_NOMATCH; RESOLVED_EXIT: // Match! // return ID of the decoding caps that match *pFormatID = pFmtMine->H245Cap.CapId; local_params.RTP_Payload = pChannelParams->bRTPPayloadType;; memcpy(lpvBuf, &local_params, sizeof(VIDEO_CHANNEL_PARAMETERS)); return hrSuccess; } HRESULT CMsivCapability::SetCapIDBase (UINT uNewBase) { uCapIDBase = uNewBase; UINT u; for (u=0;u= uCapIDBase) && ((CapID - uCapIDBase) < uNumLocalFormats)) return TRUE; else return FALSE; } HRESULT CMsivCapability::IsFormatEnabled (MEDIA_FORMAT_ID FormatID, PBOOL bRecv, PBOOL bSend) { UINT uIndex = IDToIndex(FormatID); // validate input if(uIndex >= (UINT)uNumLocalFormats) { return CAPS_E_INVALID_PARAM; } *bSend=((pLocalFormats + uIndex)->bSendEnabled); *bRecv=((pLocalFormats + uIndex)->bRecvEnabled); return hrSuccess; } BOOL CMsivCapability::IsFormatPublic (MEDIA_FORMAT_ID FormatID) { UINT uIndex = IDToIndex(FormatID); // validate input if(uIndex >= (UINT)uNumLocalFormats) return FALSE; // test if this is format is a duplicate of a public format if((pLocalFormats + uIndex)->dwPublicRefIndex) return FALSE; // then we keep this format to ourselves else return TRUE; } MEDIA_FORMAT_ID CMsivCapability::GetPublicID(MEDIA_FORMAT_ID FormatID) { UINT uIndex = IDToIndex(FormatID); // validate input if(uIndex >= (UINT)uNumLocalFormats) return INVALID_MEDIA_FORMAT; if((pLocalFormats + uIndex)->dwPublicRefIndex) { return (pLocalFormats + ((pLocalFormats + uIndex)->dwPublicRefIndex))->H245Cap.CapId; } else { return FormatID; } } // Returns the Id of the format with the smallest wSortIndex - preferred format. HRESULT CMsivCapability::GetPreferredFormatId (VIDEO_FORMAT_ID *pId) { HRESULT hr = hrSuccess; VIDCAP_DETAILS *pDetails = pLocalFormats; UINT u, uIndex; WORD wSortIndex, wMinSortIndex = SHRT_MAX; // Validate input param if (!pId) return((HRESULT)CAPS_E_INVALID_PARAM); // Validate state if(!uNumLocalFormats || !pDetails) return((HRESULT)CAPS_E_NOCAPS); // Look for the format with the smallest wSortIndex for (u = 0; (u < uNumLocalFormats) && (u < MAX_CAPS_PRESORT); u++) { pDetails = pLocalFormats + IDsByRank[u]; // Find the sort index. uIndex = (UINT)(pDetails - pLocalFormats); for (wSortIndex = 0; (wSortIndex < uNumLocalFormats) && (wSortIndex < MAX_CAPS_PRESORT); wSortIndex++) { if (uIndex == IDsByRank[wSortIndex]) break; // Found it } if (wSortIndex <= wMinSortIndex) { *pId = IndexToId(uIndex); wMinSortIndex = wSortIndex; } } return(hr); }