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