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