|
|
#include "precomp.h"
// #define LOG_COMPRESSION_PARAMS 1
// #define LOGPAYLOAD_ON 1
#ifdef LOGPAYLOAD_ON
HANDLE g_DebugFile = (HANDLE)NULL; HANDLE g_TDebugFile = (HANDLE)NULL; #endif
// #define VALIDATE_SBIT_EBIT 1
#ifdef VALIDATE_SBIT_EBIT // { VALIDATE_SBIT_EBIT
DWORD g_dwPreviousEBIT = 0; #endif // } VALIDATE_SBIT_EBIT
#define BUFFER_SIZE 50
#define NUM_FPS_ENTRIES 1
#define NUM_BITDEPTH_ENTRIES 9
#define NUM_RGB_BITDEPTH_ENTRIES 4
#define VIDEO_FORMAT_NUM_RESOLUTIONS 6
#define MAX_NUM_REGISTERED_SIZES 3
#define MAX_VERSION 80 // Needs to be in sync with the MAX_VERSION in dcap\inc\idcap.h
// String resources
#define IDS_FORMAT_1 TEXT("%4.4hs.%4.4hs, %02dbit, %02dfps, %03dx%03d")
#define IDS_FORMAT_2 TEXT("%4.4hs.%04d, %02dbit, %02dfps, %03dx%03d")
#define szRegDeviceKey TEXT("SOFTWARE\\Microsoft\\Conferencing\\CaptureDevices")
#define szRegCaptureDefaultKey TEXT("SOFTWARE\\Microsoft\\Conferencing\\CaptureDefaultFormats")
#define szRegConferencingKey TEXT("SOFTWARE\\Microsoft\\Conferencing")
#define szTotalRegDeviceKey TEXT("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Conferencing\\CaptureDevices")
#define szRegCaptureKey TEXT("CaptureDevices")
#define szRegdwImageSizeKey TEXT("dwImageSize")
#define szRegImageSizesKey TEXT("aImageSizes")
#define szRegNumImageSizesKey TEXT("nNumSizes")
#define szRegdwNumColorsKey TEXT("dwNumColors")
#define szRegdwStreamingModeKey TEXT("dwStreamingMode")
#define szRegdwDialogsKey TEXT("dwDialogs")
#define szRegbmi4bitColorsKey TEXT("bmi4bitColors")
#define szRegbmi8bitColorsKey TEXT("bmi8bitColors")
#define szRegDefaultFormatKey TEXT("DefaultFormat")
EXTERN_C HINSTANCE g_hInst; // Our module handle. defined in nac.cpp
//External function (in msiacaps.cpp) to read reg info in one shot
extern ULONG ReadRegistryFormats (LPCSTR lpszKeyName,CHAR ***pppName,BYTE ***pppData,PUINT pnFormats,DWORD dwDebugSize);
PVCM_APP_ICINFO g_aVCMAppInfo;
int g_nNumVCMAppInfoEntries; int g_nNumFrameSizesEntries; BOOL g_fNewCodecsInstalled;
#ifdef LOGFILE_ON
DWORD g_CompressTime; DWORD g_DecompressTime; HANDLE g_CompressLogFile; HANDLE g_DecompressLogFile; DWORD g_dwCompressBytesWritten; DWORD g_dwDecompressBytesWritten; char g_szCompressBuffer[256]; char g_szDecompressBuffer[256]; DWORD g_OrigCompressTime; DWORD g_OrigDecompressTime; DWORD g_AvgCompressTime; DWORD g_AvgDecompressTime; DWORD g_aCompressTime[4096]; DWORD g_aDecompressTime[4096]; SYSTEMTIME g_SystemTime; #endif
typedef struct tagDejaVu { VIDEOFORMATEX vfx; DWORD dwFlags; } DEJAVU, *PDEJAVU;
#if 1
// Array of known ITU sizes
MYFRAMESIZE g_ITUSizes[8] = { { 0, 0 }, { 128, 96 }, { 176, 144 }, { 352, 288 }, #if !defined(_ALPHA_) && defined(USE_BILINEAR_MSH26X)
{ 80, 64 }, #else
{ 704, 576 }, #endif
{ 1408,1152 }, { 0, 0 }, { 0, 0 } };
// For now, the size of the VIDEOFORMATEX being 1118 even if
// there is no palette, do not enumerate all of the possible
// formats. As soon as you have replaced the BITMAPINFOHEADER
// + Palette by pointers to such structure, enable all the
// sizes.
NCAP_APP_INFO g_awResolutions[VIDEO_FORMAT_NUM_RESOLUTIONS] = { // VIDEO_FORMAT_IMAGE_SIZE_40_30,
// VIDEO_FORMAT_IMAGE_SIZE_64_48,
// VIDEO_FORMAT_IMAGE_SIZE_80_60,
// VIDEO_FORMAT_IMAGE_SIZE_96_64,
// VIDEO_FORMAT_IMAGE_SIZE_112_80,
// VIDEO_FORMAT_IMAGE_SIZE_120_90,
{ VIDEO_FORMAT_IMAGE_SIZE_128_96, 128, 96 }, // VIDEO_FORMAT_IMAGE_SIZE_144_112,
{ VIDEO_FORMAT_IMAGE_SIZE_160_120, 160, 120 }, // VIDEO_FORMAT_IMAGE_SIZE_160_128,
{ VIDEO_FORMAT_IMAGE_SIZE_176_144, 176, 144 }, // VIDEO_FORMAT_IMAGE_SIZE_192_160,
// VIDEO_FORMAT_IMAGE_SIZE_200_150,
// VIDEO_FORMAT_IMAGE_SIZE_208_176,
// VIDEO_FORMAT_IMAGE_SIZE_224_192,
{ VIDEO_FORMAT_IMAGE_SIZE_240_180, 240, 180 }, // VIDEO_FORMAT_IMAGE_SIZE_240_208,
// VIDEO_FORMAT_IMAGE_SIZE_256_224,
// VIDEO_FORMAT_IMAGE_SIZE_272_240,
// VIDEO_FORMAT_IMAGE_SIZE_280_210,
// VIDEO_FORMAT_IMAGE_SIZE_288_256,
// VIDEO_FORMAT_IMAGE_SIZE_304_272,
{ VIDEO_FORMAT_IMAGE_SIZE_320_240, 320, 240 }, // VIDEO_FORMAT_IMAGE_SIZE_320_288,
// VIDEO_FORMAT_IMAGE_SIZE_336_288,
{ VIDEO_FORMAT_IMAGE_SIZE_352_288, 352, 288 }, // VIDEO_FORMAT_IMAGE_SIZE_640_480,
};
#else
// For now, the size of the VIDEOFORMATEX being 1118 even if
// there is no palette, do not enumerate all of the possible
// formats. As soon as you have replaced the BITMAPINFOHEADER
// + Palette by pointers to such structure, enable all the
// sizes.
DWORD g_awResolutions[VIDEO_FORMAT_NUM_RESOLUTIONS] = { // VIDEO_FORMAT_IMAGE_SIZE_40_30,
// VIDEO_FORMAT_IMAGE_SIZE_64_48,
// VIDEO_FORMAT_IMAGE_SIZE_80_60,
// VIDEO_FORMAT_IMAGE_SIZE_96_64,
// VIDEO_FORMAT_IMAGE_SIZE_112_80,
// VIDEO_FORMAT_IMAGE_SIZE_120_90,
VIDEO_FORMAT_IMAGE_SIZE_160_120, // VIDEO_FORMAT_IMAGE_SIZE_144_112,
VIDEO_FORMAT_IMAGE_SIZE_128_96, // VIDEO_FORMAT_IMAGE_SIZE_160_128,
VIDEO_FORMAT_IMAGE_SIZE_240_180, // VIDEO_FORMAT_IMAGE_SIZE_192_160,
// VIDEO_FORMAT_IMAGE_SIZE_200_150,
// VIDEO_FORMAT_IMAGE_SIZE_208_176,
// VIDEO_FORMAT_IMAGE_SIZE_224_192,
VIDEO_FORMAT_IMAGE_SIZE_176_144, // VIDEO_FORMAT_IMAGE_SIZE_240_208,
// VIDEO_FORMAT_IMAGE_SIZE_256_224,
// VIDEO_FORMAT_IMAGE_SIZE_272_240,
// VIDEO_FORMAT_IMAGE_SIZE_280_210,
// VIDEO_FORMAT_IMAGE_SIZE_288_256,
// VIDEO_FORMAT_IMAGE_SIZE_304_272,
VIDEO_FORMAT_IMAGE_SIZE_320_240, // VIDEO_FORMAT_IMAGE_SIZE_320_288,
// VIDEO_FORMAT_IMAGE_SIZE_336_288,
VIDEO_FORMAT_IMAGE_SIZE_352_288, // VIDEO_FORMAT_IMAGE_SIZE_640_480,
}; #endif
//int g_aiFps[NUM_FPS_ENTRIES] = {3, 7, 15};
int g_aiFps[NUM_FPS_ENTRIES] = {30}; // The order of the bit depths matches what I think is the
// preferred format if more than one is supported.
// For color, 16bit is almost as good as 24 but uses less memory
// and is faster for color QuickCam.
// For greyscale, 16 greyscale levels is Ok, not as good as 64,
// but Greyscale QuickCam is too slow at 64 levels.
int g_aiBitDepth[NUM_BITDEPTH_ENTRIES] = {9, 12, 12, 16, 16, 16, 24, 4, 8}; int g_aiNumColors[NUM_BITDEPTH_ENTRIES] = {VIDEO_FORMAT_NUM_COLORS_YVU9, VIDEO_FORMAT_NUM_COLORS_I420, VIDEO_FORMAT_NUM_COLORS_IYUV, VIDEO_FORMAT_NUM_COLORS_YUY2, VIDEO_FORMAT_NUM_COLORS_UYVY, VIDEO_FORMAT_NUM_COLORS_65536, VIDEO_FORMAT_NUM_COLORS_16777216, VIDEO_FORMAT_NUM_COLORS_16, VIDEO_FORMAT_NUM_COLORS_256}; int g_aiFourCCCode[NUM_BITDEPTH_ENTRIES] = {VIDEO_FORMAT_YVU9, VIDEO_FORMAT_I420, VIDEO_FORMAT_IYUV, VIDEO_FORMAT_YUY2, VIDEO_FORMAT_UYVY, VIDEO_FORMAT_BI_RGB, VIDEO_FORMAT_BI_RGB, VIDEO_FORMAT_BI_RGB, VIDEO_FORMAT_BI_RGB}; int g_aiClrUsed[NUM_BITDEPTH_ENTRIES] = {0, 0, 0, 0, 0, 0, 0, 16, 256};
PVCMSTREAMHEADER DeQueVCMHeader(PVCMSTREAM pvs); MMRESULT VCMAPI vcmDefaultFormatWriteToReg(LPSTR szDeviceName, LPSTR szDeviceVersion, LPBITMAPINFOHEADER lpbmih);
#define IsVCMHeaderPrepared(pvh) ((pvh)->fdwStatus & VCMSTREAMHEADER_STATUSF_PREPARED)
#define MarkVCMHeaderPrepared(pvh) ((pvh)->fdwStatus |= VCMSTREAMHEADER_STATUSF_PREPARED)
#define MarkVCMHeaderUnprepared(pvh) ((pvh)->fdwStatus &=~VCMSTREAMHEADER_STATUSF_PREPARED)
#define IsVCMHeaderInQueue(pvh) ((pvh)->fdwStatus & VCMSTREAMHEADER_STATUSF_INQUEUE)
#define MarkVCMHeaderInQueue(pvh) ((pvh)->fdwStatus |= VCMSTREAMHEADER_STATUSF_INQUEUE)
#define MarkVCMHeaderUnQueued(pvh) ((pvh)->fdwStatus &=~VCMSTREAMHEADER_STATUSF_INQUEUE)
#define IsVCMHeaderDone(pvh) ((pvh)->fdwStatus & VCMSTREAMHEADER_STATUSF_DONE)
#define MarkVCMHeaderDone(pvh) ((pvh)->fdwStatus |= VCMSTREAMHEADER_STATUSF_DONE)
#define MarkVCMHeaderNotDone(pvh) ((pvh)->fdwStatus &=~VCMSTREAMHEADER_STATUSF_DONE)
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmMetrics | This function returns various metrics for the Video * Compression Manager (VCM) or related VCM objects. * * @parm HVCMOBJ | hvo | Specifies the VCM object to query for the metric * specified in <p uMetric>. This argument may be NULL for some * queries. * * @parm UINT | uMetric | Specifies the metric index to be returned in * <p pMetric>. * * @flag VCM_METRIC_COUNT_COMPRESSORS | Specifies that the returned value is * the number of global VCM compressors in * the system. The <p hvo> argument must be NULL for this metric index. * The <p pMetric> argument must point to a buffer of a size equal to a * DWORD. * * @flag VCM_METRIC_COUNT_DECOMPRESSORS | Specifies that the returned value is * the number of global VCM decompressors in * the system. The <p hvo> argument must be NULL for this metric index. * The <p pMetric> argument must point to a buffer of a size equal to a * DWORD. * * @flag VCM_METRIC_MAX_SIZE_FORMAT | Specifies that the returned value * is the size of the largest <t VIDEOFORMATEX> structure. If <p hvo> * is NULL, then the return value is the largest <t VIDEOFORMATEX> * structure in the system. If <p hvo> identifies an open instance * of an VCM driver (<t HVCMDRIVER>) or a VCM driver identifier * (<t HVCMDRIVERID>), then the largest <t VIDEOFORMATEX> * structure for that driver is returned. The <p pMetric> argument must * point to a buffer of a size equal to a DWORD. This metric is not allowed * for a VCM stream handle (<t HVCMSTREAM>). * * @parm LPVOID | pMetric | Specifies a pointer to the buffer that will * receive the metric details. The exact definition depends on the * <p uMetric> index. * * @rdesc Returns zero if the function was successful. Otherwise, it returns * a non-zero error number. Possible error returns are: * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * @flag MMSYSERR_INVALPARAM | The <p pMetric> parameter is invalid. * @flag MMSYSERR_NOTSUPPORTED | The <p uMetric> index is not supported. * @flag VCMERR_NOTPOSSIBLE | The <p uMetric> index cannot be returned * for the specified <p hvo>. * ***************************************************************************/ MMRESULT VCMAPI vcmMetrics(HVCMOBJ hao, UINT uMetric, LPVOID pMetric) { MMRESULT mmr; ICINFO ICinfo;
if (!pMetric) { ERRORMESSAGE(("vcmMetrics: Specified pointer is invalid, pMetric=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); }
switch (uMetric) { case VCM_METRIC_MAX_SIZE_FORMAT: // For now, assume all VIDEOFORMATEX structures have identical sizes
*(LPDWORD)pMetric = (DWORD)sizeof(VIDEOFORMATEX); mmr = (MMRESULT)MMSYSERR_NOERROR; break; case VCM_METRIC_MAX_SIZE_BITMAPINFOHEADER: // For now, assume all BITMAPINFOHEADER structures have identical sizes
*(LPDWORD)pMetric = (DWORD)sizeof(BITMAPINFOHEADER); mmr = (MMRESULT)MMSYSERR_NOERROR; break; case VCM_METRIC_COUNT_DRIVERS: case VCM_METRIC_COUNT_COMPRESSORS: for (*(LPDWORD)pMetric = 0; ICInfo(ICTYPE_VIDEO, *(LPDWORD)pMetric, &ICinfo); (*(LPDWORD)pMetric)++) ; mmr = (MMRESULT)MMSYSERR_NOERROR; break; default: ERRORMESSAGE(("vcmMetrics: Specified index is invalid, uMetric=%ld\r\n", uMetric)); mmr = (MMRESULT)MMSYSERR_NOTSUPPORTED; break; }
return (mmr); }
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmDriverDetails | This function queries a specified * Video Compression Manager (VCM) driver to determine its driver details. * * @parm PVCMDRIVERDETAILS | pvdd | Pointer to a <t VCMDRIVERDETAILS> * structure that will receive the driver details. The * <e VCMDRIVERDETAILS.cbStruct> member must be initialized to the * size, in bytes, of the structure. The <e VCMDRIVERDETAILS.fccType> member * must be initialized to the four-character code indicating the type of * stream being compressed or decompressed. Specify VCMDRIVERDETAILS_FCCTYPE_VIDEOCODEC * for video streams. The <e VCMDRIVERDETAILS.fccHandler> member must be initialized * to the four-character code identifying the compressor. * * @rdesc Returns zero if the function was successful. Otherwise, it returns * a non-zero error number. Possible error returns are: * @flag MMSYSERR_NODRIVER | No matching codec is present. * @flag MMSYSERR_INVALPARAM | One or more arguments passed is invalid. * * @xref <f vcmDriverEnum> ***************************************************************************/ MMRESULT VCMAPI vcmDriverDetails(PVCMDRIVERDETAILS pvdd) { DWORD fccHandler; ICINFO ICinfo; HIC hIC;
// Check input params
if (!pvdd) { ERRORMESSAGE(("vcmDriverDetails: Specified pointer is invalid, pvdd=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); }
// Make fccHandler uppercase and back it up
fccHandler = pvdd->fccHandler; if (fccHandler > 256) CharUpperBuff((LPTSTR)&fccHandler, sizeof(DWORD));
// Try to open the codec
if (hIC = ICOpen(ICTYPE_VIDEO, fccHandler, ICMODE_QUERY)) { // Get the details
ICGetInfo(hIC, &ICinfo, sizeof(ICINFO));
// Restore fccHandler
ICinfo.fccHandler = fccHandler;
// VCMDRIVERDETAILS and ICINFO are identical structures
CopyMemory(pvdd, &ICinfo, sizeof(VCMDRIVERDETAILS));
// Close the codec
ICClose(hIC); } else return ((MMRESULT)MMSYSERR_NODRIVER);
return ((MMRESULT)MMSYSERR_NOERROR); }
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmFormatDetails | This function queries the Video Compression * Manager (VCM) for details on format for a specific video format. * * @parm PVCMFORMATDETAILS | pvfd | Specifies a pointer to the * <t VCMFORMATDETAILS> structure that is to receive the format * details for the given embedded pointer to a <t VIDEOFORMATEX> structure. * * @rdesc Returns zero if the function was successful. Otherwise, it returns * a non-zero error number. Possible error returns are: * @flag MMSYSERR_NODRIVER | No matching codec is present. * @flag MMSYSERR_INVALPARAM | One or more arguments passed is invalid. * * @xref <f vcmDriverDetails> ***************************************************************************/ MMRESULT VCMAPI vcmFormatDetails(PVCMFORMATDETAILS pvfd) { MMRESULT mmr = (MMRESULT)MMSYSERR_NOERROR; DWORD fccHandler; DWORD fccType; HIC hIC; char szBuffer[BUFFER_SIZE]; // Could be smaller.
int iLen;
// Check input params
if (!pvfd) { ERRORMESSAGE(("vcmDriverDetails: Specified pointer is invalid, pvdd=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if (!pvfd->pvfx) { ERRORMESSAGE(("vcmDriverDetails: Specified pointer is invalid, pvdd->pvfx=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); }
// Make fccHandler uppercase and back it up
fccHandler = pvfd->pvfx->dwFormatTag; fccType = ICTYPE_VIDEO; if (fccHandler > 256) CharUpperBuff((LPTSTR)&fccHandler, sizeof(DWORD));
// Try to open the codec
if (hIC = ICOpen(fccType, pvfd->pvfx->dwFormatTag, ICMODE_QUERY)) { // Check if the codec supports the format
if (ICDecompressQuery(hIC, &pvfd->pvfx->bih, (LPBITMAPINFOHEADER)NULL) == ICERR_OK) { #if 0
if (ICCompressQuery(hIC, (LPBITMAPINFOHEADER)NULL, &pvfd->pvfx->bih) == ICERR_OK) { #endif
// Now complete the format details info, overwrite some of the fields of
// the VIDEOFORMATEX structure too, just in case we were passed bogus values...
pvfd->pvfx->nSamplesPerSec = g_aiFps[0];
if (pvfd->pvfx->dwFormatTag > 256) wsprintf(szBuffer, IDS_FORMAT_1, (LPSTR)&fccType, (LPSTR)&fccHandler, pvfd->pvfx->bih.biBitCount, pvfd->pvfx->nSamplesPerSec, pvfd->pvfx->bih.biWidth, pvfd->pvfx->bih.biHeight); else wsprintf(szBuffer, IDS_FORMAT_2, (LPSTR)&fccType, fccHandler, pvfd->pvfx->bih.biBitCount, pvfd->pvfx->nSamplesPerSec, pvfd->pvfx->bih.biWidth, pvfd->pvfx->bih.biHeight); iLen = MultiByteToWideChar(GetACP(), 0, szBuffer, -1, pvfd->szFormat, 0); MultiByteToWideChar(GetACP(), 0, szBuffer, -1, pvfd->szFormat, iLen); #if 0
} else mmr = (MMRESULT)MMSYSERR_NODRIVER; #endif
} else mmr = (MMRESULT)MMSYSERR_NODRIVER;
// Close the codec
ICClose(hIC); } else mmr = (MMRESULT)MMSYSERR_NODRIVER;
return (mmr); }
/*****************************************************************************
* @doc EXTERNAL DEVCAPSFUNC * * @func MMRESULT | vcmGetDevCaps | This function queries a specified * video capture input device to determine its capabilities. * * @parm UINT | uDevice | Specifies the video capture input device ID. * * @parm PVIDEOINCAPS | pvc | Specifies a pointer to a <t VIDEOINCAPS> * structure. This structure is filled with information about the * capabilities of the device. * * @parm UINT | cbvc | Specifies the size of the <t VIDEOINCAPS> structure. * * @rdesc The return value is zero if the function is successful. Otherwise, it returns * an error number. Possible error values include the following: * @flag MMSYSERR_INVALHANDLE | Specified device handle is invalid. * @flag MMSYSERR_BADDEVICEID | Specified device device ID is invalid. * @flag MMSYSERR_INVALPARAM | Specified pointer to structure is invalid. * @flag MMSYSERR_NODRIVER | No capture device driver or device is present. * @flag VCMERR_NONSPECIFIC | The capture driver failed to provide description information. * * @comm Only <p cbwc> bytes (or less) of information is copied to the location * pointed to by <p pvc>. If <p cbwc> is zero, nothing is copied, and * the function returns zero. * * If the ID of the capture device passed is VIDEO_MAPPER, the first device in the list * of installed capture devices is considered. * * @devnote You never return MMSYSERR_NODRIVER. Is there a way to make a difference * between a call failing because there is no device, or because of a device failure? * * @xref <f videoDevCapsProfile> <f videoDevCapsReadFromReg> <f videoDevCapsWriteToReg> ****************************************************************************/ MMRESULT VCMAPI vcmGetDevCaps(UINT uDevice, PVIDEOINCAPS pvc, UINT cbvc) { MMRESULT mmr; FINDCAPTUREDEVICE fcd;
// Check input params
if ((uDevice >= MAXVIDEODRIVERS) && (uDevice != VIDEO_MAPPER)) { ERRORMESSAGE(("vcmGetDevCaps: Specified capture device ID is invalid, uDevice=%ld (expected values are 0x%lX or between 0 and %ld)\r\n", uDevice, VIDEO_MAPPER, MAXVIDEODRIVERS-1)); return ((MMRESULT)MMSYSERR_BADDEVICEID); } if (!pvc) { ERRORMESSAGE(("vcmGetDevCaps: Specified pointer is invalid, pvc=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if (!cbvc) { ERRORMESSAGE(("vcmGetDevCaps: Specified structure size is invalid, cbvc=%ld\r\n", cbvc)); return ((MMRESULT)MMSYSERR_NOERROR); }
// Get the driver name and version number
fcd.dwSize = sizeof (FINDCAPTUREDEVICE); if (uDevice == VIDEO_MAPPER) { if (!FindFirstCaptureDevice(&fcd, NULL)) { ERRORMESSAGE(("vcmGetDevCaps: FindFirstCaptureDevice() failed\r\n")); return ((MMRESULT)VCMERR_NONSPECIFIC); } } else { if (!FindFirstCaptureDeviceByIndex(&fcd, uDevice)) { ERRORMESSAGE(("vcmGetDevCaps: FindFirstCaptureDevice() failed\r\n")); return ((MMRESULT)VCMERR_NONSPECIFIC); } }
// Set default values
pvc->dwImageSize = pvc->dwNumColors = (DWORD)NULL; pvc->dwStreamingMode = STREAMING_PREFER_FRAME_GRAB; pvc->dwDialogs = FORMAT_DLG_OFF | SOURCE_DLG_ON;
//Look for a specific version of the driver first....
lstrcpy(pvc->szDeviceName, fcd.szDeviceDescription); lstrcpy(pvc->szDeviceVersion, fcd.szDeviceVersion);
// Based on the name and version number of the driver, set capabilities.
// We first try to look them up from the registry. If this is a very popular
// board/camera, chances are that we have set the key at install time already.
// If we can't find the key, we profile the hardware and save the results
// to the registry.
if (vcmDevCapsReadFromReg(pvc->szDeviceName, pvc->szDeviceVersion,pvc, cbvc) != MMSYSERR_NOERROR) {
//Didn't find the specific version, try it again, with NULL version info
pvc->szDeviceVersion[0]= (char) NULL; if (vcmDevCapsReadFromReg(pvc->szDeviceName, NULL,pvc, cbvc) != MMSYSERR_NOERROR) { DEBUGMSG (ZONE_VCM, ("vcmGetDevCaps: Unknown capture hardware found. Profiling...\r\n")); lstrcpy(pvc->szDeviceVersion, fcd.szDeviceVersion);
if ((mmr = vcmDevCapsProfile(uDevice, pvc, cbvc)) == MMSYSERR_NOERROR) { // record this default in the registry
if (pvc->szDeviceName[0] != '\0') { vcmDevCapsWriteToReg(pvc->szDeviceName, pvc->szDeviceVersion, pvc, cbvc); } else { //fcd.szDeviceName is the Driver Name
vcmDevCapsWriteToReg(fcd.szDeviceName, pvc->szDeviceVersion, pvc, cbvc); }
} else { ERRORMESSAGE(("vcmGetDevCaps: vcmDevCapsProfile() failed\r\n")); return (mmr); } } }
return ((MMRESULT)MMSYSERR_NOERROR); }
/****************************************************************************
* @doc INTERNAL COMPFUNC * * @func MMRESULT | AppICInfo | The <f AppICInfo> function * will either call the standard ICInfo function * function continues enumerating until there are no more suitable * formats for the format tag or the callback function returns FALSE. * ***************************************************************************/
/*
* NOTE: * * ICInfo returns TRUE on success and FALSE on failure. The documentation suggests * otherwise and is wrong. AppICInfo returns the same. */
BOOL VFWAPI AppICInfo(DWORD fccType, DWORD fccHandler, ICINFO FAR * lpicinfo, DWORD fdwEnum) { if ((fdwEnum & VCM_FORMATENUMF_ALLMASK) == VCM_FORMATENUMF_ALL) { // enumerating all formats, just do the standard ICInfo
return ICInfo(fccType, fccHandler, lpicinfo); } else { // only enumerating specific formats
// are we done ?
if (fccHandler >= (DWORD)g_nNumVCMAppInfoEntries) { // we're done enumerating app-specific formats
return FALSE; }
lpicinfo->fccType = g_aVCMAppInfo[fccHandler].fccType; lpicinfo->fccHandler = g_aVCMAppInfo[fccHandler].fccHandler; return TRUE; } }
BOOL vcmBuildDefaultEntries (void) {
//Yikes! Reg. problem (or first boot) instantiate only the minimum...
#if !defined(_ALPHA_) && defined(USE_BILINEAR_MSH26X)
g_nNumVCMAppInfoEntries=3; #else
g_nNumVCMAppInfoEntries=2; #endif
g_nNumFrameSizesEntries=MAX_NUM_REGISTERED_SIZES; g_fNewCodecsInstalled=FALSE;
//Allocate space for the VCM_APP_ICINFO structure (zero init'd)
if (!(g_aVCMAppInfo = (VCM_APP_ICINFO *)MemAlloc (g_nNumVCMAppInfoEntries*sizeof (VCM_APP_ICINFO)))) { //Aiiie!
ERRORMESSAGE (("vcmBDE: Memory Allocation Failed!\r\n")); return FALSE; }
//H.263
g_aVCMAppInfo[0].fccType=ICTYPE_VIDEO; #ifndef _ALPHA_
g_aVCMAppInfo[0].fccHandler=VIDEO_FORMAT_MSH263; #else
g_aVCMAppInfo[0].fccHandler=VIDEO_FORMAT_DECH263; #endif
g_aVCMAppInfo[0].framesize[0].biWidth=128; g_aVCMAppInfo[0].framesize[0].biHeight=96; g_aVCMAppInfo[0].framesize[1].biWidth=176; g_aVCMAppInfo[0].framesize[1].biHeight=144; g_aVCMAppInfo[0].framesize[2].biWidth=352; g_aVCMAppInfo[0].framesize[2].biHeight=288;
//H.261
g_aVCMAppInfo[1].fccType=ICTYPE_VIDEO; #ifndef _ALPHA_
g_aVCMAppInfo[1].fccHandler=VIDEO_FORMAT_MSH261; #else
g_aVCMAppInfo[1].fccHandler=VIDEO_FORMAT_DECH261; #endif
g_aVCMAppInfo[1].framesize[0].biWidth=0; g_aVCMAppInfo[1].framesize[0].biHeight=0; g_aVCMAppInfo[1].framesize[1].biWidth=176; g_aVCMAppInfo[1].framesize[1].biHeight=144; g_aVCMAppInfo[1].framesize[2].biWidth=352; g_aVCMAppInfo[1].framesize[2].biHeight=288;
#if !defined(_ALPHA_) && defined(USE_BILINEAR_MSH26X)
//H.26X
g_aVCMAppInfo[2].fccType=ICTYPE_VIDEO; g_aVCMAppInfo[2].fccHandler=VIDEO_FORMAT_MSH26X; g_aVCMAppInfo[2].framesize[0].biWidth=80; g_aVCMAppInfo[2].framesize[0].biHeight=64; g_aVCMAppInfo[2].framesize[1].biWidth=128; g_aVCMAppInfo[2].framesize[1].biHeight=96; g_aVCMAppInfo[2].framesize[2].biWidth=176; g_aVCMAppInfo[2].framesize[2].biHeight=144; #endif
return TRUE; }
BOOL vcmFillGlobalsFromRegistry (void) {
int i,j,k,iFormats,iOffset; DWORD *pTmp; BOOL bKnown; MYFRAMESIZE *pTmpFrame; char **pVCMNames; VIDCAP_DETAILS **pVCMData; UINT nFormats;
//Read the registry for all the keys that we care about
//We're loading the values of HKLM\Software\Microsoft\Internet Audio\VCMEncodings
if (ReadRegistryFormats(szRegInternetPhone TEXT("\\") szRegInternetPhoneVCMEncodings, &pVCMNames,(BYTE ***)&pVCMData,&nFormats,sizeof (VIDCAP_DETAILS)) != ERROR_SUCCESS) { ERRORMESSAGE (("vcmFillGlobalsFromRegistry, couldn't build formats from registry\r\n")); return (vcmBuildDefaultEntries()); }
//Minimum number of frame and format sizes;
g_nNumFrameSizesEntries=MAX_NUM_REGISTERED_SIZES; g_nNumVCMAppInfoEntries=0; g_fNewCodecsInstalled=FALSE;
//Allocate a temp buffer of size of nFormats, use this to track various things
if (!(pTmp = (DWORD *)MemAlloc (nFormats * sizeof (DWORD)))) {
ERRORMESSAGE (("vcmFillGlobalsFromRegistry: Memory Allocation Failed!\r\n")); return FALSE; }
//Find the number of formats,
for (i=0;i< (int )nFormats;i++) { bKnown=FALSE; for (j=0;j<g_nNumVCMAppInfoEntries;j++) { if (pVCMData[i]->dwFormatTag == pTmp[j]) { bKnown=TRUE; break; } } if (!bKnown) { //something new
pTmp[g_nNumVCMAppInfoEntries++]=pVCMData[i]->dwFormatTag; g_fNewCodecsInstalled=TRUE; } }
//Allocate space for the VCM_APP_ICINFO structure (zero init'd)
if (g_aVCMAppInfo != NULL) { MemFree(g_aVCMAppInfo); }
if (!(g_aVCMAppInfo = (VCM_APP_ICINFO *)MemAlloc (g_nNumVCMAppInfoEntries*sizeof (VCM_APP_ICINFO)))) { //Aiiie!
MemFree (pTmp); ERRORMESSAGE (("vcmFillGlobalsFromRegistry: Memory Allocation Failed!\r\n")); return FALSE; }
//Fill out the basic information.
//All elements have a certain commonality
for (j=0;j<g_nNumVCMAppInfoEntries;j++) {
g_aVCMAppInfo[j].fccType=ICTYPE_VIDEO; g_aVCMAppInfo[j].fccHandler=pTmp[j];
//Known local formats
iFormats=0;
for (i=0;i<(int )nFormats;i++) { if (pTmp[j] == pVCMData[i]->dwFormatTag) { //Ok, add the registry size, if we don't have it listed
bKnown=FALSE; for (k=0;k<iFormats;k++) { if (g_aVCMAppInfo[j].framesize[k].biWidth == pVCMData[i]->video_params.biWidth && g_aVCMAppInfo[j].framesize[k].biHeight == pVCMData[i]->video_params.biHeight ) {
bKnown=TRUE; break; } } if (!bKnown) { iOffset=pVCMData[i]->video_params.enumVideoSize; g_aVCMAppInfo[j].framesize[iOffset].biWidth = (WORD)pVCMData[i]->video_params.biWidth; g_aVCMAppInfo[j].framesize[iOffset].biHeight = (WORD)pVCMData[i]->video_params.biHeight; iFormats++; } } }
}
//Now, build the DCAP_APP_INFO ptr
//Max * is #entries * MAX_NUM_REGISTERED_SIZES
if (!(pTmpFrame = (MYFRAMESIZE *)MemAlloc ((g_nNumVCMAppInfoEntries*MAX_NUM_REGISTERED_SIZES)*sizeof (DWORD)))) { //Aiiie!
MemFree (pTmp); ERRORMESSAGE (("vcmFillGlobalsFromRegistry: Memory Allocation Failed!\r\n")); return FALSE; }
iFormats=0;
for (j=0;j<g_nNumVCMAppInfoEntries;j++) {
//Magic # of frame sizes per format
for (k=0;k < MAX_NUM_REGISTERED_SIZES;k++) { bKnown=FALSE; for (i=0;i<iFormats;i++) { if ( (g_aVCMAppInfo[j].framesize[k].biWidth == pTmpFrame[i].biWidth && g_aVCMAppInfo[j].framesize[k].biHeight == pTmpFrame[i].biHeight) || (!g_aVCMAppInfo[j].framesize[k].biWidth && !g_aVCMAppInfo[j].framesize[k].biHeight) ){ bKnown=TRUE; break; } } if (!bKnown) { pTmpFrame[iFormats].biWidth = g_aVCMAppInfo[j].framesize[k].biWidth; pTmpFrame[iFormats++].biHeight = g_aVCMAppInfo[j].framesize[k].biHeight; } } }
g_nNumFrameSizesEntries=iFormats;
//Free up the ReadRegistryEntries memory...
for (i=0;i<(int) nFormats; i++) { MemFree (pVCMNames[i]); MemFree (pVCMData[i]); }
MemFree (pVCMNames); MemFree (pVCMData);
MemFree (pTmp); MemFree (pTmpFrame);
return TRUE; }
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmFormatEnum | The <f vcmFormatEnum> function * enumerates video formats available. The <f vcmFormatEnum> * function continues enumerating until there are no more suitable * formats for the format tag or the callback function returns FALSE. * * @parm UINT | uDevice | Specifies the capture device ID. * * @parm VCMFORMATENUMCB | fnCallback | Specifies the procedure-instance * address of the application-defined callback function. * * @parm PVCMDRIVERDETAILS | pvdd | Specifies a pointer to the * <t VCMDRIVERDETAILS> structure that is to receive the driver details * passed to the <p fnCallback> function. * * @parm PVCMFORMATDETAILS | pvfd | Specifies a pointer to the * <t VCMFORMATDETAILS> structure that is to receive the format details * passed to the <p fnCallback> function. This structure must have the * <e VCMFORMATDETAILS.cbStruct>, <e VCMFORMATDETAILS.pvfx>, and * <e VCMFORMATDETAILS.cbvfx> members of the <t VCMFORMATDETAILS> * structure initialized. The <e VCMFORMATDETAILS.dwFormatTag> member * must also be initialized to either VIDEO_FORMAT_UNKNOWN or a * valid format tag. * * @parm DWORD | dwInstance | Specifies a 32-bit, application-defined value * that is passed to the callback function along with VCM format details. * * @parm DWORD | fdwEnum | Specifies flags for enumerating formats that can be * generated, or formats that can be decompressed. * * @flag VCM_FORMATENUMF_INPUT | Specifies that the format enumeration should only * return the video formats that can be transmitted. * * @flag VCM_FORMATENUMF_OUTPUT | Specifies that the format enumeration should only * return the video formats that can be received. * * @flag VCM_FORMATENUMF_BOTH | Specifies that the format enumeration should * return the video formats that can be received and transmitted. * * @flag VCM_FORMATENUMF_APP | Specifies that the format enumeration should * enumerate only video formats known to the application * * @flag VCM_FORMATENUMF_ALL | Specifies that the format enumeration should * enumerate all video formats known to VCM * * @rdesc Returns zero if the function was successful. Otherwise, it returns * a non-zero error number. Possible error returns are: * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * @flag MMSYSERR_INVALFLAG | One or more flags are invalid. * @flag MMSYSERR_INVALPARAM | One or more arguments passed are invalid. * @flag MMSYSERR_NOMEM | A memory allocation failed. * @flag MMSYSERR_BADDEVICEID | Specified device device ID is invalid. * @flag VCMERR_NOTPOSSIBLE | The details for the format cannot be * returned. * * @comm The <f vcmFormatEnum> function will return MMSYSERR_NOERROR * (zero) if no suitable VCM drivers are installed. Moreover, the * callback function will not be called. * * @xref <f vcmFormatEnumCallback> ***************************************************************************/ MMRESULT VCMAPI vcmFormatEnum( UINT uDevice, VCMFORMATENUMCB fnCallback, PVCMDRIVERDETAILS pvdd, PVCMFORMATDETAILS pvfd, DWORD_PTR dwInstance, DWORD fdwEnum) { int i, j, k, l, m; HIC hIC; ICINFO ICinfo; BITMAPINFO bmi; DWORD dw; char szBuffer[BUFFER_SIZE]; // Could be smaller.
int iLen; VIDEOINCAPS vic; PDEJAVU pdvDejaVuCurr, pdvDejaVu; BOOL bDejaVu, fUnsupportedInputSize, fUnsupportedBitDepth; DWORD fccHandler; int iNumCaps = 0; // Num of valid caps into the advDejaVu matrix
// Check input params
if (!pvdd) { ERRORMESSAGE(("vcmFormatEnum: Specified pointer is invalid, pvdd=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if (!pvfd) { ERRORMESSAGE(("vcmFormatEnum: Specified pointer is invalid, pvfd=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if (!(VCM_FORMATENUMF_TYPEMASK & fdwEnum)) { ERRORMESSAGE(("vcmFormatEnum: Specified mask is invalid, fdwEnum=0x%lX\r\n", fdwEnum)); return ((MMRESULT)MMSYSERR_INVALFLAG); } if ((uDevice >= MAXVIDEODRIVERS) && (uDevice != VIDEO_MAPPER)) { ERRORMESSAGE(("vcmFormatEnum: Specified capture device ID is invalid, uDevice=%ld (expected values are 0x%lX or between 0 and %ld)\r\n", uDevice, VIDEO_MAPPER, MAXVIDEODRIVERS-1)); return ((MMRESULT)MMSYSERR_BADDEVICEID); }
//Build the system VCM globals
if (!vcmFillGlobalsFromRegistry ()) { ERRORMESSAGE (("vcmFormatEnum, couldn't build formats from registry\r\n")); return (VCMERR_NOTPOSSIBLE); }
// We need to remember what we have already enumerated
// The formats already enumerated are stored in the following matrix
if (!(pdvDejaVu = (PDEJAVU)MemAlloc(g_nNumFrameSizesEntries * NUM_BITDEPTH_ENTRIES * NUM_FPS_ENTRIES * sizeof(DEJAVU)))) { ERRORMESSAGE(("vcmFormatEnum: A memory allocation failed\r\n")); return ((MMRESULT)MMSYSERR_NOMEM); }
// If we enumerate formats we can generate, they need to be in sync with what
// the capture hardware can actually produce, that is RGB4, RGB8, RGB16, RGB24, YUY2, UYVY, YVU9, I420 or IYUV.
if ((fdwEnum & VCM_FORMATENUMF_INPUT) || (fdwEnum & VCM_FORMATENUMF_BOTH)) { if (vcmGetDevCaps(uDevice, &vic, sizeof(VIDEOINCAPS)) != MMSYSERR_NOERROR) { if (fdwEnum & VCM_FORMATENUMF_INPUT) return ((MMRESULT)MMSYSERR_NOERROR); else fdwEnum = VCM_FORMATENUMF_OUTPUT; } }
// We're asked to enumerate all the formats that this machine can render or transmit.
// We can send or render all the RGB formats, in which case they will not be
// compressed/decompressed, but directly transmitted/rendered by the UI. But still, someone needs
// to enumerate these. This is done here.
// We, of course, also enumerate the formats that we can decompress and the ones we can generate.
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biCompression = BI_RGB; bmi.bmiHeader.biXPelsPerMeter = 0; bmi.bmiHeader.biYPelsPerMeter = 0; bmi.bmiHeader.biClrUsed = 0; bmi.bmiHeader.biClrImportant = 0;
// Now enumerate real compressors
// for (i=0; ICInfo(ICTYPE_VIDEO, i, &ICinfo); i++) == NO GOOD:
// We need to enumerate everything and then filter on
// the value of fccHandler, because some codecs will fail to
// enum entirely if the fccType parameter to ICInfo is non null.
// SOMEONE should be shot...
for (i=0; AppICInfo(0, i, &ICinfo, fdwEnum); i++, iNumCaps = 0) { // Get the details of the ICINFO structure
if ((ICinfo.fccType == ICTYPE_VIDEO) && (ICInfo(ICinfo.fccType, ICinfo.fccHandler, &ICinfo))) { // Make fccHandler uppercase and back it up
if (ICinfo.fccHandler > 256) CharUpperBuff((LPTSTR)&ICinfo.fccHandler, sizeof(DWORD)); fccHandler = ICinfo.fccHandler;
// If the client returns FALSE we need to terminate the enumeration process
if (hIC = ICOpen(ICinfo.fccType, ICinfo.fccHandler, ICMODE_QUERY)) { // Enable H.26x codecs
#ifndef _ALPHA_
#ifdef USE_BILINEAR_MSH26X
if ((ICinfo.fccHandler == VIDEO_FORMAT_MSH263) || (ICinfo.fccHandler == VIDEO_FORMAT_MSH261) || (ICinfo.fccHandler == VIDEO_FORMAT_MSH26X)) #else
if ((ICinfo.fccHandler == VIDEO_FORMAT_MSH263) || (ICinfo.fccHandler == VIDEO_FORMAT_MSH261)) #endif
#else
if ((ICinfo.fccHandler == VIDEO_FORMAT_DECH263) || (ICinfo.fccHandler == VIDEO_FORMAT_DECH261)) #endif
ICSendMessage(hIC, CUSTOM_ENABLE_CODEC, G723MAGICWORD1, G723MAGICWORD2);
ICGetInfo(hIC, &ICinfo, sizeof(ICINFO)); // The VDEC codec sets the fccType to the same
// value than the fccHandler! Correct that hereticism:
if ((ICinfo.fccType == VIDEO_FORMAT_VDEC) && (ICinfo.fccHandler == VIDEO_FORMAT_VDEC)) ICinfo.fccType = ICTYPE_VIDEO;
// Restore fccHandler
ICinfo.fccHandler = fccHandler;
// VCMDRIVERDETAILS and ICINFO are identical structures
CopyMemory(pvdd, &ICinfo, sizeof(VCMDRIVERDETAILS));
// For all the built-in sizes we support
for (l=0; l<MAX_NUM_REGISTERED_SIZES; l++) { if ((g_aVCMAppInfo[i].framesize[l].biWidth != 0) && (g_aVCMAppInfo[i].framesize[l].biHeight != 0)) { fUnsupportedInputSize = FALSE;
#ifndef NO_LARGE_SIZE_EXCLUSION_HACK
// HACK for version 2
// Since we didn't get general scaling code into version 2, we want to disable the largest size
// if the capture device doesn't support it. Otherwise we'll put a smaller size into the middle
// of a large black field which looks ugly. For version 3, we should be able to add the general
// scaling code and remove this hack.
if (l == MAX_NUM_REGISTERED_SIZES-1) { // find largest size supported by capture device
// NOTE: we assume that the bit definitions for sizes are sorted
for (k = VIDEO_FORMAT_NUM_RESOLUTIONS-1; k >= 0 && !(g_awResolutions[k].dwRes & vic.dwImageSize); k--) {}
// if we don't find a size, or the size is not greater than half the current size
// then mark the size as not supported
if ((k < 0) || (g_awResolutions[k].framesize.biWidth <= (LONG)g_aVCMAppInfo[i].framesize[l].biWidth/2) || (g_awResolutions[k].framesize.biHeight <= (LONG)g_aVCMAppInfo[i].framesize[l].biHeight/2)) { // capture doesn't support this size
if (fdwEnum & VCM_FORMATENUMF_INPUT) continue; // we're done
else if (fdwEnum & VCM_FORMATENUMF_BOTH) fUnsupportedInputSize = TRUE; } } #endif
// The new capture stuff can generate data at any size
bmi.bmiHeader.biWidth = (LONG)g_aVCMAppInfo[i].framesize[l].biWidth; bmi.bmiHeader.biHeight = (LONG)g_aVCMAppInfo[i].framesize[l].biHeight;
// For all the bit depths we support
for (k=0; k<NUM_BITDEPTH_ENTRIES; k++) { // Try the non-RGB formats only if no RGB format
fUnsupportedBitDepth = FALSE;
if (((fdwEnum & VCM_FORMATENUMF_INPUT) || (fdwEnum & VCM_FORMATENUMF_BOTH)) && !((g_aiNumColors[k] & vic.dwNumColors))) fUnsupportedBitDepth = TRUE;
if ((fdwEnum & VCM_FORMATENUMF_INPUT) && fUnsupportedBitDepth) goto NextCompressedBitDepth;
// Set the direction flag appropriately
if (fdwEnum & VCM_FORMATENUMF_OUTPUT) pvfd->dwFlags = VCM_FORMATENUMF_OUTPUT; else if (fdwEnum & VCM_FORMATENUMF_INPUT) pvfd->dwFlags = VCM_FORMATENUMF_INPUT; else if (fdwEnum & VCM_FORMATENUMF_BOTH) { if (fUnsupportedInputSize || fUnsupportedBitDepth) pvfd->dwFlags = VCM_FORMATENUMF_OUTPUT; else pvfd->dwFlags = VCM_FORMATENUMF_BOTH; }
bmi.bmiHeader.biBitCount = (WORD)g_aiBitDepth[k]; bmi.bmiHeader.biCompression = g_aiFourCCCode[k]; bmi.bmiHeader.biSizeImage = (DWORD)WIDTHBYTES(bmi.bmiHeader.biWidth * bmi.bmiHeader.biBitCount) * bmi.bmiHeader.biHeight;
// Check if the compressor supports the format
if (ICCompressQuery(hIC, &bmi, (LPBITMAPINFOHEADER)NULL) == ICERR_OK) { // Now get the size required to hold the format
dw = ICCompressGetFormatSize(hIC, &bmi); // PHILF's BUGBUG: pvfd->cbvfx is the size of the whole structure, not the bitmap info header
if ((dw >= sizeof(BITMAPINFOHEADER)) && (dw <= pvfd->cbvfx)) { if (ICCompressGetFormat(hIC, &bmi, &pvfd->pvfx->bih) == ICERR_OK) { // Check if it has alreay been enumerated
for (m=0, bDejaVu=FALSE, pdvDejaVuCurr = pdvDejaVu; m<iNumCaps; m++, pdvDejaVuCurr++) { bDejaVu = (!((pdvDejaVuCurr->vfx.bih.biWidth != pvfd->pvfx->bih.biWidth) || (pdvDejaVuCurr->vfx.bih.biHeight != pvfd->pvfx->bih.biHeight) || (pdvDejaVuCurr->vfx.bih.biBitCount != pvfd->pvfx->bih.biBitCount) || (pdvDejaVuCurr->vfx.bih.biCompression != pvfd->pvfx->bih.biCompression)));
if (bDejaVu) { // Only remember the maximum compressed size
if (pdvDejaVuCurr->vfx.bih.biSizeImage < pvfd->pvfx->bih.biSizeImage) pdvDejaVuCurr->vfx.bih.biSizeImage = pvfd->pvfx->bih.biSizeImage; break; } } if (!bDejaVu) { // Add new format to the list of DejaVus
CopyMemory(&(pdvDejaVu + iNumCaps)->vfx, pvfd->pvfx, sizeof(VIDEOFORMATEX)); (pdvDejaVu + iNumCaps)->dwFlags = pvfd->dwFlags;
// Update count of caps
iNumCaps++;
} else if ((pvfd->dwFlags == VCM_FORMATENUMF_BOTH) && ((pdvDejaVu + m)->dwFlags != VCM_FORMATENUMF_BOTH)) (pdvDejaVu + m)->dwFlags = VCM_FORMATENUMF_BOTH; } } } NextCompressedBitDepth:; } } } ICClose(hIC);
// For all the caps we have found
for (m=0; m<iNumCaps; m++) { // For all the frame rates we support
for (j=0; j<NUM_FPS_ENTRIES; j++) { // Copy the cap and flags
CopyMemory(pvfd->pvfx, &(pdvDejaVu + m)->vfx, sizeof(VIDEOFORMATEX)); pvfd->dwFlags = (pdvDejaVu + m)->dwFlags; // Update rest of the fields
pvfd->pvfx->nSamplesPerSec = g_aiFps[j]; pvfd->pvfx->wBitsPerSample = pvfd->pvfx->bih.biBitCount; #if 0
if (pvfd->pvfx->bih.biCompression > 256) { CharUpperBuff((LPTSTR)&pvfd->pvfx->bih.biCompression, sizeof(DWORD)); pvdd->fccHandler = pvfd->dwFormatTag = pvfd->pvfx->dwFormatTag = pvfd->pvfx->bih.biCompression; } else #endif
pvfd->pvfx->dwFormatTag = pvfd->dwFormatTag = pvdd->fccHandler; pvfd->pvfx->nAvgBytesPerSec = pvfd->pvfx->nMinBytesPerSec = pvfd->pvfx->nMaxBytesPerSec = pvfd->pvfx->nSamplesPerSec * pvfd->pvfx->bih.biSizeImage; pvfd->pvfx->nBlockAlign = pvfd->pvfx->bih.biSizeImage; // The following fields should probably not be modified...
pvfd->pvfx->dwRequestMicroSecPerFrame = 1000000L / g_aiFps[j]; pvfd->pvfx->dwPercentDropForError = 10UL; // pvfd->pvfx->dwNumVideoRequested = 2UL;
pvfd->pvfx->dwNumVideoRequested = g_aiFps[j]; pvfd->pvfx->dwSupportTSTradeOff = 1UL; pvfd->pvfx->bLive = TRUE; pvfd->pvfx->dwFormatSize = sizeof(VIDEOFORMATEX);
// Copy the palette if there is one
if (pvfd->pvfx->wBitsPerSample == 4) { pvfd->pvfx->bih.biClrUsed = 0; if (vic.dwFlags & VICF_4BIT_TABLE) { // Copy the 16 color palette
CopyMemory(&pvfd->pvfx->bihSLOP[0], &vic.bmi4bitColors[0], NUM_4BIT_ENTRIES * sizeof(RGBQUAD)); pvfd->pvfx->bih.biClrUsed = 16; } } else if (pvfd->pvfx->wBitsPerSample == 8) { pvfd->pvfx->bih.biClrUsed = 0; if (vic.dwFlags & VICF_8BIT_TABLE) { // Copy the 256 color palette
CopyMemory(&pvfd->pvfx->bihSLOP[0], &vic.bmi8bitColors[0], NUM_8BIT_ENTRIES * sizeof(RGBQUAD)); pvfd->pvfx->bih.biClrUsed = 256; } }
if (pvdd->fccHandler > 256) wsprintf(szBuffer, IDS_FORMAT_1, (LPSTR)&pvdd->fccType, (LPSTR)&pvdd->fccHandler, pvfd->pvfx->bih.biBitCount, pvfd->pvfx->nSamplesPerSec, pvfd->pvfx->bih.biWidth, pvfd->pvfx->bih.biHeight); else wsprintf(szBuffer, IDS_FORMAT_2, (LPSTR)&pvdd->fccType, pvdd->fccHandler, pvfd->pvfx->bih.biBitCount, pvfd->pvfx->nSamplesPerSec, pvfd->pvfx->bih.biWidth, pvfd->pvfx->bih.biHeight); iLen = MultiByteToWideChar(GetACP(), 0, szBuffer, -1, pvfd->szFormat, 0); MultiByteToWideChar(GetACP(), 0, szBuffer, -1, pvfd->szFormat, iLen); if (!((* fnCallback)((HVCMDRIVERID)hIC, pvdd, pvfd, dwInstance))) break; } } } } }
// Free table of capabilities
if (pdvDejaVu) MemFree((HANDLE)pdvDejaVu);
return ((MMRESULT)MMSYSERR_NOERROR); }
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmFormatSuggest | This function asks the Video Compression Manager * (VCM) or a specified VCM driver to suggest a destination format for * the supplied source format, or the recommended source format for a supplied destination * format. For example, an application can use this function to determine one or more * valid RGB formats to which a compressed format can be decompressed. * * @parm UINT | uDevice | Identifies the capture device ID. * * @parm HVCMDRIVER | hvd | Identifies an optional open instance of a * driver to query for a suggested destination format. If this * argument is NULL, the VCM attempts to find the best driver to suggest * a destination format or a source format. * * @parm PVIDEOFORMATEX | pvfxSrc | Specifies a pointer to a <t VIDEOFORMATEX> * structure that identifies the source format to suggest a destination * format to be used for a conversion, or that will receive the suggested * source format for the <p pvfxDst> format. Note * that based on the <p fdwSuggest> argument, some members of the structure * pointed to by <p pvfxSrc> may require initialization. * * @parm PVIDEOFORMATEX | pvfxDst | Specifies a pointer to a <t VIDEOFORMATEX> * data structure that will receive the suggested destination format * for the <p pvfxSrc> format, or that identifies the destination format to * suggest a recommended source format to be used for a conversion. Note * that based on the <p fdwSuggest> argument, some members of the structure * pointed to by <p pvfxDst> may require initialization. * * @parm DWORD | cbvfxDst | Specifies the size in bytes available for * the destination, or the source format. The <f vcmMetrics> * functions can be used to determine the maximum size required for any * format available for the specified driver (or for all installed VCM * drivers). * * @parm DWORD | fdwSuggest | Specifies flags for matching the desired * destination format, or source format. * * @flag VCM_FORMATSUGGESTF_DST_WFORMATTAG | Specifies that the * <e VIDEOFORMATEX.dwFormatTag> member of the <p pvfxDst> structure is * valid. The VCM will query acceptable installed drivers that can * use the <p pvfxSrc> structure as their source format and output a * destination format matching the <e VIDEOFORMATEX.dwFormatTag> * member, or fail. The <p pvfxDst> structure is updated with the complete * destination format. * * @flag VCM_FORMATSUGGESTF_DST_NSAMPLESPERSEC | Specifies that the * <e VIDEOFORMATEX.nSamplesPerSec> member of the <p pvfxDst> structure * is valid. The VCM will query acceptable installed drivers that can * use the <p pvfxSrc> structure as their source format and output a * destination format matching the <e VIDEOFORMATEX.nSamplesPerSec> * member, or fail. The <p pvfxDst> structure is updated with the complete * destination format. * * @flag VCM_FORMATSUGGESTF_DST_WBITSPERSAMPLE | Specifies that the * <e VIDEOFORMATEX.wBitsPerSample> member of the <p pvfxDst> structure * is valid. The VCM will query acceptable installed drivers that can * use the <p pvfxSrc> structure as their source format and output a * destination format matching the <e VIDEOFORMATEX.wBitsPerSample> * member, or fail. The <p pvfxDst> structure is updated with the complete * destination format. * * @flag VCM_FORMATSUGGESTF_SRC_WFORMATTAG | Specifies that the * <e VIDEOFORMATEX.dwFormatTag> member of the <p pvfxSrc> structure is * valid. The VCM will query acceptable installed drivers that can * use the <p pvfxDst> structure as their destination format and accept a * source format matching the <e VIDEOFORMATEX.dwFormatTag> * member, or fail. The <p pvfxSrc> structure is updated with the complete * source format. * * @flag VCM_FORMATSUGGESTF_SRC_NSAMPLESPERSEC | Specifies that the * <e VIDEOFORMATEX.nSamplesPerSec> member of the <p pvfxSrc> structure * is valid. The VCM will query acceptable installed drivers that can * use the <p pvfxDst> structure as their destination format and accept a * source format matching the <e VIDEOFORMATEX.nSamplesPerSec> * member or fail. The <p pvfxSrc> structure is updated with the complete * source format. * * @flag VCM_FORMATSUGGESTF_SRC_WBITSPERSAMPLE | Specifies that the * <e VIDEOFORMATEX.wBitsPerSample> member of the <p pvfxSrc> structure * is valid. The VCM will query acceptable installed drivers that can * use the <p pvfxDst> structure as their destination format and accept a * source format matching the <e VIDEOFORMATEX.wBitsPerSample> * member, or fail. The <p pvfxSrc> structure is updated with the complete * source format. * * @rdesc Returns zero if the function was successful. Otherwise, it returns * a non-zero error number. Possible error returns are: * * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * * @flag MMSYSERR_INVALFLAG | One or more flags are invalid. * * @flag MMSYSERR_INVALPARAM | One or more arguments passed are invalid. * * @flag MMSYSERR_NOTSUPPORTED | One or more of the restriction bits is not supported. * * @flag MMSYSERR_NODRIVER | No capture device driver or device is present. * * @devnote PhilF: For now, only the VCM_FORMATSUGGESTF_DST_WFORMATTAG and VCM_FORMATSUGGESTF_SRC_WFORMATTAG * are supported. The other flags are just ignored. Add real support for other flags * if they would really make a difference. But for the two current Data Pump calls, * they don't influence the outcome of the call. * * The cbvfxDst is never used. Should we still pass it? How can I make a good use of it? * * Should there also be cbvfxSrc parameter? * * This function is used to determine what should the (source) capture format of the capture * device be in order to generate a specific compressed destination format. * Now, there are two possibilities. Either we can directly capture at a frame size * identical to the one in the <p pvfxDst> structure, or we can't, but still, once compressed * the output frame has the same size than the one in the <p pvfxDst> structure. * Typical example: Greyscale QuickCam. If the output format were set to 128x96 (SQCIF) * and we were to try capturing directly at this size, this would fail, since 128x96 * is not supported by the hardware. On the other hand, if we capture at 160x120, * the codec will truncate to 128x96. Now, how can we figure this out programmatically? * For now, the next largest size is ASSUMED to be truncatable by the codec to the right size. * This needs to be actually run through the codec for validation. Fix that. * * If the capture driver capture with a format that is not RGB, this call will fail to suggest * a valid source format and will return MMSYSERR_NODRIVER. Fix that. * * @xref <f vcmMetrics> <f vcmFormatEnum> ***************************************************************************/ MMRESULT VCMAPI vcmFormatSuggest(UINT uDevice, HVCMDRIVER hvd, PVIDEOFORMATEX pvfxSrc, PVIDEOFORMATEX pvfxDst, DWORD cbvfxDst, DWORD fdwSuggest) { DWORD dwSize; MMRESULT mmr; WORD wFlags; HIC hIC; DWORD fdwSuggestL; DWORD dwFormatTag; VIDEOINCAPS vic; int i, delta, best, tmp;
#define VCM_FORMAT_SUGGEST_SUPPORT VCM_FORMATSUGGESTF_TYPEMASK
// Check input params
if (!pvfxSrc) { ERRORMESSAGE(("vcmFormatSuggest: Specified pointer is invalid, pvfxSrc=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if (!pvfxDst) { ERRORMESSAGE(("vcmFormatSuggest: Specified pointer is invalid, pvfxSrc=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if ((uDevice >= MAXVIDEODRIVERS) && (uDevice != VIDEO_MAPPER)) { ERRORMESSAGE(("vcmFormatSuggest: Specified capture device ID is invalid, uDevice=%ld (expected values are 0x%lX or between 0 and %ld)\r\n", uDevice, VIDEO_MAPPER, MAXVIDEODRIVERS-1)); return ((MMRESULT)MMSYSERR_BADDEVICEID); }
// Grab the suggestion restriction bits and verify that we support
// the ones that are specified
fdwSuggestL = (VCM_FORMATSUGGESTF_TYPEMASK & fdwSuggest);
if (~VCM_FORMAT_SUGGEST_SUPPORT & fdwSuggestL) { ERRORMESSAGE(("vcmFormatSuggest: Specified mask is invalid, fdwSuggest=0x%lX\r\n", fdwSuggest)); return ((MMRESULT)MMSYSERR_NOTSUPPORTED); }
// Get the size of the largest bitmap info header
if (((mmr = vcmMetrics((HVCMOBJ)NULL, VCM_METRIC_MAX_SIZE_BITMAPINFOHEADER, &dwSize)) == MMSYSERR_NOERROR) && (dwSize >= sizeof(BITMAPINFOHEADER))) { if (fdwSuggest & VCM_FORMATSUGGESTF_DST_WFORMATTAG) { if (pvfxSrc->bih.biCompression == BI_RGB) { if (pvfxDst->bih.biCompression == BI_RGB) { // Input and output format are uncompressed
CopyMemory(pvfxDst, pvfxSrc, pvfxSrc->dwFormatSize); return ((MMRESULT)MMSYSERR_NOERROR); } else { wFlags = ICMODE_COMPRESS; dwFormatTag = pvfxDst->dwFormatTag; } } else { wFlags = ICMODE_DECOMPRESS; dwFormatTag = pvfxSrc->dwFormatTag; }
#ifndef _ALPHA_
#ifdef USE_BILINEAR_MSH26X
if ((dwFormatTag == VIDEO_FORMAT_MSH263) || (dwFormatTag == VIDEO_FORMAT_MSH261) || (dwFormatTag == VIDEO_FORMAT_MSH26X)) #else
if ((dwFormatTag == VIDEO_FORMAT_MSH263) || (dwFormatTag == VIDEO_FORMAT_MSH261)) #endif
#else
if ((dwFormatTag == VIDEO_FORMAT_DECH263) || (dwFormatTag == VIDEO_FORMAT_DECH261)) #endif
{ hIC = ICOpen(VCMDRIVERDETAILS_FCCTYPE_VIDEOCODEC, dwFormatTag, wFlags);
if (hIC && (wFlags == ICMODE_COMPRESS)) ICSendMessage(hIC, CUSTOM_ENABLE_CODEC, G723MAGICWORD1, G723MAGICWORD2); } else hIC = ICLocate(VCMDRIVERDETAILS_FCCTYPE_VIDEOCODEC, dwFormatTag, (LPBITMAPINFOHEADER)&pvfxSrc->bih, (LPBITMAPINFOHEADER)NULL, wFlags);
if (hIC) { if (wFlags == ICMODE_COMPRESS) { // Now get the size required to hold the format
dwSize = ICCompressGetFormatSize(hIC, &pvfxSrc->bih); if ((dwSize >= sizeof(BITMAPINFOHEADER)) && (dwSize <= cbvfxDst)) { if (ICCompressGetFormat(hIC, &pvfxSrc->bih, &pvfxDst->bih) == ICERR_OK) { pvfxDst->nSamplesPerSec = pvfxSrc->nSamplesPerSec; pvfxDst->wBitsPerSample = pvfxDst->bih.biBitCount; pvfxDst->dwFormatTag = pvfxDst->bih.biCompression; pvfxDst->nAvgBytesPerSec = pvfxDst->nMinBytesPerSec = pvfxDst->nMaxBytesPerSec = pvfxDst->nSamplesPerSec * pvfxDst->bih.biSizeImage; pvfxDst->nBlockAlign = pvfxDst->bih.biSizeImage; // The following fields should probably not be modified...
pvfxDst->dwRequestMicroSecPerFrame = pvfxSrc->dwRequestMicroSecPerFrame; pvfxDst->dwPercentDropForError = pvfxSrc->dwPercentDropForError; pvfxDst->dwNumVideoRequested = pvfxSrc->dwNumVideoRequested; pvfxDst->dwSupportTSTradeOff = pvfxSrc->dwSupportTSTradeOff; pvfxDst->bLive = pvfxSrc->bLive; pvfxDst->dwFormatSize = sizeof(VIDEOFORMATEX); } } } else { // Now get the size required to hold the format
dwSize = ICDecompressGetFormatSize(hIC, &pvfxSrc->bih); if ((dwSize >= sizeof(BITMAPINFOHEADER)) && (dwSize <= cbvfxDst)) { if (ICDecompressGetFormat(hIC, &pvfxSrc->bih, &pvfxDst->bih) == ICERR_OK) { pvfxDst->nSamplesPerSec = pvfxSrc->nSamplesPerSec; pvfxDst->wBitsPerSample = pvfxDst->bih.biBitCount; pvfxDst->dwFormatTag = pvfxDst->bih.biCompression; pvfxDst->nAvgBytesPerSec = pvfxDst->nMinBytesPerSec = pvfxDst->nMaxBytesPerSec = pvfxDst->nSamplesPerSec * pvfxDst->bih.biSizeImage; pvfxDst->nBlockAlign = pvfxDst->bih.biSizeImage; pvfxDst->dwRequestMicroSecPerFrame = pvfxSrc->dwRequestMicroSecPerFrame; // The following fields should probably not be modified...
pvfxDst->dwRequestMicroSecPerFrame = pvfxSrc->dwRequestMicroSecPerFrame; pvfxDst->dwPercentDropForError = pvfxSrc->dwPercentDropForError; pvfxDst->dwNumVideoRequested = pvfxSrc->dwNumVideoRequested; pvfxDst->dwSupportTSTradeOff = pvfxSrc->dwSupportTSTradeOff; pvfxDst->bLive = pvfxSrc->bLive; pvfxDst->dwFormatSize = sizeof(VIDEOFORMATEX); } } } ICClose(hIC); } } else if (fdwSuggest & VCM_FORMATSUGGESTF_SRC_WFORMATTAG) {
// In case only the format tag was initialized, copy it to the biCompression field
pvfxSrc->bih.biCompression = pvfxSrc->dwFormatTag;
if (pvfxSrc->bih.biCompression == BI_RGB) { if (pvfxDst->bih.biCompression == BI_RGB) { // Input and output format are uncompressed
CopyMemory(pvfxSrc, pvfxDst, pvfxDst->dwFormatSize); return ((MMRESULT)MMSYSERR_NOERROR); } else { wFlags = ICMODE_COMPRESS; dwFormatTag = pvfxDst->dwFormatTag; } } else { if (pvfxDst->bih.biCompression == BI_RGB) { wFlags = ICMODE_DECOMPRESS; dwFormatTag = pvfxSrc->dwFormatTag; } else { wFlags = ICMODE_COMPRESS; dwFormatTag = pvfxDst->dwFormatTag; } }
if (wFlags == ICMODE_COMPRESS) { // Now, there are two possibilities. Either we can directly capture at a frame size
// identical to the one in the pvfxDst structure, or we can't, but once compressed
// the output frame has the same size than the one in the pvfxDst structure.
// Typical example, Greyscale QuickCam. If the output format were set to 128x96 (SQCIF)
// and we were to try capturing directly at this size, this would fail, since 128x96
// is not supported by the hardware. On the other hand, if we capture at 160x120,
// the codec will truncate to 128x96. Now, how can we figure this out programmatically?
// The color and greyscale capability field will let us know what bit depth to use.
// We should probably have a field that also says which bit depth is preferred in the
// case more than one are supported. For now, assume the priority order is: 16, 24, 4, 8
if ((mmr = vcmGetDevCaps(uDevice, &vic, sizeof(VIDEOINCAPS))) != MMSYSERR_NOERROR) return (mmr);
if (vic.dwImageSize & VIDEO_FORMAT_IMAGE_SIZE_USE_DEFAULT) { ERRORMESSAGE(("vcmFormatSuggest: suggest using default\r\n")); return ((MMRESULT)MMSYSERR_NOTSUPPORTED); }
CopyMemory(&pvfxSrc->bih, &pvfxDst->bih, sizeof(BITMAPINFOHEADER));
// Assume the next resolution will be correctly truncated to the output size
best = -1; delta = 999999; for (i=0; i<VIDEO_FORMAT_NUM_RESOLUTIONS; i++) { if (g_awResolutions[i].dwRes & vic.dwImageSize) { tmp = g_awResolutions[i].framesize.biWidth - pvfxDst->bih.biWidth; if (tmp < 0) tmp = -tmp; if (tmp < delta) { delta = tmp; best = i; } tmp = g_awResolutions[i].framesize.biHeight - pvfxDst->bih.biHeight; if (tmp < 0) tmp = -tmp; if (tmp < delta) { delta = tmp; best = i; } } } if (best < 0) { ERRORMESSAGE(("vcmFormatSuggest: no available formats\r\n")); return ((MMRESULT)MMSYSERR_NOTSUPPORTED); } // Actually, you don't have to assume it will work. You can directly ask the codec
// is this would work...
pvfxSrc->bih.biWidth = g_awResolutions[best].framesize.biWidth; pvfxSrc->bih.biHeight = g_awResolutions[best].framesize.biHeight;
// Now, we assume that the captured format is an RGB format. Once in place, you should
// verify this from the capability set of the capture device.
if (pvfxSrc->bih.biSize != sizeof(BITMAPINFOHEADER)) pvfxSrc->bih.biSize = sizeof(BITMAPINFOHEADER);
// If the capture hardware does not support RGB, we need to use its compressed format
for (i=0; i<NUM_BITDEPTH_ENTRIES; i++) { if (vic.dwNumColors & g_aiNumColors[i]) { pvfxSrc->bih.biBitCount = (WORD)g_aiBitDepth[i]; pvfxSrc->bih.biCompression = g_aiFourCCCode[i]; break; } } // Copy the palette if there is one
if (pvfxSrc->bih.biBitCount == 4) { pvfxSrc->bih.biClrUsed = 0; if (vic.dwFlags & VICF_4BIT_TABLE) { // Copy the 16 color palette
CopyMemory(&pvfxSrc->bihSLOP[0], &vic.bmi4bitColors[0], NUM_4BIT_ENTRIES * sizeof(RGBQUAD)); pvfxSrc->bih.biClrUsed = 16; } } else if (pvfxSrc->bih.biBitCount == 8) { pvfxSrc->bih.biClrUsed = 0; if (vic.dwFlags & VICF_8BIT_TABLE) { // Copy the 256 color palette
CopyMemory(&pvfxSrc->bihSLOP[0], &vic.bmi8bitColors[0], NUM_8BIT_ENTRIES * sizeof(RGBQUAD)); pvfxSrc->bih.biClrUsed = 256; } }
pvfxSrc->bih.biSizeImage = WIDTHBYTES(pvfxSrc->bih.biWidth * pvfxSrc->bih.biBitCount) * pvfxSrc->bih.biHeight; } else { #ifndef _ALPHA_
#ifdef USE_BILINEAR_MSH26X
if ((dwFormatTag == VIDEO_FORMAT_MSH263) || (dwFormatTag == VIDEO_FORMAT_MSH261) || (dwFormatTag == VIDEO_FORMAT_MSH26X)) #else
if ((dwFormatTag == VIDEO_FORMAT_MSH263) || (dwFormatTag == VIDEO_FORMAT_MSH261)) #endif
#else
if ((dwFormatTag == VIDEO_FORMAT_DECH263) || (dwFormatTag == VIDEO_FORMAT_DECH261)) #endif
hIC = ICOpen(VCMDRIVERDETAILS_FCCTYPE_VIDEOCODEC, dwFormatTag, wFlags); else hIC = ICLocate(VCMDRIVERDETAILS_FCCTYPE_VIDEOCODEC, dwFormatTag, (LPBITMAPINFOHEADER)&pvfxSrc->bih, (LPBITMAPINFOHEADER)NULL, wFlags);
if (hIC) { // Now get the size required to hold the format
dwSize = ICDecompressGetFormatSize(hIC, &pvfxSrc->bih); if ((dwSize >= sizeof(BITMAPINFOHEADER)) && (dwSize <= cbvfxDst)) { if (ICDecompressGetFormat(hIC, &pvfxSrc->bih, &pvfxDst->bih) == ICERR_OK) { pvfxSrc->nSamplesPerSec = pvfxSrc->nSamplesPerSec; pvfxSrc->wBitsPerSample = pvfxDst->bih.biBitCount; pvfxSrc->dwFormatTag = pvfxDst->bih.biCompression; pvfxDst->nAvgBytesPerSec = pvfxDst->nMinBytesPerSec = pvfxDst->nMaxBytesPerSec = pvfxDst->nSamplesPerSec * pvfxDst->bih.biSizeImage; pvfxDst->nBlockAlign = pvfxDst->bih.biSizeImage; pvfxDst->dwRequestMicroSecPerFrame = pvfxSrc->dwRequestMicroSecPerFrame; // The following fields should probably not be modified...
pvfxDst->dwRequestMicroSecPerFrame = pvfxSrc->dwRequestMicroSecPerFrame; pvfxDst->dwPercentDropForError = pvfxSrc->dwPercentDropForError; pvfxDst->dwNumVideoRequested = pvfxSrc->dwNumVideoRequested; pvfxDst->dwSupportTSTradeOff = pvfxSrc->dwSupportTSTradeOff; pvfxDst->bLive = pvfxSrc->bLive; pvfxDst->dwFormatSize = sizeof(VIDEOFORMATEX); } } ICClose(hIC); } } } }
return (mmr); }
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmStreamOpen | The vcmStreamOpen function opens a Video Compression * Manager (VCM) conversion stream. Conversion streams are used to convert data from * one specified video format to another. * * @parm PHVCMSTREAM | phvs | Specifies a pointer to a <t HVCMSTREAM> * handle that will receive the new stream handle that can be used to * perform conversions. Use this handle to identify the stream * when calling other VCM stream conversion functions. This parameter * should be NULL if the VCM_STREAMOPENF_QUERY flag is specified. * * @parm HVCMDRIVER | hvd | Specifies an optional handle to a VCM driver. * If specified, this handle identifies a specific driver to be used * for a conversion stream. If this argument is NULL, then all suitable * installed VCM drivers are queried until a match is found. * * @parm PVIDEOFORMATEX | pvfxSrc | Specifies a pointer to a <t VIDEOFORMATEX> * structure that identifies the desired source format for the * conversion. * * @parm PVIDEOFORMATEX | pvfxDst | Specifies a pointer to a <t VIDEOFORMATEX> * structure that identifies the desired destination format for the * conversion. * * @parm DWORD | dwImageQuality | Specifies an image quality value (between 0 * and 31. The lower number indicates a high spatial quality at a low frame * rate, the larger number indiocates a low spatial quality at a high frame * rate. * * @parm DWORD | dwCallback | Specifies the address of a callback function * or a handle to a window called after each buffer is converted. A * callback will only be called if the conversion stream is opened with * the VCM_STREAMOPENF_ASYNC flag. If the conversion stream is opened * without the CCM_STREAMOPENF_ASYNC flag, then this parameter should * be set to zero. * * @parm DWORD | dwInstance | Specifies user-instance data passed on to the * callback specified by <p dwCallback>. This argument is not used with * window callbacks. If the conversion stream is opened without the * VCM_STREAMOPENF_ASYNC flag, then this parameter should be set to zero. * * @parm DWORD | fdwOpen | Specifies flags for opening the conversion stream. * * @flag VCM_STREAMOPENF_QUERY | Specifies that the VCM will be queried * to determine whether it supports the given conversion. A conversion * stream will not be opened and no <t HVCMSTREAM> handle will be * returned. * * @flag VCM_STREAMOPENF_NONREALTIME | Specifies that the VCM will not * consider time constraints when converting the data. By default, the * driver will attempt to convert the data in real time. Note that for * some formats, specifying this flag might improve the video quality * or other characteristics. * * @flag VCM_STREAMOPENF_ASYNC | Specifies that conversion of the stream should * be performed asynchronously. If this flag is specified, the application * can use a callback to be notified on open and close of the conversion * stream, and after each buffer is converted. In addition to using a * callback, an application can examine the <e VCMSTREAMHEADER.fdwStatus> * of the <t VCMSTREAMHEADER> structure for the VCMSTREAMHEADER_STATUSF_DONE * flag. * * @flag CALLBACK_WINDOW | Specifies that <p dwCallback> is assumed to * be a window handle. * * @flag CALLBACK_FUNCTION | Specifies that <p dwCallback> is assumed to * be a callback procedure address. The function prototype must conform * to the <f vcmStreamConvertCallback> convention. * * @rdesc Returns zero if the function was successful. Otherwise, it returns * a non-zero error number. Possible error returns are: * * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * * @flag MMSYSERR_INVALFLAG | One or more flags are invalid. * * @flag MMSYSERR_INVALPARAM | One or more arguments passed are invalid. * * @flag MMSYSERR_NOMEM | Unable to allocate resources. * * @flag VCMERR_NOTPOSSIBLE | The requested operation cannot be performed. * * @comm Note that if a VCM driver cannot perform real-time conversions, * and the VCM_STREAMOPENF_NONREALTIME flag is not specified for * the <p fdwOpen> argument, the open will fail returning an * VCMERR_NOTPOSSIBLE error code. An application can use the * VCM_STREAMOPENF_QUERY flag to determine if real-time conversions * are supported for the input arguments. * * If a window is chosen to receive callback information, the * following messages are sent to the window procedure function to * indicate the progress of the conversion stream: <m MM_VCM_OPEN>, * <m MM_VCM_CLOSE>, and <m MM_VCM_DONE>. The <p wParam> parameter identifies * the <t HVCMSTREAM> handle. The <p lParam> parameter identifies the * <t VCMSTREAMHEADER> structure for <m MM_VCM_DONE>, but is not used * for <m MM_VCM_OPEN> and <m MM_VCM_CLOSE>. * * If a function is chosen to receive callback information, the * following messages are sent to the function to indicate the progress * of output: <m MM_VCM_OPEN>, <m MM_VCM_CLOSE>, and * <m MM_VCM_DONE>. The callback function must reside in a DLL. You do * not need to use <f MakeProcInstance> to get a procedure-instance * address for the callback function. * * @xref <f vcmStreamClose> <f vcmStreamConvert> * <f vcmFormatSuggest> <f vcmStreamConvertCallback> ***************************************************************************/ MMRESULT VCMAPI vcmStreamOpen(PHVCMSTREAM phvs, HVCMDRIVER hvd, PVIDEOFORMATEX pvfxSrc, PVIDEOFORMATEX pvfxDst, DWORD dwImageQuality, DWORD dwPacketSize, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen) { MMRESULT mmr; DWORD dwFlags; DWORD fccHandler; HIC hIC; VIDEOFORMATEX *pvfxS; VIDEOFORMATEX *pvfxD; BITMAPINFOHEADER *pbmiPrev; // Pointer to reconstructed frame bitmap info header (for now assume it is the same than the input format...)
ICINFO icInfo; PVOID pvState; // Pointer to codec configuration information
DWORD dw; // Size of codec configuration information or destination BITAMPINFO
ICCOMPRESSFRAMES iccf = {0}; // Structure used to set compression parameters
PMSH26XCOMPINSTINFO pciMSH26XInfo; // Pointer to MS H26X configuration information
#ifdef USE_MPEG4_SCRUNCH
PMPEG4COMPINSTINFO pciMPEG4Info; // Pointer to MPEG4 Scrunch configuration information
#endif
// Check input params
if (!pvfxSrc) { ERRORMESSAGE(("vcmStreamOpen: Specified pointer is invalid, pvfxSrc=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if (!pvfxDst) { ERRORMESSAGE(("vcmStreamOpen: Specified pointer is invalid, pvfxSrc=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if (!pvfxSrc->dwFormatSize) { ERRORMESSAGE(("vcmStreamOpen: Specified format size is invalid, pvfxSrc->dwFormatSize=%ld\r\n", pvfxSrc->dwFormatSize)); return ((MMRESULT)MMSYSERR_INVALPARAM); } if (!pvfxDst->dwFormatSize) { ERRORMESSAGE(("vcmStreamOpen: Specified format size is invalid, pvfxDst->dwFormatSize=%ld\r\n", pvfxDst->dwFormatSize)); return ((MMRESULT)MMSYSERR_INVALPARAM); } if ((dwImageQuality < VCM_MIN_IMAGE_QUALITY) || (dwImageQuality > VCM_MAX_IMAGE_QUALITY)) dwImageQuality = VCM_DEFAULT_IMAGE_QUALITY;
// Set default values
*phvs = (HVCMSTREAM)NULL;
// Are we compressing of decompressing?
if (pvfxSrc->bih.biCompression == BI_RGB) { dwFlags = ICMODE_COMPRESS; fccHandler = (DWORD)pvfxDst->bih.biCompression; } else { if (pvfxDst->bih.biCompression == BI_RGB) { dwFlags = ICMODE_DECOMPRESS; fccHandler = (DWORD)pvfxSrc->bih.biCompression; } else { dwFlags = ICMODE_COMPRESS; fccHandler = (DWORD)pvfxDst->bih.biCompression; } }
// Get a handle to the compressor/decompressor
#ifndef _ALPHA_
#ifdef USE_BILINEAR_MSH26X
if ((fccHandler == VIDEO_FORMAT_MSH263) || (fccHandler == VIDEO_FORMAT_MSH261) || (fccHandler == VIDEO_FORMAT_MSH26X)) #else
if ((fccHandler == VIDEO_FORMAT_MSH263) || (fccHandler == VIDEO_FORMAT_MSH261)) #endif
#else
if ((fccHandler == VIDEO_FORMAT_DECH263) || (fccHandler == VIDEO_FORMAT_DECH261)) #endif
{ hIC = ICOpen(VCMDRIVERDETAILS_FCCTYPE_VIDEOCODEC, fccHandler, (WORD)dwFlags); if (hIC && (dwFlags == ICMODE_COMPRESS)) ICSendMessage(hIC, CUSTOM_ENABLE_CODEC, G723MAGICWORD1, G723MAGICWORD2); } else hIC = ICLocate(VCMDRIVERDETAILS_FCCTYPE_VIDEOCODEC, fccHandler, (LPBITMAPINFOHEADER)&pvfxSrc->bih, (LPBITMAPINFOHEADER)&pvfxDst->bih, (WORD)dwFlags);
if (hIC) { // Get info about this compressor
ICGetInfo(hIC, &icInfo, sizeof(ICINFO));
// Allocate VCM stream structure
if (!(*phvs = (HVCMSTREAM)MemAlloc(sizeof(VCMSTREAM)))) { ERRORMESSAGE(("vcmStreamOpen: Memory allocation of a VCM stream structure failed\r\n")); mmr = (MMRESULT)MMSYSERR_NOMEM; goto MyExit0; } else { ((PVCMSTREAM)(*phvs))->hIC = (HVCMDRIVER)hIC; ((PVCMSTREAM)(*phvs))->dwICInfoFlags = icInfo.dwFlags; ((PVCMSTREAM)(*phvs))->dwQuality = dwImageQuality; ((PVCMSTREAM)(*phvs))->dwMaxPacketSize = dwPacketSize; ((PVCMSTREAM)(*phvs))->dwFrame = 0L; // For now, issue a key frame every 15 seconds
((PVCMSTREAM)(*phvs))->dwLastIFrameTime = GetTickCount(); ((PVCMSTREAM)(*phvs))->fPeriodicIFrames = TRUE; ((PVCMSTREAM)(*phvs))->dwCallback = dwCallback; ((PVCMSTREAM)(*phvs))->dwCallbackInstance = dwInstance; ((PVCMSTREAM)(*phvs))->fdwOpen = fdwOpen; ((PVCMSTREAM)(*phvs))->fdwStream = dwFlags; ((PVCMSTREAM)(*phvs))->dwLastTimestamp = ULONG_MAX;
// We need the following crs to make sure we don't miss any of the I-Frame requests
// emittted by the UI. Problematic scenario: pvs->dwFrame is at 123 for instance.
// The UI thread requests an I-Frame by setting pvs->dwFrame. If the capture/compression
// thread was in ICCompress() (which is very probable since it takes quite some time
// to compress a frame), pvs->dwFrame will be incremented by one when ICCompress()
// returns. We failed to handle the I-Frame request correctly, since the next time
// ICCompress() gets called pvs->dwFrame will be equal to 1, for which we do not
// generate an I-Frame.
if ((dwFlags == ICMODE_COMPRESS) || (dwFlags == ICMODE_FASTCOMPRESS)) // Hmmm... where could you have set the second mode?
InitializeCriticalSection(&(((PVCMSTREAM)(*phvs))->crsFrameNumber));
// Allocate the video formats
if (!(pvfxS = (VIDEOFORMATEX *)MemAlloc(pvfxSrc->dwFormatSize))) { ERRORMESSAGE(("vcmStreamOpen: Memory allocation of source video format failed\r\n")); mmr = (MMRESULT)MMSYSERR_NOMEM; goto MyExit1; } else { if (!(pvfxD = (VIDEOFORMATEX *)MemAlloc(pvfxDst->dwFormatSize))) { ERRORMESSAGE(("vcmStreamOpen: Memory allocation of destination video format failed\r\n")); mmr = (MMRESULT)MMSYSERR_NOMEM; goto MyExit2; } else { // This is naive. You need to query the codec for its decompressed format size and data
if (!(pbmiPrev = (BITMAPINFOHEADER *)MemAlloc(sizeof(BITMAPINFOHEADER)))) { ERRORMESSAGE(("vcmStreamOpen: Memory allocation of previous video frame failed\r\n")); mmr = (MMRESULT)MMSYSERR_NOMEM; goto MyExit3; } else { CopyMemory(((PVCMSTREAM)(*phvs))->pvfxSrc = pvfxS, pvfxSrc, pvfxSrc->dwFormatSize); CopyMemory(((PVCMSTREAM)(*phvs))->pvfxDst = pvfxD, pvfxDst, pvfxDst->dwFormatSize); CopyMemory(((PVCMSTREAM)(*phvs))->pbmiPrev = pbmiPrev, &pvfxSrc->bih, sizeof(BITMAPINFOHEADER)); } } }
if ((dwFlags == ICMODE_COMPRESS) || (dwFlags == ICMODE_FASTCOMPRESS)) // Hmmm... where could you have set the second mode?
{ // Get the state of the compressor
if (dw = ICGetStateSize(hIC)) { if (!(pvState = (PVOID)MemAlloc(dw))) { ERRORMESSAGE(("vcmStreamOpen: Memory allocation of codec configuration information structure failed\r\n")); mmr = (MMRESULT)MMSYSERR_NOMEM; goto MyExit4; } if (((DWORD) ICGetState(hIC, pvState, dw)) != dw) { ERRORMESSAGE(("vcmStreamOpen: ICGetState() failed\r\n")); mmr = (MMRESULT)VCMERR_FAILED; goto MyExit5; } }
// Do any of the stuff that is MS H.263 or MS H.261 specific right here
#ifndef _ALPHA_
#ifdef USE_BILINEAR_MSH26X
if ((pvfxDst->bih.biCompression == VIDEO_FORMAT_MSH263) || (pvfxDst->bih.biCompression == VIDEO_FORMAT_MSH261) || (pvfxDst->bih.biCompression == VIDEO_FORMAT_MSH26X)) #else
if ((pvfxDst->bih.biCompression == VIDEO_FORMAT_MSH263) || (pvfxDst->bih.biCompression == VIDEO_FORMAT_MSH261)) #endif
#else
if ((pvfxDst->bih.biCompression == VIDEO_FORMAT_DECH263) || (pvfxDst->bih.biCompression == VIDEO_FORMAT_DECH261)) #endif
{ pciMSH26XInfo = (PMSH26XCOMPINSTINFO)pvState;
// Really configure the codec for compression
pciMSH26XInfo->Configuration.bRTPHeader = TRUE; pciMSH26XInfo->Configuration.unPacketSize = ((PVCMSTREAM)(*phvs))->dwMaxPacketSize; pciMSH26XInfo->Configuration.bEncoderResiliency = FALSE; pciMSH26XInfo->Configuration.unPacketLoss = 0; // PhilF-: Make this work on the alpha
#ifndef _ALPHA_
pciMSH26XInfo->Configuration.bBitRateState = TRUE; #else
pciMSH26XInfo->Configuration.bBitRateState = FALSE; #endif
pciMSH26XInfo->Configuration.unBytesPerSecond = 1664; if (((DWORD) ICSetState(hIC, (PVOID)pciMSH26XInfo, dw)) != dw) { ERRORMESSAGE(("vcmStreamOpen: ICSetState() failed\r\n")); mmr = (MMRESULT)VCMERR_FAILED; goto MyExit5; }
// Get rid of the state structure
MemFree((HANDLE)pvState); } #ifdef USE_MPEG4_SCRUNCH
else if ((pvfxDst->bih.biCompression == VIDEO_FORMAT_MPEG4_SCRUNCH)) { pciMPEG4Info = (PMPEG4COMPINSTINFO)pvState;
// Configure the codec for compression
pciMPEG4Info->lMagic = MPG4_STATE_MAGIC; pciMPEG4Info->dDataRate = 20; pciMPEG4Info->lCrisp = CRISP_DEF; pciMPEG4Info->lKeydist = 30; pciMPEG4Info->lPScale = MPG4_PSEUDO_SCALE; pciMPEG4Info->lTotalWindowMs = MPG4_TOTAL_WINDOW_DEFAULT; pciMPEG4Info->lVideoWindowMs = MPG4_VIDEO_WINDOW_DEFAULT; pciMPEG4Info->lFramesInfoValid = FALSE; pciMPEG4Info->lBFrameOn = MPG4_B_FRAME_ON; pciMPEG4Info->lLiveEncode = MPG4_LIVE_ENCODE; if (((DWORD) ICSetState(hIC, (PVOID)pciMPEG4Info, dw)) != dw) { ERRORMESSAGE(("vcmStreamOpen: ICSetState() failed\r\n")); mmr = (MMRESULT)VCMERR_FAILED; goto MyExit5; }
// Get rid of the state structure
MemFree((HANDLE)pvState); } #endif
// Initialize ICCOMPRESSFRAMES structure
iccf.dwFlags = icInfo.dwFlags; ((PVCMSTREAM)(*phvs))->dwQuality = dwImageQuality; iccf.lQuality = 10000UL - (dwImageQuality * 322UL); iccf.lDataRate = 1664; // Look into this...
iccf.lKeyRate = LONG_MAX; iccf.dwRate = 1000UL; #ifdef USE_MPEG4_SCRUNCH
iccf.dwScale = 142857; #else
iccf.dwScale = pvfxDst->dwRequestMicroSecPerFrame / 1000UL; #endif
((PVCMSTREAM)(*phvs))->dwTargetFrameRate = iccf.dwScale; ((PVCMSTREAM)(*phvs))->dwTargetByterate = iccf.lDataRate;
// Send this guy to the compressor
if ((mmr = ICSendMessage(hIC, ICM_COMPRESS_FRAMES_INFO, (DWORD_PTR)&iccf, sizeof(iccf)) != ICERR_OK)) { ERRORMESSAGE(("vcmStreamOpen: Codec failed to handle ICM_COMPRESS_FRAMES_INFO message correctly\r\n")); mmr = (MMRESULT)VCMERR_FAILED; goto MyExit4; }
// Start the compressor/decompressor with the right format
if ((dw = ICCompressGetFormatSize(hIC, &pvfxSrc->bih) < sizeof(BITMAPINFOHEADER))) { ERRORMESSAGE(("vcmStreamOpen: Codec failed to answer request for compressed format size\r\n")); mmr = (MMRESULT)VCMERR_FAILED; goto MyExit4; }
// BUG_BUG: Where has pvfxDst been re-allocated ???
if ((dw = (DWORD)ICCompressGetFormat(hIC, &pvfxSrc->bih, &pvfxD->bih)) != ICERR_OK) { ERRORMESSAGE(("vcmStreamOpen: Codec failed to answer request for compressed format\r\n")); mmr = (MMRESULT)VCMERR_FAILED; goto MyExit4; }
if ((mmr = (MMRESULT)ICCompressBegin(hIC, &pvfxSrc->bih, &pvfxD->bih)) != MMSYSERR_NOERROR) { ERRORMESSAGE(("vcmStreamOpen: Codec failed to start\r\n")); mmr = (MMRESULT)VCMERR_FAILED; goto MyExit4; }
DEBUGMSG (1, ("vcmStreamOpen: Opening %.4s compression stream\r\n", (LPSTR)&pvfxDst->bih.biCompression));
// Update the passed destination video format. The caller really needs to use
// that information to allocate the buffer sizes appropriately.
CopyMemory(pvfxDst, pvfxD, sizeof(VIDEOFORMATEX));
// Here, you can probably get the size of the compressed frames and update the destination format
// with the real size of the compressed video buffer so that the DP can allocate the right set
// of video buffers.
} else if ((dwFlags == ICMODE_DECOMPRESS) || (dwFlags == ICMODE_FASTDECOMPRESS)) { if (mmr = ICDecompressBegin(hIC, &pvfxSrc->bih, &pvfxDst->bih) != MMSYSERR_NOERROR) { ERRORMESSAGE(("vcmStreamOpen: Codec failed to start\r\n")); mmr = (MMRESULT)VCMERR_FAILED; goto MyExit4; }
DEBUGMSG (1, ("vcmStreamOpen: Opening %.4s decompression stream\r\n", (LPSTR)&pvfxSrc->bih.biCompression));
#ifndef _ALPHA_
#ifdef USE_BILINEAR_MSH26X
if ((pvfxSrc->bih.biCompression == VIDEO_FORMAT_MSH263) || (pvfxSrc->bih.biCompression == VIDEO_FORMAT_MSH261) || (pvfxSrc->bih.biCompression == VIDEO_FORMAT_MSH26X)) #else
if ((pvfxSrc->bih.biCompression == VIDEO_FORMAT_MSH263) || (pvfxSrc->bih.biCompression == VIDEO_FORMAT_MSH261)) #endif
#else
if ((pvfxSrc->bih.biCompression == VIDEO_FORMAT_DECH263) || (pvfxSrc->bih.biCompression == VIDEO_FORMAT_DECH261)) #endif
vcmStreamMessage(*phvs, CUSTOM_ENABLE_CODEC, G723MAGICWORD1, G723MAGICWORD2); }
}
#ifdef LOGFILE_ON
if ((dwFlags == ICMODE_COMPRESS) || (dwFlags == ICMODE_FASTCOMPRESS)) { if ((g_CompressLogFile = CreateFile("C:\\VCMCLog.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL)) != INVALID_HANDLE_VALUE) { GetLocalTime(&g_SystemTime); wsprintf(g_szCompressBuffer, "Date %hu/%hu/%hu, Time %hu:%hu\r\n", g_SystemTime.wMonth, g_SystemTime.wDay, g_SystemTime.wYear, g_SystemTime.wHour, g_SystemTime.wMinute); WriteFile(g_CompressLogFile, g_szCompressBuffer, strlen(g_szCompressBuffer), &g_dwCompressBytesWritten, NULL); wsprintf(g_szCompressBuffer, "Frame#\t\tSize\t\tArrival Time\t\tCompression Time\r\n"); WriteFile(g_CompressLogFile, g_szCompressBuffer, strlen(g_szCompressBuffer), &g_dwCompressBytesWritten, NULL); CloseHandle(g_CompressLogFile); } } else if ((dwFlags == ICMODE_DECOMPRESS) || (dwFlags == ICMODE_FASTDECOMPRESS)) { if ((g_DecompressLogFile = CreateFile("C:\\VCMDLog.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL)) != INVALID_HANDLE_VALUE) { GetLocalTime(&g_SystemTime); wsprintf(g_szDecompressBuffer, "Date %hu/%hu/%hu, Time %hu:%hu\r\n", g_SystemTime.wMonth, g_SystemTime.wDay, g_SystemTime.wYear, g_SystemTime.wHour, g_SystemTime.wMinute); WriteFile(g_DecompressLogFile, g_szDecompressBuffer, strlen(g_szDecompressBuffer), &g_dwDecompressBytesWritten, NULL); wsprintf(g_szDecompressBuffer, "Frame#\t\tSize\t\tArrival Time\t\tDecompression Time\r\n"); WriteFile(g_DecompressLogFile, g_szDecompressBuffer, strlen(g_szDecompressBuffer), &g_dwDecompressBytesWritten, NULL); CloseHandle(g_DecompressLogFile); } } #endif
return ((MMRESULT)MMSYSERR_NOERROR);
} else return ((MMRESULT)VCMERR_NOTPOSSIBLE);
MyExit5: if (pvState) MemFree(pvState); MyExit4: if (pbmiPrev) MemFree(pbmiPrev); MyExit3: if (pvfxD) MemFree(pvfxD); MyExit2: if (pvfxS) MemFree(pvfxS); MyExit1: if ((dwFlags == ICMODE_COMPRESS) || (dwFlags == ICMODE_FASTCOMPRESS)) // Hmmm... where could you have set the second mode?
DeleteCriticalSection(&(((PVCMSTREAM)(*phvs))->crsFrameNumber)); if (*phvs) MemFree(*phvs); *phvs = (HVCMSTREAM)(PVCMSTREAM)NULL; MyExit0: return (mmr);
}
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmStreamClose | The vcmStreamClose function closes a previously * opened Video Compression Manager (VCM) conversion stream. If the function is * successful, the handle is invalidated. * * @parm HVCMSTREAM | hvs | Identifies the open conversion stream to be closed. * * @rdesc Returns zero if the function was successful. Otherwise, it returns * a non-zero error number. Possible error returns are: * * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * * @flag VCMERR_BUSY | The conversion stream cannot be closed because * an asynchronous conversion is still in progress. * * @xref <f vcmStreamOpen> <f vcmStreamReset> ***************************************************************************/ MMRESULT VCMAPI vcmStreamClose(HVCMSTREAM hvs) { PVCMSTREAM pvs = (PVCMSTREAM)hvs; #ifdef LOGFILE_ON
DWORD i; #endif
// Check input params
if (!hvs) { ERRORMESSAGE(("vcmStreamClose: Specified HVCMSTREAM handle is invalid, hvs=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALHANDLE); }
// Stop the compressor/decompressor
if ((pvs->fdwStream == ICMODE_COMPRESS) || (pvs->fdwStream == ICMODE_FASTCOMPRESS)) { #ifdef LOGFILE_ON
g_OrigCompressTime = GetTickCount() - g_OrigCompressTime; if (pvs->dwFrame) { for (i=0, g_AvgCompressTime=0; i<pvs->dwFrame && i<4096; i++) g_AvgCompressTime += g_aCompressTime[i]; g_AvgCompressTime /= i; } if ((g_CompressLogFile = CreateFile("C:\\VCMCLog.txt", GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL)) != INVALID_HANDLE_VALUE) { SetFilePointer(g_CompressLogFile, 0, NULL, FILE_END); if (pvs->dwFrame) { wsprintf(g_szCompressBuffer, "Frames/s\tAvg. Comp. Time\r\n"); WriteFile(g_CompressLogFile, g_szCompressBuffer, strlen(g_szCompressBuffer), &g_dwCompressBytesWritten, NULL); wsprintf(g_szCompressBuffer, "%04d\t\t%03d\r\n", pvs->dwFrame * 1000 / g_OrigCompressTime, g_AvgCompressTime); WriteFile(g_CompressLogFile, g_szCompressBuffer, strlen(g_szCompressBuffer), &g_dwCompressBytesWritten, NULL); } else { wsprintf(g_szCompressBuffer, "No frames were compressed!"); WriteFile(g_CompressLogFile, g_szCompressBuffer, strlen(g_szCompressBuffer), &g_dwCompressBytesWritten, NULL); } CloseHandle(g_CompressLogFile); } #endif
if (ICCompressEnd((HIC)pvs->hIC) != MMSYSERR_NOERROR) { ERRORMESSAGE(("vcmStreamClose: Codec failed to stop\r\n")); return ((MMRESULT)VCMERR_FAILED); } } else if ((pvs->fdwStream == ICMODE_DECOMPRESS) || (pvs->fdwStream == ICMODE_FASTDECOMPRESS)) { #ifdef LOGFILE_ON
g_OrigDecompressTime = GetTickCount() - g_OrigDecompressTime; if (pvs->dwFrame) { for (i=0, g_AvgDecompressTime=0; i<pvs->dwFrame && i<4096; i++) g_AvgDecompressTime += g_aDecompressTime[i]; g_AvgDecompressTime /= i; } if ((g_DecompressLogFile = CreateFile("C:\\VCMDLog.txt", GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL)) != INVALID_HANDLE_VALUE) { SetFilePointer(g_DecompressLogFile, 0, NULL, FILE_END); if (pvs->dwFrame) { wsprintf(g_szDecompressBuffer, "Frames/s\tAvg. Decomp. Time\r\n"); WriteFile(g_DecompressLogFile, g_szDecompressBuffer, strlen(g_szDecompressBuffer), &g_dwDecompressBytesWritten, NULL); wsprintf(g_szDecompressBuffer, "%04d\t\t%03d\r\n", pvs->dwFrame * 1000 / g_OrigDecompressTime, g_AvgDecompressTime); WriteFile(g_DecompressLogFile, g_szDecompressBuffer, strlen(g_szDecompressBuffer), &g_dwDecompressBytesWritten, NULL); } else { wsprintf(g_szDecompressBuffer, "No frames were decompressed!"); WriteFile(g_DecompressLogFile, g_szDecompressBuffer, strlen(g_szDecompressBuffer), &g_dwDecompressBytesWritten, NULL); } CloseHandle(g_DecompressLogFile); } #endif
if (ICDecompressEnd((HIC)pvs->hIC) != MMSYSERR_NOERROR) { ERRORMESSAGE(("vcmStreamClose: Codec failed to stop\r\n")); return ((MMRESULT)VCMERR_FAILED); } }
// Close compressor/decompressor
if (pvs->hIC) ICClose((HIC)pvs->hIC);
// Nuke critical section
if ((pvs->fdwStream == ICMODE_COMPRESS) || (pvs->fdwStream == ICMODE_FASTCOMPRESS)) DeleteCriticalSection(&pvs->crsFrameNumber);
// Free video format buffers
if (pvs->pvfxSrc) MemFree(pvs->pvfxSrc); if (pvs->pvfxDst) MemFree(pvs->pvfxDst); if (pvs->pbmiPrev) MemFree(pvs->pbmiPrev);
// Free main VCM structure
MemFree(pvs); return ((MMRESULT)MMSYSERR_NOERROR); }
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmStreamSize | The vcmStreamSize function returns a recommended size for a * source or destination buffer on an Video Compression Manager (VCM) * stream. * * @parm HVCMSTREAM | hvs | Specifies the conversion stream. * * @parm DWORD | cbInput | Specifies the size in bytes of either the source * or destination buffer. The <p fdwSize> flags specify what the * input argument defines. This argument must be non-zero. * * @parm LPDWORD | pdwOutputBytes | Specifies a pointer to a <t DWORD> * that contains the size in bytes of the source or destination buffer. * The <p fdwSize> flags specify what the output argument defines. * If the <f vcmStreamSize> function succeeds, this location will * always be filled with a non-zero value. * * @parm DWORD | fdwSize | Specifies flags for the stream-size query. * * @flag VCM_STREAMSIZEF_SOURCE | Indicates that <p cbInput> contains * the size of the source buffer. The <p pdwOutputBytes> argument will * receive the recommended destination buffer size in bytes. * * @flag VCM_STREAMSIZEF_DESTINATION | Indicates that <p cbInput> * contains the size of the destination buffer. The <p pdwOutputBytes> * argument will receive the recommended source buffer size in bytes. * * @rdesc Returns zero if the function was successful. Otherwise, it returns * a non-zero error number. Possible error returns are: * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * @flag MMSYSERR_INVALFLAG | One or more flags are invalid. * @flag MMSYSERR_INVALPARAM | One or more arguments passed are invalid. * @flag VCMERR_NOTPOSSIBLE | The requested operation cannot be performed. * * @comm An application can use the <f vcmStreamSize> function to determine * suggested buffer sizes for either source or destination buffers. * The buffer sizes returned might be only an estimation of the * actual sizes required for conversion. Because actual conversion * sizes cannot always be determined without performing the conversion, * the sizes returned will usually be overestimated. * * In the event of an error, the location pointed to by * <p pdwOutputBytes> will receive zero. This assumes that the pointer * specified by <p pdwOutputBytes> is valid. * * @xref <f vcmStreamPrepareHeader> <f vcmStreamConvert> ***************************************************************************/ MMRESULT VCMAPI vcmStreamSize(HVCMSTREAM hvs, DWORD cbInput, PDWORD pdwOutputBytes, DWORD fdwSize) { PVCMSTREAM pvs = (PVCMSTREAM)hvs; DWORD dwNumFrames;
// Check input params
if (!hvs) { ERRORMESSAGE(("vcmStreamSize: Specified HVCMSTREAM handle is invalid, hvs=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALHANDLE); }
// Do the math
switch (VCM_STREAMSIZEF_QUERYMASK & fdwSize) { case VCM_STREAMSIZEF_SOURCE: if (pvs->pvfxSrc->dwFormatTag != VIDEO_FORMAT_BI_RGB) { // How many destination RGB bytes are needed to hold the decoded source
// buffer of cbInput compressed bytes
if (!(dwNumFrames = cbInput / pvs->pvfxSrc->nBlockAlign)) { ERRORMESSAGE(("vcmStreamSize: The requested operation cannot be performed\r\n")); return ((MMRESULT)VCMERR_NOTPOSSIBLE); } else *pdwOutputBytes = dwNumFrames * pvs->pvfxDst->nBlockAlign; } else { // How many destination compressed bytes are needed to hold the encoded source
// buffer of cbInput RGB bytes
if (!(dwNumFrames = cbInput / pvs->pvfxSrc->nBlockAlign)) { ERRORMESSAGE(("vcmStreamSize: The requested operation cannot be performed\r\n")); return ((MMRESULT)VCMERR_NOTPOSSIBLE); } else { if (cbInput % pvs->pvfxSrc->nBlockAlign) dwNumFrames++; *pdwOutputBytes = dwNumFrames * pvs->pvfxDst->nBlockAlign; } } return ((MMRESULT)MMSYSERR_NOERROR);
case VCM_STREAMSIZEF_DESTINATION: if (pvs->pvfxDst->dwFormatTag != VIDEO_FORMAT_BI_RGB) { // How many source RGB bytes can be encoded into a destination buffer
// of cbInput bytes
if (!(dwNumFrames = cbInput / pvs->pvfxDst->nBlockAlign)) { ERRORMESSAGE(("vcmStreamSize: The requested operation cannot be performed\r\n")); return ((MMRESULT)VCMERR_NOTPOSSIBLE); } else *pdwOutputBytes = dwNumFrames * pvs->pvfxSrc->nBlockAlign; } else { // How many source encoded bytes can be decoded into a destination buffer
// of cbInput RGB bytes
if (!(dwNumFrames = cbInput / pvs->pvfxDst->nBlockAlign)) { ERRORMESSAGE(("vcmStreamSize: The requested operation cannot be performed\r\n")); return ((MMRESULT)VCMERR_NOTPOSSIBLE); } else *pdwOutputBytes = dwNumFrames * pvs->pvfxSrc->nBlockAlign; } return ((MMRESULT)MMSYSERR_NOERROR);
default: ERRORMESSAGE(("vcmStreamSize: One or more flags are invalid\r\n")); return ((MMRESULT)MMSYSERR_NOTSUPPORTED); }
}
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmStreamReset | The vcmStreamReset function stops conversions * for a given Video Compression Manager (VCM) stream. All pending * buffers are marked as done and returned to the application. * * @parm HVCMSTREAM | hvs | Specifies the conversion stream. * * @rdesc Returns zero if the function was successful. Otherwise, it returns * a non-zero error number. Possible error returns are: * * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * * @flag MMSYSERR_INVALFLAG | One or more flags are invalid. * * @comm Resetting a VCM conversion stream is only necessary to reset * asynchronous conversion streams. However, resetting a synchronous * conversion stream will succeed, but no action will be taken. * * @xref <f vcmStreamConvert> <f vcmStreamClose> ***************************************************************************/ MMRESULT VCMAPI vcmStreamReset(HVCMSTREAM hvs) { PVCMSTREAM pvs = (PVCMSTREAM)hvs; PVCMSTREAMHEADER pvsh;
// Check input params
if (!hvs) { ERRORMESSAGE(("vcmSreamReset: Specified HVCMSTREAM handle is invalid, hvs=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALHANDLE); }
while (pvsh = DeQueVCMHeader(pvs)) { MarkVCMHeaderDone(pvsh); // Look into how this would be best handled
// What if the capture driver sends us those exact same
// messages for its own buffers?
// Test for the validity of the callback before doing this...
switch (pvs->fdwOpen) { case CALLBACK_FUNCTION: (*(VCMSTREAMPROC)pvs->dwCallback)(hvs, VCM_DONE, pvs->dwCallbackInstance, (DWORD_PTR)pvsh, 0); break;
case CALLBACK_EVENT: SetEvent((HANDLE)pvs->dwCallback); break;
case CALLBACK_WINDOW: PostMessage((HWND)pvs->dwCallback, MM_VCM_DONE, (WPARAM)hvs, (LPARAM)pvsh); break;
case CALLBACK_THREAD: PostThreadMessage((DWORD)pvs->dwCallback, MM_VCM_DONE, (WPARAM)hvs, (LPARAM)pvsh); break;
case CALLBACK_NULL: break;
default: break; } }
pvs->pvhFirst = NULL; pvs->pvhLast = NULL;
return ((MMRESULT)MMSYSERR_NOERROR); }
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmStreamMessage | This function sends a user-defined * message to a given Video Compression Manager (VCM) stream instance. * * @parm HVCMSTREAM | hvs | Specifies the conversion stream. * * @parm UINT | uMsg | Specifies the message that the VCM stream must * process. This message must be in the <m VCMDM_USER> message range * (above or equal to <m VCMDM_USER> and less than * <m VCMDM_RESERVED_LOW>). The exception to this restriction is * the <m VCMDM_STREAM_UPDATE> message. * * @parm LPARAM | lParam1 | Specifies the first message parameter. * * @parm LPARAM | lParam2 | Specifies the second message parameter. * * @rdesc The return value is specific to the user-defined VCM driver * message <p uMsg> sent. However, the following return values are * possible: * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * @flag MMSYSERR_INVALPARAM | <p uMsg> is not in the VCMDM_USER range. * @flag MMSYSERR_NOTSUPPORTED | The VCM driver did not process the message. ***************************************************************************/ MMRESULT VCMAPI vcmStreamMessage(HVCMSTREAM hvs, UINT uMsg, LPARAM lParam1, LPARAM lParam2) { PVCMSTREAM pvs = (PVCMSTREAM)hvs;
// Check input params
if (!hvs) { ERRORMESSAGE(("vcmStreamMessage: Specified HVCMSTREAM handle is invalid, hvs=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALHANDLE); }
// Check input params
if ((uMsg > VCMDM_RESERVED_HIGH) || (uMsg < VCMDM_RESERVED_LOW)) { ERRORMESSAGE(("vcmStreamMessage: Specified message is out of range, uMsg=0x%lX (expected value is between 0x%lX and 0x%lX)\r\n", uMsg, VCMDM_RESERVED_LOW, VCMDM_RESERVED_HIGH)); return ((MMRESULT)MMSYSERR_INVALPARAM); }
// Send the message to the codec.
if (pvs->hIC) if (ICSendMessage((HIC)(HVCMDRIVERID)pvs->hIC, uMsg, lParam1, lParam2) != ICERR_OK) { ERRORMESSAGE(("vcmStreamMessage: Codec failed to handle user-defined message correctly\r\n")); return ((MMRESULT)MMSYSERR_NOTSUPPORTED); }
return ((MMRESULT)MMSYSERR_NOERROR); }
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmStreamConvert | The vcmStreamConvert function requests the Video * Compression Manager (VCM) to perform a conversion on the specified conversion stream. A * conversion may be synchronous or asynchronous depending on how the * stream was opened. * * @parm HVCMSTREAM | has | Identifies the open conversion stream. * * @parm PVCMSTREAMHEADER | pash | Specifies a pointer to a stream header * that describes source and destination buffers for a conversion. This * header must have been prepared previously using the * <f vcmStreamPrepareHeader> function. * * @parm DWORD | fdwConvert | Specifies flags for doing the conversion. * * @flag VCM_STREAMCONVERTF_BLOCKALIGN | Specifies that only integral * numbers of blocks will be converted. Converted data will end on * block aligned boundaries. An application should use this flag for * all conversions on a stream until there is not enough source data * to convert to a block-aligned destination. In this case, the last * conversion should be specified without this flag. * * @flag VCM_STREAMCONVERTF_START | Specifies that the VCM conversion * stream should reinitialize its instance data. For example, if a * conversion stream holds instance data, such as delta or predictor * information, this flag will restore the stream to starting defaults. * Note that this flag can be specified with the VCM_STREAMCONVERTF_END * flag. * * @flag VCM_STREAMCONVERTF_END | Specifies that the VCM conversion * stream should begin returning pending instance data. For example, if * a conversion stream holds instance data, such as the tail end of an * echo filter operation, this flag will cause the stream to start * returning this remaining data with optional source data. Note that * this flag can be specified with the VCM_STREAMCONVERTF_START flag. * * @flag VCM_STREAMCONVERTF_FORCE_KEYFRAME | Specifies that the VCM conversion * stream should compress the current frame as a key frame. * * @rdesc Returns zero if the function was successful. Otherwise, it returns * a non-zero error number. Possible error returns are: * * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * * @flag MMSYSERR_INVALFLAG | One or more flags are invalid. * * @flag MMSYSERR_INVALPARAM | One or more arguments passed are invalid. * * @flag VCMERR_BUSY | The stream header <p pash> is currently in use * and cannot be reused. * * @flag VCMERR_UNPREPARED | The stream header <p pash> is currently * not prepared by the <f vcmStreamPrepareHeader> function. * * @comm The source and destination data buffers must be prepared with * <f vcmStreamPrepareHeader> before they are passed to <f vcmStreamConvert>. * * If an asynchronous conversion request is successfully queued by * the VCM or driver, and later the conversion is determined to * be impossible, then the <t VCMSTREAMHEADER> will be posted back to * the application's callback with the <e VCMSTREAMHEADER.cbDstLengthUsed> * member set to zero. * * @xref <f vcmStreamOpen> <f vcmStreamReset> <f vcmStreamPrepareHeader> * <f vcmStreamUnprepareHeader> ***************************************************************************/ MMRESULT VCMAPI vcmStreamConvert(HVCMSTREAM hvs, PVCMSTREAMHEADER pvsh, DWORD fdwConvert) { MMRESULT mmr; PVCMSTREAM pvs = (PVCMSTREAM)hvs; BOOL fKeyFrame; BOOL fTemporalCompress; BOOL fFastTemporal; DWORD dwMaxSizeThisFrame = 0xffffff; DWORD ckid = 0UL; DWORD dwFlags; DWORD dwTimestamp;
#ifdef LOGFILE_ON
if ((pvs->fdwStream == ICMODE_COMPRESS) || (pvs->fdwStream == ICMODE_FASTCOMPRESS)) g_CompressTime = GetTickCount(); else if ((pvs->fdwStream == ICMODE_DECOMPRESS) || (pvs->fdwStream == ICMODE_FASTDECOMPRESS)) g_DecompressTime = GetTickCount(); #endif
// Check input params
if (!hvs) { ERRORMESSAGE(("vcmStreamConvert: Specified HVCMSTREAM handle is invalid, hvs=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALHANDLE); } if (!pvsh) { ERRORMESSAGE(("vcmStreamConvert: Specified PVCMSTREAMHEADER pointer is invalid, pvsh=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALHANDLE); } if (pvsh->cbStruct < sizeof(VCMSTREAMHEADER)) { ERRORMESSAGE(("vcmStreamConvert: The size of the VCM stream header is invalid, pvsh->cbStruct=%ld (expected value is %ld)\r\n", pvsh->cbStruct, sizeof(VCMSTREAMHEADER))); return ((MMRESULT)MMSYSERR_INVALHANDLE); }
// Return if buffer is already being converted
if (IsVCMHeaderInQueue(pvsh)) { ERRORMESSAGE(("vcmStreamConvert: Buffer is already being converted\r\n")); return ((MMRESULT)VCMERR_BUSY); }
// Return if buffer has not been prepared
if (!IsVCMHeaderPrepared(pvsh)) { ERRORMESSAGE(("vcmStreamConvert: Buffer has not been prepared\r\n")); return ((MMRESULT)VCMERR_UNPREPARED); }
// Set flags
MarkVCMHeaderNotDone(pvsh); pvsh->cbSrcLengthUsed = pvsh->cbSrcLength; pvsh->cbDstLengthUsed = pvsh->cbDstLength; pvsh->cbPrevLengthUsed = pvsh->cbPrevLength; MarkVCMHeaderInQueue(pvsh);
// Queue buffer
pvsh->pNext = NULL; if (pvs->pvhLast) pvs->pvhLast->pNext = pvsh; else pvs->pvhFirst = pvsh; pvs->pvhLast = pvsh;
if ((pvs->fdwStream == ICMODE_COMPRESS) || (pvs->fdwStream == ICMODE_FASTCOMPRESS)) { // Save the current time
dwTimestamp = GetTickCount();
// We need the following crs to make sure we don't miss any of the I-Frame requests
// emittted by the UI. Problematic scenario: pvs->dwFrame is at 123 for instance.
// The UI thread requests an I-Frame by setting pvs->dwFrame to 0. If the capture/compression
// thread was in ICCompress() (which is very probable since it takes quite some time
// to compress a frame), pvs->dwFrame will be incremented by one when ICCompress()
// returns. We failed to handle the I-Frame request correctly, since the next time
// ICCompress() gets called pvs->dwFrame will be equal to 1, for which we do not
// generate an I-Frame.
EnterCriticalSection(&pvs->crsFrameNumber);
// Compress
fTemporalCompress = pvs->dwICInfoFlags & VIDCF_TEMPORAL; fFastTemporal = pvs->dwICInfoFlags & VIDCF_FASTTEMPORALC; fKeyFrame = !fTemporalCompress || (fTemporalCompress && !fFastTemporal && ((pvsh->pbPrev == (PBYTE)NULL) || (pvsh->cbPrevLength == (DWORD)NULL))) || (pvs->fPeriodicIFrames && (((dwTimestamp > pvs->dwLastIFrameTime) && ((dwTimestamp - pvs->dwLastIFrameTime) > MIN_IFRAME_REQUEST_INTERVAL)))) || (pvs->dwFrame == 0) || (fdwConvert & VCM_STREAMCONVERTF_FORCE_KEYFRAME); dwFlags = fKeyFrame ? AVIIF_KEYFRAME : 0; #if 0
dwMaxSizeThisFrame = fKeyFrame ? 0xffffff : pvs->dwTargetFrameRate ? pvs->dwTargetByterate * pvs->dwTargetFrameRate / 1000UL : 0; #else
dwMaxSizeThisFrame = pvs->dwTargetFrameRate ? pvs->dwTargetByterate * 100UL / pvs->dwTargetFrameRate : 0; #endif
// We need to modify the frame number so that the codec can generate
// a valid TR. TRs use MPIs as their units. So we need to generate a
// frame number assuming a 29.97Hz capture rate, even though we will be
// capturing at some other rate.
if (pvs->dwLastTimestamp == ULONG_MAX) { // This is the first frame
pvs->dwFrame = 0UL;
// Save the current time
pvs->dwLastTimestamp = dwTimestamp;
// DEBUGMSG (ZONE_VCM, ("vcmStreamConvert: Last Timestamp = ULONG_MAX => Frame # = 0\r\n"));
} else { // Compare the current timestamp to the last one we saved. The difference
// will let us normalize the frame count to 29.97Hz.
if (fKeyFrame) { pvs->dwFrame = 0UL; pvs->dwLastTimestamp = dwTimestamp; } else pvs->dwFrame = (dwTimestamp - pvs->dwLastTimestamp) * 2997 / 100000UL;
// DEBUGMSG (ZONE_VCM, ("vcmStreamConvert: Last Timestamp = %ld => Frame # = %ld\r\n", pvs->dwLastTimestamp, pvs->dwFrame));
}
if (fKeyFrame) { pvs->dwLastIFrameTime = dwTimestamp; DEBUGMSG (ZONE_VCM, ("vcmStreamConvert: Generating an I-Frame...\r\n")); }
mmr = ICCompress((HIC)pvs->hIC, fKeyFrame ? ICCOMPRESS_KEYFRAME : 0, (LPBITMAPINFOHEADER)&pvs->pvfxDst->bih, pvsh->pbDst, (LPBITMAPINFOHEADER)&pvs->pvfxSrc->bih, pvsh->pbSrc, &ckid, &dwFlags, pvs->dwFrame++, dwMaxSizeThisFrame, 10000UL - (pvs->dwQuality * 322UL), fKeyFrame | fFastTemporal ? NULL : (LPBITMAPINFOHEADER)&pvs->pbmiPrev, fKeyFrame | fFastTemporal ? NULL : pvsh->pbPrev);
// Allow the UI to modify the frame number on its own thread
LeaveCriticalSection(&pvs->crsFrameNumber);
if (mmr != MMSYSERR_NOERROR) { #ifdef LOGFILE_ON
if (pvs->dwFrame < 4096) { if (pvs->dwFrame ==1) g_OrigCompressTime = g_CompressTime; g_aCompressTime[pvs->dwFrame-1] = g_CompressTime = GetTickCount() - g_CompressTime; if ((g_CompressLogFile = CreateFile("C:\\VCMCLog.txt", GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL)) != INVALID_HANDLE_VALUE) { SetFilePointer(g_CompressLogFile, 0, NULL, FILE_END); wsprintf(g_szCompressBuffer, "%04d\t\t%08d\t\t.o0Failed!0o.\r\n", pvs->dwFrame-1, g_OrigCompressTime); WriteFile(g_CompressLogFile, g_szCompressBuffer, strlen(g_szCompressBuffer), &g_dwCompressBytesWritten, NULL); CloseHandle(g_CompressLogFile); } } #endif
ERRORMESSAGE(("vcmStreamConvert: ICCompress() failed, mmr=%ld\r\n", mmr)); // Get the handle to the video device associated to the capture window
pvsh = DeQueVCMHeader(pvs); MarkVCMHeaderDone(pvsh);
return ((MMRESULT)VCMERR_FAILED); }
pvsh->cbDstLengthUsed = pvs->pvfxDst->bih.biSizeImage;
if ((fTemporalCompress) && (!fFastTemporal)) { if (!pvsh->pbPrev) pvsh->pbPrev = (PBYTE)MemAlloc(pvs->pvfxSrc->bih.biSizeImage);
if (pvsh->pbPrev) { // What about fast temporal?
if (mmr = ICDecompress((HIC)pvs->hIC, 0, (LPBITMAPINFOHEADER)&pvs->pvfxDst->bih, pvsh->pbDst, (LPBITMAPINFOHEADER)&pvs->pvfxSrc->bih, pvsh->pbPrev) != MMSYSERR_NOERROR) { ERRORMESSAGE(("vcmStreamConvert: ICCompress() failed, mmr=%ld\r\n", mmr)); // Get the handle to the video device associated to the capture window
pvsh = DeQueVCMHeader(pvs); MarkVCMHeaderDone(pvsh); return ((MMRESULT)VCMERR_FAILED); // Do we really want to quit?
} } } } else if ((pvs->fdwStream == ICMODE_DECOMPRESS) || (pvs->fdwStream == ICMODE_FASTDECOMPRESS)) { // Decompress
pvs->dwFrame++;
pvs->pvfxSrc->bih.biSizeImage = pvsh->cbSrcLength;
if (mmr = ICDecompress((HIC)pvs->hIC, 0, (LPBITMAPINFOHEADER)&pvs->pvfxSrc->bih, pvsh->pbSrc, (LPBITMAPINFOHEADER)&pvs->pvfxDst->bih, pvsh->pbDst) != MMSYSERR_NOERROR) { #ifdef LOGFILE_ON
if (pvs->dwFrame < 4096) { if (pvs->dwFrame ==1) g_OrigDecompressTime = g_DecompressTime; g_aDecompressTime[pvs->dwFrame-1] = g_DecompressTime = GetTickCount() - g_DecompressTime; if ((g_DecompressLogFile = CreateFile("C:\\VCMDLog.txt", GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL)) != INVALID_HANDLE_VALUE) { SetFilePointer(g_DecompressLogFile, 0, NULL, FILE_END); wsprintf(g_szDecompressBuffer, "%04d\t\t%08d\t\t.o0Failed!0o.\r\n", pvs->dwFrame-1, g_OrigDecompressTime); WriteFile(g_DecompressLogFile, g_szDecompressBuffer, strlen(g_szDecompressBuffer), &g_dwDecompressBytesWritten, NULL); CloseHandle(g_DecompressLogFile); } } #endif
ERRORMESSAGE(("vcmStreamConvert: ICDecompress() failed, mmr=%ld\r\n", mmr)); // Get the handle to the video device associated to the capture window
pvsh = DeQueVCMHeader(pvs); MarkVCMHeaderDone(pvsh); return ((MMRESULT)VCMERR_FAILED); }
}
#ifdef LOGFILE_ON
if (pvs->dwFrame < 4096) { if ((pvs->fdwStream == ICMODE_COMPRESS) || (pvs->fdwStream == ICMODE_FASTCOMPRESS)) { if (pvs->dwFrame == 1) g_OrigCompressTime = g_CompressTime; g_aCompressTime[pvs->dwFrame-1] = g_CompressTime = GetTickCount() - g_CompressTime; if ((g_CompressLogFile = CreateFile("C:\\VCMCLog.txt", GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL)) != INVALID_HANDLE_VALUE) { SetFilePointer(g_CompressLogFile, 0, NULL, FILE_END); wsprintf(g_szCompressBuffer, "%04d\t\t%08d\t\t%05d\t\t%03d\r\n", pvs->dwFrame-1, g_OrigCompressTime, pvs->pvfxDst->bih.biSizeImage, g_CompressTime); WriteFile(g_CompressLogFile, g_szCompressBuffer, strlen(g_szCompressBuffer), &g_dwCompressBytesWritten, NULL); CloseHandle(g_CompressLogFile); } } else if ((pvs->fdwStream == ICMODE_DECOMPRESS) || (pvs->fdwStream == ICMODE_FASTDECOMPRESS)) { if (pvs->dwFrame == 1) g_OrigDecompressTime = g_DecompressTime; g_aDecompressTime[pvs->dwFrame-1] = g_DecompressTime = GetTickCount() - g_DecompressTime; if ((g_DecompressLogFile = CreateFile("C:\\VCMDLog.txt", GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL)) != INVALID_HANDLE_VALUE) { SetFilePointer(g_DecompressLogFile, 0, NULL, FILE_END); wsprintf(g_szDecompressBuffer, "%04d\t\t%08d\t\t%05d\t\t%03d\r\n", pvs->dwFrame-1, g_OrigDecompressTime, pvs->pvfxDst->bih.biSizeImage, g_DecompressTime); WriteFile(g_DecompressLogFile, g_szDecompressBuffer, strlen(g_szDecompressBuffer), &g_dwDecompressBytesWritten, NULL); CloseHandle(g_DecompressLogFile); } } } #endif
// Get the handle to the video device associated to the capture window
pvsh = DeQueVCMHeader(pvs); MarkVCMHeaderDone(pvsh);
// Test for the validity of the callback before doing this...
switch (pvs->fdwOpen) { case CALLBACK_FUNCTION: (*(VCMSTREAMPROC)pvs->dwCallback)(hvs, VCM_DONE, pvs->dwCallbackInstance, (DWORD_PTR)pvsh, 0); break;
case CALLBACK_EVENT: SetEvent((HANDLE)pvs->dwCallback); break;
case CALLBACK_WINDOW: PostMessage((HWND)pvs->dwCallback, MM_VCM_DONE, (WPARAM)hvs, (LPARAM)pvsh); break;
case CALLBACK_THREAD: PostThreadMessage((DWORD)pvs->dwCallback, MM_VCM_DONE, (WPARAM)hvs, (LPARAM)pvsh); break;
case CALLBACK_NULL: default: break; }
return ((MMRESULT)MMSYSERR_NOERROR);
}
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmStreamPrepareHeader | The vcmStreamPrepareHeader * function prepares an <t VCMSTREAMHEADER> for an Video Compression * Manager (VCM) stream conversion. This function must be called for * every stream header before it can be used in a conversion stream. An * application only needs to prepare a stream header once for the life of * a given stream; the stream header can be reused as long as the same * source and destiniation buffers are used, and the size of the source * and destination buffers do not exceed the sizes used when the stream * header was originally prepared. * * @parm HVCMSTREAM | has | Specifies a handle to the conversion steam. * * @parm PVCMSTREAMHEADER | pash | Specifies a pointer to an <t VCMSTREAMHEADER> * structure that identifies the source and destination data buffers to * be prepared. * * @parm DWORD | fdwPrepare | This argument is not used and must be set to * zero. * * @rdesc Returns zero if the function was successful. Otherwise, it returns * a non-zero error number. Possible error returns are: * * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * * @flag MMSYSERR_INVALPARAM | One or more arguments passed are invalid. * * @flag MMSYSERR_INVALFLAG | One or more flags are invalid. * * @flag MMSYSERR_NOMEM | Unable to allocate resources. * * @comm Preparing a stream header that has already been prepared has no * effect, and the function returns zero. However, an application should * take care to structure code so multiple prepares do not occur. * * @xref <f vcmStreamUnprepareHeader> <f vcmStreamOpen> ***************************************************************************/ MMRESULT VCMAPI vcmStreamPrepareHeader(HVCMSTREAM hvs, PVCMSTREAMHEADER pvsh, DWORD fdwPrepare) { MMRESULT mmr = (MMRESULT)MMSYSERR_NOERROR;
// Check input params
if (!hvs) { ERRORMESSAGE(("vcmStreamPrepareHeader: Specified handle is invalid, hvs=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALHANDLE); } if (!pvsh) { ERRORMESSAGE(("vcmStreamPrepareHeader: Specified pointer is invalid, pvsh=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALHANDLE); }
// Return if buffer has already been prepared
if (IsVCMHeaderPrepared(pvsh)) { ERRORMESSAGE(("vcmStreamPrepareHeader: Buffer has already been prepared\r\n")); return (mmr); }
#ifdef REALLY_LOCK
// Lock the buffers
if (!VirtualLock(pvsh, (DWORD)sizeof(VCMSTREAMHEADER))) { ERRORMESSAGE(("vcmStreamPrepareHeader: VirtualLock() failed\r\n")); mmr = (MMRESULT)MMSYSERR_NOMEM; } else { if (!VirtualLock(pvsh->pbSrc, pvsh->cbSrcLength)) { ERRORMESSAGE(("vcmStreamPrepareHeader: VirtualLock() failed\r\n")); VirtualUnlock(pvsh, (DWORD)sizeof(VCMSTREAMHEADER)); mmr = (MMRESULT)MMSYSERR_NOMEM; } else { if (!VirtualLock(pvsh->pbDst, pvsh->cbDstLength)) { ERRORMESSAGE(("vcmStreamPrepareHeader: VirtualLock() failed\r\n")); VirtualUnlock(pvsh->pbSrc, pvsh->cbSrcLength); VirtualUnlock(pvsh, (DWORD)sizeof(VCMSTREAMHEADER)); mmr = (MMRESULT)MMSYSERR_NOMEM; } } } #endif
// Update flag
if (mmr == MMSYSERR_NOERROR) MarkVCMHeaderPrepared(pvsh);
return (mmr); }
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmStreamUnprepareHeader | The vcmStreamUnprepareHeader function * cleans up the preparation performed by the <f vcmStreamPrepareHeader> * function for an Video Compression Manager (VCM) stream. This function must * be called after the VCM is finished with the given buffers. An * application must call this function before freeing the source and * destination buffers. * * @parm HVCMSTREAM | has | Specifies a handle to the conversion steam. * * @parm PVCMSTREAMHEADER | pash | Specifies a pointer to an <t VCMSTREAMHEADER> * structure that identifies the source and destination data buffers to * be unprepared. * * @parm DWORD | fdwUnprepare | This argument is not used and must be set to * zero. * * @rdesc Returns zero if the function was successful. Otherwise, it returns * a non-zero error number. Possible error returns are: * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * @flag MMSYSERR_INVALPARAM | One or more arguments passed are invalid. * @flag MMSYSERR_INVALFLAG | One or more flags are invalid. * @flag VCMERR_BUSY | The stream header <p pash> is currently in use * and cannot be unprepared. * @flag VCMERR_UNPREPARED | The stream header <p pash> was * not prepared by the <f vcmStreamPrepareHeader> function. * * @comm Unpreparing a stream header that has already been unprepared is * an error. An application must specify the source and destination * buffer lengths (<e VCMSTREAMHEADER.cbSrcLength> and * <e VCMSTREAMHEADER.cbDstLength> respectively) that were used * during the corresponding <f vcmStreamPrepareHeader> call. Failing * to reset these member values will cause <f vcmStreamUnprepareHeader> * to fail with MMSYSERR_INVALPARAM. * * Note that there are some errors that the VCM can recover from. The * VCM will return a non-zero error, yet the stream header will be * properly unprepared. To determine whether the stream header was * actually unprepared an application can examine the * VCMSTREAMHEADER_STATUSF_PREPARED flag. The header will always be * unprepared if <f vcmStreamUnprepareHeader> returns success. * * @xref <f vcmStreamPrepareHeader> <f vcmStreamClose> ***************************************************************************/ MMRESULT VCMAPI vcmStreamUnprepareHeader(HVCMSTREAM hvs, PVCMSTREAMHEADER pvsh, DWORD fdwUnprepare) { MMRESULT mmr = (MMRESULT)MMSYSERR_NOERROR;
// Check input params
if (!hvs) { ERRORMESSAGE(("vcmStreamUnprepareHeader: Specified handle is invalid, hvs=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALHANDLE); } if (!pvsh) { ERRORMESSAGE(("vcmStreamUnprepareHeader: Specified pointer is invalid, pvsh=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); }
// Return if buffer is currently in use
if (IsVCMHeaderInQueue(pvsh)) { ERRORMESSAGE(("vcmStreamUnprepareHeader: Buffer is currently in use\r\n")); return ((MMRESULT)VCMERR_BUSY); }
// Return if buffer has not been prepared
if (!IsVCMHeaderPrepared(pvsh)) { ERRORMESSAGE(("vcmStreamUnprepareHeader: Buffer has not been prepared\r\n")); return ((MMRESULT)VCMERR_UNPREPARED); }
#ifdef REALLY_LOCK
// Unlock the buffers
VirtualUnlock(pvsh->pbSrc, pvsh->cbSrcLength); VirtualUnlock(pvsh->pbDst, pvsh->cbDstLength); VirtualUnlock(pvsh, (DWORD)sizeof(VCMSTREAMHEADER)); #endif
// Update flag
MarkVCMHeaderUnprepared(pvsh);
return ((MMRESULT)MMSYSERR_NOERROR); }
PVCMSTREAMHEADER DeQueVCMHeader(PVCMSTREAM pvs) { PVCMSTREAMHEADER pvsh;
if (pvsh = pvs->pvhFirst) { MarkVCMHeaderUnQueued(pvsh); pvs->pvhFirst = pvsh->pNext; if (pvs->pvhFirst == NULL) pvs->pvhLast = NULL; }
return (pvsh); }
/*****************************************************************************
* @doc INTERNAL DEVCAPSFUNC * * @func MMRESULT | vcmDevCapsReadFromReg | This function looks up the * capabilities of a specified video capture input device from the registry. * * @parm UINT | szDeviceName | Specifies the video capture input device driver name. * * @parm UINT | szDeviceVersion | Specifies the video capture input device driver version. * May be NULL. * * @parm PVIDEOINCAPS | pvc | Specifies a pointer to a <t VIDEOINCAPS> * structure. This structure is filled with information about the * capabilities of the device. * * @parm UINT | cbvc | Specifies the size of the <t VIDEOINCAPS> structure. * * @rdesc The return value is zero if the function is successful. Otherwise, it returns * an error number. Possible error values include the following: * @flag MMSYSERR_INVALPARAM | Specified pointer is invalid, or its content is invalid. * @flag VCMERR_NOREGENTRY | No registry entry for specified capture device driver was found. * * @comm Only <p cbwc> bytes (or less) of information is copied to the location * pointed to by <p pvc>. If <p cbwc> is zero, nothing is copied, and * the function returns zero. * * @xref <f vcmGetDevCaps> <f vcmDevCapsProfile> <f vcmDevCapsWriteToReg> ****************************************************************************/ MMRESULT VCMAPI vcmDevCapsReadFromReg(LPSTR szDeviceName, LPSTR szDeviceVersion,PVIDEOINCAPS pvc, UINT cbvc) { MMRESULT mmr = (MMRESULT)MMSYSERR_NOERROR; HKEY hDeviceKey, hKey; DWORD dwSize, dwType; char szKey[MAX_PATH + MAX_VERSION + 2]; LONG lRet;
// Check input params
if (!szDeviceName) { ERRORMESSAGE(("vcmDevCapsReadFromReg: Specified pointer is invalid, szDeviceName=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if (szDeviceName[0] == '\0') { ERRORMESSAGE(("vcmDevCapsReadFromReg: Video capture input device driver name is empty\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if (!pvc) { ERRORMESSAGE(("vcmDevCapsReadFromReg: Specified pointer is invalid, pvc=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if (!cbvc) { ERRORMESSAGE(("vcmDevCapsReadFromReg: Specified structure size is invalid, cbvc=0\r\n")); return ((MMRESULT)MMSYSERR_NOERROR); }
// Check if the main capture devices key is there
if (RegOpenKey(HKEY_LOCAL_MACHINE, szRegDeviceKey, &hDeviceKey) != ERROR_SUCCESS) return ((MMRESULT)VCMERR_NOREGENTRY);
//If we have version info use that to build the key name
if (szDeviceVersion) { wsprintf(szKey, "%s, %s", szDeviceName, szDeviceVersion); } else { wsprintf(szKey, "%s", szDeviceName); }
// Check if there already is a key for the current device
if (RegOpenKey(hDeviceKey, szKey, &hKey) != ERROR_SUCCESS) { mmr = (MMRESULT)VCMERR_NOREGENTRY; goto MyExit0; }
// Get the values stored in the key
dwSize = sizeof(DWORD); RegQueryValueEx(hKey, (LPTSTR)szRegdwImageSizeKey, NULL, &dwType, (LPBYTE)&pvc->dwImageSize, &dwSize); dwSize = sizeof(DWORD); RegQueryValueEx(hKey, (LPTSTR)szRegdwNumColorsKey, NULL, &dwType, (LPBYTE)&pvc->dwNumColors, &dwSize); dwSize = sizeof(DWORD); pvc->dwStreamingMode = STREAMING_PREFER_FRAME_GRAB; RegQueryValueEx(hKey, (LPTSTR)szRegdwStreamingModeKey, NULL, &dwType, (LPBYTE)&pvc->dwStreamingMode, &dwSize); dwSize = sizeof(DWORD); pvc->dwDialogs = FORMAT_DLG_OFF | SOURCE_DLG_ON; RegQueryValueEx(hKey, (LPTSTR)szRegdwDialogsKey, NULL, &dwType, (LPBYTE)&pvc->dwDialogs, &dwSize);
// Check dwNumColors to figure out if we need to read the palettes too
if (pvc->dwNumColors & VIDEO_FORMAT_NUM_COLORS_16) { dwSize = NUM_4BIT_ENTRIES * sizeof(RGBQUAD); if (RegQueryValueEx(hKey, (LPTSTR)szRegbmi4bitColorsKey, NULL, &dwType, (LPBYTE)&pvc->bmi4bitColors[0], &dwSize) == ERROR_SUCCESS) { pvc->dwFlags |= VICF_4BIT_TABLE; } else FillMemory ((LPBYTE)&pvc->bmi4bitColors[0], NUM_4BIT_ENTRIES * sizeof(RGBQUAD), 0); } if (pvc->dwNumColors & VIDEO_FORMAT_NUM_COLORS_256) { dwSize = NUM_8BIT_ENTRIES * sizeof(RGBQUAD); if (RegQueryValueEx(hKey, (LPTSTR)szRegbmi8bitColorsKey, NULL, &dwType, (LPBYTE)&pvc->bmi8bitColors[0], &dwSize) == ERROR_SUCCESS) { pvc->dwFlags |= VICF_8BIT_TABLE; } else FillMemory ((LPBYTE)&pvc->bmi8bitColors[0], NUM_8BIT_ENTRIES * sizeof(RGBQUAD), 0); }
// Close the registry keys
RegCloseKey(hKey); MyExit0: RegCloseKey(hDeviceKey);
return (mmr);
}
/*****************************************************************************
* @doc INTERNAL DEVCAPSFUNC * * @func MMRESULT | vcmDevCapsProfile | This function profiles the video capture * input device to figure out its capabilities. * * @parm PVIDEOINCAPS | pvc | Specifies a pointer to a <t VIDEOINCAPS> * structure. This structure is filled with information about the * capabilities of the device. * * @parm UINT | cbvc | Specifies the size of the <t VIDEOINCAPS> structure. * * @rdesc The return value is zero if the function is successful. Otherwise, it returns * an error number. Possible error values include the following: * @flag MMSYSERR_INVALPARAM | Specified pointer is invalid, or its content is invalid. * @flag MMSYSERR_NOMEM | A memory allocation failed. * @flag MMSYSERR_NODRIVER | No capture device driver or device is present. * @flag VCMERR_NONSPECIFIC | The capture driver failed to provide description information. * * @comm Only <p cbwc> bytes (or less) of information is copied to the location * pointed to by <p pvc>. If <p cbwc> is zero, nothing is copied, and * the function returns zero. * * @xref <f vcmGetDevCaps> <f videoDevCapsReadFromReg> <f videoDevCapsWriteToReg> ****************************************************************************/ MMRESULT VCMAPI vcmDevCapsProfile(UINT uDevice, PVIDEOINCAPS pvc, UINT cbvc) { MMRESULT mmr = (MMRESULT)MMSYSERR_NOERROR; FINDCAPTUREDEVICE fcd; LPBITMAPINFO lpbmi; HCAPDEV hCapDev = (HCAPDEV)NULL; int k,l; BOOL b4bitPalInitialized = FALSE; BOOL b8bitPalInitialized = FALSE; BOOL bRet;
// Check input params
if (!pvc) { ERRORMESSAGE(("vcmDevCapsProfile: Specified pointer is invalid, pvc=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if (!cbvc) { ERRORMESSAGE(("vcmDevCapsProfile: Specified structure size is invalid, cbvc=0\r\n")); return ((MMRESULT)MMSYSERR_NOERROR); }
// Check input params
if ((uDevice >= MAXVIDEODRIVERS) && (uDevice != VIDEO_MAPPER)) { ERRORMESSAGE(("vcmGetDevCaps: Specified capture device ID is invalid, uDevice=%ld (expected values are 0x%lX or between 0 and %ld)\r\n", uDevice, VIDEO_MAPPER, MAXVIDEODRIVERS-1)); return ((MMRESULT)MMSYSERR_BADDEVICEID); }
// Allocate space for BMIH and palette
if ((lpbmi = (LPBITMAPINFO)MemAlloc(sizeof(BITMAPINFOHEADER) + NUM_8BIT_ENTRIES * sizeof(RGBQUAD))) == NULL) { ERRORMESSAGE(("vcmDevCapsProfile: BMIH and palette allocation failed\r\n")); return ((MMRESULT)MMSYSERR_NOMEM); }
// For now, always set the preferred streaming mode to STREAMING_PREFER_FRAME_GRAB
// But in the future, do some real profiling...
pvc->dwStreamingMode = STREAMING_PREFER_FRAME_GRAB; pvc->dwDialogs = FORMAT_DLG_OFF | SOURCE_DLG_OFF;
lpbmi->bmiHeader.biPlanes = 1;
// if VIDEO_MAPPER: use first capture device
fcd.dwSize = sizeof (FINDCAPTUREDEVICE);
if (uDevice == VIDEO_MAPPER) { bRet = FindFirstCaptureDevice(&fcd, NULL); }
else bRet = FindFirstCaptureDeviceByIndex(&fcd, uDevice);
if (bRet) hCapDev = OpenCaptureDevice(fcd.nDeviceIndex);
if (hCapDev != NULL) { // If the driver exposes a source dialog, there is no need to go further:
// we advertise this dialog and that's it. On the other hand, if there isn't
// a source dialog per se, it may be hidden in the format dialog, in which case
// we advertise the format dialog.
if (CaptureDeviceDialog(hCapDev, (HWND)NULL, CAPDEV_DIALOG_SOURCE | CAPDEV_DIALOG_QUERY, NULL)) pvc->dwDialogs |= SOURCE_DLG_ON; else if (CaptureDeviceDialog(hCapDev, (HWND)NULL, CAPDEV_DIALOG_IMAGE | CAPDEV_DIALOG_QUERY, NULL)) pvc->dwDialogs |= FORMAT_DLG_ON;
// since we don't know anything about this adapter, we just use its default format
// and report that we can get any size, which we will do through conversion
// we will report the correct color depth only if the default size matches one of
// our sizes, we'll always report 24bit color
pvc->dwImageSize |= VIDEO_FORMAT_IMAGE_SIZE_USE_DEFAULT;
// get the device's default format
lpbmi->bmiHeader.biSize = GetCaptureDeviceFormatHeaderSize(hCapDev); GetCaptureDeviceFormat(hCapDev, (LPBITMAPINFOHEADER)lpbmi);
// record this default in the registry
if (pvc->szDeviceName[0] != '\0') { vcmDefaultFormatWriteToReg(pvc->szDeviceName, pvc->szDeviceVersion, (LPBITMAPINFOHEADER)lpbmi); } else { //Fall back and use driver name as the key
vcmDefaultFormatWriteToReg(fcd.szDeviceName, pvc->szDeviceVersion, (LPBITMAPINFOHEADER)lpbmi); }
if ((lpbmi->bmiHeader.biCompression == VIDEO_FORMAT_BI_RGB) || (lpbmi->bmiHeader.biCompression == VIDEO_FORMAT_YVU9) || (lpbmi->bmiHeader.biCompression == VIDEO_FORMAT_YUY2) || (lpbmi->bmiHeader.biCompression == VIDEO_FORMAT_UYVY) || (lpbmi->bmiHeader.biCompression == VIDEO_FORMAT_I420) || (lpbmi->bmiHeader.biCompression == VIDEO_FORMAT_IYUV)) { if (lpbmi->bmiHeader.biCompression == VIDEO_FORMAT_YVU9) k = VIDEO_FORMAT_NUM_COLORS_YVU9; else if (lpbmi->bmiHeader.biCompression == VIDEO_FORMAT_YUY2) k = VIDEO_FORMAT_NUM_COLORS_YUY2; else if (lpbmi->bmiHeader.biCompression == VIDEO_FORMAT_UYVY) k = VIDEO_FORMAT_NUM_COLORS_UYVY; else if (lpbmi->bmiHeader.biCompression == VIDEO_FORMAT_I420) k = VIDEO_FORMAT_NUM_COLORS_I420; else if (lpbmi->bmiHeader.biCompression == VIDEO_FORMAT_IYUV) k = VIDEO_FORMAT_NUM_COLORS_IYUV; else { for (k = 0; k < NUM_BITDEPTH_ENTRIES; k++) { if (lpbmi->bmiHeader.biBitCount == g_aiBitDepth[k]) break; } if (k < NUM_BITDEPTH_ENTRIES) k = g_aiNumColors[k]; else k = 0; } }
// converted sizes will probably get to RGB24, so always say that we support it
pvc->dwNumColors |= VIDEO_FORMAT_NUM_COLORS_16777216;
// always say that we support these 2 standard formats
pvc->dwImageSize |= VIDEO_FORMAT_IMAGE_SIZE_176_144 | VIDEO_FORMAT_IMAGE_SIZE_128_96; for (l=0; l<VIDEO_FORMAT_NUM_RESOLUTIONS; l++) { if ((lpbmi->bmiHeader.biWidth == (LONG)g_awResolutions[l].framesize.biWidth) && (lpbmi->bmiHeader.biHeight == (LONG)g_awResolutions[l].framesize.biHeight)) { pvc->dwImageSize |= g_awResolutions[l].dwRes; pvc->dwNumColors |= k; break; } } } else mmr = (MMRESULT)MMSYSERR_NODRIVER;
// Close capture device
if (hCapDev) CloseCaptureDevice(hCapDev);
// Free BMIH + palette space
if (lpbmi) MemFree(lpbmi);
return (mmr);
}
/*****************************************************************************
* @doc EXTERNAL DEVCAPSFUNC * * @func MMRESULT | vcmDevCapsWriteToReg | This function writes the * capabilities of a specified video capture input device into the registry. * * @parm UINT | szDeviceName | Specifies the video capture input device driver name. * * @parm UINT | szDeviceVersion | Specifies the video capture input device driver version. * May be NULL. * * @parm PVIDEOINCAPS | pvc | Specifies a pointer to a <t VIDEOINCAPS> * structure. This structure is filled with information about the * capabilities of the device. * * @parm UINT | cbvc | Specifies the size of the <t VIDEOINCAPS> structure. * * @rdesc The return value is zero if the function is successful. Otherwise, it returns * an error number. Possible error values include the following: * @flag MMSYSERR_INVALPARAM | Specified pointer is invalid, or its content is invalid. * @flag VCMERR_NOREGENTRY | No registry entry could be created for the specified capture device driver. * * @comm Only <p cbwc> bytes (or less) of information is copied to the location * pointed to by <p pvc>. If <p cbwc> is zero, nothing is copied, and * the function returns zero. * * @xref <f vcmGetDevCaps> <f videoDevCapsProfile> <f videoDevCapsWriteToReg> ****************************************************************************/ MMRESULT VCMAPI vcmDevCapsWriteToReg(LPSTR szDeviceName, LPSTR szDeviceVersion, PVIDEOINCAPS pvc, UINT cbvc) { HKEY hDeviceKey; HKEY hKey; DWORD dwDisposition; DWORD dwSize; char szKey[MAX_PATH + MAX_VERSION + 2];
// Check input params
if (!szDeviceName) { ERRORMESSAGE(("vcmDevCapsWriteToReg: Specified pointer is invalid, szDeviceName=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if (szDeviceName[0] == '\0') { ERRORMESSAGE(("vcmDevCapsWriteToReg: Video capture input device driver name is empty\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if (!pvc) { ERRORMESSAGE(("vcmDevCapsWriteToReg: Specified pointer is invalid, pvc=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if (!cbvc) { ERRORMESSAGE(("vcmDevCapsWriteToReg: Specified structure size is invalid, cbvc=0\r\n")); return ((MMRESULT)MMSYSERR_NOERROR); }
// Open the main capture devices key, or create it if it doesn't exist
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, szRegDeviceKey, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hDeviceKey, &dwDisposition) != ERROR_SUCCESS) return ((MMRESULT)VCMERR_NOREGENTRY);
//If we have version info use that to build the key name
if (szDeviceVersion && szDeviceVersion[0] != '\0') { wsprintf(szKey, "%s, %s", szDeviceName, szDeviceVersion); } else { wsprintf(szKey, "%s", szDeviceName); }
// Check if there already is a key for the current device
// Open the key for the current device, or create the key if it doesn't exist
if (RegCreateKeyEx(hDeviceKey, szKey, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, &dwDisposition) != ERROR_SUCCESS) return ((MMRESULT)VCMERR_NOREGENTRY);
// Set the values in the key
dwSize = sizeof(DWORD); RegSetValueEx(hKey, (LPTSTR)szRegdwImageSizeKey, (DWORD)NULL, REG_DWORD, (LPBYTE)&pvc->dwImageSize, dwSize); dwSize = sizeof(DWORD); RegSetValueEx(hKey, (LPTSTR)szRegdwNumColorsKey, (DWORD)NULL, REG_DWORD, (LPBYTE)&pvc->dwNumColors, dwSize); dwSize = sizeof(DWORD); RegSetValueEx(hKey, (LPTSTR)szRegdwStreamingModeKey, (DWORD)NULL, REG_DWORD, (LPBYTE)&pvc->dwStreamingMode, dwSize); dwSize = sizeof(DWORD); RegSetValueEx(hKey, (LPTSTR)szRegdwDialogsKey, (DWORD)NULL, REG_DWORD, (LPBYTE)&pvc->dwDialogs, dwSize);
// Check dwNumColors to figure out if we need to set the palettes too
if (pvc->dwNumColors & VIDEO_FORMAT_NUM_COLORS_16) { dwSize = NUM_4BIT_ENTRIES * sizeof(RGBQUAD); RegSetValueEx(hKey, (LPTSTR)szRegbmi4bitColorsKey, (DWORD)NULL, REG_BINARY, (LPBYTE)&pvc->bmi4bitColors[0], dwSize); } if (pvc->dwNumColors & VIDEO_FORMAT_NUM_COLORS_256) { dwSize = NUM_8BIT_ENTRIES * sizeof(RGBQUAD); RegSetValueEx(hKey, (LPTSTR)szRegbmi8bitColorsKey, (DWORD)NULL, REG_BINARY, (LPBYTE)&pvc->bmi8bitColors[0], dwSize); }
// Close the keys
RegCloseKey(hKey); RegCloseKey(hDeviceKey);
return ((MMRESULT)MMSYSERR_NOERROR);
}
MMRESULT VCMAPI vcmDefaultFormatWriteToReg(LPSTR szDeviceName, LPSTR szDeviceVersion, LPBITMAPINFOHEADER lpbmih) { HKEY hDeviceKey; HKEY hKey; DWORD dwDisposition; DWORD dwSize; char szKey[MAX_PATH + MAX_VERSION + 2]; char szFOURCC[5];
// Check input params
if (!szDeviceName) { ERRORMESSAGE(("vcmDefaultFormatWriteToReg: Specified pointer is invalid, szDeviceName=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if (szDeviceName[0] == '\0') { ERRORMESSAGE(("vcmDefaultFormatWriteToReg: Video capture input device driver name is empty\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if (!lpbmih) { ERRORMESSAGE(("vcmDefaultFormatWriteToReg: Specified pointer is invalid, lpbmih=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); }
// Open the main capture devices key, or create it if it doesn't exist
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, szRegCaptureDefaultKey, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hDeviceKey, &dwDisposition) != ERROR_SUCCESS) return ((MMRESULT)VCMERR_NOREGENTRY);
//If we have version info use that to build the key name
if (szDeviceVersion && szDeviceVersion[0] != '\0') { wsprintf(szKey, "%s, %s", szDeviceName, szDeviceVersion); } else { wsprintf(szKey, "%s", szDeviceName); }
// Check if there already is a key for the current device
// Open the key for the current device, or create the key if it doesn't exist
if (RegCreateKeyEx(hDeviceKey, szKey, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, &dwDisposition) != ERROR_SUCCESS) return ((MMRESULT)VCMERR_NOREGENTRY);
if (lpbmih->biCompression == BI_RGB) wsprintf(szFOURCC, "RGB"); else { *((DWORD*)&szFOURCC) = lpbmih->biCompression; szFOURCC[4] = '\0'; }
dwSize = wsprintf(szKey, "%s, %dx%dx%d", szFOURCC, lpbmih->biWidth, lpbmih->biHeight, lpbmih->biBitCount); RegSetValueEx(hKey, (LPTSTR)szRegDefaultFormatKey, (DWORD)NULL, REG_SZ, (CONST BYTE *)szKey, dwSize+1);
// Close the keys
RegCloseKey(hKey); RegCloseKey(hDeviceKey);
return ((MMRESULT)MMSYSERR_NOERROR); }
/*****************************************************************************
* @doc EXTERNAL DEVCAPSFUNC * * @func MMRESULT | vcmGetDevCapsPreferredFormatTag | This function queries a specified * video capture input device to determine the format tag it will be effectively * capturing at. * * @parm UINT | uDevice | Specifies the video capture input device ID. * * @parm PINT | pbiWidth | Specifies a pointer to the actual width * the capture will be performed at. * * @parm PINT | pbiHeight | Specifies a pointer to the actual height * the capture will be performed at. * * @rdesc The return value is zero if the function is successful. Otherwise, it returns * an error number. Possible error values include the following: * @flag MMSYSERR_INVALPARAM | Specified pointer to structure is invalid. * @flag MMSYSERR_BADDEVICEID | Specified device device ID is invalid. * @flag VCMERR_NONSPECIFIC | The capture driver failed to provide valid information. * * @xref <f vcmGetDevCaps> ****************************************************************************/ MMRESULT VCMAPI vcmGetDevCapsPreferredFormatTag(UINT uDevice, PDWORD pdwFormatTag) { MMRESULT mmr = (MMRESULT)MMSYSERR_NOERROR; VIDEOINCAPS vic; int i;
// Check input params
if (!pdwFormatTag) { ERRORMESSAGE(("vcmGetDevCapsPreferredFormatTag: Specified pointer is invalid, pdwFormatTag=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if ((uDevice >= MAXVIDEODRIVERS) && (uDevice != VIDEO_MAPPER)) { ERRORMESSAGE(("vcmGetDevCapsPreferredFormatTag: Specified capture device ID is invalid, uDevice=%ld (expected values are 0x%lX or between 0 and %ld)\r\n", uDevice, VIDEO_MAPPER, MAXVIDEODRIVERS-1)); return ((MMRESULT)MMSYSERR_BADDEVICEID); }
// Get the capabilities of the capture hardware
if ((mmr = vcmGetDevCaps(uDevice, &vic, sizeof(VIDEOINCAPS))) != MMSYSERR_NOERROR) return (mmr);
// WE prefer to use I420 or IYUV, YVU9, YUY2, UYVY, RGB16, RGB24, RGB4, RGB8 in that order.
for (i=0; i<NUM_BITDEPTH_ENTRIES; i++) if (g_aiNumColors[i] & vic.dwNumColors) break; if (i == NUM_BITDEPTH_ENTRIES) return ((MMRESULT)VCMERR_NONSPECIFIC); else *pdwFormatTag = g_aiFourCCCode[i];
return ((MMRESULT)MMSYSERR_NOERROR);
}
/*****************************************************************************
* @doc EXTERNAL DEVCAPSFUNC * * @func MMRESULT | vcmGetDevCapsStreamingMode | This function queries a specified * video capture input device to determine its preferred streaming mode. * * @parm UINT | uDevice | Specifies the video capture input device ID. * * @parm PDWORD | pdwStreamingMode | Specifies a pointer to the preferred streaming mode. * * @rdesc The return value is zero if the function is successful. Otherwise, it returns * an error number. Possible error values include the following: * @flag MMSYSERR_INVALPARAM | Specified pointer to structure is invalid. * @flag MMSYSERR_BADDEVICEID | Specified device device ID is invalid. * @flag VCMERR_NONSPECIFIC | The capture driver failed to provide valid information. * * @xref <f vcmGetDevCaps> ****************************************************************************/ MMRESULT VCMAPI vcmGetDevCapsStreamingMode(UINT uDevice, PDWORD pdwStreamingMode) { MMRESULT mmr = (MMRESULT)MMSYSERR_NOERROR; VIDEOINCAPS vic;
// Check input params
if (!pdwStreamingMode) { ERRORMESSAGE(("vcmGetDevCapsStreamingMode: Specified pointer is invalid, pdwStreamingMode=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if ((uDevice >= MAXVIDEODRIVERS) && (uDevice != VIDEO_MAPPER)) { ERRORMESSAGE(("vcmGetDevCapsStreamingMode: Specified capture device ID is invalid, uDevice=%ld (expected values are 0x%lX or between 0 and %ld)\r\n", uDevice, VIDEO_MAPPER, MAXVIDEODRIVERS-1)); return ((MMRESULT)MMSYSERR_BADDEVICEID); }
// Get the capabilities of the capture hardware
if ((mmr = vcmGetDevCaps(uDevice, &vic, sizeof(VIDEOINCAPS))) != MMSYSERR_NOERROR) return (mmr);
// Get the streaming mode.
*pdwStreamingMode = vic.dwStreamingMode;
return ((MMRESULT)MMSYSERR_NOERROR);
}
/*****************************************************************************
* @doc EXTERNAL DEVCAPSFUNC * * @func MMRESULT | vcmGetDevCapsDialogs | This function queries a specified * video capture input device to determine if its dialog and source format * its should be exposed. * * @parm UINT | uDevice | Specifies the video capture input device ID. * * @parm PDWORD | pdwDialogs | Specifies a pointer to the dialogs to be exposed. * * @rdesc The return value is zero if the function is successful. Otherwise, it returns * an error number. Possible error values include the following: * @flag MMSYSERR_INVALPARAM | Specified pointer to structure is invalid. * @flag MMSYSERR_BADDEVICEID | Specified device device ID is invalid. * @flag VCMERR_NONSPECIFIC | The capture driver failed to provide valid information. * * @xref <f vcmGetDevCaps> ****************************************************************************/ MMRESULT VCMAPI vcmGetDevCapsDialogs(UINT uDevice, PDWORD pdwDialogs) { MMRESULT mmr = (MMRESULT)MMSYSERR_NOERROR; VIDEOINCAPS vic;
// Check input params
if (!pdwDialogs) { ERRORMESSAGE(("vcmGetDevCapsDialogs: Specified pointer is invalid, pdwDialogs=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if ((uDevice >= MAXVIDEODRIVERS) && (uDevice != VIDEO_MAPPER)) { ERRORMESSAGE(("vcmGetDevCapsDialogs: Specified capture device ID is invalid, uDevice=%ld (expected values are 0x%lX or between 0 and %ld)\r\n", uDevice, VIDEO_MAPPER, MAXVIDEODRIVERS-1)); return ((MMRESULT)MMSYSERR_BADDEVICEID); }
// Get the capabilities of the capture hardware
if ((mmr = vcmGetDevCaps(uDevice, &vic, sizeof(VIDEOINCAPS))) != MMSYSERR_NOERROR) return (mmr);
// Get the streaming mode.
*pdwDialogs = vic.dwDialogs;
return ((MMRESULT)MMSYSERR_NOERROR);
}
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmStreamSetBrightness | This function sends a user-defined * message to a given Video Compression Manager (VCM) stream instance to set * the brightness of the decompressed images. The brightness is a value defined * between 0 and 255. The brightness can also be reset by passing a value equal * to VCM_RESET_BRIGHTNESS. * * @parm HVCMSTREAM | hvs | Specifies the conversion stream. * * @parm DWORD | dwBrightness | Specifies the value of the brightness requested. * * @rdesc The return value is zero if the function is successful. Otherwise, it returns * an error number. Possible error values include the following: * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * @flag MMSYSERR_INVALPARAM | Specified brightness value is invalid. * @flag MMSYSERR_NOTSUPPORTED | The VCM driver cannot set the brightness. * * @xref <f vcmStreamMessage> ***************************************************************************/ MMRESULT VCMAPI vcmStreamSetBrightness(HVCMSTREAM hvs, DWORD dwBrightness) { PVCMSTREAM pvs = (PVCMSTREAM)hvs;
// Check input params
if (!hvs) { ERRORMESSAGE(("vcmStreamSetBrightness: Specified HVCMSTREAM handle is invalid, hvs=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALHANDLE); } if ((dwBrightness != VCM_RESET_BRIGHTNESS) && ((dwBrightness > VCM_MAX_BRIGHTNESS) || (dwBrightness < VCM_MIN_BRIGHTNESS))) { ERRORMESSAGE(("vcmStreamSetBrightness: Specified brightness value is invalid, dwBrightness=%ld (expected value is between %ld and %ld)\r\n", dwBrightness, VCM_MIN_BRIGHTNESS, VCM_MAX_BRIGHTNESS)); return ((MMRESULT)MMSYSERR_INVALPARAM); }
// Only our (intel h.263) codec supports this. If the codec used is different,
// that's Ok: no need to return an error.
#if !defined(_ALPHA_) && defined(USE_BILINEAR_MSH26X)
if (pvs->pvfxSrc && ((pvs->pvfxSrc->dwFormatTag == VIDEO_FORMAT_MSH263) || (pvs->pvfxSrc->dwFormatTag == VIDEO_FORMAT_MSH26X))) #else
if (pvs->pvfxSrc && (pvs->pvfxSrc->dwFormatTag == VIDEO_FORMAT_MSH263)) #endif
vcmStreamMessage(hvs, PLAYBACK_CUSTOM_CHANGE_BRIGHTNESS, (dwBrightness != VCM_RESET_BRIGHTNESS) ? (LPARAM)dwBrightness : (LPARAM)VCM_DEFAULT_BRIGHTNESS, (LPARAM)0);
return ((MMRESULT)MMSYSERR_NOERROR); }
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmStreamSetContrast | This function sends a user-defined * message to a given Video Compression Manager (VCM) stream instance to set * the contrast of the decompressed images. The contrast is a value defined * between 0 and 255. The contrast can also be reset by passing a value equal * to VCM_RESET_CONTRAST. * * @parm HVCMSTREAM | hvs | Specifies the conversion stream. * * @parm DWORD | dwContrast | Specifies the value of the contrast requested. * * @rdesc The return value is zero if the function is successful. Otherwise, it returns * an error number. Possible error values include the following: * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * @flag MMSYSERR_INVALPARAM | Specified contrast value is invalid. * @flag MMSYSERR_NOTSUPPORTED | The VCM driver cannot set the contrast. * * @xref <f vcmStreamMessage> ***************************************************************************/ MMRESULT VCMAPI vcmStreamSetContrast(HVCMSTREAM hvs, DWORD dwContrast) { PVCMSTREAM pvs = (PVCMSTREAM)hvs;
// Check input params
if (!hvs) { ERRORMESSAGE(("vcmStreamSetContrast: Specified handle is invalid, hvs=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALHANDLE); } if ((dwContrast != VCM_RESET_CONTRAST) && ((dwContrast > VCM_MAX_CONTRAST) || (dwContrast < VCM_MIN_CONTRAST))) { ERRORMESSAGE(("vcmStreamSetContrast: Specified contrast value is invalid, dwContrast=%ld (expected value is between %ld and %ld)\r\n", dwContrast, VCM_MIN_CONTRAST, VCM_MAX_CONTRAST)); return ((MMRESULT)MMSYSERR_INVALPARAM); }
// Only our (intel ) codec supports this. If the codec used is different,
// that's Ok: no need to return an error.
#if !defined(_ALPHA_) && defined(USE_BILINEAR_MSH26X)
if (pvs->pvfxSrc && ((pvs->pvfxSrc->dwFormatTag == VIDEO_FORMAT_MSH263) || (pvs->pvfxSrc->dwFormatTag == VIDEO_FORMAT_MSH26X))) #else
if (pvs->pvfxSrc && (pvs->pvfxSrc->dwFormatTag == VIDEO_FORMAT_MSH263)) #endif
vcmStreamMessage(hvs, PLAYBACK_CUSTOM_CHANGE_CONTRAST, (dwContrast != VCM_RESET_CONTRAST) ? (LPARAM)dwContrast : (LPARAM)VCM_DEFAULT_CONTRAST, (LPARAM)0);
return ((MMRESULT)MMSYSERR_NOERROR); }
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmStreamSetSaturation | This function sends a user-defined * message to a given Video Compression Manager (VCM) stream instance to set * the saturation of the decompressed images. The saturation is a value defined * between 0 and 255. The saturation can also be reset by passing a value equal * to VCM_RESET_SATURATION. * * @parm HVCMSTREAM | hvs | Specifies the conversion stream. * * @parm DWORD | dwSaturation | Specifies the value of the saturation requested. * * @rdesc The return value is zero if the function is successful. Otherwise, it returns * an error number. Possible error values include the following: * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * @flag MMSYSERR_INVALPARAM | Specified saturation value is invalid. * @flag MMSYSERR_NOTSUPPORTED | The VCM driver cannot set the saturation. * * @xref <f vcmStreamMessage> ***************************************************************************/ MMRESULT VCMAPI vcmStreamSetSaturation(HVCMSTREAM hvs, DWORD dwSaturation) { PVCMSTREAM pvs = (PVCMSTREAM)hvs;
// Check input params
if (!hvs) { ERRORMESSAGE(("vcmStreamSetSaturation: Specified handle is invalid, hvs=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALHANDLE); } if ((dwSaturation != VCM_RESET_SATURATION) && ((dwSaturation > VCM_MAX_SATURATION) || (dwSaturation < VCM_MIN_SATURATION))) { ERRORMESSAGE(("vcmStreamSetSaturation: Specified saturation value is invalid, dwSaturation=%ld (expected value is between %ld and %ld)\r\n", dwSaturation, VCM_MIN_SATURATION, VCM_MAX_SATURATION)); return ((MMRESULT)MMSYSERR_INVALPARAM); }
// Only our (H.263 intel) codec supports this. If the codec used is different,
// that's Ok: no need to return an error.
#if !defined(_ALPHA_) && defined(USE_BILINEAR_MSH26X)
if (pvs->pvfxSrc && ((pvs->pvfxSrc->dwFormatTag == VIDEO_FORMAT_MSH263) || (pvs->pvfxSrc->dwFormatTag == VIDEO_FORMAT_MSH26X))) #else
if (pvs->pvfxSrc && (pvs->pvfxSrc->dwFormatTag == VIDEO_FORMAT_MSH263)) #endif
vcmStreamMessage(hvs, PLAYBACK_CUSTOM_CHANGE_SATURATION, (dwSaturation != VCM_RESET_SATURATION) ? (LPARAM)dwSaturation : (LPARAM)VCM_DEFAULT_SATURATION, (LPARAM)0);
return ((MMRESULT)MMSYSERR_NOERROR); }
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmStreamIsPostProcessingSupported | This function is used to find * out if the decompressor can post-process the decompressed image to, for * instance, modify its brightness, contrast or saturation. * * @parm HVCMSTREAM | hvs | Specifies the conversion stream. * * @rdesc The return value is TRUE if the decompressor supports post-processing. Otherwise, it returns FALSE. * * @xref <f vcmStreamMessage> ***************************************************************************/ BOOL VCMAPI vcmStreamIsPostProcessingSupported(HVCMSTREAM hvs) { // Check input params
if (!hvs) return (FALSE);
// Put the code that checks this property right here!!!
return (FALSE); }
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmStreamSetImageQuality | This function sends the image * quality compression parameter. * * @parm HVCMSTREAM | hvs | Specifies the conversion stream. * * @parm DWORD | dwImageQuality | Specifies an image quality value (between 0 * and 31. The lower number indicates a high spatial quality at a low frame * rate, the larger number indiocates a low spatial quality at a high frame * rate. * * @rdesc The return value is zero if the function is successful. Otherwise, it returns * an error number. Possible error values include the following: * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * @flag MMSYSERR_INVALPARAM | Specified image quality value is invalid. * @flag MMSYSERR_NOTSUPPORTED | The VCM driver cannot set the compression ratio. * * @xref <f vcmStreamMessage> ***************************************************************************/ MMRESULT VCMAPI vcmStreamSetImageQuality(HVCMSTREAM hvs, DWORD dwImageQuality) { PVCMSTREAM pvs = (PVCMSTREAM)hvs; #ifdef USE_MPEG4_SCRUNCH
PVOID pvState; DWORD dw; PMPEG4COMPINSTINFO pciMPEG4Info; #endif
#ifdef LOG_COMPRESSION_PARAMS
char szDebug[100]; #endif
// Check input param
if (!hvs) { ERRORMESSAGE(("vcmStreamSetImageQuality: Specified handle is invalid, hvs=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALHANDLE); } // Set to default value if out or range
if ((dwImageQuality > VCM_MIN_IMAGE_QUALITY)) { pvs->dwQuality = VCM_DEFAULT_IMAGE_QUALITY; ERRORMESSAGE(("vcmStreamSetImageQuality: Specified image quality value is invalid, dwImageQuality=%ld (expected value is between %ld and %ld)\r\n", dwImageQuality, VCM_MAX_IMAGE_QUALITY, VCM_MIN_IMAGE_QUALITY)); return ((MMRESULT)MMSYSERR_INVALPARAM); }
// Put the code that sets this property right here!!!
pvs->dwQuality = dwImageQuality;
#ifdef USE_MPEG4_SCRUNCH
// Get the state of the compressor
if (dw = ICGetStateSize((HIC)pvs->hIC)) { if (pvState = (PVOID)MemAlloc(dw)) { if (((DWORD) ICGetState((HIC)pvs->hIC, pvState, dw)) == dw) { pciMPEG4Info = (PMPEG4COMPINSTINFO)pvState;
// Configure the codec for compression
pciMPEG4Info->lMagic = MPG4_STATE_MAGIC; pciMPEG4Info->dDataRate = 20; pciMPEG4Info->lCrisp = dwImageQuality * 3; pciMPEG4Info->lKeydist = 30; pciMPEG4Info->lPScale = MPG4_PSEUDO_SCALE; pciMPEG4Info->lTotalWindowMs = MPG4_TOTAL_WINDOW_DEFAULT; pciMPEG4Info->lVideoWindowMs = MPG4_VIDEO_WINDOW_DEFAULT; pciMPEG4Info->lFramesInfoValid = FALSE; pciMPEG4Info->lBFrameOn = MPG4_B_FRAME_ON; pciMPEG4Info->lLiveEncode = MPG4_LIVE_ENCODE;
ICSetState((HIC)pvs->hIC, (PVOID)pciMPEG4Info, dw);
// Get rid of the state structure
MemFree((HANDLE)pvState); } } } #endif
#ifdef LOG_COMPRESSION_PARAMS
wsprintf(szDebug, "New image quality: %ld\r\n", dwImageQuality); OutputDebugString(szDebug); #endif
return ((MMRESULT)MMSYSERR_NOERROR); }
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmStreamSetMaxPacketSize | This function sets the maximum * video packet size. * * @parm HVCMSTREAM | hvs | Specifies the conversion stream. * * @parm DWORD | dwMaxPacketSize | Specifies the maximum packet size. * * @rdesc The return value is zero if the function is successful. Otherwise, it returns * an error number. Possible error values include the following: * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * @flag MMSYSERR_INVALPARAM | Specified image quality value is invalid. * @flag MMSYSERR_NOTSUPPORTED | The VCM driver cannot set the size. * * @xref <f vcmStreamMessage> ***************************************************************************/ MMRESULT VCMAPI vcmStreamSetMaxPacketSize(HVCMSTREAM hvs, DWORD dwMaxPacketSize) { PVCMSTREAM pvs = (PVCMSTREAM)hvs;
// Check input params
if (!hvs) { ERRORMESSAGE(("vcmStreamSetMaxPacketSize: Specified handle is invalid, hvs=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALHANDLE); } if ((dwMaxPacketSize != VCM_RESET_PACKET_SIZE) && ((dwMaxPacketSize > VCM_MAX_PACKET_SIZE) || (dwMaxPacketSize < VCM_MIN_PACKET_SIZE))) { ERRORMESSAGE(("vcmStreamSetMaxPacketSize: Specified max packet size value is invalid, dwMaxPacketSize=%ld (expected value is between %ld and %ld)\r\n", dwMaxPacketSize, VCM_MIN_PACKET_SIZE, VCM_MAX_PACKET_SIZE)); return ((MMRESULT)MMSYSERR_INVALPARAM); }
// Only our (H.26x intel) codecs supports this. If the codec used is different,
// just return an 'unsupported' error.
#if !defined(_ALPHA_) && defined(USE_BILINEAR_MSH26X)
if (pvs->pvfxDst && ((pvs->pvfxDst->dwFormatTag == VIDEO_FORMAT_MSH263) || (pvs->pvfxDst->dwFormatTag == VIDEO_FORMAT_MSH261) || (pvs->pvfxDst->dwFormatTag == VIDEO_FORMAT_MSH26X))) #else
if (pvs->pvfxDst && ((pvs->pvfxDst->dwFormatTag == VIDEO_FORMAT_MSH263) || (pvs->pvfxDst->dwFormatTag == VIDEO_FORMAT_MSH261))) #endif
{ if (dwMaxPacketSize != VCM_RESET_PACKET_SIZE) pvs->dwMaxPacketSize = dwMaxPacketSize; else pvs->dwMaxPacketSize = VCM_DEFAULT_PACKET_SIZE; vcmStreamMessage(hvs, CODEC_CUSTOM_ENCODER_CONTROL, MAKELONG(EC_PACKET_SIZE,EC_SET_CURRENT), (LPARAM)pvs->dwMaxPacketSize); } else return ((MMRESULT)MMSYSERR_NOTSUPPORTED);
return ((MMRESULT)MMSYSERR_NOERROR); }
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmStreamSetTargetRates | This function sets the target * bitrate and frame rate to be used in the estimation of the target frame * size at compression time. * * @parm HVCMSTREAM | hvs | Specifies the conversion stream. * * @parm DWORD | dwTargetFrameRate | Specifies a target frame rate value. * * @parm DWORD | dwTargetByterate | Specifies a target byterate value. * * @rdesc The return value is zero if the function is successful. Otherwise, * it returns an error number. Possible error values include the following: * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * @flag MMSYSERR_INVALPARAM | Specified target frame rate value is * invalid. * @flag MMSYSERR_NOTSUPPORTED | The VCM driver cannot set the compression * ratio. * * @xref <f vcmStreamMessage> ***************************************************************************/ MMRESULT VCMAPI vcmStreamSetTargetRates(HVCMSTREAM hvs, DWORD dwTargetFrameRate, DWORD dwTargetByterate) { FX_ENTRY("vcmStreamSetTargetRates");
// IP + UDP + RTP + payload mode C header - worst case
#define TRANSPORT_HEADER_SIZE (20 + 8 + 12 + 12)
PVCMSTREAM pvs = (PVCMSTREAM)hvs; ICCOMPRESSFRAMES iccf = {0};
ASSERT(hvs && ((dwTargetFrameRate == VCM_RESET_FRAME_RATE) || ((dwTargetFrameRate <= VCM_MAX_FRAME_RATE) && (dwTargetFrameRate >= VCM_MIN_FRAME_RATE))) && ((dwTargetByterate == VCM_RESET_BYTE_RATE) || ((dwTargetByterate <= VCM_MAX_BYTE_RATE) && (dwTargetByterate >= VCM_MIN_BYTE_RATE))));
// Check input params
if (!hvs) { ERRORMESSAGE(("%s: Specified handle is invalid, hvs=NULL\r\n", _fx_)); return ((MMRESULT)MMSYSERR_INVALHANDLE); } if ((dwTargetFrameRate != VCM_RESET_FRAME_RATE) && (dwTargetFrameRate > VCM_MAX_FRAME_RATE) && (dwTargetFrameRate < VCM_MIN_FRAME_RATE)) { ERRORMESSAGE(("%s: Specified target frame rate value is invalid, dwTargetFrameRate=%ld (expected value is between %ld and %ld)\r\n", _fx_, dwTargetFrameRate, VCM_MIN_FRAME_RATE, VCM_MAX_FRAME_RATE)); return ((MMRESULT)MMSYSERR_INVALPARAM); } if ((dwTargetByterate != VCM_RESET_BYTE_RATE) && (dwTargetByterate > VCM_MAX_BYTE_RATE) && (dwTargetByterate < VCM_MIN_BYTE_RATE)) { ERRORMESSAGE(("%s: Specified target bitrate value is invalid, dwTargetBitrate=%ld bps (expected value is between %ld and %ld bps)\r\n", _fx_, dwTargetByterate << 3, VCM_MIN_BYTE_RATE << 3, VCM_MAX_BYTE_RATE << 3)); return ((MMRESULT)MMSYSERR_INVALPARAM); }
// Don't change the state of the codec while it's compressing a frame
EnterCriticalSection(&pvs->crsFrameNumber);
// Set the new rates on the codec
iccf.lQuality = 10000UL - (pvs->dwQuality * 322UL); if (pvs->dwMaxPacketSize) iccf.lDataRate = pvs->dwTargetByterate = dwTargetByterate - (dwTargetByterate / pvs->dwMaxPacketSize + 1) * TRANSPORT_HEADER_SIZE; else iccf.lDataRate = pvs->dwTargetByterate = dwTargetByterate; iccf.lKeyRate = LONG_MAX; iccf.dwRate = 1000UL; pvs->dwTargetFrameRate = dwTargetFrameRate; iccf.dwScale = iccf.dwRate * 100UL / dwTargetFrameRate; if (ICSendMessage((HIC)(HVCMDRIVERID)pvs->hIC, ICM_COMPRESS_FRAMES_INFO, (DWORD_PTR)&iccf, sizeof(iccf)) != ICERR_OK) { LeaveCriticalSection(&pvs->crsFrameNumber);
ERRORMESSAGE(("%s: Codec failed to handle ICM_COMPRESS_FRAMES_INFO message correctly\r\n", _fx_));
return ((MMRESULT)VCMERR_FAILED); }
LeaveCriticalSection(&pvs->crsFrameNumber);
DEBUGMSG(ZONE_VCM, ("%s: New targets:\r\n Frame rate: %ld.%ld fps\r\n Bitrate (minus network overhead): %ld bps\r\n Frame size: %ld bits\r\n", _fx_, pvs->dwTargetFrameRate / 100UL, (DWORD)(pvs->dwTargetFrameRate - (DWORD)(pvs->dwTargetFrameRate / 100UL) * 100UL), pvs->dwTargetByterate << 3, (pvs->dwTargetByterate << 3) * 100UL / pvs->dwTargetFrameRate));
return ((MMRESULT)MMSYSERR_NOERROR); }
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmStreamRestorePayload | This function takes a list of video * packets and recreates the video payload of a complete frame from these. * * @parm HVCMSTREAM | hvs | Specifies the conversion stream. * * @parm WSABUF* | ppDataPkt | Specifies a pointer to the list of video packets. * * @parm DWORD | dwPktCount | Specifies the number of packets in the list. * * @parm PBYTE | pbyFrame | Specifies a pointer to the reconstructed video data. * * @parm DWORD* | pdwFrameSize | Specifies a pointer to the size of reconstructed video data. * * @parm BOOL* | pfReceivedKeyframe | Specifies a pointer to receive the type (I or P) of a frame. * * @rdesc The return value is zero if the function is successful. Otherwise, it returns * an error number. Possible error values include the following: * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * @flag MMSYSERR_INVALPARAM | Specified data pointer is invalid. * * @comm The <p pdwFrameSize> parameter should be initialized to the maximum frame * size, before calling the <f vcmStreamRestorePayload> function. * * @xref <f vcmStreamFormatPayload> ***************************************************************************/ MMRESULT VCMAPI vcmStreamRestorePayload(HVCMSTREAM hvs, WSABUF *ppDataPkt, DWORD dwPktCount, PBYTE pbyFrame, PDWORD pdwFrameSize, BOOL *pfReceivedKeyframe) { PVCMSTREAM pvs = (PVCMSTREAM)hvs; DWORD dwHeaderSize = 0UL; DWORD dwPSCBytes = 0UL; DWORD dwMaxFrameSize; #ifdef DEBUG
char szTDebug[256]; #endif
#ifdef LOGPAYLOAD_ON
PBYTE p = pbyFrame; HANDLE g_TDebugFile; DWORD d, GOBn; long j = (long)(BYTE)ppDataPkt->buf[3]; #endif
// Check input params
if (!hvs) { ERRORMESSAGE(("vcmStreamRestorePayload: Specified handle is invalid, hvs=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALHANDLE); } if (!ppDataPkt) { ERRORMESSAGE(("vcmStreamRestorePayload: Specified pointer is invalid, hvs=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if (!dwPktCount) { ERRORMESSAGE(("vcmStreamRestorePayload: Specified packet count is invalid, dwPktCount=0\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if (!pbyFrame) { ERRORMESSAGE(("vcmStreamRestorePayload: Specified pointer is invalid, pbyFrame=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if (!pdwFrameSize) { ERRORMESSAGE(("vcmStreamRestorePayload: Specified pointer is invalid, pdwFrameSize=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); }
// Save maximum payload size
dwMaxFrameSize = *pdwFrameSize;
// Initialize payload size
*pdwFrameSize = 0;
// Initialize default frame type
*pfReceivedKeyframe = FALSE;
// What is the type of this payload
#ifndef _ALPHA_
#ifdef USE_BILINEAR_MSH26X
if ((pvs->pvfxSrc->dwFormatTag == VIDEO_FORMAT_MSH263) || (pvs->pvfxSrc->dwFormatTag == VIDEO_FORMAT_MSH26X)) #else
if (pvs->pvfxSrc->dwFormatTag == VIDEO_FORMAT_MSH263) #endif
#else
if (pvs->pvfxSrc->dwFormatTag == VIDEO_FORMAT_DECH263) #endif
{ // Strip the header of each packet and copy the payload in the video buffer
while (dwPktCount--) { // Look for the first two bits to figure out what's the mode used.
// This will dictate the size of the header to be removed.
// Mode A is 4 bytes: first bit is set to 1,
// Mode B is 8 bytes: first bit is set to 0, second bit is set to 0,
// Mode C is 12 bytes: first bit is set to 0, second bit is set to 1.
dwHeaderSize = ((ppDataPkt->buf[0] & 0x80) ? ((ppDataPkt->buf[0] & 0x40) ? 12 : 8) : 4);
// Look at the payload header to figure out if the frame is a keyframe
*pfReceivedKeyframe |= (BOOL)(ppDataPkt->buf[2] & 0x80);
#ifdef LOGPAYLOAD_ON
// Output some debug stuff
if (dwHeaderSize == 4) { GOBn = (DWORD)((BYTE)ppDataPkt->buf[4]) << 24 | (DWORD)((BYTE)ppDataPkt->buf[5]) << 16 | (DWORD)((BYTE)ppDataPkt->buf[6]) << 8 | (DWORD)((BYTE)ppDataPkt->buf[7]); GOBn >>= (DWORD)(10 - (DWORD)((ppDataPkt->buf[0] & 0x38) >> 3)); GOBn &= 0x0000001F; wsprintf(szTDebug, "Header content: Frame %3ld, GOB %0ld\r\n", (DWORD)(ppDataPkt->buf[3]), GOBn); OutputDebugString(szTDebug); wsprintf(szTDebug, (ppDataPkt->buf[0] & 0x80) ? " F: '1' => Mode B or C\r\n" : " F: '0' => Mode A\r\n"); OutputDebugString(szTDebug); wsprintf(szTDebug, (ppDataPkt->buf[0] & 0x40) ? " P: '1' => PB-frame\r\n" : " P: '0' => I or P frame\r\n"); OutputDebugString(szTDebug); wsprintf(szTDebug, " SBIT: %01ld\r\n", (DWORD)((ppDataPkt->buf[0] & 0x38) >> 3)); OutputDebugString(szTDebug); wsprintf(szTDebug, " EBIT: %01ld\r\n", (DWORD)(ppDataPkt->buf[0] & 0x07)); OutputDebugString(szTDebug); switch ((DWORD)(ppDataPkt->buf[1] >> 5)) { case 0: wsprintf(szTDebug, " SRC: '000' => Source format forbidden!\r\n"); break; case 1: wsprintf(szTDebug, " SRC: '001' => Source format sub-QCIF\r\n"); break; case 2: wsprintf(szTDebug, " SRC: '010' => Source format QCIF\r\n"); break; case 3: wsprintf(szTDebug, " SRC: '011' => Source format CIF\r\n"); break; case 4: wsprintf(szTDebug, " SRC: '100' => Source format 4CIF\r\n"); break; case 5: wsprintf(szTDebug, " SRC: '101' => Source format 16CIF\r\n"); break; case 6: wsprintf(szTDebug, " SRC: '110' => Source format reserved\r\n"); break; case 7: wsprintf(szTDebug, " SRC: '111' => Source format reserved\r\n"); break; default: wsprintf(szTDebug, " SRC: %ld => Source format unknown!\r\n", (DWORD)(ppDataPkt->buf[1] >> 5)); break; } OutputDebugString(szTDebug); wsprintf(szTDebug, " R: %02ld => Reserved, must be 0\r\n", (DWORD)((ppDataPkt->buf[1] & 0x1F) >> 5)); OutputDebugString(szTDebug); wsprintf(szTDebug, (ppDataPkt->buf[2] & 0x80) ? " I: '1' => Intra-coded\r\n" : " I: '0' => Not Intra-coded\r\n"); OutputDebugString(szTDebug); wsprintf(szTDebug, (ppDataPkt->buf[2] & 0x40) ? " A: '1' => Optional Advanced Prediction mode ON\r\n" : " A: '0' => Optional Advanced Prediction mode OFF\r\n"); OutputDebugString(szTDebug); wsprintf(szTDebug, (ppDataPkt->buf[2] & 0x20) ? " S: '1' => Optional Syntax-based Arithmetic Code mode ON\r\n" : " S: '0' => Optional Syntax-based Arithmetic Code mode OFF\r\n"); OutputDebugString(szTDebug); wsprintf(szTDebug, " DBQ: %01ld => Should be 0\r\n", (DWORD)((ppDataPkt->buf[2] & 0x18) >> 3)); OutputDebugString(szTDebug); wsprintf(szTDebug, " TRB: %01ld => Should be 0\r\n", (DWORD)(ppDataPkt->buf[2] & 0x07)); OutputDebugString(szTDebug); wsprintf(szTDebug, " TR: %03ld\r\n", (DWORD)(ppDataPkt->buf[3])); OutputDebugString(szTDebug); wsprintf(szTDebug, "Header: %02lX %02lX %02lX %02lX\r\n", (BYTE)ppDataPkt->buf[0], (BYTE)ppDataPkt->buf[1], (BYTE)ppDataPkt->buf[2], (BYTE)ppDataPkt->buf[3]); OutputDebugString(szTDebug); wsprintf(szTDebug, "dword1: %02lX %02lX %02lX %02lX\r\n", (BYTE)ppDataPkt->buf[4], (BYTE)ppDataPkt->buf[5], (BYTE)ppDataPkt->buf[6], (BYTE)ppDataPkt->buf[7]); OutputDebugString(szTDebug); wsprintf(szTDebug, "dword2: %02lX %02lX %02lX %02lX\r\n", (BYTE)ppDataPkt->buf[8], (BYTE)ppDataPkt->buf[9], (BYTE)ppDataPkt->buf[10], (BYTE)ppDataPkt->buf[11]); OutputDebugString(szTDebug); } else if (dwHeaderSize == 8) { wsprintf(szTDebug, "Header content:\r\n"); OutputDebugString(szTDebug); wsprintf(szTDebug, (ppDataPkt->buf[0] & 0x80) ? " F: '1' => Mode B or C\r\n" : " F: '0' => Mode A\r\n"); OutputDebugString(szTDebug); wsprintf(szTDebug, (ppDataPkt->buf[0] & 0x40) ? " P: '1' => PB-frame\r\n" : " P: '0' => I or P frame\r\n"); OutputDebugString(szTDebug); wsprintf(szTDebug, " SBIT: %01ld\r\n", (DWORD)((ppDataPkt->buf[0] & 0x38) >> 3)); OutputDebugString(szTDebug); wsprintf(szTDebug, " EBIT: %01ld\r\n", (DWORD)(ppDataPkt->buf[0] & 0x07)); OutputDebugString(szTDebug); switch ((DWORD)(ppDataPkt->buf[1] >> 5)) { case 0: wsprintf(szTDebug, " SRC: '000' => Source format forbidden!\r\n"); break; case 1: wsprintf(szTDebug, " SRC: '001' => Source format sub-QCIF\r\n"); break; case 2: wsprintf(szTDebug, " SRC: '010' => Source format QCIF\r\n"); break; case 3: wsprintf(szTDebug, " SRC: '011' => Source format CIF\r\n"); break; case 4: wsprintf(szTDebug, " SRC: '100' => Source format 4CIF\r\n"); break; case 5: wsprintf(szTDebug, " SRC: '101' => Source format 16CIF\r\n"); break; case 6: wsprintf(szTDebug, " SRC: '110' => Source format reserved\r\n"); break; case 7: wsprintf(szTDebug, " SRC: '111' => Source format reserved\r\n"); break; default: wsprintf(szTDebug, " SRC: %ld => Source format unknown!\r\n", (DWORD)(ppDataPkt->buf[1] >> 5)); break; } OutputDebugString(szTDebug); wsprintf(szTDebug, " QUANT: %02ld\r\n", (DWORD)((ppDataPkt->buf[1] & 0x1F) >> 5)); OutputDebugString(szTDebug); wsprintf(szTDebug, (ppDataPkt->buf[2] & 0x80) ? " I: '1' => Intra-coded\r\n" : " I: '0' => Not Intra-coded\r\n"); OutputDebugString(szTDebug); wsprintf(szTDebug, (ppDataPkt->buf[2] & 0x40) ? " A: '1' => Optional Advanced Prediction mode ON\r\n" : " A: '0' => Optional Advanced Prediction mode OFF\r\n"); OutputDebugString(szTDebug); wsprintf(szTDebug, (ppDataPkt->buf[2] & 0x20) ? " S: '1' => Optional Syntax-based Arithmetic Code mode ON\r\n" : " S: '0' => Optional Syntax-based Arithmetic Code mode OFF\r\n"); OutputDebugString(szTDebug); wsprintf(szTDebug, " GOBN: %03ld\r\n", (DWORD)(ppDataPkt->buf[2] & 0x1F)); OutputDebugString(szTDebug); wsprintf(szTDebug, " MBA: %03ld\r\n", (DWORD)(ppDataPkt->buf[3])); OutputDebugString(szTDebug); wsprintf(szTDebug, " HMV1: %03ld\r\n", (DWORD)(ppDataPkt->buf[7])); OutputDebugString(szTDebug); wsprintf(szTDebug, " VMV1: %03ld\r\n", (DWORD)(ppDataPkt->buf[6])); OutputDebugString(szTDebug); wsprintf(szTDebug, " HMV2: %03ld\r\n", (DWORD)(ppDataPkt->buf[5])); OutputDebugString(szTDebug); wsprintf(szTDebug, " VMV2: %03ld\r\n", (DWORD)(ppDataPkt->buf[4])); OutputDebugString(szTDebug); wsprintf(szTDebug, "Header: %02lX %02lX %02lX %02lX %02lX %02lX %02lX %02lX\r\n", (BYTE)ppDataPkt->buf[0], (BYTE)ppDataPkt->buf[1], (BYTE)ppDataPkt->buf[2], (BYTE)ppDataPkt->buf[3], (BYTE)ppDataPkt->buf[4], (BYTE)ppDataPkt->buf[5], (BYTE)ppDataPkt->buf[6], (BYTE)ppDataPkt->buf[7]); OutputDebugString(szTDebug); wsprintf(szTDebug, "dword1: %02lX %02lX %02lX %02lX\r\n", (BYTE)ppDataPkt->buf[8], (BYTE)ppDataPkt->buf[9], (BYTE)ppDataPkt->buf[10], (BYTE)ppDataPkt->buf[11]); OutputDebugString(szTDebug); } #endif
// The purpose of this code is to look for the presence of the
// Picture Start Code at the beginning of the frame. If it is
// not present, we should break in debug mode.
// Only look for PSC at the beginning of the frame
if (!*pdwFrameSize) { // The start of the frame may not be at a byte boundary. The SBIT field
// of the header ((BYTE)ppDataPkt->buf[0] & 0xE0) will tell us exactly where
// our frame starts. We then look for the PSC (0000 0000 0000 0000 1000 00 bits)
*((BYTE *)&dwPSCBytes + 3) = *(BYTE *)&(ppDataPkt->buf[dwHeaderSize]); *((BYTE *)&dwPSCBytes + 2) = *(BYTE *)&(ppDataPkt->buf[dwHeaderSize + 1]); *((BYTE *)&dwPSCBytes + 1) = *(BYTE *)&(ppDataPkt->buf[dwHeaderSize + 2]); *((BYTE *)&dwPSCBytes + 0) = *(BYTE *)&(ppDataPkt->buf[dwHeaderSize + 3]); dwPSCBytes <<= ((DWORD)((BYTE)ppDataPkt->buf[0] & 0x38) >> 3); if ((dwPSCBytes & 0xFFFFFC00) != 0x00008000) { #ifdef DEBUG
wsprintf(szTDebug, "VCMSTRM: The first packet to reassemble is missing a PSC!\r\n"); OutputDebugString(szTDebug); // DebugBreak();
#endif
return ((MMRESULT)VCMERR_PSCMISSING); } }
// The end of a buffer and the start of the next buffer could belong to the
// same byte. If this is the case, the first byte of the next buffer was already
// copied in the video data buffer, with the previous packet. It should not be copied
// twice. The SBIT field of the payload header allows us to figure out if this is the case.
if (*pdwFrameSize && (ppDataPkt->buf[0] & 0x38)) dwHeaderSize++;
#if 0
//
// THIS IS FOR EXPERIMENTATION ONLY !!!
//
// For I frames, ditch their middle GOB
if (((dwHeaderSize == 4) || (dwHeaderSize == 5)) && (GOBn == 8) && (ppDataPkt->buf[2] & 0x80)) { wsprintf(szTDebug, "Ditched GOB %2ld of I frame %3ld!\r\n", GOBn, (DWORD)(ppDataPkt->buf[3])); OutputDebugString(szTDebug); ppDataPkt++; } else if (((dwHeaderSize == 4) || (dwHeaderSize == 5)) && GOBn && !(ppDataPkt->buf[2] & 0x80)) { wsprintf(szTDebug, "Ditched all GOBs after GOB %2ld of P frame %3ld!\r\n", GOBn, (DWORD)(ppDataPkt->buf[3])); OutputDebugString(szTDebug); ppDataPkt++; } else #endif
// Verify that the source format has the same video resolution than the conversion stream
// Test for invalid packets that have a length below the size of the payload header
if ( (g_ITUSizes[(DWORD)(((BYTE)ppDataPkt->buf[1]) >> 5)].biWidth == pvs->pvfxSrc->bih.biWidth) && (g_ITUSizes[(DWORD)(((BYTE)ppDataPkt->buf[1]) >> 5)].biHeight == pvs->pvfxSrc->bih.biHeight) && (ppDataPkt->len >= dwHeaderSize) && ((*pdwFrameSize + ppDataPkt->len - dwHeaderSize) <= dwMaxFrameSize) ) { // Copy the payload
CopyMemory(pbyFrame + *pdwFrameSize, ppDataPkt->buf + dwHeaderSize, ppDataPkt->len - dwHeaderSize);
// Update the payload size and pointer to the input video packets
*pdwFrameSize += ppDataPkt->len - dwHeaderSize; } else { // The total size of the reassembled packet would be larger than the maximum allowed!!!
// Or the packet has a length less than the payload header size
// Dump the frame
#ifdef DEBUG
wsprintf(szTDebug, (ppDataPkt->len >= dwHeaderSize) ? "VCMSTRM: Cumulative size of the reassembled packets is too large: discarding frame!\r\n" : "VCMSTRM: Packet length is smaller than payload header size: discarding frame!\r\n"); OutputDebugString(szTDebug); #endif
return ((MMRESULT)VCMERR_NONSPECIFIC); } ppDataPkt++;
}
#ifdef LOGPAYLOAD_ON
g_TDebugFile = CreateFile("C:\\RecvLog.txt", GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); SetFilePointer(g_TDebugFile, 0, NULL, FILE_END); wsprintf(szTDebug, "Frame #%03ld\r\n", (DWORD)j); WriteFile(g_TDebugFile, szTDebug, strlen(szTDebug), &d, NULL); for (j=*pdwFrameSize; j>0; j-=4, p+=4) { wsprintf(szTDebug, "%02lX %02lX %02lX %02lX\r\n", *((BYTE *)p), *((BYTE *)p+1), *((BYTE *)p+2), *((BYTE *)p+3)); WriteFile(g_TDebugFile, szTDebug, strlen(szTDebug), &d, NULL); } CloseHandle(g_TDebugFile); #endif
} #ifndef _ALPHA_
else if (pvs->pvfxSrc->dwFormatTag == VIDEO_FORMAT_MSH261) #else
else if (pvs->pvfxSrc->dwFormatTag == VIDEO_FORMAT_DECH261) #endif
{ // Strip the header of each packet and copy the payload in the video buffer
while (dwPktCount--) {
#ifdef LOGPAYLOAD_ON
// wsprintf(szDebug, "Header: %02lX %02lX %02lX %02lX\r\ndword1: %02lX %02lX %02lX %02lX\r\ndword2: %02lX %02lX %02lX %02lX\r\n", ppDataPkt->buf[0], ppDataPkt->buf[1], ppDataPkt->buf[2], ppDataPkt->buf[3], ppDataPkt->buf[4], ppDataPkt->buf[5], ppDataPkt->buf[6], ppDataPkt->buf[7], ppDataPkt->buf[8], ppDataPkt->buf[9], ppDataPkt->buf[10], ppDataPkt->buf[11]);
wsprintf(szTDebug, "Header: %02lX %02lX %02lX %02lX\r\n", (BYTE)ppDataPkt->buf[0], (BYTE)ppDataPkt->buf[1], (BYTE)ppDataPkt->buf[2], (BYTE)ppDataPkt->buf[3]); OutputDebugString(szTDebug); wsprintf(szTDebug, "dword1: %02lX %02lX %02lX %02lX\r\n", (BYTE)ppDataPkt->buf[4], (BYTE)ppDataPkt->buf[5], (BYTE)ppDataPkt->buf[6], (BYTE)ppDataPkt->buf[7]); OutputDebugString(szTDebug); wsprintf(szTDebug, "dword2: %02lX %02lX %02lX %02lX\r\n", (BYTE)ppDataPkt->buf[8], (BYTE)ppDataPkt->buf[9], (BYTE)ppDataPkt->buf[10], (BYTE)ppDataPkt->buf[11]); OutputDebugString(szTDebug); #endif
// The H.261 payload header size is always 4 bytes long
dwHeaderSize = 4;
// Look at the payload header to figure out if the frame is a keyframe
*pfReceivedKeyframe |= (BOOL)(ppDataPkt->buf[0] & 0x02);
// The purpose of this code is to look for the presence of the
// Picture Start Code at the beginning of the frame. If it is
// not present, we should break in debug mode.
// Only look for PSC at the beginning of the frame
if (!*pdwFrameSize) { // The start of the frame may not be at a byte boundary. The SBIT field
// of the header ((BYTE)ppDataPkt->buf[0] & 0xE0) will tell us exactly where
// our frame starts. We then look for the PSC (0000 0000 0000 0001 0000 bits)
*((BYTE *)&dwPSCBytes + 3) = *(BYTE *)&(ppDataPkt->buf[dwHeaderSize]); *((BYTE *)&dwPSCBytes + 2) = *(BYTE *)&(ppDataPkt->buf[dwHeaderSize + 1]); *((BYTE *)&dwPSCBytes + 1) = *(BYTE *)&(ppDataPkt->buf[dwHeaderSize + 2]); *((BYTE *)&dwPSCBytes + 0) = *(BYTE *)&(ppDataPkt->buf[dwHeaderSize + 3]); dwPSCBytes <<= ((DWORD)((BYTE)ppDataPkt->buf[0] & 0xE0) >> 5); if ((dwPSCBytes & 0xFFFFF000) != 0x00010000) { #ifdef DEBUG
wsprintf(szTDebug, "VCMSTRM: The first packet to reassemble is missing a PSC!\r\n"); OutputDebugString(szTDebug); // DebugBreak();
#endif
return ((MMRESULT)VCMERR_PSCMISSING); } }
// The end of a buffer and the start of the next buffer could belong to the
// same byte. If this is the case, the first byte of the next buffer was already
// copied in the video data buffer, with the previous packet. It should not be copied
// twice. The SBIT field of the payload header allows us to figure out if this is the case.
if (*pdwFrameSize && (ppDataPkt->buf[0] & 0xE0)) dwHeaderSize++;
// Copy the payload
// Test for invalid packets that have a length below the size of the payload header
if ( (ppDataPkt->len >= dwHeaderSize) && ((*pdwFrameSize + ppDataPkt->len - dwHeaderSize) <= dwMaxFrameSize) ) { // Copy the payload
CopyMemory(pbyFrame + *pdwFrameSize, ppDataPkt->buf + dwHeaderSize, ppDataPkt->len - dwHeaderSize);
// Update the payload size and pointer to the input video packets
*pdwFrameSize += ppDataPkt->len - dwHeaderSize; ppDataPkt++; } else { // The total size of the reassembled packet would be larger than the maximum allowed!!!
// Or the packet has a length less than the payload header size
// Dump the frame
#ifdef DEBUG
wsprintf(szTDebug, (ppDataPkt->len >= dwHeaderSize) ? "VCMSTRM: Cumulative size of the reassembled packets is too large: discarding frame!\r\n" : "VCMSTRM: Packet length is smaller than payload header size: discarding frame!\r\n"); OutputDebugString(szTDebug); // DebugBreak();
#endif
return ((MMRESULT)VCMERR_NONSPECIFIC); }
}
#ifdef LOGPAYLOAD_ON
g_TDebugFile = CreateFile("C:\\RecvLog.txt", GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); SetFilePointer(g_TDebugFile, 0, NULL, FILE_END); wsprintf(szTDebug, "Frame #%03ld\r\n", (DWORD)j); WriteFile(g_TDebugFile, szTDebug, strlen(szTDebug), &d, NULL); for (j=*pdwFrameSize; j>0; j-=4, p+=4) { wsprintf(szTDebug, "%02lX %02lX %02lX %02lX\r\n", *((BYTE *)p), *((BYTE *)p+1), *((BYTE *)p+2), *((BYTE *)p+3)); WriteFile(g_TDebugFile, szTDebug, strlen(szTDebug), &d, NULL); } CloseHandle(g_TDebugFile); #endif
} else { // Strip the header of each packet and copy the payload in the video buffer
while (dwPktCount--) { // Copy the payload
// Test for invalid packets that have a length below the size of the payload header
if ( (ppDataPkt->len >= dwHeaderSize) && ((*pdwFrameSize + ppDataPkt->len - dwHeaderSize) <= dwMaxFrameSize)) { // Copy the payload
CopyMemory(pbyFrame + *pdwFrameSize, ppDataPkt->buf + dwHeaderSize, ppDataPkt->len - dwHeaderSize);
// Update the payload size and pointer to the input video packets
*pdwFrameSize += ppDataPkt->len - dwHeaderSize; ppDataPkt++; } else { // The total size of the reassembled packet would be larger than the maximum allowed!!!
// Or the packet has a length less than the payload header size
// Dump the frame
#ifdef DEBUG
wsprintf(szTDebug, (ppDataPkt->len >= dwHeaderSize) ? "VCMSTRM: Cumulative size of the reassembled packets is too large: discarding frame!\r\n" : "VCMSTRM: Packet length is smaller than payload header size: discarding frame!\r\n"); OutputDebugString(szTDebug); // DebugBreak();
#endif
return ((MMRESULT)VCMERR_NONSPECIFIC); }
} }
return ((MMRESULT)MMSYSERR_NOERROR); }
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmStreamFormatPayload | This function returns compressed data * spread into data packets with a payload header for the specific format of the * compressed data. * * @parm HVCMSTREAM | hvs | Specifies the conversion stream. * * @parm PBYTE | pDataSrc | Specifies a pointer to the whole compressed data. * * @parm DWORD | dwDataSize | Specifies the size of the input data in bytes. * * @parm PBYTE* | ppDataPkt | Specifies a pointer to a pointer to a packet. * * @parm DWORD* | pdwPktSize | Specifies a pointer to the size of the packet. * * @parm DWORD | dwPktCount | Specifies what packet to return (0 first packet, 1 second packet, ...) * * @parm DWORD | dwMaxFragSize | Specifies the maximum packet size * * @rdesc The return value is zero if the function is successful. Otherwise, it returns * an error number. Possible error values include the following: * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * @flag MMSYSERR_INVALPARAM | Specified data pointer is invalid. * @flag VCMERR_NOMOREPACKETS | There is no more data for the requested packet number, or there isn't any handler for this payload. * @flag VCMERR_NONSPECIFIC | We were asked to put a header we do not know how to generate. ***************************************************************************/ MMRESULT VCMAPI vcmStreamFormatPayload( HVCMSTREAM hvs, PBYTE pDataSrc, DWORD dwDataSize, PBYTE *ppDataPkt, PDWORD pdwPktSize, PDWORD pdwPktCount, UINT *pfMark, PBYTE *pHdrInfo, PDWORD pdwHdrSize) { PVCMSTREAM pvs = (PVCMSTREAM)hvs; PH26X_RTP_BSINFO_TRAILER pbsiT; PRTP_H263_BSINFO pbsi263; PRTP_H261_BSINFO pbsi261; PBYTE pb; DWORD dwHeaderHigh = 0UL; // most significant
DWORD dwHeaderMiddle = 0UL; DWORD dwHeaderLow = 0UL; // least significant
BOOL bOneFrameOnePacket; #ifdef DEBUG
char szDebug[256]; #endif
long i; #ifdef LOGPAYLOAD_ON
PBYTE p; DWORD d; DWORD dwLastChunk; DWORD wPrevOffset; #endif
// Check input params
if (!hvs) { ERRORMESSAGE(("vcmStreamFormatPayload: Specified handle is invalid, hvs=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALHANDLE); } if (!pDataSrc) { ERRORMESSAGE(("vcmStreamFormatPayload: Specified pointer is invalid, pDataSrc=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); }
// Initialize packet pointer
*ppDataPkt = pDataSrc; *pdwPktSize = dwDataSize; *pfMark = 1; bOneFrameOnePacket = FALSE;
// Put the code that builds the packets right here!!!
#ifndef _ALPHA_
#ifdef USE_BILINEAR_MSH26X
if ((pvs->pvfxDst->dwFormatTag == VIDEO_FORMAT_MSH263) || (pvs->pvfxDst->dwFormatTag == VIDEO_FORMAT_MSH26X)) #else
if (pvs->pvfxDst->dwFormatTag == VIDEO_FORMAT_MSH263) #endif
#else
if (pvs->pvfxDst->dwFormatTag == VIDEO_FORMAT_DECH263) #endif
{ // Look for the bitstream info trailer
pbsiT = (PH26X_RTP_BSINFO_TRAILER)(pDataSrc + dwDataSize - sizeof(H26X_RTP_BSINFO_TRAILER));
// If the whole frame can fit in pvs->dwMaxPacketSize, send it non fragmented
if ((pbsiT->dwCompressedSize + 4) < pvs->dwMaxPacketSize) bOneFrameOnePacket = TRUE;
// Look for the packet to receive a H.263 payload header
if ((*pdwPktCount < pbsiT->dwNumOfPackets) && !(bOneFrameOnePacket && *pdwPktCount)) {
#ifdef _ALPHA_
// Verify that the content of the bistream info structures is correct
// If not, do not parse the frame, and fail the call
// This is to solve problems with the data returned by the DEC codecs
if (!*pdwPktCount) { pbsi263 = (PRTP_H263_BSINFO)((PBYTE)pbsiT - pbsiT->dwNumOfPackets * sizeof(RTP_H263_BSINFO)); for (i=1; i<(long)pbsiT->dwNumOfPackets; i++, pbsi263++) { if ((pbsi263->dwBitOffset >= (pbsi263+1)->dwBitOffset) || ((pbsiT->dwCompressedSize*8) <= pbsi263->dwBitOffset)) { #ifdef DEBUG
OutputDebugString("VCMSTRM: The content of the extended bitstream info structures is invalid!\r\n"); #endif
// return ((MMRESULT)VCMERR_NONSPECIFIC);
bOneFrameOnePacket = TRUE; } }
// Test last info strucure
if ( !bOneFrameOnePacket && ((pbsiT->dwCompressedSize*8) <= pbsi263->dwBitOffset)) { #ifdef DEBUG
OutputDebugString("VCMSTRM: The content of the extended bitstream info structures is invalid!\r\n"); #endif
// return ((MMRESULT)VCMERR_NONSPECIFIC);
bOneFrameOnePacket = TRUE; } } #endif
#ifdef LOGPAYLOAD_ON
// Dump the whole frame in the debug window for comparison with receive side
if (!*pdwPktCount) { g_DebugFile = CreateFile("C:\\SendLog.txt", GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); SetFilePointer(g_DebugFile, 0, NULL, FILE_END); wsprintf(szDebug, "Frame #%03ld\r\n", (DWORD)pbsiT->byTR); WriteFile(g_DebugFile, szDebug, strlen(szDebug), &d, NULL); wsprintf(szDebug, "Frame #%03ld has %1ld packets of size ", (DWORD)pbsiT->byTR, (DWORD)pbsiT->dwNumOfPackets); OutputDebugString(szDebug); pbsi263 = (PRTP_H263_BSINFO)((PBYTE)pbsiT - pbsiT->dwNumOfPackets * sizeof(RTP_H263_BSINFO)); for (i=1; i<(long)pbsiT->dwNumOfPackets; i++) { wPrevOffset = pbsi263->dwBitOffset; pbsi263++; wsprintf(szDebug, "%04ld, ", (DWORD)(pbsi263->dwBitOffset - wPrevOffset) >> 3); OutputDebugString(szDebug); } wsprintf(szDebug, "%04ld\r\n", (DWORD)(pbsiT->dwCompressedSize * 8 - pbsi263->dwBitOffset) >> 3); OutputDebugString(szDebug); for (i=pbsiT->dwCompressedSize, p=pDataSrc; i>0; i-=4, p+=4) { wsprintf(szDebug, "%02lX %02lX %02lX %02lX\r\n", *((BYTE *)p), *((BYTE *)p+1), *((BYTE *)p+2), *((BYTE *)p+3)); WriteFile(g_DebugFile, szDebug, strlen(szDebug), &d, NULL); } CloseHandle(g_DebugFile); } #endif
// Look for the bitstream info structure
pbsi263 = (PRTP_H263_BSINFO)((PBYTE)pbsiT - (pbsiT->dwNumOfPackets - *pdwPktCount) * sizeof(RTP_H263_BSINFO)); // Set the marker bit: as long as this is not the last packet of the frame
// this bit needs to be set to 0
if (!bOneFrameOnePacket) { // Count the number of GOBS that could fit in pvs->dwMaxPacketSize
for (i=1; (i<(long)(pbsiT->dwNumOfPackets - *pdwPktCount)) && (pbsi263->byMode != RTP_H263_MODE_B); i++) { // Don't try to add a Mode B packet to the end of another Mode A or Mode B packet
if (((pbsi263+i)->dwBitOffset - pbsi263->dwBitOffset > (pvs->dwMaxPacketSize * 8)) || ((pbsi263+i)->byMode == RTP_H263_MODE_B)) break; }
if (i < (long)(pbsiT->dwNumOfPackets - *pdwPktCount)) { *pfMark = 0; if (i>1) i--; } else { // Hey! You 're forgetting the last GOB! It could make the total
// size of the last packet larger than pvs->dwMaxPacketSize... Imbecile!
if ((pbsiT->dwCompressedSize * 8 - pbsi263->dwBitOffset > (pvs->dwMaxPacketSize * 8)) && (i>1)) { *pfMark = 0; i--; } }
#if 0
//
// THIS IS FOR EXPERIMENTATION ONLY !!!
//
// Ditch the last GOB
if ((*pfMark == 1) && (i == 1)) return ((MMRESULT)VCMERR_NOMOREPACKETS); #endif
}
// Go to the beginning of the data
pb = pDataSrc + pbsi263->dwBitOffset / 8;
#if 0
//
// THIS IS FOR EXPERIMENTATION ONLY !!!
//
// Trash the PSC once in a while to see how the other end reacts
if (!*pdwPktCount && (((*pb == 0) && (*(pb+1) == 0) && ((*(pb+2) & 0xFC) == 0x80)))) { // The previous test guarantees that it is in fact a PSC that we trash...
if ((DWORD)(RAND_MAX - rand()) < (DWORD)(RAND_MAX / 10)) *pb = 0xFF; } #endif
#ifdef DEBUG
if (!*pdwPktCount && (((*pb != 0) || (*(pb+1) != 0) || ((*(pb+2) & 0xFC) != 0x80)))) { wsprintf(szDebug, "VCMSTRM: This compressed frame is missing a PSC!\r\n"); OutputDebugString(szDebug); // DebugBreak();
} #endif
// Look for the kind of header to be built
if (pbsi263->byMode == RTP_H263_MODE_A) { // Build a header in mode A
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//|F|P|SBIT |EBIT | SRC | R |I|A|S|DBQ| TRB | TR |
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// But that's the network byte order...
// F bit already set to 0
// Set the SRC bits
dwHeaderHigh |= ((DWORD)(pbsiT->bySrc)) << 21;
// R bits already set to 0
// Set the P bit
dwHeaderHigh |= (pbsiT->dwFlags & RTP_H263_PB) << 29;
// Set the I bit
dwHeaderHigh |= (pbsiT->dwFlags & RTP_H26X_INTRA_CODED) << 15;
// Set the A bit
dwHeaderHigh |= (pbsiT->dwFlags & RTP_H263_AP) << 12;
// Set the S bit
dwHeaderHigh |= (pbsiT->dwFlags & RTP_H263_SAC) << 10;
// Set the DBQ bits
dwHeaderHigh |= ((DWORD)(pbsiT->byDBQ)) << 11;
// Set the TRB bits
dwHeaderHigh |= ((DWORD)(pbsiT->byTRB)) << 8;
// Set the TR bits
dwHeaderHigh |= ((DWORD)(pbsiT->byTR));
// Special case: 1 frame = 1 packet
if (bOneFrameOnePacket) { // SBIT is already set to 0
// EBIT is already set to 0
#ifdef VALIDATE_SBIT_EBIT // { VALIDATE_SBIT_EBIT
ERRORMESSAGE(("vcmFormatPayload: (1F1P) Previous EBIT=%ld, current SBIT=%ld, current EBIT=%ld (New frame)\r\n", g_dwPreviousEBIT, (DWORD)(dwHeaderHigh & 0x38000000) >> 27, (DWORD)(dwHeaderHigh & 0x07000000) >> 24)); #endif // } VALIDATE_SBIT_EBIT
// Update the packet size
*pdwPktSize = pbsiT->dwCompressedSize + 4;
// Update the packet count
*pdwPktCount = pbsiT->dwNumOfPackets;
} else { #ifdef VALIDATE_SBIT_EBIT // { VALIDATE_SBIT_EBIT
DWORD dwCurrentSBIT; #endif // } VALIDATE_SBIT_EBIT
// Set the SBIT bits
dwHeaderHigh |= (pbsi263->dwBitOffset % 8) << 27;
// Set the EBIT bits
if ((pbsiT->dwNumOfPackets - *pdwPktCount - i) >= 1) dwHeaderHigh |= (DWORD)((8UL - ((pbsi263+i)->dwBitOffset % 8)) & 0x00000007) << 24;
#ifdef VALIDATE_SBIT_EBIT // { VALIDATE_SBIT_EBIT
// Compare this to the previous EBIT. If the sum of the two
// is not equal to 8 or 0, something's broken
if (*pdwPktCount) ERRORMESSAGE(("vcmFormatPayload: Previous EBIT=%ld, current SBIT=%ld, current EBIT=%ld\r\n", g_dwPreviousEBIT, (DWORD)(dwHeaderHigh & 0x38000000) >> 27, (DWORD)(dwHeaderHigh & 0x07000000) >> 24)); else ERRORMESSAGE(("vcmFormatPayload: Previous EBIT=%ld, current SBIT=%ld, current EBIT=%ld (New frame)\r\n", g_dwPreviousEBIT, (DWORD)(dwHeaderHigh & 0x38000000) >> 27, (DWORD)(dwHeaderHigh & 0x07000000) >> 24));
// Only test this if this is the first packet
dwCurrentSBIT = (DWORD)(dwHeaderHigh & 0x38000000) >> 27; if ((*pdwPktCount) && (((dwCurrentSBIT + g_dwPreviousEBIT) != 8) && (((dwCurrentSBIT + g_dwPreviousEBIT) != 0)))) DebugBreak();
g_dwPreviousEBIT = (dwHeaderHigh & 0x07000000) >> 24; #endif // } VALIDATE_SBIT_EBIT
// Update the packet size
if ((pbsiT->dwNumOfPackets - *pdwPktCount - i) >= 1) *pdwPktSize = (((pbsi263+i)->dwBitOffset - 1) / 8) - (pbsi263->dwBitOffset / 8) + 1 + 4; else *pdwPktSize = pbsiT->dwCompressedSize - pbsi263->dwBitOffset / 8 + 4;
// Update the packet count
*pdwPktCount += i;
}
#if 0
// Save the header right before the data chunk
*ppDataPkt = pDataSrc + (pbsi263->dwBitOffset / 8) - 4;
// Convert to network byte order
*((BYTE *)*ppDataPkt+3) = (BYTE)(dwHeaderHigh & 0x000000FF); *((BYTE *)*ppDataPkt+2) = (BYTE)((dwHeaderHigh >> 8) & 0x000000FF); *((BYTE *)*ppDataPkt+1) = (BYTE)((dwHeaderHigh >> 16) & 0x000000FF); *((BYTE *)*ppDataPkt) = (BYTE)((dwHeaderHigh >> 24) & 0x000000FF); #else
// Save the header right before the data chunk
*ppDataPkt = pDataSrc + (pbsi263->dwBitOffset / 8) - 4; *pdwHdrSize=4;
// Convert to network byte order
*((BYTE *)*pHdrInfo+3) = (BYTE)(dwHeaderHigh & 0x000000FF); *((BYTE *)*pHdrInfo+2) = (BYTE)((dwHeaderHigh >> 8) & 0x000000FF); *((BYTE *)*pHdrInfo+1) = (BYTE)((dwHeaderHigh >> 16) & 0x000000FF); *((BYTE *)*pHdrInfo) = (BYTE)((dwHeaderHigh >> 24) & 0x000000FF); #endif
#ifdef LOGPAYLOAD_ON
// Output some debug stuff
wsprintf(szDebug, "Header content:\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, (*(BYTE *)*ppDataPkt & 0x80) ? " F: '1' => Mode B or C\r\n" : " F: '0' => Mode A\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, (*(BYTE *)*ppDataPkt & 0x40) ? " P: '1' => PB-frame\r\n" : " P: '0' => I or P frame\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, " SBIT: %01ld\r\n", (DWORD)((*(BYTE *)*ppDataPkt & 0x38) >> 3)); OutputDebugString(szDebug); wsprintf(szDebug, " EBIT: %01ld\r\n", (DWORD)(*(BYTE *)*ppDataPkt & 0x07)); OutputDebugString(szDebug); switch ((DWORD)(*((BYTE *)*ppDataPkt+1) >> 5)) { case 0: wsprintf(szDebug, " SRC: '000' => Source format forbidden!\r\n"); break; case 1: wsprintf(szDebug, " SRC: '001' => Source format sub-QCIF\r\n"); break; case 2: wsprintf(szDebug, " SRC: '010' => Source format QCIF\r\n"); break; case 3: wsprintf(szDebug, " SRC: '011' => Source format CIF\r\n"); break; case 4: wsprintf(szDebug, " SRC: '100' => Source format 4CIF\r\n"); break; case 5: wsprintf(szDebug, " SRC: '101' => Source format 16CIF\r\n"); break; case 6: wsprintf(szDebug, " SRC: '110' => Source format reserved\r\n"); break; case 7: wsprintf(szDebug, " SRC: '111' => Source format reserved\r\n"); break; default: wsprintf(szDebug, " SRC: %ld => Source format unknown!\r\n", (DWORD)(*((BYTE *)*ppDataPkt+1) >> 5)); break; } OutputDebugString(szDebug); wsprintf(szDebug, " R: %02ld => Reserved, must be 0\r\n", (DWORD)((*((BYTE *)*ppDataPkt+1) & 0x1F) >> 5)); OutputDebugString(szDebug); wsprintf(szDebug, (*((BYTE *)*ppDataPkt+2) & 0x80) ? " I: '1' => Intra-coded\r\n" : " I: '0' => Not Intra-coded\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, (*((BYTE *)*ppDataPkt+2) & 0x40) ? " A: '1' => Optional Advanced Prediction mode ON\r\n" : " A: '0' => Optional Advanced Prediction mode OFF\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, (*((BYTE *)*ppDataPkt+2) & 0x20) ? " S: '1' => Optional Syntax-based Arithmetic Code mode ON\r\n" : " S: '0' => Optional Syntax-based Arithmetic Code mode OFF\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, " DBQ: %01ld => Should be 0\r\n", (DWORD)((*((BYTE *)*ppDataPkt+2) & 0x18) >> 3)); OutputDebugString(szDebug); wsprintf(szDebug, " TRB: %01ld => Should be 0\r\n", (DWORD)(*((BYTE *)*ppDataPkt+2) & 0x07)); OutputDebugString(szDebug); wsprintf(szDebug, " TR: %03ld\r\n", (DWORD)(*((BYTE *)*ppDataPkt+3))); OutputDebugString(szDebug); wsprintf(szDebug, "Packet: %02lX\r\n Header: %02lX %02lX %02lX %02lX\r\n dword1: %02lX %02lX %02lX %02lX\r\n dword2: %02lX %02lX %02lX %02lX\r\n", *pdwPktCount, *((BYTE *)*ppDataPkt), *((BYTE *)*ppDataPkt+1), *((BYTE *)*ppDataPkt+2), *((BYTE *)*ppDataPkt+3), *((BYTE *)*ppDataPkt+4), *((BYTE *)*ppDataPkt+5), *((BYTE *)*ppDataPkt+6), *((BYTE *)*ppDataPkt+7), *((BYTE *)*ppDataPkt+8), *((BYTE *)*ppDataPkt+9), *((BYTE *)*ppDataPkt+10), *((BYTE *)*ppDataPkt+11)); OutputDebugString(szDebug); if (*pdwPktCount == pbsiT->dwNumOfPackets) wsprintf(szDebug, " Tail : %02lX %02lX XX XX\r\n", *((BYTE *)*ppDataPkt+*pdwPktSize-2), *((BYTE *)*ppDataPkt+*pdwPktSize-1)); else wsprintf(szDebug, " Tail : %02lX %02lX %02lX %02lX\r\n", *((BYTE *)*ppDataPkt+*pdwPktSize-2), *((BYTE *)*ppDataPkt+*pdwPktSize-1), *((BYTE *)*ppDataPkt+*pdwPktSize), *((BYTE *)*ppDataPkt+*pdwPktSize+1)); OutputDebugString(szDebug); if (*pfMark == 1) wsprintf(szDebug, " Marker: ON\r\n"); else wsprintf(szDebug, " Marker: OFF\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, "Frame #%03ld, Packet of size %04ld\r\n", (DWORD)pbsiT->byTR, *pdwPktSize); OutputDebugString(szDebug); #endif
} else if (pbsi263->byMode == RTP_H263_MODE_B) { // Build a header in mode B
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//|F|P|SBIT |EBIT | SRC | QUANT |I|A|S| GOBN | MBA |
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//| HMV1 | VMV1 | HMV2 | VMV2 |
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// But that's the network byte order...
// Set the F bit to 1
dwHeaderHigh = 0x80000000;
// Set the SRC bits
dwHeaderHigh |= ((DWORD)(pbsiT->bySrc)) << 21;
// Set the QUANT bits
dwHeaderHigh |= ((DWORD)(pbsi263->byQuant)) << 16;
// Set the P bit
dwHeaderHigh |= (pbsiT->dwFlags & RTP_H263_PB) << 29;
// Set the I bit
dwHeaderHigh |= (pbsiT->dwFlags & RTP_H26X_INTRA_CODED) << 15;
// Set the A bit
dwHeaderHigh |= (pbsiT->dwFlags & RTP_H263_AP) << 12;
// Set the S bit
dwHeaderHigh |= (pbsiT->dwFlags & RTP_H263_SAC) << 10;
// Set the GOBN bits
dwHeaderHigh |= ((DWORD)(pbsi263->byGOBN)) << 8;
// Set the TR bits
dwHeaderHigh |= ((DWORD)(pbsi263->byMBA));
// Set the HMV1 bits
dwHeaderLow |= ((DWORD)(BYTE)(pbsi263->cHMV1)) << 24;
// Set the VMV1 bits
dwHeaderLow |= ((DWORD)(BYTE)(pbsi263->cVMV1)) << 16;
// Set the HMV2 bits
dwHeaderLow |= ((DWORD)(BYTE)(pbsi263->cHMV2)) << 8;
// Set the VMV2 bits
dwHeaderLow |= ((DWORD)(BYTE)(pbsi263->cVMV2));
// Special case: 1 frame = 1 packet
if (bOneFrameOnePacket) { // SBIT is already set to 0
// EBIT is already set to 0
#ifdef VALIDATE_SBIT_EBIT // { VALIDATE_SBIT_EBIT
ERRORMESSAGE(("vcmFormatPayload: (1F1P) Previous EBIT=%ld, current SBIT=%ld, current EBIT=%ld (New frame)\r\n", g_dwPreviousEBIT, (DWORD)(dwHeaderHigh & 0x38000000) >> 27, (DWORD)(dwHeaderHigh & 0x07000000) >> 24)); #endif // } VALIDATE_SBIT_EBIT
// Update the packet size
*pdwPktSize = pbsiT->dwCompressedSize + 8;
// Update the packet count
*pdwPktCount = pbsiT->dwNumOfPackets;
} else {
#ifdef VALIDATE_SBIT_EBIT // { VALIDATE_SBIT_EBIT
DWORD dwCurrentSBIT; #endif // } VALIDATE_SBIT_EBIT
// Set the SBIT bits
dwHeaderHigh |= (pbsi263->dwBitOffset % 8) << 27;
// Set the EBIT bits
if ((pbsiT->dwNumOfPackets - *pdwPktCount - i) >= 1) dwHeaderHigh |= (DWORD)((8UL - ((pbsi263+i)->dwBitOffset % 8)) & 0x00000007) << 24;
#ifdef VALIDATE_SBIT_EBIT // { VALIDATE_SBIT_EBIT
// Compare this to the previous EBIT. If the sum of the two
// is not equal to 8 or 0, something's broken
if (*pdwPktCount) ERRORMESSAGE(("vcmFormatPayload: Previous EBIT=%ld, current SBIT=%ld, current EBIT=%ld\r\n", g_dwPreviousEBIT, (DWORD)(dwHeaderHigh & 0x38000000) >> 27, (DWORD)(dwHeaderHigh & 0x07000000) >> 24)); else ERRORMESSAGE(("vcmFormatPayload: Previous EBIT=%ld, current SBIT=%ld, current EBIT=%ld (New frame)\r\n", g_dwPreviousEBIT, (DWORD)(dwHeaderHigh & 0x38000000) >> 27, (DWORD)(dwHeaderHigh & 0x07000000) >> 24));
// Only test this if this is the first packet
dwCurrentSBIT = (DWORD)(dwHeaderHigh & 0x38000000) >> 27; if ((*pdwPktCount) && (((dwCurrentSBIT + g_dwPreviousEBIT) != 8) && (((dwCurrentSBIT + g_dwPreviousEBIT) != 0)))) DebugBreak();
g_dwPreviousEBIT = (dwHeaderHigh & 0x07000000) >> 24; #endif // } VALIDATE_SBIT_EBIT
// Update the packet size
if ((pbsiT->dwNumOfPackets - *pdwPktCount - i) >= 1) *pdwPktSize = (((pbsi263+i)->dwBitOffset - 1) / 8) - (pbsi263->dwBitOffset / 8) + 1 + 8; else *pdwPktSize = pbsiT->dwCompressedSize - pbsi263->dwBitOffset / 8 + 8;
// Update the packet count
*pdwPktCount += i;
}
#if 0
// Save the header right before the data chunk
*ppDataPkt = pDataSrc + (pbsi263->dwBitOffset / 8) - 8;
// Convert to network byte order
*((BYTE *)*ppDataPkt+3) = (BYTE)(dwHeaderHigh & 0x000000FF); *((BYTE *)*ppDataPkt+2) = (BYTE)((dwHeaderHigh >> 8) & 0x000000FF); *((BYTE *)*ppDataPkt+1) = (BYTE)((dwHeaderHigh >> 16) & 0x000000FF); *((BYTE *)*ppDataPkt) = (BYTE)((dwHeaderHigh >> 24) & 0x000000FF); *((BYTE *)*ppDataPkt+7) = (BYTE)(dwHeaderLow & 0x000000FF); *((BYTE *)*ppDataPkt+6) = (BYTE)((dwHeaderLow >> 8) & 0x000000FF); *((BYTE *)*ppDataPkt+5) = (BYTE)((dwHeaderLow >> 16) & 0x000000FF); *((BYTE *)*ppDataPkt+4) = (BYTE)((dwHeaderLow >> 24) & 0x000000FF); #else
// Save the header right before the data chunk
*ppDataPkt = pDataSrc + (pbsi263->dwBitOffset / 8) - 8; *pdwHdrSize=8;
// Convert to network byte order
*((BYTE *)*pHdrInfo+3) = (BYTE)(dwHeaderHigh & 0x000000FF); *((BYTE *)*pHdrInfo+2) = (BYTE)((dwHeaderHigh >> 8) & 0x000000FF); *((BYTE *)*pHdrInfo+1) = (BYTE)((dwHeaderHigh >> 16) & 0x000000FF); *((BYTE *)*pHdrInfo) = (BYTE)((dwHeaderHigh >> 24) & 0x000000FF); *((BYTE *)*pHdrInfo+7) = (BYTE)(dwHeaderLow & 0x000000FF); *((BYTE *)*pHdrInfo+6) = (BYTE)((dwHeaderLow >> 8) & 0x000000FF); *((BYTE *)*pHdrInfo+5) = (BYTE)((dwHeaderLow >> 16) & 0x000000FF); *((BYTE *)*pHdrInfo+4) = (BYTE)((dwHeaderLow >> 24) & 0x000000FF); #endif
#ifdef LOGPAYLOAD_ON
// Output some info
wsprintf(szDebug, "Header content:\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, (*(BYTE *)*ppDataPkt & 0x80) ? " F: '1' => Mode B or C\r\n" : " F: '0' => Mode A\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, (*(BYTE *)*ppDataPkt & 0x40) ? " P: '1' => PB-frame\r\n" : " P: '0' => I or P frame\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, " SBIT: %01ld\r\n", (DWORD)((*(BYTE *)*ppDataPkt & 0x38) >> 3)); OutputDebugString(szDebug); wsprintf(szDebug, " EBIT: %01ld\r\n", (DWORD)(*(BYTE *)*ppDataPkt & 0x07)); OutputDebugString(szDebug); switch ((DWORD)(*((BYTE *)*ppDataPkt+1) >> 5)) { case 0: wsprintf(szDebug, " SRC: '000' => Source format forbidden!\r\n"); break; case 1: wsprintf(szDebug, " SRC: '001' => Source format sub-QCIF\r\n"); break; case 2: wsprintf(szDebug, " SRC: '010' => Source format QCIF\r\n"); break; case 3: wsprintf(szDebug, " SRC: '011' => Source format CIF\r\n"); break; case 4: wsprintf(szDebug, " SRC: '100' => Source format 4CIF\r\n"); break; case 5: wsprintf(szDebug, " SRC: '101' => Source format 16CIF\r\n"); break; case 6: wsprintf(szDebug, " SRC: '110' => Source format reserved\r\n"); break; case 7: wsprintf(szDebug, " SRC: '111' => Source format reserved\r\n"); break; default: wsprintf(szDebug, " SRC: %ld => Source format unknown!\r\n", (DWORD)(*((BYTE *)*ppDataPkt+1) >> 5)); break; } OutputDebugString(szDebug); wsprintf(szDebug, " QUANT: %02ld\r\n", (DWORD)((*((BYTE *)*ppDataPkt+1) & 0x1F) >> 5)); OutputDebugString(szDebug); wsprintf(szDebug, (*((BYTE *)*ppDataPkt+2) & 0x80) ? " I: '1' => Intra-coded\r\n" : " I: '0' => Not Intra-coded\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, (*((BYTE *)*ppDataPkt+2) & 0x40) ? " A: '1' => Optional Advanced Prediction mode ON\r\n" : " A: '0' => Optional Advanced Prediction mode OFF\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, (*((BYTE *)*ppDataPkt+2) & 0x20) ? " S: '1' => Optional Syntax-based Arithmetic Code mode ON\r\n" : " S: '0' => Optional Syntax-based Arithmetic Code mode OFF\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, " GOBN: %03ld\r\n", (DWORD)(*((BYTE *)*ppDataPkt+2) & 0x1F)); OutputDebugString(szDebug); wsprintf(szDebug, " MBA: %03ld\r\n", (DWORD)(*((BYTE *)*ppDataPkt+3))); OutputDebugString(szDebug); wsprintf(szDebug, " HMV1: %03ld\r\n", (DWORD)(*((BYTE *)*ppDataPkt+7))); OutputDebugString(szDebug); wsprintf(szDebug, " VMV1: %03ld\r\n", (DWORD)(*((BYTE *)*ppDataPkt+6))); OutputDebugString(szDebug); wsprintf(szDebug, " HMV2: %03ld\r\n", (DWORD)(*((BYTE *)*ppDataPkt+5))); OutputDebugString(szDebug); wsprintf(szDebug, " VMV2: %03ld\r\n", (DWORD)(*((BYTE *)*ppDataPkt+4))); OutputDebugString(szDebug); wsprintf(szDebug, "Packet: %02lX\r\n Header: %02lX %02lX %02lX %02lX %02lX %02lX %02lX %02lX\r\n dword1: %02lX %02lX %02lX %02lX\r\n", *pdwPktCount, *((BYTE *)*ppDataPkt), *((BYTE *)*ppDataPkt+1), *((BYTE *)*ppDataPkt+2), *((BYTE *)*ppDataPkt+3), *((BYTE *)*ppDataPkt+4), *((BYTE *)*ppDataPkt+5), *((BYTE *)*ppDataPkt+6), *((BYTE *)*ppDataPkt+7), *((BYTE *)*ppDataPkt+8), *((BYTE *)*ppDataPkt+9), *((BYTE *)*ppDataPkt+10), *((BYTE *)*ppDataPkt+11)); OutputDebugString(szDebug); if (*pdwPktCount == pbsiT->dwNumOfPackets) wsprintf(szDebug, " Tail : %02lX %02lX XX XX\r\n", *((BYTE *)*ppDataPkt+*pdwPktSize-2), *((BYTE *)*ppDataPkt+*pdwPktSize-1)); else wsprintf(szDebug, " Tail : %02lX %02lX %02lX %02lX\r\n", *((BYTE *)*ppDataPkt+*pdwPktSize-2), *((BYTE *)*ppDataPkt+*pdwPktSize-1), *((BYTE *)*ppDataPkt+*pdwPktSize), *((BYTE *)*ppDataPkt+*pdwPktSize+1)); OutputDebugString(szDebug); if (*pfMark == 1) wsprintf(szDebug, " Marker: ON\r\n"); else wsprintf(szDebug, " Marker: OFF\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, "Frame #%03ld, Packet of size %04ld\r\n", (DWORD)pbsiT->byTR, *pdwPktSize); OutputDebugString(szDebug); #endif
} else if (pbsi263->byMode == RTP_H263_MODE_C) { // Build a header in mode C
#ifdef DEBUG
wsprintf(szDebug, "VCMSTRM: We were asked to generate a MODE C H.263 payload header!"); OutputDebugString(szDebug); // DebugBreak();
#endif
return ((MMRESULT)VCMERR_NONSPECIFIC); } } else return ((MMRESULT)VCMERR_NOMOREPACKETS); } #ifndef _ALPHA_
else if (pvs->pvfxDst->dwFormatTag == VIDEO_FORMAT_MSH261) #else
else if (pvs->pvfxDst->dwFormatTag == VIDEO_FORMAT_DECH261) #endif
{ // Look for the bitstream info trailer
pbsiT = (PH26X_RTP_BSINFO_TRAILER)(pDataSrc + dwDataSize - sizeof(H26X_RTP_BSINFO_TRAILER));
// If the whole frame can fit in dwMaxFragSize, send it non fragmented
if ((pbsiT->dwCompressedSize + 4) < pvs->dwMaxPacketSize) bOneFrameOnePacket = TRUE;
// Look for the packet to receive a H.261 payload header
if ((*pdwPktCount < pbsiT->dwNumOfPackets) && !(bOneFrameOnePacket && *pdwPktCount)) {
#ifdef _ALPHA_
// Verify that the content of the bistream info structures is correct
// If not, do not parse the frame, and fail the call
// This is to solve problems with the data returned by the DEC codecs
if (!*pdwPktCount) { pbsi261 = (PRTP_H261_BSINFO)((PBYTE)pbsiT - pbsiT->dwNumOfPackets * sizeof(RTP_H261_BSINFO)); for (i=1; i<(long)pbsiT->dwNumOfPackets; i++, pbsi261++) { if ((pbsi261->dwBitOffset >= (pbsi261+1)->dwBitOffset) || ((pbsiT->dwCompressedSize*8) <= pbsi261->dwBitOffset)) { #ifdef DEBUG
OutputDebugString("VCMSTRM: The content of the extended bitstream info structures is invalid!\r\n"); #endif
// return ((MMRESULT)VCMERR_NONSPECIFIC);
bOneFrameOnePacket = TRUE; } }
// Test last info strucure
if ( !bOneFrameOnePacket && ((pbsiT->dwCompressedSize*8) <= pbsi261->dwBitOffset)) { #ifdef DEBUG
OutputDebugString("VCMSTRM: The content of the extended bitstream info structures is invalid!\r\n"); #endif
// return ((MMRESULT)VCMERR_NONSPECIFIC);
bOneFrameOnePacket = TRUE; } } #endif
#ifdef LOGPAYLOAD_ON
// Dump the whole frame in the debug window for comparison with receive side
if (!*pdwPktCount) { g_DebugFile = CreateFile("C:\\SendLog.txt", GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); SetFilePointer(g_DebugFile, 0, NULL, FILE_END); wsprintf(szDebug, "Frame #%03ld\r\n", (DWORD)pbsiT->byTR); WriteFile(g_DebugFile, szDebug, strlen(szDebug), &d, NULL); wsprintf(szDebug, "Frame #%03ld has %1ld GOBs of size ", (DWORD)pbsiT->byTR, (DWORD)pbsiT->dwNumOfPackets); OutputDebugString(szDebug); pbsi261 = (PRTP_H261_BSINFO)((PBYTE)pbsiT - pbsiT->dwNumOfPackets * sizeof(RTP_H261_BSINFO)); for (i=1; i<(long)pbsiT->dwNumOfPackets; i++) { wPrevOffset = pbsi261->dwBitOffset; pbsi261++; wsprintf(szDebug, "%04ld, ", (DWORD)(pbsi261->dwBitOffset - wPrevOffset) >> 3); OutputDebugString(szDebug); } wsprintf(szDebug, "%04ld\r\n", (DWORD)(pbsiT->dwCompressedSize * 8 - pbsi261->dwBitOffset) >> 3); OutputDebugString(szDebug); for (i=pbsiT->dwCompressedSize, p=pDataSrc; i>0; i-=4, p+=4) { wsprintf(szDebug, "%02lX %02lX %02lX %02lX\r\n", *((BYTE *)p), *((BYTE *)p+1), *((BYTE *)p+2), *((BYTE *)p+3)); WriteFile(g_DebugFile, szDebug, strlen(szDebug), &d, NULL); } CloseHandle(g_DebugFile); } #endif
// Look for the bitstream info structure
pbsi261 = (PRTP_H261_BSINFO)((PBYTE)pbsiT - (pbsiT->dwNumOfPackets - *pdwPktCount) * sizeof(RTP_H261_BSINFO)); // Set the marker bit: as long as this is not the last packet of the frame
// this bit needs to be set to 0
if (!bOneFrameOnePacket) { // Count the number of GOBS that could fit in dwMaxFragSize
for (i=1; i<(long)(pbsiT->dwNumOfPackets - *pdwPktCount); i++) { if ((pbsi261+i)->dwBitOffset - pbsi261->dwBitOffset > (pvs->dwMaxPacketSize * 8)) break; }
if (i < (long)(pbsiT->dwNumOfPackets - *pdwPktCount)) { *pfMark = 0; if (i>1) i--; } else { // Hey! You 're forgetting the last GOB! It could make the total
// size of the last packet larger than dwMaxFragSize... Imbecile!
if ((pbsiT->dwCompressedSize * 8 - pbsi261->dwBitOffset > (pvs->dwMaxPacketSize * 8)) && (i>1)) { *pfMark = 0; i--; } } }
// Go to the beginning of the data
pb = pDataSrc + pbsi261->dwBitOffset / 8;
#ifdef DEBUG
if (!*pdwPktCount && ((*pb != 0) || (*(pb+1) != 1))) { wsprintf(szDebug, "VCMSTRM: This GOB is missing a GOB Start!"); OutputDebugString(szDebug); // DebugBreak();
} #endif
// Build a header to this thing!
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//|SBIT |EBIT |I|V| GOBN | MBAP | QUANT | HMVD | VMVD |
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// But that's the network byte order...
// Set the V bit to 1
dwHeaderHigh |= 0x01000000;
// Set the I bit
dwHeaderHigh |= (pbsiT->dwFlags & RTP_H26X_INTRA_CODED) << 25;
// Set the GOBn bits
dwHeaderHigh |= ((DWORD)(pbsi261->byGOBN)) << 20;
// Set the MBAP bits
dwHeaderHigh |= ((DWORD)(pbsi261->byMBA)) << 15;
// Set the QUANT bits
dwHeaderHigh |= ((DWORD)(pbsi261->byQuant)) << 10;
// Set the HMVD bits
dwHeaderHigh |= ((DWORD)(BYTE)(pbsi261->cHMV)) << 5;
// Set the VMVD bits
dwHeaderHigh |= ((DWORD)(BYTE)(pbsi261->cVMV));
// Special case: 1 frame = 1 packet
if (bOneFrameOnePacket) { // SBIT is already set to 0
// EBIT is already set to 0
#ifdef VALIDATE_SBIT_EBIT // { VALIDATE_SBIT_EBIT
ERRORMESSAGE(("vcmFormatPayload: (1F1P) Previous EBIT=%ld, current SBIT=%ld, current EBIT=%ld (New frame)\r\n", g_dwPreviousEBIT, (DWORD)(dwHeaderHigh & 0xE0000000) >> 29, (DWORD)(dwHeaderHigh & 0x1C000000) >> 26)); #endif // } VALIDATE_SBIT_EBIT
// Update the packet size
*pdwPktSize = pbsiT->dwCompressedSize + 4;
// Update the packet count
*pdwPktCount = pbsiT->dwNumOfPackets;
} else { #ifdef VALIDATE_SBIT_EBIT // { VALIDATE_SBIT_EBIT
DWORD dwCurrentSBIT; #endif // } VALIDATE_SBIT_EBIT
// Set the SBIT bits
dwHeaderHigh |= (pbsi261->dwBitOffset % 8) << 29;
// Set the EBIT bits
if ((pbsiT->dwNumOfPackets - *pdwPktCount - i) >= 1) dwHeaderHigh |= (DWORD)((8UL - ((pbsi261+i)->dwBitOffset % 8)) & 0x00000007) << 26;
#ifdef VALIDATE_SBIT_EBIT // { VALIDATE_SBIT_EBIT
// Compare this to the previous EBIT. If the sum of the two
// is not equal to 8, something's broken
if (*pdwPktCount) ERRORMESSAGE(("vcmFormatPayload: Previous EBIT=%ld, current SBIT=%ld, current EBIT=%ld\r\n", g_dwPreviousEBIT, (DWORD)(dwHeaderHigh & 0xE0000000) >> 29, (DWORD)(dwHeaderHigh & 0x1C000000) >> 26)); else ERRORMESSAGE(("vcmFormatPayload: Previous EBIT=%ld, current SBIT=%ld, current EBIT=%ld (New frame)\r\n", g_dwPreviousEBIT, (DWORD)(dwHeaderHigh & 0xE0000000) >> 29, (DWORD)(dwHeaderHigh & 0x1C000000) >> 26));
// Only test this if this is the first packet
dwCurrentSBIT = (DWORD)(dwHeaderHigh & 0xE0000000) >> 29; if ((*pdwPktCount) && (((dwCurrentSBIT + g_dwPreviousEBIT) != 8) && (((dwCurrentSBIT + g_dwPreviousEBIT) != 0)))) DebugBreak();
g_dwPreviousEBIT = (dwHeaderHigh & 0x1C000000) >> 26; #endif // } VALIDATE_SBIT_EBIT
// Update the packet size
if ((pbsiT->dwNumOfPackets - *pdwPktCount - i) >= 1) *pdwPktSize = (((pbsi261+i)->dwBitOffset - 1) / 8) - (pbsi261->dwBitOffset / 8) + 1 + 4; else *pdwPktSize = pbsiT->dwCompressedSize - pbsi261->dwBitOffset / 8 + 4;
// Update the packet count
*pdwPktCount += i;
}
#if 0
// Save the header right before the data chunk
*ppDataPkt = pDataSrc + (pbsi261->dwBitOffset / 8) - 4;
// Convert to network byte order
*((BYTE *)*ppDataPkt+3) = (BYTE)(dwHeaderHigh & 0x000000FF); *((BYTE *)*ppDataPkt+2) = (BYTE)((dwHeaderHigh >> 8) & 0x000000FF); *((BYTE *)*ppDataPkt+1) = (BYTE)((dwHeaderHigh >> 16) & 0x000000FF); *((BYTE *)*ppDataPkt) = (BYTE)((dwHeaderHigh >> 24) & 0x000000FF); #else
// Save the header right before the data chunk
*ppDataPkt = pDataSrc + (pbsi261->dwBitOffset / 8) - 4; *pdwHdrSize=4;
// Convert to network byte order
*((BYTE *)*pHdrInfo+3) = (BYTE)(dwHeaderHigh & 0x000000FF); *((BYTE *)*pHdrInfo+2) = (BYTE)((dwHeaderHigh >> 8) & 0x000000FF); *((BYTE *)*pHdrInfo+1) = (BYTE)((dwHeaderHigh >> 16) & 0x000000FF); *((BYTE *)*pHdrInfo) = (BYTE)((dwHeaderHigh >> 24) & 0x000000FF); #endif
#ifdef LOGPAYLOAD_ON
// Output some debug stuff
wsprintf(szDebug, "Packet: %02lX\r\n Header: %02lX %02lX %02lX %02lX\r\n dword1: %02lX %02lX %02lX %02lX\r\n dword2: %02lX %02lX %02lX %02lX\r\n", *pdwPktCount, *((BYTE *)*ppDataPkt), *((BYTE *)*ppDataPkt+1), *((BYTE *)*ppDataPkt+2), *((BYTE *)*ppDataPkt+3), *((BYTE *)*ppDataPkt+4), *((BYTE *)*ppDataPkt+5), *((BYTE *)*ppDataPkt+6), *((BYTE *)*ppDataPkt+7), *((BYTE *)*ppDataPkt+8), *((BYTE *)*ppDataPkt+9), *((BYTE *)*ppDataPkt+10), *((BYTE *)*ppDataPkt+11)); OutputDebugString(szDebug); if (*pdwPktCount == pbsiT->dwNumOfPackets) wsprintf(szDebug, " Tail : %02lX %02lX XX XX\r\n", *((BYTE *)*ppDataPkt+*pdwPktSize-2), *((BYTE *)*ppDataPkt+*pdwPktSize-1)); else wsprintf(szDebug, " Tail : %02lX %02lX %02lX %02lX\r\n", *((BYTE *)*ppDataPkt+*pdwPktSize-2), *((BYTE *)*ppDataPkt+*pdwPktSize-1), *((BYTE *)*ppDataPkt+*pdwPktSize), *((BYTE *)*ppDataPkt+*pdwPktSize+1)); OutputDebugString(szDebug); if (*pfMark == 1) wsprintf(szDebug, " Marker: ON\r\n"); else wsprintf(szDebug, " Marker: OFF\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, "Frame #%03ld, Packet of size %04ld\r\n", (DWORD)pbsiT->byTR, *pdwPktSize); OutputDebugString(szDebug); #endif
} else return ((MMRESULT)VCMERR_NOMOREPACKETS); } else { if (!*pdwPktCount) { *pdwPktCount = 1; *pdwHdrSize = 0; } else return ((MMRESULT)VCMERR_NOMOREPACKETS); }
return ((MMRESULT)MMSYSERR_NOERROR); }
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmStreamGetPayloadHeaderSize | This function gets the size * of the RTP payload header associated to a video codec. * * @parm HVCMSTREAM | hvs | Specifies the conversion stream. * * @parm PDWORD | pdwPayloadHeaderSize | Specifies a pointer to the payload header size. * * @rdesc The return value is zero if the function is successful. Otherwise, it returns * an error number. Possible error values include the following: * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * @flag MMSYSERR_INVALPARAM | Specified saturation value is invalid. * * @xref <f vcmStreamFormatPayload> ***************************************************************************/ MMRESULT VCMAPI vcmStreamGetPayloadHeaderSize(HVCMSTREAM hvs, PDWORD pdwPayloadHeaderSize) { PVCMSTREAM pvs = (PVCMSTREAM)hvs;
// Check input params
if (!hvs) { ERRORMESSAGE(("vcmStreamGetPayloadHeaderSize: Specified handle is invalid, hvs=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALHANDLE); } if (!pdwPayloadHeaderSize) { ERRORMESSAGE(("vcmStreamGetPayloadHeaderSize: Specified pointer is invalid, pdwPayloadHeaderSize=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); }
// Set default payload header size to 0
*pdwPayloadHeaderSize = 0;
// The name of the codec will tell us how to get to the payload header size info
#ifndef _ALPHA_
#ifdef USE_BILINEAR_MSH26X
if ((pvs->pvfxDst->dwFormatTag == VIDEO_FORMAT_MSH263) || (pvs->pvfxDst->dwFormatTag == VIDEO_FORMAT_MSH26X)) #else
if (pvs->pvfxDst->dwFormatTag == VIDEO_FORMAT_MSH263) #endif
#else
if (pvs->pvfxDst->dwFormatTag == VIDEO_FORMAT_DECH263) #endif
{ // H.263 has a max payload header size of 12 bytes
*pdwPayloadHeaderSize = 12; } #ifndef _ALPHA_
else if (pvs->pvfxDst->dwFormatTag == VIDEO_FORMAT_MSH261) #else
else if (pvs->pvfxDst->dwFormatTag == VIDEO_FORMAT_DECH261) #endif
{ // H.261 has a unique payload header size of 4 bytes
*pdwPayloadHeaderSize = 4; }
return ((MMRESULT)MMSYSERR_NOERROR); }
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmStreamRequestIFrame | This function forces the * codec to generate an I-Frame. * * @parm HVCMSTREAM | hvs | Specifies the conversion stream. * * @rdesc The return value is zero if the function is successful. Otherwise, it returns * an error number. Possible error values include the following: * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * ***************************************************************************/ MMRESULT VCMAPI vcmStreamRequestIFrame(HVCMSTREAM hvs) { PVCMSTREAM pvs = (PVCMSTREAM)hvs;
// Check input params
if (!hvs) { ERRORMESSAGE(("vcmStreamRequestIFrame: Specified handle is invalid, hvs=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALHANDLE); }
DEBUGMSG (ZONE_VCM, ("vcmStreamRequestIFrame: Requesting an I-Frame...\r\n"));
// We need the following crs to make sure we don't miss any of the I-Frame requests
// emitted by the UI. Problematic scenario: pvs->dwFrame is at 123 for instance.
// The UI thread requests an I-Frame by setting pvs->dwFrame to 0. If the capture/compression
// thread was in ICCompress() (which is very probable since it takes quite some time
// to compress a frame), pvs->dwFrame will be incremented by one when ICCompress()
// returns. We fail to handle the I-Frame request correctly, since the next time
// ICCompress() gets called pvs->dwFrame will be equal to 1, for which we do not
// generate an I-Frame.
EnterCriticalSection(&pvs->crsFrameNumber);
// Set the frame number to 0. This will force the codec to generate an I-Frame
pvs->dwFrame = 0;
// Allow the capture/compression thread to proceed.
LeaveCriticalSection(&pvs->crsFrameNumber);
return ((MMRESULT)MMSYSERR_NOERROR); }
/****************************************************************************
* @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmStreamPeriodicIFrames | This function enables or * disables generation of I-Frames periodically. * * @parm HVCMSTREAM | hvs | Specifies the conversion stream. * * @parm BOOL | fPeriodicIFrames | Set to TRUE to generate I-Frames * periodically, FALSE otherwise. * * @rdesc The return value is zero if the function is successful. Otherwise, it returns * an error number. Possible error values include the following: * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * ***************************************************************************/ MMRESULT VCMAPI vcmStreamPeriodicIFrames(HVCMSTREAM hvs, BOOL fPeriodicIFrames) { PVCMSTREAM pvs = (PVCMSTREAM)hvs;
// Check input params
if (!hvs) { ERRORMESSAGE(("vcmStreamDisablePeriodicIFrames: Specified handle is invalid, hvs=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALHANDLE); }
DEBUGMSG (ZONE_VCM, ("vcmStreamDisablePeriodicIFrames: Disabling periodic generation of I-Frames...\r\n"));
// No more periodic I-Frames
pvs->fPeriodicIFrames = fPeriodicIFrames;
return ((MMRESULT)MMSYSERR_NOERROR); }
// frees memory prior to shutdown
MMRESULT VCMAPI vcmReleaseResources() { if (g_aVCMAppInfo) { MemFree(g_aVCMAppInfo); g_aVCMAppInfo = NULL; }
return MMSYSERR_NOERROR;
}
|