1819 lines
51 KiB
C++
1819 lines
51 KiB
C++
/*
|
||
* 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;
|
||
|
||
}
|
||
|
||
|
||
|
||
|