#include "precomp.h" // #define LOG_COMPRESSION_PARAMS 1 // #define LOGPAYLOAD_ON 1 #ifdef LOGPAYLOAD_ON HANDLE g_DebugFile = (HANDLE)NULL; HANDLE g_TDebugFile = (HANDLE)NULL; #endif // #define VALIDATE_SBIT_EBIT 1 #ifdef VALIDATE_SBIT_EBIT // { VALIDATE_SBIT_EBIT DWORD g_dwPreviousEBIT = 0; #endif // } VALIDATE_SBIT_EBIT #define BUFFER_SIZE 50 #define NUM_FPS_ENTRIES 1 #define NUM_BITDEPTH_ENTRIES 9 #define NUM_RGB_BITDEPTH_ENTRIES 4 #define VIDEO_FORMAT_NUM_RESOLUTIONS 6 #define MAX_NUM_REGISTERED_SIZES 3 #define MAX_VERSION 80 // Needs to be in sync with the MAX_VERSION in dcap\inc\idcap.h // String resources #define IDS_FORMAT_1 TEXT("%4.4hs.%4.4hs, %02dbit, %02dfps, %03dx%03d") #define IDS_FORMAT_2 TEXT("%4.4hs.%04d, %02dbit, %02dfps, %03dx%03d") #define szRegDeviceKey TEXT("SOFTWARE\\Microsoft\\Conferencing\\CaptureDevices") #define szRegCaptureDefaultKey TEXT("SOFTWARE\\Microsoft\\Conferencing\\CaptureDefaultFormats") #define szRegConferencingKey TEXT("SOFTWARE\\Microsoft\\Conferencing") #define szTotalRegDeviceKey TEXT("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Conferencing\\CaptureDevices") #define szRegCaptureKey TEXT("CaptureDevices") #define szRegdwImageSizeKey TEXT("dwImageSize") #define szRegImageSizesKey TEXT("aImageSizes") #define szRegNumImageSizesKey TEXT("nNumSizes") #define szRegdwNumColorsKey TEXT("dwNumColors") #define szRegdwStreamingModeKey TEXT("dwStreamingMode") #define szRegdwDialogsKey TEXT("dwDialogs") #define szRegbmi4bitColorsKey TEXT("bmi4bitColors") #define szRegbmi8bitColorsKey TEXT("bmi8bitColors") #define szRegDefaultFormatKey TEXT("DefaultFormat") EXTERN_C HINSTANCE g_hInst; // Our module handle. defined in nac.cpp //External function (in msiacaps.cpp) to read reg info in one shot extern ULONG ReadRegistryFormats (LPCSTR lpszKeyName,CHAR ***pppName,BYTE ***pppData,PUINT pnFormats,DWORD dwDebugSize); PVCM_APP_ICINFO g_aVCMAppInfo; int g_nNumVCMAppInfoEntries; int g_nNumFrameSizesEntries; BOOL g_fNewCodecsInstalled; #ifdef LOGFILE_ON DWORD g_CompressTime; DWORD g_DecompressTime; HANDLE g_CompressLogFile; HANDLE g_DecompressLogFile; DWORD g_dwCompressBytesWritten; DWORD g_dwDecompressBytesWritten; char g_szCompressBuffer[256]; char g_szDecompressBuffer[256]; DWORD g_OrigCompressTime; DWORD g_OrigDecompressTime; DWORD g_AvgCompressTime; DWORD g_AvgDecompressTime; DWORD g_aCompressTime[4096]; DWORD g_aDecompressTime[4096]; SYSTEMTIME g_SystemTime; #endif typedef struct tagDejaVu { VIDEOFORMATEX vfx; DWORD dwFlags; } DEJAVU, *PDEJAVU; #if 1 // Array of known ITU sizes MYFRAMESIZE g_ITUSizes[8] = { { 0, 0 }, { 128, 96 }, { 176, 144 }, { 352, 288 }, #if !defined(_ALPHA_) && defined(USE_BILINEAR_MSH26X) { 80, 64 }, #else { 704, 576 }, #endif { 1408,1152 }, { 0, 0 }, { 0, 0 } }; // For now, the size of the VIDEOFORMATEX being 1118 even if // there is no palette, do not enumerate all of the possible // formats. As soon as you have replaced the BITMAPINFOHEADER // + Palette by pointers to such structure, enable all the // sizes. NCAP_APP_INFO g_awResolutions[VIDEO_FORMAT_NUM_RESOLUTIONS] = { // VIDEO_FORMAT_IMAGE_SIZE_40_30, // VIDEO_FORMAT_IMAGE_SIZE_64_48, // VIDEO_FORMAT_IMAGE_SIZE_80_60, // VIDEO_FORMAT_IMAGE_SIZE_96_64, // VIDEO_FORMAT_IMAGE_SIZE_112_80, // VIDEO_FORMAT_IMAGE_SIZE_120_90, { VIDEO_FORMAT_IMAGE_SIZE_128_96, 128, 96 }, // VIDEO_FORMAT_IMAGE_SIZE_144_112, { VIDEO_FORMAT_IMAGE_SIZE_160_120, 160, 120 }, // VIDEO_FORMAT_IMAGE_SIZE_160_128, { VIDEO_FORMAT_IMAGE_SIZE_176_144, 176, 144 }, // VIDEO_FORMAT_IMAGE_SIZE_192_160, // VIDEO_FORMAT_IMAGE_SIZE_200_150, // VIDEO_FORMAT_IMAGE_SIZE_208_176, // VIDEO_FORMAT_IMAGE_SIZE_224_192, { VIDEO_FORMAT_IMAGE_SIZE_240_180, 240, 180 }, // VIDEO_FORMAT_IMAGE_SIZE_240_208, // VIDEO_FORMAT_IMAGE_SIZE_256_224, // VIDEO_FORMAT_IMAGE_SIZE_272_240, // VIDEO_FORMAT_IMAGE_SIZE_280_210, // VIDEO_FORMAT_IMAGE_SIZE_288_256, // VIDEO_FORMAT_IMAGE_SIZE_304_272, { VIDEO_FORMAT_IMAGE_SIZE_320_240, 320, 240 }, // VIDEO_FORMAT_IMAGE_SIZE_320_288, // VIDEO_FORMAT_IMAGE_SIZE_336_288, { VIDEO_FORMAT_IMAGE_SIZE_352_288, 352, 288 }, // VIDEO_FORMAT_IMAGE_SIZE_640_480, }; #else // For now, the size of the VIDEOFORMATEX being 1118 even if // there is no palette, do not enumerate all of the possible // formats. As soon as you have replaced the BITMAPINFOHEADER // + Palette by pointers to such structure, enable all the // sizes. DWORD g_awResolutions[VIDEO_FORMAT_NUM_RESOLUTIONS] = { // VIDEO_FORMAT_IMAGE_SIZE_40_30, // VIDEO_FORMAT_IMAGE_SIZE_64_48, // VIDEO_FORMAT_IMAGE_SIZE_80_60, // VIDEO_FORMAT_IMAGE_SIZE_96_64, // VIDEO_FORMAT_IMAGE_SIZE_112_80, // VIDEO_FORMAT_IMAGE_SIZE_120_90, VIDEO_FORMAT_IMAGE_SIZE_160_120, // VIDEO_FORMAT_IMAGE_SIZE_144_112, VIDEO_FORMAT_IMAGE_SIZE_128_96, // VIDEO_FORMAT_IMAGE_SIZE_160_128, VIDEO_FORMAT_IMAGE_SIZE_240_180, // VIDEO_FORMAT_IMAGE_SIZE_192_160, // VIDEO_FORMAT_IMAGE_SIZE_200_150, // VIDEO_FORMAT_IMAGE_SIZE_208_176, // VIDEO_FORMAT_IMAGE_SIZE_224_192, VIDEO_FORMAT_IMAGE_SIZE_176_144, // VIDEO_FORMAT_IMAGE_SIZE_240_208, // VIDEO_FORMAT_IMAGE_SIZE_256_224, // VIDEO_FORMAT_IMAGE_SIZE_272_240, // VIDEO_FORMAT_IMAGE_SIZE_280_210, // VIDEO_FORMAT_IMAGE_SIZE_288_256, // VIDEO_FORMAT_IMAGE_SIZE_304_272, VIDEO_FORMAT_IMAGE_SIZE_320_240, // VIDEO_FORMAT_IMAGE_SIZE_320_288, // VIDEO_FORMAT_IMAGE_SIZE_336_288, VIDEO_FORMAT_IMAGE_SIZE_352_288, // VIDEO_FORMAT_IMAGE_SIZE_640_480, }; #endif //int g_aiFps[NUM_FPS_ENTRIES] = {3, 7, 15}; int g_aiFps[NUM_FPS_ENTRIES] = {30}; // The order of the bit depths matches what I think is the // preferred format if more than one is supported. // For color, 16bit is almost as good as 24 but uses less memory // and is faster for color QuickCam. // For greyscale, 16 greyscale levels is Ok, not as good as 64, // but Greyscale QuickCam is too slow at 64 levels. int g_aiBitDepth[NUM_BITDEPTH_ENTRIES] = {9, 12, 12, 16, 16, 16, 24, 4, 8}; int g_aiNumColors[NUM_BITDEPTH_ENTRIES] = {VIDEO_FORMAT_NUM_COLORS_YVU9, VIDEO_FORMAT_NUM_COLORS_I420, VIDEO_FORMAT_NUM_COLORS_IYUV, VIDEO_FORMAT_NUM_COLORS_YUY2, VIDEO_FORMAT_NUM_COLORS_UYVY, VIDEO_FORMAT_NUM_COLORS_65536, VIDEO_FORMAT_NUM_COLORS_16777216, VIDEO_FORMAT_NUM_COLORS_16, VIDEO_FORMAT_NUM_COLORS_256}; int g_aiFourCCCode[NUM_BITDEPTH_ENTRIES] = {VIDEO_FORMAT_YVU9, VIDEO_FORMAT_I420, VIDEO_FORMAT_IYUV, VIDEO_FORMAT_YUY2, VIDEO_FORMAT_UYVY, VIDEO_FORMAT_BI_RGB, VIDEO_FORMAT_BI_RGB, VIDEO_FORMAT_BI_RGB, VIDEO_FORMAT_BI_RGB}; int g_aiClrUsed[NUM_BITDEPTH_ENTRIES] = {0, 0, 0, 0, 0, 0, 0, 16, 256}; PVCMSTREAMHEADER DeQueVCMHeader(PVCMSTREAM pvs); MMRESULT VCMAPI vcmDefaultFormatWriteToReg(LPSTR szDeviceName, LPSTR szDeviceVersion, LPBITMAPINFOHEADER lpbmih); #define IsVCMHeaderPrepared(pvh) ((pvh)->fdwStatus & VCMSTREAMHEADER_STATUSF_PREPARED) #define MarkVCMHeaderPrepared(pvh) ((pvh)->fdwStatus |= VCMSTREAMHEADER_STATUSF_PREPARED) #define MarkVCMHeaderUnprepared(pvh) ((pvh)->fdwStatus &=~VCMSTREAMHEADER_STATUSF_PREPARED) #define IsVCMHeaderInQueue(pvh) ((pvh)->fdwStatus & VCMSTREAMHEADER_STATUSF_INQUEUE) #define MarkVCMHeaderInQueue(pvh) ((pvh)->fdwStatus |= VCMSTREAMHEADER_STATUSF_INQUEUE) #define MarkVCMHeaderUnQueued(pvh) ((pvh)->fdwStatus &=~VCMSTREAMHEADER_STATUSF_INQUEUE) #define IsVCMHeaderDone(pvh) ((pvh)->fdwStatus & VCMSTREAMHEADER_STATUSF_DONE) #define MarkVCMHeaderDone(pvh) ((pvh)->fdwStatus |= VCMSTREAMHEADER_STATUSF_DONE) #define MarkVCMHeaderNotDone(pvh) ((pvh)->fdwStatus &=~VCMSTREAMHEADER_STATUSF_DONE) /**************************************************************************** * @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmMetrics | This function returns various metrics for the Video * Compression Manager (VCM) or related VCM objects. * * @parm HVCMOBJ | hvo | Specifies the VCM object to query for the metric * specified in

. 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 { wsprintf(szKey, "%s", szDeviceName); } // Check if there already is a key for the current device if (RegOpenKey(hDeviceKey, szKey, &hKey) != ERROR_SUCCESS) { mmr = (MMRESULT)VCMERR_NOREGENTRY; goto MyExit0; } // Get the values stored in the key dwSize = sizeof(DWORD); RegQueryValueEx(hKey, (LPTSTR)szRegdwImageSizeKey, NULL, &dwType, (LPBYTE)&pvc->dwImageSize, &dwSize); dwSize = sizeof(DWORD); RegQueryValueEx(hKey, (LPTSTR)szRegdwNumColorsKey, NULL, &dwType, (LPBYTE)&pvc->dwNumColors, &dwSize); dwSize = sizeof(DWORD); pvc->dwStreamingMode = STREAMING_PREFER_FRAME_GRAB; RegQueryValueEx(hKey, (LPTSTR)szRegdwStreamingModeKey, NULL, &dwType, (LPBYTE)&pvc->dwStreamingMode, &dwSize); dwSize = sizeof(DWORD); pvc->dwDialogs = FORMAT_DLG_OFF | SOURCE_DLG_ON; RegQueryValueEx(hKey, (LPTSTR)szRegdwDialogsKey, NULL, &dwType, (LPBYTE)&pvc->dwDialogs, &dwSize); // Check dwNumColors to figure out if we need to read the palettes too if (pvc->dwNumColors & VIDEO_FORMAT_NUM_COLORS_16) { dwSize = NUM_4BIT_ENTRIES * sizeof(RGBQUAD); if (RegQueryValueEx(hKey, (LPTSTR)szRegbmi4bitColorsKey, NULL, &dwType, (LPBYTE)&pvc->bmi4bitColors[0], &dwSize) == ERROR_SUCCESS) { pvc->dwFlags |= VICF_4BIT_TABLE; } else FillMemory ((LPBYTE)&pvc->bmi4bitColors[0], NUM_4BIT_ENTRIES * sizeof(RGBQUAD), 0); } if (pvc->dwNumColors & VIDEO_FORMAT_NUM_COLORS_256) { dwSize = NUM_8BIT_ENTRIES * sizeof(RGBQUAD); if (RegQueryValueEx(hKey, (LPTSTR)szRegbmi8bitColorsKey, NULL, &dwType, (LPBYTE)&pvc->bmi8bitColors[0], &dwSize) == ERROR_SUCCESS) { pvc->dwFlags |= VICF_8BIT_TABLE; } else FillMemory ((LPBYTE)&pvc->bmi8bitColors[0], NUM_8BIT_ENTRIES * sizeof(RGBQUAD), 0); } // Close the registry keys RegCloseKey(hKey); MyExit0: RegCloseKey(hDeviceKey); return (mmr); } /***************************************************************************** * @doc INTERNAL DEVCAPSFUNC * * @func MMRESULT | vcmDevCapsProfile | This function profiles the video capture * input device to figure out its capabilities. * * @parm PVIDEOINCAPS | pvc | Specifies a pointer to a * 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 { wsprintf(szKey, "%s", szDeviceName); } // Check if there already is a key for the current device // Open the key for the current device, or create the key if it doesn't exist if (RegCreateKeyEx(hDeviceKey, szKey, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, &dwDisposition) != ERROR_SUCCESS) return ((MMRESULT)VCMERR_NOREGENTRY); // Set the values in the key dwSize = sizeof(DWORD); RegSetValueEx(hKey, (LPTSTR)szRegdwImageSizeKey, (DWORD)NULL, REG_DWORD, (LPBYTE)&pvc->dwImageSize, dwSize); dwSize = sizeof(DWORD); RegSetValueEx(hKey, (LPTSTR)szRegdwNumColorsKey, (DWORD)NULL, REG_DWORD, (LPBYTE)&pvc->dwNumColors, dwSize); dwSize = sizeof(DWORD); RegSetValueEx(hKey, (LPTSTR)szRegdwStreamingModeKey, (DWORD)NULL, REG_DWORD, (LPBYTE)&pvc->dwStreamingMode, dwSize); dwSize = sizeof(DWORD); RegSetValueEx(hKey, (LPTSTR)szRegdwDialogsKey, (DWORD)NULL, REG_DWORD, (LPBYTE)&pvc->dwDialogs, dwSize); // Check dwNumColors to figure out if we need to set the palettes too if (pvc->dwNumColors & VIDEO_FORMAT_NUM_COLORS_16) { dwSize = NUM_4BIT_ENTRIES * sizeof(RGBQUAD); RegSetValueEx(hKey, (LPTSTR)szRegbmi4bitColorsKey, (DWORD)NULL, REG_BINARY, (LPBYTE)&pvc->bmi4bitColors[0], dwSize); } if (pvc->dwNumColors & VIDEO_FORMAT_NUM_COLORS_256) { dwSize = NUM_8BIT_ENTRIES * sizeof(RGBQUAD); RegSetValueEx(hKey, (LPTSTR)szRegbmi8bitColorsKey, (DWORD)NULL, REG_BINARY, (LPBYTE)&pvc->bmi8bitColors[0], dwSize); } // Close the keys RegCloseKey(hKey); RegCloseKey(hDeviceKey); return ((MMRESULT)MMSYSERR_NOERROR); } MMRESULT VCMAPI vcmDefaultFormatWriteToReg(LPSTR szDeviceName, LPSTR szDeviceVersion, LPBITMAPINFOHEADER lpbmih) { HKEY hDeviceKey; HKEY hKey; DWORD dwDisposition; DWORD dwSize; char szKey[MAX_PATH + MAX_VERSION + 2]; char szFOURCC[5]; // Check input params if (!szDeviceName) { ERRORMESSAGE(("vcmDefaultFormatWriteToReg: Specified pointer is invalid, szDeviceName=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if (szDeviceName[0] == '\0') { ERRORMESSAGE(("vcmDefaultFormatWriteToReg: Video capture input device driver name is empty\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } if (!lpbmih) { ERRORMESSAGE(("vcmDefaultFormatWriteToReg: Specified pointer is invalid, lpbmih=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } // Open the main capture devices key, or create it if it doesn't exist if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, szRegCaptureDefaultKey, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hDeviceKey, &dwDisposition) != ERROR_SUCCESS) return ((MMRESULT)VCMERR_NOREGENTRY); //If we have version info use that to build the key name if (szDeviceVersion && szDeviceVersion[0] != '\0') { wsprintf(szKey, "%s, %s", szDeviceName, szDeviceVersion); } else { wsprintf(szKey, "%s", szDeviceName); } // Check if there already is a key for the current device // Open the key for the current device, or create the key if it doesn't exist if (RegCreateKeyEx(hDeviceKey, szKey, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, &dwDisposition) != ERROR_SUCCESS) return ((MMRESULT)VCMERR_NOREGENTRY); if (lpbmih->biCompression == BI_RGB) wsprintf(szFOURCC, "RGB"); else { *((DWORD*)&szFOURCC) = lpbmih->biCompression; szFOURCC[4] = '\0'; } dwSize = wsprintf(szKey, "%s, %dx%dx%d", szFOURCC, lpbmih->biWidth, lpbmih->biHeight, lpbmih->biBitCount); RegSetValueEx(hKey, (LPTSTR)szRegDefaultFormatKey, (DWORD)NULL, REG_SZ, (CONST BYTE *)szKey, dwSize+1); // Close the keys RegCloseKey(hKey); RegCloseKey(hDeviceKey); return ((MMRESULT)MMSYSERR_NOERROR); } /***************************************************************************** * @doc EXTERNAL DEVCAPSFUNC * * @func MMRESULT | vcmGetDevCapsPreferredFormatTag | This function queries a specified * video capture input device to determine the format tag it will be effectively * capturing at. * * @parm UINT | uDevice | Specifies the video capture input device ID. * * @parm PINT | pbiWidth | Specifies a pointer to the actual width * the capture will be performed at. * * @parm PINT | pbiHeight | Specifies a pointer to the actual height * the capture will be performed at. * * @rdesc The return value is zero if the function is successful. Otherwise, it returns * an error number. Possible error values include the following: * @flag MMSYSERR_INVALPARAM | Specified pointer to structure is invalid. * @flag MMSYSERR_BADDEVICEID | Specified device device ID is invalid. * @flag VCMERR_NONSPECIFIC | The capture driver failed to provide valid information. * * @xref ****************************************************************************/ 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 wsprintf(szTDebug, (ppDataPkt->len >= dwHeaderSize) ? "VCMSTRM: Cumulative size of the reassembled packets is too large: discarding frame!\r\n" : "VCMSTRM: Packet length is smaller than payload header size: discarding frame!\r\n"); OutputDebugString(szTDebug); #endif return ((MMRESULT)VCMERR_NONSPECIFIC); } ppDataPkt++; } #ifdef LOGPAYLOAD_ON g_TDebugFile = CreateFile("C:\\RecvLog.txt", GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); SetFilePointer(g_TDebugFile, 0, NULL, FILE_END); wsprintf(szTDebug, "Frame #%03ld\r\n", (DWORD)j); WriteFile(g_TDebugFile, szTDebug, strlen(szTDebug), &d, NULL); for (j=*pdwFrameSize; j>0; j-=4, p+=4) { wsprintf(szTDebug, "%02lX %02lX %02lX %02lX\r\n", *((BYTE *)p), *((BYTE *)p+1), *((BYTE *)p+2), *((BYTE *)p+3)); WriteFile(g_TDebugFile, szTDebug, strlen(szTDebug), &d, NULL); } CloseHandle(g_TDebugFile); #endif } #ifndef _ALPHA_ else if (pvs->pvfxSrc->dwFormatTag == VIDEO_FORMAT_MSH261) #else else if (pvs->pvfxSrc->dwFormatTag == VIDEO_FORMAT_DECH261) #endif { // Strip the header of each packet and copy the payload in the video buffer while (dwPktCount--) { #ifdef LOGPAYLOAD_ON // wsprintf(szDebug, "Header: %02lX %02lX %02lX %02lX\r\ndword1: %02lX %02lX %02lX %02lX\r\ndword2: %02lX %02lX %02lX %02lX\r\n", ppDataPkt->buf[0], ppDataPkt->buf[1], ppDataPkt->buf[2], ppDataPkt->buf[3], ppDataPkt->buf[4], ppDataPkt->buf[5], ppDataPkt->buf[6], ppDataPkt->buf[7], ppDataPkt->buf[8], ppDataPkt->buf[9], ppDataPkt->buf[10], ppDataPkt->buf[11]); wsprintf(szTDebug, "Header: %02lX %02lX %02lX %02lX\r\n", (BYTE)ppDataPkt->buf[0], (BYTE)ppDataPkt->buf[1], (BYTE)ppDataPkt->buf[2], (BYTE)ppDataPkt->buf[3]); OutputDebugString(szTDebug); wsprintf(szTDebug, "dword1: %02lX %02lX %02lX %02lX\r\n", (BYTE)ppDataPkt->buf[4], (BYTE)ppDataPkt->buf[5], (BYTE)ppDataPkt->buf[6], (BYTE)ppDataPkt->buf[7]); OutputDebugString(szTDebug); wsprintf(szTDebug, "dword2: %02lX %02lX %02lX %02lX\r\n", (BYTE)ppDataPkt->buf[8], (BYTE)ppDataPkt->buf[9], (BYTE)ppDataPkt->buf[10], (BYTE)ppDataPkt->buf[11]); OutputDebugString(szTDebug); #endif // The H.261 payload header size is always 4 bytes long dwHeaderSize = 4; // Look at the payload header to figure out if the frame is a keyframe *pfReceivedKeyframe |= (BOOL)(ppDataPkt->buf[0] & 0x02); // The purpose of this code is to look for the presence of the // Picture Start Code at the beginning of the frame. If it is // not present, we should break in debug mode. // Only look for PSC at the beginning of the frame if (!*pdwFrameSize) { // The start of the frame may not be at a byte boundary. The SBIT field // of the header ((BYTE)ppDataPkt->buf[0] & 0xE0) will tell us exactly where // our frame starts. We then look for the PSC (0000 0000 0000 0001 0000 bits) *((BYTE *)&dwPSCBytes + 3) = *(BYTE *)&(ppDataPkt->buf[dwHeaderSize]); *((BYTE *)&dwPSCBytes + 2) = *(BYTE *)&(ppDataPkt->buf[dwHeaderSize + 1]); *((BYTE *)&dwPSCBytes + 1) = *(BYTE *)&(ppDataPkt->buf[dwHeaderSize + 2]); *((BYTE *)&dwPSCBytes + 0) = *(BYTE *)&(ppDataPkt->buf[dwHeaderSize + 3]); dwPSCBytes <<= ((DWORD)((BYTE)ppDataPkt->buf[0] & 0xE0) >> 5); if ((dwPSCBytes & 0xFFFFF000) != 0x00010000) { #ifdef DEBUG wsprintf(szTDebug, "VCMSTRM: The first packet to reassemble is missing a PSC!\r\n"); OutputDebugString(szTDebug); // DebugBreak(); #endif return ((MMRESULT)VCMERR_PSCMISSING); } } // The end of a buffer and the start of the next buffer could belong to the // same byte. If this is the case, the first byte of the next buffer was already // copied in the video data buffer, with the previous packet. It should not be copied // twice. The SBIT field of the payload header allows us to figure out if this is the case. if (*pdwFrameSize && (ppDataPkt->buf[0] & 0xE0)) dwHeaderSize++; // Copy the payload // Test for invalid packets that have a length below the size of the payload header if ( (ppDataPkt->len >= dwHeaderSize) && ((*pdwFrameSize + ppDataPkt->len - dwHeaderSize) <= dwMaxFrameSize) ) { // Copy the payload CopyMemory(pbyFrame + *pdwFrameSize, ppDataPkt->buf + dwHeaderSize, ppDataPkt->len - dwHeaderSize); // Update the payload size and pointer to the input video packets *pdwFrameSize += ppDataPkt->len - dwHeaderSize; ppDataPkt++; } else { // The total size of the reassembled packet would be larger than the maximum allowed!!! // Or the packet has a length less than the payload header size // Dump the frame #ifdef DEBUG wsprintf(szTDebug, (ppDataPkt->len >= dwHeaderSize) ? "VCMSTRM: Cumulative size of the reassembled packets is too large: discarding frame!\r\n" : "VCMSTRM: Packet length is smaller than payload header size: discarding frame!\r\n"); OutputDebugString(szTDebug); // DebugBreak(); #endif return ((MMRESULT)VCMERR_NONSPECIFIC); } } #ifdef LOGPAYLOAD_ON g_TDebugFile = CreateFile("C:\\RecvLog.txt", GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); SetFilePointer(g_TDebugFile, 0, NULL, FILE_END); wsprintf(szTDebug, "Frame #%03ld\r\n", (DWORD)j); WriteFile(g_TDebugFile, szTDebug, strlen(szTDebug), &d, NULL); for (j=*pdwFrameSize; j>0; j-=4, p+=4) { wsprintf(szTDebug, "%02lX %02lX %02lX %02lX\r\n", *((BYTE *)p), *((BYTE *)p+1), *((BYTE *)p+2), *((BYTE *)p+3)); WriteFile(g_TDebugFile, szTDebug, strlen(szTDebug), &d, NULL); } CloseHandle(g_TDebugFile); #endif } else { // Strip the header of each packet and copy the payload in the video buffer while (dwPktCount--) { // Copy the payload // Test for invalid packets that have a length below the size of the payload header if ( (ppDataPkt->len >= dwHeaderSize) && ((*pdwFrameSize + ppDataPkt->len - dwHeaderSize) <= dwMaxFrameSize)) { // Copy the payload CopyMemory(pbyFrame + *pdwFrameSize, ppDataPkt->buf + dwHeaderSize, ppDataPkt->len - dwHeaderSize); // Update the payload size and pointer to the input video packets *pdwFrameSize += ppDataPkt->len - dwHeaderSize; ppDataPkt++; } else { // The total size of the reassembled packet would be larger than the maximum allowed!!! // Or the packet has a length less than the payload header size // Dump the frame #ifdef DEBUG wsprintf(szTDebug, (ppDataPkt->len >= dwHeaderSize) ? "VCMSTRM: Cumulative size of the reassembled packets is too large: discarding frame!\r\n" : "VCMSTRM: Packet length is smaller than payload header size: discarding frame!\r\n"); OutputDebugString(szTDebug); // DebugBreak(); #endif return ((MMRESULT)VCMERR_NONSPECIFIC); } } } return ((MMRESULT)MMSYSERR_NOERROR); } /**************************************************************************** * @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmStreamFormatPayload | This function returns compressed data * spread into data packets with a payload header for the specific format of the * compressed data. * * @parm HVCMSTREAM | hvs | Specifies the conversion stream. * * @parm PBYTE | pDataSrc | Specifies a pointer to the whole compressed data. * * @parm DWORD | dwDataSize | Specifies the size of the input data in bytes. * * @parm PBYTE* | ppDataPkt | Specifies a pointer to a pointer to a packet. * * @parm DWORD* | pdwPktSize | Specifies a pointer to the size of the packet. * * @parm DWORD | dwPktCount | Specifies what packet to return (0 first packet, 1 second packet, ...) * * @parm DWORD | dwMaxFragSize | Specifies the maximum packet size * * @rdesc The return value is zero if the function is successful. Otherwise, it returns * an error number. Possible error values include the following: * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * @flag MMSYSERR_INVALPARAM | Specified data pointer is invalid. * @flag VCMERR_NOMOREPACKETS | There is no more data for the requested packet number, or there isn't any handler for this payload. * @flag VCMERR_NONSPECIFIC | We were asked to put a header we do not know how to generate. ***************************************************************************/ MMRESULT VCMAPI vcmStreamFormatPayload( HVCMSTREAM hvs, PBYTE pDataSrc, DWORD dwDataSize, PBYTE *ppDataPkt, PDWORD pdwPktSize, PDWORD pdwPktCount, UINT *pfMark, PBYTE *pHdrInfo, PDWORD pdwHdrSize) { PVCMSTREAM pvs = (PVCMSTREAM)hvs; PH26X_RTP_BSINFO_TRAILER pbsiT; PRTP_H263_BSINFO pbsi263; PRTP_H261_BSINFO pbsi261; PBYTE pb; DWORD dwHeaderHigh = 0UL; // most significant DWORD dwHeaderMiddle = 0UL; DWORD dwHeaderLow = 0UL; // least significant BOOL bOneFrameOnePacket; #ifdef DEBUG char szDebug[256]; #endif long i; #ifdef LOGPAYLOAD_ON PBYTE p; DWORD d; DWORD dwLastChunk; DWORD wPrevOffset; #endif // Check input params if (!hvs) { ERRORMESSAGE(("vcmStreamFormatPayload: Specified handle is invalid, hvs=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALHANDLE); } if (!pDataSrc) { ERRORMESSAGE(("vcmStreamFormatPayload: Specified pointer is invalid, pDataSrc=NULL\r\n")); return ((MMRESULT)MMSYSERR_INVALPARAM); } // Initialize packet pointer *ppDataPkt = pDataSrc; *pdwPktSize = dwDataSize; *pfMark = 1; bOneFrameOnePacket = FALSE; // Put the code that builds the packets right here!!! #ifndef _ALPHA_ #ifdef USE_BILINEAR_MSH26X if ((pvs->pvfxDst->dwFormatTag == VIDEO_FORMAT_MSH263) || (pvs->pvfxDst->dwFormatTag == VIDEO_FORMAT_MSH26X)) #else if (pvs->pvfxDst->dwFormatTag == VIDEO_FORMAT_MSH263) #endif #else if (pvs->pvfxDst->dwFormatTag == VIDEO_FORMAT_DECH263) #endif { // Look for the bitstream info trailer pbsiT = (PH26X_RTP_BSINFO_TRAILER)(pDataSrc + dwDataSize - sizeof(H26X_RTP_BSINFO_TRAILER)); // If the whole frame can fit in pvs->dwMaxPacketSize, send it non fragmented if ((pbsiT->dwCompressedSize + 4) < pvs->dwMaxPacketSize) bOneFrameOnePacket = TRUE; // Look for the packet to receive a H.263 payload header if ((*pdwPktCount < pbsiT->dwNumOfPackets) && !(bOneFrameOnePacket && *pdwPktCount)) { #ifdef _ALPHA_ // Verify that the content of the bistream info structures is correct // If not, do not parse the frame, and fail the call // This is to solve problems with the data returned by the DEC codecs if (!*pdwPktCount) { pbsi263 = (PRTP_H263_BSINFO)((PBYTE)pbsiT - pbsiT->dwNumOfPackets * sizeof(RTP_H263_BSINFO)); for (i=1; i<(long)pbsiT->dwNumOfPackets; i++, pbsi263++) { if ((pbsi263->dwBitOffset >= (pbsi263+1)->dwBitOffset) || ((pbsiT->dwCompressedSize*8) <= pbsi263->dwBitOffset)) { #ifdef DEBUG OutputDebugString("VCMSTRM: The content of the extended bitstream info structures is invalid!\r\n"); #endif // return ((MMRESULT)VCMERR_NONSPECIFIC); bOneFrameOnePacket = TRUE; } } // Test last info strucure if ( !bOneFrameOnePacket && ((pbsiT->dwCompressedSize*8) <= pbsi263->dwBitOffset)) { #ifdef DEBUG OutputDebugString("VCMSTRM: The content of the extended bitstream info structures is invalid!\r\n"); #endif // return ((MMRESULT)VCMERR_NONSPECIFIC); bOneFrameOnePacket = TRUE; } } #endif #ifdef LOGPAYLOAD_ON // Dump the whole frame in the debug window for comparison with receive side if (!*pdwPktCount) { g_DebugFile = CreateFile("C:\\SendLog.txt", GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); SetFilePointer(g_DebugFile, 0, NULL, FILE_END); wsprintf(szDebug, "Frame #%03ld\r\n", (DWORD)pbsiT->byTR); WriteFile(g_DebugFile, szDebug, strlen(szDebug), &d, NULL); wsprintf(szDebug, "Frame #%03ld has %1ld packets of size ", (DWORD)pbsiT->byTR, (DWORD)pbsiT->dwNumOfPackets); OutputDebugString(szDebug); pbsi263 = (PRTP_H263_BSINFO)((PBYTE)pbsiT - pbsiT->dwNumOfPackets * sizeof(RTP_H263_BSINFO)); for (i=1; i<(long)pbsiT->dwNumOfPackets; i++) { wPrevOffset = pbsi263->dwBitOffset; pbsi263++; wsprintf(szDebug, "%04ld, ", (DWORD)(pbsi263->dwBitOffset - wPrevOffset) >> 3); OutputDebugString(szDebug); } wsprintf(szDebug, "%04ld\r\n", (DWORD)(pbsiT->dwCompressedSize * 8 - pbsi263->dwBitOffset) >> 3); OutputDebugString(szDebug); for (i=pbsiT->dwCompressedSize, p=pDataSrc; i>0; i-=4, p+=4) { wsprintf(szDebug, "%02lX %02lX %02lX %02lX\r\n", *((BYTE *)p), *((BYTE *)p+1), *((BYTE *)p+2), *((BYTE *)p+3)); WriteFile(g_DebugFile, szDebug, strlen(szDebug), &d, NULL); } CloseHandle(g_DebugFile); } #endif // Look for the bitstream info structure pbsi263 = (PRTP_H263_BSINFO)((PBYTE)pbsiT - (pbsiT->dwNumOfPackets - *pdwPktCount) * sizeof(RTP_H263_BSINFO)); // Set the marker bit: as long as this is not the last packet of the frame // this bit needs to be set to 0 if (!bOneFrameOnePacket) { // Count the number of GOBS that could fit in pvs->dwMaxPacketSize for (i=1; (i<(long)(pbsiT->dwNumOfPackets - *pdwPktCount)) && (pbsi263->byMode != RTP_H263_MODE_B); i++) { // Don't try to add a Mode B packet to the end of another Mode A or Mode B packet if (((pbsi263+i)->dwBitOffset - pbsi263->dwBitOffset > (pvs->dwMaxPacketSize * 8)) || ((pbsi263+i)->byMode == RTP_H263_MODE_B)) break; } if (i < (long)(pbsiT->dwNumOfPackets - *pdwPktCount)) { *pfMark = 0; if (i>1) i--; } else { // Hey! You 're forgetting the last GOB! It could make the total // size of the last packet larger than pvs->dwMaxPacketSize... Imbecile! if ((pbsiT->dwCompressedSize * 8 - pbsi263->dwBitOffset > (pvs->dwMaxPacketSize * 8)) && (i>1)) { *pfMark = 0; i--; } } #if 0 // // THIS IS FOR EXPERIMENTATION ONLY !!! // // Ditch the last GOB if ((*pfMark == 1) && (i == 1)) return ((MMRESULT)VCMERR_NOMOREPACKETS); #endif } // Go to the beginning of the data pb = pDataSrc + pbsi263->dwBitOffset / 8; #if 0 // // THIS IS FOR EXPERIMENTATION ONLY !!! // // Trash the PSC once in a while to see how the other end reacts if (!*pdwPktCount && (((*pb == 0) && (*(pb+1) == 0) && ((*(pb+2) & 0xFC) == 0x80)))) { // The previous test guarantees that it is in fact a PSC that we trash... if ((DWORD)(RAND_MAX - rand()) < (DWORD)(RAND_MAX / 10)) *pb = 0xFF; } #endif #ifdef DEBUG if (!*pdwPktCount && (((*pb != 0) || (*(pb+1) != 0) || ((*(pb+2) & 0xFC) != 0x80)))) { wsprintf(szDebug, "VCMSTRM: This compressed frame is missing a PSC!\r\n"); OutputDebugString(szDebug); // DebugBreak(); } #endif // Look for the kind of header to be built if (pbsi263->byMode == RTP_H263_MODE_A) { // Build a header in mode A // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //|F|P|SBIT |EBIT | SRC | R |I|A|S|DBQ| TRB | TR | //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // But that's the network byte order... // F bit already set to 0 // Set the SRC bits dwHeaderHigh |= ((DWORD)(pbsiT->bySrc)) << 21; // R bits already set to 0 // Set the P bit dwHeaderHigh |= (pbsiT->dwFlags & RTP_H263_PB) << 29; // Set the I bit dwHeaderHigh |= (pbsiT->dwFlags & RTP_H26X_INTRA_CODED) << 15; // Set the A bit dwHeaderHigh |= (pbsiT->dwFlags & RTP_H263_AP) << 12; // Set the S bit dwHeaderHigh |= (pbsiT->dwFlags & RTP_H263_SAC) << 10; // Set the DBQ bits dwHeaderHigh |= ((DWORD)(pbsiT->byDBQ)) << 11; // Set the TRB bits dwHeaderHigh |= ((DWORD)(pbsiT->byTRB)) << 8; // Set the TR bits dwHeaderHigh |= ((DWORD)(pbsiT->byTR)); // Special case: 1 frame = 1 packet if (bOneFrameOnePacket) { // SBIT is already set to 0 // EBIT is already set to 0 #ifdef VALIDATE_SBIT_EBIT // { VALIDATE_SBIT_EBIT ERRORMESSAGE(("vcmFormatPayload: (1F1P) Previous EBIT=%ld, current SBIT=%ld, current EBIT=%ld (New frame)\r\n", g_dwPreviousEBIT, (DWORD)(dwHeaderHigh & 0x38000000) >> 27, (DWORD)(dwHeaderHigh & 0x07000000) >> 24)); #endif // } VALIDATE_SBIT_EBIT // Update the packet size *pdwPktSize = pbsiT->dwCompressedSize + 4; // Update the packet count *pdwPktCount = pbsiT->dwNumOfPackets; } else { #ifdef VALIDATE_SBIT_EBIT // { VALIDATE_SBIT_EBIT DWORD dwCurrentSBIT; #endif // } VALIDATE_SBIT_EBIT // Set the SBIT bits dwHeaderHigh |= (pbsi263->dwBitOffset % 8) << 27; // Set the EBIT bits if ((pbsiT->dwNumOfPackets - *pdwPktCount - i) >= 1) dwHeaderHigh |= (DWORD)((8UL - ((pbsi263+i)->dwBitOffset % 8)) & 0x00000007) << 24; #ifdef VALIDATE_SBIT_EBIT // { VALIDATE_SBIT_EBIT // Compare this to the previous EBIT. If the sum of the two // is not equal to 8 or 0, something's broken if (*pdwPktCount) ERRORMESSAGE(("vcmFormatPayload: Previous EBIT=%ld, current SBIT=%ld, current EBIT=%ld\r\n", g_dwPreviousEBIT, (DWORD)(dwHeaderHigh & 0x38000000) >> 27, (DWORD)(dwHeaderHigh & 0x07000000) >> 24)); else ERRORMESSAGE(("vcmFormatPayload: Previous EBIT=%ld, current SBIT=%ld, current EBIT=%ld (New frame)\r\n", g_dwPreviousEBIT, (DWORD)(dwHeaderHigh & 0x38000000) >> 27, (DWORD)(dwHeaderHigh & 0x07000000) >> 24)); // Only test this if this is the first packet dwCurrentSBIT = (DWORD)(dwHeaderHigh & 0x38000000) >> 27; if ((*pdwPktCount) && (((dwCurrentSBIT + g_dwPreviousEBIT) != 8) && (((dwCurrentSBIT + g_dwPreviousEBIT) != 0)))) DebugBreak(); g_dwPreviousEBIT = (dwHeaderHigh & 0x07000000) >> 24; #endif // } VALIDATE_SBIT_EBIT // Update the packet size if ((pbsiT->dwNumOfPackets - *pdwPktCount - i) >= 1) *pdwPktSize = (((pbsi263+i)->dwBitOffset - 1) / 8) - (pbsi263->dwBitOffset / 8) + 1 + 4; else *pdwPktSize = pbsiT->dwCompressedSize - pbsi263->dwBitOffset / 8 + 4; // Update the packet count *pdwPktCount += i; } #if 0 // Save the header right before the data chunk *ppDataPkt = pDataSrc + (pbsi263->dwBitOffset / 8) - 4; // Convert to network byte order *((BYTE *)*ppDataPkt+3) = (BYTE)(dwHeaderHigh & 0x000000FF); *((BYTE *)*ppDataPkt+2) = (BYTE)((dwHeaderHigh >> 8) & 0x000000FF); *((BYTE *)*ppDataPkt+1) = (BYTE)((dwHeaderHigh >> 16) & 0x000000FF); *((BYTE *)*ppDataPkt) = (BYTE)((dwHeaderHigh >> 24) & 0x000000FF); #else // Save the header right before the data chunk *ppDataPkt = pDataSrc + (pbsi263->dwBitOffset / 8) - 4; *pdwHdrSize=4; // Convert to network byte order *((BYTE *)*pHdrInfo+3) = (BYTE)(dwHeaderHigh & 0x000000FF); *((BYTE *)*pHdrInfo+2) = (BYTE)((dwHeaderHigh >> 8) & 0x000000FF); *((BYTE *)*pHdrInfo+1) = (BYTE)((dwHeaderHigh >> 16) & 0x000000FF); *((BYTE *)*pHdrInfo) = (BYTE)((dwHeaderHigh >> 24) & 0x000000FF); #endif #ifdef LOGPAYLOAD_ON // Output some debug stuff wsprintf(szDebug, "Header content:\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, (*(BYTE *)*ppDataPkt & 0x80) ? " F: '1' => Mode B or C\r\n" : " F: '0' => Mode A\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, (*(BYTE *)*ppDataPkt & 0x40) ? " P: '1' => PB-frame\r\n" : " P: '0' => I or P frame\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, " SBIT: %01ld\r\n", (DWORD)((*(BYTE *)*ppDataPkt & 0x38) >> 3)); OutputDebugString(szDebug); wsprintf(szDebug, " EBIT: %01ld\r\n", (DWORD)(*(BYTE *)*ppDataPkt & 0x07)); OutputDebugString(szDebug); switch ((DWORD)(*((BYTE *)*ppDataPkt+1) >> 5)) { case 0: wsprintf(szDebug, " SRC: '000' => Source format forbidden!\r\n"); break; case 1: wsprintf(szDebug, " SRC: '001' => Source format sub-QCIF\r\n"); break; case 2: wsprintf(szDebug, " SRC: '010' => Source format QCIF\r\n"); break; case 3: wsprintf(szDebug, " SRC: '011' => Source format CIF\r\n"); break; case 4: wsprintf(szDebug, " SRC: '100' => Source format 4CIF\r\n"); break; case 5: wsprintf(szDebug, " SRC: '101' => Source format 16CIF\r\n"); break; case 6: wsprintf(szDebug, " SRC: '110' => Source format reserved\r\n"); break; case 7: wsprintf(szDebug, " SRC: '111' => Source format reserved\r\n"); break; default: wsprintf(szDebug, " SRC: %ld => Source format unknown!\r\n", (DWORD)(*((BYTE *)*ppDataPkt+1) >> 5)); break; } OutputDebugString(szDebug); wsprintf(szDebug, " R: %02ld => Reserved, must be 0\r\n", (DWORD)((*((BYTE *)*ppDataPkt+1) & 0x1F) >> 5)); OutputDebugString(szDebug); wsprintf(szDebug, (*((BYTE *)*ppDataPkt+2) & 0x80) ? " I: '1' => Intra-coded\r\n" : " I: '0' => Not Intra-coded\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, (*((BYTE *)*ppDataPkt+2) & 0x40) ? " A: '1' => Optional Advanced Prediction mode ON\r\n" : " A: '0' => Optional Advanced Prediction mode OFF\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, (*((BYTE *)*ppDataPkt+2) & 0x20) ? " S: '1' => Optional Syntax-based Arithmetic Code mode ON\r\n" : " S: '0' => Optional Syntax-based Arithmetic Code mode OFF\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, " DBQ: %01ld => Should be 0\r\n", (DWORD)((*((BYTE *)*ppDataPkt+2) & 0x18) >> 3)); OutputDebugString(szDebug); wsprintf(szDebug, " TRB: %01ld => Should be 0\r\n", (DWORD)(*((BYTE *)*ppDataPkt+2) & 0x07)); OutputDebugString(szDebug); wsprintf(szDebug, " TR: %03ld\r\n", (DWORD)(*((BYTE *)*ppDataPkt+3))); OutputDebugString(szDebug); wsprintf(szDebug, "Packet: %02lX\r\n Header: %02lX %02lX %02lX %02lX\r\n dword1: %02lX %02lX %02lX %02lX\r\n dword2: %02lX %02lX %02lX %02lX\r\n", *pdwPktCount, *((BYTE *)*ppDataPkt), *((BYTE *)*ppDataPkt+1), *((BYTE *)*ppDataPkt+2), *((BYTE *)*ppDataPkt+3), *((BYTE *)*ppDataPkt+4), *((BYTE *)*ppDataPkt+5), *((BYTE *)*ppDataPkt+6), *((BYTE *)*ppDataPkt+7), *((BYTE *)*ppDataPkt+8), *((BYTE *)*ppDataPkt+9), *((BYTE *)*ppDataPkt+10), *((BYTE *)*ppDataPkt+11)); OutputDebugString(szDebug); if (*pdwPktCount == pbsiT->dwNumOfPackets) wsprintf(szDebug, " Tail : %02lX %02lX XX XX\r\n", *((BYTE *)*ppDataPkt+*pdwPktSize-2), *((BYTE *)*ppDataPkt+*pdwPktSize-1)); else wsprintf(szDebug, " Tail : %02lX %02lX %02lX %02lX\r\n", *((BYTE *)*ppDataPkt+*pdwPktSize-2), *((BYTE *)*ppDataPkt+*pdwPktSize-1), *((BYTE *)*ppDataPkt+*pdwPktSize), *((BYTE *)*ppDataPkt+*pdwPktSize+1)); OutputDebugString(szDebug); if (*pfMark == 1) wsprintf(szDebug, " Marker: ON\r\n"); else wsprintf(szDebug, " Marker: OFF\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, "Frame #%03ld, Packet of size %04ld\r\n", (DWORD)pbsiT->byTR, *pdwPktSize); OutputDebugString(szDebug); #endif } else if (pbsi263->byMode == RTP_H263_MODE_B) { // Build a header in mode B // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //|F|P|SBIT |EBIT | SRC | QUANT |I|A|S| GOBN | MBA | //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //| HMV1 | VMV1 | HMV2 | VMV2 | //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // But that's the network byte order... // Set the F bit to 1 dwHeaderHigh = 0x80000000; // Set the SRC bits dwHeaderHigh |= ((DWORD)(pbsiT->bySrc)) << 21; // Set the QUANT bits dwHeaderHigh |= ((DWORD)(pbsi263->byQuant)) << 16; // Set the P bit dwHeaderHigh |= (pbsiT->dwFlags & RTP_H263_PB) << 29; // Set the I bit dwHeaderHigh |= (pbsiT->dwFlags & RTP_H26X_INTRA_CODED) << 15; // Set the A bit dwHeaderHigh |= (pbsiT->dwFlags & RTP_H263_AP) << 12; // Set the S bit dwHeaderHigh |= (pbsiT->dwFlags & RTP_H263_SAC) << 10; // Set the GOBN bits dwHeaderHigh |= ((DWORD)(pbsi263->byGOBN)) << 8; // Set the TR bits dwHeaderHigh |= ((DWORD)(pbsi263->byMBA)); // Set the HMV1 bits dwHeaderLow |= ((DWORD)(BYTE)(pbsi263->cHMV1)) << 24; // Set the VMV1 bits dwHeaderLow |= ((DWORD)(BYTE)(pbsi263->cVMV1)) << 16; // Set the HMV2 bits dwHeaderLow |= ((DWORD)(BYTE)(pbsi263->cHMV2)) << 8; // Set the VMV2 bits dwHeaderLow |= ((DWORD)(BYTE)(pbsi263->cVMV2)); // Special case: 1 frame = 1 packet if (bOneFrameOnePacket) { // SBIT is already set to 0 // EBIT is already set to 0 #ifdef VALIDATE_SBIT_EBIT // { VALIDATE_SBIT_EBIT ERRORMESSAGE(("vcmFormatPayload: (1F1P) Previous EBIT=%ld, current SBIT=%ld, current EBIT=%ld (New frame)\r\n", g_dwPreviousEBIT, (DWORD)(dwHeaderHigh & 0x38000000) >> 27, (DWORD)(dwHeaderHigh & 0x07000000) >> 24)); #endif // } VALIDATE_SBIT_EBIT // Update the packet size *pdwPktSize = pbsiT->dwCompressedSize + 8; // Update the packet count *pdwPktCount = pbsiT->dwNumOfPackets; } else { #ifdef VALIDATE_SBIT_EBIT // { VALIDATE_SBIT_EBIT DWORD dwCurrentSBIT; #endif // } VALIDATE_SBIT_EBIT // Set the SBIT bits dwHeaderHigh |= (pbsi263->dwBitOffset % 8) << 27; // Set the EBIT bits if ((pbsiT->dwNumOfPackets - *pdwPktCount - i) >= 1) dwHeaderHigh |= (DWORD)((8UL - ((pbsi263+i)->dwBitOffset % 8)) & 0x00000007) << 24; #ifdef VALIDATE_SBIT_EBIT // { VALIDATE_SBIT_EBIT // Compare this to the previous EBIT. If the sum of the two // is not equal to 8 or 0, something's broken if (*pdwPktCount) ERRORMESSAGE(("vcmFormatPayload: Previous EBIT=%ld, current SBIT=%ld, current EBIT=%ld\r\n", g_dwPreviousEBIT, (DWORD)(dwHeaderHigh & 0x38000000) >> 27, (DWORD)(dwHeaderHigh & 0x07000000) >> 24)); else ERRORMESSAGE(("vcmFormatPayload: Previous EBIT=%ld, current SBIT=%ld, current EBIT=%ld (New frame)\r\n", g_dwPreviousEBIT, (DWORD)(dwHeaderHigh & 0x38000000) >> 27, (DWORD)(dwHeaderHigh & 0x07000000) >> 24)); // Only test this if this is the first packet dwCurrentSBIT = (DWORD)(dwHeaderHigh & 0x38000000) >> 27; if ((*pdwPktCount) && (((dwCurrentSBIT + g_dwPreviousEBIT) != 8) && (((dwCurrentSBIT + g_dwPreviousEBIT) != 0)))) DebugBreak(); g_dwPreviousEBIT = (dwHeaderHigh & 0x07000000) >> 24; #endif // } VALIDATE_SBIT_EBIT // Update the packet size if ((pbsiT->dwNumOfPackets - *pdwPktCount - i) >= 1) *pdwPktSize = (((pbsi263+i)->dwBitOffset - 1) / 8) - (pbsi263->dwBitOffset / 8) + 1 + 8; else *pdwPktSize = pbsiT->dwCompressedSize - pbsi263->dwBitOffset / 8 + 8; // Update the packet count *pdwPktCount += i; } #if 0 // Save the header right before the data chunk *ppDataPkt = pDataSrc + (pbsi263->dwBitOffset / 8) - 8; // Convert to network byte order *((BYTE *)*ppDataPkt+3) = (BYTE)(dwHeaderHigh & 0x000000FF); *((BYTE *)*ppDataPkt+2) = (BYTE)((dwHeaderHigh >> 8) & 0x000000FF); *((BYTE *)*ppDataPkt+1) = (BYTE)((dwHeaderHigh >> 16) & 0x000000FF); *((BYTE *)*ppDataPkt) = (BYTE)((dwHeaderHigh >> 24) & 0x000000FF); *((BYTE *)*ppDataPkt+7) = (BYTE)(dwHeaderLow & 0x000000FF); *((BYTE *)*ppDataPkt+6) = (BYTE)((dwHeaderLow >> 8) & 0x000000FF); *((BYTE *)*ppDataPkt+5) = (BYTE)((dwHeaderLow >> 16) & 0x000000FF); *((BYTE *)*ppDataPkt+4) = (BYTE)((dwHeaderLow >> 24) & 0x000000FF); #else // Save the header right before the data chunk *ppDataPkt = pDataSrc + (pbsi263->dwBitOffset / 8) - 8; *pdwHdrSize=8; // Convert to network byte order *((BYTE *)*pHdrInfo+3) = (BYTE)(dwHeaderHigh & 0x000000FF); *((BYTE *)*pHdrInfo+2) = (BYTE)((dwHeaderHigh >> 8) & 0x000000FF); *((BYTE *)*pHdrInfo+1) = (BYTE)((dwHeaderHigh >> 16) & 0x000000FF); *((BYTE *)*pHdrInfo) = (BYTE)((dwHeaderHigh >> 24) & 0x000000FF); *((BYTE *)*pHdrInfo+7) = (BYTE)(dwHeaderLow & 0x000000FF); *((BYTE *)*pHdrInfo+6) = (BYTE)((dwHeaderLow >> 8) & 0x000000FF); *((BYTE *)*pHdrInfo+5) = (BYTE)((dwHeaderLow >> 16) & 0x000000FF); *((BYTE *)*pHdrInfo+4) = (BYTE)((dwHeaderLow >> 24) & 0x000000FF); #endif #ifdef LOGPAYLOAD_ON // Output some info wsprintf(szDebug, "Header content:\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, (*(BYTE *)*ppDataPkt & 0x80) ? " F: '1' => Mode B or C\r\n" : " F: '0' => Mode A\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, (*(BYTE *)*ppDataPkt & 0x40) ? " P: '1' => PB-frame\r\n" : " P: '0' => I or P frame\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, " SBIT: %01ld\r\n", (DWORD)((*(BYTE *)*ppDataPkt & 0x38) >> 3)); OutputDebugString(szDebug); wsprintf(szDebug, " EBIT: %01ld\r\n", (DWORD)(*(BYTE *)*ppDataPkt & 0x07)); OutputDebugString(szDebug); switch ((DWORD)(*((BYTE *)*ppDataPkt+1) >> 5)) { case 0: wsprintf(szDebug, " SRC: '000' => Source format forbidden!\r\n"); break; case 1: wsprintf(szDebug, " SRC: '001' => Source format sub-QCIF\r\n"); break; case 2: wsprintf(szDebug, " SRC: '010' => Source format QCIF\r\n"); break; case 3: wsprintf(szDebug, " SRC: '011' => Source format CIF\r\n"); break; case 4: wsprintf(szDebug, " SRC: '100' => Source format 4CIF\r\n"); break; case 5: wsprintf(szDebug, " SRC: '101' => Source format 16CIF\r\n"); break; case 6: wsprintf(szDebug, " SRC: '110' => Source format reserved\r\n"); break; case 7: wsprintf(szDebug, " SRC: '111' => Source format reserved\r\n"); break; default: wsprintf(szDebug, " SRC: %ld => Source format unknown!\r\n", (DWORD)(*((BYTE *)*ppDataPkt+1) >> 5)); break; } OutputDebugString(szDebug); wsprintf(szDebug, " QUANT: %02ld\r\n", (DWORD)((*((BYTE *)*ppDataPkt+1) & 0x1F) >> 5)); OutputDebugString(szDebug); wsprintf(szDebug, (*((BYTE *)*ppDataPkt+2) & 0x80) ? " I: '1' => Intra-coded\r\n" : " I: '0' => Not Intra-coded\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, (*((BYTE *)*ppDataPkt+2) & 0x40) ? " A: '1' => Optional Advanced Prediction mode ON\r\n" : " A: '0' => Optional Advanced Prediction mode OFF\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, (*((BYTE *)*ppDataPkt+2) & 0x20) ? " S: '1' => Optional Syntax-based Arithmetic Code mode ON\r\n" : " S: '0' => Optional Syntax-based Arithmetic Code mode OFF\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, " GOBN: %03ld\r\n", (DWORD)(*((BYTE *)*ppDataPkt+2) & 0x1F)); OutputDebugString(szDebug); wsprintf(szDebug, " MBA: %03ld\r\n", (DWORD)(*((BYTE *)*ppDataPkt+3))); OutputDebugString(szDebug); wsprintf(szDebug, " HMV1: %03ld\r\n", (DWORD)(*((BYTE *)*ppDataPkt+7))); OutputDebugString(szDebug); wsprintf(szDebug, " VMV1: %03ld\r\n", (DWORD)(*((BYTE *)*ppDataPkt+6))); OutputDebugString(szDebug); wsprintf(szDebug, " HMV2: %03ld\r\n", (DWORD)(*((BYTE *)*ppDataPkt+5))); OutputDebugString(szDebug); wsprintf(szDebug, " VMV2: %03ld\r\n", (DWORD)(*((BYTE *)*ppDataPkt+4))); OutputDebugString(szDebug); wsprintf(szDebug, "Packet: %02lX\r\n Header: %02lX %02lX %02lX %02lX %02lX %02lX %02lX %02lX\r\n dword1: %02lX %02lX %02lX %02lX\r\n", *pdwPktCount, *((BYTE *)*ppDataPkt), *((BYTE *)*ppDataPkt+1), *((BYTE *)*ppDataPkt+2), *((BYTE *)*ppDataPkt+3), *((BYTE *)*ppDataPkt+4), *((BYTE *)*ppDataPkt+5), *((BYTE *)*ppDataPkt+6), *((BYTE *)*ppDataPkt+7), *((BYTE *)*ppDataPkt+8), *((BYTE *)*ppDataPkt+9), *((BYTE *)*ppDataPkt+10), *((BYTE *)*ppDataPkt+11)); OutputDebugString(szDebug); if (*pdwPktCount == pbsiT->dwNumOfPackets) wsprintf(szDebug, " Tail : %02lX %02lX XX XX\r\n", *((BYTE *)*ppDataPkt+*pdwPktSize-2), *((BYTE *)*ppDataPkt+*pdwPktSize-1)); else wsprintf(szDebug, " Tail : %02lX %02lX %02lX %02lX\r\n", *((BYTE *)*ppDataPkt+*pdwPktSize-2), *((BYTE *)*ppDataPkt+*pdwPktSize-1), *((BYTE *)*ppDataPkt+*pdwPktSize), *((BYTE *)*ppDataPkt+*pdwPktSize+1)); OutputDebugString(szDebug); if (*pfMark == 1) wsprintf(szDebug, " Marker: ON\r\n"); else wsprintf(szDebug, " Marker: OFF\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, "Frame #%03ld, Packet of size %04ld\r\n", (DWORD)pbsiT->byTR, *pdwPktSize); OutputDebugString(szDebug); #endif } else if (pbsi263->byMode == RTP_H263_MODE_C) { // Build a header in mode C #ifdef DEBUG wsprintf(szDebug, "VCMSTRM: We were asked to generate a MODE C H.263 payload header!"); OutputDebugString(szDebug); // DebugBreak(); #endif return ((MMRESULT)VCMERR_NONSPECIFIC); } } else return ((MMRESULT)VCMERR_NOMOREPACKETS); } #ifndef _ALPHA_ else if (pvs->pvfxDst->dwFormatTag == VIDEO_FORMAT_MSH261) #else else if (pvs->pvfxDst->dwFormatTag == VIDEO_FORMAT_DECH261) #endif { // Look for the bitstream info trailer pbsiT = (PH26X_RTP_BSINFO_TRAILER)(pDataSrc + dwDataSize - sizeof(H26X_RTP_BSINFO_TRAILER)); // If the whole frame can fit in dwMaxFragSize, send it non fragmented if ((pbsiT->dwCompressedSize + 4) < pvs->dwMaxPacketSize) bOneFrameOnePacket = TRUE; // Look for the packet to receive a H.261 payload header if ((*pdwPktCount < pbsiT->dwNumOfPackets) && !(bOneFrameOnePacket && *pdwPktCount)) { #ifdef _ALPHA_ // Verify that the content of the bistream info structures is correct // If not, do not parse the frame, and fail the call // This is to solve problems with the data returned by the DEC codecs if (!*pdwPktCount) { pbsi261 = (PRTP_H261_BSINFO)((PBYTE)pbsiT - pbsiT->dwNumOfPackets * sizeof(RTP_H261_BSINFO)); for (i=1; i<(long)pbsiT->dwNumOfPackets; i++, pbsi261++) { if ((pbsi261->dwBitOffset >= (pbsi261+1)->dwBitOffset) || ((pbsiT->dwCompressedSize*8) <= pbsi261->dwBitOffset)) { #ifdef DEBUG OutputDebugString("VCMSTRM: The content of the extended bitstream info structures is invalid!\r\n"); #endif // return ((MMRESULT)VCMERR_NONSPECIFIC); bOneFrameOnePacket = TRUE; } } // Test last info strucure if ( !bOneFrameOnePacket && ((pbsiT->dwCompressedSize*8) <= pbsi261->dwBitOffset)) { #ifdef DEBUG OutputDebugString("VCMSTRM: The content of the extended bitstream info structures is invalid!\r\n"); #endif // return ((MMRESULT)VCMERR_NONSPECIFIC); bOneFrameOnePacket = TRUE; } } #endif #ifdef LOGPAYLOAD_ON // Dump the whole frame in the debug window for comparison with receive side if (!*pdwPktCount) { g_DebugFile = CreateFile("C:\\SendLog.txt", GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); SetFilePointer(g_DebugFile, 0, NULL, FILE_END); wsprintf(szDebug, "Frame #%03ld\r\n", (DWORD)pbsiT->byTR); WriteFile(g_DebugFile, szDebug, strlen(szDebug), &d, NULL); wsprintf(szDebug, "Frame #%03ld has %1ld GOBs of size ", (DWORD)pbsiT->byTR, (DWORD)pbsiT->dwNumOfPackets); OutputDebugString(szDebug); pbsi261 = (PRTP_H261_BSINFO)((PBYTE)pbsiT - pbsiT->dwNumOfPackets * sizeof(RTP_H261_BSINFO)); for (i=1; i<(long)pbsiT->dwNumOfPackets; i++) { wPrevOffset = pbsi261->dwBitOffset; pbsi261++; wsprintf(szDebug, "%04ld, ", (DWORD)(pbsi261->dwBitOffset - wPrevOffset) >> 3); OutputDebugString(szDebug); } wsprintf(szDebug, "%04ld\r\n", (DWORD)(pbsiT->dwCompressedSize * 8 - pbsi261->dwBitOffset) >> 3); OutputDebugString(szDebug); for (i=pbsiT->dwCompressedSize, p=pDataSrc; i>0; i-=4, p+=4) { wsprintf(szDebug, "%02lX %02lX %02lX %02lX\r\n", *((BYTE *)p), *((BYTE *)p+1), *((BYTE *)p+2), *((BYTE *)p+3)); WriteFile(g_DebugFile, szDebug, strlen(szDebug), &d, NULL); } CloseHandle(g_DebugFile); } #endif // Look for the bitstream info structure pbsi261 = (PRTP_H261_BSINFO)((PBYTE)pbsiT - (pbsiT->dwNumOfPackets - *pdwPktCount) * sizeof(RTP_H261_BSINFO)); // Set the marker bit: as long as this is not the last packet of the frame // this bit needs to be set to 0 if (!bOneFrameOnePacket) { // Count the number of GOBS that could fit in dwMaxFragSize for (i=1; i<(long)(pbsiT->dwNumOfPackets - *pdwPktCount); i++) { if ((pbsi261+i)->dwBitOffset - pbsi261->dwBitOffset > (pvs->dwMaxPacketSize * 8)) break; } if (i < (long)(pbsiT->dwNumOfPackets - *pdwPktCount)) { *pfMark = 0; if (i>1) i--; } else { // Hey! You 're forgetting the last GOB! It could make the total // size of the last packet larger than dwMaxFragSize... Imbecile! if ((pbsiT->dwCompressedSize * 8 - pbsi261->dwBitOffset > (pvs->dwMaxPacketSize * 8)) && (i>1)) { *pfMark = 0; i--; } } } // Go to the beginning of the data pb = pDataSrc + pbsi261->dwBitOffset / 8; #ifdef DEBUG if (!*pdwPktCount && ((*pb != 0) || (*(pb+1) != 1))) { wsprintf(szDebug, "VCMSTRM: This GOB is missing a GOB Start!"); OutputDebugString(szDebug); // DebugBreak(); } #endif // Build a header to this thing! // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //|SBIT |EBIT |I|V| GOBN | MBAP | QUANT | HMVD | VMVD | //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // But that's the network byte order... // Set the V bit to 1 dwHeaderHigh |= 0x01000000; // Set the I bit dwHeaderHigh |= (pbsiT->dwFlags & RTP_H26X_INTRA_CODED) << 25; // Set the GOBn bits dwHeaderHigh |= ((DWORD)(pbsi261->byGOBN)) << 20; // Set the MBAP bits dwHeaderHigh |= ((DWORD)(pbsi261->byMBA)) << 15; // Set the QUANT bits dwHeaderHigh |= ((DWORD)(pbsi261->byQuant)) << 10; // Set the HMVD bits dwHeaderHigh |= ((DWORD)(BYTE)(pbsi261->cHMV)) << 5; // Set the VMVD bits dwHeaderHigh |= ((DWORD)(BYTE)(pbsi261->cVMV)); // Special case: 1 frame = 1 packet if (bOneFrameOnePacket) { // SBIT is already set to 0 // EBIT is already set to 0 #ifdef VALIDATE_SBIT_EBIT // { VALIDATE_SBIT_EBIT ERRORMESSAGE(("vcmFormatPayload: (1F1P) Previous EBIT=%ld, current SBIT=%ld, current EBIT=%ld (New frame)\r\n", g_dwPreviousEBIT, (DWORD)(dwHeaderHigh & 0xE0000000) >> 29, (DWORD)(dwHeaderHigh & 0x1C000000) >> 26)); #endif // } VALIDATE_SBIT_EBIT // Update the packet size *pdwPktSize = pbsiT->dwCompressedSize + 4; // Update the packet count *pdwPktCount = pbsiT->dwNumOfPackets; } else { #ifdef VALIDATE_SBIT_EBIT // { VALIDATE_SBIT_EBIT DWORD dwCurrentSBIT; #endif // } VALIDATE_SBIT_EBIT // Set the SBIT bits dwHeaderHigh |= (pbsi261->dwBitOffset % 8) << 29; // Set the EBIT bits if ((pbsiT->dwNumOfPackets - *pdwPktCount - i) >= 1) dwHeaderHigh |= (DWORD)((8UL - ((pbsi261+i)->dwBitOffset % 8)) & 0x00000007) << 26; #ifdef VALIDATE_SBIT_EBIT // { VALIDATE_SBIT_EBIT // Compare this to the previous EBIT. If the sum of the two // is not equal to 8, something's broken if (*pdwPktCount) ERRORMESSAGE(("vcmFormatPayload: Previous EBIT=%ld, current SBIT=%ld, current EBIT=%ld\r\n", g_dwPreviousEBIT, (DWORD)(dwHeaderHigh & 0xE0000000) >> 29, (DWORD)(dwHeaderHigh & 0x1C000000) >> 26)); else ERRORMESSAGE(("vcmFormatPayload: Previous EBIT=%ld, current SBIT=%ld, current EBIT=%ld (New frame)\r\n", g_dwPreviousEBIT, (DWORD)(dwHeaderHigh & 0xE0000000) >> 29, (DWORD)(dwHeaderHigh & 0x1C000000) >> 26)); // Only test this if this is the first packet dwCurrentSBIT = (DWORD)(dwHeaderHigh & 0xE0000000) >> 29; if ((*pdwPktCount) && (((dwCurrentSBIT + g_dwPreviousEBIT) != 8) && (((dwCurrentSBIT + g_dwPreviousEBIT) != 0)))) DebugBreak(); g_dwPreviousEBIT = (dwHeaderHigh & 0x1C000000) >> 26; #endif // } VALIDATE_SBIT_EBIT // Update the packet size if ((pbsiT->dwNumOfPackets - *pdwPktCount - i) >= 1) *pdwPktSize = (((pbsi261+i)->dwBitOffset - 1) / 8) - (pbsi261->dwBitOffset / 8) + 1 + 4; else *pdwPktSize = pbsiT->dwCompressedSize - pbsi261->dwBitOffset / 8 + 4; // Update the packet count *pdwPktCount += i; } #if 0 // Save the header right before the data chunk *ppDataPkt = pDataSrc + (pbsi261->dwBitOffset / 8) - 4; // Convert to network byte order *((BYTE *)*ppDataPkt+3) = (BYTE)(dwHeaderHigh & 0x000000FF); *((BYTE *)*ppDataPkt+2) = (BYTE)((dwHeaderHigh >> 8) & 0x000000FF); *((BYTE *)*ppDataPkt+1) = (BYTE)((dwHeaderHigh >> 16) & 0x000000FF); *((BYTE *)*ppDataPkt) = (BYTE)((dwHeaderHigh >> 24) & 0x000000FF); #else // Save the header right before the data chunk *ppDataPkt = pDataSrc + (pbsi261->dwBitOffset / 8) - 4; *pdwHdrSize=4; // Convert to network byte order *((BYTE *)*pHdrInfo+3) = (BYTE)(dwHeaderHigh & 0x000000FF); *((BYTE *)*pHdrInfo+2) = (BYTE)((dwHeaderHigh >> 8) & 0x000000FF); *((BYTE *)*pHdrInfo+1) = (BYTE)((dwHeaderHigh >> 16) & 0x000000FF); *((BYTE *)*pHdrInfo) = (BYTE)((dwHeaderHigh >> 24) & 0x000000FF); #endif #ifdef LOGPAYLOAD_ON // Output some debug stuff wsprintf(szDebug, "Packet: %02lX\r\n Header: %02lX %02lX %02lX %02lX\r\n dword1: %02lX %02lX %02lX %02lX\r\n dword2: %02lX %02lX %02lX %02lX\r\n", *pdwPktCount, *((BYTE *)*ppDataPkt), *((BYTE *)*ppDataPkt+1), *((BYTE *)*ppDataPkt+2), *((BYTE *)*ppDataPkt+3), *((BYTE *)*ppDataPkt+4), *((BYTE *)*ppDataPkt+5), *((BYTE *)*ppDataPkt+6), *((BYTE *)*ppDataPkt+7), *((BYTE *)*ppDataPkt+8), *((BYTE *)*ppDataPkt+9), *((BYTE *)*ppDataPkt+10), *((BYTE *)*ppDataPkt+11)); OutputDebugString(szDebug); if (*pdwPktCount == pbsiT->dwNumOfPackets) wsprintf(szDebug, " Tail : %02lX %02lX XX XX\r\n", *((BYTE *)*ppDataPkt+*pdwPktSize-2), *((BYTE *)*ppDataPkt+*pdwPktSize-1)); else wsprintf(szDebug, " Tail : %02lX %02lX %02lX %02lX\r\n", *((BYTE *)*ppDataPkt+*pdwPktSize-2), *((BYTE *)*ppDataPkt+*pdwPktSize-1), *((BYTE *)*ppDataPkt+*pdwPktSize), *((BYTE *)*ppDataPkt+*pdwPktSize+1)); OutputDebugString(szDebug); if (*pfMark == 1) wsprintf(szDebug, " Marker: ON\r\n"); else wsprintf(szDebug, " Marker: OFF\r\n"); OutputDebugString(szDebug); wsprintf(szDebug, "Frame #%03ld, Packet of size %04ld\r\n", (DWORD)pbsiT->byTR, *pdwPktSize); OutputDebugString(szDebug); #endif } else return ((MMRESULT)VCMERR_NOMOREPACKETS); } else { if (!*pdwPktCount) { *pdwPktCount = 1; *pdwHdrSize = 0; } else return ((MMRESULT)VCMERR_NOMOREPACKETS); } return ((MMRESULT)MMSYSERR_NOERROR); } /**************************************************************************** * @doc EXTERNAL COMPFUNC * * @func MMRESULT | vcmStreamGetPayloadHeaderSize | This function gets the size * of the RTP payload header associated to a video codec. * * @parm HVCMSTREAM | hvs | Specifies the conversion stream. * * @parm PDWORD | pdwPayloadHeaderSize | Specifies a pointer to the payload header size. * * @rdesc The return value is zero if the function is successful. Otherwise, it returns * an error number. Possible error values include the following: * @flag MMSYSERR_INVALHANDLE | Specified handle is invalid. * @flag MMSYSERR_INVALPARAM | Specified saturation value is invalid. * * @xref ***************************************************************************/ 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; }