#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
#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
// 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
. This argument may be NULL for some
* queries.
*
* @parm UINT | uMetric | Specifies the metric index to be returned in
*
.
*
* @flag VCM_METRIC_COUNT_COMPRESSORS | Specifies that the returned value is
* the number of global VCM compressors in
* the system. The
argument must be NULL for this metric index.
* The
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
argument must be NULL for this metric index.
* The
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 structure. If
* is NULL, then the return value is the largest
* structure in the system. If identifies an open instance
* of an VCM driver () or a VCM driver identifier
* (), then the largest
* structure for that driver is returned. The argument must
* point to a buffer of a size equal to a DWORD. This metric is not allowed
* for a VCM stream handle ().
*
* @parm LPVOID | pMetric | Specifies a pointer to the buffer that will
* receive the metric details. The exact definition depends on the
* 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
parameter is invalid.
* @flag MMSYSERR_NOTSUPPORTED | The
index is not supported.
* @flag VCMERR_NOTPOSSIBLE | The
index cannot be returned
* for the specified
.
*
***************************************************************************/
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
* structure that will receive the driver details. The
* member must be initialized to the
* size, in bytes, of the structure. The 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 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
***************************************************************************/
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
* structure that is to receive the format
* details for the given embedded pointer to a 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
***************************************************************************/
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
* structure. This structure is filled with information about the
* capabilities of the device.
*
* @parm UINT | cbvc | Specifies the size of the 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 bytes (or less) of information is copied to the location
* pointed to by
. If
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
****************************************************************************/
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 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;jdwFormatTag == 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;jdwFormatTag) {
//Ok, add the registry size, if we don't have it listed
bKnown=FALSE;
for (k=0;kvideo_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 function
* enumerates video formats available. The
* 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
* structure that is to receive the driver details
* passed to the function.
*
* @parm PVCMFORMATDETAILS | pvfd | Specifies a pointer to the
* structure that is to receive the format details
* passed to the function. This structure must have the
* , , and
* members of the
* structure initialized. The 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 function will return MMSYSERR_NOERROR
* (zero) if no suitable VCM drivers are installed. Moreover, the
* callback function will not be called.
*
* @xref
***************************************************************************/
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= 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; kdwFlags = 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; mvfx.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; mpvfx, &(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
* 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 format. Note
* that based on the
argument, some members of the structure
* pointed to by
may require initialization.
*
* @parm PVIDEOFORMATEX | pvfxDst | Specifies a pointer to a
* data structure that will receive the suggested destination format
* for the format, or that identifies the destination format to
* suggest a recommended source format to be used for a conversion. Note
* that based on the
argument, some members of the structure
* pointed to by
may require initialization.
*
* @parm DWORD | cbvfxDst | Specifies the size in bytes available for
* the destination, or the source format. The
* 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
* member of the structure is
* valid. The VCM will query acceptable installed drivers that can
* use the
structure as their source format and output a
* destination format matching the
* member, or fail. The structure is updated with the complete
* destination format.
*
* @flag VCM_FORMATSUGGESTF_DST_NSAMPLESPERSEC | Specifies that the
* member of the structure
* is valid. The VCM will query acceptable installed drivers that can
* use the
structure as their source format and output a
* destination format matching the
* member, or fail. The structure is updated with the complete
* destination format.
*
* @flag VCM_FORMATSUGGESTF_DST_WBITSPERSAMPLE | Specifies that the
* member of the structure
* is valid. The VCM will query acceptable installed drivers that can
* use the
structure as their source format and output a
* destination format matching the
* member, or fail. The structure is updated with the complete
* destination format.
*
* @flag VCM_FORMATSUGGESTF_SRC_WFORMATTAG | Specifies that the
* member of the structure is
* valid. The VCM will query acceptable installed drivers that can
* use the
structure as their destination format and accept a
* source format matching the
* member, or fail. The structure is updated with the complete
* source format.
*
* @flag VCM_FORMATSUGGESTF_SRC_NSAMPLESPERSEC | Specifies that the
* member of the structure
* is valid. The VCM will query acceptable installed drivers that can
* use the
structure as their destination format and accept a
* source format matching the
* member or fail. The structure is updated with the complete
* source format.
*
* @flag VCM_FORMATSUGGESTF_SRC_WBITSPERSAMPLE | Specifies that the
* member of the structure
* is valid. The VCM will query acceptable installed drivers that can
* use the
structure as their destination format and accept a
* source format matching the
* member, or fail. The 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
structure, or we can't, but still, once compressed
* the output frame has the same size than the one in the
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
***************************************************************************/
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; ibih.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; ibih.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
* 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
* structure that identifies the desired source format for the
* conversion.
*
* @parm PVIDEOFORMATEX | pvfxDst | Specifies a pointer to a
* 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 . 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 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
* of the structure for the VCMSTREAMHEADER_STATUSF_DONE
* flag.
*
* @flag CALLBACK_WINDOW | Specifies that is assumed to
* be a window handle.
*
* @flag CALLBACK_FUNCTION | Specifies that
is assumed to
* be a callback procedure address. The function prototype must conform
* to the 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 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: ,
* , and . The parameter identifies
* the handle. The parameter identifies the
* structure for , but is not used
* for and .
*
* If a function is chosen to receive callback information, the
* following messages are sent to the function to indicate the progress
* of output: , , and
* . The callback function must reside in a DLL. You do
* not need to use to get a procedure-instance
* address for the callback function.
*
* @xref
*
***************************************************************************/
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
***************************************************************************/
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; idwFrame && 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; idwFrame && 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 flags specify what the
* input argument defines. This argument must be non-zero.
*
* @parm LPDWORD | pdwOutputBytes | Specifies a pointer to a
* that contains the size in bytes of the source or destination buffer.
* The flags specify what the output argument defines.
* If the 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 contains
* the size of the source buffer. The
argument will
* receive the recommended destination buffer size in bytes.
*
* @flag VCM_STREAMSIZEF_DESTINATION | Indicates that
* contains the size of the destination buffer. The
* 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 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
* will receive zero. This assumes that the pointer
* specified by
is valid.
*
* @xref
***************************************************************************/
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
***************************************************************************/
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 message range
* (above or equal to and less than
* ). The exception to this restriction is
* the 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 sent. However, the following return values are
* possible:
* @flag MMSYSERR_INVALHANDLE | Specified handle is invalid.
* @flag MMSYSERR_INVALPARAM |
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
* 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 is currently in use
* and cannot be reused.
*
* @flag VCMERR_UNPREPARED | The stream header
is currently
* not prepared by the function.
*
* @comm The source and destination data buffers must be prepared with
* before they are passed to .
*
* If an asynchronous conversion request is successfully queued by
* the VCM or driver, and later the conversion is determined to
* be impossible, then the will be posted back to
* the application's callback with the
* member set to zero.
*
* @xref
*
***************************************************************************/
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 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
* 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
***************************************************************************/
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
* 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
* 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 is currently in use
* and cannot be unprepared.
* @flag VCMERR_UNPREPARED | The stream header
was
* not prepared by the function.
*
* @comm Unpreparing a stream header that has already been unprepared is
* an error. An application must specify the source and destination
* buffer lengths ( and
* respectively) that were used
* during the corresponding call. Failing
* to reset these member values will cause
* 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 returns success.
*
* @xref
***************************************************************************/
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
* structure. This structure is filled with information about the
* capabilities of the device.
*
* @parm UINT | cbvc | Specifies the size of the 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 bytes (or less) of information is copied to the location
* pointed to by
. If
is zero, nothing is copied, and
* the function returns zero.
*
* @xref
****************************************************************************/
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 {
lstrcpyn(szKey, szDeviceName, ARRAYSIZE(szKey));
}
// 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
* structure. This structure is filled with information about the
* capabilities of the device.
*
* @parm UINT | cbvc | Specifies the size of the 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 bytes (or less) of information is copied to the location
* pointed to by
. If
is zero, nothing is copied, and
* the function returns zero.
*
* @xref
****************************************************************************/
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; lbmiHeader.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
* structure. This structure is filled with information about the
* capabilities of the device.
*
* @parm UINT | cbvc | Specifies the size of the 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 bytes (or less) of information is copied to the location
* pointed to by
. If
is zero, nothing is copied, and
* the function returns zero.
*
* @xref
****************************************************************************/
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 {
lstrcpyn(szKey, szDeviceName, ARRAYSIZE(szKey));
}
// 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 {
lstrcpyn(szKey, szDeviceName, ARRAYSIZE(szKey));
}
// 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
****************************************************************************/
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
****************************************************************************/
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
****************************************************************************/
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
***************************************************************************/
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
***************************************************************************/
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
***************************************************************************/
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
***************************************************************************/
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
***************************************************************************/
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
***************************************************************************/
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
***************************************************************************/
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 parameter should be initialized to the maximum frame
* size, before calling the function.
*
* @xref
***************************************************************************/
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
lstrcpyn(
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",
ARRAYSIZE(szTDebug));
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
lstrcpyn(
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",
ARRAYSIZE(szTDebug));
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
lstrcpyn(
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",
ARRAYSIZE(szTDebug));
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
***************************************************************************/
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;
}