Source code of Windows XP (NT5)
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.

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