/*
 *  	File: msiacaps.cpp
 *
 *		ACM implementation of Microsoft Network Audio capability object.
 *
 *		Revision History:
 *
 *		06/06/96	mikev	created
 */


#include "precomp.h"

//Prototypes....
ULONG ReadRegistryFormats (LPCSTR lpszKeyName,CHAR ***pppName,BYTE ***pppData,PUINT pnFormats,DWORD dwDebugSize);
void FreeRegistryFormats (PRRF_INFO pRegFmts);

static BOOL bUseDefault;


#define szRegMSIPAndH323Encodings	TEXT("ACMH323Encodings")


AUDCAP_DETAILS default_id_table[] =
{


{WAVE_FORMAT_ADPCM, NONSTD_TERMCAP, STD_CHAN_PARAMS, {RTP_DYNAMIC_MIN, 0, 8000, 4},
	0, TRUE, TRUE, 500, 32000,32000,50,0,PREF_ORDER_UNASSIGNED,0,NULL,0, NULL,
	"Microsoft ADPCM"},


	{WAVE_FORMAT_LH_CELP, NONSTD_TERMCAP, STD_CHAN_PARAMS,{RTP_DYNAMIC_MIN,  0, 8000, 16},
		0, TRUE, TRUE, 640, 5600,5600,LNH_48_CPU,0,3,0,NULL,0,NULL,
		"Lernout & Hauspie CELP 4.8kbit/s"},
	{WAVE_FORMAT_LH_SB8,  NONSTD_TERMCAP, STD_CHAN_PARAMS,{RTP_DYNAMIC_MIN,  0, 8000, 16},
		0, TRUE, TRUE, 640, 8000,8000,LNH_8_CPU,0,0,0,NULL,0,NULL,
		"Lernout & Hauspie SBC 8kbit/s"},
	{WAVE_FORMAT_LH_SB12, NONSTD_TERMCAP, STD_CHAN_PARAMS, {RTP_DYNAMIC_MIN,  0, 8000, 16},
		0, TRUE, TRUE, 640, 12000,12000,LNH_12_CPU,0,1,0,NULL,0,NULL,
		"Lernout & Hauspie SBC 12kbit/s"},
	{WAVE_FORMAT_LH_SB16, NONSTD_TERMCAP, STD_CHAN_PARAMS,{RTP_DYNAMIC_MIN,  0, 8000, 16},
		0, TRUE, TRUE, 640, 16000,16000,LNH_16_CPU,0,2,0,NULL,0,NULL,
		"Lernout & Hauspie SBC 16kbit/s"},
	{WAVE_FORMAT_MSRT24, NONSTD_TERMCAP, STD_CHAN_PARAMS,{RTP_DYNAMIC_MIN,  0, 8000, 16},
		0, TRUE, TRUE, 720, 2400,2400,MSRT24_CPU,0,4,0,NULL,0,NULL,
		"Voxware RT 2.4kbit/s"},
	{WAVE_FORMAT_MSG723,  STD_TERMCAP(H245_CLIENT_AUD_G723), 	// client type H245_CLIENT_AUD_G723,
		STD_CHAN_PARAMS, {RTP_PAYLOAD_G723,  0, 8000, 0},
		0, TRUE, TRUE, 960, 5600,5600,MS_G723_CPU,0,
		0,	// priority
		0,NULL,0,NULL, "Microsoft G.723.1"},
	{WAVE_FORMAT_ALAW,	STD_TERMCAP( H245_CLIENT_AUD_G711_ALAW64),
		STD_CHAN_PARAMS, {RTP_PAYLOAD_G711_ALAW,  0, 8000, 8},
		0, TRUE, TRUE, 500, 64000,64000,CCITT_A_CPU,0,0,
		0, NULL, 0, NULL, "CCITT A-Law"},
	{WAVE_FORMAT_MULAW,	STD_TERMCAP( H245_CLIENT_AUD_G711_ULAW64),
		STD_CHAN_PARAMS, {RTP_PAYLOAD_G711_MULAW,  0, 8000, 8},
		0, TRUE, TRUE, 500, 64000,64000,CCITT_U_CPU,0,0,
		0, NULL, 0, NULL, "CCITT u-Law"},
		
#if(0)
// do not use this version of the G.723 codec
	{WAVE_FORMAT_INTELG723,  STD_TERMCAP(H245_CLIENT_AUD_G723), 	// client type H245_CLIENT_AUD_G723,
		STD_CHAN_PARAMS, {RTP_DYNAMIC_MIN,  0, 8000, 0},
		0, TRUE, TRUE, 960, 16000,16000,99,0,
		0,	// priority
		0,NULL,0,NULL, "G.723"},
		
	{WAVE_FORMAT_DSPGROUP_TRUESPEECH, {
		NONSTD_TERMCAP, {RTP_DYNAMIC_MIN,  0, 5510, 4},
		0, TRUE, TRUE, 500, 5510,5510,50,0,0,0,NULL},
		"DSP Group TrueSpeech(TM)"},
    {WAVE_FORMAT_PCM, {{RTP_DYNAMIC_MIN,  0, 8000, 8}, TRUE, TRUE, 160, 64000,64000,50,0,0,0,NULL,0,NULL}, "MS-ADPCM"},
	{WAVE_FORMAT_PCM, {{RTP_DYNAMIC_MIN,  0, 5510, 8}, TRUE, TRUE, 160, 44080,44080,50,0,0,0,NULL,0,NULL}, "MS-ADPCM"},
	{WAVE_FORMAT_PCM, {{RTP_DYNAMIC_MIN,  0, 11025, 8}, TRUE, TRUE, 160, 88200,88200,50,0,0,0,NULL,0,NULL},"MS-ADPCM"},
	{WAVE_FORMAT_PCM, {{RTP_DYNAMIC_MIN,  0, 8000, 16}, TRUE, TRUE, 160, 128000,128000,50,0,0,0,NULL,0,NULL},"MS-ADPCM"},
	{WAVE_FORMAT_ADPCM, {{RTP_DYNAMIC_MIN,  0, 8000, 4}, TRUE, TRUE, 500, 16000,16000,50,0,,0,0,NULL,0,NULL}, "MS-ADPCM"},
	{WAVE_FORMAT_GSM610, 	 STD_CHAN_PARAMS,{RTP_DYNAMIC_MIN,  0, 8000, 0},
		0, TRUE, TRUE, 320, 8000,8000,96,0,PREF_ORDER_UNASSIGNED,0,NULL,0,NULL,
		//"Microsoft GSM 6.10"
		"GSM 6.10"},
#endif	// DEF_USE_ALLPCM	


};


UINT uDefTableEntries = sizeof(default_id_table) /sizeof(AUDCAP_DETAILS);
static BOOL bCreateDefTable = FALSE;


//
//	static members of CMsiaCapability
//

MEDIA_FORMAT_ID CMsiaCapability::IDsByRank[MAX_CAPS_PRESORT];
UINT CMsiaCapability::uNumLocalFormats = 0;			// # of active entries in pLocalFormats
UINT CMsiaCapability::uStaticRef = 0;					// global ref count
UINT CMsiaCapability::uCapIDBase = 0;					// rebase capability ID to index into IDsByRank
UINT CMsiaCapability::uLocalFormatCapacity = 0;		// size of pLocalFormats (in multiples of AUDCAP_DETAILS)
AUDCAP_DETAILS * CMsiaCapability::pLocalFormats = NULL;	

CMsiaCapability::CMsiaCapability()
:uRef(1),
wMaxCPU(95),
m_uPacketDuration(90),
uNumRemoteDecodeFormats(0),
uRemoteDecodeFormatCapacity(0),
pRemoteDecodeFormats(NULL),
bPublicizeTXCaps(FALSE),
bPublicizeTSTradeoff(FALSE),
pRegFmts(NULL)

{
	m_IAppCap.Init(this);
}

CMsiaCapability::~CMsiaCapability()
{
	CloseACMDriver();
	UINT u;
	AUDCAP_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 <uNumLocalFormats; u++)
			{
				if(pDetails->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--;

        //Clean up the format cache. - This was the audio format information that
        //Was in the registry. A list of names, ptrs to AUDCAP_DETAILS blocks
        //and a count of formats. Memory is allocated in ReadRegistryFormats,
        //called from ReInit()

	}
	else
	{
		uStaticRef--;
	}
	
	if (pRemoteDecodeFormats)
	{	
		pDetails = pRemoteDecodeFormats;
		for(u=0; u <uNumRemoteDecodeFormats; u++)
		{
			if(pDetails->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;
	}

	FreeRegistryFormats(pRegFmts);
}

BOOL CMsiaCapability::Init()
{
	BOOL bRet;
	if(uStaticRef == 0)
	{
		if(bRet = ReInit())
		{
			uStaticRef++;
		}
	}
	else
	{
		uStaticRef++;
		bRet = TRUE;
	}
	return bRet;
}


BOOL CMsiaCapability::ReInit()
{
	SYSTEM_INFO si;
	DWORD dwDisposition;
	BOOL bRet = TRUE;
	ACM_APP_PARAM sAppParam={this, NULL, ACMAPP_FORMATENUMHANDLER_ENUM, NULL, NULL, 0, NULL};
	ACMFORMATTAGDETAILS aftd;
	AUDCAP_DETAILS audcapDetails;
	UINT i;

	ZeroMemory(&IDsByRank, sizeof(IDsByRank));

	// LOOKLOOK - this supports a hack to disable CPU intensive codecs if not running on a pentium
	GetSystemInfo(&si);
#ifdef _M_IX86
	wMaxCPU = (si.dwProcessorType == PROCESSOR_INTEL_PENTIUM )? 100 : 50;
#endif
#ifdef _ALPHA_
	wMaxCPU = 100;
#endif
	if (pLocalFormats)
	{	
		UINT u;
		AUDCAP_DETAILS *pDetails = pLocalFormats;
		for(u=0; u <uNumLocalFormats; u++)
		{
			if(pDetails->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;				

    /*
	 *	Format cache
	 */


    if (!pRegFmts) {	
        if (!(pRegFmts=(PRRF_INFO)MemAlloc (sizeof (RRF_INFO)))) {
            bRet = FALSE;
            goto RELEASE_AND_EXIT;
        }

   		bUseDefault=FALSE;

        if (ReadRegistryFormats (szRegInternetPhone TEXT("\\") szRegMSIPAndH323Encodings,
				&pRegFmts->pNames,(BYTE ***)&pRegFmts->pData,&pRegFmts->nFormats,sizeof (AUDCAP_DETAILS)) != ERROR_SUCCESS) {
    		bUseDefault=TRUE;
    		MemFree ((void *) pRegFmts);
    		pRegFmts=NULL;
        }
    }

	// pass the registry formats through ACM to the handler
    sAppParam.pRegCache=pRegFmts;

	if(!DriverEnum((DWORD_PTR) &sAppParam))
	{
		bRet = FALSE;
		goto RELEASE_AND_EXIT;
	}
				
 	SortEncodeCaps(SortByAppPref);
RELEASE_AND_EXIT:
	return bRet;
}


STDMETHODIMP CMsiaCapability::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_IAppAudioCap )
	{
		*ppvObject = (LPAPPCAPPIF)&m_IAppCap;
		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 CMsiaCapability::AddRef()
{
	uRef++;
	return uRef;
}

ULONG CMsiaCapability::Release()
{
	uRef--;
	if(uRef == 0)
	{
		delete this;
		return 0;
	}
	return uRef;
}
VOID CMsiaCapability::FreeRegistryKeyName(LPTSTR lpszKeyName)
{
	if (lpszKeyName)
    {
		LocalFree(lpszKeyName);
    }
}

LPTSTR CMsiaCapability::AllocRegistryKeyName(LPTSTR lpDriverName,
		UINT uSampleRate, UINT uBitsPerSample, UINT uBytesPerSec)
{
	FX_ENTRY(("MsiaCapability::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
	lpszKeyName = (LPTSTR)LocalAlloc (LPTR, lstrlen(lpDriverName) * sizeof(*lpDriverName)+3*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",
				lpDriverName,
				uSampleRate,
				uBitsPerSample,
				uBytesPerSec);

	return (lpszKeyName);
}


VOID CMsiaCapability::SortEncodeCaps(SortMode sortmode)
{
	UINT iSorted=0;
	UINT iInsert = 0;
	UINT iCache=0;
	UINT iTemp =0;
	BOOL bInsert;	
	AUDCAP_DETAILS *pDetails1, *pDetails2;
	
	if(!uNumLocalFormats)
		return;
	if(uNumLocalFormats ==1)
	{
		IDsByRank[0]=0;
		return;
	}
	
	// look at every cached format, build index array
	for(iCache=0;iCache<uNumLocalFormats;iCache++)
	{
		pDetails1 = pLocalFormats+iCache;
		for(iInsert=0;iInsert < iSorted; iInsert++)
		{
			pDetails2 = pLocalFormats+IDsByRank[iInsert];
			// if existing stuff is less than new stuff....
			
			bInsert = FALSE;
			switch(sortmode)
			{
				case SortByAppPref:
					if(pDetails2->wApplicationPrefOrder > 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++;
		}
	}
}



STDMETHODIMP CMsiaCapability::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_WAVEFORMATEX((WAVEFORMATEX*)(*ppFormat));
	return S_OK;

}

STDMETHODIMP CMsiaCapability::GetEncodeFormatDetails(MEDIA_FORMAT_ID FormatID, VOID **ppFormat, UINT *puSize)
{
	// same as GetDecodeFormatDetails
	return GetDecodeFormatDetails(FormatID, ppFormat, puSize);
}

VOID CMsiaCapability::CalculateFormatProperties(AUDCAP_DETAILS *pFmtBuf,LPWAVEFORMATEX lpwfx)
{
	WORD wFrames;
	if(!pFmtBuf)
	{
		return;
	}
	
	// use actual bits per sample unless the bps field is zero, in which case
	// assume 16 bits (worst case).  This is a typical GSM scenario
	UINT uBitrateIn = (pFmtBuf->audio_params.uSamplesPerSec) *
		((pFmtBuf->audio_params.uBitsPerSample)
		? pFmtBuf->audio_params.uBitsPerSample
		:16);

	// set the maximum bitrate (uMaxBitrate). we're not setting the average bitrate (uAvgBitrate),
	// since the nAvgBytesPerSec reported by ACM is really worst case. uAvgBitrate will be set
	// from the hardcoded numbers for our known codecs and from the provided AUDCAP_INFO for
	// installable codecs
	pFmtBuf->uMaxBitrate = (lpwfx->nAvgBytesPerSec)? lpwfx->nAvgBytesPerSec*8:uBitrateIn;

	pFmtBuf->dwDefaultSamples = MinSampleSize(lpwfx);
	
	// nonstandard channel parameters.  This
	// might be a good point to calculate values that don't have valid defaults set.
	wFrames = pFmtBuf->nonstd_params.wFramesPerPktMax;
	if(!pFmtBuf->nonstd_params.wFramesPerPktMax)	
	{
		pFmtBuf->nonstd_params.wFramesPerPktMax=
			wFrames = LOWORD(MaxFramesPerPacket(lpwfx));
	}
	// if the preferred frames/packet is 0 or greater than the max, set it to min
	if((pFmtBuf->nonstd_params.wFramesPerPkt ==0) ||
		(pFmtBuf->nonstd_params.wFramesPerPkt > wFrames))
	{
		pFmtBuf->nonstd_params.wFramesPerPkt =
			LOWORD(MinFramesPerPacket(lpwfx));
	}
	// if the min is more than preferred, fix it
	if(pFmtBuf->nonstd_params.wFramesPerPktMin > pFmtBuf->nonstd_params.wFramesPerPkt)
	{
		pFmtBuf->nonstd_params.wFramesPerPktMin =
			LOWORD(MinFramesPerPacket(lpwfx));
	}

	pFmtBuf->nonstd_params.wDataRate =0;  // default
	pFmtBuf->nonstd_params.wFrameSize = (pFmtBuf->nonstd_params.wFramesPerPkt)
			? LOWORD(pFmtBuf->dwDefaultSamples / pFmtBuf->nonstd_params.wFramesPerPkt): 0;
	pFmtBuf->nonstd_params.UsePostFilter = 0;
	pFmtBuf->nonstd_params.UseSilenceDet = 0;

}


AUDIO_FORMAT_ID CMsiaCapability::AddFormat(AUDCAP_DETAILS *pFmtBuf,LPVOID lpvMappingData, UINT uSize)
{
	FX_ENTRY(("CMsiaCapability::AddFormat"));
	AUDCAP_DETAILS *pTemp;
	WORD wFrames;
	UINT uSamples;
	if(!pFmtBuf || !lpvMappingData || !uSize)
	{
		return INVALID_AUDIO_FORMAT;
	}
	// check room
	if(uLocalFormatCapacity <= uNumLocalFormats)
	{
		// get more mem, realloc memory by CAP_CHUNK_SIZE for pLocalFormats
		pTemp = (AUDCAP_DETAILS *)MEMALLOC((uNumLocalFormats + CAP_CHUNK_SIZE)*sizeof(AUDCAP_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(("%s:leak! uNumLocalFormats:0x%08lX, pLocalFormats:0x%08lX\r\n",
				_fx_, uNumLocalFormats,pLocalFormats));
		}
		#endif
		// copy old stuff, discard old mem
		if(uNumLocalFormats && pLocalFormats)
		{
			memcpy(pTemp, pLocalFormats, uNumLocalFormats*sizeof(AUDCAP_DETAILS));
			MEMFREE(pLocalFormats);
		}
		pLocalFormats = pTemp;
	}
	// pTemp is where the stuff is cached
	pTemp = pLocalFormats+uNumLocalFormats;
	memcpy(pTemp, pFmtBuf, sizeof(AUDCAP_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(("%s:allocation failed!\r\n",_fx_));
			}
		#endif
	//}
	//else
	//{
	//}

	// in all cases, fixup channel parameters.

	pTemp->dwDefaultSamples = uSamples =pTemp->dwDefaultSamples;
	
	wFrames = pTemp->nonstd_params.wFramesPerPktMax;
	if(!pTemp->nonstd_params.wFramesPerPktMax)	
	{
		pTemp->nonstd_params.wFramesPerPktMax=
			wFrames = LOWORD(MaxFramesPerPacket((LPWAVEFORMATEX)lpvMappingData));
	}
	// if the preferred frames/packet is 0 or greater than the max, set it to min
	if((pTemp->nonstd_params.wFramesPerPkt ==0) ||
		(pTemp->nonstd_params.wFramesPerPkt > wFrames))
	{
		pTemp->nonstd_params.wFramesPerPkt =
			LOWORD(MinFramesPerPacket((LPWAVEFORMATEX)lpvMappingData));
	}
	// if the min is more than preferred, fix it
	if(pTemp->nonstd_params.wFramesPerPktMin > pTemp->nonstd_params.wFramesPerPkt)
	{
		pTemp->nonstd_params.wFramesPerPktMin =
			LOWORD(MinFramesPerPacket((LPWAVEFORMATEX)lpvMappingData));
	}
	pTemp->nonstd_params.wDataRate =0;  // default
	pTemp->nonstd_params.wFrameSize = (pTemp->nonstd_params.wFramesPerPkt)
			?uSamples / pTemp->nonstd_params.wFramesPerPkt: 0;
    if(pTemp->nonstd_params.wFrameSizeMax < pTemp->nonstd_params.wFrameSize)
        pTemp->nonstd_params.wFrameSizeMax = pTemp->nonstd_params.wFrameSize;

	pTemp->nonstd_params.UsePostFilter = 0;
	pTemp->nonstd_params.UseSilenceDet = 0;


	// fixup the H245 parameters.  Use the REBASED index of the cap entry as the cap ID
	pTemp->H245TermCap.CapId = (USHORT)IndexToId(uNumLocalFormats);

	if(pTemp->H245TermCap.ClientType ==0
				|| pTemp->H245TermCap.ClientType ==H245_CLIENT_AUD_NONSTD)
	{
		LPWAVEFORMATEX lpwfx;
		lpwfx = (LPWAVEFORMATEX)pTemp->lpLocalFormatDetails;
		if(lpwfx)
		{	
			pTemp->H245TermCap.ClientType = H245_CLIENT_AUD_NONSTD;

			// all nonstandard identifier fields are unsigned short
			// two possibilities for choice are "h221NonStandard_chosen" and "object_chosen"
			pTemp->H245TermCap.H245Aud_NONSTD.nonStandardIdentifier.choice = h221NonStandard_chosen;
		
			pTemp->H245TermCap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35CountryCode = USA_H221_COUNTRY_CODE;
			pTemp->H245TermCap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35Extension = USA_H221_COUNTRY_EXTENSION;
			pTemp->H245TermCap.H245Aud_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.
			// set size of buffer
			pTemp->H245TermCap.H245Aud_NONSTD.data.length = 0;
			pTemp->H245TermCap.H245Aud_NONSTD.data.value = NULL;
		}
	}
	else
	{
		// the following should already have been set in *pFmtBuf by the calling function
		// and it should have already been copied to *pTemp
		
		//pTemp->ClientType = (H245_CLIENT_T)pDecodeDetails->H245Cap.ClientType;
		//pTemp->DataType = H245_DATA_AUDIO;
		//pTemp->Dir = H245_CAPDIR_LCLTX;  // should this be H245_CAPDIR_LCLRX for receive caps?
		
		// issue:special case G723 params ???
		if(pTemp->H245TermCap.ClientType == H245_CLIENT_AUD_G723) 	
		{
			pTemp->H245TermCap.H245Aud_G723.maxAl_sduAudioFrames = 4;
// mikev 9/10/96 - we may NOT want to advertise silence suppression capability of
// the codec because our silence detection scheme works out-of-band for any codec
// 9/29/96 - this gets overwritten in SerializeH323DecodeFormats() anyway
			pTemp->H245TermCap.H245Aud_G723.silenceSuppression = 0;
		}
		
		// check for pre-existing capability with the same standard ID.
		pTemp->dwPublicRefIndex = 0;	// forget old association, assume that there
										// is no pre-existing capability with the same
										//standard ID.
		UINT i;
		AUDCAP_DETAILS *pFmtExisting = pLocalFormats;
		BOOL bRefFound = FALSE; // this var needed only to support backward
								// compatibility with Netmeeting 2.0 Beta 1
		if(uNumLocalFormats && pLocalFormats)
		{
			for(i=0; i<uNumLocalFormats; i++)
			{
				pFmtExisting = pLocalFormats + i;
				// see if it is the same defined codepoint
				if(pFmtExisting->H245TermCap.ClientType == pTemp->H245TermCap.ClientType)
				{
					// mark this capability entry as being publically advertised
					// by the existing entry.  If the existing entry also refs
					// another, follow the reference
					pTemp->dwPublicRefIndex = (pFmtExisting->dwPublicRefIndex)?
						pFmtExisting->dwPublicRefIndex : i;
					bRefFound = TRUE;
					break;
				}
			}
		}

	}		

	uNumLocalFormats++;
	
	// return the capability ID.
	//return (uNumLocalFormats-1);
	return pTemp->H245TermCap.CapId;
	
	ERROR_EXIT:
	return INVALID_AUDIO_FORMAT;
			
}

UINT CMsiaCapability::GetNumCaps(BOOL bRXCaps)
{
	UINT u, uOut=0;
	
	AUDCAP_DETAILS *pDecodeDetails = pLocalFormats;
	if(bRXCaps)
	{
		for(u=0; u <uNumLocalFormats; u++)
		{
			if(pDecodeDetails->bRecvEnabled)
				uOut++;
			
			pDecodeDetails++;
		}
		return uOut;
	}
	else
		return uNumLocalFormats;
}

VOID CMsiaCapability::FlushRemoteCaps()
{
	if(pRemoteDecodeFormats)
	{
		MEMFREE(pRemoteDecodeFormats);
		pRemoteDecodeFormats = NULL;
		uNumRemoteDecodeFormats = 0;
		uRemoteDecodeFormatCapacity = 0;
	}
}
HRESULT CMsiaCapability::GetNumFormats(UINT *puNumFmtOut)
{
	*puNumFmtOut = uNumLocalFormats;
	return hrSuccess;
}


/***************************************************************************

    Name      : CMsiaCapability::FormatEnumHandler

    Purpose   : Enumerate ACM formats coming from ACM, and see if they
					are ones that we use.

    Parameters:	Standard ACM EnumFormatCallback parameters

    Returns   : BOOL

    Comment   :

***************************************************************************/
BOOL CMsiaCapability::FormatEnumHandler(HACMDRIVERID hadid,
    LPACMFORMATDETAILS pafd, DWORD_PTR dwInstance, DWORD fdwSupport)
{
	PACM_APP_PARAM pAppParam = (PACM_APP_PARAM) dwInstance;
	LPACMFORMATTAGDETAILS paftd = pAppParam->paftd;
	AUDCAP_DETAILS audcap_entry;

	// look at the passed in dwInstance. this will tell us which format handler
	// to call
	if ((pAppParam->dwFlags && ACMAPP_FORMATENUMHANDLER_MASK) == ACMAPP_FORMATENUMHANDLER_ADD)
	{
		// this one was called for add format purposes
		return AddFormatEnumHandler(hadid, pafd, dwInstance, fdwSupport);
	}

	// evaluate the details
	if(pafd->pwfx->nChannels ==1)
	{
		if(IsFormatSpecified(pafd->pwfx, pafd, paftd, &audcap_entry))
		{
			DEBUGMSG(ZONE_ACM,("FormatEnumHandler: tag 0x%04X, nChannels %d\r\n",
				pafd->pwfx->wFormatTag, pafd->pwfx->nChannels));
			DEBUGMSG(ZONE_ACM,("FormatEnumHandler: nSamplesPerSec 0x%08lX, nAvgBytesPerSec 0x%08lX,\r\n",
				pafd->pwfx->nSamplesPerSec, pafd->pwfx->nAvgBytesPerSec));
			DEBUGMSG(ZONE_ACM,("FormatEnumHandler: nBlockAlign 0x%04X, wBitsPerSample 0x%04X, cbSize 0x%04X\r\n",
				pafd->pwfx->nBlockAlign, pafd->pwfx->wBitsPerSample, pafd->pwfx->cbSize));
			DEBUGMSG(ZONE_ACM,("FormatEnumHandler: szFormat %s,\r\n",
				 pafd->szFormat));

		//	done inside IsFormatSpecified and/or whatever it calls
		//  CalculateFormatProperties(&audcap_details, pafd->pwfx);
			AddFormat(&audcap_entry, (LPVOID)pafd->pwfx,
				(pafd->pwfx) ? (sizeof(WAVEFORMATEX)+pafd->pwfx->cbSize):0);	
				
		}
		//#define BUILD_TEST_ENTRIES
#ifdef BUILD_TEST_ENTRIES
		else
		{
			AUDCAP_INFO sAudCapInfo;

			if(paftd)
			{		
				if((lstrcmp(paftd->szFormatTag, "G.723" ) ==0)
				/*	||  (lstrcmp(paftd->szFormatTag, "MSN Audio" ) ==0) */
					||  (lstrcmp(paftd->szFormatTag, "GSM 6.10" ) ==0))
				{
					lstrcpyn(audcap_entry.szFormat, paftd->szFormatTag,
						sizeof(audcap_entry.szFormat));
					int iLen = lstrlen(audcap_entry.szFormat);
					if(iLen < (sizeof(audcap_entry.szFormat) + 8*sizeof(TCHAR)))
					{
						// ok to concatenate
						lstrcat(audcap_entry.szFormat,", ");
						// must check for truncation. so do the final concatenation via lstrcpyn
						// lstrcat(audcap_entry.szFormat, pafd->szFormat);
						iLen = lstrlen(audcap_entry.szFormat);
						lstrcpyn(&audcap_entry.szFormat[iLen], pafd->szFormat,
							sizeof(audcap_entry.szFormat) - iLen - sizeof(TCHAR));
					}
					lstrcpyn(sAudCapInfo.szFormat, audcap_entry.szFormat,
								sizeof(sAudCapInfo.szFormat); 		
					AddACMFormat (pafd->pwfx, &sAudCapInfo);						
				}
			}
		}
#endif	// BUILD_TEST_ENTRIES
	}

	return TRUE;

}


/***************************************************************************

    Name      : CMsiaCapability::BuildFormatName

    Purpose   : Builds a format name for a format, from the format name and
				the tag name

    Parameters:	pAudcapDetails [out] - pointer to an AUDCAP_DETAILS structure, where the
					created value name will be stored
				pszFormatTagName [in] - pointer to the name of the format tag
				pszFormatName [in] - pointer to the name of the format

    Returns   : BOOL

    Comment   :

***************************************************************************/
BOOL CMsiaCapability::BuildFormatName(	AUDCAP_DETAILS *pAudcapDetails,
													char *pszFormatTagName,
													char *pszFormatName)
{
	BOOL bRet = TRUE;
	int iLen=0;

	if (!pAudcapDetails ||
		!pszFormatTagName	||
		!pszFormatName)
	{
		bRet = FALSE;
		goto out;
	}

	// concatenate ACM strings to form the first part of the registry key - the
	// format is szFormatTag (actually pAudcapDetails->szFormat)
	// (the string  which describes the format tag followed by szFormatDetails
	// (the string which describes parameters, e.g. sample rate)

	lstrcpyn(pAudcapDetails->szFormat, pszFormatTagName, sizeof(pAudcapDetails->szFormat));
	iLen = lstrlen(pAudcapDetails->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 ACM driver writer was  misinformed
	if(iLen < (sizeof(pAudcapDetails->szFormat) + 8*sizeof(TCHAR)))
	{
		// ok to concatenate
		lstrcat(pAudcapDetails->szFormat,", ");
		// must check for truncation. so do the final concatenation via lstrcpyn
		// lstrcat(pFormatPrefsBuf->szFormat, pafd->szFormat);
		iLen = lstrlen(pAudcapDetails->szFormat);
		lstrcpyn(pAudcapDetails->szFormat+iLen, pszFormatName,
					sizeof(pAudcapDetails->szFormat) - iLen - sizeof(TCHAR));
	}		

out:
	return bRet;
}

// Free a a structure of registry formats info (PRRF_INFO), including memory pointed
// from this structure
void FreeRegistryFormats (PRRF_INFO pRegFmts)
{
	UINT u;

    if (pRegFmts) {
        for (u=0;u<pRegFmts->nFormats;u++) {
            MemFree ((void *) pRegFmts->pNames[u]);
            MemFree ((void *) pRegFmts->pData[u]);
        }
        MemFree ((void *) pRegFmts->pNames);
        MemFree ((void *) pRegFmts->pData);
        MemFree ((void *) pRegFmts);

    }
}

ULONG ReadRegistryFormats (LPCSTR lpszKeyName,CHAR ***pppName,BYTE ***pppData,PUINT pnFormats,DWORD dwDebugSize)
{

    HKEY hKeyParent;
    DWORD nSubKey,nMaxSubLen,nValues=0,nValNamelen,nValDatalen,nValTemp,nDataTemp,i;
    ULONG hRes;

    //Not neccessary but makes life easier
    CHAR **pNames=NULL;
    BYTE **pData=NULL;

    *pnFormats=0;


    //Get the top level node.
    hRes=RegOpenKeyEx (HKEY_LOCAL_MACHINE,lpszKeyName,0,KEY_READ,&hKeyParent);

    if (hRes != ERROR_SUCCESS)
    {
        return hRes;
    }

    //Get some info about this key
    hRes=RegQueryInfoKey (hKeyParent,NULL,NULL,NULL,&nSubKey,&nMaxSubLen,NULL,&nValues,&nValNamelen,&nValDatalen,NULL,NULL);

    if (hRes != ERROR_SUCCESS)
    {
        goto Error_Out;
    }


    if (nValDatalen != dwDebugSize) {
        DEBUGMSG (ZONE_ACM,("Largest Data Value not expected size!\r\n"));
        hRes=ERROR_INVALID_DATA;
        goto Error_Out;
    }

    //Allocate some memory for the various pointers.
    if (!(pNames=(char **) MemAlloc (sizeof(char *)*nValues))) {
        hRes=ERROR_OUTOFMEMORY;
        goto Error_Out;
    }
    ZeroMemory (pNames,sizeof (char *)*nValues);

    if (!(pData = (BYTE **) MemAlloc (sizeof(BYTE *)*nValues))) {
        hRes=ERROR_OUTOFMEMORY;
        goto Error_Out;
    }
    ZeroMemory (pData,sizeof (BYTE *)*nValues);


    //Walk the value list.
    for (i=0;i<nValues;i++)
    {
        //Yes, we're wasting memory here, oh well it's not a lot.
        //probably 40 bytes. We free it later
        if (!(pNames[i] = (char *)MemAlloc (nValNamelen))) {
            hRes=ERROR_OUTOFMEMORY;
            goto Error_Out;
        }

        if (!(pData[i] = (BYTE *)MemAlloc (nValDatalen))) {
            hRes=ERROR_OUTOFMEMORY;
            goto Error_Out;
        }

        //This needs to be able to be smashed, but is an in/out param.
        nValTemp=nValNamelen;
        nDataTemp=nValDatalen;

        hRes=RegEnumValue (hKeyParent,i,(pNames[i]),&nValTemp,NULL,NULL,(pData[i]),&nDataTemp);

#ifdef DEBUG
        if (nDataTemp != dwDebugSize) {
            DEBUGMSG (ZONE_ACM, ("ReadRegistryFormats: Data block not expected size!\r\n"));
            //Return?
        }
#endif

    }

    //Fill in the output.
    *pnFormats=nValues;
    *pppName=pNames;
    *pppData=pData;

    RegCloseKey (hKeyParent);

    return (ERROR_SUCCESS);

Error_Out:
        RegCloseKey (hKeyParent);
        //Free any allocations
        if(pNames)
        {
        	for (i=0;i<nValues;i++)
        	{
            	if (pNames[i])
            	{
                	MemFree (pNames[i]);
            	}
        	}
            MemFree (pNames);
        }

        if (pData)
        {
           	for (i=0;i<nValues;i++)
           	{
           		if (pData[i])
           		{
                	MemFree (pData[i]);
                }
           	}
        	
            MemFree (pData);
        }
        return hRes;
}


BOOL CMsiaCapability::IsFormatSpecified(LPWAVEFORMATEX lpwfx,  LPACMFORMATDETAILS pafd,
	LPACMFORMATTAGDETAILS paftd, AUDCAP_DETAILS *pAudcapDetails)
{
	AUDCAP_DETAILS cap_entry;
	BOOL bRet = FALSE;
	LPTSTR lpszValueName = NULL;
	DWORD dwRes;
	UINT i;


	if(!lpwfx || !pAudcapDetails)
	{
		return FALSE;
	}


    if (!bUseDefault) {
        for (i=0;i<pRegFmts->nFormats;i++) {
            // do a quick sanity check on the contents
            if ( (lpwfx->wFormatTag == ((AUDCAP_DETAILS *)pRegFmts->pData[i])->wFormatTag) &&
                 (lpwfx->nSamplesPerSec == ((DWORD)((AUDCAP_DETAILS *)pRegFmts->pData[i])->audio_params.uSamplesPerSec)) &&
                 ((lpwfx->nAvgBytesPerSec * 8) == (((AUDCAP_DETAILS *)pRegFmts->pData[i])->uMaxBitrate))) {
                break;
            }
        }

        if (i == pRegFmts->nFormats) {
            //Check the case that some (but not all) of the default formats are missing.
            for (i=0;i<uDefTableEntries;i++) {
                if ((paftd->dwFormatTag == default_id_table[i].wFormatTag)
                    && (lpwfx->nSamplesPerSec == (DWORD)default_id_table[i].audio_params.uSamplesPerSec)
                    && (lpwfx->wBitsPerSample == LOWORD(default_id_table[i].audio_params.uBitsPerSample))) {

                    //Arrgh!! Jump down, and rebuild this format
                    goto RebuildFormat;
                }
            }
            if (i==uDefTableEntries) {
                //We don't care about this format, it's not in the cache, or default list
                return FALSE;
            }

        }

        memcpy(pAudcapDetails, pRegFmts->pData[i], sizeof(AUDCAP_DETAILS));
        bRet=TRUE;
    } else {

RebuildFormat:
    	RtlZeroMemory((PVOID) pAudcapDetails, sizeof(AUDCAP_DETAILS));

    	// fixup the bits per sample and sample rate fields of audio_params so that the key name can be built
    	pAudcapDetails->audio_params.uSamplesPerSec = lpwfx->nSamplesPerSec;
    	pAudcapDetails->audio_params.uBitsPerSample = MAKELONG(lpwfx->wBitsPerSample,0);
    	pAudcapDetails->uMaxBitrate = lpwfx->nAvgBytesPerSec * 8;	

    	if (!paftd	||
    		(!BuildFormatName(	pAudcapDetails,
    							paftd->szFormatTag,
    							pafd->szFormat)))
    	{
    		ERRORMESSAGE(("IsFormatSpecified: Couldn't build format name\r\n"));
    		return(FALSE);
    	}

        for(i=0;i< uDefTableEntries; i++)
        {
            if((lpwfx->wFormatTag == default_id_table[i].wFormatTag)
                && (lpwfx->nSamplesPerSec == (DWORD)default_id_table[i].audio_params.uSamplesPerSec)
                && (lpwfx->wBitsPerSample == LOWORD(default_id_table[i].audio_params.uBitsPerSample)))
                //&& strnicmp(lpwfx->szFormat, default_id_table[i].szFormat)
            {
                // found matching default entry - copy stuff from table
                // (but don't overwrite the string)
                memcpy(pAudcapDetails, &default_id_table[i],
                    sizeof(AUDCAP_DETAILS) - sizeof(pAudcapDetails->szFormat));

                // LOOKLOOK - test against CPU limitations.
                // this supports a hack to disable CPU intensive codecs if not running
                //on a pentium

                if(default_id_table[i].wCPUUtilizationEncode > wMaxCPU)
                {					
                    pAudcapDetails->bSendEnabled = FALSE;
                }			
                if(default_id_table[i].wCPUUtilizationDecode > wMaxCPU)
                {					
                    pAudcapDetails->bRecvEnabled = FALSE;		
                }			

                // add this to the registry
                CalculateFormatProperties(pAudcapDetails, lpwfx);
                bRet = UpdateFormatInRegistry(pAudcapDetails);
                break;
            }
        }

    }



    return bRet;
}


/***************************************************************************

    Name      : CMsiaCapability::CopyAudcapInfo

    Purpose   : Copies basic audio info from an AUDCAP_INFO structure to an
				AUDCAP_DETAILS structure, or vice versa. AUDCAP_INFO is external
				representation. AUDCAP_DETAILS is internal one.

    Parameters:	pDetails - pointer to an AUDCAP_DETAILS structure
				pInfo - pointer to an AUDCAP_INFO structure
				bDirection - 0 = ->, 1 = <-

    Returns   : HRESULT

    Comment   :

***************************************************************************/
HRESULT CMsiaCapability::CopyAudcapInfo (PAUDCAP_DETAILS pDetails,
	PAUDCAP_INFO pInfo, BOOL bDirection)
{
	WORD wSortIndex;
	UINT uIndex;
	AUDIO_FORMAT_ID Id;
	HRESULT hr=NOERROR;
	
	if(!pInfo || !pDetails)
	{
		hr = CAPS_E_INVALID_PARAM;
		goto out;
	}

	if (bDirection)
	{
		// AUDCAP_INFO -> AUDCAP_DETAILS
		// the caller cannot modify szFormat, Id, wSortIndex and uMaxBitrate, all calculated fields

		pDetails->wFormatTag = pInfo->wFormatTag;	
		pDetails->uAvgBitrate = pInfo->uAvgBitrate;
		pDetails->wCPUUtilizationEncode	= pInfo->wCPUUtilizationEncode;
		pDetails->wCPUUtilizationDecode	= pInfo->wCPUUtilizationDecode;
		pDetails->bSendEnabled =  pInfo->bSendEnabled;
		pDetails->bRecvEnabled = pInfo->bRecvEnabled;
	}
	else
	{		
		// find the sort index.
		uIndex = (UINT)(pDetails - pLocalFormats);
		Id = IndexToId(uIndex);
		for(wSortIndex=0; wSortIndex<uNumLocalFormats && wSortIndex < MAX_CAPS_PRESORT; wSortIndex++)
		{
			if (uIndex == IDsByRank[wSortIndex])
				break; // found it
		}
		// note:  recall that only  MAX_CAPS_PRESORT are sorted and the rest are in random order.
		// the rest all have a value of MAX_CAPS_PRESORT for the sort index

		memcpy(pInfo->szFormat, pDetails->szFormat, sizeof(pInfo->szFormat));

		// AUDCAP_DETAILS -> AUDCAP_INFO	
		pInfo->wFormatTag = pDetails->wFormatTag;	
		pInfo->Id = Id;
		pInfo->uMaxBitrate = pDetails->uMaxBitrate;
		pInfo->uAvgBitrate = pDetails->uAvgBitrate;
		pInfo->wCPUUtilizationEncode	= pDetails->wCPUUtilizationEncode;
		pInfo->wCPUUtilizationDecode	= pDetails->wCPUUtilizationDecode;
		pInfo->bSendEnabled =  pDetails->bSendEnabled;
		pInfo->bRecvEnabled = pDetails->bRecvEnabled;
		pInfo->wSortIndex = wSortIndex;
	}

out:
	return hr;
}


HRESULT CMsiaCapability::EnumCommonFormats(PBASIC_AUDCAP_INFO pFmtBuf, UINT uBufsize,
	UINT *uNumFmtOut, BOOL bTXCaps)
{
	UINT u, uNumOut =0;
	HRESULT hr = hrSuccess;
	MEDIA_FORMAT_ID FormatIDRemote;
	HRESULT hrIsCommon;	
	
	AUDCAP_DETAILS *pDetails = pLocalFormats;
	// validate input
	if(!pFmtBuf || !uNumFmtOut || (uBufsize < (sizeof(BASIC_AUDCAP_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 <uNumLocalFormats) && (u <MAX_CAPS_PRESORT); u++)
	{
		pDetails = pLocalFormats + IDsByRank[u];	
		// if there is a session, then return formats that are common to local and remote.
		if(uNumRemoteDecodeFormats)
		{
			hrIsCommon = ResolveToLocalFormat(IndexToId(IDsByRank[u]), &FormatIDRemote);
			if(HR_SUCCEEDED(hrIsCommon))	
			{
				hr = CopyAudcapInfo (pDetails, pFmtBuf, 0);	
				if(!HR_SUCCEEDED(hr))	
					goto EXIT;
				uNumOut++;
				pFmtBuf++;
			}
		}
		else	// no remote capabilities exist because there is no current session
		{
			hr = CAPS_E_NOCAPS;
		}
	}

	*uNumFmtOut = uNumOut;
EXIT:
	return hr;
}

HRESULT CMsiaCapability::EnumFormats(PBASIC_AUDCAP_INFO pFmtBuf, UINT uBufsize,
	UINT *uNumFmtOut)
{
	UINT u;
	HRESULT hr = hrSuccess;
	AUDCAP_DETAILS *pDetails = pLocalFormats;
	// validate input
	if(!pFmtBuf || !uNumFmtOut || (uBufsize < (sizeof(BASIC_AUDCAP_INFO)*uNumLocalFormats)))
	{
		return CAPS_E_INVALID_PARAM;
	}
	if(!uNumLocalFormats || !pDetails)
	{
		return CAPS_E_NOCAPS;
	}

	for(u=0; (u <uNumLocalFormats) && (u <MAX_CAPS_PRESORT); u++)
	{
		pDetails = pLocalFormats + IDsByRank[u];	
		hr = CopyAudcapInfo (pDetails, pFmtBuf, 0);	
		if(!HR_SUCCEEDED(hr))	
			goto EXIT;
		pFmtBuf++;
	}

	*uNumFmtOut = min(uNumLocalFormats, MAX_CAPS_PRESORT);
EXIT:
	return hr;
}

HRESULT CMsiaCapability::GetBasicAudcapInfo (AUDIO_FORMAT_ID Id, PBASIC_AUDCAP_INFO pFormatPrefsBuf)
{
	AUDCAP_DETAILS *pFmt;
	UINT uIndex = IDToIndex(Id);
	if(!pFormatPrefsBuf || (uNumLocalFormats <= uIndex))
	{
		return CAPS_E_INVALID_PARAM;
	}
	pFmt = pLocalFormats + uIndex;

	return (CopyAudcapInfo(pFmt,pFormatPrefsBuf, 0));
}

HRESULT CMsiaCapability::ApplyAppFormatPrefs (PBASIC_AUDCAP_INFO pFormatPrefsBuf,
	UINT uNumFormatPrefs)
{
	FX_ENTRY ("CMsiaCapability::ApplyAppFormatPrefs");
	UINT u, v;
	PBASIC_AUDCAP_INFO pTemp;
	AUDCAP_DETAILS *pFmt;

	if(!pFormatPrefsBuf || (uNumLocalFormats != uNumFormatPrefs))
	{
		ERRORMESSAGE(("%s invalid param: pFbuf:0x%08lx, uNumIN:%d, uNum:%d\r\n",
			_fx_, pFormatPrefsBuf, uNumFormatPrefs, uNumLocalFormats));
		return CAPS_E_INVALID_PARAM;
	}
	
	// validate
	for(u=0; u <uNumLocalFormats; u++)
	{
		pTemp =  pFormatPrefsBuf+u;
		// make sure that the format ID is real
		if(IDToIndex(pTemp->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 <uNumLocalFormats; v++)
		{
			if((pTemp->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 <uNumLocalFormats; u++)
	{
		pTemp =  pFormatPrefsBuf+u;			// next entry of the input
		pFmt = pLocalFormats + IDToIndex(pTemp->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;
		
// only the tuning wizard or other profiling app can write these (via other apis only)
		pFmt->wCPUUtilizationEncode	= pTemp->wCPUUtilizationEncode;
		pFmt->wCPUUtilizationDecode	= pTemp->wCPUUtilizationDecode;
//		pFmt->wApplicationPrefOrder	= pTemp->wApplicationPrefOrder;
//		pFmt->uAvgBitrate	= pTemp->
//		pFmt->wCompressionRatio	= pTemp->
		
		// update the registry
		UpdateFormatInRegistry(pFmt);
		
		// now update the sort order contained in IDsByRank
		// 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 IDsByRank
		// 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);
		}
		
	}

#ifdef DEBUG
	for(u=0; u <uNumLocalFormats; u++) {
		pTemp =  pFormatPrefsBuf+u;			// next entry of the input
		pFmt = pLocalFormats + IDToIndex(pTemp->Id);	// identifies this local format
	    DEBUGMSG (ZONE_ACM,("Format %s: Sort Index: %d\r\n",pTemp->szFormat,pTemp->wSortIndex));
    }
#endif

	return hrSuccess;
}

		// update the registry
BOOL CMsiaCapability::UpdateFormatInRegistry(AUDCAP_DETAILS *pAudcapDetails)
{

	FX_ENTRY(("CMsiaCapability::UpdateFormatInRegistry"));
	LPTSTR lpszKeyName = NULL;
	BOOL bRet;
	UINT i;
	if(!pAudcapDetails)
	{
		return FALSE;
	}	

    //Update the CACHE info!!!
    if (pRegFmts) {
        for (i=0;i<pRegFmts->nFormats;i++) {
            if (!lstrcmp (((AUDCAP_DETAILS *)pRegFmts->pData[i])->szFormat,pAudcapDetails->szFormat) &&
				pAudcapDetails->audio_params.uSamplesPerSec == ((AUDCAP_DETAILS *)pRegFmts->pData[i])->audio_params.uSamplesPerSec &&
				pAudcapDetails->audio_params.uBitsPerSample == ((AUDCAP_DETAILS *)pRegFmts->pData[i])->audio_params.uBitsPerSample &&
				pAudcapDetails->uMaxBitrate == ((AUDCAP_DETAILS *)pRegFmts->pData[i])->uMaxBitrate) {

                memcpy (pRegFmts->pData[i],pAudcapDetails,sizeof (AUDCAP_DETAILS));
                break;
            }
        }
    }


	lpszKeyName = AllocRegistryKeyName(	pAudcapDetails->szFormat,
										pAudcapDetails->audio_params.uSamplesPerSec,
										pAudcapDetails->audio_params.uBitsPerSample,
										pAudcapDetails->uMaxBitrate);
	if (!lpszKeyName)
	{
		ERRORMESSAGE(("%s:Alloc failed\r\n",_fx_));
        return(FALSE);
    }

	DEBUGMSG(ZONE_ACM,("%s:updating %s, wPref:0x%04x, bS:%d, bR:%d\r\n",
			_fx_, lpszKeyName, pAudcapDetails->wApplicationPrefOrder,
			pAudcapDetails->bSendEnabled, pAudcapDetails->bRecvEnabled));
	// add this to the registry
	RegEntry reAudCaps(szRegInternetPhone TEXT("\\") szRegMSIPAndH323Encodings,
						HKEY_LOCAL_MACHINE);

	bRet = (ERROR_SUCCESS == reAudCaps.SetValue(lpszKeyName,
												pAudcapDetails,
												sizeof(AUDCAP_DETAILS)));

	FreeRegistryKeyName(lpszKeyName);
    return(bRet);				
}

/***************************************************************************

    Name      : CMsiaCapability::AddFormatEnumHandler

    Purpose   : Enumerates the ACM formats for the case in which we want
				to see all formats, and find the one we need. Used for installable
				codecs when we want to find more info on the format being added

    Parameters:	Standard ACM EnumFormatCallback parameters

    Returns   : BOOL (somewhat upside down logic)
				TRUE - not out format. keep calling.
				FALSE - found our format. don't call anymore

    Comment   :

***************************************************************************/
BOOL CMsiaCapability::AddFormatEnumHandler(HACMDRIVERID hadid,
    LPACMFORMATDETAILS pafd, DWORD_PTR dwInstance, DWORD fdwSupport)
{
	PACM_APP_PARAM pAppParam = (PACM_APP_PARAM) dwInstance;
	LPWAVEFORMATEX lpwfx = pAppParam->lpwfx;
	LPACMFORMATTAGDETAILS paftd = pAppParam->paftd;
	AUDCAP_DETAILS *pAudcapDetails = pAppParam->pAudcapDetails;
	BOOL bRet = TRUE;

	if (pAppParam->hr == NOERROR)
	{
		// already got what we wanted
		bRet = FALSE;
		goto out;
	}
	
	// check to see if this is the format we're looking for
	if ((lpwfx->cbSize != pafd->pwfx->cbSize) ||
		!RtlEqualMemory(lpwfx, pafd->pwfx, sizeof(WAVEFORMATEX)+lpwfx->cbSize))
	{
		// not the one. out of here asap, but tell ACM to keep calling us
		bRet = TRUE;
		goto out;
	}

	// this is the format tag we're looking for
	if (BuildFormatName(pAudcapDetails,
						paftd->szFormatTag,
						pafd->szFormat))
	{
		pAppParam->hr = NOERROR;
	}
	
	// either an error or we found what we want. tell ACM not to call us anymore
	bRet = FALSE;	

out:
	return bRet;
}

/***************************************************************************

    Name      : NormalizeCPUUtilization

    Purpose   : Normalize CPU utilization numbers for an audio format

    Parameters:	pAudcapDetails [in/out] pointer to an AUDCAP_DETAILS structure
					with the wCPUUtilizationEncode and wCPUUtilizationDecode
					correctly initialized. These fields will be scaled in place
					per the machine CPU.

    Returns   : FALSE for error

***************************************************************************/
BOOL NormalizeCPUUtilization (PAUDCAP_DETAILS pAudcapDetails)
{
#define wCPUEncode pAudcapDetails->wCPUUtilizationEncode
#define wCPUDecode pAudcapDetails->wCPUUtilizationDecode
#define BASE_PENTIUM 90
	int nNormalizedSpeed, iFamily=0;

	if (!pAudcapDetails)
	{
		ASSERT(pAudcapDetails);
		return FALSE;
	}

#ifdef	_M_IX86
	GetNormalizedCPUSpeed (&nNormalizedSpeed,&iFamily);
#else
	// profile the CPU on, say, an Alpha
	// see ui\conf\audiocpl.cpp
	iFamily=5;
	nNormalizedSpeed=300;
#endif

	// base is Pentium 90Mhz.
	if (iFamily < 5)
	{	// 486 or below, inlcuding Cyrix parts
		if (nNormalizedSpeed > 50)
		{	// Cyrix or friends. 1.5 the utilization of a P5-90. Make it so.
			wCPUEncode += max(1, wCPUEncode / 2);
			wCPUDecode += max(1, wCPUDecode / 2);
		}
		else
		{	// 486 is half a P5-90. This is not accurate, but good enough
			wCPUEncode = max(1, wCPUEncode * 2);
			wCPUDecode = max(1, wCPUDecode * 2);
		}
	}
	else
	{	// it's a Pentium or TNGs
		// nNormalizedSpeed ALREADY accounts for P-Pro and later families
		wCPUEncode=max(1,((wCPUEncode*BASE_PENTIUM)/nNormalizedSpeed));
		wCPUDecode=max(1,((wCPUDecode*BASE_PENTIUM)/nNormalizedSpeed));
	}

	// disable this format if encode utilization is too high
	// we compare to 80%, since there's no QoS CPU utilization number at
	// this point, and if there was, it would usually select 81%
	if (wCPUEncode > 80)
		pAudcapDetails->bSendEnabled = FALSE;

	return TRUE;
}

/***************************************************************************

    Name      : CMsiaCapability::AddACMFormat

    Purpose   : Adds an ACM format to the list of formats we support

    Parameters:	lpwfx - pointer to the waveformat structure for the added codec
				pAudCapInfo - additional format info that is not in the waveformat
					structure

    Returns   : HRESULT

    Comment   :

***************************************************************************/
HRESULT CMsiaCapability::AddACMFormat (LPWAVEFORMATEX lpwfx, PBASIC_AUDCAP_INFO pAudcapInfo)
{
	HRESULT hr = hrSuccess;
	// initialize cap entry with default values
	AUDCAP_DETAILS cap_entry =
		{WAVE_FORMAT_UNKNOWN,  NONSTD_TERMCAP, STD_CHAN_PARAMS,
		{RTP_DYNAMIC_MIN,  0, 8000, 16},
		0, TRUE, TRUE,
		960, 				// default number of samples per packet
		16000,				// default to 16kbs bitrate
		0, 					// unknown average bitrate
		90, 90,	// default CPU utilization
		PREF_ORDER_UNASSIGNED,	// unassigned sort order
		0,NULL,0,NULL,
		""};
	ACM_APP_PARAM sAppParam = {	this, &cap_entry, ACMAPP_FORMATENUMHANDLER_ADD,
								lpwfx, NULL, CAPS_E_SYSTEM_ERROR, NULL};
		
	/*
	 *	Parameter validation
	 */

	if (!lpwfx || !pAudcapInfo)
	{
		hr = CAPS_E_INVALID_PARAM;
		goto out;
	}
		
	// nBlockAlign of 0 is illegal and will crash NAC
	if (lpwfx->nBlockAlign == 0)
	{
		hr = CAPS_E_INVALID_PARAM;
		goto out;
	}
		
	// only supporting formats with one audio channel
	if (lpwfx->nChannels != 1)
	{
		hr = CAPS_E_INVALID_PARAM;
		goto out;
	}
		
	/*
	 *	Build the AUDCAP_DETALS structure for this format
	 */

	// WAVEFORMAT info first
	// fixup the bits per sample and sample rate fields of audio_params so that
	// the key name can be built
	cap_entry.audio_params.uSamplesPerSec = lpwfx->nSamplesPerSec;
	cap_entry.audio_params.uBitsPerSample = MAKELONG(lpwfx->wBitsPerSample,0);

	// fill in info given in lpwfx, 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).
	cap_entry.wFormatTag = lpwfx->wFormatTag;

	// now add in the caller AUDCAP_INFO information
	CopyAudcapInfo(&cap_entry, pAudcapInfo, 1);

	// normalize the encode and decode CPU utilization numbers
	NormalizeCPUUtilization(&cap_entry);

	// get the values we need to get from the WAVEFORMATEX structure
	CalculateFormatProperties(&cap_entry, lpwfx);

	// set the RTP payload number. We are using a random number from the dynamic range
	// for the installable codecs
	cap_entry.audio_params.RTPPayload = RTP_DYNAMIC_MIN;

	// get ACM to enumerate all formats, and see if we can find this one
	// this call will make ACM call into AddFormatEnumHandler, which will try to
	// match formats returned by ACM with the added format, and if successful,
	// will create a format name for it into cap_entry.szFormat;
	if(!DriverEnum((DWORD_PTR) &sAppParam))
	{
		hr = CAPS_E_NOMATCH;
		goto out;
	}
				
	if (HR_FAILED(sAppParam.hr))
	{
		ERRORMESSAGE(("CMsiaCapability::AddACMFormat: format enum problem\r\n"));
		hr = CAPS_E_NOMATCH;
		goto out;
	}

	// add this to the registry
	if(!UpdateFormatInRegistry(&cap_entry))
	{
		ERRORMESSAGE(("CMsiaCapability::AddACMFormat: can't update registry\r\n"));
		hr = CAPS_E_SYSTEM_ERROR;
		goto out;
	}
	// free the old format cache...
    FreeRegistryFormats(pRegFmts);
	pRegFmts=NULL;

	// reinit to update the list of local formats
    if (!ReInit())
	{
		ERRORMESSAGE(("CMsiaCapability::AddACMFormat: Reinit failed\r\n"));
		hr = CAPS_E_SYSTEM_ERROR;
   		goto out;
	}

out:
	return hr;
}

/***************************************************************************

    Name      : CMsiaCapability::RemoveACMFormat

    Purpose   : Removes an ACM format from the list of formats we support

    Parameters:	lpwfx - pointer to the waveformat structure for the added codec

    Returns   : HRESULT

    Comment   :

***************************************************************************/
HRESULT CMsiaCapability::RemoveACMFormat (LPWAVEFORMATEX lpwfx)
{
	HRESULT hr = hrSuccess;
    HKEY hKey = NULL;
	LPTSTR lpszValueName = NULL;
    DWORD dwErr;
	AUDCAP_DETAILS cap_entry;
	ACM_APP_PARAM sAppParam = {	this, &cap_entry, ACMAPP_FORMATENUMHANDLER_ADD,
								lpwfx, NULL, CAPS_E_SYSTEM_ERROR, NULL};
	
	/*
	 *	Parameter validation
	 */

	if(!lpwfx)
	{
		ERRORMESSAGE(("CMsiaCapability::RemoveACMFormat: NULL WAVEFORMAT pointer\r\n"));
		return CAPS_E_INVALID_PARAM;
	}	

	// nBlockAlign of 0 is illegal and will crash NAC
	if (lpwfx->nBlockAlign == 0)
	{
		hr = CAPS_E_INVALID_PARAM;
		goto out;
	}
		
	// only supporting formats with one audio channel
	if (lpwfx->nChannels != 1)
	{
		hr = CAPS_E_INVALID_PARAM;
		goto out;
	}
		
	/*
	 *	Enumerate ACM formats
	 */

	if(!DriverEnum((DWORD_PTR) &sAppParam))
	{
		ERRORMESSAGE(("CMsiaCapability::RemoveACMFormat: Couldn't find format\r\n"));
		hr = CAPS_E_NOMATCH;
		goto out;
	}
				
	if (HR_FAILED(sAppParam.hr))
	{
		ERRORMESSAGE(("CMsiaCapability::RemoveACMFormat: format enum problem\r\n"));
		hr = CAPS_E_SYSTEM_ERROR;
		goto out;
	}

	lpszValueName = AllocRegistryKeyName(cap_entry.szFormat,
										lpwfx->nSamplesPerSec,
										MAKELONG(lpwfx->wBitsPerSample,0),
										lpwfx->nAvgBytesPerSec * 8);
	if (!lpszValueName)
	{
		ERRORMESSAGE(("CMsiaCapability::RemoveACMFormat: Alloc failed\r\n"));
	    hr = CAPS_E_SYSTEM_ERROR;
	    goto out;
    }

	// Get the key handle
    if (dwErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
					szRegInternetPhone TEXT("\\") szRegMSIPAndH323Encodings, 0,
					KEY_ALL_ACCESS, &hKey))
	{
		ERRORMESSAGE(("CMsiaCapability::RemoveACMFormat: 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;
	}

	// free the old format cache...
    FreeRegistryFormats(pRegFmts);
    pRegFmts=NULL;

	// 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;
}

HRESULT CMsiaCapability::SetCapIDBase (UINT uNewBase)
{
	uCapIDBase = uNewBase;	
	UINT u;
	for (u=0;u<uNumLocalFormats;u++)
	{
    	pLocalFormats[u].H245TermCap.CapId = u + uCapIDBase;
	}
   	return hrSuccess;
}

BOOL CMsiaCapability::IsHostForCapID(MEDIA_FORMAT_ID CapID)
{
	if((CapID >= uCapIDBase) && ((CapID - uCapIDBase) < uNumLocalFormats))
		return TRUE;
	else
		return FALSE;
	
}