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.

736 lines
20 KiB

  1. /****************************************************************************
  2. *
  3. * GETFRAME.CPP
  4. *
  5. * this file contains the GetFrame APIs
  6. *
  7. * AVIStreamGetFrameOpen
  8. * AVIStreamGetFrameClose
  9. * AVIStreamGetFrame
  10. *
  11. * it also contains the default GetFrame implemenation
  12. *
  13. * GetFrameDef
  14. *
  15. ***************************************************************************/
  16. #include <win32.h>
  17. #include <vfw.h>
  18. #include <memory.h> // for _fmemset
  19. #include "debug.h" // for good ol' DPF()
  20. /****************************************************************************
  21. *
  22. ***************************************************************************/
  23. //!!! ACK
  24. #define AVISF_VIDEO_PALCHANGES 0x00010000
  25. #define ERR_FAIL ResultFromScode(E_FAIL)
  26. #define ERR_MEMORY ResultFromScode(E_OUTOFMEMORY)
  27. #define WIDTHBYTES(i) ((UINT)((i+31)&(~31))/8)
  28. #define DIBWIDTHBYTES(lpbi) (UINT)WIDTHBYTES((UINT)(lpbi)->biWidth * (UINT)(lpbi)->biBitCount)
  29. /****************************************************************************
  30. *
  31. * class for default IGetFrame
  32. *
  33. ***************************************************************************/
  34. class FAR GetFrameDef : public IGetFrame
  35. {
  36. public:
  37. GetFrameDef(IAVIStream FAR *pavi=NULL);
  38. public:
  39. // IUnknown stuff
  40. STDMETHODIMP QueryInterface(REFIID riid, LPVOID FAR* ppv);
  41. STDMETHODIMP_(ULONG) AddRef();
  42. STDMETHODIMP_(ULONG) Release();
  43. // IGetFrame stuff.
  44. STDMETHODIMP Begin (LONG lStart, LONG lEnd, LONG lRate);
  45. STDMETHODIMP End ();
  46. STDMETHODIMP SetFormat (LPBITMAPINFOHEADER lpbi, LPVOID lpBits, int x, int y, int dx, int dy);
  47. STDMETHODIMP_(LPVOID) GetFrame (LONG lPos);
  48. private:
  49. ~GetFrameDef();
  50. void FreeStuff();
  51. // for AddRef
  52. ULONG ulRefCount;
  53. // instance data.
  54. BOOL fBegin; // inside of Begin/End
  55. BOOL fFmtChanges; // file has format changes.
  56. PAVISTREAM pavi;
  57. LONG lFrame; // last frame decompressed
  58. LPVOID lpBuffer; // read buffer.
  59. LONG cbBuffer; // size of read buffer
  60. LPVOID lpFormat; // stream format
  61. LONG cbFormat; // size of format
  62. LPVOID lpFrame; // the frame (format)
  63. LPVOID lpBits; // the frame (bits)
  64. HIC hic; // decompress handle
  65. BOOL fDecompressEx; // using ICDecompressEx
  66. int x,y,dx,dy; // where to decompress
  67. // to watch for the format changing.
  68. DWORD dwFormatChangeCount;
  69. DWORD dwEditCount;
  70. };
  71. /****************************************************************************
  72. IUnknown stuff.
  73. ***************************************************************************/
  74. STDMETHODIMP GetFrameDef::QueryInterface(REFIID riid, LPVOID FAR* ppv)
  75. {
  76. if (riid == IID_IGetFrame ||
  77. riid == IID_IUnknown) { //!!! should we do Unknown or pass on?
  78. *ppv = (LPVOID)this;
  79. AddRef();
  80. return ResultFromScode(S_OK);
  81. }
  82. else if (pavi) {
  83. return pavi->QueryInterface(riid, ppv);
  84. }
  85. else {
  86. *ppv = NULL;
  87. return ResultFromScode(E_NOINTERFACE);
  88. }
  89. }
  90. STDMETHODIMP_(ULONG) GetFrameDef::AddRef()
  91. {
  92. return ulRefCount++;
  93. }
  94. STDMETHODIMP_(ULONG) GetFrameDef::Release()
  95. {
  96. if (--ulRefCount == 0) {
  97. delete this;
  98. return 0;
  99. }
  100. return ulRefCount;
  101. }
  102. /****************************************************************************
  103. ***************************************************************************/
  104. GetFrameDef::GetFrameDef(IAVIStream FAR *pavi)
  105. {
  106. this->pavi = pavi;
  107. ulRefCount = 1;
  108. fBegin = FALSE;
  109. fFmtChanges = FALSE;
  110. fDecompressEx = FALSE;
  111. lFrame = -4242;
  112. lpBuffer = NULL;
  113. lpFormat = NULL;
  114. cbBuffer = 0;
  115. cbFormat = 0;
  116. lpFrame = NULL;
  117. lpBits = NULL;
  118. hic = NULL;
  119. if (this->pavi == NULL)
  120. return;
  121. pavi->AddRef();
  122. }
  123. /****************************************************************************
  124. ***************************************************************************/
  125. GetFrameDef::~GetFrameDef()
  126. {
  127. FreeStuff();
  128. if (pavi)
  129. pavi->Release();
  130. }
  131. /****************************************************************************
  132. ***************************************************************************/
  133. void GetFrameDef::FreeStuff()
  134. {
  135. if (this->lpFrame && this->lpFrame != this->lpFormat) {
  136. GlobalFreePtr(this->lpFrame);
  137. this->lpFrame = 0;
  138. }
  139. if (this->lpFormat) {
  140. GlobalFreePtr(this->lpFormat);
  141. this->lpFormat = 0;
  142. }
  143. if (this->hic) {
  144. if (this->fDecompressEx)
  145. ICDecompressExEnd(this->hic);
  146. else
  147. ICDecompressEnd(this->hic);
  148. ICClose(this->hic);
  149. this->hic = 0;
  150. }
  151. }
  152. /****************************************************************************
  153. ***************************************************************************/
  154. STDMETHODIMP GetFrameDef::SetFormat(LPBITMAPINFOHEADER lpbi, LPVOID lpBits, int x, int y, int dx, int dy)
  155. {
  156. LPBITMAPINFOHEADER lpbiC;
  157. LPBITMAPINFOHEADER lpbiU;
  158. LRESULT dw;
  159. DWORD fccHandler;
  160. AVISTREAMINFOW info;
  161. BOOL fScreen;
  162. //
  163. // lpbi == AVIGETFRAMEF_BESTDISPLAYFMT means choose the best format for the
  164. // screen.
  165. //
  166. if (fScreen = (lpbi == (LPBITMAPINFOHEADER)AVIGETFRAMEF_BESTDISPLAYFMT))
  167. lpbi = NULL;
  168. //
  169. // get the vital stats
  170. //
  171. _fmemset(&info, 0, sizeof(info));
  172. pavi->Info(&info, sizeof(info));
  173. //
  174. // is this a video stream?
  175. //
  176. if (info.fccType != streamtypeVIDEO)
  177. return ERR_FAIL;
  178. this->fBegin = FALSE;
  179. this->fFmtChanges = (info.dwFlags & AVISF_VIDEO_PALCHANGES) != 0;
  180. this->dwEditCount = info.dwEditCount;
  181. this->dwFormatChangeCount = info.dwFormatChangeCount;
  182. //
  183. // get the stream format
  184. //
  185. if (this->lpFormat == NULL) {
  186. //
  187. // alocate a read buffer.
  188. //
  189. this->cbBuffer = (LONG)info.dwSuggestedBufferSize;
  190. if (this->cbBuffer == 0)
  191. this->cbBuffer = 1024;
  192. AVIStreamFormatSize(this->pavi,
  193. AVIStreamStart(this->pavi),
  194. &this->cbFormat);
  195. this->lpFormat = GlobalAllocPtr(GHND,this->cbFormat + this->cbBuffer);
  196. if (this->lpFormat == NULL)
  197. goto error;
  198. AVIStreamReadFormat(this->pavi, AVIStreamStart(this->pavi),
  199. this->lpFormat, &this->cbFormat);
  200. this->lpBuffer = (LPBYTE)this->lpFormat+this->cbFormat;
  201. }
  202. lpbiC = (LPBITMAPINFOHEADER)this->lpFormat;
  203. //
  204. // do standard BITMAPINFO header cleanup!
  205. //
  206. if (lpbiC->biClrUsed == 0 && lpbiC->biBitCount <= 8)
  207. lpbiC->biClrUsed = (1 << (int)lpbiC->biBitCount);
  208. if (lpbiC->biSizeImage == 0 && lpbiC->biCompression == BI_RGB)
  209. lpbiC->biSizeImage = DIBWIDTHBYTES(lpbiC) * lpbiC->biHeight;
  210. //
  211. // if the stream is uncompressed, we dont need a decompress buffer
  212. // make sure the caller hs not suggested a format first.
  213. //
  214. if (lpbiC->biCompression == 0 && lpBits == NULL) {
  215. if (lpbi == NULL ||
  216. (lpbi->biCompression == lpbiC->biCompression &&
  217. lpbi->biWidth == lpbiC->biWidth &&
  218. lpbi->biHeight == lpbiC->biHeight &&
  219. lpbi->biBitCount == lpbiC->biBitCount)) {
  220. this->lpBits = (LPBYTE)lpbiC + (int)lpbiC->biSize +
  221. (int)lpbiC->biClrUsed * sizeof(RGBQUAD);
  222. goto done;
  223. }
  224. }
  225. //
  226. // alocate the decompress buffer.
  227. //
  228. if (this->lpFrame == NULL) {
  229. this->lpFrame = GlobalAllocPtr(GHND,
  230. sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD));
  231. if (this->lpFrame == NULL) {
  232. DPF("GetFrameInit: Can't allocate frame buffer!\n");
  233. goto error;
  234. }
  235. }
  236. lpbiC = (LPBITMAPINFOHEADER)this->lpFormat;
  237. lpbiU = (LPBITMAPINFOHEADER)this->lpFrame;
  238. if (this->hic == NULL) {
  239. if (lpbiC->biCompression == 0)
  240. fccHandler = mmioFOURCC('D','I','B',' ');
  241. else if (lpbiC->biCompression == BI_RLE8)
  242. fccHandler = mmioFOURCC('R','L','E',' ');
  243. else
  244. fccHandler = info.fccHandler;
  245. if (lpbi) {
  246. if (lpbi->biWidth == 0)
  247. lpbi->biWidth = lpbiC->biWidth;
  248. if (lpbi->biHeight == 0)
  249. lpbi->biHeight = lpbiC->biHeight;
  250. }
  251. this->hic = ICDecompressOpen(ICTYPE_VIDEO, /*info.fccType,*/
  252. fccHandler,lpbiC,lpbi);
  253. if (this->hic == NULL) {
  254. DPF("GetFrameInit: Can't find decompressor!\n");
  255. goto error;
  256. }
  257. }
  258. if (lpbi) {
  259. if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
  260. lpbi->biClrUsed = (1 << (int)lpbi->biBitCount);
  261. hmemcpy(lpbiU,lpbi,lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD));
  262. if (lpbi->biBitCount <= 8) {
  263. ICDecompressGetPalette(this->hic,lpbiC,lpbiU);
  264. }
  265. } else if (fScreen) {
  266. ICGetDisplayFormat(this->hic, lpbiC, lpbiU, 0, dx, dy);
  267. } else {
  268. dw = ICDecompressGetFormat(this->hic,lpbiC,lpbiU);
  269. if ((LONG)dw < ICERR_OK)
  270. goto error;
  271. }
  272. //
  273. // do standard BITMAPINFO header cleanup!
  274. //
  275. if (lpbiU->biClrUsed == 0 && lpbiU->biBitCount <= 8)
  276. lpbiU->biClrUsed = (1 << (int)lpbiU->biBitCount);
  277. if (lpbiU->biSizeImage == 0 && lpbiU->biCompression == BI_RGB)
  278. lpbiU->biSizeImage = DIBWIDTHBYTES(lpbiU) * lpbiU->biHeight;
  279. //
  280. // if we were passed a bits pointer, use it else re-alloc lpFrame
  281. // to contain the bits too.
  282. //
  283. if (lpBits) {
  284. this->lpBits = lpBits;
  285. }
  286. else {
  287. this->lpFrame = GlobalReAllocPtr(this->lpFrame,lpbiU->biSize +
  288. lpbiU->biSizeImage +
  289. lpbiU->biClrUsed * sizeof(RGBQUAD), GMEM_MOVEABLE);
  290. if (this->lpFrame == NULL) {
  291. DPF("GetFrameInit: Can't resize frame buffer!\n");
  292. goto error;
  293. }
  294. lpbiU = (LPBITMAPINFOHEADER)this->lpFrame;
  295. this->lpBits = (LPBYTE)lpbiU + (int)lpbiU->biSize +
  296. (int)lpbiU->biClrUsed * sizeof(RGBQUAD);
  297. }
  298. //
  299. // use ICDecompressEx if we need to. we need DecompressEx if
  300. // we are decompressing into a smaller area of the DIB, not the
  301. // whole surface.
  302. //
  303. if (dx == -1)
  304. dx = (int)lpbiU->biWidth;
  305. if (dy == -1)
  306. dy = (int)lpbiU->biHeight;
  307. this->fDecompressEx = (x != 0 || y != 0 ||
  308. dy != (int)lpbiU->biHeight || dx != (int)lpbiU->biWidth);
  309. if (this->fDecompressEx) {
  310. this->x = x;
  311. this->y = y;
  312. this->dx = dx;
  313. this->dy = dy;
  314. dw = ICDecompressExBegin(this->hic, 0,
  315. lpbiC, NULL, 0, 0, lpbiC->biWidth, lpbiC->biHeight,
  316. lpbiU, NULL, x, y, dx, dy);
  317. }
  318. else {
  319. dw = ICDecompressBegin(this->hic,lpbiC,lpbiU);
  320. }
  321. if (dw != ICERR_OK) {
  322. DPF("GetFrameSetFormat: ICDecompressBegin failed!\n");
  323. goto error;
  324. }
  325. done:
  326. this->lFrame = -4224; // bogus value
  327. return AVIERR_OK;
  328. error:
  329. FreeStuff();
  330. return ERR_FAIL;
  331. }
  332. /****************************************************************************
  333. ***************************************************************************/
  334. STDMETHODIMP GetFrameDef::Begin(LONG lStart, LONG lEnd, LONG lRate)
  335. {
  336. fBegin = TRUE;
  337. GetFrame(lStart);
  338. return AVIERR_OK;
  339. }
  340. /****************************************************************************
  341. ***************************************************************************/
  342. STDMETHODIMP GetFrameDef::End()
  343. {
  344. fBegin = FALSE;
  345. return AVIERR_OK;
  346. }
  347. /****************************************************************************
  348. ***************************************************************************/
  349. STDMETHODIMP_(LPVOID) GetFrameDef::GetFrame(LONG lPos)
  350. {
  351. LPBITMAPINFOHEADER lpbiC;
  352. LPBITMAPINFOHEADER lpbiU;
  353. LONG l;
  354. LONG lKey;
  355. LONG lBytes;
  356. LONG lSize;
  357. LONG lRead;
  358. LRESULT err;
  359. AVISTREAMINFOW info;
  360. HRESULT hr;
  361. if (!this->pavi) {
  362. DPF("AVIStreamGetFrame: bad pointer\n");
  363. return NULL;
  364. }
  365. if (this->lpFormat == NULL) {
  366. return NULL;
  367. }
  368. //
  369. // if we are not in a Begin/End pair check for the format changing etc.
  370. //
  371. if (!this->fBegin) {
  372. _fmemset(&info, 0, sizeof(info));
  373. this->pavi->Info(&info, sizeof(info));
  374. if (info.dwFormatChangeCount != dwFormatChangeCount) {
  375. DPF("AVIStreamGetFrame: format has changed\n");
  376. if (this->lpFrame) {
  377. BITMAPINFOHEADER bi = *((LPBITMAPINFOHEADER)this->lpFrame);
  378. FreeStuff(); // nuke it all.
  379. if (SetFormat(&bi, NULL, 0, 0, -1, -1) != 0 &&
  380. SetFormat(NULL, NULL, 0, 0, -1, -1) != 0)
  381. return NULL;
  382. } else {
  383. if (SetFormat(NULL, NULL, 0, 0, -1, -1) != 0) {
  384. return NULL;
  385. }
  386. }
  387. }
  388. if (info.dwEditCount != dwEditCount) {
  389. DPF("AVIStreamGetFrame: stream has been edited (%lu)\n", info.dwEditCount);
  390. dwEditCount = info.dwEditCount;
  391. this->lFrame = -4224; // Invalidate the cached frame
  392. }
  393. }
  394. //
  395. // quick check for the last frame.
  396. //
  397. if (this->lFrame == lPos)
  398. return this->hic ? this->lpFrame : this->lpFormat;
  399. //
  400. // locate the nearest key frame.
  401. //
  402. lKey = AVIStreamFindSample(this->pavi, lPos, FIND_KEY|FIND_PREV);
  403. //
  404. // either lPos was out of range or some internal error!
  405. //
  406. if (lKey == -1) {
  407. DPF("AVIStreamGetFrame: Couldn't find key frame!\n");
  408. return NULL;
  409. }
  410. //
  411. // we need to go back to the specifed key frame
  412. // or our current frame witch ever is closer
  413. //
  414. if (this->lFrame < lPos && this->lFrame >= lKey)
  415. lKey = this->lFrame + 1;
  416. lpbiC = (LPBITMAPINFOHEADER)this->lpFormat;
  417. lpbiU = (LPBITMAPINFOHEADER)this->lpFrame;
  418. //
  419. // decompress frame data from key frame to current frame.
  420. //
  421. for (l=lKey; l<=lPos; l++) {
  422. //
  423. // go read the format and call ICDecompressGetPalette() so
  424. // if the palette changes things will work.
  425. //
  426. if (this->fFmtChanges) {
  427. AVIStreamReadFormat(this->pavi, l, lpbiC, &this->cbFormat);
  428. if (lpbiU && lpbiU->biBitCount <= 8) {
  429. ICDecompressGetPalette(this->hic,lpbiC,lpbiU);
  430. }
  431. }
  432. try_read_again:
  433. hr = AVIStreamRead(this->pavi, l, 1,
  434. this->lpBuffer, this->cbBuffer, &lBytes, &lRead);
  435. //
  436. // the read failed, mabey our buffer was too small
  437. // or it was a real error.
  438. //
  439. if (hr != NOERROR) {
  440. DPF("AVIStreamGetFrame: AVIStreamRead returns %lx\n", (DWORD) hr);
  441. lSize = 0;
  442. hr = AVIStreamSampleSize(this->pavi, l, &lSize);
  443. if (lSize > this->cbBuffer) {
  444. LPVOID lp;
  445. DPF("AVIStreamGetFrame: re-sizing read buffer from %ld to %ld\n", this->cbBuffer, lSize);
  446. lp = GlobalReAllocPtr(this->lpFormat,this->cbFormat+lSize,0);
  447. if (lp == NULL) {
  448. DPF("AVIStreamGetFrame: Couldn't resize buffer\n");
  449. return NULL;
  450. }
  451. this->lpFormat = lp;
  452. lpbiC = (LPBITMAPINFOHEADER)this->lpFormat;
  453. this->lpBuffer = (LPBYTE)lp + this->cbFormat;
  454. this->cbBuffer = lSize;
  455. goto try_read_again;
  456. }
  457. }
  458. if (lRead != 1) {
  459. DPF("AVIStreamGetFrame: AVIStreamRead failed!\n");
  460. return NULL;
  461. }
  462. if (lBytes == 0)
  463. continue;
  464. lpbiC->biSizeImage = lBytes;
  465. if (this->hic == NULL) {
  466. this->lFrame = lPos;
  467. return this->lpFormat;
  468. }
  469. else if (this->fDecompressEx) {
  470. err = ICDecompressEx(this->hic,0,
  471. lpbiC,this->lpBuffer,
  472. 0,0,(int)lpbiC->biWidth,(int)lpbiC->biHeight,
  473. lpbiU,this->lpBits,
  474. this->x,this->y,this->dx,this->dy);
  475. }
  476. else {
  477. err = ICDecompress(this->hic,0,
  478. lpbiC,this->lpBuffer,lpbiU,this->lpBits);
  479. }
  480. // !!! Error check?
  481. if (err < 0) {
  482. }
  483. }
  484. this->lFrame = lPos;
  485. return this->hic ? this->lpFrame : this->lpFormat;
  486. }
  487. /********************************************************************
  488. * @doc EXTERNAL AVIStreamGetFrameOpen
  489. *
  490. * @api PGETFRAME | AVIStreamGetFrameOpen | This functions prepares
  491. * to decompress video frames from the stream specified.
  492. *
  493. * @parm PAVISTREAM | pavi | Specifies a pointer to the
  494. * stream used as the video source.
  495. *
  496. * @parm LPBITMAPINFOHEADER | lpbiWanted | Specifies a pointer to
  497. * a structure defining the desired video format. If this is NULL,
  498. * a default format is used.
  499. *
  500. * @rdesc Returns a GetFrame object, which can be used with
  501. * <f AVIStreamGetFrame>.
  502. *
  503. * If the system can't find decompressor that can decompress the stream
  504. * to the format given, or to any RGB format, the function returns NULL.
  505. *
  506. * @comm The <p pavi> parameter must specify a video stream.
  507. *
  508. * This is essentially just a helper function to handle a simple form
  509. * of decompression.
  510. *
  511. * @xref <f AVIStreamGetFrame> <f AVIStreamGetFrameClose>
  512. **********************************************************************/
  513. STDAPI_(PGETFRAME) AVIStreamGetFrameOpen(PAVISTREAM pavi, LPBITMAPINFOHEADER lpbiWanted)
  514. {
  515. PGETFRAME pgf=NULL;
  516. //
  517. // first ask the IAVIStream object if it can handle IGetFrame and
  518. // if it can let it do it.
  519. //
  520. pavi->QueryInterface(IID_IGetFrame, (LPVOID FAR *)&pgf);
  521. if (pgf == NULL) {
  522. //
  523. // the stream can't do it, make our own object.
  524. //
  525. pgf = new GetFrameDef(pavi);
  526. }
  527. //
  528. // set the format the caller wants
  529. //
  530. if (pgf->SetFormat(lpbiWanted, NULL, 0, 0, -1, -1)) {
  531. DPF("AVIStreamGetFrameOpen: unable to set format\n");
  532. pgf->Release();
  533. return NULL;
  534. }
  535. return pgf;
  536. }
  537. /********************************************************************
  538. * @doc EXTERNAL AVIStreamGetFrameClose
  539. *
  540. * @api LONG | AVIStreamGetFrameClose | This function releases resources
  541. * used to decompress video frames.
  542. *
  543. * @parm PGETFRAME | pget | Specifies a handle returned from <f AVIStreamGetFrameOpen>.
  544. * After calling this function, the handle is invalid.
  545. *
  546. * @rdesc Returns an error code.
  547. *
  548. * @xref <f AVIStreamGetFrameOpen> <f AVIStreamGetFrame>
  549. **********************************************************************/
  550. STDAPI AVIStreamGetFrameClose(PGETFRAME pgf)
  551. {
  552. if (pgf)
  553. pgf->Release();
  554. return AVIERR_OK;
  555. }
  556. /********************************************************************
  557. * @doc EXTERNAL AVIStreamGetFrame
  558. *
  559. * @api LPVOID | AVIStreamGetFrame | This function returns a pointer to
  560. * a decompressed frame of video.
  561. *
  562. * @parm PGETFRAME | pgf | Specifies a pointer to a GetFrame object.
  563. *
  564. * @parm LONG | lPos | Specifies the position of desired frame in samples.
  565. *
  566. * @rdesc Returns NULL on error; otherwise it returns a far pointer
  567. * to the frame data. The returned data is a packed DIB.
  568. *
  569. * @comm The returned frame is valid only until the next call
  570. * to <f AVIStreamGetFrame> or <f AVIStreamGetFrameClose>.
  571. *
  572. * @xref <f AVIStreamGetFrameOpen>
  573. **********************************************************************/
  574. STDAPI_(LPVOID) AVIStreamGetFrame(PGETFRAME pgf, LONG lPos)
  575. {
  576. if (pgf == NULL)
  577. return NULL;
  578. return pgf->GetFrame(lPos);
  579. }
  580. // !!! Do we need an AVIStreamGetFrameSetFormat?