You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1819 lines
53 KiB
1819 lines
53 KiB
/*
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|