Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1282 lines
34 KiB

  1. /****************************************************************************
  2. *
  3. * AVICMPRS.C
  4. *
  5. * routine for compressing AVI files...
  6. *
  7. * AVISave()
  8. *
  9. * Copyright (c) 1992 - 1995 Microsoft Corporation. All Rights Reserved.
  10. *
  11. * You have a royalty-free right to use, modify, reproduce and
  12. * distribute the Sample Files (and/or any modified version) in
  13. * any way you find useful, provided that you agree that
  14. * Microsoft has no warranty obligations or liability for any
  15. * Sample Application Files which are modified.
  16. *
  17. ***************************************************************************/
  18. //
  19. // What this file does:
  20. //
  21. // Given an AVI Stream (that is, essentially, a function that it can call
  22. // to get video frames), this presents the same sort of interface and allows
  23. // other people to call it to get compressed frames.
  24. //
  25. #include <win32.h>
  26. #include <vfw.h>
  27. #include "avifilei.h"
  28. #include "avicmprs.h"
  29. #include "debug.h"
  30. #ifndef _WIN32
  31. #define AVIStreamInfoW AVIStreamInfo
  32. #endif
  33. #define ALIGNULONG(i) ((i+3)&(~3)) /* ULONG aligned ! */
  34. #define WIDTHBYTES(i) ((unsigned)((i+31)&(~31))/8) /* ULONG aligned ! */
  35. #define DIBWIDTHBYTES(bi) (int)WIDTHBYTES((int)(bi).biWidth * (int)(bi).biBitCount)
  36. #define DIBPTR(lpbi) ((LPBYTE)(lpbi) + \
  37. (int)(lpbi)->biSize + \
  38. (int)(lpbi)->biClrUsed * sizeof(RGBQUAD) )
  39. void CAVICmpStream::ResetInst(void)
  40. {
  41. lFrameCurrent = -1;
  42. lLastKeyFrame = 0;
  43. dwQualityLast = ICQUALITY_HIGH;
  44. dwSaved = 0;
  45. }
  46. /* - - - - - - - - */
  47. HRESULT CAVICmpStream::Create(
  48. IUnknown FAR* pUnknownOuter,
  49. const IID FAR& riid,
  50. void FAR* FAR* ppv)
  51. {
  52. IUnknown FAR* pUnknown;
  53. CAVICmpStream FAR* pAVIStream;
  54. HRESULT hresult;
  55. pAVIStream = new FAR CAVICmpStream(pUnknownOuter, &pUnknown);
  56. if (!pAVIStream)
  57. return ResultFromScode(E_OUTOFMEMORY);
  58. hresult = pUnknown->QueryInterface(riid, ppv);
  59. if (FAILED(GetScode(hresult)))
  60. delete pAVIStream;
  61. return hresult;
  62. }
  63. /* - - - - - - - - */
  64. CAVICmpStream::CAVICmpStream(
  65. IUnknown FAR* pUnknownOuter,
  66. IUnknown FAR* FAR* ppUnknown) :
  67. m_Unknown(this),
  68. m_AVIStream(this)
  69. {
  70. // clear extra junk...
  71. pavi = 0;
  72. pgf = 0;
  73. hic = 0;
  74. lpbiC = 0;
  75. lpbiU = 0;
  76. lpFormat = 0;
  77. cbFormat = 0;
  78. lpFormatOrig = 0;
  79. cbFormatOrig = 0;
  80. lpHandler = 0;
  81. cbHandler = 0;
  82. if (pUnknownOuter)
  83. m_pUnknownOuter = pUnknownOuter;
  84. else
  85. m_pUnknownOuter = &m_Unknown;
  86. *ppUnknown = &m_Unknown;
  87. }
  88. /* - - - - - - - - */
  89. CAVICmpStream::CUnknownImpl::CUnknownImpl(
  90. CAVICmpStream FAR* pAVIStream)
  91. {
  92. m_pAVIStream = pAVIStream;
  93. m_refs = 0;
  94. }
  95. /* - - - - - - - - */
  96. STDMETHODIMP CAVICmpStream::CUnknownImpl::QueryInterface(
  97. const IID FAR& iid,
  98. void FAR* FAR* ppv)
  99. {
  100. if (iid == IID_IUnknown)
  101. *ppv = &m_pAVIStream->m_Unknown;
  102. else if (iid == IID_IAVIStream)
  103. *ppv = &m_pAVIStream->m_AVIStream;
  104. else {
  105. *ppv = NULL;
  106. return ResultFromScode(E_NOINTERFACE);
  107. }
  108. AddRef();
  109. return AVIERR_OK;
  110. }
  111. /* - - - - - - - - */
  112. STDMETHODIMP_(ULONG) CAVICmpStream::CUnknownImpl::AddRef()
  113. {
  114. uUseCount++;
  115. return ++m_refs;
  116. }
  117. /* - - - - - - - - */
  118. CAVICmpStream::CAVICmpStreamImpl::CAVICmpStreamImpl(
  119. CAVICmpStream FAR* pAVIStream)
  120. {
  121. m_pAVIStream = pAVIStream;
  122. }
  123. /* - - - - - - - - */
  124. CAVICmpStream::CAVICmpStreamImpl::~CAVICmpStreamImpl()
  125. {
  126. }
  127. /* - - - - - - - - */
  128. STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::QueryInterface(
  129. const IID FAR& iid,
  130. void FAR* FAR* ppv)
  131. {
  132. return m_pAVIStream->m_pUnknownOuter->QueryInterface(iid, ppv);
  133. }
  134. /* - - - - - - - - */
  135. STDMETHODIMP_(ULONG) CAVICmpStream::CAVICmpStreamImpl::AddRef()
  136. {
  137. return m_pAVIStream->m_pUnknownOuter->AddRef();
  138. }
  139. /* - - - - - - - - */
  140. STDMETHODIMP_(ULONG) CAVICmpStream::CAVICmpStreamImpl::Release()
  141. {
  142. return m_pAVIStream->m_pUnknownOuter->Release();
  143. }
  144. /* - - - - - - - - */
  145. HRESULT CAVICmpStream::SetUpCompression()
  146. {
  147. LONG lRet = AVIERR_OK;
  148. LPBITMAPINFOHEADER lpbi;
  149. CAVICmpStream FAR * pinst = this; // for convenience....
  150. LRESULT dw;
  151. pinst->pgf = AVIStreamGetFrameOpen(pinst->pavi, NULL);
  152. if (!pinst->pgf) {
  153. // !!! we couldn't decompress the stream!
  154. lRet = AVIERR_INTERNAL;
  155. goto exit;
  156. }
  157. if (pinst->avistream.fccHandler == comptypeDIB)
  158. goto exit;
  159. lpbi = (LPBITMAPINFOHEADER) AVIStreamGetFrame(pinst->pgf, 0);
  160. if (lpbi == NULL) {
  161. lRet = AVIERR_INTERNAL;
  162. goto exit;
  163. }
  164. /*
  165. ** get the size required to hold the format.
  166. ** if the compressor fails return an error
  167. */
  168. dw = ICCompressGetFormatSize(pinst->hic, lpbi);
  169. if ((LONG) dw < (LONG)sizeof(BITMAPINFOHEADER))
  170. goto ic_error;
  171. pinst->cbFormat = (DWORD) dw;
  172. pinst->lpFormat = (LPBITMAPINFOHEADER) GlobalAllocPtr(GHND | GMEM_SHARE, pinst->cbFormat);
  173. if (!pinst->lpFormat) {
  174. lRet = AVIERR_MEMORY;
  175. goto exit;
  176. }
  177. /*
  178. ** get the compressed format from the compressor.
  179. */
  180. dw = ICCompressGetFormat(pinst->hic, lpbi, pinst->lpFormat);
  181. if ((LONG) dw < 0)
  182. goto ic_error;
  183. pinst->avistream.rcFrame.right = pinst->avistream.rcFrame.left +
  184. (int) pinst->lpFormat->biWidth;
  185. pinst->avistream.rcFrame.bottom = pinst->avistream.rcFrame.top +
  186. (int) pinst->lpFormat->biHeight;
  187. dw = ICCompressBegin(pinst->hic, lpbi, pinst->lpFormat);
  188. if (dw != ICERR_OK)
  189. goto ic_error;
  190. /*
  191. ** allocate buffer to hold compressed data.
  192. */
  193. dw = ICCompressGetSize(pinst->hic, lpbi, pinst->lpFormat);
  194. pinst->lpbiC = (LPBITMAPINFOHEADER)
  195. GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, pinst->cbFormat + dw);
  196. if (!pinst->lpbiC) {
  197. lRet = AVIERR_MEMORY;
  198. goto exit;
  199. }
  200. hmemcpy((LPVOID)pinst->lpbiC, pinst->lpFormat, pinst->cbFormat);
  201. pinst->lpC = (LPBYTE) pinst->lpbiC + pinst->lpbiC->biSize +
  202. pinst->lpbiC->biClrUsed * sizeof(RGBQUAD);
  203. //
  204. // check for temporal compress, and allocate a previous
  205. // DIB buffer if needed
  206. //
  207. if (pinst->dwKeyFrameEvery != 1 && !(dwICFlags & VIDCF_FASTTEMPORALC)) {
  208. pinst->lpbiU = (LPBITMAPINFOHEADER)
  209. GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
  210. sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
  211. if (!pinst->lpbiU) {
  212. lRet = AVIERR_MEMORY;
  213. goto exit;
  214. }
  215. dw = ICDecompressGetFormat(pinst->hic, pinst->lpFormat, pinst->lpbiU);
  216. if ((LONG) dw < 0)
  217. goto ic_error;
  218. if (pinst->lpbiU->biSizeImage == 0)
  219. pinst->lpbiU->biSizeImage = pinst->lpbiU->biHeight *
  220. DIBWIDTHBYTES(*pinst->lpbiU);
  221. pinst->lpbiU = (LPBITMAPINFOHEADER)
  222. GlobalReAllocPtr(pinst->lpbiU,
  223. pinst->lpbiU->biSize +
  224. pinst->lpbiU->biClrUsed * sizeof(RGBQUAD) +
  225. pinst->lpbiU->biSizeImage,
  226. GMEM_MOVEABLE | GMEM_SHARE);
  227. if (!pinst->lpbiU) {
  228. lRet = AVIERR_MEMORY;
  229. goto exit;
  230. }
  231. pinst->lpU = (LPBYTE) pinst->lpbiU + pinst->lpbiU->biSize +
  232. pinst->lpbiU->biClrUsed * sizeof(RGBQUAD);
  233. dw = ICDecompressBegin(pinst->hic, pinst->lpFormat, pinst->lpbiU);
  234. if (dw != ICERR_OK)
  235. goto ic_error;
  236. }
  237. // !!! We really should check if the new stream has palette changes....
  238. exit:
  239. if (lRet != AVIERR_OK) {
  240. // Clean up before returning...
  241. }
  242. return ResultFromScode(lRet);
  243. ic_error:
  244. if (dw == ICERR_BADFORMAT)
  245. lRet = AVIERR_BADFORMAT;
  246. else if (dw == ICERR_MEMORY)
  247. lRet = AVIERR_MEMORY;
  248. else
  249. lRet = AVIERR_INTERNAL;
  250. goto exit;
  251. }
  252. /* - - - - - - - - */
  253. STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::Create(LPARAM lParam1, LPARAM lParam2)
  254. {
  255. CAVICmpStream FAR * pinst = m_pAVIStream;
  256. ICINFO icinfo;
  257. AVICOMPRESSOPTIONS FAR *lpOpt = (AVICOMPRESSOPTIONS FAR *)lParam2;
  258. LONG lRet = AVIERR_OK;
  259. // The AVI Stream that we're compressing is passsed in in the <szFile>
  260. // parameter.
  261. pinst->pavi = (PAVISTREAM)lParam1;
  262. // Make sure the uncompressed stream doesn't go away without our
  263. // knowledge....
  264. AVIStreamAddRef(pinst->pavi);
  265. // !!! how can we check if pinst->pavi is valid?
  266. // Get the stream header for future reference....
  267. AVIStreamInfoW(pinst->pavi, &pinst->avistream, sizeof(pinst->avistream));
  268. pinst->ResetInst();
  269. if (!lpOpt || (lpOpt->fccHandler == comptypeDIB)) {
  270. pinst->avistream.fccHandler = comptypeDIB;
  271. lRet = AVIERR_OK;
  272. goto exit;
  273. }
  274. pinst->avistream.fccHandler = lpOpt->fccHandler;
  275. // Open the compressor they asked for in the options structure...
  276. pinst->hic = ICOpen(ICTYPE_VIDEO, lpOpt->fccHandler, ICMODE_COMPRESS);
  277. if (!pinst->hic) {
  278. lRet = AVIERR_NOCOMPRESSOR;
  279. goto exit;
  280. }
  281. if (lpOpt->cbParms) {
  282. ICSetState(pinst->hic, lpOpt->lpParms, lpOpt->cbParms);
  283. }
  284. pinst->avistream.dwQuality = lpOpt->dwQuality;
  285. if (pinst->avistream.dwQuality == ICQUALITY_DEFAULT) {
  286. pinst->avistream.dwQuality = ICGetDefaultQuality(pinst->hic);
  287. }
  288. /*
  289. ** get info about this compressor
  290. */
  291. ICGetInfo(pinst->hic,&icinfo,sizeof(icinfo));
  292. pinst->dwICFlags = icinfo.dwFlags;
  293. if (lpOpt->dwFlags & AVICOMPRESSF_KEYFRAMES)
  294. pinst->dwKeyFrameEvery = lpOpt->dwKeyFrameEvery;
  295. else
  296. pinst->dwKeyFrameEvery = 1;
  297. if (!(icinfo.dwFlags & VIDCF_TEMPORAL))
  298. pinst->dwKeyFrameEvery = 1; // compressor doesn't do temporal
  299. if (lpOpt->dwFlags & AVICOMPRESSF_DATARATE)
  300. pinst->dwMaxSize = muldiv32(lpOpt->dwBytesPerSecond,
  301. pinst->avistream.dwScale,
  302. pinst->avistream.dwRate);
  303. else
  304. pinst->dwMaxSize = 0;
  305. {
  306. ICCOMPRESSFRAMES iccf;
  307. LRESULT dw;
  308. iccf.lpbiOutput = pinst->lpbiC;
  309. iccf.lOutput = 0;
  310. iccf.lpbiInput = pinst->lpbiU;
  311. iccf.lInput = 0;
  312. iccf.lStartFrame = 0;
  313. iccf.lFrameCount = (LONG) pinst->avistream.dwLength;
  314. iccf.lQuality = (LONG) pinst->avistream.dwQuality;
  315. iccf.lDataRate = (LONG) lpOpt->dwBytesPerSecond;
  316. iccf.lKeyRate = (LONG) pinst->dwKeyFrameEvery;
  317. iccf.dwRate = pinst->avistream.dwRate;
  318. iccf.dwScale = pinst->avistream.dwScale;
  319. iccf.dwOverheadPerFrame = 0;
  320. iccf.dwReserved2 = 0;
  321. iccf.GetData = NULL;
  322. iccf.PutData = NULL;
  323. dw = ICSendMessage(pinst->hic,
  324. ICM_COMPRESS_FRAMES_INFO,
  325. (DWORD_PTR) (LPVOID) &iccf,
  326. sizeof(iccf));
  327. // If they support this message, don't give
  328. // warning for data rate!
  329. if (dw == ICERR_OK) {
  330. DPF("Compressor supports COMPRESSFRAMESINFO\n");
  331. // !!! fDataRateChanged = TRUE;
  332. }
  333. #ifdef STATUSCALLBACKS
  334. ICSetStatusProc(pinst->hic,
  335. 0,
  336. pinst,
  337. CompressStatusProc);
  338. #endif
  339. }
  340. exit:
  341. if (lRet != AVIERR_OK) {
  342. // Clean up before returning...
  343. }
  344. return ResultFromScode(lRet);
  345. }
  346. STDMETHODIMP_(ULONG) CAVICmpStream::CUnknownImpl::Release()
  347. {
  348. CAVICmpStream FAR * pinst = m_pAVIStream;
  349. uUseCount--;
  350. if (!--m_refs) {
  351. if (pinst->hic) {
  352. ICCompressEnd(pinst->hic);
  353. if (pinst->dwKeyFrameEvery != 1 && pinst->lpbiU)
  354. ICDecompressEnd(pinst->hic);
  355. if (pinst->lpbiU)
  356. GlobalFreePtr((LPVOID) pinst->lpbiU);
  357. if (pinst->lpbiC)
  358. GlobalFreePtr((LPVOID) pinst->lpbiC);
  359. ICClose(pinst->hic);
  360. }
  361. if (pinst->pgf) {
  362. AVIStreamGetFrameClose(pinst->pgf);
  363. pinst->pgf = 0;
  364. }
  365. if (pinst->pavi) {
  366. // Release our hold on the uncompressed stream....
  367. AVIStreamClose(pinst->pavi);
  368. }
  369. if (pinst->lpFormat)
  370. GlobalFreePtr(pinst->lpFormat);
  371. if (pinst->lpFormatOrig)
  372. GlobalFreePtr(pinst->lpFormatOrig);
  373. delete pinst;
  374. return 0;
  375. }
  376. return m_refs;
  377. }
  378. STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::Info(AVISTREAMINFOW FAR * psi, LONG lSize)
  379. {
  380. CAVICmpStream FAR * pinst = m_pAVIStream;
  381. hmemcpy(psi, &pinst->avistream, min(lSize, sizeof(pinst->avistream)));
  382. // return sizeof(pinst->avistream);
  383. return ResultFromScode(0);
  384. }
  385. STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::Read(
  386. LONG lStart,
  387. LONG lSamples,
  388. LPVOID lpBuffer,
  389. LONG cbBuffer,
  390. LONG FAR * plBytes,
  391. LONG FAR * plSamples)
  392. {
  393. CAVICmpStream FAR * pinst = m_pAVIStream;
  394. LPBITMAPINFOHEADER lpbi;
  395. LONG lRet;
  396. if (!pinst->pgf) {
  397. HRESULT hr;
  398. hr = pinst->SetUpCompression();
  399. if (hr != NOERROR)
  400. return hr;
  401. }
  402. if (pinst->hic == 0) {
  403. lpbi = (LPBITMAPINFOHEADER) AVIStreamGetFrame(pinst->pgf, lStart);
  404. if (!lpbi)
  405. return ResultFromScode(AVIERR_MEMORY);
  406. if (plBytes)
  407. *plBytes = lpbi->biSizeImage;
  408. if (lpBuffer) {
  409. if ((LONG) lpbi->biSizeImage > cbBuffer)
  410. return ResultFromScode(AVIERR_BUFFERTOOSMALL);
  411. hmemcpy(lpBuffer, DIBPTR(lpbi), min((DWORD) cbBuffer, lpbi->biSizeImage));
  412. }
  413. if (plSamples)
  414. *plSamples = 1;
  415. return AVIERR_OK;
  416. }
  417. if (lStart < pinst->lFrameCurrent)
  418. pinst->ResetInst();
  419. while (pinst->lFrameCurrent < lStart) {
  420. ++pinst->lFrameCurrent;
  421. lpbi = (LPBITMAPINFOHEADER) AVIStreamGetFrame(pinst->pgf, pinst->lFrameCurrent);
  422. if (lpbi == NULL) {
  423. pinst->ResetInst(); // Make sure we don't assume anything
  424. return ResultFromScode(AVIERR_INTERNAL);
  425. }
  426. // !!! Check if format has changed!
  427. lRet = pinst->ICCrunch(lpbi, DIBPTR(lpbi));
  428. if (lRet != AVIERR_OK) {
  429. pinst->ResetInst(); // Make sure we don't assume anything
  430. return ResultFromScode(AVIERR_INTERNAL); // !!! error < 0.
  431. }
  432. }
  433. if (plBytes)
  434. *plBytes = pinst->lpbiC->biSizeImage;
  435. if (lpBuffer) {
  436. if ((LONG) pinst->lpbiC->biSizeImage > cbBuffer)
  437. return ResultFromScode(AVIERR_BUFFERTOOSMALL);
  438. hmemcpy(lpBuffer, pinst->lpC,
  439. min((DWORD) cbBuffer, pinst->lpbiC->biSizeImage));
  440. }
  441. if (plSamples)
  442. *plSamples = 1;
  443. return AVIERR_OK;
  444. }
  445. STDMETHODIMP_(LONG) CAVICmpStream::CAVICmpStreamImpl::FindSample(LONG lPos, LONG lFlags)
  446. {
  447. CAVICmpStream FAR * pinst = m_pAVIStream;
  448. if (lFlags & FIND_KEY) {
  449. if (pinst->hic == 0)
  450. return lPos;
  451. if (lFlags & FIND_PREV) {
  452. /* If the frame they're asking about isn't the one we have,
  453. ** we have to go actually do the work and find out.
  454. */
  455. if (lPos < pinst->lLastKeyFrame || lPos > pinst->lFrameCurrent)
  456. Read(lPos, 1, NULL, 0, NULL, NULL);
  457. return pinst->lLastKeyFrame;
  458. } else {
  459. return -1; // !!! Find Next KeyFrame
  460. }
  461. }
  462. if (lFlags & FIND_ANY) {
  463. return lPos;
  464. }
  465. if (lFlags & FIND_FORMAT) {
  466. // !!! This is wrong in the case where we're compressing something
  467. // with a palette change and the compressor preserves it....
  468. if (lFlags & FIND_PREV)
  469. return 0;
  470. else
  471. return -1;
  472. }
  473. return -1;
  474. }
  475. /////////////////////////////////////////////////////////////////////////////
  476. //
  477. // ICCrunch()
  478. //
  479. // crunch a frame and make it fit into the specifed size, by varing the
  480. // quality. the suplied quality is the upper bound.
  481. //
  482. // if the compressor can crunch, then let it crunch
  483. //
  484. // if the compressor does quality, then vary the quality
  485. //
  486. // if the compressor does not do quality, then the caller gets what
  487. // ever it will do.
  488. //
  489. //
  490. // The frame to be compressed is passed in in lpbi.
  491. //
  492. // The compressed frame can be found in the lpC member variable....
  493. //
  494. /////////////////////////////////////////////////////////////////////////////
  495. LONG CAVICmpStream::ICCrunch(LPBITMAPINFOHEADER lpbi, LPVOID lp)
  496. {
  497. DWORD dw;
  498. DWORD dwFlags;
  499. DWORD dwSize;
  500. DWORD ckid;
  501. DWORD dwQuality = avistream.dwQuality;
  502. DWORD dwQualityMin;
  503. DWORD dwQualityMax;
  504. DWORD dwMaxSizeThisFrame;
  505. DWORD dwSizeMin;
  506. DWORD dwSizeMax;
  507. BOOL fKeyFrame=FALSE;
  508. BOOL fFastTemporal = (dwICFlags & VIDCF_FASTTEMPORALC) != 0;
  509. BOOL fCrunch; /* are we crunching? */
  510. BOOL fFirst=TRUE;
  511. dwMaxSizeThisFrame = dwMaxSize;
  512. if (lFrameCurrent == 0 || (dwKeyFrameEvery != 0 &&
  513. lFrameCurrent - lLastKeyFrame >= (long)dwKeyFrameEvery)) {
  514. fKeyFrame = TRUE;
  515. }
  516. //
  517. // give the key frames more space, and take some away from the
  518. // non key frames.
  519. //
  520. // give the key frame two shares, assuming we have more frames to
  521. // go around.
  522. //
  523. if (dwKeyFrameEvery > 0) {
  524. if (lFrameCurrent == 0) {
  525. dwMaxSizeThisFrame = 0xffffff;
  526. } else if (fKeyFrame) {
  527. dwMaxSizeThisFrame = dwMaxSizeThisFrame + dwSaved;
  528. dwSaved = 0;
  529. } else {
  530. DWORD dwTakeAway;
  531. dwTakeAway = dwMaxSizeThisFrame / dwKeyFrameEvery;
  532. if (dwSaved > dwMaxSizeThisFrame)
  533. dwTakeAway = 0;
  534. dwMaxSizeThisFrame -= dwTakeAway;
  535. dwSaved += dwTakeAway;
  536. /* Try to give a little extra space to each frame */
  537. dwMaxSizeThisFrame += dwSaved / dwKeyFrameEvery;
  538. dwSaved -= dwSaved / dwKeyFrameEvery;
  539. }
  540. } else {
  541. // the only key frame is frame zero
  542. if (lFrameCurrent == 0)
  543. dwMaxSizeThisFrame = 0xffffff;
  544. else {
  545. /* Give each frame whatever extra there is.... */
  546. dwMaxSizeThisFrame += dwSaved;
  547. dwSaved = 0;
  548. }
  549. }
  550. //
  551. // if the device supports crunching or does not do quality we dont
  552. // crunch.
  553. //
  554. fCrunch = dwMaxSizeThisFrame > 0 && !(dwICFlags & VIDCF_CRUNCH) &&
  555. (dwICFlags & VIDCF_QUALITY);
  556. ////if (lFrameCurrent > 0 && fCrunch)
  557. //// dwQuality = dwQualityLast;
  558. DPF("ICCrunch: Frame %ld, Quality = %ld, MaxSize = %ld\n", lFrameCurrent, avistream.dwQuality, dwMaxSizeThisFrame);
  559. dwQualityMin = 0;
  560. dwQualityMax = dwQuality;
  561. dwSizeMin = 0;
  562. dwSizeMax = dwMaxSizeThisFrame;
  563. for (;;) {
  564. ckid = 0L;
  565. // This is NOT how we tell the compressor to make a keyframe, but
  566. // somebody might think it is, so this is just to avoid the possibility
  567. // of breaking an old compressor. Probably not necessary.
  568. //
  569. dwFlags = fKeyFrame ? AVIIF_KEYFRAME : 0;
  570. //
  571. // compress the frame
  572. //
  573. dw = ICCompress(hic,
  574. // THIS is how we tell the compressor to make a keyframe
  575. fKeyFrame ? ICCOMPRESS_KEYFRAME : 0, // flags
  576. lpbiC, // output format
  577. lpC, // output data
  578. lpbi, // format of frame to compress
  579. lp, // frame data to compress
  580. &ckid, // ckid for data in AVI file
  581. &dwFlags, // flags in the AVI index.
  582. lFrameCurrent, // frame number of seq.
  583. dwMaxSizeThisFrame, // reqested size in bytes. (if non zero)
  584. dwQuality, // quality value
  585. fKeyFrame | fFastTemporal ? NULL : lpbiU,
  586. fKeyFrame | fFastTemporal ? NULL : lpU);
  587. if (dw != ICERR_OK)
  588. break;
  589. dwSize = lpbiC->biSizeImage;
  590. DPF(" Quality = %ld, Size = %ld, %c\n", dwQuality, dwSize, (dwFlags & AVIIF_KEYFRAME) ? 'K' : ' ');
  591. //
  592. // if the device can't crunch (does not do it it self, or does not do
  593. // quality) then we are done.
  594. //
  595. if (!fCrunch)
  596. break;
  597. //
  598. // we are crunching, see if the frame fit.
  599. //
  600. if (dwSize <= dwMaxSizeThisFrame) {
  601. dwQualityMin = dwQuality;
  602. dwSizeMin = dwSize;
  603. //
  604. // when the quality gets too close bail out.
  605. //
  606. if (dwQualityMax - dwQualityMin <= 10)
  607. break;
  608. //
  609. // if we get within 512 bytes it is good enough
  610. //
  611. if ((LONG) (dwMaxSizeThisFrame - dwSize) <= (LONG) min(512L, dwMaxSizeThisFrame / 8L))
  612. break;
  613. //
  614. // if the first try, (with the user specifed quality) made it
  615. // then use it. otherwise we need to search.
  616. //
  617. if (fFirst)
  618. break;
  619. }
  620. else {
  621. //
  622. // when the quality gets too close bail out.
  623. //
  624. if (dwQualityMax - dwQualityMin <= 1)
  625. break;
  626. dwQualityMax = dwQuality;
  627. dwSizeMax = dwSize;
  628. }
  629. if (fFirst && dwQuality != dwQualityLast)
  630. dwQuality = dwQualityLast;
  631. else
  632. dwQuality = (dwQualityMin + dwQualityMax) / 2;
  633. #if 0
  634. //
  635. // make a guess based on how close we are now.
  636. //
  637. dwQuality = dwQualityMin + muldiv32(dwQualityMax-dwQualityMin,
  638. dwMaxSizeThisFrame-dwSizeMin,dwSizeMax-dwSizeMin);
  639. #endif
  640. fFirst = FALSE;
  641. }
  642. #if 0
  643. /* If this wasn't the first frame, save up any extra space for later */
  644. if (dwSize < dwMaxSizeThisFrame && lFrameCurrent > 0) {
  645. dwSaved += dwMaxSizeThisFrame - dwSize;
  646. // HACK: limit this, so it doesn't get too big!!!
  647. if (dwSaved > 32768L)
  648. dwSaved = 32768L;
  649. if (dwSaved > dwMaxSizeThisFrame * 5)
  650. dwSaved = dwMaxSizeThisFrame * 5;
  651. }
  652. #endif
  653. if (dw != ICERR_OK) {
  654. if (dw == ICERR_BADFORMAT)
  655. return AVIERR_BADFORMAT;
  656. else
  657. return AVIERR_INTERNAL;
  658. }
  659. if (dwFlags & AVIIF_KEYFRAME) {
  660. lLastKeyFrame = lFrameCurrent;
  661. }
  662. //
  663. // remember the quality that worked, it will be the best guess next time.
  664. //
  665. dwQualityLast = dwQuality;
  666. //
  667. // decompress the image into the offscreen buffer, for use next time.
  668. //
  669. if (dwKeyFrameEvery != 1 && lpbiU && !fFastTemporal) {
  670. dw = ICDecompress(hic, 0,
  671. lpbiC,lpC,
  672. lpbiU,lpU);
  673. // !!! error check?
  674. }
  675. //
  676. // return the dwFlags and ckid, by stuffing them in the stream info.
  677. //
  678. m_ckid = ckid;
  679. m_dwFlags = dwFlags;
  680. return AVIERR_OK;
  681. }
  682. /**************************************************************************
  683. * @doc INTERNAL DRAWDIB
  684. *
  685. * @api BOOL | DibEq | This function compares two dibs.
  686. *
  687. * @parm LPBITMAPINFOHEADER lpbi1 | Pointer to one bitmap.
  688. * this DIB is assumed to have the colors after the BITMAPINFOHEADER
  689. *
  690. * @parm LPBITMAPINFOHEADER | lpbi2 | Pointer to second bitmap.
  691. * this DIB is assumed to have the colors after biSize bytes.
  692. *
  693. * @rdesc Returns TRUE if bitmaps are identical, FALSE otherwise.
  694. *
  695. **************************************************************************/
  696. static inline BOOL DibEq(LPBITMAPINFOHEADER lpbi1, LPBITMAPINFOHEADER lpbi2)
  697. {
  698. return
  699. lpbi1->biCompression == lpbi2->biCompression &&
  700. lpbi1->biSize == lpbi2->biSize &&
  701. lpbi1->biWidth == lpbi2->biWidth &&
  702. lpbi1->biHeight == lpbi2->biHeight &&
  703. lpbi1->biBitCount == lpbi2->biBitCount;
  704. }
  705. STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::SetFormat(LONG lPos,LPVOID lpFormat,LONG cbFormat)
  706. {
  707. CAVICmpStream FAR * pinst = m_pAVIStream;
  708. LONG lRet = AVIERR_OK;
  709. HRESULT hr;
  710. LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER) lpFormat;
  711. LRESULT dw;
  712. if (pinst->pgf)
  713. return ResultFromScode(AVIERR_UNSUPPORTED);
  714. if (lpbi->biCompression != BI_RGB)
  715. return ResultFromScode(AVIERR_UNSUPPORTED);
  716. if (pinst->avistream.fccHandler == 0)
  717. pinst->avistream.fccHandler = comptypeDIB;
  718. if (pinst->lpFormatOrig) {
  719. if ((cbFormat = pinst->cbFormatOrig) &&
  720. (_fmemcmp(pinst->lpFormatOrig, lpFormat, (int) cbFormat) == 0))
  721. return AVIERR_OK;
  722. DPF("AVICmprs: SetFormat when format already set!\n");
  723. }
  724. //
  725. // Can only currently set the palette at the end of the file
  726. //
  727. if (lPos < (LONG) (pinst->avistream.dwStart + pinst->avistream.dwLength))
  728. return ResultFromScode(AVIERR_UNSUPPORTED);
  729. if (pinst->avistream.fccHandler == comptypeDIB) {
  730. return AVIStreamSetFormat(pinst->pavi, lPos,
  731. lpFormat, cbFormat);
  732. }
  733. if (pinst->lpFormatOrig) {
  734. //
  735. // We can only change the palette for things with palettes....
  736. //
  737. if (lpbi->biBitCount > 8 || lpbi->biClrUsed == 0)
  738. return ResultFromScode(AVIERR_UNSUPPORTED);
  739. //
  740. // Be sure only the palette is changing, nothing else....
  741. //
  742. if (cbFormat != pinst->cbFormatOrig)
  743. return ResultFromScode(AVIERR_UNSUPPORTED);
  744. if (!DibEq((LPBITMAPINFOHEADER) lpFormat,
  745. (LPBITMAPINFOHEADER) pinst->lpFormatOrig))
  746. return ResultFromScode(AVIERR_UNSUPPORTED);
  747. dw = ICCompressGetFormat(pinst->hic, lpFormat, pinst->lpFormat);
  748. if ((LONG) dw < 0)
  749. goto ic_error;
  750. ICCompressEnd(pinst->hic);
  751. dw = ICCompressBegin(pinst->hic, lpFormat, pinst->lpFormat);
  752. if (dw != ICERR_OK)
  753. goto ic_error;
  754. if (pinst->dwKeyFrameEvery != 1 && pinst->lpbiU &&
  755. !(pinst->dwICFlags & VIDCF_FASTTEMPORALC)) {
  756. ICDecompressEnd(pinst->hic);
  757. dw = ICDecompressGetFormat(pinst->hic, pinst->lpFormat, pinst->lpbiU);
  758. if ((LONG) dw < 0)
  759. goto ic_error;
  760. dw = ICDecompressBegin(pinst->hic, pinst->lpFormat, pinst->lpbiU);
  761. if (dw != ICERR_OK)
  762. goto ic_error;
  763. }
  764. goto setformatandexit;
  765. }
  766. pinst->lpFormatOrig = (LPBITMAPINFOHEADER)
  767. GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, cbFormat);
  768. pinst->cbFormatOrig = cbFormat;
  769. if (!pinst->lpFormatOrig) {
  770. lRet = AVIERR_MEMORY;
  771. goto exit;
  772. }
  773. hmemcpy(pinst->lpFormatOrig, lpFormat, cbFormat);
  774. /*
  775. ** get the size required to hold the format.
  776. ** if the compressor fails return an error
  777. */
  778. dw = ICCompressGetFormatSize(pinst->hic, lpFormat);
  779. if ((LONG) dw < (LONG)sizeof(BITMAPINFOHEADER))
  780. goto ic_error;
  781. pinst->cbFormat = (DWORD) dw;
  782. pinst->lpFormat = (LPBITMAPINFOHEADER) GlobalAllocPtr(GHND | GMEM_SHARE, pinst->cbFormat);
  783. if (!pinst->lpFormat) {
  784. lRet = AVIERR_MEMORY;
  785. goto exit;
  786. }
  787. /*
  788. ** get the compressed format from the compressor.
  789. */
  790. dw = ICCompressGetFormat(pinst->hic, lpFormat, pinst->lpFormat);
  791. if ((LONG) dw < 0)
  792. goto ic_error;
  793. pinst->avistream.rcFrame.right = pinst->avistream.rcFrame.left +
  794. (int) pinst->lpFormat->biWidth;
  795. pinst->avistream.rcFrame.bottom = pinst->avistream.rcFrame.top +
  796. (int) pinst->lpFormat->biHeight;
  797. dw = ICCompressBegin(pinst->hic, lpFormat, pinst->lpFormat);
  798. if (dw != ICERR_OK)
  799. goto ic_error;
  800. /*
  801. ** allocate buffer to hold compressed data.
  802. */
  803. dw = ICCompressGetSize(pinst->hic, lpFormat, pinst->lpFormat);
  804. pinst->lpbiC = (LPBITMAPINFOHEADER)
  805. GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, pinst->cbFormat + dw);
  806. if (!pinst->lpbiC) {
  807. lRet = AVIERR_MEMORY;
  808. goto exit;
  809. }
  810. hmemcpy((LPVOID)pinst->lpbiC, pinst->lpFormat, pinst->cbFormat);
  811. pinst->lpC = (LPBYTE) pinst->lpbiC + pinst->lpbiC->biSize +
  812. pinst->lpbiC->biClrUsed * sizeof(RGBQUAD);
  813. //
  814. // check for temporal compress, and allocate a previous
  815. // DIB buffer if needed
  816. //
  817. if (pinst->dwKeyFrameEvery != 1 &&
  818. !(pinst->dwICFlags & VIDCF_FASTTEMPORALC)) {
  819. pinst->lpbiU = (LPBITMAPINFOHEADER)
  820. GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
  821. sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
  822. if (!pinst->lpbiU) {
  823. lRet = AVIERR_MEMORY;
  824. goto exit;
  825. }
  826. dw = ICDecompressGetFormat(pinst->hic, pinst->lpFormat, pinst->lpbiU);
  827. if ((LONG) dw < 0)
  828. goto ic_error;
  829. if (pinst->lpbiU->biSizeImage == 0)
  830. pinst->lpbiU->biSizeImage = pinst->lpbiU->biHeight *
  831. DIBWIDTHBYTES(*pinst->lpbiU);
  832. pinst->lpbiU = (LPBITMAPINFOHEADER)
  833. GlobalReAllocPtr(pinst->lpbiU,
  834. pinst->lpbiU->biSize +
  835. pinst->lpbiU->biClrUsed * sizeof(RGBQUAD) +
  836. pinst->lpbiU->biSizeImage,
  837. GMEM_MOVEABLE | GMEM_SHARE);
  838. if (!pinst->lpbiU) {
  839. lRet = AVIERR_MEMORY;
  840. goto exit;
  841. }
  842. pinst->lpU = (LPBYTE) pinst->lpbiU + pinst->lpbiU->biSize +
  843. pinst->lpbiU->biClrUsed * sizeof(RGBQUAD);
  844. dw = ICDecompressBegin(pinst->hic, pinst->lpFormat, pinst->lpbiU);
  845. if (dw != ICERR_OK)
  846. goto ic_error;
  847. }
  848. setformatandexit:
  849. hr = AVIStreamSetFormat(pinst->pavi, lPos,
  850. pinst->lpFormat, pinst->cbFormat);
  851. if (hr != NOERROR)
  852. return hr;
  853. exit:
  854. if (lRet != AVIERR_OK) {
  855. // Clean up before returning...
  856. }
  857. return ResultFromScode(lRet);
  858. ic_error:
  859. if (dw == ICERR_BADFORMAT)
  860. lRet = AVIERR_BADFORMAT;
  861. else if (dw == ICERR_MEMORY)
  862. lRet = AVIERR_MEMORY;
  863. else
  864. lRet = AVIERR_INTERNAL;
  865. goto exit;
  866. }
  867. STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::Write(LONG lStart,
  868. LONG lSamples,
  869. LPVOID lpBuffer,
  870. LONG cbBuffer,
  871. DWORD dwFlags,
  872. LONG FAR *plSampWritten,
  873. LONG FAR *plBytesWritten)
  874. {
  875. CAVICmpStream FAR * pinst = m_pAVIStream;
  876. LONG lRet;
  877. if (pinst->pgf)
  878. return ResultFromScode(AVIERR_UNSUPPORTED);
  879. if (lStart < (LONG) (pinst->avistream.dwStart + pinst->avistream.dwLength))
  880. return ResultFromScode(AVIERR_UNSUPPORTED);
  881. if (lSamples > 1)
  882. return ResultFromScode(AVIERR_UNSUPPORTED);
  883. pinst->lFrameCurrent = lStart;
  884. if (pinst->avistream.fccHandler == comptypeDIB) {
  885. // !!! Check here that the frame is the right size....
  886. dwFlags |= AVIIF_KEYFRAME;
  887. } else {
  888. lRet = pinst->ICCrunch(pinst->lpFormatOrig, lpBuffer);
  889. if (lRet != AVIERR_OK)
  890. return ResultFromScode(lRet);
  891. lpBuffer = pinst->lpC;
  892. cbBuffer = pinst->lpbiC->biSizeImage;
  893. dwFlags = pinst->lLastKeyFrame == lStart ? AVIIF_KEYFRAME : 0;
  894. }
  895. return AVIStreamWrite(pinst->pavi,
  896. lStart,
  897. lSamples,
  898. lpBuffer,
  899. cbBuffer,
  900. dwFlags,
  901. plSampWritten,
  902. plBytesWritten);
  903. }
  904. STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::Delete(LONG lStart,LONG lSamples)
  905. {
  906. CAVICmpStream FAR * pinst = m_pAVIStream;
  907. return ResultFromScode(AVIERR_UNSUPPORTED);
  908. }
  909. STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::ReadData(DWORD fcc, LPVOID lp, LONG FAR *lpcb)
  910. {
  911. CAVICmpStream FAR * pinst = m_pAVIStream;
  912. // Don't pass through 'strd' data!
  913. if (fcc == ckidSTREAMHANDLERDATA) {
  914. if (pinst->cbHandler) {
  915. hmemcpy(lp, pinst->lpHandler, min(*lpcb, pinst->cbHandler));
  916. }
  917. *lpcb = pinst->cbHandler;
  918. return AVIERR_OK;
  919. }
  920. return AVIStreamReadData(pinst->pavi, fcc, lp, lpcb);
  921. }
  922. STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::WriteData(DWORD fcc, LPVOID lp, LONG cb)
  923. {
  924. CAVICmpStream FAR * pinst = m_pAVIStream;
  925. return ResultFromScode(AVIERR_UNSUPPORTED);
  926. }
  927. #if 0
  928. STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::Clone(PAVISTREAM FAR * ppaviNew)
  929. {
  930. CAVICmpStream FAR * pinst = m_pAVIStream;
  931. return ResultFromScode(AVIERR_UNSUPPORTED);
  932. }
  933. #endif
  934. #ifdef _WIN32
  935. STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::SetInfo(AVISTREAMINFOW FAR *lpInfo, LONG cbInfo)
  936. {
  937. return ResultFromScode(AVIERR_UNSUPPORTED);
  938. }
  939. #else
  940. STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::Reserved1(void)
  941. {
  942. return ResultFromScode(AVIERR_UNSUPPORTED);
  943. }
  944. STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::Reserved2(void)
  945. {
  946. return ResultFromScode(AVIERR_UNSUPPORTED);
  947. }
  948. STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::Reserved3(void)
  949. {
  950. return ResultFromScode(AVIERR_UNSUPPORTED);
  951. }
  952. STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::Reserved4(void)
  953. {
  954. return ResultFromScode(AVIERR_UNSUPPORTED);
  955. }
  956. STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::Reserved5(void)
  957. {
  958. return ResultFromScode(AVIERR_UNSUPPORTED);
  959. }
  960. #endif
  961. /* - - - - - - - - */
  962. #ifndef _WIN32
  963. static void C816InternalCompilerError(CAVICmpStream FAR * pinst, LPBITMAPINFOHEADER lpbi, LPVOID lpFormat, LONG cbFormat)
  964. {
  965. if (pinst->hic == 0) {
  966. pinst->cbFormat = lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD);
  967. if (lpFormat)
  968. hmemcpy(lpFormat, lpbi, min(cbFormat, (LONG) pinst->cbFormat));
  969. } else {
  970. if (lpFormat) {
  971. hmemcpy(lpFormat, pinst->lpFormat, min(cbFormat, (LONG) pinst->cbFormat));
  972. if (pinst->lpFormat->biClrUsed > 0) {
  973. // Make sure we have the right colors!
  974. // !!! This is bad--We may need to restart the compressor...
  975. hmemcpy((LPBYTE) lpFormat + pinst->lpFormat->biSize,
  976. (LPBYTE) lpbi + lpbi->biSize,
  977. pinst->lpFormat->biClrUsed * sizeof(RGBQUAD));
  978. }
  979. }
  980. }
  981. }
  982. #endif
  983. STDMETHODIMP CAVICmpStream::CAVICmpStreamImpl::ReadFormat(LONG lPos, LPVOID lpFormat, LONG FAR *lpcbFormat)
  984. {
  985. CAVICmpStream FAR * pinst = m_pAVIStream;
  986. LPBITMAPINFOHEADER lpbi;
  987. if (!pinst->pgf) {
  988. HRESULT hr;
  989. hr = pinst->SetUpCompression();
  990. if (hr != NOERROR)
  991. return hr;
  992. }
  993. lpbi = (LPBITMAPINFOHEADER) AVIStreamGetFrame(pinst->pgf, lPos);
  994. if (!lpbi)
  995. return ResultFromScode(AVIERR_MEMORY);
  996. #ifdef _WIN32
  997. if (pinst->hic == 0) {
  998. pinst->cbFormat = lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD);
  999. if (lpFormat)
  1000. hmemcpy(lpFormat, lpbi, min(*lpcbFormat, (LONG) pinst->cbFormat));
  1001. } else {
  1002. if (lpFormat) {
  1003. hmemcpy(lpFormat, pinst->lpFormat, min(*lpcbFormat, (LONG) pinst->cbFormat));
  1004. // Fix: Ensure we don't write more than *lpcbFormat bytes by ensuring biSize is smaller
  1005. // than *lpcbFormat, and only copying at most *lpcbFormat - pinst->lpFormat->biSize bytes.
  1006. // The "signed" and "unsigned" casts are all correct - they avoid compiler errors.
  1007. if (pinst->lpFormat->biClrUsed > 0 && signed(pinst->lpFormat->biSize) >= 0 && signed(pinst->lpFormat->biSize) < *lpcbFormat) {
  1008. // Make sure we have the right colors!
  1009. // !!! This is bad--We may need to restart the compressor...
  1010. hmemcpy((LPBYTE) lpFormat + pinst->lpFormat->biSize,
  1011. (LPBYTE) lpbi + lpbi->biSize,
  1012. min(unsigned(*lpcbFormat) - pinst->lpFormat->biSize,pinst->lpFormat->biClrUsed * sizeof(RGBQUAD)));
  1013. }
  1014. }
  1015. }
  1016. #else
  1017. ::C816InternalCompilerError(pinst, lpbi, lpFormat, *lpcbFormat);
  1018. #endif
  1019. *lpcbFormat = pinst->cbFormat;
  1020. return AVIERR_OK;
  1021. }