|
|
#include <objbase.h>
#include <qos.h>
#include <winsock2.h>
#define INITGUID // Only do this in one file
#include "capture.h"
#include "frameop.h"
#include "filters.h"
#include <confdbg.h>
#include <avutil.h>
#include "..\nac\utils.h"
#include "vidinout.h"
#include "vcmstrm.h"
CCaptureChain::CCaptureChain(void) { m_opchain = NULL; m_filterchain = NULL; m_filtertags = NULL; InitializeCriticalSection(&m_capcs); }
CCaptureChain::~CCaptureChain(void) { CFrameOp *pchain;
EnterCriticalSection(&m_capcs); pchain = m_opchain; m_opchain = NULL; LeaveCriticalSection(&m_capcs); if (pchain) pchain->Release(); DeleteCriticalSection(&m_capcs); }
STDMETHODIMP CCaptureChain::GrabFrame( IBitmapSurface** ppBS ) { CFrameOp *cfo; HRESULT hres;
*ppBS = NULL; EnterCriticalSection(&m_capcs); if (m_opchain) { m_opchain->AddRef(); // lock chain - prevents chain from being released
cfo = m_opchain; while (cfo && ((hres = cfo->DoOp(ppBS)) == NOERROR)) { cfo = cfo->m_next; } if (*ppBS && hres != NOERROR) { // failed conversion, so discard last pBSin frame
(*ppBS)->Release(); *ppBS = NULL; } m_opchain->Release(); // unlock chain
} else hres = E_UNEXPECTED;
LeaveCriticalSection(&m_capcs); return hres; }
typedef struct _CONVERTINFO { long ci_width; long ci_height; long ci_dstwidth; long ci_dstheight; long ci_delta; long ci_UVDownSampling; long ci_ZeroingDWORD; void (*ci_Copy) (LPBYTE *, LPBYTE *); RGBQUAD ci_colortable[1]; } CONVERTINFO, FAR* PCONVERTINFO;
#ifdef ENABLE_ZOOM_CODE
typedef struct _rv { long x_i; long p; long p1; } ROW_VALUES;
typedef struct _ZOOMCONVERTINFO { long ci_width; long ci_height; long ci_dstwidth; long ci_dstheight; ROW_VALUES *ci_rptr; RGBQUAD ci_colortable[1]; } ZOOMCONVERTINFO, FAR* PZOOMCONVERTINFO; #endif // ENABLE_ZOOM_CODE
// sub worker routines for conversion of RGB16, RGB24 and RGB32 to RGB24
BYTE Byte16[32] = {0,8,16,25,33,41,49,58,66,74,82,91,99,107,115,123,132,140,148,156,165,173, 181,189,197,206,214,222,230,239,247,255};
void Copy16(LPBYTE *ppsrc, LPBYTE *ppdst) { DWORD tmp;
tmp = *(WORD *)(*ppsrc); *(*ppdst)++ = Byte16[tmp & 31]; // blue
*(*ppdst)++ = Byte16[(tmp >> 5) & 31]; // green
*(*ppdst)++ = Byte16[(tmp >> 10) & 31]; // red
*ppsrc += 2; }
void Copy24(LPBYTE *ppsrc, LPBYTE *ppdst) { *(*ppdst)++ = *(*ppsrc)++; // blue
*(*ppdst)++ = *(*ppsrc)++; // green
*(*ppdst)++ = *(*ppsrc)++; // red
}
void Copy32(LPBYTE *ppsrc, LPBYTE *ppdst) { *(*ppdst)++ = *(*ppsrc)++; // blue
*(*ppdst)++ = *(*ppsrc)++; // green
*(*ppdst)++ = *(*ppsrc)++; // red
(*ppsrc)++; }
// worker routine to shrink an RGB16, RGB24 or RGB32 in half (width & height)
// result is RGB24
BOOL DoHalfSize( IBitmapSurface* pbsIn, IBitmapSurface* pbsOut, PCONVERTINFO refdata ) { LPBYTE pBits, pCvtBits, pIn, pOut; long ipitch, opitch; long x, y;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch); pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
ipitch = (ipitch * 2) - (refdata->ci_dstwidth * 2 * refdata->ci_delta); opitch -= refdata->ci_dstwidth * 3; // bytes at end of each row
pIn = pBits; pOut = pCvtBits; for (y = 0; y < refdata->ci_dstheight; y++) { for (x = 0; x < refdata->ci_dstwidth; x++) { refdata->ci_Copy(&pIn, &pOut); pIn += refdata->ci_delta; // skip to next pixel
} pIn += ipitch; // get to start of row after next
pOut += opitch; // get to start of next row
}
pbsIn->UnlockBits(NULL, pBits); pbsOut->UnlockBits(NULL, pCvtBits); return TRUE; }
// worker routine to shrink an RGB4 in half (width & height)
// result is RGB24
BOOL DoHalfSize4( IBitmapSurface* pbsIn, IBitmapSurface* pbsOut, PCONVERTINFO refdata ) { LPBYTE pBits, pCvtBits, pIn, pOut; long ipitch, opitch; long x, y; BYTE pixel;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch); pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
ipitch = (ipitch * 2) - refdata->ci_dstwidth; opitch -= refdata->ci_dstwidth * 3; // bytes at end of each row
pIn = pBits; pOut = pCvtBits; for (y = 0; y < refdata->ci_dstheight; y++) { for (x = 0; x < refdata->ci_dstwidth; x++) { pixel = (*pIn++/16) & 15; *pOut++ = refdata->ci_colortable[pixel].rgbBlue; *pOut++ = refdata->ci_colortable[pixel].rgbGreen; *pOut++ = refdata->ci_colortable[pixel].rgbRed; } pIn += ipitch; // get to start of row after next
pOut += opitch; // get to start of next row
}
pbsIn->UnlockBits(NULL, pBits); pbsOut->UnlockBits(NULL, pCvtBits); return TRUE; }
// worker routine to shrink an RGB8 in half (width & height)
// result is RGB24
BOOL DoHalfSize8( IBitmapSurface* pbsIn, IBitmapSurface* pbsOut, PCONVERTINFO refdata ) { LPBYTE pBits, pCvtBits, pIn, pOut; long ipitch, opitch; long x, y;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch); pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
ipitch = (ipitch * 2) - refdata->ci_dstwidth * 2; opitch -= refdata->ci_dstwidth * 3; // bytes at end of each row
pIn = pBits; pOut = pCvtBits; for (y = 0; y < refdata->ci_dstheight; y++) { for (x = 0; x < refdata->ci_dstwidth; x++) { *pOut++ = refdata->ci_colortable[*pIn].rgbBlue; *pOut++ = refdata->ci_colortable[*pIn].rgbGreen; *pOut++ = refdata->ci_colortable[*pIn].rgbRed; pIn += 2; } pIn += ipitch; // get to start of row after next
pOut += opitch; // get to start of next row
}
pbsIn->UnlockBits(NULL, pBits); pbsOut->UnlockBits(NULL, pCvtBits); return TRUE; }
// worker routine to shrink a YVU9 or YUV12 in half (width & height)
// result is YVU9 or YUV12
BOOL DoHalfSizeYUVPlanar( IBitmapSurface* pbsIn, IBitmapSurface* pbsOut, PCONVERTINFO refdata ) { LPBYTE pBits, pCvtBits, pIn, pIn2, pOut; long pitch; long x, y, w, h;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &pitch); pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &pitch);
// Do the Y component first
pitch = refdata->ci_width * 2 - refdata->ci_dstwidth * 2; // amount to add for skip
pIn = pBits; pOut = pCvtBits; for (y = 0; y < refdata->ci_dstheight; y++) { for (x = 0; x < refdata->ci_dstwidth; x++) { *pOut++ = *pIn++; pIn++; // skip to next pixel
} pIn += pitch; // get to start of row after next
} // if source height is odd, then we've added 1 line too many onto pIn
if (refdata->ci_height & 1) pIn -= refdata->ci_width;
// Do the first color component next
h = refdata->ci_dstheight / refdata->ci_UVDownSampling; w = refdata->ci_dstwidth / refdata->ci_UVDownSampling; pitch = refdata->ci_width / refdata->ci_UVDownSampling * 2 - w * 2; pIn2 = pIn + refdata->ci_width / refdata->ci_UVDownSampling; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { *pOut++ = (*pIn++ + *(++pIn) + *pIn2++ + *(++pIn2)) / 4; } pIn += pitch; // get to start of row after next
pIn2 += pitch; // get to start of row after next
} // if source height is odd, then we've added 1 line too many onto pIn
if (refdata->ci_height & 1) pIn -= refdata->ci_width / refdata->ci_UVDownSampling; // Do the second color component next
pIn2 = pIn + refdata->ci_width / refdata->ci_UVDownSampling; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { *pOut++ = (*pIn++ + *(++pIn) + *pIn2++ + *(++pIn2)) / 4; } pIn += pitch; // get to start of row after next
pIn2 += pitch; // get to start of row after next
}
pbsIn->UnlockBits(NULL, pBits); pbsOut->UnlockBits(NULL, pCvtBits); return TRUE; }
// worker routine to shrink a YUV packed DIB in half (width & height)
// result is YUY2, or UYVY
BOOL DoHalfSizeYUVPacked( IBitmapSurface* pbsIn, IBitmapSurface* pbsOut, PCONVERTINFO refdata ) { LPBYTE pBits, pCvtBits; LPDWORD pIn, pOut; long ipitch, opitch; long x, y; long prelines, postlines, prebytes, postbytes, ibytes, obytes;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch); pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
pIn = (LPDWORD)pBits; pOut = (LPDWORD)pCvtBits;
// copy one line out of two
for (y = 0; y < refdata->ci_dstheight; y++) { // copy one pixel out of two
for (x = 0; x < refdata->ci_dstwidth / 2; x++) { *pOut++ = *pIn++; pIn++; // skip to next pixel
} pIn += refdata->ci_width / 2; // skip to next line
}
pbsIn->UnlockBits(NULL, pBits); pbsOut->UnlockBits(NULL, pCvtBits);
return TRUE; }
// worker routine to shrink an RGB16, RGB24 or RGB32 by cropping
// result is RGB24
BOOL Crop( IBitmapSurface* pbsIn, IBitmapSurface* pbsOut, PCONVERTINFO refdata ) { LPBYTE pBits, pCvtBits, pIn, pOut; long ipitch, opitch; long extra, x, y;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch); pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
pOut = pCvtBits;
// pIn starts by skipping half of the height change
pIn = pBits + (refdata->ci_height - refdata->ci_dstheight) / 2 * ipitch;
// extra = # of source bytes per scan line that are to be cropped
extra = (refdata->ci_width - refdata->ci_dstwidth) * refdata->ci_delta;
// advance pIn by half of extra to crop left most pixels
pIn += extra / 2;
// adjust ipitch so we can add it at the end of each scan to get to start of next scan
ipitch = ipitch - (refdata->ci_width * refdata->ci_delta) + extra; opitch -= refdata->ci_dstwidth * 3; // bytes at end of each row
for (y = 0; y < refdata->ci_dstheight; y++) { for (x = 0; x < refdata->ci_dstwidth; x++) { refdata->ci_Copy(&pIn, &pOut); } pIn += ipitch; // get to start of next row
pOut += opitch; // get to start of next row
}
pbsIn->UnlockBits(NULL, pBits); pbsOut->UnlockBits(NULL, pCvtBits); return TRUE; }
// worker routine to shrink an RGB4 by cropping
// result is RGB24
BOOL Crop4( IBitmapSurface* pbsIn, IBitmapSurface* pbsOut, PCONVERTINFO refdata ) { LPBYTE pBits, pCvtBits, pIn, pOut; long ipitch, opitch; long extra, x, y; BYTE val, pixel;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch); pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
pOut = pCvtBits;
// pIn starts by skipping half of the height change
pIn = pBits + (refdata->ci_height - refdata->ci_dstheight) / 2 * ipitch;
// extra = # of source bytes per scan line that are to be cropped
extra = (refdata->ci_width - refdata->ci_dstwidth) / 2;
// advance pIn by half of extra to crop left most pixels
pIn += extra / 2;
// adjust ipitch so we can add it at the end of each scan to get to start of next scan
ipitch = ipitch - (refdata->ci_width / 2) + extra; opitch -= refdata->ci_dstwidth * 3; // bytes at end of each row
for (y = 0; y < refdata->ci_dstheight; y++) { for (x = 0; x < refdata->ci_dstwidth/2; x++) { val = *pIn++; pixel = (val/16) & 15; *pOut++ = refdata->ci_colortable[pixel].rgbBlue; *pOut++ = refdata->ci_colortable[pixel].rgbGreen; *pOut++ = refdata->ci_colortable[pixel].rgbRed; pixel = val & 15; *pOut++ = refdata->ci_colortable[pixel].rgbBlue; *pOut++ = refdata->ci_colortable[pixel].rgbGreen; *pOut++ = refdata->ci_colortable[pixel].rgbRed; } pIn += ipitch; // get to start of next row
pOut += opitch; // get to start of next row
}
pbsIn->UnlockBits(NULL, pBits); pbsOut->UnlockBits(NULL, pCvtBits); return TRUE; }
// worker routine to shrink an RGB8 by cropping
// result is RGB24
BOOL Crop8( IBitmapSurface* pbsIn, IBitmapSurface* pbsOut, PCONVERTINFO refdata ) { LPBYTE pBits, pCvtBits, pIn, pOut; long ipitch, opitch; long extra, x, y;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch); pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
pOut = pCvtBits;
// pIn starts by skipping half of the height change
pIn = pBits + (refdata->ci_height - refdata->ci_dstheight) / 2 * ipitch;
// extra = # of source bytes per scan line that are to be cropped
extra = refdata->ci_width - refdata->ci_dstwidth;
// advance pIn by half of extra to crop left most pixels
pIn += extra / 2;
// adjust ipitch so we can add it at the end of each scan to get to start of next scan
ipitch = ipitch - refdata->ci_width + extra; opitch -= refdata->ci_dstwidth * 3; // bytes at end of each row
for (y = 0; y < refdata->ci_dstheight; y++) { for (x = 0; x < refdata->ci_dstwidth; x++) { *pOut++ = refdata->ci_colortable[*pIn].rgbBlue; *pOut++ = refdata->ci_colortable[*pIn].rgbGreen; *pOut++ = refdata->ci_colortable[*pIn++].rgbRed; } pIn += ipitch; // get to start of next row
pOut += opitch; // get to start of next row
}
pbsIn->UnlockBits(NULL, pBits); pbsOut->UnlockBits(NULL, pCvtBits); return TRUE; }
// worker routine to shrink a YVU9 or YUV12 by cropping
// result is YVU9 or YUV12
BOOL CropYUVPlanar( IBitmapSurface* pbsIn, IBitmapSurface* pbsOut, PCONVERTINFO refdata ) { LPBYTE pBits, pCvtBits, pIn, pOut; long pitch, prelines, bytes, prebytes; long extra, y;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &pitch); pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &pitch);
pOut = pCvtBits;
// pIn starts by skipping half of the height change
prelines = ((refdata->ci_height - refdata->ci_dstheight) >> 1) / refdata->ci_UVDownSampling * refdata->ci_UVDownSampling; pIn = pBits + prelines * refdata->ci_width;
// extra = # of source bytes per scan line that are to be cropped
extra = refdata->ci_width - refdata->ci_dstwidth; prebytes = (extra >> 1) / refdata->ci_UVDownSampling * refdata->ci_UVDownSampling;
// advance pIn by half of extra to crop left most pixels
pIn += prebytes;
// Do the Y component first
pitch = extra + refdata->ci_dstwidth; for (y = 0; y < refdata->ci_dstheight; y++) { CopyMemory (pOut, pIn, refdata->ci_dstwidth); pIn += pitch; pOut += refdata->ci_dstwidth; }
// Do the first color component next
prelines /= refdata->ci_UVDownSampling; prebytes /= refdata->ci_UVDownSampling; pIn = pBits + (refdata->ci_width * refdata->ci_height) + // skip Y section
prelines * refdata->ci_width / refdata->ci_UVDownSampling + // skip half of the crop lines
prebytes; // skip half of the crop pixels
pitch /= refdata->ci_UVDownSampling; bytes = refdata->ci_dstwidth / refdata->ci_UVDownSampling; for (y=0; y < refdata->ci_dstheight / refdata->ci_UVDownSampling; y++) { CopyMemory (pOut, pIn, bytes); pIn += pitch; pOut += bytes; }
// Do the second color component next
pIn = pBits + (refdata->ci_width * refdata->ci_height) + // skip Y section
(refdata->ci_width * refdata->ci_height) / (refdata->ci_UVDownSampling * refdata->ci_UVDownSampling) + // skip first color component section
prelines * refdata->ci_width / refdata->ci_UVDownSampling + // skip half of the crop lines
prebytes; // skip half of the crop pixels
for (y=0; y < refdata->ci_dstheight / refdata->ci_UVDownSampling; y++) { CopyMemory (pOut, pIn, bytes); pIn += pitch; pOut += bytes; }
pbsIn->UnlockBits(NULL, pBits); pbsOut->UnlockBits(NULL, pCvtBits); return TRUE; }
// worker routine to shrink a YUV packed DIB by cropping
// result is YUY2 or UYVY
BOOL CropYUVPacked( IBitmapSurface* pbsIn, IBitmapSurface* pbsOut, PCONVERTINFO refdata ) { LPBYTE pBits, pCvtBits, pIn, pOut; long ipitch, opitch; long extra, x, y;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch); pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
pOut = pCvtBits;
// pIn starts by skipping half of the height change
pIn = pBits + (refdata->ci_height - refdata->ci_dstheight) * refdata->ci_width * 2;
// extra = # of source bytes per scan line that are to be cropped
extra = (refdata->ci_width - refdata->ci_dstwidth) * 2;
// advance pIn by half of extra to crop left most pixels
pIn += extra / 2;
// adjust ipitch so we can add it at the end of each scan to get to start of next scan
ipitch = refdata->ci_width * 2; opitch = refdata->ci_dstwidth * 2; // bytes at end of each row
for (y = 0; y < refdata->ci_dstheight; y++) { for (x = 0; x < refdata->ci_dstwidth; x++) { CopyMemory(pOut, pIn, refdata->ci_dstwidth * 2); } pIn += ipitch; // get to start of next row
pOut += opitch; // get to start of next row
}
pbsIn->UnlockBits(NULL, pBits); pbsOut->UnlockBits(NULL, pCvtBits); return TRUE; }
// routine to prepare for calling shrink worker routines
// it allocates and initializes a reference data structure
BOOL InitShrink( LPBITMAPINFOHEADER lpbmhIn, long desiredwidth, long desiredheight, LPBITMAPINFOHEADER *lpbmhOut, FRAMECONVERTPROC **convertproc, LPVOID *refdata ) { PCONVERTINFO pcvt; DWORD dwSize; long crop_ratio, black_ratio, target_size;
*convertproc = NULL; *refdata = NULL;
if ((lpbmhIn->biCompression != VIDEO_FORMAT_BI_RGB) && (lpbmhIn->biCompression != VIDEO_FORMAT_YVU9) && (lpbmhIn->biCompression != VIDEO_FORMAT_YUY2) && (lpbmhIn->biCompression != VIDEO_FORMAT_UYVY) && (lpbmhIn->biCompression != VIDEO_FORMAT_I420) && (lpbmhIn->biCompression != VIDEO_FORMAT_IYUV)) return FALSE;
// calculate size of convertinfo struct, if we need a colortable, then add 256 entries
// else subtract off the 1 built into the struct definition
dwSize = sizeof(CONVERTINFO) - sizeof(RGBQUAD); if (lpbmhIn->biBitCount <= 8) dwSize += 256 * sizeof(RGBQUAD);
// for RGB, and YUV input formats, we know that the output format will never need
// an attached color table, so we can allocate lpbmhOut without one
if ((pcvt = (PCONVERTINFO)LocalAlloc(LPTR, dwSize)) && (*lpbmhOut = (LPBITMAPINFOHEADER)LocalAlloc(LPTR, lpbmhIn->biSize))) { CopyMemory(*lpbmhOut, lpbmhIn, lpbmhIn->biSize); pcvt->ci_width = lpbmhIn->biWidth; pcvt->ci_height = lpbmhIn->biHeight;
target_size = desiredwidth * desiredheight; crop_ratio = pcvt->ci_width * pcvt->ci_height; black_ratio = ((target_size - (crop_ratio / 4)) * 100) / target_size; crop_ratio = ((crop_ratio - target_size) * 100) / crop_ratio; if (crop_ratio < black_ratio) { // cropping the source makes more sense
pcvt->ci_dstwidth = desiredwidth; pcvt->ci_dstheight = desiredheight; crop_ratio = 1; // flag that we'll crop
} else { // halfsizing makes more sense
pcvt->ci_dstwidth = lpbmhIn->biWidth / 2; pcvt->ci_dstheight = lpbmhIn->biHeight / 2; crop_ratio = 0; // flag that we'll half size
} (*lpbmhOut)->biWidth = pcvt->ci_dstwidth; (*lpbmhOut)->biHeight = pcvt->ci_dstheight;
// copy colortable from input bitmapinfoheader
if (lpbmhIn->biBitCount <= 8) CopyMemory(&pcvt->ci_colortable[0], (LPBYTE)lpbmhIn + lpbmhIn->biSize, 256 * sizeof(RGBQUAD));
if (lpbmhIn->biCompression == VIDEO_FORMAT_BI_RGB) { (*lpbmhOut)->biBitCount = 24; (*lpbmhOut)->biSizeImage = pcvt->ci_dstwidth * pcvt->ci_dstheight * 3; if (lpbmhIn->biBitCount == 4) { if (crop_ratio) *convertproc = (FRAMECONVERTPROC*)&Crop4; else *convertproc = (FRAMECONVERTPROC*)&DoHalfSize4; } else if (lpbmhIn->biBitCount == 8) { if (crop_ratio) *convertproc = (FRAMECONVERTPROC*)&Crop8; else *convertproc = (FRAMECONVERTPROC*)&DoHalfSize8; } else { if (crop_ratio) *convertproc = (FRAMECONVERTPROC*)&Crop; else *convertproc = (FRAMECONVERTPROC*)&DoHalfSize; pcvt->ci_delta = lpbmhIn->biBitCount / 8; if (lpbmhIn->biBitCount == 16) { pcvt->ci_Copy = &Copy16; } else if (lpbmhIn->biBitCount == 24) { pcvt->ci_Copy = &Copy24; } else if (lpbmhIn->biBitCount == 32) { pcvt->ci_Copy = &Copy32; } } } else if (lpbmhIn->biCompression == VIDEO_FORMAT_YVU9) { (*lpbmhOut)->biBitCount = lpbmhIn->biBitCount; (*lpbmhOut)->biSizeImage = pcvt->ci_dstwidth * pcvt->ci_dstheight + (pcvt->ci_dstwidth * pcvt->ci_dstheight)/8; if (crop_ratio) *convertproc = (FRAMECONVERTPROC*)&CropYUVPlanar; else *convertproc = (FRAMECONVERTPROC*)&DoHalfSizeYUVPlanar; pcvt->ci_UVDownSampling = 4; } else if ((lpbmhIn->biCompression == VIDEO_FORMAT_YUY2) || (lpbmhIn->biCompression == VIDEO_FORMAT_UYVY)) { (*lpbmhOut)->biBitCount = lpbmhIn->biBitCount; (*lpbmhOut)->biSizeImage = (DWORD)WIDTHBYTES(pcvt->ci_dstwidth * lpbmhIn->biBitCount) * pcvt->ci_dstheight; if (crop_ratio) *convertproc = (FRAMECONVERTPROC*)&CropYUVPacked; else *convertproc = (FRAMECONVERTPROC*)&DoHalfSizeYUVPacked; } else if ((lpbmhIn->biCompression == VIDEO_FORMAT_I420) || (lpbmhIn->biCompression == VIDEO_FORMAT_IYUV)) { (*lpbmhOut)->biBitCount = lpbmhIn->biBitCount; (*lpbmhOut)->biSizeImage = (DWORD)WIDTHBYTES(pcvt->ci_dstwidth * lpbmhIn->biBitCount) * pcvt->ci_dstheight; if (crop_ratio) *convertproc = (FRAMECONVERTPROC*)&CropYUVPlanar; else *convertproc = (FRAMECONVERTPROC*)&DoHalfSizeYUVPlanar; pcvt->ci_UVDownSampling = 2; }
*refdata = (LPVOID)pcvt; return TRUE; } else { if (pcvt) LocalFree((HANDLE)pcvt); } return FALSE; }
// worker routine to expand an RGB16, RGB24 or RGB32 by copying source into middle of destination
// result is RGB24
BOOL DoBlackBar( IBitmapSurface* pbsIn, IBitmapSurface* pbsOut, PCONVERTINFO refdata ) { LPBYTE pBits, pCvtBits, pIn, pOut; long ipitch, opitch, oextra; long x, y; long prelines, postlines, prebytes, postbytes, bytes;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch); pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
prelines = (refdata->ci_dstheight - refdata->ci_height) / 2; postlines = refdata->ci_dstheight - refdata->ci_height - prelines;
prebytes = (refdata->ci_dstwidth - refdata->ci_width) / 2; postbytes = (refdata->ci_dstwidth - refdata->ci_width - prebytes) * 3; prebytes *= 3;
ipitch -= refdata->ci_width * refdata->ci_delta; // bytes at end of each src row
bytes = refdata->ci_dstwidth * 3; oextra = opitch - bytes + postbytes; // bytes at end of each dst row
pIn = pBits; pOut = pCvtBits;
// do blank lines at front of destination
for (y = 0; y < prelines; y++) { ZeroMemory (pOut, bytes); pOut += opitch; }
// copy source lines with blank space at front and rear
for (y = 0; y < refdata->ci_height; y++) { ZeroMemory (pOut, prebytes); pOut += prebytes;
for (x = 0; x < refdata->ci_width; x++) { refdata->ci_Copy(&pIn, &pOut); }
ZeroMemory (pOut, postbytes); pIn += ipitch; pOut += oextra; }
// do blank lines at end of destination
for (y = 0; y < postlines; y++) { ZeroMemory (pOut, bytes); pOut += opitch; }
pbsIn->UnlockBits(NULL, pBits); pbsOut->UnlockBits(NULL, pCvtBits); return TRUE; }
// worker routine to expand an RGB4 by copying source into middle of destination
// result is RGB24
BOOL DoBlackBar4( IBitmapSurface* pbsIn, IBitmapSurface* pbsOut, PCONVERTINFO refdata ) { LPBYTE pBits, pCvtBits, pIn, pOut; long ipitch, opitch, oextra; long x, y; long prelines, postlines, prebytes, postbytes, bytes; BYTE val, pixel;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch); pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
prelines = (refdata->ci_dstheight - refdata->ci_height) / 2; postlines = refdata->ci_dstheight - refdata->ci_height - prelines;
prebytes = (refdata->ci_dstwidth - refdata->ci_width) / 2; postbytes = (refdata->ci_dstwidth - refdata->ci_width - prebytes) * 3; prebytes *= 3;
ipitch -= refdata->ci_width/2; // bytes at end of each src row
bytes = refdata->ci_dstwidth * 3; oextra = opitch - bytes + postbytes; // bytes at end of each dst row
pIn = pBits; pOut = pCvtBits;
// do blank lines at front of destination
for (y = 0; y < prelines; y++) { ZeroMemory (pOut, bytes); pOut += opitch; }
// copy source lines with blank space at front and rear
for (y = 0; y < refdata->ci_height; y++) { ZeroMemory (pOut, prebytes); pOut += prebytes;
for (x = 0; x < refdata->ci_width/2; x++) { val = *pIn++; pixel = (val/16) & 15; *pOut++ = refdata->ci_colortable[pixel].rgbBlue; *pOut++ = refdata->ci_colortable[pixel].rgbGreen; *pOut++ = refdata->ci_colortable[pixel].rgbRed; pixel = val & 15; *pOut++ = refdata->ci_colortable[pixel].rgbBlue; *pOut++ = refdata->ci_colortable[pixel].rgbGreen; *pOut++ = refdata->ci_colortable[pixel].rgbRed; }
ZeroMemory (pOut, postbytes); pIn += ipitch; pOut += oextra; }
// do blank lines at end of destination
for (y = 0; y < postlines; y++) { ZeroMemory (pOut, bytes); pOut += opitch; }
pbsIn->UnlockBits(NULL, pBits); pbsOut->UnlockBits(NULL, pCvtBits); return TRUE; }
// worker routine to expand an RGB8 by copying source into middle of destination
// result is RGB24
BOOL DoBlackBar8( IBitmapSurface* pbsIn, IBitmapSurface* pbsOut, PCONVERTINFO refdata ) { LPBYTE pBits, pCvtBits, pIn, pOut; long ipitch, opitch, oextra; long x, y; long prelines, postlines, prebytes, postbytes, bytes;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch); pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
prelines = (refdata->ci_dstheight - refdata->ci_height) / 2; postlines = refdata->ci_dstheight - refdata->ci_height - prelines;
prebytes = (refdata->ci_dstwidth - refdata->ci_width) / 2; postbytes = (refdata->ci_dstwidth - refdata->ci_width - prebytes) * 3; prebytes *= 3;
ipitch -= refdata->ci_width; // bytes at end of each src row
bytes = refdata->ci_dstwidth * 3; oextra = opitch - bytes + postbytes; // bytes at end of each dst row
pIn = pBits; pOut = pCvtBits;
// do blank lines at front of destination
for (y = 0; y < prelines; y++) { ZeroMemory (pOut, bytes); pOut += opitch; }
// copy source lines with blank space at front and rear
for (y = 0; y < refdata->ci_height; y++) { ZeroMemory (pOut, prebytes); pOut += prebytes;
for (x = 0; x < refdata->ci_width; x++) { *pOut++ = refdata->ci_colortable[*pIn].rgbBlue; *pOut++ = refdata->ci_colortable[*pIn].rgbGreen; *pOut++ = refdata->ci_colortable[*pIn++].rgbRed; }
ZeroMemory (pOut, postbytes); pIn += ipitch; pOut += oextra; }
// do blank lines at end of destination
for (y = 0; y < postlines; y++) { ZeroMemory (pOut, bytes); pOut += opitch; }
pbsIn->UnlockBits(NULL, pBits); pbsOut->UnlockBits(NULL, pCvtBits); return TRUE; }
// worker routine to expand a YVU9 or YUV12 by copying source into middle of destination
// result is YVU9 or YUV12
BOOL DoBlackBarYUVPlanar( IBitmapSurface* pbsIn, IBitmapSurface* pbsOut, PCONVERTINFO refdata ) { LPBYTE pBits, pCvtBits; LONG prelines, postlines, bytesperpixel, prebytes, postbytes, y, bytes; LONG prelinebytes, postlinebytes; LPBYTE lpsrc, lpdst;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &bytes); pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &bytes);
lpsrc = pBits; lpdst = pCvtBits;
// Do the Y component first
prelines = ((refdata->ci_dstheight - refdata->ci_height) / (refdata->ci_UVDownSampling << 1)) * refdata->ci_UVDownSampling; postlines = refdata->ci_dstheight - refdata->ci_height - prelines;
prebytes = ((refdata->ci_dstwidth - refdata->ci_width) / (refdata->ci_UVDownSampling << 1)) * refdata->ci_UVDownSampling; postbytes = (refdata->ci_dstwidth - refdata->ci_width - prebytes);
bytes = prelines * refdata->ci_dstwidth + prebytes; FillMemory (lpdst, bytes, 0x10); lpdst += bytes;
bytes = refdata->ci_width; prebytes += postbytes; for (y=0; y < refdata->ci_height; y++) { MoveMemory (lpdst, lpsrc, bytes); lpsrc += bytes; lpdst += bytes; FillMemory (lpdst, prebytes, 0x10); lpdst += prebytes; }
// already filled the prebytes of the first postline in loop above
prebytes -= postbytes; bytes = postlines * refdata->ci_dstwidth - prebytes; FillMemory (lpdst, bytes, (BYTE)0x10); lpdst += bytes;
// Do the first color component next
prelines /= refdata->ci_UVDownSampling; postlines = refdata->ci_dstheight / refdata->ci_UVDownSampling - refdata->ci_height / refdata->ci_UVDownSampling - prelines;
prebytes = prebytes / refdata->ci_UVDownSampling; postbytes = refdata->ci_dstwidth / refdata->ci_UVDownSampling - refdata->ci_width / refdata->ci_UVDownSampling - prebytes;
prelinebytes = prelines * refdata->ci_dstwidth / refdata->ci_UVDownSampling + prebytes; FillMemory (lpdst, prelinebytes, 0x80); lpdst += prelinebytes; bytes = refdata->ci_width / refdata->ci_UVDownSampling; prebytes += postbytes; for (y=0; y < refdata->ci_height / refdata->ci_UVDownSampling; y++) { MoveMemory (lpdst, lpsrc, bytes); lpsrc += bytes; lpdst += bytes; FillMemory (lpdst, prebytes, 0x80); lpdst += prebytes; }
// already filled the prebytes of the first postline in loop above
postlinebytes = postlines * refdata->ci_dstwidth / refdata->ci_UVDownSampling - (prebytes - postbytes); FillMemory (lpdst, postlinebytes, 0x80); lpdst += postlinebytes; // Do the second color component next
FillMemory (lpdst, prelinebytes, 0x80); lpdst += prelinebytes; for (y=0; y < refdata->ci_height / refdata->ci_UVDownSampling; y++) { MoveMemory (lpdst, lpsrc, bytes); lpsrc += bytes; lpdst += bytes; FillMemory (lpdst, prebytes, 0x80); lpdst += prebytes; } FillMemory (lpdst, postlinebytes, 0x80);
pbsIn->UnlockBits(NULL, pBits); pbsOut->UnlockBits(NULL, pCvtBits); return TRUE; }
// worker routine to expand a YUV packed DIB by copying source into middle of destination
// result is YUY2 or UYVY
BOOL DoBlackBarYUVPacked( IBitmapSurface* pbsIn, IBitmapSurface* pbsOut, PCONVERTINFO refdata ) { LPBYTE pBits, pCvtBits, pIn, pOut; long ipitch, opitch; long x, y; long prelines, postlines, prebytes, postbytes, ibytes, obytes;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch); pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
prelines = (refdata->ci_dstheight - refdata->ci_height) / 2; postlines = refdata->ci_dstheight - refdata->ci_height - prelines;
prebytes = (refdata->ci_dstwidth - refdata->ci_width) / 2; postbytes = (refdata->ci_dstwidth - refdata->ci_width - prebytes) / 2; prebytes /= 2;
ibytes = refdata->ci_width * 2; obytes = refdata->ci_dstwidth / 2; pIn = pBits; pOut = pCvtBits;
// do blank lines at front of destination
for (y = 0; y < prelines; y++) { for (x = 0; x < obytes; x++) { *(DWORD *)pOut = refdata->ci_ZeroingDWORD; pOut += sizeof(DWORD); } }
// copy source lines with blank space at front and rear
for (y = 0; y < refdata->ci_height; y++) { for (x = 0; x < prebytes; x++) { *(DWORD *)pOut = refdata->ci_ZeroingDWORD; pOut += sizeof(DWORD); }
CopyMemory(pOut, pIn, ibytes); pOut += ibytes; pIn += ibytes;
for (x = 0; x < postbytes; x++) { *(DWORD *)pOut = refdata->ci_ZeroingDWORD; pOut += sizeof(DWORD); } }
// do blank lines at end of destination
for (y = 0; y < postlines; y++) { for (x = 0; x < obytes; x++) { *(DWORD *)pOut = refdata->ci_ZeroingDWORD; pOut += sizeof(DWORD); } }
pbsIn->UnlockBits(NULL, pBits); pbsOut->UnlockBits(NULL, pCvtBits);
return TRUE; }
// routine to prepare for calling blackbar worker routines
// it allocates and initializes a reference data structure
BOOL InitBlackbar( LPBITMAPINFOHEADER lpbmhIn, long desiredwidth, long desiredheight, LPBITMAPINFOHEADER *lpbmhOut, FRAMECONVERTPROC **convertproc, LPVOID *refdata ) { PCONVERTINFO pcvt; DWORD dwSize;
*convertproc = NULL; *refdata = NULL;
if ((lpbmhIn->biCompression != VIDEO_FORMAT_BI_RGB) && (lpbmhIn->biCompression != VIDEO_FORMAT_YVU9) && (lpbmhIn->biCompression != VIDEO_FORMAT_YUY2) && (lpbmhIn->biCompression != VIDEO_FORMAT_UYVY) && (lpbmhIn->biCompression != VIDEO_FORMAT_I420) && (lpbmhIn->biCompression != VIDEO_FORMAT_IYUV)) return FALSE;
// calculate size of convertinfo struct, if we need a colortable, then add 256 entries
// else subtract off the 1 built into the struct definition
dwSize = sizeof(CONVERTINFO) - sizeof(RGBQUAD); if (lpbmhIn->biBitCount <= 8) dwSize += 256 * sizeof(RGBQUAD);
// for RGB, YUV input formats, we know that the output format will never need
// an attached color table, so we can allocate lpbmhOut without one
if ((pcvt = (PCONVERTINFO)LocalAlloc(LPTR, dwSize)) && (*lpbmhOut = (LPBITMAPINFOHEADER)LocalAlloc(LPTR, lpbmhIn->biSize))) { CopyMemory(*lpbmhOut, lpbmhIn, lpbmhIn->biSize); pcvt->ci_width = lpbmhIn->biWidth; pcvt->ci_height = lpbmhIn->biHeight; pcvt->ci_dstwidth = desiredwidth; pcvt->ci_dstheight = desiredheight; (*lpbmhOut)->biWidth = desiredwidth; (*lpbmhOut)->biHeight = desiredheight;
// copy colortable from input bitmapinfoheader
if (lpbmhIn->biBitCount <= 8) CopyMemory(&pcvt->ci_colortable[0], (LPBYTE)lpbmhIn + lpbmhIn->biSize, 256 * sizeof(RGBQUAD));
if (lpbmhIn->biCompression == VIDEO_FORMAT_BI_RGB) { (*lpbmhOut)->biBitCount = 24; (*lpbmhOut)->biSizeImage = desiredwidth * desiredheight * 3;
if (lpbmhIn->biBitCount == 4) { *convertproc = (FRAMECONVERTPROC*)&DoBlackBar4; } else if (lpbmhIn->biBitCount == 8) { *convertproc = (FRAMECONVERTPROC*)&DoBlackBar8; } else { *convertproc = (FRAMECONVERTPROC*)&DoBlackBar; pcvt->ci_delta = lpbmhIn->biBitCount / 8; if (lpbmhIn->biBitCount == 16) { pcvt->ci_Copy = &Copy16; } else if (lpbmhIn->biBitCount == 24) { pcvt->ci_Copy = &Copy24; } else if (lpbmhIn->biBitCount == 32) { pcvt->ci_Copy = &Copy32; } } } else if (lpbmhIn->biCompression == VIDEO_FORMAT_YVU9) { (*lpbmhOut)->biBitCount = lpbmhIn->biBitCount; (*lpbmhOut)->biSizeImage = desiredwidth * desiredheight + (desiredwidth * desiredheight)/8; *convertproc = (FRAMECONVERTPROC*)&DoBlackBarYUVPlanar; pcvt->ci_UVDownSampling = 4; } else if (lpbmhIn->biCompression == VIDEO_FORMAT_YUY2) { (*lpbmhOut)->biBitCount = lpbmhIn->biBitCount; (*lpbmhOut)->biSizeImage = (DWORD)WIDTHBYTES(desiredwidth * lpbmhIn->biBitCount) * desiredheight; pcvt->ci_ZeroingDWORD = 0x80108010; *convertproc = (FRAMECONVERTPROC*)&DoBlackBarYUVPacked; } else if (lpbmhIn->biCompression == VIDEO_FORMAT_UYVY) { (*lpbmhOut)->biBitCount = lpbmhIn->biBitCount; (*lpbmhOut)->biSizeImage = (DWORD)WIDTHBYTES(desiredwidth * lpbmhIn->biBitCount) * desiredheight; pcvt->ci_ZeroingDWORD = 0x10801080; *convertproc = (FRAMECONVERTPROC*)&DoBlackBarYUVPacked; } else if ((lpbmhIn->biCompression == VIDEO_FORMAT_I420) || (lpbmhIn->biCompression == VIDEO_FORMAT_IYUV)) { (*lpbmhOut)->biBitCount = lpbmhIn->biBitCount; (*lpbmhOut)->biSizeImage = (DWORD)WIDTHBYTES(desiredwidth * lpbmhIn->biBitCount) * desiredheight; *convertproc = (FRAMECONVERTPROC*)&DoBlackBarYUVPlanar; pcvt->ci_UVDownSampling = 2; }
*refdata = (LPVOID)pcvt; return TRUE; } else { if (pcvt) LocalFree((HANDLE)pcvt); } return FALSE; }
#ifdef ENABLE_ZOOM_CODE
BOOL Zoom4( IBitmapSurface* pbsIn, IBitmapSurface* pbsOut, PZOOMCONVERTINFO refdata ) { return FALSE; }
BOOL Zoom8( IBitmapSurface* pbsIn, IBitmapSurface* pbsOut, PZOOMCONVERTINFO refdata ) { return FALSE; }
BOOL Zoom16( IBitmapSurface* pbsIn, IBitmapSurface* pbsOut, PZOOMCONVERTINFO refdata ) { return FALSE; }
BOOL Zoom24( IBitmapSurface* pbsIn, IBitmapSurface* pbsOut, PZOOMCONVERTINFO refdata ) { LPBYTE pBits, pCvtBits, pIn1, pIn2, pTmp, pOut; ROW_VALUES *rptr; long i, j, yfac_inv, src_y, src_y_i, q, q1; long a;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &i); pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &i);
pOut = pCvtBits;
yfac_inv = refdata->ci_height * 256 / refdata->ci_dstheight;
for (i = 0; i < refdata->ci_dstheight; i++) { src_y = i * yfac_inv; src_y_i = src_y / 256; q = src_y - src_y_i * 256; q1 = 256 - q; rptr = refdata->ci_rptr;
pIn1 = pBits + src_y_i * refdata->ci_width * 3; pIn2 = pIn1 + refdata->ci_width * 3; for (j = 0; j < refdata->ci_dstwidth; j++, rptr++) { a = rptr->x_i * 3; pIn1 += a; pIn2 += a; a = (((*pIn1) * rptr->p1 + (*(pIn1+3)) * rptr->p) * q1 + ((*pIn2) * rptr->p1 + (*(pIn2+3)) * rptr->p) * q) / 256 / 256; if (a > 256) a = 255; *pOut++ = (BYTE)a; // blue
pIn1++; pIn2++;
a = (((*pIn1) * rptr->p1 + (*(pIn1+3)) * rptr->p) * q1 + ((*pIn2) * rptr->p1 + (*(pIn2+3)) * rptr->p) * q) / 256 / 256; if (a > 256) a = 255; *pOut++ = (BYTE)a; // green
pIn1++; pIn2++;
a = (((*pIn1) * rptr->p1 + (*(pIn1+3)) * rptr->p) * q1 + ((*pIn2) * rptr->p1 + (*(pIn2+3)) * rptr->p) * q) / 256 / 256; if (a > 256) a = 255; *pOut++ = (BYTE)a; // red
pIn1 -= 2; pIn2 -= 2; } }
pbsIn->UnlockBits(NULL, pBits); pbsOut->UnlockBits(NULL, pCvtBits); return TRUE; }
BOOL ZoomYVU9( IBitmapSurface* pbsIn, IBitmapSurface* pbsOut, PZOOMCONVERTINFO refdata ) { LPBYTE pBits, pCvtBits, pIn1, pIn2, pOut, pOut2, pU1; ROW_VALUES *rptr; long i, j, yfac_inv, src_y, src_y_i, q, q1; long a, b, c, d;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &i); pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &i);
pOut = pCvtBits;
yfac_inv = refdata->ci_height * 256 / refdata->ci_dstheight;
// Do the Y component first as a bilinear zoom
for (i = 0; i < refdata->ci_dstheight; i++) { src_y = i * yfac_inv; src_y_i = src_y / 256; q = src_y - src_y_i * 256; q1 = 256 - q; rptr = refdata->ci_rptr;
pIn1 = pBits + src_y_i * refdata->ci_width; pIn2 = pIn1 + refdata->ci_width; for (j = 0; j < refdata->ci_dstwidth; j++, rptr++) { pIn1 += rptr->x_i; pIn2 += rptr->x_i; a = *pIn1; b = *(pIn1+1); c = *pIn2; d = *(pIn2+1); a = ((a * rptr->p1 + b * rptr->p) * q1 + (c * rptr->p1 + d * rptr->p) * q) / 256 / 256; if (a > 256) a = 255; *pOut++ = (BYTE)a; } }
// Do the V and U components next as a nearest neighbor zoom
pIn1 = pBits + refdata->ci_width * refdata->ci_height; // start of source V table
pU1 = pIn1 + (refdata->ci_width * refdata->ci_height) / 16; // start of source U table
pOut2 = pOut + (refdata->ci_dstwidth * refdata->ci_dstheight) / 16; // start of dest U table
src_y = 0; for (i = 0; i < refdata->ci_dstheight; i += 4) { src_y_i = (i * yfac_inv) / 256 / 4; d = (src_y_i - src_y) * refdata->ci_width / 4; pIn1 += d; pU1 += d; src_y = src_y_i;
a = 0; rptr = refdata->ci_rptr; for (j = 0; j < refdata->ci_dstwidth/4; j++) { *pOut++ = *(pIn1+a/4); *pOut2++ = *(pU1+a/4);
a += rptr->x_i; rptr++; a += rptr->x_i; rptr++; a += rptr->x_i; rptr++; a += rptr->x_i; rptr++; } }
pbsIn->UnlockBits(NULL, pBits); pbsOut->UnlockBits(NULL, pCvtBits); return TRUE; }
BOOL InitScale( LPBITMAPINFOHEADER lpbmhIn, long desiredwidth, long desiredheight, LPBITMAPINFOHEADER *lpbmhOut, FRAMECONVERTPROC **convertproc, LPVOID *refdata ) { PZOOMCONVERTINFO pcvt; DWORD dwSize, dwBaseSize; ROW_VALUES *rptr; long i, x, xfac_inv, x_i_last, tmp;
*convertproc = NULL; *refdata = NULL;
if ((lpbmhIn->biCompression != VIDEO_FORMAT_BI_RGB) && (lpbmhIn->biCompression != VIDEO_FORMAT_YVU9)) return FALSE;
// calculate size of zoomconvertinfo struct, if we need a colortable, then add 256 entries
// else subtract off the 1 built into the struct definition
dwBaseSize = sizeof(ZOOMCONVERTINFO) - sizeof(RGBQUAD); if (lpbmhIn->biBitCount <= 8) dwBaseSize += 256 * sizeof(RGBQUAD);
dwSize = dwBaseSize + desiredwidth * sizeof(ROW_VALUES);
// for RGB and YVU9 input formats, we know that the output format will never need
// an attached color table, so we can allocate lpbmhOut without one
if ((pcvt = (PZOOMCONVERTINFO)LocalAlloc(LPTR, dwSize)) && (*lpbmhOut = (LPBITMAPINFOHEADER)LocalAlloc(LPTR, lpbmhIn->biSize))) { CopyMemory(*lpbmhOut, lpbmhIn, lpbmhIn->biSize); pcvt->ci_width = lpbmhIn->biWidth; pcvt->ci_height = lpbmhIn->biHeight; pcvt->ci_dstwidth = desiredwidth; pcvt->ci_dstheight = desiredheight; (*lpbmhOut)->biWidth = desiredwidth; (*lpbmhOut)->biHeight = desiredheight;
pcvt->ci_rptr = (ROW_VALUES *)(((BYTE *)pcvt) + dwBaseSize); rptr = pcvt->ci_rptr; xfac_inv = lpbmhIn->biWidth * 256 / desiredwidth; x_i_last = 0; for (i = 0; i < desiredwidth; i++) { x = i * xfac_inv; tmp = x / 256; rptr->x_i = tmp - x_i_last; x_i_last = tmp; rptr->p = x - x_i_last * 256; rptr->p1 = 256 - rptr->p; rptr++; }
// copy colortable from input bitmapinfoheader
if (lpbmhIn->biBitCount <= 8) CopyMemory(&pcvt->ci_colortable[0], (LPBYTE)lpbmhIn + lpbmhIn->biSize, 256 * sizeof(RGBQUAD));
if (lpbmhIn->biCompression == VIDEO_FORMAT_BI_RGB) { (*lpbmhOut)->biBitCount = 24; (*lpbmhOut)->biSizeImage = desiredwidth * desiredheight * 3;
if (lpbmhIn->biBitCount == 4) { *convertproc = (FRAMECONVERTPROC*)&Zoom4; } else if (lpbmhIn->biBitCount == 8) { *convertproc = (FRAMECONVERTPROC*)&Zoom8; } else if (lpbmhIn->biBitCount == 16) { *convertproc = (FRAMECONVERTPROC*)&Zoom16; } else { *convertproc = (FRAMECONVERTPROC*)&Zoom24; } } else if (lpbmhIn->biCompression == VIDEO_FORMAT_YVU9) { (*lpbmhOut)->biBitCount = lpbmhIn->biBitCount; (*lpbmhOut)->biSizeImage = desiredwidth * desiredheight + (desiredwidth * desiredheight)/8; *convertproc = (FRAMECONVERTPROC*)&ZoomYVU9; }
*refdata = (LPVOID)pcvt; return TRUE; } else { if (pcvt) LocalFree((HANDLE)pcvt); } return FALSE; } #endif // ENABLE_ZOOM_CODE
STDMETHODIMP CCaptureChain::InitCaptureChain( HCAPDEV hcapdev, BOOL streaming, LPBITMAPINFOHEADER lpcap, LONG desiredwidth, LONG desiredheight, DWORD desiredformat, LPBITMAPINFOHEADER *plpdsp ) { CFrameOp *ccf; CFrameOp *clast; CFilterChain *cfilterchain; LPBITMAPINFOHEADER lpcvt; DWORD lpcapsize;
FX_ENTRY("CCaptureChain::InitCaptureChain");
*plpdsp = NULL;
#ifndef SUPPORT_DESIRED_FORMAT
if (desiredformat != 0) { ERRORMESSAGE(("%s: Invalid desiredformat parameter", _fx_)); return E_FAIL; } #endif
if (streaming) { if ((ccf = new CStreamCaptureFrame)) { ccf->AddRef(); if (hcapdev && !((CStreamCaptureFrame*)ccf)->InitCapture(hcapdev, lpcap)) { ERRORMESSAGE(("%s: Failed to init capture object", _fx_)); ccf->Release(); return E_FAIL; } } } else { if ((ccf = new CCaptureFrame)) { ccf->AddRef(); if (hcapdev && !((CCaptureFrame*)ccf)->InitCapture(hcapdev, lpcap)) { ERRORMESSAGE(("%s: Failed to init capture object", _fx_)); ccf->Release(); return E_FAIL; } } }
if (!ccf) { ERRORMESSAGE(("%s: Failed to alloc capture object", _fx_)); return E_OUTOFMEMORY; } clast = ccf;
lpcapsize = lpcap->biSize; if (lpcap->biBitCount <= 8) lpcapsize += 256 * sizeof(RGBQUAD);
#if 0
if ((lpcap->biCompression != BI_RGB) && (lpcap->biCompression != VIDEO_FORMAT_YVU9) && (lpcap->biCompression != VIDEO_FORMAT_INTELI420)) { #else
if ((lpcap->biCompression != BI_RGB) && (lpcap->biCompression != VIDEO_FORMAT_YVU9) && (lpcap->biCompression != VIDEO_FORMAT_YUY2) && (lpcap->biCompression != VIDEO_FORMAT_UYVY) && (lpcap->biCompression != VIDEO_FORMAT_I420) && (lpcap->biCompression != VIDEO_FORMAT_IYUV)) { #endif
// attempt to instantiate an ICM CFrameOp
CICMcvtFrame *cicm;
if ((cicm = new CICMcvtFrame)) { cicm->AddRef(); #if 0
if (cicm->InitCvt(lpcap, lpcapsize, plpdsp, BI_RGB)) { #else
if (cicm->InitCvt(lpcap, lpcapsize, plpdsp)) { #endif
clast->m_next = (CFrameOp*)cicm; // add ICM FrameOp into chain
clast = (CFrameOp*)cicm; } else { cicm->Release();
if (!*plpdsp) { ERRORMESSAGE(("%s: Failed to find a codec", _fx_)); } } } else { ERRORMESSAGE(("%s: Failed to alloc codec object", _fx_)); } } else if (!*plpdsp) { if (*plpdsp = (LPBITMAPINFOHEADER)LocalAlloc(LPTR, lpcapsize)) CopyMemory(*plpdsp, lpcap, lpcapsize); else { ERRORMESSAGE(("%s: Failed to alloc display bitmapinfoheader", _fx_)); } }
#ifdef SUPPORT_DESIRED_FORMAT
#if 0
// LOOKLOOK RP - this isn't done yet, something to do beyond NM2.0
if ((desiredformat == VIDEO_FORMAT_INTELI420) && ((*plpdsp)->biCompression != VIDEO_FORMAT_INTELI420)) { CConvertFrame *ccvt;
if (ccvt = new CConvertFrame) { ccvt->AddRef(); if (ccvt->InitConverter(lpcvt, convertproc, refdata)) { LocalFree((HANDLE)*plpdsp); *plpdsp = lpcvt; clast->m_next = (CFrameOp*)ccvt; // add FrameOp into chain
clast = (CFrameOp*)ccvt; } else ccvt->Release(); } } #endif
#if 0
if (((desiredformat == VIDEO_FORMAT_YVU9) && ((*plpdsp)->biCompression != VIDEO_FORMAT_YVU9)) || ((desiredformat == VIDEO_FORMAT_INTELI420) && ((*plpdsp)->biCompression != VIDEO_FORMAT_INTELI420))) { #else
if (((desiredformat == VIDEO_FORMAT_YVU9) && ((*plpdsp)->biCompression != VIDEO_FORMAT_YVU9)) || ((desiredformat == VIDEO_FORMAT_YUY2) && ((*plpdsp)->biCompression != VIDEO_FORMAT_YUY2)) || ((desiredformat == VIDEO_FORMAT_UYVY) && ((*plpdsp)->biCompression != VIDEO_FORMAT_UYVY)) || ((desiredformat == VIDEO_FORMAT_I420) && ((*plpdsp)->biCompression != VIDEO_FORMAT_I420)) || ((desiredformat == VIDEO_FORMAT_IYUV) && ((*plpdsp)->biCompression != VIDEO_FORMAT_IYUV))) { #endif
// attempt to instantiate an ICM CFrameOp
CICMcvtFrame *cicm;
if ((cicm = new CICMcvtFrame)) { cicm->AddRef(); if (cicm->InitCvt(*plpdsp, lpcapsize, &lpcvt, desiredformat)) { clast->m_next = (CFrameOp*)cicm; // add ICM FrameOp into chain
clast = (CFrameOp*)cicm; LocalFree((HANDLE)*plpdsp); *plpdsp = lpcvt; } else { cicm->Release();
if (!*plpdsp) { ERRORMESSAGE(("%s: Failed to find a codec", _fx_)); } } } else { ERRORMESSAGE(("%s: Failed to alloc codec object", _fx_)); } } #endif // SUPPORT_DESIRED_FORMAT
{ CConvertFrame *ccvt; FRAMECONVERTPROC *convertproc; LPVOID refdata;
#ifdef ENABLE_ZOOM_CODE
BOOL attemptzoom;
attemptzoom = TRUE; #endif
while (*plpdsp && (((*plpdsp)->biWidth != desiredwidth) || ((*plpdsp)->biHeight != desiredheight) || (((*plpdsp)->biCompression == BI_RGB) && ((*plpdsp)->biBitCount <= 8)))) { lpcvt = NULL; #ifdef ENABLE_ZOOM_CODE
if (attemptzoom) { InitScale(*plpdsp, desiredwidth, desiredheight, &lpcvt, &convertproc, &refdata); attemptzoom = FALSE; } #endif
if (!lpcvt) { if (((*plpdsp)->biWidth >= desiredwidth) && ((*plpdsp)->biHeight >= desiredheight)) { // try to shrink
InitShrink(*plpdsp, desiredwidth, desiredheight, &lpcvt, &convertproc, &refdata); } else { // try to blackbar
InitBlackbar(*plpdsp, desiredwidth, desiredheight, &lpcvt, &convertproc, &refdata); } } if (lpcvt) { if (ccvt = new CConvertFrame) { ccvt->AddRef(); if (ccvt->InitConverter(lpcvt, convertproc, refdata)) { LocalFree((HANDLE)*plpdsp); *plpdsp = lpcvt; clast->m_next = (CFrameOp*)ccvt; // add FrameOp into chain
clast = (CFrameOp*)ccvt; continue; } else ccvt->Release(); } } else { ERRORMESSAGE(("%s: Can't convert", _fx_)); LocalFree((HANDLE)*plpdsp); *plpdsp = NULL; } } }
if (*plpdsp) { // allocate a placeholder for a filter chain
if (cfilterchain = new CFilterChain) { cfilterchain->AddRef(); // placeholder needs reference to a pool to pass to added filters
if (clast->m_pool && clast->m_pool->Growable()) { cfilterchain->m_pool = clast->m_pool; cfilterchain->m_pool->AddRef(); } else { if ((cfilterchain->m_pool = new CVidPool)) { cfilterchain->m_pool->AddRef(); if (cfilterchain->m_pool->InitPool(2, *plpdsp) != NO_ERROR) { ERRORMESSAGE(("%s: Failed to init filter pool", _fx_)); cfilterchain->m_pool->Release(); cfilterchain->m_pool = NULL; } } else { ERRORMESSAGE(("%s: Failed to alloc filter pool", _fx_)); } } if (cfilterchain->m_pool) { clast->m_next = (CFrameOp*)cfilterchain; // add placeholder FrameOp into chain
clast = (CFrameOp*)cfilterchain; } else { cfilterchain->Release(); cfilterchain = NULL; } }
if (m_opchain) m_opchain->Release(); m_opchain = ccf; m_filterchain = cfilterchain; return NO_ERROR; } ccf->Release(); // discard partial chain
return E_FAIL; }
// AddFilter
// Adds a filter to the chain. If hAfter is NULL, the filter is added
// to the head of the chain.
STDMETHODIMP CCaptureChain::AddFilter( CLSID* pclsid, LPBITMAPINFOHEADER lpbmhIn, HANDLE* phNew, HANDLE hAfter ) { HRESULT hres; IBitmapEffect *effect; CFilterFrame *cff; CFilterChain *chain; CFilterFrame *previous;
if (m_filterchain) { m_filterchain->AddRef(); // lock chain from destruction
// find insertion point
previous = m_filterchain->m_head; if (hAfter) { while (previous && (previous->m_tag != hAfter)) previous = (CFilterFrame*)previous->m_next; if (!previous) { // can't find hAfter, so fail call
m_filterchain->Release(); // unlock m_filterchain
return E_INVALIDARG; } }
// load, init and link in new filter
if (cff = new CFilterFrame) { cff->AddRef(); if ((hres = LoadFilter(pclsid, &effect)) == NO_ERROR) { m_filterchain->m_pool->AddRef(); if (cff->InitFilter(effect, lpbmhIn, m_filterchain->m_pool)) hres = NO_ERROR; else hres = E_OUTOFMEMORY; m_filterchain->m_pool->Release(); if (hres == NO_ERROR) { cff->m_clsid = *pclsid; cff->m_tag = (HANDLE)(++m_filtertags); if (phNew) *phNew = (HANDLE)cff->m_tag;
EnterCriticalSection(&m_capcs); if (previous) { cff->m_next = previous->m_next; previous->m_next = cff; } else { cff->m_next = m_filterchain->m_head; m_filterchain->m_head = cff; } LeaveCriticalSection(&m_capcs); m_filterchain->Release(); return NO_ERROR; } } cff->Release(); } else hres = E_OUTOFMEMORY; m_filterchain->Release(); // unlock m_filterchain
return hres; } return E_UNEXPECTED; }
STDMETHODIMP CCaptureChain::RemoveFilter( HANDLE hFilter ) { return E_NOTIMPL; }
STDMETHODIMP CCaptureChain::DisplayFilterProperties( HANDLE hFilter, HWND hwndParent ) { return E_NOTIMPL; }
|