Windows2000/private/windows/media/avi/avifile/avicmprs.cpp
2020-09-30 17:12:32 +02:00

1280 lines
33 KiB
C++

/*
* AVICMPRS.C
* routine for compressing AVI files...
* AVISave()
* Copyright (c) 1992 - 1995 Microsoft Corporation. All Rights Reserved.
* You have a royalty-free right to use, modify, reproduce and
* distribute the Sample Files (and/or any modified version) in
* any way you find useful, provided that you agree that
* Microsoft has no warranty obligations or liability for any
* Sample Application Files which are modified.
*/
// What this file does:
// Given an AVI Stream (that is, essentially, a function that it can call
// to get video frames), this presents the same sort of interface and allows
// other people to call it to get compressed frames.
#include <win32.h>
#include <vfw.h>
#include "avifilei.h"
#include "avicmprs.h"
#include "debug.h"
#ifndef _WIN32
#define AVIStreamInfoW AVIStreamInfo
#endif
#define ALIGNULONG(i) ((i+3)&(~3)) /* ULONG aligned ! */
#define WIDTHBYTES(i) ((unsigned)((i+31)&(~31))/8) /* ULONG aligned ! */
#define DIBWIDTHBYTES(bi) (int)WIDTHBYTES((int)(bi).biWidth * (int)(bi).biBitCount)
#define DIBPTR(lpbi) ((LPBYTE)(lpbi) + \
(int)(lpbi)->biSize + \
(int)(lpbi)->biClrUsed * sizeof(RGBQUAD) )
void CAVICmpStream::ResetInst(void)
{
lFrameCurrent = -1;
lLastKeyFrame = 0;
dwQualityLast = ICQUALITY_HIGH;
dwSaved = 0;
}
/* - - - - - - - - */
HRESULT CAVICmpStream::Create(
IUnknown FAR* pUnknownOuter,
const IID FAR& riid,
void FAR* FAR* ppv)
{
IUnknown FAR* pUnknown;
CAVICmpStream FAR* pAVIStream;
HRESULT hresult;
pAVIStream = new FAR CAVICmpStream(pUnknownOuter, &pUnknown);
if (!pAVIStream)
return ResultFromScode(E_OUTOFMEMORY);
hresult = pUnknown->QueryInterface(riid, ppv);
if (FAILED(GetScode(hresult)))
delete pAVIStream;
return hresult;
}
/* - - - - - - - - */
CAVICmpStream::CAVICmpStream(
IUnknown FAR* pUnknownOuter,
IUnknown FAR* FAR* ppUnknown) :
m_Unknown(this),
m_AVIStream(this)
{
// clear extra junk...
pavi = 0;
pgf = 0;
hic = 0;
lpbiC = 0;
lpbiU = 0;
lpFormat = 0;
cbFormat = 0;
lpFormatOrig = 0;
cbFormatOrig = 0;
lpHandler = 0;
cbHandler = 0;
if (pUnknownOuter)
m_pUnknownOuter = pUnknownOuter;
else
m_pUnknownOuter = &m_Unknown;
*ppUnknown = &m_Unknown;
}
/* - - - - - - - - */
CAVICmpStream::CUnknownImpl::CUnknownImpl(
CAVICmpStream FAR* pAVIStream)
{
m_pAVIStream = pAVIStream;
m_refs = 0;
}
/* - - - - - - - - */
STDMETHODIMP CAVICmpStream::CUnknownImpl::QueryInterface(
const IID FAR& iid,
void FAR* FAR* ppv)
{
if (iid == IID_IUnknown)
*ppv = &m_pAVIStream->m_Unknown;
else if (iid == IID_IAVIStream)
*ppv = &m_pAVIStream->m_AVIStream;
else {
*ppv = NULL;
return ResultFromScode(E_NOINTERFACE);
}
AddRef();
return AVIERR_OK;
}
/* - - - - - - - - */
STDMETHODIMP_(ULONG) CAVICmpStream::CUnknownImpl::AddRef()
{
uUseCount++;
return ++m_refs;
}
/* - - - - - - - - */
CAVICmpStream::CAVICmpStreamImpl::CAVICmpStreamImpl(
CAVICmpStream FAR* pAVIStream)
{
m_pAVIStream = pAVIStream;
}
/* - - - - - - - - */
CAVICmpStream::CAVICmpStreamImpl::~CAVICmpStreamImpl()
{
}
/* - - - - - - - - */
STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::QueryInterface(
const IID FAR& iid,
void FAR* FAR* ppv)
{
return m_pAVIStream->m_pUnknownOuter->QueryInterface(iid, ppv);
}
/* - - - - - - - - */
STDMETHODIMP_(ULONG) CAVICmpStream::CAVICmpStreamImpl::AddRef()
{
return m_pAVIStream->m_pUnknownOuter->AddRef();
}
/* - - - - - - - - */
STDMETHODIMP_(ULONG) CAVICmpStream::CAVICmpStreamImpl::Release()
{
return m_pAVIStream->m_pUnknownOuter->Release();
}
/* - - - - - - - - */
HRESULT CAVICmpStream::SetUpCompression()
{
LONG lRet = AVIERR_OK;
LPBITMAPINFOHEADER lpbi;
CAVICmpStream FAR * pinst = this; // for convenience....
LRESULT dw;
pinst->pgf = AVIStreamGetFrameOpen(pinst->pavi, NULL);
if (!pinst->pgf) {
// !!! we couldn't decompress the stream!
lRet = AVIERR_INTERNAL;
goto exit;
}
if (pinst->avistream.fccHandler == comptypeDIB)
goto exit;
lpbi = (LPBITMAPINFOHEADER) AVIStreamGetFrame(pinst->pgf, 0);
if (lpbi == NULL) {
lRet = AVIERR_INTERNAL;
goto exit;
}
/*
** get the size required to hold the format.
** if the compressor fails return an error
*/
dw = ICCompressGetFormatSize(pinst->hic, lpbi);
if ((LONG) dw < (LONG)sizeof(BITMAPINFOHEADER))
goto ic_error;
pinst->cbFormat = (DWORD) dw;
pinst->lpFormat = (LPBITMAPINFOHEADER) GlobalAllocPtr(GHND | GMEM_SHARE, pinst->cbFormat);
if (!pinst->lpFormat) {
lRet = AVIERR_MEMORY;
goto exit;
}
/*
** get the compressed format from the compressor.
*/
dw = ICCompressGetFormat(pinst->hic, lpbi, pinst->lpFormat);
if ((LONG) dw < 0)
goto ic_error;
pinst->avistream.rcFrame.right = pinst->avistream.rcFrame.left +
(int) pinst->lpFormat->biWidth;
pinst->avistream.rcFrame.bottom = pinst->avistream.rcFrame.top +
(int) pinst->lpFormat->biHeight;
dw = ICCompressBegin(pinst->hic, lpbi, pinst->lpFormat);
if (dw != ICERR_OK)
goto ic_error;
/*
** allocate buffer to hold compressed data.
*/
dw = ICCompressGetSize(pinst->hic, lpbi, pinst->lpFormat);
pinst->lpbiC = (LPBITMAPINFOHEADER)
GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, pinst->cbFormat + dw);
if (!pinst->lpbiC) {
lRet = AVIERR_MEMORY;
goto exit;
}
hmemcpy((LPVOID)pinst->lpbiC, pinst->lpFormat, pinst->cbFormat);
pinst->lpC = (LPBYTE) pinst->lpbiC + pinst->lpbiC->biSize +
pinst->lpbiC->biClrUsed * sizeof(RGBQUAD);
// check for temporal compress, and allocate a previous
// DIB buffer if needed
if (pinst->dwKeyFrameEvery != 1 && !(dwICFlags & VIDCF_FASTTEMPORALC)) {
pinst->lpbiU = (LPBITMAPINFOHEADER)
GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
if (!pinst->lpbiU) {
lRet = AVIERR_MEMORY;
goto exit;
}
dw = ICDecompressGetFormat(pinst->hic, pinst->lpFormat, pinst->lpbiU);
if ((LONG) dw < 0)
goto ic_error;
if (pinst->lpbiU->biSizeImage == 0)
pinst->lpbiU->biSizeImage = pinst->lpbiU->biHeight *
DIBWIDTHBYTES(*pinst->lpbiU);
pinst->lpbiU = (LPBITMAPINFOHEADER)
GlobalReAllocPtr(pinst->lpbiU,
pinst->lpbiU->biSize +
pinst->lpbiU->biClrUsed * sizeof(RGBQUAD) +
pinst->lpbiU->biSizeImage,
GMEM_MOVEABLE | GMEM_SHARE);
if (!pinst->lpbiU) {
lRet = AVIERR_MEMORY;
goto exit;
}
pinst->lpU = (LPBYTE) pinst->lpbiU + pinst->lpbiU->biSize +
pinst->lpbiU->biClrUsed * sizeof(RGBQUAD);
dw = ICDecompressBegin(pinst->hic, pinst->lpFormat, pinst->lpbiU);
if (dw != ICERR_OK)
goto ic_error;
}
// !!! We really should check if the new stream has palette changes....
exit:
if (lRet != AVIERR_OK) {
// Clean up before returning...
}
return ResultFromScode(lRet);
ic_error:
if (dw == ICERR_BADFORMAT)
lRet = AVIERR_BADFORMAT;
else if (dw == ICERR_MEMORY)
lRet = AVIERR_MEMORY;
else
lRet = AVIERR_INTERNAL;
goto exit;
}
/* - - - - - - - - */
STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::Create(LPARAM lParam1, LPARAM lParam2)
{
CAVICmpStream FAR * pinst = m_pAVIStream;
ICINFO icinfo;
AVICOMPRESSOPTIONS FAR *lpOpt = (AVICOMPRESSOPTIONS FAR *)lParam2;
LONG lRet = AVIERR_OK;
// The AVI Stream that we're compressing is passsed in in the <szFile>
// parameter.
pinst->pavi = (PAVISTREAM)lParam1;
// Make sure the uncompressed stream doesn't go away without our
// knowledge....
AVIStreamAddRef(pinst->pavi);
// !!! how can we check if pinst->pavi is valid?
// Get the stream header for future reference....
AVIStreamInfoW(pinst->pavi, &pinst->avistream, sizeof(pinst->avistream));
pinst->ResetInst();
if (!lpOpt || (lpOpt->fccHandler == comptypeDIB)) {
pinst->avistream.fccHandler = comptypeDIB;
lRet = AVIERR_OK;
goto exit;
}
pinst->avistream.fccHandler = lpOpt->fccHandler;
// Open the compressor they asked for in the options structure...
pinst->hic = ICOpen(ICTYPE_VIDEO, lpOpt->fccHandler, ICMODE_COMPRESS);
if (!pinst->hic) {
lRet = AVIERR_NOCOMPRESSOR;
goto exit;
}
if (lpOpt->cbParms) {
ICSetState(pinst->hic, lpOpt->lpParms, lpOpt->cbParms);
}
pinst->avistream.dwQuality = lpOpt->dwQuality;
if (pinst->avistream.dwQuality == ICQUALITY_DEFAULT) {
pinst->avistream.dwQuality = ICGetDefaultQuality(pinst->hic);
}
/*
** get info about this compressor
*/
ICGetInfo(pinst->hic,&icinfo,sizeof(icinfo));
pinst->dwICFlags = icinfo.dwFlags;
if (lpOpt->dwFlags & AVICOMPRESSF_KEYFRAMES)
pinst->dwKeyFrameEvery = lpOpt->dwKeyFrameEvery;
else
pinst->dwKeyFrameEvery = 1;
if (!(icinfo.dwFlags & VIDCF_TEMPORAL))
pinst->dwKeyFrameEvery = 1; // compressor doesn't do temporal
if (lpOpt->dwFlags & AVICOMPRESSF_DATARATE)
pinst->dwMaxSize = muldiv32(lpOpt->dwBytesPerSecond,
pinst->avistream.dwScale,
pinst->avistream.dwRate);
else
pinst->dwMaxSize = 0;
{
ICCOMPRESSFRAMES iccf;
LRESULT dw;
iccf.lpbiOutput = pinst->lpbiC;
iccf.lOutput = 0;
iccf.lpbiInput = pinst->lpbiU;
iccf.lInput = 0;
iccf.lStartFrame = 0;
iccf.lFrameCount = (LONG) pinst->avistream.dwLength;
iccf.lQuality = (LONG) pinst->avistream.dwQuality;
iccf.lDataRate = (LONG) lpOpt->dwBytesPerSecond;
iccf.lKeyRate = (LONG) pinst->dwKeyFrameEvery;
iccf.dwRate = pinst->avistream.dwRate;
iccf.dwScale = pinst->avistream.dwScale;
iccf.dwOverheadPerFrame = 0;
iccf.dwReserved2 = 0;
iccf.GetData = NULL;
iccf.PutData = NULL;
dw = ICSendMessage(pinst->hic,
ICM_COMPRESS_FRAMES_INFO,
(DWORD_PTR) (LPVOID) &iccf,
sizeof(iccf));
// If they support this message, don't give
// warning for data rate!
if (dw == ICERR_OK) {
DPF("Compressor supports COMPRESSFRAMESINFO\n");
// !!! fDataRateChanged = TRUE;
}
#ifdef STATUSCALLBACKS
ICSetStatusProc(pinst->hic,
0,
pinst,
CompressStatusProc);
#endif
}
exit:
if (lRet != AVIERR_OK) {
// Clean up before returning...
}
return ResultFromScode(lRet);
}
STDMETHODIMP_(ULONG) CAVICmpStream::CUnknownImpl::Release()
{
CAVICmpStream FAR * pinst = m_pAVIStream;
uUseCount--;
if (!--m_refs) {
if (pinst->hic) {
ICCompressEnd(pinst->hic);
if (pinst->dwKeyFrameEvery != 1 && pinst->lpbiU)
ICDecompressEnd(pinst->hic);
if (pinst->lpbiU)
GlobalFreePtr((LPVOID) pinst->lpbiU);
if (pinst->lpbiC)
GlobalFreePtr((LPVOID) pinst->lpbiC);
ICClose(pinst->hic);
}
if (pinst->pgf) {
AVIStreamGetFrameClose(pinst->pgf);
pinst->pgf = 0;
}
if (pinst->pavi) {
// Release our hold on the uncompressed stream....
AVIStreamClose(pinst->pavi);
}
if (pinst->lpFormat)
GlobalFreePtr(pinst->lpFormat);
if (pinst->lpFormatOrig)
GlobalFreePtr(pinst->lpFormatOrig);
delete pinst;
return 0;
}
return m_refs;
}
STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::Info(AVISTREAMINFOW FAR * psi, LONG lSize)
{
CAVICmpStream FAR * pinst = m_pAVIStream;
hmemcpy(psi, &pinst->avistream, min(lSize, sizeof(pinst->avistream)));
// return sizeof(pinst->avistream);
return ResultFromScode(0);
}
STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::Read(
LONG lStart,
LONG lSamples,
LPVOID lpBuffer,
LONG cbBuffer,
LONG FAR * plBytes,
LONG FAR * plSamples)
{
CAVICmpStream FAR * pinst = m_pAVIStream;
LPBITMAPINFOHEADER lpbi;
LONG lRet;
if (!pinst->pgf) {
HRESULT hr;
hr = pinst->SetUpCompression();
if (hr != NOERROR)
return hr;
}
if (pinst->hic == 0) {
lpbi = (LPBITMAPINFOHEADER) AVIStreamGetFrame(pinst->pgf, lStart);
if (!lpbi)
return ResultFromScode(AVIERR_MEMORY);
if (plBytes)
*plBytes = lpbi->biSizeImage;
if (lpBuffer) {
if ((LONG) lpbi->biSizeImage > cbBuffer)
return ResultFromScode(AVIERR_BUFFERTOOSMALL);
hmemcpy(lpBuffer, DIBPTR(lpbi), min((DWORD) cbBuffer, lpbi->biSizeImage));
}
if (plSamples)
*plSamples = 1;
return AVIERR_OK;
}
if (lStart < pinst->lFrameCurrent)
pinst->ResetInst();
while (pinst->lFrameCurrent < lStart) {
++pinst->lFrameCurrent;
lpbi = (LPBITMAPINFOHEADER) AVIStreamGetFrame(pinst->pgf, pinst->lFrameCurrent);
if (lpbi == NULL) {
pinst->ResetInst(); // Make sure we don't assume anything
return ResultFromScode(AVIERR_INTERNAL);
}
// !!! Check if format has changed!
lRet = pinst->ICCrunch(lpbi, DIBPTR(lpbi));
if (lRet != AVIERR_OK) {
pinst->ResetInst(); // Make sure we don't assume anything
return ResultFromScode(AVIERR_INTERNAL); // !!! error < 0.
}
}
if (plBytes)
*plBytes = pinst->lpbiC->biSizeImage;
if (lpBuffer) {
if ((LONG) pinst->lpbiC->biSizeImage > cbBuffer)
return ResultFromScode(AVIERR_BUFFERTOOSMALL);
hmemcpy(lpBuffer, pinst->lpC,
min((DWORD) cbBuffer, pinst->lpbiC->biSizeImage));
}
if (plSamples)
*plSamples = 1;
return AVIERR_OK;
}
STDMETHODIMP_(LONG) CAVICmpStream::CAVICmpStreamImpl::FindSample(LONG lPos, LONG lFlags)
{
CAVICmpStream FAR * pinst = m_pAVIStream;
if (lFlags & FIND_KEY) {
if (pinst->hic == 0)
return lPos;
if (lFlags & FIND_PREV) {
/* If the frame they're asking about isn't the one we have,
** we have to go actually do the work and find out.
*/
if (lPos < pinst->lLastKeyFrame || lPos > pinst->lFrameCurrent)
Read(lPos, 1, NULL, 0, NULL, NULL);
return pinst->lLastKeyFrame;
} else {
return -1; // !!! Find Next KeyFrame
}
}
if (lFlags & FIND_ANY) {
return lPos;
}
if (lFlags & FIND_FORMAT) {
// !!! This is wrong in the case where we're compressing something
// with a palette change and the compressor preserves it....
if (lFlags & FIND_PREV)
return 0;
else
return -1;
}
return -1;
}
// ICCrunch()
// crunch a frame and make it fit into the specifed size, by varing the
// quality. the suplied quality is the upper bound.
// if the compressor can crunch, then let it crunch
// if the compressor does quality, then vary the quality
// if the compressor does not do quality, then the caller gets what
// ever it will do.
// The frame to be compressed is passed in in lpbi.
// The compressed frame can be found in the lpC member variable....
LONG CAVICmpStream::ICCrunch(LPBITMAPINFOHEADER lpbi, LPVOID lp)
{
DWORD dw;
DWORD dwFlags;
DWORD dwSize;
DWORD ckid;
DWORD dwQuality = avistream.dwQuality;
DWORD dwQualityMin;
DWORD dwQualityMax;
DWORD dwMaxSizeThisFrame;
DWORD dwSizeMin;
DWORD dwSizeMax;
BOOL fKeyFrame=FALSE;
BOOL fFastTemporal = (dwICFlags & VIDCF_FASTTEMPORALC) != 0;
BOOL fCrunch; /* are we crunching? */
BOOL fFirst=TRUE;
dwMaxSizeThisFrame = dwMaxSize;
if (lFrameCurrent == 0 || (dwKeyFrameEvery != 0 &&
lFrameCurrent - lLastKeyFrame >= (long)dwKeyFrameEvery)) {
fKeyFrame = TRUE;
}
// give the key frames more space, and take some away from the
// non key frames.
// give the key frame two shares, assuming we have more frames to
// go around.
if (dwKeyFrameEvery > 0) {
if (lFrameCurrent == 0) {
dwMaxSizeThisFrame = 0xffffff;
} else if (fKeyFrame) {
dwMaxSizeThisFrame = dwMaxSizeThisFrame + dwSaved;
dwSaved = 0;
} else {
DWORD dwTakeAway;
dwTakeAway = dwMaxSizeThisFrame / dwKeyFrameEvery;
if (dwSaved > dwMaxSizeThisFrame)
dwTakeAway = 0;
dwMaxSizeThisFrame -= dwTakeAway;
dwSaved += dwTakeAway;
/* Try to give a little extra space to each frame */
dwMaxSizeThisFrame += dwSaved / dwKeyFrameEvery;
dwSaved -= dwSaved / dwKeyFrameEvery;
}
} else {
// the only key frame is frame zero
if (lFrameCurrent == 0)
dwMaxSizeThisFrame = 0xffffff;
else {
/* Give each frame whatever extra there is.... */
dwMaxSizeThisFrame += dwSaved;
dwSaved = 0;
}
}
// if the device supports crunching or does not do quality we dont
// crunch.
fCrunch = dwMaxSizeThisFrame > 0 && !(dwICFlags & VIDCF_CRUNCH) &&
(dwICFlags & VIDCF_QUALITY);
//if (lFrameCurrent > 0 && fCrunch)
// dwQuality = dwQualityLast;
DPF("ICCrunch: Frame %ld, Quality = %ld, MaxSize = %ld\n", lFrameCurrent, avistream.dwQuality, dwMaxSizeThisFrame);
dwQualityMin = 0;
dwQualityMax = dwQuality;
dwSizeMin = 0;
dwSizeMax = dwMaxSizeThisFrame;
for (;;) {
ckid = 0L;
// This is NOT how we tell the compressor to make a keyframe, but
// somebody might think it is, so this is just to avoid the possibility
// of breaking an old compressor. Probably not necessary, but what the
// hell.
dwFlags = fKeyFrame ? AVIIF_KEYFRAME : 0;
// compress the frame
dw = ICCompress(hic,
// THIS is how we tell the compressor to make a keyframe
fKeyFrame ? ICCOMPRESS_KEYFRAME : 0, // flags
lpbiC, // output format
lpC, // output data
lpbi, // format of frame to compress
lp, // frame data to compress
&ckid, // ckid for data in AVI file
&dwFlags, // flags in the AVI index.
lFrameCurrent, // frame number of seq.
dwMaxSizeThisFrame, // reqested size in bytes. (if non zero)
dwQuality, // quality value
fKeyFrame | fFastTemporal ? NULL : lpbiU,
fKeyFrame | fFastTemporal ? NULL : lpU);
if (dw != ICERR_OK)
break;
dwSize = lpbiC->biSizeImage;
DPF(" Quality = %ld, Size = %ld, %c\n", dwQuality, dwSize, (dwFlags & AVIIF_KEYFRAME) ? 'K' : ' ');
// if the device can't crunch (does not do it it self, or does not do
// quality) then we are done.
if (!fCrunch)
break;
// we are crunching, see if the frame fit.
if (dwSize <= dwMaxSizeThisFrame) {
dwQualityMin = dwQuality;
dwSizeMin = dwSize;
// when the quality gets too close bail out.
if (dwQualityMax - dwQualityMin <= 10)
break;
// if we get within 512 bytes it is good enough
if ((LONG) (dwMaxSizeThisFrame - dwSize) <= (LONG) min(512L, dwMaxSizeThisFrame / 8L))
break;
// if the first try, (with the user specifed quality) made it
// then use it. otherwise we need to search.
if (fFirst)
break;
}
else {
// when the quality gets too close bail out.
if (dwQualityMax - dwQualityMin <= 1)
break;
dwQualityMax = dwQuality;
dwSizeMax = dwSize;
}
if (fFirst && dwQuality != dwQualityLast)
dwQuality = dwQualityLast;
else
dwQuality = (dwQualityMin + dwQualityMax) / 2;
#if 0
// make a guess based on how close we are now.
dwQuality = dwQualityMin + muldiv32(dwQualityMax-dwQualityMin,
dwMaxSizeThisFrame-dwSizeMin,dwSizeMax-dwSizeMin);
#endif
fFirst = FALSE;
}
#if 0
/* If this wasn't the first frame, save up any extra space for later */
if (dwSize < dwMaxSizeThisFrame && lFrameCurrent > 0) {
dwSaved += dwMaxSizeThisFrame - dwSize;
// HACK: limit this, so it doesn't get too big!!!
if (dwSaved > 32768L)
dwSaved = 32768L;
if (dwSaved > dwMaxSizeThisFrame * 5)
dwSaved = dwMaxSizeThisFrame * 5;
}
#endif
if (dw != ICERR_OK) {
if (dw == ICERR_BADFORMAT)
return AVIERR_BADFORMAT;
else
return AVIERR_INTERNAL;
}
if (dwFlags & AVIIF_KEYFRAME) {
lLastKeyFrame = lFrameCurrent;
}
// remember the quality that worked, it will be the best guess next time.
dwQualityLast = dwQuality;
// decompress the image into the offscreen buffer, for use next time.
if (dwKeyFrameEvery != 1 && lpbiU && !fFastTemporal) {
dw = ICDecompress(hic, 0,
lpbiC,lpC,
lpbiU,lpU);
// !!! error check?
}
// return the dwFlags and ckid, by stuffing them in the stream info.
m_ckid = ckid;
m_dwFlags = dwFlags;
return AVIERR_OK;
}
/*
* @doc INTERNAL DRAWDIB
*
* @api BOOL | DibEq | This function compares two dibs.
*
* @parm LPBITMAPINFOHEADER lpbi1 | Pointer to one bitmap.
* this DIB is assumed to have the colors after the BITMAPINFOHEADER
*
* @parm LPBITMAPINFOHEADER | lpbi2 | Pointer to second bitmap.
* this DIB is assumed to have the colors after biSize bytes.
*
* @rdesc Returns TRUE if bitmaps are identical, FALSE otherwise.
*
*/
static inline BOOL DibEq(LPBITMAPINFOHEADER lpbi1, LPBITMAPINFOHEADER lpbi2)
{
return
lpbi1->biCompression == lpbi2->biCompression &&
lpbi1->biSize == lpbi2->biSize &&
lpbi1->biWidth == lpbi2->biWidth &&
lpbi1->biHeight == lpbi2->biHeight &&
lpbi1->biBitCount == lpbi2->biBitCount;
}
STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::SetFormat(LONG lPos,LPVOID lpFormat,LONG cbFormat)
{
CAVICmpStream FAR * pinst = m_pAVIStream;
LONG lRet = AVIERR_OK;
HRESULT hr;
LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER) lpFormat;
LRESULT dw;
if (pinst->pgf)
return ResultFromScode(AVIERR_UNSUPPORTED);
if (lpbi->biCompression != BI_RGB)
return ResultFromScode(AVIERR_UNSUPPORTED);
if (pinst->avistream.fccHandler == 0)
pinst->avistream.fccHandler = comptypeDIB;
if (pinst->lpFormatOrig) {
if ((cbFormat = pinst->cbFormatOrig) &&
(_fmemcmp(pinst->lpFormatOrig, lpFormat, (int) cbFormat) == 0))
return AVIERR_OK;
DPF("AVICmprs: SetFormat when format already set!\n");
}
// Can only currently set the palette at the end of the file
if (lPos < (LONG) (pinst->avistream.dwStart + pinst->avistream.dwLength))
return ResultFromScode(AVIERR_UNSUPPORTED);
if (pinst->avistream.fccHandler == comptypeDIB) {
return AVIStreamSetFormat(pinst->pavi, lPos,
lpFormat, cbFormat);
}
if (pinst->lpFormatOrig) {
// We can only change the palette for things with palettes....
if (lpbi->biBitCount > 8 || lpbi->biClrUsed == 0)
return ResultFromScode(AVIERR_UNSUPPORTED);
// Be sure only the palette is changing, nothing else....
if (cbFormat != pinst->cbFormatOrig)
return ResultFromScode(AVIERR_UNSUPPORTED);
if (!DibEq((LPBITMAPINFOHEADER) lpFormat,
(LPBITMAPINFOHEADER) pinst->lpFormatOrig))
return ResultFromScode(AVIERR_UNSUPPORTED);
dw = ICCompressGetFormat(pinst->hic, lpFormat, pinst->lpFormat);
if ((LONG) dw < 0)
goto ic_error;
ICCompressEnd(pinst->hic);
dw = ICCompressBegin(pinst->hic, lpFormat, pinst->lpFormat);
if (dw != ICERR_OK)
goto ic_error;
if (pinst->dwKeyFrameEvery != 1 && pinst->lpbiU &&
!(pinst->dwICFlags & VIDCF_FASTTEMPORALC)) {
ICDecompressEnd(pinst->hic);
dw = ICDecompressGetFormat(pinst->hic, pinst->lpFormat, pinst->lpbiU);
if ((LONG) dw < 0)
goto ic_error;
dw = ICDecompressBegin(pinst->hic, pinst->lpFormat, pinst->lpbiU);
if (dw != ICERR_OK)
goto ic_error;
}
goto setformatandexit;
}
pinst->lpFormatOrig = (LPBITMAPINFOHEADER)
GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, cbFormat);
pinst->cbFormatOrig = cbFormat;
if (!pinst->lpFormatOrig) {
lRet = AVIERR_MEMORY;
goto exit;
}
hmemcpy(pinst->lpFormatOrig, lpFormat, cbFormat);
/*
** get the size required to hold the format.
** if the compressor fails return an error
*/
dw = ICCompressGetFormatSize(pinst->hic, lpFormat);
if ((LONG) dw < (LONG)sizeof(BITMAPINFOHEADER))
goto ic_error;
pinst->cbFormat = (DWORD) dw;
pinst->lpFormat = (LPBITMAPINFOHEADER) GlobalAllocPtr(GHND | GMEM_SHARE, pinst->cbFormat);
if (!pinst->lpFormat) {
lRet = AVIERR_MEMORY;
goto exit;
}
/*
** get the compressed format from the compressor.
*/
dw = ICCompressGetFormat(pinst->hic, lpFormat, pinst->lpFormat);
if ((LONG) dw < 0)
goto ic_error;
pinst->avistream.rcFrame.right = pinst->avistream.rcFrame.left +
(int) pinst->lpFormat->biWidth;
pinst->avistream.rcFrame.bottom = pinst->avistream.rcFrame.top +
(int) pinst->lpFormat->biHeight;
dw = ICCompressBegin(pinst->hic, lpFormat, pinst->lpFormat);
if (dw != ICERR_OK)
goto ic_error;
/*
** allocate buffer to hold compressed data.
*/
dw = ICCompressGetSize(pinst->hic, lpFormat, pinst->lpFormat);
pinst->lpbiC = (LPBITMAPINFOHEADER)
GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, pinst->cbFormat + dw);
if (!pinst->lpbiC) {
lRet = AVIERR_MEMORY;
goto exit;
}
hmemcpy((LPVOID)pinst->lpbiC, pinst->lpFormat, pinst->cbFormat);
pinst->lpC = (LPBYTE) pinst->lpbiC + pinst->lpbiC->biSize +
pinst->lpbiC->biClrUsed * sizeof(RGBQUAD);
// check for temporal compress, and allocate a previous
// DIB buffer if needed
if (pinst->dwKeyFrameEvery != 1 &&
!(pinst->dwICFlags & VIDCF_FASTTEMPORALC)) {
pinst->lpbiU = (LPBITMAPINFOHEADER)
GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
if (!pinst->lpbiU) {
lRet = AVIERR_MEMORY;
goto exit;
}
dw = ICDecompressGetFormat(pinst->hic, pinst->lpFormat, pinst->lpbiU);
if ((LONG) dw < 0)
goto ic_error;
if (pinst->lpbiU->biSizeImage == 0)
pinst->lpbiU->biSizeImage = pinst->lpbiU->biHeight *
DIBWIDTHBYTES(*pinst->lpbiU);
pinst->lpbiU = (LPBITMAPINFOHEADER)
GlobalReAllocPtr(pinst->lpbiU,
pinst->lpbiU->biSize +
pinst->lpbiU->biClrUsed * sizeof(RGBQUAD) +
pinst->lpbiU->biSizeImage,
GMEM_MOVEABLE | GMEM_SHARE);
if (!pinst->lpbiU) {
lRet = AVIERR_MEMORY;
goto exit;
}
pinst->lpU = (LPBYTE) pinst->lpbiU + pinst->lpbiU->biSize +
pinst->lpbiU->biClrUsed * sizeof(RGBQUAD);
dw = ICDecompressBegin(pinst->hic, pinst->lpFormat, pinst->lpbiU);
if (dw != ICERR_OK)
goto ic_error;
}
setformatandexit:
hr = AVIStreamSetFormat(pinst->pavi, lPos,
pinst->lpFormat, pinst->cbFormat);
if (hr != NOERROR)
return hr;
exit:
if (lRet != AVIERR_OK) {
// Clean up before returning...
}
return ResultFromScode(lRet);
ic_error:
if (dw == ICERR_BADFORMAT)
lRet = AVIERR_BADFORMAT;
else if (dw == ICERR_MEMORY)
lRet = AVIERR_MEMORY;
else
lRet = AVIERR_INTERNAL;
goto exit;
}
STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::Write(LONG lStart,
LONG lSamples,
LPVOID lpBuffer,
LONG cbBuffer,
DWORD dwFlags,
LONG FAR *plSampWritten,
LONG FAR *plBytesWritten)
{
CAVICmpStream FAR * pinst = m_pAVIStream;
LONG lRet;
if (pinst->pgf)
return ResultFromScode(AVIERR_UNSUPPORTED);
if (lStart < (LONG) (pinst->avistream.dwStart + pinst->avistream.dwLength))
return ResultFromScode(AVIERR_UNSUPPORTED);
if (lSamples > 1)
return ResultFromScode(AVIERR_UNSUPPORTED);
pinst->lFrameCurrent = lStart;
if (pinst->avistream.fccHandler == comptypeDIB) {
// !!! Check here that the frame is the right size....
dwFlags |= AVIIF_KEYFRAME;
} else {
lRet = pinst->ICCrunch(pinst->lpFormatOrig, lpBuffer);
if (lRet != AVIERR_OK)
return ResultFromScode(lRet);
lpBuffer = pinst->lpC;
cbBuffer = pinst->lpbiC->biSizeImage;
dwFlags = pinst->lLastKeyFrame == lStart ? AVIIF_KEYFRAME : 0;
}
return AVIStreamWrite(pinst->pavi,
lStart,
lSamples,
lpBuffer,
cbBuffer,
dwFlags,
plSampWritten,
plBytesWritten);
}
STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::Delete(LONG lStart,LONG lSamples)
{
CAVICmpStream FAR * pinst = m_pAVIStream;
return ResultFromScode(AVIERR_UNSUPPORTED);
}
STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::ReadData(DWORD fcc, LPVOID lp, LONG FAR *lpcb)
{
CAVICmpStream FAR * pinst = m_pAVIStream;
// Don't pass through 'strd' data!
if (fcc == ckidSTREAMHANDLERDATA) {
if (pinst->cbHandler) {
hmemcpy(lp, pinst->lpHandler, min(*lpcb, pinst->cbHandler));
}
*lpcb = pinst->cbHandler;
return AVIERR_OK;
}
return AVIStreamReadData(pinst->pavi, fcc, lp, lpcb);
}
STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::WriteData(DWORD fcc, LPVOID lp, LONG cb)
{
CAVICmpStream FAR * pinst = m_pAVIStream;
return ResultFromScode(AVIERR_UNSUPPORTED);
}
#if 0
STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::Clone(PAVISTREAM FAR * ppaviNew)
{
CAVICmpStream FAR * pinst = m_pAVIStream;
return ResultFromScode(AVIERR_UNSUPPORTED);
}
#endif
#ifdef _WIN32
STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::SetInfo(AVISTREAMINFOW FAR *lpInfo, LONG cbInfo)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}
#else
STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::Reserved1(void)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}
STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::Reserved2(void)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}
STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::Reserved3(void)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}
STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::Reserved4(void)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}
STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::Reserved5(void)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}
#endif
/* - - - - - - - - */
#ifndef _WIN32
static void C816InternalCompilerError(CAVICmpStream FAR * pinst, LPBITMAPINFOHEADER lpbi, LPVOID lpFormat, LONG cbFormat)
{
if (pinst->hic == 0) {
pinst->cbFormat = lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD);
if (lpFormat)
hmemcpy(lpFormat, lpbi, min(cbFormat, (LONG) pinst->cbFormat));
} else {
if (lpFormat) {
hmemcpy(lpFormat, pinst->lpFormat, min(cbFormat, (LONG) pinst->cbFormat));
if (pinst->lpFormat->biClrUsed > 0) {
// Make sure we have the right colors!
// !!! This is bad--We may need to restart the compressor...
hmemcpy((LPBYTE) lpFormat + pinst->lpFormat->biSize,
(LPBYTE) lpbi + lpbi->biSize,
pinst->lpFormat->biClrUsed * sizeof(RGBQUAD));
}
}
}
}
#endif
STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::ReadFormat(LONG lPos, LPVOID lpFormat, LONG FAR *lpcbFormat)
{
CAVICmpStream FAR * pinst = m_pAVIStream;
LPBITMAPINFOHEADER lpbi;
if (!pinst->pgf) {
HRESULT hr;
hr = pinst->SetUpCompression();
if (hr != NOERROR)
return hr;
}
lpbi = (LPBITMAPINFOHEADER) AVIStreamGetFrame(pinst->pgf, lPos);
if (!lpbi)
return ResultFromScode(AVIERR_MEMORY);
#ifdef _WIN32
if (pinst->hic == 0) {
pinst->cbFormat = lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD);
if (lpFormat)
hmemcpy(lpFormat, lpbi, min(*lpcbFormat, (LONG) pinst->cbFormat));
} else {
if (lpFormat) {
hmemcpy(lpFormat, pinst->lpFormat, min(*lpcbFormat, (LONG) pinst->cbFormat));
if (pinst->lpFormat->biClrUsed > 0) {
// Make sure we have the right colors!
// !!! This is bad--We may need to restart the compressor...
hmemcpy((LPBYTE) lpFormat + pinst->lpFormat->biSize,
(LPBYTE) lpbi + lpbi->biSize,
pinst->lpFormat->biClrUsed * sizeof(RGBQUAD));
}
}
}
#else
::C816InternalCompilerError(pinst, lpbi, lpFormat, *lpcbFormat);
#endif
*lpcbFormat = pinst->cbFormat;
return AVIERR_OK;
}