/**************************************************************************** * * AVICMPRS.C * * routine for compressing AVI files... * * AVISave() * * Copyright (c) 1992 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 #include #include #include #include "avifile.h" #include "avifilei.h" #include "avicmprs.h" #include "debug.h" #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 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.... DWORD 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 requied to hold the format. */ dw = ICCompressGetFormatSize(pinst->hic, lpbi); if ((LONG) dw < sizeof(BITMAPINFOHEADER)) goto ic_error; pinst->cbFormat = 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 = (LPSTR) pinst->lpbiC + pinst->lpbiC->biSize + pinst->lpbiC->biClrUsed * sizeof(RGBQUAD); // // check for temporal compress, and alocate a previous // DIB buffer if needed // if (pinst->dwKeyFrameEvery != 1) { 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 = (LPSTR) 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(LONG lParam1, LONG 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 // 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.... AVIStreamInfo(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; DWORD 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) (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(AVISTREAMINFO 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::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); 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)); } } } *lpcbFormat = pinst->cbFormat; return AVIERR_OK; } 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 ((LONG) lpbi->biSizeImage > cbBuffer) return ResultFromScode(AVIERR_BUFFERTOOSMALL); if (lpBuffer) 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 ((LONG) pinst->lpbiC->biSizeImage > cbBuffer) return ResultFromScode(AVIERR_BUFFERTOOSMALL); if (lpBuffer) 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 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; /* If we're padding, take away a multiple of 2K. */ if (fPad) { if (dwMaxSizeThisFrame > dwTakeAway + 2048) dwTakeAway += 2047; dwTakeAway -= dwTakeAway % 2048; } dwMaxSizeThisFrame -= dwTakeAway; dwSaved += dwTakeAway; if (!fPad) { /* 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; dwFlags = fKeyFrame ? AVIIF_KEYFRAME : 0; // // compress the frame // dw = ICCompress(hic, 0, // flags lpbiC, // ouput 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 ? NULL : lpbiU, fKeyFrame ? 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; if (fPad) { dwSaved -= ((dwMaxSizeThisFrame - dwSize) % 2048L); } // 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) { 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. * **************************************************************************/ 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; DWORD dw; if (pinst->pgf) return ResultFromScode(AVIERR_UNSUPPORTED); if (lpbi->biCompression != BI_RGB) return ResultFromScode(AVIERR_UNSUPPORTED); if (pinst->avistream.fccHandler == comptypeDIB) goto exit; 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->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) { 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 requied to hold the format. */ dw = ICCompressGetFormatSize(pinst->hic, lpFormat); if ((LONG) dw < sizeof(BITMAPINFOHEADER)) goto ic_error; pinst->cbFormat = 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 = (LPSTR) pinst->lpbiC + pinst->lpbiC->biSize + pinst->lpbiC->biClrUsed * sizeof(RGBQUAD); // // check for temporal compress, and alocate a previous // DIB buffer if needed // if (pinst->dwKeyFrameEvery != 1) { 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 = (LPSTR) 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) { 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 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); }